// Probe 04: structured output (json_schema strict) — with and without provider.require_parameters, streaming once. import { raw, rawSSE, save, short } from "./lib.ts"; import { MODELS, OPENAI, byId } from "./models.ts"; const response_format = { type: "json_schema", json_schema: { name: "city_info", strict: true, 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 results: any = {}; for (const model of MODELS) { const body: any = { model, messages: [{ role: "user", content: "Give facts about Montreal." }], response_format, max_tokens: 200, provider: { require_parameters: true }, usage: { include: true }, ...(byId.get(model)?.reasoning ? { reasoning: { effort: "low" } } : {}), }; const r = await raw("/chat/completions", { method: "POST", body: JSON.stringify(body) }); const b: any = r.body; const content = b?.choices?.[0]?.message?.content; let parsed: any = null; let valid = false; try { parsed = JSON.parse(content); valid = typeof parsed.city === "string" && Array.isArray(parsed.landmarks) && typeof parsed.population_millions === "number"; } catch {} results[model] = { status: r.status, provider: b?.provider, finish: b?.choices?.[0]?.finish_reason, content, valid, usage: b?.usage, error: b?.error }; console.log(`[${model}] ${r.status} provider=${b?.provider} valid=${valid} ${short(content, 160)} ${b?.error ? "ERR=" + short(b.error, 300) : ""}`); } // streaming structured output const s = await rawSSE("/chat/completions", { model: OPENAI, messages: [{ role: "user", content: "Give facts about Montreal." }], response_format, max_tokens: 200, stream: true, reasoning: { effort: "low" }, }); let text = ""; for (const e of s.events) if (e.data !== "[DONE]") text += e.data?.choices?.[0]?.delta?.content ?? ""; let streamValid = false; try { streamValid = typeof JSON.parse(text).city === "string"; } catch {} results.streamed = { model: OPENAI, status: s.status, events: s.events.length, text, valid: streamValid }; console.log(`[stream ${OPENAI}] events=${s.events.length} valid=${streamValid} ${short(text, 160)}`); save("04-structured.json", results);