Structured Output
Structured output lets agents return validated JSON instead of free-form text. Define a JSON Schema in spec.output and the agent's response is parsed, validated, and returned as JSON that matches your schema.
This is useful for pipelines, automation, and any case where downstream code needs to consume agent output programmatically.
Quick Example
apiVersion: initrunner/v1
kind: Agent
metadata:
name: invoice-classifier
description: Classifies invoices and extracts structured data
spec:
role: |
You are an invoice classifier. Given a description of an invoice,
extract the relevant fields and return structured JSON.
model:
provider: openai
name: gpt-5-mini
temperature: 0.0
output:
type: json_schema
schema:
type: object
properties:
status:
type: string
enum: [approved, rejected, needs_review]
amount:
type: number
description: Invoice amount in USD
vendor:
type: string
required: [status, amount, vendor]initrunner run invoice-classifier.yaml -p "Acme Corp invoice for $250 for office supplies"
# → {"status": "approved", "amount": 250.0, "vendor": "Acme Corp"}Configuration
Structured output is configured in the spec.output section:
spec:
output:
type: json_schema # "text" (default) or "json_schema"
mode: auto # how structured output is requested (see Output Modes below)
schema: { ... } # inline JSON Schema (mutually exclusive with schema_file)
schema_file: schema.json # path to external JSON Schema file| Field | Type | Default | Description |
|---|---|---|---|
type | str | "text" | Output type. "text" for free-form text, "json_schema" for validated JSON. |
mode | str | "auto" | Strategy used to obtain structured output: auto, tool, native, prompted, or text. See Output Modes. |
schema | dict | null | Inline JSON Schema definition. Required when type is json_schema (unless schema_file is set). |
schema_file | str | null | Path to an external JSON Schema file. Relative paths are resolved from the role file's directory. |
When type is json_schema, exactly one of schema or schema_file must be provided.
Output Modes
The mode field controls how InitRunner asks the model for structured output. Each mode maps onto one of PydanticAI's output markers. With mode: auto (the default) InitRunner passes the resolved model to PydanticAI without a marker, so PydanticAI selects the strategy from the model's structured-output profile. The explicit modes pin a single strategy regardless of which model runs the role.
| Mode | Behavior |
|---|---|
auto | Default. Defers to PydanticAI, which picks the strategy from the model's profile (usually a tool call). The output type is passed without a marker wrapper, so behavior matches earlier InitRunner releases. |
tool | Forces a tool call (PydanticAI's ToolOutput). The most widely compatible option, and it keeps the same strategy when you switch models. |
native | Uses the provider's native structured-output API (NativeOutput), such as OpenAI Structured Outputs. Faster and cheaper on models that support it. |
prompted | Describes the schema in the prompt and asks the model to reply with matching JSON (PromptedOutput). A fallback for providers without native or tool support. |
text | Plain unstructured text. Only valid with type: text. There is no structured wrapper; the output is returned as a string. |
Native support varies by provider, so check Providers before pinning mode: native. Whether a model defaults to a tool call or native output under auto is also a provider and model detail.
Validation Rules
The mode and type must agree, or loading the role fails:
tool,native, andpromptedrequiretype: json_schema.mode: textrequirestype: text.- With
type: text, onlyautoortextare allowed.
Pinning a Mode
To request native structured output explicitly:
spec:
output:
type: json_schema
mode: native
schema:
type: object
properties:
status:
type: string
enum: [approved, rejected, needs_review]
amount:
type: number
required: [status, amount]For reasoning models, auto is usually the right choice, since PydanticAI matches the strategy to the model. Pin an explicit mode only when you need the same behavior across different models.
Supported Types
| JSON Schema Type | Python Type | Notes |
|---|---|---|
string | str | Plain string |
string + enum | Literal[...] | Constrained to listed values |
number | float | Floating-point number |
integer | int | Integer number |
boolean | bool | True/false |
object | nested BaseModel | Recursive: nested objects become nested models |
array | list[ItemType] | Item type resolved from items schema |
Schema Keywords
propertiesdefines the fields of an objectrequiredlists field names that must be present (non-required fields becomeOptionalwith aNonedefault)descriptionis field-level documentation passed to the modelenumconstrains a string field to specific valuesitemsdefines the element type for arrays
Nested Objects & Arrays
spec:
output:
type: json_schema
schema:
type: object
properties:
title:
type: string
description: Report title
sections:
type: array
items:
type: object
properties:
heading:
type: string
body:
type: string
required: [heading, body]
metadata:
type: object
properties:
author:
type: string
tags:
type: array
items:
type: string
required: [title, sections]External Schema File
For larger schemas, use schema_file to reference a separate JSON file:
spec:
output:
type: json_schema
schema_file: schemas/invoice.jsonThe file must contain a valid JSON Schema object. Relative paths are resolved from the role YAML file's directory. Absolute paths are used as-is.
{
"type": "object",
"properties": {
"status": { "type": "string", "enum": ["approved", "rejected"] },
"amount": { "type": "number" }
},
"required": ["status", "amount"]
}Streaming Partials
Since v2026.4.17, structured-output roles stream progressively-validated partials. The previous hard restriction on output.type != "text" is gone, so both the sync and async streaming paths accept structured output.
- Sync.
StreamedRunResultSync.stream_output()yields partials as JSON fragments validate against the schema. Anon_partialcallback receives each validated partial. - Async. The async path accepts both
on_partialandon_event.run_stream_events()yields typedAgentStreamEventinstances you can branch on. - API and dashboard. The dashboard emits a
partial_outputSSE frame for structured roles instead of falling back to non-streaming. Consumers that previously handled onlytokenframes should add apartial_outputhandler to surface the intermediate shape.
Behavior is unchanged for non-structured (type: text) roles.
See also: Guardrails for enforcing resource limits on structured output agents, and Flow for wiring structured-output roles into multi-agent pipelines.
Reasoning Primitives
Structured agent cognition with think, todo, and spawn tools, native extended thinking, and react, todo_driven, plan_execute, and reflexion execution strategies.
Report Export
Export structured markdown reports from any agent run, capturing prompt, output, token usage, timing, and status.