|
1 | | -import { assertEquals } from "jsr:@std/assert@1.0.19"; |
| 1 | +import { assertEquals, assertRejects, assertThrows } from "jsr:@std/assert@1.0.19"; |
2 | 2 | for (const [key, value] of Object.entries({ |
3 | 3 | SUPABASE_URL: "https://supabase.test", SUPABASE_ANON_KEY: "test-anon-key", |
4 | 4 | SUPABASE_SERVICE_ROLE_KEY: "test-service-key", OPENROUTER_API_KEY: "test-provider-key", |
@@ -40,3 +40,40 @@ Deno.test("AI JSON and SSE failures never expose provider response bodies", asyn |
40 | 40 | } |
41 | 41 | } finally { globalThis.fetch = originalFetch; } |
42 | 42 | }); |
| 43 | + |
| 44 | +Deno.test("common JSON slips are repaired locally instead of paying for a retry", async () => { |
| 45 | + const { parseJsonFromText } = await import("./provider.ts"); |
| 46 | + const parse = (text: string) => parseJsonFromText(text, "OpenRouter"); |
| 47 | + assertEquals(parse('```JSON\n{"a":1}\n```'), { a: 1 }); |
| 48 | + assertEquals(parse('```\n{"a":1}\n```'), { a: 1 }); |
| 49 | + assertEquals(parse('Here you go: {"a":"x"} Note: {not json}'), { a: "x" }); |
| 50 | + assertEquals(parse('Use {braces} like so. {"a":[1,2,],}'), { a: [1, 2] }); |
| 51 | + assertEquals(parse('{"a":"line one\nline two\t!"}'), { a: "line one\nline two\t!" }); |
| 52 | + assertEquals(parse('{"a":"keep , ] inside strings",}'), { a: "keep , ] inside strings" }); |
| 53 | + // A broken outer object never yields one of its nested objects. |
| 54 | + assertThrows(() => parse('{"case": {"title": "x"}, oops}')); |
| 55 | + // A reply cut off mid-object is not completed. |
| 56 | + assertThrows(() => parse('{"contentions": [{"title": "x"}, {"title": "y"')); |
| 57 | +}); |
| 58 | + |
| 59 | +Deno.test("a reply cut at the output cap is not retried, and a schema is sent as strict json_schema", async () => { |
| 60 | + const { callAI } = await import("./provider.ts"); |
| 61 | + const originalFetch = globalThis.fetch; |
| 62 | + const bodies: Array<Record<string, unknown>> = []; |
| 63 | + globalThis.fetch = (async (_input: Request | URL | string, init?: RequestInit) => { |
| 64 | + bodies.push(JSON.parse(String(init?.body))); |
| 65 | + return Response.json({ |
| 66 | + choices: [{ message: { content: '{"queries": ["a", "b' }, finish_reason: "length" }], |
| 67 | + usage: { prompt_tokens: 10, completion_tokens: 5, cost: 0.01 }, |
| 68 | + }); |
| 69 | + }) as typeof fetch; |
| 70 | + try { |
| 71 | + const schema = { name: "follow_up_queries", schema: { type: "object" } }; |
| 72 | + const error = await assertRejects(() => |
| 73 | + callAI("openrouter", "openai/gpt-6-luna", "system", [{ role: "user", content: "hi" }], "low", undefined, undefined, schema) |
| 74 | + ) as { usage?: { inputTokens?: number } }; |
| 75 | + assertEquals(bodies.length, 1); |
| 76 | + assertEquals(bodies[0].response_format, { type: "json_schema", json_schema: { name: "follow_up_queries", strict: true, schema: { type: "object" } } }); |
| 77 | + assertEquals(error.usage?.inputTokens, 10); |
| 78 | + } finally { globalThis.fetch = originalFetch; } |
| 79 | +}); |
0 commit comments