Sometimes we want structured data. responseFormat is the option: tell the model the structure 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);
}
}The last step is reading the schema-valid output back. 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.
Question 1 of 2
What's the difference between responseFormat: { type: 'json_object' } and type: 'json_schema'?
Question 2 of 2
Does the TypeScript type on parsed come from PERSON_SCHEMA automatically?
Run your code, check your answer, or ask a question. It all shows up here.