Structured outputs

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

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:

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:

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:

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:

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

SurfaceField
/v1/chat/completionsresponse_format (json_schema, json_object)
/v1/responsestext.format
/v1/messagesNot supported — Anthropic's Messages API has no equivalent field
Batch rowsSame body shape as the surface the row targets