Structured output

response_format constrains the model to emit valid JSON, optionally schema-conformant. Eliminates parse-retry loops in your code.

Two modes:

Both work on epithre-omni and epithre-prme. Our grammar enforcement layer is fast and reliable across the typical prompt-length range; for most workloads it just works.

JSON mode (any valid JSON)

resp = client.chat.completions.create(
    model="epithre-omni",
    messages=[
        {"role": "system", "content": "Reply with valid JSON only."},
        {"role": "user", "content": "Ekstrak: 'Dimas, umur 29, dari Jakarta'"},
    ],
    response_format={"type": "json_object"},
)
import json
data = json.loads(resp.choices[0].message.content)
# {"name": "Dimas", "age": 29, "city": "Jakarta"}

The model picks the keys. Useful when shape is loosely defined or you want exploration. For production, use json_schema.

Strict JSON schema

Provide a JSON Schema; output is guaranteed to validate.

resp = client.chat.completions.create(
    model="epithre-omni",
    messages=[
        {"role": "user", "content": "Ekstrak: 'Sari, 34 tahun, dokter, Surabaya'"},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "person_extract",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name":       {"type": "string"},
                    "age":        {"type": "integer", "minimum": 0, "maximum": 150},
                    "profession": {"type": "string"},
                    "city":       {"type": "string"}
                },
                "required": ["name", "age", "profession", "city"],
                "additionalProperties": False,
            },
        },
    },
)
import json
data = json.loads(resp.choices[0].message.content)
# {"name": "Sari", "age": 34, "profession": "dokter", "city": "Surabaya"}

Schema features supported:

Enum classification example

Particularly clean pattern for sentiment / category labels:

resp = client.chat.completions.create(
    model="epithre-lyt",
    messages=[
        {"role": "system", "content": "Classify sentiment of Indonesian product reviews."},
        {"role": "user", "content": "Barang nyampe tapi salah warna. Komplain blm direspon."},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "sentiment",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "sentiment": {"type": "string", "enum": ["positif", "netral", "negatif"]},
                    "confidence": {"type": "number", "minimum": 0, "maximum": 1},
                },
                "required": ["sentiment", "confidence"],
                "additionalProperties": False,
            },
        },
    },
)
# {"sentiment": "negatif", "confidence": 0.92}

Using epithre-lyt for this kind of high-volume classification keeps cost minimal.

Nested structure

schema = {
    "type": "object",
    "properties": {
        "invoice_number": {"type": "string"},
        "vendor": {
            "type": "object",
            "properties": {
                "name":  {"type": "string"},
                "npwp":  {"type": "string", "pattern": r"^\d{2}\.\d{3}\.\d{3}\.\d-\d{3}\.\d{3}$"},
            },
            "required": ["name"],
            "additionalProperties": False,
        },
        "items": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "description": {"type": "string"},
                    "quantity":    {"type": "integer", "minimum": 1},
                    "unit_price":  {"type": "number"},
                    "subtotal":    {"type": "number"},
                },
                "required": ["description", "quantity", "unit_price", "subtotal"],
                "additionalProperties": False,
            },
        },
        "total":  {"type": "number"},
        "ppn":    {"type": "number"},
        "grand_total": {"type": "number"},
    },
    "required": ["invoice_number", "vendor", "items", "total", "grand_total"],
    "additionalProperties": False,
}

Caveats and stability notes

What works well

What works less well