Structured outputs
Getting JSON back, and how much of a guarantee that is.
response_format is passed to the model as you send it, in both its forms:
{ "response_format": { "type": "json_object" } }{
"response_format": {
"type": "json_schema",
"json_schema": { "name": "invoice", "schema": { "...": "..." }, "strict": true }
}
}This is not a guarantee we make
Eterial does not validate the response against your schema, and does not re-prompt when a model ignores it. Whatever enforcement you get comes from the model that served the request — so the same request can be strictly conformant on one day and merely well-intentioned on another, since the backend serving a model is not fixed. See Routing.
The reliable way to get a typed object
Ask for it as a tool. A function with a parameter schema is understood by every model that supports tools, and the arguments come back as a JSON string built against that schema:
tools = [{
"type": "function",
"function": {
"name": "emit_invoice",
"parameters": {
"type": "object",
"properties": {
"total": {"type": "number"},
"currency": {"type": "string"},
},
"required": ["total", "currency"],
},
},
}]
response = client.chat.completions.create(
model="minimax-m2.7",
messages=[{"role": "user", "content": invoice_text}],
tools=tools,
tool_choice={"type": "function", "function": {"name": "emit_invoice"}},
)
data = json.loads(response.choices[0].message.tool_calls[0].function.arguments)Forcing tool_choice to that function removes the model's option to answer in
prose at all. See Tool calling.
Either way, validate
Parse into a schema you own — Pydantic, Zod, whatever your stack uses — and treat a parse failure as a normal, expected outcome rather than an exception. Retrying once on a failure costs less than a schema violation reaching your database.