rig-v0.41.0
Added
- (agent) restore dynamic context helper (#2219) (by gold-silver-copper)
- [breaking] split rig-core and rig-agent behind the rig facade (#2197) (by gold-silver-copper) - #2197
- (agent) add response retry hooks (#2182) (by gold-silver-copper)
- (doubleword) add provider with cassette coverage (#2163) (by gold-silver-copper)
- (telemetry) make sensitive span content opt-in (#2151) (by gold-silver-copper)
- (openai) expose complete Responses reasoning metadata (#2112) (by gold-silver-copper)
- (openai) support GPT-5.6 models and reasoning controls (#2106) (by gold-silver-copper)
Fixed
- (ollama) send max_tokens as options.num_predict in native requests (#2185) (by bugprone)
- (openai) omit filename for URL-backed PDFs in Responses API requests (#2166) (by dgrijalva)
- (anthropic) support URL-backed PDF documents in requests (#2215) (by gold-silver-copper)
- (openai) omit empty non-streaming encrypted reasoning (#2209) (by gold-silver-copper)
- (anthropic) support code execution tool results (#2158) (by gold-silver-copper)
- (aws) remove legacy rustls connector (#2152) (by gold-silver-copper)
- (release) avoid contributor mention notifications (#2110) (by gold-silver-copper)
Other
- (openai) cover nullable strict extractor responses (#2218) (by gold-silver-copper)
- (candle) harden local model runtime (#2214) (by gold-silver-copper)
- (core,agent) [breaking] make the WASM support matrix explicit and true (#2213) (by gold-silver-copper)
- (telemetry) single declarative completion-parent contract (#2208) (by gold-silver-copper)
- (derive) [breaking] single resolution authority, coherent required semantics, dependency hygiene (#2207) (by gold-silver-copper)
- (agent) [breaking] remove premature runtime-conformance crate, backfill gaps (#2206) (by gold-silver-copper)
- (client) [breaking] single canonical CompletionClient + AgentClientExt (#2205) (by gold-silver-copper)
- Make managed agent hooks provider-independent (#2176) (by gold-silver-copper) - #2176
- Remove built-in agent dynamic context (#2174) (by gold-silver-copper) - #2174
- Make AgentRunner the only Agent execution path (#2161) (by gold-silver-copper) - #2161
- Add rig-candle local inference and WASM chat (#2155) (by gold-silver-copper) - #2155
- remove AI assistance policy (#2159) (by gold-silver-copper) - #2159
- Simplify tool execution and hook APIs (#2132) (by gold-silver-copper) - #2132
- (telemetry) centralize completion span lifecycle (#2115) (by gold-silver-copper)
- (core) [breaking] make core errors non-exhaustive (#2114) (by gold-silver-copper)
- bump rmcp depency to latest (#2103) (by ThomasMarches) - #2103
- update README links to new rig.rs/docs URL structure (#2105) (by gold-silver-copper) - #2105
Contributors
Added
-
(agent) Restore
AgentBuilder::dynamic_contextand
ExtractorBuilder::dynamic_contextas convenience wrappers around the
existing completion-call hook lifecycle. The helper retains the former query
selection and document formatting behavior without restoring a separate
retrieval path in agent request construction. As an ordinary hook, retrieval
and injected documents follow registration order relative to application
hooks; register stop policies before it when they should prevent retrieval. -
(core)
rig_core::telemetry::Emptyre-exportstracing::field::Empty, so a
runtime can declare a completion-parent field as not-yet-valued without taking
a directtracingdependency.
Changed
-
(core, agent) [breaking] Remove every wasm feature flag in the workspace
—rig-core'swasm,rig-agent'swasm, and therigfacade'swasm.
Browser wasm needs no feature flags at all:cargo build --target wasm32-unknown-unknownis the entire opt-in. The feature was a purecfg
switch that every consumer already flipped from a target table, and its one
optional dependency was never referenced. Relaxing the bounds cannot break
implementors — the relaxed markers are blanket-implemented
(impl<T> WasmCompatSend for T {}), so every type that satisfied the strict
form satisfies the relaxed one. (Generic consumers on browser wasm that
wroteT: WasmCompatSendand then relied onT: Sendinternally are the one
exception, and only if they were previously building with the feature off.)
Dependents passingfeatures = ["wasm"]should drop it; nothing replaces it. -
(core)
if_wasm!/if_not_wasm!now key on the target rather than a feature.
These are#[macro_export]ed, and acfginside a macro expansion is
evaluated in the calling crate — so the old expansion tested whether the
caller had a feature namedwasm, notrig-core. Any caller without one
took theif_not_wasm!branch on every target, browser wasm included. Called
out separately because unlike the feature removal, which Cargo rejects at
resolution, this one changes behavior with nothing to fail on: a downstream
crate that did define awasmfeature and expected it to drive these macros
gets the target's answer now, silently. Gate on the target directly if you
need the old association. -
(agent) [breaking] The
rmcpfeature is native-only. It never compiled
for wasm — rmcp'sClientHandlerrequiresSend + Syncunconditionally,
which rig's wasm tool registry cannot satisfy — but it failed with a wall of
dyn ErasedTooltrait errors. It now fails with one sentence naming the cause,
and CI asserts that stays true. -
(agent) Document the supported target matrix: native is fully supported,
wasm32-unknown-unknown(browser) is supported, and WASI is not — its
dependency graph has never built. Browser-only dependencies andSend-relaxed
aliases are scoped accordingly, andwasm-bindgen-futuresis no longer a
rig-agentdependency, its only user having been the now-native-only MCP
cancellation dispatch. -
(core) Fix
rig-core's SSEResponseFuture/EventStreamaliases, whose
cfgarms did not partition and left some targets matching neither, so the
types were undefined there. Both arms now share one predicate. -
(core) The telemetry completion-parent contract has one declarative
source: the newrig_core::telemetry::completion_parent_span!macro
declares the adoption marker and every requiredgen_ai.*field.tracing
bakes a span's field set into static metadata andSpan::recordsilently
no-ops on undeclared fields, so a hand-mirrored field list that drops one
field loses that telemetry with no error — the contract was previously
duplicated in six places. Exact-set tests now pin the macro against
COMPLETION_PARENT_REQUIRED_FIELDSand against the span the completion
builder itself creates, so those lists (includingrig-agent's chat span,
which now delegates to the macro) can no longer drift. A completion parent
that carries the marker but omits a required field triggers awarn!naming
the missing fields — once per offending span callsite, so two broken runtimes
are both reported — before it degrades to a freshrig::completionschild
span, so the degradation is visible in logs rather than only as a duplicated
span layer in dashboards. The macro accepts an
optionalparent:argument (default: the current span), and its expansion
resolvestracingthroughrig-core, so downstream crates do not need a
directtracingdependency merely to invoke it (see theEmptyre-export
above). Nothing is breaking: the marker field and the required field set are
unchanged. -
(derive) [breaking]
#[rig_tool]required-ness is now derived from the
parameter types, and the advertised schema always agrees with the
deserializer. Without an explicitrequired(...), non-Optionparameters
are required andOption<T>parameters are optional (previouslyOption
parameters were advertised as required even though absence deserialized to
None). With an explicitrequired(...), parameters omitted from the list
are deserialized with#[serde(default)], so omitting a non-Option,
non-Defaultparameter is now a compile error instead of a runtime
deserialization failure when the model leaves it out. Names inparams(...)
andrequired(...)must match actual parameters, and malformed or duplicate
attribute entries are compile errors instead of being silently ignored.
Listing anOption<T>parameter inrequired(...)is a compile error
(schemars and serde would both silently ignore the directive), and a
wildcard context binding (#[rig(context)] _: &mut ToolContext) is now
rejected — name it_contextinstead. -
(derive)
#[rig_tool]recognizes fully qualified&mut ToolContext
parameters under renamedrig/rig-agentdependencies without the
#[rig(context)]marker; crate-name resolution and context classification
now share one authority. A contextual tool in a crate with neitherrignor
rig-agentreachable gets a targeted diagnostic instead of an unresolved
::rig_agentpath error. Generatedparameters()builds the schema once
(LazyLock) and no longer contains anexpect, so downstream crates
denyingclippy::expect_usedare unaffected. -
(derive, core) Macro-generated code resolves
serde,serde_json, and
schemarsthroughrig-core's re-exports (rig_core::{serde, serde_json, schemars}are now public), so crates using#[rig_tool]or
#[derive(Embed)]no longer need directserde/serde_jsondependencies.
TheEmbedderive emits fully qualified trait impls and no longer requires
theEmbedtrait to be imported at the call site. A field carrying both
#[embed]and#[embed(embed_with = "...")]is now a compile error instead
of being embedded twice, and a field carrying more than one
#[embed(embed_with = "...")]attribute is a compile error instead of the
first silently winning.
Removed
- (agent) Remove the experimental
rig-runtime-conformancecrate and its
classic-runtime adapter. With a single runtime it was a premature cross-runtime
abstraction, and its scenarios were ~90% redundant withrig-agent's own test
suite. The genuinely-unique invariants (multi-step memory append-once, append
of only newly-committed messages, no-append on hook stop, committed-transcript
role validity, and a two-sided concurrency bound) are now covered by direct
tests inrig-agent. A real conformance contract can be re-extracted once a
second runtime exists.
Fixed
-
(examples)
candle_wasm_chatnow declares theagentfeature it actually
imports (rig::agent::{Agent, AgentBuilder},rig::completion::Chat), so it
builds standalone rather than only inside a workspace-wide--all-features
build that happened to unify the feature onto the sharedrig. The wasm CI
matrix now checks the example on its own, so a manifest that under-declares its
features fails instead of being masked by feature unification. -
(openai) Treat empty
encrypted_contentin non-streaming Responses API
reasoning items as absent, matching streaming behavior and avoiding empty
encrypted reasoning blocks. -
(aws) Stop enabling the AWS SDK's legacy Rustls connector in the Bedrock and S3 Vectors integrations, removing vulnerable
rustls-webpki0.101 from their active dependency graphs while retaining the modern default HTTPS client.
Changed
-
(core, agent) [breaking] Split the monolithic core into a portable
contracts crate (rig-core) and the classic agent runtime crate (rig-agent),
presented behind therigfacade. Code using therigfacade needs
essentially no source changes —rig::…paths,rig::prelude::*, and
rig::tool::{Tool, ToolContext}all keep working. Directrig-coredependents
that constructed agents must now depend onrig-agent. See the migration
guide (MIGRATING.md). -
(tool) [breaking] The portable, context-free tool contract is now named
PortableTool(withPortableToolEmbedding,PortableDynamicTool,
portable_tool_definition); therig_core::tool::Toolalias is removed. On
therigfacade,rig::tool::Toolremains the classic contextual trait, so
existing facade code is unchanged; portable contracts are always available as
rig::tool::PortableTool(and in full underrig::tool::portable). -
(client) [breaking] Provider clients no longer carry inherent
agent()/extractor()methods. There is a single canonical
CompletionClienttrait (inrig-core, providingcompletion_model); the
classicagent()/extractor()constructors live on the newAgentClientExt
extension trait.use rig::prelude::*;brings both into scope for the full
pre-split client surface (or importrig::client::{CompletionClient, AgentClientExt}explicitly). -
(agent) [breaking]
rig-agentno longer re-exports all ofrig-core
at its crate root. The previouspub use rig_core::*;maderig-agentan
implicit second facade; the root now exports only runtime-owned items (plus
the runtime-facingrig_tool/tool_macromacros). Code that depends on
rig-agentdirectly and reached a portablerig-coreitem through the
rig-agentroot must import it fromrig_agent::core(e.g.
rig_agent::core::OneOrMany) or depend onrig-coredirectly. The root
rigfacade is unaffected:rig::…andrig::prelude::*are unchanged.// Before use rig_agent::{OneOrMany, message::Message}; // After use rig_agent::core::{OneOrMany, message::Message};
-
(agent) [breaking] Managed agent hooks are now provider-independent.
AgentHook,HookStack, and the internal erased-hook interface no longer
carry a completion-model type parameter.CompletionResponseEventand
StreamResponseFinishnow expose canonical Rig content, usage, prompt, and
message ID fields instead of typed provider responses. Direct
CompletionModelcompletion and streaming APIs continue to return their
typed raw provider responses.// Before impl<M: CompletionModel> AgentHook<M> for TelemetryHook { /* ... */ } // After impl AgentHook for TelemetryHook { /* ... */ }
-
(agent) [breaking] Make
AgentRunnerthe only execution path for configured agents: remove the rawCompletionandStreamingCompletiontraits and theirAgentimplementations, make agent execution state private, add runner-backed per-request overrides, and routeExtractorthrough the full hook lifecycle. Raw hook-free requests remain available explicitly throughCompletionModel.- For managed agent execution, replace
agent.completion(prompt, history).await?.send().await?withagent.runner(prompt).history(history).max_turns(3).run().await?, choosing a turn budget large enough for tool follow-ups. - For managed streaming execution, replace
agent.stream_completion(prompt, history).await?.stream().await?withagent.runner(prompt).history(history).max_turns(3).stream().await. - The runner consumes tool calls rather than returning the first raw model response. Callers that handled that response manually, and other intentionally hook-free transport, should start from
model.completion_request(prompt).messages(history)and then call.send().await?or.stream().await?. AgentRun::new(prompt).with_history(history)remains a sans-I/O state machine for custom drivers; it contains no configured agent model, tools, memory, or hooks and is not an alternate configured-agent execution path.- An
Agent's model is fixed and private. Former per-call.model(...)/.model_opt(...)users should retain the providerCompletionModeland use its raw request API, or construct a separateAgentfor the selected model.
- For managed agent execution, replace
-
(tool) [breaking] Replace the parallel tool-execution APIs with one structured path. Typed tools now implement only
Tool::call(&mut ToolContext, Args) -> Result<Output, Error>; author-facing errors remain typed until private runtime erasure normalizes them intoToolExecutionError,ToolContextcarries inbound values and host-only result metadata,ToolResultis the single runtime observation, andToolSet::execute/ToolServerHandle::executeare the dispatch surfaces. Event-specific hook action types make invalid event/action combinations unrepresentable.- Tool implementations: retain one typed
type Errorfor ordinary?propagation and direct-call tests; removeclassify_error,call_with_extensions, andcall_structured. The optionalmap_errormethod classifies domain failures at the erased boundary, while its default preserves the source asOther. Return refusals throughmap_errorwithToolExecutionError::refused, and attach host-only result metadata withToolContext::insert_result. - Context: replace
ToolCallExtensionsandToolResultExtensionswithToolContext; replace request/runner.tool_extensions(...)with.tool_context(...). Each dispatch snapshots inbound context exactly once, isolates tool-local mutations, and publishes only result metadata back to the caller and hooks. - Dynamic tools:
ToolDynis removed from the public API; useDynamicToolfor runtime-defined tools. Rig's erased dispatch trait is private. Typed tools useTool::NAMEas their sole identity; runtime-named agents convert explicitly withAgent::into_tool(). - Registration vocabulary:
AgentBuilder::tools(Vec<Box<dyn ToolDyn>>)is removed; use repeated.tool(...)calls for typed tools ordynamic_tools(Vec<DynamicTool>)for runtime-defined callbacks. Retrieval-backeddynamic_tools(sample, index, toolset)becomesretrieved_tools. OnToolSetBuilder,static_toolremains the typed-tool path, the former embedding-backeddynamic_tool(ToolEmbedding)becomesretrieved_tool, and runtime-defined callbacks usedynamic_tool(DynamicTool). - Results and errors: replace
ToolError,ToolFailure,ToolFailureKind,ToolReturn,ToolReturnOutcome,ToolExecutionResult, andToolOutcomewithToolExecutionError,ToolErrorKind, and the read-onlyToolResultobserved by hooks. - Model presentation: serializable outputs convert once into canonical
ToolOutputcontent blocks; strings remain literal text, explicitserde_json::Valuevalues remain JSON, and multimodal tools useToolOutput::content/ToolOutput::oneor return typedToolResultContentdirectly. Result hooks now rewriteToolOutput, provider adapters preserve native JSON where supported or render it only at their terminal wire boundary, mixed user/tool-result blocks retain order, and Rig never reparses strings to infer rich content. Consumers can inspectToolResultContentwithas_text/as_jsonand explicitly decode either structured JSON or legacy JSON-bearing text withdeserialize_json. - Error presentation: explicit
ToolExecutionErrorconstructors keep actionable diagnostics model-visible, while the genericToolExecutionError::from_errorpath preserves operator diagnostics and the concrete source but defaults to safe kind-level model feedback. Usewith_model_feedbackfor deliberate replacement text orwith_model_outputfor JSON/multimodal feedback. MCP responses preserve ordered supported text/image content, retain unsupported and future blocks as typed JSON, and attach rawCallToolResult,structuredContent, and response metadata toToolContext. MCP list installation and refresh are atomic and ownership-aware, so stale handlers cannot replace or remove newer registrations, while disconnected owners are retired during refresh, provider exposure, or direct dispatch. - Dispatch: replace
ToolSet::{call, call_with_extensions, call_structured}withToolSet::execute; replaceToolServerHandle::{call_tool, call_tool_with_extensions, call_tool_structured}withToolServerHandle::execute. - Registration and definitions:
ToolSetis the single ordered registry and records whether each tool is always advertised or retrieval-only.ToolSet::{get_tool_definitions, documents}are now synchronous and infallible,ToolServerHandleregistration/removal methods no longer return an artificialResult, and the obsoleteToolSetErroris removed. - Hooks: replace
AgentHook::on_event,StepEvent, andFlowwith the event-specificAgentHookmethods and their corresponding action types (CompletionCallAction,ToolCallAction,ToolResultAction,InvalidToolCallAction, andObservationAction). Result rewrites replace the effective model and result-content telemetry presentation while preserving the rawToolResultandToolContextfor policy; result stops omit result-content telemetry. Invalid-tool hooks returnNoneto defer; every explicit action, includingFail, is terminal for that hook stack. - Streaming execution observation: the atomically surfaced post-batch event is named
ToolExecutionCommitted, reflecting that it is not a real-time start notification. Applications that need live host lifecycle events should observeon_tool_call/on_tool_result; typed result metadata remains available throughToolResultEvent::tool_contextwithout entering model-facing messages.
- Tool implementations: retain one typed
-
(core) [breaking] Mark
PromptError,StructuredOutputError, andVectorStoreErroras non-exhaustive, requiring downstream match expressions to include a wildcard arm. Conversation memory load failures now surface as the typedPromptError::MemoryErrorvariant instead ofCompletionError::RequestError.