Sometimes we want structured data. responseFormat is the option: tell the model the shape we need, and the engine enforces it through grammar.
The option takes one of three values, each giving a different strength of guarantee over the output:
text (default). Free-form text. No constraints.json_object. The output is some valid JSON object, but the keys aren't pinned. Small models tend to emit {}.json_schema. The output matches a JSON Schema we provide. The grammar engine forces the keys, the types, and the required fields.Telling TypeScript the schema is read-only lets inference run end-to-end. The schema constant declared with as const looks like this:
const PERSON_SCHEMA = {
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer" },
occupation: { type: "string" },
},
required: ["name", "age", "occupation"],
additionalProperties: false,
} as const;The responseFormat option hands the schema to the grammar engine. The engine constrains the keys, the types, and the required fields, so the streamed output is always valid JSON. The correct completion call would look like:
const result = completion({
modelId,
history: [
{ role: "system", content: "Extract structured info about people." },
{ role: "user", content: "Hi, I'm Alice, 30, data engineer." },
],
captureThinking: true,
responseFormat: {
type: "json_schema",
json_schema: { name: "person", schema: PERSON_SCHEMA },
},
stream: true,
});
let raw = "";
for await (const event of result.events) {
if (event.type === "contentDelta") {
raw += event.text;
process.stdout.write(event.text);
}
}Reading the schema-valid output back is the last step of the pipeline. Note that the JSON.parse step requires the loop to finish first. Use it as follows:
const parsed = JSON.parse(raw.trim()) as {
name: string;
age: number;
occupation: string;
};
console.log("\n▸ Parsed:", parsed);The result is already valid JSON. No regex, no repair, no fallback parsing.
Note:
as conston the schema tells TypeScript the value is read-only. The runtime API does not care, but the type system is happier this way.
PERSON_SCHEMA (or similar) as a const JSON Schema object with type: "object", properties, and required.responseFormat: { type: "json_schema", json_schema: { name: "person", schema: PERSON_SCHEMA } } to completion(), stream contentDelta events.await result.final and JSON.parse(final.contentText). Log the parsed object.$ Run your code to see results
$