Skip to main content

rig_agent/agent/run/
output_mode.rs

1//! How an agent enforces its `output_schema` (see issue #1928).
2//!
3//! Mirrors pydantic-ai's output modes. When an agent has both tools and an
4//! `output_schema`, applying the provider's native structured-output constraint
5//! (`format`/`response_format`) on every turn suppresses tool calls — the model
6//! is forced to emit schema JSON instead of using its tools. `OutputMode` lets
7//! the structured output be produced as a *tool call* the model makes after
8//! using its tools, which composes correctly.
9
10use serde::{Deserialize, Serialize};
11
12/// Controls how an agent's `output_schema` is enforced.
13///
14/// # Strictness
15///
16/// [`Native`](OutputMode::Native) is the only mode whose output is *constrained*
17/// by the provider — the response is guaranteed to match the schema (on
18/// providers that support it). [`Tool`](OutputMode::Tool) and
19/// [`Prompted`](OutputMode::Prompted) are **best-effort**: the schema is offered
20/// to the model (as a tool or in the prompt) but the model is asked, not forced,
21/// to honor it, so the agent re-prompts a bounded number of times and otherwise
22/// validate the returned JSON before relying on it. The default
23/// [`Auto`](OutputMode::Auto) is provider-aware: for a tool + schema agent it
24/// routes to `Tool` only on providers whose native constraint would suppress
25/// tool calls, and keeps guaranteed `Native` structured output on providers that
26/// compose the two (e.g. OpenAI, Anthropic).
27#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
28pub enum OutputMode {
29    /// Resolve at request time: [`OutputMode::Tool`] when the agent has an
30    /// `output_schema` **and** at least one function tool (and the tool choice
31    /// permits the output-tool call), otherwise [`OutputMode::Native`]. This is
32    /// the default and only changes behavior for the tool + schema case (which is
33    /// broken under `Native` on providers whose native constraint suppresses
34    /// tool calls).
35    #[default]
36    Auto,
37    /// Register the schema as a synthetic "output tool" the model calls to
38    /// finalize. No native structured-output constraint is sent, so the model can
39    /// freely call its other tools first. Best-effort: the model is instructed to
40    /// call the output tool but is not forced to, so validate the result.
41    /// (pydantic-ai `ToolOutput`.)
42    Tool,
43    /// Use the provider's native structured output (`format`/`response_format`).
44    /// Constrains every turn, so the output is guaranteed to match the schema,
45    /// but may suppress tool calls on some providers (e.g. Ollama). (pydantic-ai
46    /// `NativeOutput`.)
47    Native,
48    /// Inject the schema into the system prompt and return the model's final text
49    /// verbatim. The caller parses it — the text is *not* guaranteed to be clean
50    /// JSON and may include prose or markdown fences, so extract/validate before
51    /// deserializing. Useful for weak/local models that lack reliable tool calling
52    /// or native structured output. (pydantic-ai `PromptedOutput`.)
53    Prompted,
54}