Structured Output
Force models to return structured data that strictly conforms to a JSON Schema, eliminating the need to parse strings or use regex extraction. Ideal for data extraction, form filling, and workflow intermediate artifacts.
When to Use
- You need directly deserializable JSON (no tolerance for
json.loadsfailures) - Downstream systems have a strict schema (database fields, API payloads)
- Data extraction / entity recognition / classification
- Decision output in an Agent (model must output a specific action object)
Difference from Function Calling:
| Structured Output | Function Calling | |
|---|---|---|
| Purpose | Make the final response a JSON object | Have the model call your functions |
| Rounds | Single request | At least two turns (call → return result) |
| Typical use | Data extraction, field filling | Agents, external system integration |
Three Implementation Approaches
Different providers take different implementation paths. Turing transparently proxies all of them via the Chat Completions / Messages protocols.
Approach A: response_format JSON Mode (earliest OpenAI version)
Guarantees the output is valid JSON, but does not enforce a specific schema. The model may return a structure that does not match expectations.
from openai import OpenAI
client = OpenAI(
api_key="your-api-key",
base_url="https://live-turing.cn.llm.tcljd.com/api/v1",
)
response = client.chat.completions.create(
model="turing/gpt-5.4-mini",
messages=[
{"role": "system", "content": "You are a JSON output assistant. Return strict JSON."},
{"role": "user", "content": "Extract: John Smith, 28 years old, engineer"},
],
response_format={"type": "json_object"},
)
data = json.loads(response.choices[0].message.content)
When using json_object, you must mention "JSON" in the prompt; otherwise OpenAI will reject the request.
Approach B: response_format with JSON Schema (Recommended)
Forces output to strictly conform to the schema. Supported by OpenAI gpt-4o / gpt-5.x.
response = client.chat.completions.create(
model="turing/gpt-5.4-mini",
messages=[
{"role": "user", "content": "Extract: John Smith, 28 years old, engineer"},
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "person_info",
"strict": True,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"}
},
"required": ["name", "age", "occupation"],
"additionalProperties": False
}
}
},
)
# Parsing is guaranteed to succeed with all fields present
data = json.loads(response.choices[0].message.content)
curl $TURING_BASE_URL/chat/completions \
-H "Authorization: Bearer $TURING_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "turing/gpt-5.4-mini",
"messages": [{"role": "user", "content": "Extract: John Smith, 28 years old, engineer"}],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "person_info",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"}
},
"required": ["name", "age", "occupation"],
"additionalProperties": false
}
}
}
}'
Approach C: Gemini Native responseSchema
The Gemini family uses two fields: responseMimeType and responseSchema.
response = client.chat.completions.create(
model="turing/gemini-3.1-pro-latest",
messages=[{"role": "user", "content": "Extract: John Smith, 28 years old, engineer"}],
extra_body={
"responseMimeType": "application/json",
"responseSchema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"}
},
"required": ["name", "age", "occupation"]
}
}
)
Claude: Simulated via Function Calling
Claude does not natively support response_format. The most reliable approach is to use function calling as a single-turn structured output: define a tool and force the model to call it.
from anthropic import Anthropic
client = Anthropic(
base_url="https://live-turing.cn.llm.tcljd.com/api/v1",
auth_token="your-api-key",
)
response = client.messages.create(
model="turing/claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Extract: John Smith, 28 years old, engineer"}],
tools=[{
"name": "emit_person_info",
"description": "Return the extracted person information",
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"}
},
"required": ["name", "age", "occupation"]
}
}],
tool_choice={"type": "tool", "name": "emit_person_info"},
)
# Retrieve structured data from the tool_use block
data = next(b.input for b in response.content if b.type == "tool_use")
Provider Comparison
| Provider | JSON Mode | JSON Schema | Native Fields | Model Selection |
|---|---|---|---|---|
| OpenAI | ✅ response_format: {"type":"json_object"} | ✅ response_format: {"type":"json_schema","json_schema":{…,"strict":true}} | — | See Model List → OpenAI; prefer models that support JSON Schema / strict |
| Anthropic | ❌ Simulate via function calling | ❌ Use function calling + tool_choice: {"type":"tool",…} | — | See Model List → Claude; prefer larger models with stable function calling |
| Gemini | ✅ responseMimeType: "application/json" | ✅ responseSchema + responseMimeType | responseMimeType / responseSchema | See Model List → Gemini; prefer models that support structured output |
| Qwen | ✅ response_format: {"type":"json_object"} | ⚠ Model-dependent | — | See Model List → Alibaba; prefer primary models with better JSON stability |
| DeepSeek | ✅ | ⚠ Model-dependent | — | See Model List → DeepSeek |
Parameter Reference
Full schema documentation:
- OpenAI / cross-provider:
response_formatin/api/create-chat-completion - Gemini native: see Approach C above
- Claude tool method:
tools+tool_choicein/api/create-message
Billing Impact
- The model must generate the entire JSON structure, so
completion_tokensincludes all field names and punctuation - The schema definition (
parameters/json_schema) itself also consumes input tokens - OpenAI
strict: trueincurs a small amount of extra latency on the first call to compile the schema (subsequently cached on the model server)
FAQ
strict: truereports unsupported schema → OpenAI strict mode requiresadditionalProperties: falseand does not supportoneOf/anyOfat the root level; refer to the constraints listed in the OpenAI Structured Outputs Docs- Gemini returns JSON but field order is inconsistent → This is expected; JSON objects are inherently unordered. Access fields by key.
- Claude calls the tool but parameter fields are empty → Add more
requiredfields and richerdescriptionentries toinput_schema - Smaller models produce invalid JSON → Upgrade to a larger model, or combine
json_objectwith secondary regex validation
See also
- Function Calling — Use this when you need the model to call your functions
- Vertex AI GenerationConfig — Official parameter reference for Gemini
responseSchema - Chat Completions — Base protocol