Structured Outputs

This document explains how to use the structured outputs feature with the OpenAI Responses Elixir library.

Introduction

Structured Outputs is a feature that ensures the model will always generate responses that adhere to your supplied JSON Schema, so you don't need to worry about the model omitting a required key, or hallucinating an invalid enum value.

The OpenAI Responses Elixir library provides a simple, idiomatic way to define schemas and parse responses with structured outputs. This library supports the latest OpenAI API format for structured outputs.

Basic Usage

alias OpenAI.Responses
alias OpenAI.Responses.Schema

# Define a schema
calendar_event_schema = Schema.object(%{
  name: :string,
  date: :string,
  participants: {:array, :string}
})

# Create a response with structured output
{:ok, result} = Responses.parse(
  calendar_event_schema,
  model: "gpt-4.1",
  input: "Alice and Bob are going to a science fair on Friday.",
  schema_name: "event"
)

# Access the parsed data
IO.puts("Event: #{result.parsed["name"]} on #{result.parsed["date"]}")
IO.puts("Participants: #{Enum.join(result.parsed["participants"], ", ")}")
IO.inspect(result.token_usage, label: "Token Usage")

Defining Schemas

Simple Types

For simple types without constraints, you can use atoms directly:

schema = Schema.object(%{
  name: :string,
  age: :integer,
  is_active: :boolean,
  scores: {:array, :number}
})

Types with Constraints

For types with constraints or additional options, use the corresponding functions:

schema = Schema.object(%{
  username: Schema.string(min_length: 3, max_length: 20),
  rating: Schema.number(minimum: 0, maximum: 5),
  tags: Schema.array(:string, min_items: 1, max_items: 5)
})

Nested Objects

You can define nested objects:

schema = Schema.object(%{
  user: Schema.object(%{
    name: :string,
    email: Schema.string(format: "email")
  }),
  preferences: Schema.object(%{
    theme: :string,
    notifications: :boolean
  })
})

Arrays

Arrays can be defined in two ways:

# Simple array of strings
tags_schema = {:array, :string}

# Array with constraints
tags_schema = Schema.array(:string, min_items: 1, max_items: 5)

# Array of objects
steps_schema = Schema.array(
  Schema.object(%{
    explanation: :string,
    output: :string
  })
)

Nullable Fields

You can make fields nullable:

schema = Schema.object(%{
  middle_name: Schema.nullable(:string),
  address: Schema.nullable(
    Schema.object(%{
      street: :string,
      city: :string,
      zip: :string
    })
  )
})

Parsing Responses

Regular Parsing

Use the parse/2 function with options to get a structured response:

{:ok, result} = Responses.parse(
  my_schema,
  model: "gpt-4.1",
  input: "Extract information from this text..."
)
# Access parsed data with result.parsed

Examples

See the examples/structured_output_example.exs file for complete examples of using structured outputs.

Supported Models

Structured Outputs is available in OpenAI's latest large language models, starting with gpt-4.1:

  • gpt-4.5-preview-2025-02-27 and later
  • o3-mini-2025-1-31 and later
  • o1-2024-12-17 and later
  • gpt-4.1-mini-2024-07-18 and later
  • gpt-4.1-2024-08-06 and later

Older models like gpt-4-turbo and earlier may use JSON mode instead.

API Implementation Details

This library implements the latest OpenAI API format for structured outputs, which uses the text parameter with a format specification instead of the older response_format parameter. The implementation handles both the direct API response format and streaming responses.

Key features of the implementation:

  • Automatic handling of the new API format with the text parameter
  • Support for schema validation with additionalProperties: false by default
  • Proper extraction of structured data from both regular and streaming responses
  • Comprehensive error handling for JSON parsing and schema validation