import { chat, rawSSE, save, MODELS, short } from "./lib.ts"; const schema = { type: "object", properties: { city: { type: "string" }, country: { type: "string" }, population_millions: { type: "number" }, landmarks: { type: "array", items: { type: "string" } }, }, required: ["city", "country", "population_millions", "landmarks"], additionalProperties: false, }; const out: Record = {}; for (const model of MODELS) { const payload = { model, messages: [{ role: "user", content: "Describe Montreal. Two landmarks max." }], max_completion_tokens: 200, response_format: { type: "json_schema", json_schema: { name: "city_info", strict: true, schema } }, }; const r = await chat(payload); const b: any = r.body; let parsed: unknown = null; let valid = false; try { parsed = JSON.parse(b?.choices?.[0]?.message?.content ?? ""); valid = typeof (parsed as any).city === "string" && Array.isArray((parsed as any).landmarks); } catch {} out[`${model}:json_schema`] = { status: r.status, valid, parsed, finish: b?.choices?.[0]?.finish_reason, usage: b?.usage, reasoning: short(b?.choices?.[0]?.message?.reasoning, 100), error: r.status !== 200 ? b : undefined }; console.log(model, "json_schema strict:", r.status, "valid", valid, short(parsed ?? b, 250)); // streamed json_schema const s = await rawSSE("/chat/completions", { ...payload, stream: true }); let content = ""; for (const e of s.events) if (e.data !== "[DONE]" && e.data?.choices?.[0]?.delta?.content) content += e.data.choices[0].delta.content; let sValid = false; try { sValid = typeof JSON.parse(content).city === "string"; } catch {} out[`${model}:json_schema_stream`] = { status: s.status, valid: sValid, content: short(content, 300), error: s.error }; console.log(model, "json_schema stream:", s.status, "valid", sValid); // schema with a forbidden keyword (pattern) to capture the error shape const r3 = await chat({ ...payload, response_format: { type: "json_schema", json_schema: { name: "x", strict: true, schema: { type: "object", properties: { code: { type: "string", pattern: "^[A-Z]{3}$" } }, required: ["code"], additionalProperties: false } } }, }); out[`${model}:json_schema_pattern`] = { status: r3.status, body: r3.status !== 200 ? r3.body : short((r3.body as any)?.choices?.[0]?.message?.content, 100) }; console.log(model, "json_schema with pattern:", r3.status, short(r3.body, 300)); // strict without additionalProperties:false const r4 = await chat({ ...payload, response_format: { type: "json_schema", json_schema: { name: "x", strict: true, schema: { type: "object", properties: { a: { type: "string" } }, required: ["a"] } } }, }); out[`${model}:json_schema_no_addprops`] = { status: r4.status, body: r4.status !== 200 ? r4.body : short((r4.body as any)?.choices?.[0]?.message?.content, 100) }; console.log(model, "strict w/o additionalProperties:false:", r4.status, short(r4.body, 300)); } save("04-structured.json", out);