DSPy.rb can obtain structured responses in two ways. The prompt-based path renders the signature schema and asks the model for JSON. The provider-native path translates the signature into the selected provider’s structured-output request fields.
I ran both paths once across eight models in September and October 2025. The table records those requests, not a standing model recommendation. Provider APIs, prices, model aliases, and latency change. One request per cell also gives us no estimate of variance.
The Two Request Paths
Prompt-based JSON includes schema guidance and output instructions in the messages. It works when an adapter has no native schema path or when structured_outputs: false is selected. DSPy.rb still extracts and parses the returned JSON.
Provider-native structured output sends the converted signature through provider-specific fields. Current adapters use OpenAI-compatible response_format, Gemini generation_config, and Anthropic structured-output parameters. Support depends on the provider and model.
Both paths end at the same runtime boundary: parse the response and convert its fields against the signature.
Recorded Results
All 16 recorded calls returned a result accepted by the benchmark. That means a 100% observed success rate for this small run, not a 100% reliability claim.
| Provider | Model | Prompt-based JSON | Native structured output |
|---|---|---|---|
| OpenAI | gpt-4o | 2.302s / $0.002833 | 1.769s / $0.001658 |
| OpenAI | gpt-4o-mini | 2.944s / $0.000169 | 2.111s / $0.000097 |
| OpenAI | gpt-5 | 16.005s / $0.011895 | 22.921s / $0.015065 |
| OpenAI | gpt-5-mini | 8.303s / $0.001361 | 10.694s / $0.001881 |
| Anthropic | claude-sonnet-4-5 | 3.411s / $0.004581 | 3.401s / $0.005886 |
| Anthropic | claude-opus-4-1 | 4.993s / $0.022380 | 4.796s / $0.025335 |
| gemini-2.5-pro | 10.478s / $0.001623 | 6.787s / $0.001023 | |
| gemini-2.5-flash | 15.704s / $0.000096 | 7.943s / $0.000050 |
Source: examples/json_modes_benchmark.rb. The repository script remains useful for rerunning the comparison, but the historical output file linked by the original article is no longer present on main.
The recorded run favored native output for GPT-4o and Gemini, prompt-based JSON for the two GPT-5 variants, and showed similar latency with different token costs for the Anthropic models. Model behavior, generated token counts, provider implementation, and ordinary request variance can all affect those differences. This run cannot separate them.
Running the Same Program Both Ways
class ActionType < T::Enum
enums do
Create = new('create')
Update = new('update')
Delete = new('delete')
end
end
class TodoAction < T::Struct
const :action_type, ActionType
const :task, String
const :priority, String, default: 'medium'
end
class TodoListManagement < DSPy::Signature
description "Parse a request into todo actions"
input do
const :user_request, String
end
output do
const :actions, T::Array[TodoAction]
const :summary, String
end
end
Enable native structured output when the adapter and model support it:
DSPy.configure do |c|
c.lm = DSPy::LM.new(
'openai/gpt-4o-mini',
api_key: ENV.fetch('OPENAI_API_KEY'),
structured_outputs: true
)
end
Set structured_outputs: false to benchmark the prompt-based JSON path with the same signature and call:
predictor = DSPy::Predict.new(TodoListManagement)
result = predictor.call(
user_request: "Add groceries and schedule Friday's team meeting"
)
Choosing a Path
Start with native structured output when the selected provider and model support the required schema. It removes some format-following work from the prompt. Keep prompt-based JSON for unsupported models, portable experiments, or compact schema and data formats such as BAML and TOON.
Then rerun the benchmark with several repetitions, realistic signatures, current prices, and a task metric. Latency and token cost matter only alongside accepted output quality.