Skip to content

Structured Output

Structured output ensures model responses conform to a specific format, making them easier to parse and integrate into applications. Iris provides two modes: JSON mode for freeform valid JSON, and JSON Schema mode for strict schema enforcement.

  • Reliable parsing: Guaranteed valid JSON eliminates parsing errors
  • Type safety: Schema enforcement ensures required fields are present
  • Integration: Direct mapping to Go structs without manual extraction
  • Validation: Schema constraints prevent malformed responses

JSON mode instructs the model to output valid JSON without enforcing a specific structure. Use this when you need valid JSON but want flexibility in the response format.

package main
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/petal-labs/iris/core"
"github.com/petal-labs/iris/providers/openai"
)
func main() {
provider := openai.New(os.Getenv("OPENAI_API_KEY"))
client := core.NewClient(provider)
ctx := context.Background()
resp, err := client.Chat("gpt-4o-mini").
System("You are a helpful assistant that responds in JSON format.").
User("List 3 programming languages with their name, year created, and creator.").
ResponseJSON().
GetResponse(ctx)
if err != nil {
panic(err)
}
fmt.Println("Raw JSON:")
fmt.Println(resp.Output)
// Parse the response
var result struct {
Languages []struct {
Name string `json:"name"`
Year int `json:"year"`
Creator string `json:"creator"`
} `json:"languages"`
}
if err := json.Unmarshal([]byte(resp.Output), &result); err != nil {
panic(err)
}
fmt.Println("\nParsed:")
for _, lang := range result.Languages {
fmt.Printf(" %s (%d) by %s\n", lang.Name, lang.Year, lang.Creator)
}
}

Output:

Raw JSON:
{"languages": [{"name": "Go", "year": 2009, "creator": "Robert Griesemer, Rob Pike, Ken Thompson"}, ...]}
Parsed:
Go (2009) by Robert Griesemer, Rob Pike, Ken Thompson
Python (1991) by Guido van Rossum
Rust (2010) by Graydon Hoare

JSON Schema mode enforces a strict schema on the model output. The model is guaranteed to produce JSON that validates against your schema, including required fields and type constraints.

package main
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/petal-labs/iris/core"
"github.com/petal-labs/iris/providers/openai"
)
// Define your Go types
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Occupation string `json:"occupation"`
Hobbies []string `json:"hobbies"`
}
func main() {
provider := openai.New(os.Getenv("OPENAI_API_KEY"))
client := core.NewClient(provider)
ctx := context.Background()
// Define the JSON Schema
schema := &core.JSONSchemaDefinition{
Name: "person_info",
Description: "Information about a person",
Strict: true,
Schema: json.RawMessage(`{
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The person's full name"
},
"age": {
"type": "integer",
"description": "The person's age in years"
},
"occupation": {
"type": "string",
"description": "The person's job or profession"
},
"hobbies": {
"type": "array",
"items": {"type": "string"},
"description": "List of hobbies"
}
},
"required": ["name", "age", "occupation", "hobbies"],
"additionalProperties": false
}`),
}
resp, err := client.Chat("gpt-4o-mini").
User("Extract information: Sarah is a 28-year-old software engineer who enjoys hiking, reading, and playing chess.").
ResponseJSONSchema(schema).
GetResponse(ctx)
if err != nil {
panic(err)
}
// Parse directly into Go struct
var person Person
if err := json.Unmarshal([]byte(resp.Output), &person); err != nil {
panic(err)
}
fmt.Printf("Name: %s\n", person.Name)
fmt.Printf("Age: %d\n", person.Age)
fmt.Printf("Occupation: %s\n", person.Occupation)
fmt.Printf("Hobbies: %v\n", person.Hobbies)
}

Output:

Name: Sarah
Age: 28
Occupation: software engineer
Hobbies: [hiking reading playing chess]

When using strict mode (Strict: true), your schema must follow these rules:

  1. All properties must be required: List every property in the required array
  2. No additional properties: Set additionalProperties: false
  3. Supported types: string, number, integer, boolean, array, object, null
  4. No unsupported keywords: Avoid oneOf, anyOf, not, patternProperties
schema := &core.JSONSchemaDefinition{
Name: "order",
Strict: true,
Schema: json.RawMessage(`{
"type": "object",
"properties": {
"order_id": {"type": "string"},
"customer": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"}
},
"required": ["name", "email"],
"additionalProperties": false
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"product": {"type": "string"},
"quantity": {"type": "integer"},
"price": {"type": "number"}
},
"required": ["product", "quantity", "price"],
"additionalProperties": false
}
},
"total": {"type": "number"}
},
"required": ["order_id", "customer", "items", "total"],
"additionalProperties": false
}`),
}
schema := &core.JSONSchemaDefinition{
Name: "ticket",
Strict: true,
Schema: json.RawMessage(`{
"type": "object",
"properties": {
"title": {"type": "string"},
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "critical"]
},
"status": {
"type": "string",
"enum": ["open", "in_progress", "resolved", "closed"]
},
"tags": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["title", "priority", "status", "tags"],
"additionalProperties": false
}`),
}
ProviderJSON ModeJSON Schema Mode
OpenAI
Anthropic-
Gemini
xAI-
Ollama-

Design schemas for specific extraction tasks rather than general-purpose structures.

Clear field names help the model understand what to extract.

Include descriptions for complex or ambiguous fields.

Even with schema enforcement, validate business logic after parsing.

Tools Guide

Combine structured output with tool calling. Tools →

API Reference

Full API documentation. API →