Nous.OutputSchema (nous v0.13.3)
View SourceStructured output support for Nous agents.
Converts Ecto schemas and other type specifications into JSON Schema, generates provider-specific settings, and validates LLM responses against the declared output type.
Inspired by instructor_ex.
Output Type Variants
:string— raw text (default, no processing)module()— Ecto schema → JSON schema + changeset validation%{atom() => atom()}— schemaless Ecto types (e.g.%{name: :string})%{String.t() => map()}— raw JSON schema (string keys, passed through){:regex, pattern}— regex-constrained output (vLLM/SGLang){:grammar, ebnf}— grammar-constrained output (vLLM){:choice, choices}— choice-constrained output (vLLM/SGLang)
Modes
| Mode | Mechanism | Providers |
|---|---|---|
:auto | Pick best for provider | All |
:tool_call | Synthetic tool + tool_choice | All (native for Anthropic) |
:json_schema | response_format: {type: "json_schema", ...} | OpenAI, vLLM, SGLang |
:json | response_format: {type: "json_object"} | OpenAI-compatible |
:md_json | Prompt + markdown fence + stop token | All (fallback) |
Summary
Functions
Cast parsed JSON data and validate it against the output type.
Extract the response text and matched schema from a {:one_of, schemas} message.
Extract the response text from a provider response message.
Find the schema module whose synthetic tool name matches tool_name.
Format a validation error into a human-readable string for LLM retry.
Parse raw LLM response text and validate it against the output type.
Return a snake_case name for the given output type.
Return true if name is a synthetic structured output tool name.
Generate system prompt suffix describing the expected output schema.
Convert an output type specification to a JSON Schema map.
Generate provider-specific model settings for structured output.
Build the synthetic tool name for a given schema module.
Functions
@spec cast_and_validate(map(), Nous.Types.output_type()) :: {:ok, any()} | {:error, Nous.Errors.ValidationError.t()}
Cast parsed JSON data and validate it against the output type.
For Ecto schemas, uses Ecto.Changeset.cast/3 + validate_changeset/1.
For schemaless types, uses Ecto.Changeset.cast/4 with {data, types}.
For raw JSON schema maps, returns the parsed data as-is.
@spec extract_response_for_one_of(Nous.Message.t(), [module()]) :: {String.t(), module() | nil}
Extract the response text and matched schema from a {:one_of, schemas} message.
If the message contains a synthetic tool call matching one of the schemas,
returns {json_text, schema_module}. Otherwise returns {text, nil}.
@spec extract_response_text(Nous.Message.t(), atom()) :: String.t()
Extract the response text from a provider response message.
For :tool_call mode on Anthropic, extracts the __structured_output__
tool call arguments. For all other modes, extracts the text content.
Find the schema module whose synthetic tool name matches tool_name.
Returns nil if no schema matches.
@spec format_errors(Nous.Errors.ValidationError.t()) :: String.t()
Format a validation error into a human-readable string for LLM retry.
@spec parse_and_validate(String.t(), Nous.Types.output_type()) :: {:ok, any()} | {:error, Nous.Errors.ValidationError.t()}
Parse raw LLM response text and validate it against the output type.
Returns {:ok, result} where result is a struct, map, or string,
or {:error, %ValidationError{}} on failure.
@spec schema_name(Nous.Types.output_type()) :: String.t()
Return a snake_case name for the given output type.
For Ecto schema modules, returns the last segment of the module name
underscored (e.g. MyApp.SentimentResult → "sentiment_result").
For maps or other types, returns "output".
Return true if name is a synthetic structured output tool name.
Matches both the standard "__structured_output__" and per-schema
names like "__structured_output_sentiment_result__".
@spec system_prompt_suffix( Nous.Types.output_type(), keyword() ) :: String.t() | nil
Generate system prompt suffix describing the expected output schema.
@spec to_json_schema(Nous.Types.output_type()) :: map() | nil
Convert an output type specification to a JSON Schema map.
Returns nil for types that don't produce JSON schema (:string, tuples).
@spec to_provider_settings(Nous.Types.output_type(), atom(), keyword()) :: map()
Generate provider-specific model settings for structured output.
Returns a map of settings to merge into the model request. The map may
contain special keys prefixed with __structured_output that the
AgentRunner handles separately (e.g. synthetic tool injection).
Options
:mode— output mode (:auto,:tool_call,:json_schema,:json,:md_json):has_other_tools— whether the agent has non-synthetic tools
Build the synthetic tool name for a given schema module.
Example
iex> OutputSchema.tool_name_for_schema(SentimentResult)
"__structured_output_sentiment_result__"