Skip to content

[Feat]: Support schema-constrained structured output #73

Description

@vvlrff

Is your feature request related to a problem? Please describe.

Summary

Today the chat completions API only supports response_format of {"type": "text"} and {"type": "json_object"}. JSON mode guarantees syntactically valid JSON, but it does not let the caller specify a schema the model must conform to. Every other major provider now supports schema-constrained structured output:

  • OpenAIresponse_format: {"type": "json_schema", "json_schema": {...}, "strict": true}
  • Google Geminiresponse_schema / responseJsonSchema
  • xAI (Grok)response_format: {"type": "json_schema", ...}

This means GLM models cannot be used as a drop-in replacement when an application relies on guaranteed-shape output. I'm integrating Z.AI as a provider in AG2, which exposes a single structured-output interface across providers; without schema support, Z.AI is the odd one out and we fall back to prompt-engineered json_object + client-side validation, which is unreliable (the model can return valid JSON that doesn't match the requested schema).

What I'm asking for

  1. API: Support response_format = {"type": "json_schema", "json_schema": {...}} on chat completions, where json_schema carries a standard JSON Schema and the decoder constrains output to conform to it (ideally a strict flag for guaranteed conformance, as OpenAI does).
  2. SDK (z-ai-sdk-python): Accept and forward this shape with proper typing. Currently response_format is typed as object | None and passed through untyped, so even once the API supports it there's no typed surface or documentation for it. (No opinion on whether a Pydantic-model convenience helper is added — the core need is just being able to pass a JSON Schema and have it enforced.)

Desired usage

from zai import ZaiClient

client = ZaiClient(api_key="your-api-key")

schema = {
    "type": "object",
    "properties": {
        "sentiment":  {"type": "string", "enum": ["positive", "neutral", "negative"]},
        "confidence": {"type": "number"},
        "keywords":   {"type": "array", "items": {"type": "string"}},
    },
    "required": ["sentiment", "confidence", "keywords"],
    "additionalProperties": False,
}

response = client.chat.completions.create(
    model="glm-4.6",
    messages=[
        {"role": "user", "content": "Analyze the sentiment of: 'I love this product!'"},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "sentiment_analysis",
            "schema": schema,
            "strict": True,
        },
    },
)

# Guaranteed to parse AND match `schema`:
import json
data = json.loads(response.choices[0].message.content)

Current behavior

# Only these are supported today:
response_format={"type": "text"}
response_format={"type": "json_object"}   # valid JSON, but shape is not enforced

Passing a json_schema object is silently ignored / not honored by the API, and the SDK's response_format: object | None typing gives no indication of what's accepted.

Why it matters

Schema-constrained output is now the standard way applications get reliable, machine-parseable results from LLMs (tool/agent frameworks, extraction pipelines, multi-provider routers). Supporting it would make GLM models usable as a drop-in replacement in these stacks instead of requiring provider-specific fallbacks.

Thanks for considering this!

Describe the solution you'd like

schema = {
    "type": "object",
    "properties": {
        "sentiment":  {"type": "string", "enum": ["positive", "neutral", "negative"]},
        "confidence": {"type": "number"},
        "keywords":   {"type": "array", "items": {"type": "string"}},
    },
    "required": ["sentiment", "confidence", "keywords"],
    "additionalProperties": False,
}

response = client.chat.completions.create(
    model="glm-5.2",
    messages=[{"role": "user", "content": "Analyze the sentiment of: 'I love this product!'"}],
    response_format={
        "type": "json_schema",
        "json_schema": {"name": "sentiment_analysis", "schema": schema, "strict": True},
    },
)
# response.choices[0].message.content is guaranteed to parse AND match `schema`

Describe alternatives you've considered

No response

Additional context

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Fields

    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions