Skip to main content

rig_core/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)]
28#[non_exhaustive]
29pub enum OutputMode {
30    /// Resolve at request time: [`OutputMode::Tool`] when the agent has an
31    /// `output_schema` **and** at least one function tool (and the tool choice
32    /// permits the output-tool call), otherwise [`OutputMode::Native`]. This is
33    /// the default and only changes behavior for the tool + schema case (which is
34    /// broken under `Native` on providers whose native constraint suppresses
35    /// tool calls).
36    #[default]
37    Auto,
38    /// Register the schema as a synthetic "output tool" the model calls to
39    /// finalize. No native structured-output constraint is sent, so the model can
40    /// freely call its other tools first. Best-effort: the model is instructed to
41    /// call the output tool but is not forced to, so validate the result.
42    /// (pydantic-ai `ToolOutput`.)
43    Tool,
44    /// Use the provider's native structured output (`format`/`response_format`).
45    /// Constrains every turn, so the output is guaranteed to match the schema,
46    /// but may suppress tool calls on some providers (e.g. Ollama). (pydantic-ai
47    /// `NativeOutput`.)
48    Native,
49    /// Inject the schema into the system prompt and return the model's final text
50    /// verbatim. The caller parses it — the text is *not* guaranteed to be clean
51    /// JSON and may include prose or markdown fences, so extract/validate before
52    /// deserializing. Useful for weak/local models that lack reliable tool calling
53    /// or native structured output. (pydantic-ai `PromptedOutput`.)
54    Prompted,
55}