# Structured outputs

> Constrain a model's response to a JSON schema so you get parseable, predictable JSON on sference.

Source: https://sference.com/docs/guides/structured-outputs

Most production callers do not want prose — they want a JSON object they can parse. sference supports the OpenAI structured-output fields so you can constrain the response to a schema instead of hoping the model formats it correctly and repairing failures with regex.

## JSON schema

Pass `response_format` with `type: "json_schema"` on `/v1/chat/completions`, or `text.format` on `/v1/responses`:

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.sference.com/v1",
    api_key=os.environ["SFERENCE_API_KEY"],
)

response = client.chat.completions.create(
    model="zai-org/GLM-5.3",
    messages=[
        {"role": "system", "content": "Extract the fields from the support ticket."},
        {"role": "user", "content": ticket_text},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "ticket",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "severity": {"type": "string", "enum": ["low", "medium", "high"]},
                    "product": {"type": "string"},
                    "summary": {"type": "string"},
                },
                "required": ["severity", "product", "summary"],
                "additionalProperties": False,
            },
        },
    },
)

ticket = json.loads(response.choices[0].message.content)
```

On the Responses API the same schema goes in `text.format`:

```python
response = client.responses.create(
    model="zai-org/GLM-5.3",
    input=ticket_text,
    text={
        "format": {
            "type": "json_schema",
            "name": "ticket",
            "schema": {...},
            "strict": True,
        }
    },
)
```

## JSON mode

If you only need *valid JSON* and not a specific shape, `{"type": "json_object"}` is cheaper and less constrained. Remember to describe the shape in the prompt — JSON mode guarantees syntax, not structure:

```python
response = client.chat.completions.create(
    model="zai-org/GLM-5.3",
    messages=[
        {"role": "system", "content": "Reply with JSON: {\"sentiment\": \"positive\"|\"negative\", \"confidence\": 0-1}"},
        {"role": "user", "content": review},
    ],
    response_format={"type": "json_object"},
)
```

## Schema guidance

Write schemas that are easy to satisfy, not just correct:

- **Use `enum` for closed sets.** `{"type": "string", "enum": ["low", "medium", "high"]}` is far more reliable than asking for "a severity level".
- **Set `additionalProperties: false` and list every field in `required`.** Optional fields invite the model to omit them inconsistently.
- **Prefer flat objects.** Deeply nested schemas fail more often and are harder to debug.
- **Avoid `oneOf`/`anyOf` where a plain optional field would do.** Unions are the most common source of malformed output.
- **Keep field names and descriptions explicit.** The description is part of the prompt.

> **Reasoning models and constrained output**
>
> On reasoning models, `max_output_tokens` covers hidden reasoning as well as the answer. If the cap is too low the response can settle `incomplete` with a partially-formed object. Collect on the finish reason rather than assuming a parseable body, and give reasoning models headroom above the size of the object you expect.

## Failure handling

Even with a schema, treat the response as untrusted input:

```python
try:
    data = json.loads(response.choices[0].message.content)
except json.JSONDecodeError:
    # Retry once, or fall back to a repair pass
    ...
```

A constrained decode makes malformed JSON rare, not impossible — truncation from a token cap, an upstream error mid-stream, and model-specific quirks all still produce unparseable output occasionally. Validate against your schema before use, and retry on failure rather than writing the bad value.

## Where it is supported

| Surface | Field |
| --- | --- |
| `/v1/chat/completions` | `response_format` (`json_schema`, `json_object`) |
| `/v1/responses` | `text.format` |
| `/v1/messages` | Not supported — Anthropic's Messages API has no equivalent field |
| Batch rows | Same body shape as the surface the row targets |

- [Agent SDKs](https://sference.com/docs/agents): Structured output as the return type of an agent tool.
- [Vision](https://sference.com/docs/guides/vision): Schemas over images: extract fields from screenshots and documents.
- [API reference](https://sference.com/docs/api-reference): Full field list for response_format and text.format.
