All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- (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.
-
(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.CompletionResponseEventandStreamResponseFinishnow expose canonical Rig content, usage, prompt, and message ID fields instead of typed provider responses. DirectCompletionModelcompletion 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] Remove the built-in
AgentBuilder::dynamic_context,ExtractorBuilder::dynamic_context, and internalDynamicContextStorepassive-retrieval pipeline. Static builder context remains available. For passive RAG, applications now own query selection, retrieval, filtering, reranking, formatting, caching, failure handling, and per-turn policy in a localAgentHook:// Application code: Rig does not provide AppRetrievalHook. struct AppRetrievalHook<I> { index: I, samples: u64, } impl<I> AgentHook for AppRetrievalHook<I> where I: VectorStoreIndexDyn, { async fn on_completion_call( &self, _ctx: &HookContext, event: CompletionCallEvent<'_>, ) -> CompletionCallAction { let message_text = |message: &Message| match message { Message::User { content } => content.iter().find_map(|part| match part { UserContent::Text(text) => Some(text.text.clone()), _ => None, }), _ => None, }; let Some(query) = message_text(event.prompt) .or_else(|| event.history.iter().rev().find_map(message_text)) else { return CompletionCallAction::continue_run(); }; let request = VectorSearchRequest::builder() .query(query) .samples(self.samples) .build(); match self.index.top_n(request).await { Ok(results) => { let documents = results.into_iter().map(|(_, id, value)| Document { id, text: serde_json::to_string_pretty(&value) .unwrap_or_else(|_| value.to_string()), additional_props: Default::default(), }); CompletionCallAction::patch( RequestPatch::new().extra_context(documents), ) } Err(error) => CompletionCallAction::stop( format!("application retrieval failed: {error}"), ), } } } // Before let agent = client.agent(model).dynamic_context(3, index).build(); // After let agent = client .agent(model) .add_hook(AppRetrievalHook { index, samples: 3 }) .build();
Returning
CompletionCallAction::stop(...)prevents provider I/O for that turn. The same hook-awareAgentRunnerlifecycle is used by blocking, streaming, and extractor execution; register the local hook withExtractorBuilder::add_hookfor extraction. For active RAG, expose a vector index through its blanketToolimplementation or provide a custom retriever tool so the model decides whether and when to retrieve. -
(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.
0.40.0 - 2026-07-10
- (tool) [breaking] structured tool-execution results (#2015) (by @gold-silver-copper)
- (agent) [breaking] hook system v2 — composable middleware (#2012) (by @gold-silver-copper)
- (examples) human-in-the-loop tool-call approval — examples + tests (#1967) (by @gold-silver-copper)
- (rig-core) steer the model request per turn from a hook via Flow::OverrideRequest (#1966) (by @gold-silver-copper)
- (rig-core) rewrite tool results from a hook via Flow::RewriteResult (#1965) (by @gold-silver-copper)
- (rig-core) rewrite tool-call arguments from a hook via Flow::RewriteArgs (#1963) (by @gold-silver-copper)
- (openai) preserve responses prompt cache parameters (#1830) (by @Kade-Powell)
- (streaming) [breaking] surface unmodeled provider output items through the stream (#1951) (by @gold-silver-copper)
- (rig-core) [breaking] integrate hooks into AgentRun via a composable AgentRunner (#1945) (by @gold-silver-copper)
- (message) add video helper constructors + OpenRouter audio/video conversion tests (#1942) (by @gold-silver-copper)
- (agent) add OutputMode to compose structured output with tools (#1928) (#1929) (by @gold-silver-copper)
- (telemetry) keep GenAI message span fields empty (#2066) (by @gold-silver-copper)
- (chatgpt) preserve non-success response errors (#2053) (by @gold-silver-copper)
- (vertexai) preserve signed thought text parts (#2052) (by @gold-silver-copper)
- (chatgpt) fallback on empty SSE output (#2001) (by @gold-silver-copper)
- (openai) preserve reasoning text content (#1999) (by @gold-silver-copper)
- preserve OpenAI Responses instructions (#1995) (by @gold-silver-copper) - #1995
- (openai) accept null Responses metadata (#1993) (by @gold-silver-copper)
- (postgres) update sqlx and pgvector (#1992) (by @gold-silver-copper)
- (openai) make Responses API strict tools opt-in (#1991) (by @gold-silver-copper)
- (agent) stream concurrent tool results as they complete (#1981) (by @gold-silver-copper)
- (rig-core) fix epub loader tests + prevent CWD-relative fixture-path regressions (#1940) (by @gold-silver-copper)
- (ollama) preserve assistant reasoning from non-streaming responses (#1926) (#1927) (by @gold-silver-copper)
- Remove unused derive and core APIs (#2087) (by @gold-silver-copper) - #2087
- add Bedrock cassette coverage (#2084) (by @gold-silver-copper) - #2084
- Remove unused stream completion stdout helper (#2085) (by @gold-silver-copper) - #2085
- Remove unused generation wrapper traits (#2083) (by @gold-silver-copper) - #2083
- Remove unused Anthropic decoders (#2082) (by @gold-silver-copper) - #2082
- (agent) [breaking] unify PromptResponse and FinalResponse into one type (#2056) (by @gold-silver-copper)
- (core) [breaking] API paper cuts — duplicate names, hand-copied setters, dead types (#2055) (by @gold-silver-copper)
- (examples) add force_tool_first_turn hook example (#2014) (by @gold-silver-copper)
- (auth) add non-interactive oauth cassette coverage (#2050) (by @gold-silver-copper)
- (perplexity) add cassette coverage (#2049) (by @gold-silver-copper)
- (providers) [breaking] remove galadriel provider (#2041) (by @gold-silver-copper)
- (providers) [breaking] collapse remaining providers onto GenericCompletionModel (#2035 phases 2–4) (#2040) (by @gold-silver-copper)
- (providers) [breaking] migrate llamafile onto GenericCompletionModel (#2035 phase 1) (#2038) (by @gold-silver-copper)
- (core) [breaking] delete unused evals module and experimental feature flag (#2036) (by @gold-silver-copper)
- Flatten Tool metadata API (#2029) (by @gold-silver-copper) - #2029
- (gemini) live cassette hook-system stress suite (#2013) (by @gold-silver-copper)
- Add Groq agent tool cassette regressions (#2011) (by @gold-silver-copper) - #2011
- Add Mistral agent tool cassette regressions (#2010) (by @gold-silver-copper) - #2010
- Add DeepSeek agent tool cassette regressions (#2009) (by @gold-silver-copper) - #2009
- Add xAI agent tool cassette regressions (#2008) (by @gold-silver-copper) - #2008
- Gate Gemini image cassette tests on image feature (#2007) (by @gold-silver-copper) - #2007
- Add OpenRouter agent tool cassette regressions (#2006) (by @gold-silver-copper) - #2006
- Add ChatGPT Codex cassette regression suite (#2005) (by @gold-silver-copper) - #2005
- (gemini) production-grade generateContent cassette suite (#2004) (by @gold-silver-copper)
- (anthropic) production-grade Messages API cassette suite (#2003) (by @gold-silver-copper)
- (openai) production-grade Responses API cassette suite + tool_choice and replay-ID fixes (#2002) (by @gold-silver-copper)
- (providers) add provider implementation checklist (#1997) (by @gold-silver-copper)
- (deps) bump assert_fs from 1.1.3 to 1.1.4 (#1933) (by @dependabot[bot])
- (deps) bump trybuild from 1.0.116 to 1.0.117 (#1935) (by @dependabot[bot])
- (deps) bump chrono from 0.4.44 to 0.4.45 (#1934) (by @dependabot[bot])
- (deps) bump uuid from 1.23.3 to 1.23.4 (#1975) (by @dependabot[bot])
- (deps) bump scylla from 1.6.0 to 1.7.0 (#1932) (by @dependabot[bot])
- (anthropic) add null citation streaming cassette (#1978) (by @gold-silver-copper)
- update agent and contribution guidance (#1974) (by @gold-silver-copper) - #1974
- (openai-compat) genuinely exercise the #1958 tool-call eviction string-leak (+ live cassette) (#1962) (by @gold-silver-copper)
- (rig-core) [breaking] remove the experimental pipeline module (#1941) (by @gold-silver-copper)
- run doctests and stop rig-sqlite opting out of them (#1939) (by @gold-silver-copper) - #1939
- (rig-core) replace nanoid with fastrand for internal IDs (#1938) (by @gold-silver-copper)
- (examples) migrate to a package-per-example layout (#1937) (by @gold-silver-copper)
- add Archestra to "Who is using Rig?" section (#1925) (by @arsenyinfo) - #1925
- @gold-silver-copper
- @dependabot[bot]
- @Kade-Powell
- @arsenyinfo
-
(agent) [breaking]
max_turnsanddefault_max_turnsnow bound the exact total number of model calls, including the initial call, tool continuations, and retries. A budget of0makes no model call, while1permits only the initial call. Unconfigured tool-then-answer flows now need an explicit total budget of2. To preserve the former maximum allowance of an explicit old budgetn, account for the old effectiven + 2calls; otherwise, set the intended literal total. -
(tool) [breaking] flatten
Tool/ToolDynmetadata: tool authors now implementdescription()andparameters()directly, andTool::definition(prompt)/ToolDyn::definition(prompt)are removed.ToolDefinitionremains a provider/request artifact generated from registered tools, withTool::NAME/Tool::name()/ToolDyn::name()as the single source of truth for advertised and dispatched tool names. -
(providers) [breaking] migrate
llamafileonto the sharedGenericCompletionModel<Ext>/GenericEmbeddingModel<Ext>path, deleting its hand-rolled completion model, request types, message flattening, and streaming profile.llamafile::CompletionModel/llamafile::EmbeddingModelare now type aliases for the generic models; the provider-specificStreamingCompletionResponsetype is replaced by the shared OpenAI one. Requests now serialize messages in the shared OpenAI shape (single-text user content still flattens to a string; system/multi-part content is sent as a content-part array, which llama.cpp-family servers accept). -
(openai) [breaking] new
OpenAICompatibleProvidertrait (mirroringAnthropicCompatibleProvider) is now required byGenericCompletionModel'sExtparameter; it carries the telemetry provider name (so minimax/zai/xiaomimimo spans stop reporting as "openai") and anEMITS_COMPLETE_SINGLE_CHUNK_TOOL_CALLSflag for llama.cpp-style streaming tool calls. -
(providers) [breaking] migrate the remaining OpenAI-chat-compatible providers onto
GenericCompletionModel<Ext>— groq, deepseek, mistral, together, moonshot (OpenAI side), perplexity, hyperbolic, mira, azure, and huggingface all lose their hand-rolledCompletionModelstructs, request types, andTryFrom<message::Message>conversions;CompletionModelin each module is now a type alias for the generic model. Provider wire dialects live inOpenAICompatibleProviderhooks: an associatedResponsetype,completion_path(Azure deployment URLs,/v1-prefixed routes),prepare_request(Groq native-tool folding, Moonshotrequiredtool-choice coercion, HuggingFace Fireworks model ids, Perplexity/Mira tool stripping),finalize_request_body(DeepSeek string content + thinking-gated tool choice, Mistral"any"tool choice +prefixfield + reasoning stripping, Mira raw-message flattening), andSUPPORTS_RESPONSE_FORMAT/STREAM_INCLUDE_USAGEconsts. Provider-specificStreamingCompletionResponsetypes are replaced by the shared OpenAI one. -
(openai) [breaking]
ToolChoicegains aFunction { name }variant serializing OpenAI's{"type":"function","function":{"name":...}}form, somessage::ToolChoice::Specificwith one function is now supported instead of erroring;CompletionRequestfields are now public;OpenAIRequestParamsgains asupports_response_formatfield. -
(openai) the shared
TryFrom<message::ToolResult>conversion now preferscall_idoveridfortool_call_id(matching provider-issued call ids); the shared streaming delta acceptsreasoningas an alias forreasoning_content(Groq), and the deprecatedfunction_callfinish reason maps to tool-call handling. -
(providers) behavior notes from the migration:
max_tokensis now forwarded by deepseek, together, hyperbolic, and azure (previously silently dropped); together's streaming request uses standardstream/stream_optionsinstead ofstream_tokens, and a rig-levelToolChoice::Requirednow serializes asrequiredinstead of erroring; perplexity's non-streaming endpoint drops its stray/v1prefix (matching its streaming path and the real API); mira's preamble is sent as asystemmessage instead ofuser;response_formatderived fromoutput_schemais deferred while tools are pending a result (groq/mistral/azure previously applied it unconditionally); groq's streaming usage no longer falls back to the legacyx_groq.usageenvelope. -
(openrouter) [breaking] de-fork OpenRouter's parallel message model (issue #2035 phase 4):
openrouter::{Message, UserContent, ImageUrl}are now re-exports of the shared OpenAI types, and the fork'sFileContent/VideoUrlContentare replaced by sharedFileData/VideoUrl. To support this, the shared OpenAI types gain OpenRouter's optional extensions —UserContent::Video,ImageUrl.detailbecomesOption<ImageDetail>(OpenAI still sends"detail":"auto"), andMessage::Assistantgains a skip-when-emptyreasoning_detailsfield, an inbound-onlyimagesfield (never serialized back into requests), plus a deserialize-onlyrole: "model"alias.ReasoningDetails/ResponseImagemove into the openai module (re-exported from openrouter). OpenRouter-specific message conversion now goes throughopenrouter::messages_from_rig_message;TryInto<Vec<openrouter::Message>>resolves to the plain shared conversion. OpenRouter keeps its own request/response/streaming layer (provider preferences, cost accounting, reasoning-details grouping, generated-image extraction) as a documented exception. -
(openai) the
UserContentaudio part now serializes its tag asinput_audio(matching OpenAI's actual API);audiois still accepted when deserializing. -
(openai)
StreamingCompletionResponseis now generic over the provider's streaming usage payload (StreamingCompletionResponse<U = Usage>, selected viaOpenAICompatibleProvider::StreamingUsage), so Mistral's cached-token fallbacks and DeepSeek's cache hit/miss counters survive streaming instead of being narrowed to OpenAI's usage shape. -
(providers) pre-migration request filtering is preserved where provider support is unverified: hyperbolic still drops
tools/tool_choice/output_schemawith warnings, and perplexity flattens text-only message content back to plain strings (mixed multimodal content is passed through for sonar models). llamafile keeps the current mapping ofoutput_schemato ajson_schemaresponse format (as on the shared path since the llamafile migration; modern llama.cpp servers support it). -
(llamafile) the chat cassettes are now recorded against an actual llama.cpp
llama-server, confirming the shared OpenAI wire shape (content-part arrays, tool calls, tool results) against the real llamafile-family server rather than an OpenAI-compatible proxy. -
(openai) the assistant tool-call echo now serializes
call_id(falling back toid) so it stays consistent with the tool-result side when history recorded via the Responses API is replayed through chat completions; streaming deltacontenttolerates content-part arrays (Mistral reasoning models) instead of dropping the chunk. -
(providers) review fixes: mira and perplexity no longer send
stream_options(their APIs never received it pre-migration); moonshot rejects a specific forced tool client-side again; openrouter serializes plain assistant reasoning under its documentedreasoningkey; azure telemetry spans reportazure.openaiagain; mira usage math saturates instead of overflowing; perplexity strips tool-exchange remnants from shared histories. -
(providers) second review round: openrouter tool-result messages prefer the provider-issued
call_id(matching the assistant echo side); Azure's deployment URL stays pinned to the model the handle was created with (a per-requestmodeloverride only changes the body, as pre-migration); shared streaming spans recordgen_ai.system_instructionsagain; providers without tool support (perplexity, mira, and now hyperbolic) sanitize tool-exchange remnants from shared histories via one shared helper that also preserves strict role alternation (tool-call-only assistant turns are dropped and consecutive assistant turns merged); openrouter's dead pre-migrationToolChoicetype is removed, andToolChoice::Specificwith multiple function names now errors client-side for openrouter (the old fork serialized a non-standard array). -
(moonshot) [breaking] reasoning-only assistant history turns are no longer preserved: the shared conversion drops assistant messages with neither text nor tool calls. Reasoning attached to text or tool-call turns still round-trips via
reasoning_content. -
(providers) [breaking] responses with empty assistant content and no tool calls now surface the shared path's "empty response" error for hyperbolic, perplexity, and huggingface (previously they returned an empty text completion).
-
(providers) [breaking] additional removed public items: the raw response types of perplexity, hyperbolic, and huggingface (each module keeps a
CompletionResponsealias to the shared OpenAI payload;Message/Choice/Usage/Delta/Rolecompanions are gone),together::ToolChoice/ToolChoiceFunctionKind,moonshot::ToolChoice,groq::send_compatible_streaming_requestanddeepseek::send_compatible_streaming_request(useopenai::send_compatible_streaming_request), and openrouter'sUserContentbuilder helpers (image_url,file_base64,video_url, ...) — construct the sharedopenaicontent variants directly. -
(openai) [breaking] sending rig
Videouser content to providers on the shared conversion now serializes avideo_urlcontent part (an OpenRouter/gateway extension) instead of returning a client-side conversion error; providers without video support will reject it server-side. -
(providers) [breaking] telemetry: migrated providers' streaming spans are now named
chatwithgen_ai.operation.name = "chat"(previouslychat_streaming). GenAI message-content span fields (gen_ai.input.messages/gen_ai.output.messages) are intentionally left empty instead of recording serialized request/response messages, preserving the privacy/cardinality behavior from #2065; the publicSpanCombinator::record_model_outputhelper is removed.gen_ai.request.modelreports the per-request model override when one applies. -
(providers) third review round: history sanitization treats
refusalparts as text when flattening and, for alternation-strict perplexity, merges consecutive same-role turns (dropping a tool exchange could previously leaveuser/useradjacency its API rejects); streaming no longer overwrites caller-suppliedstream_options; openrouter's encrypted reasoning details now correlate with the wire tool-call id and its non-streaming usage uses the reportedcompletion_tokens(no underflow); base64 videos with unrecognized MIME types round-trip as data-URI URLs instead of failing conversion. -
(providers) fourth review round — agent structured output:
GenericCompletionModelno longer claims native structured output composes with tools for every provider; it now followsSUPPORTS_RESPONSE_FORMAT. Agents with tools plus an output schema on deepseek/together/moonshot/huggingface/hyperbolic/perplexity/mira fall back to tool-mode schema enforcement as their pre-migration models did (the migration had silently dropped the schema entirely); groq/mistral/azure now compose natively like openai. -
(openai) new
OpenAICompatibleProvider::SUPPORTS_TOOLSconst (default true): perplexity, hyperbolic, and mira set it false andtools/tool_choiceare dropped with a warning during request conversion — before tool-choice validation, so a multi-nameToolChoice::Specificno longer errors client-side on providers that ignored it pre-migration. -
(openai) streaming robustness:
include_usageis inserted into caller-suppliedstream_optionsinstead of being skipped (or clobbering the caller's keys, the pre-migration behavior); a delta carrying bothreasoning_contentandreasoningno longer fails as a serde duplicate-field error that dropped the whole chunk; streaming tool-callindexdefaults to 0 when omitted (Mistral marks it optional);CompletionResponse.object/createdare defaulted on deserialization for gateways that omit them (HuggingFace router sub-providers). -
(openrouter) non-streaming usage falls back to
total - prompt(saturating) when the gateway omitscompletion_tokens; streaming spans follow the shared telemetry behavior of leaving GenAI message-content fields empty. -
(openai) [breaking]
ToolChoiceis now#[non_exhaustive];GenericCompletionModel'sstrict_tools/tool_result_array_contentfields are private (use thewith_*builder methods) and the redundantwith_modelconstructor is removed (usenew).
- (derive) [breaking] remove the unused public
rig_derive::ProviderClientderive macro and itsdeluxedependency;Embedandrig_toolare unchanged, and no replacement is provided. - (core) [breaking] remove unused
Extractor::{get_inner, into_inner}and the always-failingTryFrom<String> for Nothing; no direct replacements are provided. - (core) [breaking] remove the unused public
streaming::stream_completion_to_stdouthelper; use the high-levelagent::stream_to_stdouthelper instead. - (core) [breaking] remove the unused public
AudioGeneration<M>,ImageGeneration<M>, andTranscription<M>wrapper traits; use the correspondingAudioGenerationModel,ImageGenerationModel, andTranscriptionModelAPIs and request builders directly. - (core) [breaking] remove the unused
evalsmodule (Evaltrait, judge metrics, and builders) along with theexperimentalfeature flag that gated it - (anthropic) [breaking] remove the unused public
providers::anthropic::decodersmodule; Anthropic streaming uses the shared SSE machinery. - (providers) [breaking] remove the Galadriel provider integration (
providers::galadriel), including its client, model constants, environment-variable support, and ignored live tests.
0.39.0 - 2026-06-19
- (providers) add VoyageAI rerank support (#1917) (by @sergiomeneses)
- (agent) [breaking] sans-IO AgentRun state machine; both agent loops become thin drivers (#1899) (by @gold-silver-copper)
- correct possessive pronoun typo in CONTRIBUTING.md (#1865) (by @abhicris) - #1865
- (tool) [breaking] deterministic, duplicate-safe tool registration + cassette tests (#1913) (by @gold-silver-copper)
- (deps) bump uuid from 1.23.1 to 1.23.3 (#1907) (by @dependabot[bot])
- (deps) bump lopdf from 0.40.0 to 0.41.0 (#1877) (by @dependabot[bot])
- (deps) bump http from 1.4.0 to 1.4.2 (#1909) (by @dependabot[bot])
- (deps) bump futures-timer from 3.0.3 to 3.0.4 (#1908) (by @dependabot[bot])
- (examples) add Gemini mid-stream disruption token-counting example (#1918) (by @gold-silver-copper)
- (tool) back ToolSet with an IndexMap instead of HashMap + order Vec (#1916) (by @gold-silver-copper)
- de-flake tracing span tests and deepseek permission_control race (#1915) (by @gold-silver-copper) - #1915
- (agent) cassette-backed AgentRun coverage against real Gemini turns (#1901) (by @gold-silver-copper)
- Fix streaming reasoning history order (#1898) (by @gold-silver-copper) - #1898
- Fix context document ordering (#1893) (by @gold-silver-copper) - #1893
- Point ecosystem link to awesome-rig (#1895) (by @gold-silver-copper) - #1895
- Add Gemini Nano Banana image generation (#1889) (by @gold-silver-copper) - #1889
- @dependabot[bot]
- @abhicris
- @gold-silver-copper
- @sergiomeneses
0.38.2 - 2026-06-09
- support Anthropic mid-conversation system role (#1862) (by @fangkangmi) - #1862
- (deps) bump tonic-prost-build from 0.14.5 to 0.14.6 (#1874) (by @dependabot[bot])
- (deps) bump convert_case from 0.10.0 to 0.11.0 (#1875) (by @dependabot[bot])
- (deps) bump reqwest from 0.13.3 to 0.13.4 (#1873) (by @dependabot[bot])
- (deps) bump reqwest-middleware from 0.5.1 to 0.5.2 (#1876) (by @dependabot[bot])
- Remove rig-redis integration (#1887) (by @gold-silver-copper) - #1887
- migrate Copilot tests to cassette replay (#1882) (by @gold-silver-copper) - #1882
- Redis vector store integration (#1509) (by @daric93) - #1509
- add Ryzome to README nav links (#1879) (by @mateobelanger) - #1879
- [codex] support mistral.rs OpenAI-compatible reasoning (#1864) (by @gold-silver-copper) - #1864
- convert DeepSeek live tests to cassettes (#1870) (by @gold-silver-copper) - #1870
- [codex] add OpenRouter cassette-backed provider coverage (#1869) (by @gold-silver-copper) - #1869
- convert xAI live tests to cassettes (#1868) (by @gold-silver-copper) - #1868
- [codex] cover Anthropic streaming tool result batching (#1863) (by @gold-silver-copper) - #1863
- @dependabot[bot]
- @gold-silver-copper
- @daric93
- @mateobelanger
- @fangkangmi
0.38.1 - 2026-06-02
- unify workspace crate versions (#1853) (by @gold-silver-copper) - #1853
- @gold-silver-copper
0.37.1 - 2026-06-02
- (rig-derive) replace hand-rolled schema with schemars in #[rig_tool] (#1576) (by @tomasz-feliksik)
- (gemini) expose streaming response metadata (#1790) (by @mateobelanger)
- (anthropic) support document citations (#1778) (by @temrjan)
- (chatgpt) Handle ChatGPT response.completed events without output field (#1825) (by @geraschenko)
- (rig-gemini-grpc) populate FunctionDeclaration.parameters from ToolDefinition (#1763) (by @abhicris)
- fix sqlite threshold and null tool call streaming (#1786) (by @gold-silver-copper) - #1786
- (deps) bump mongodb from 3.6.0 to 3.7.0 (#1848) (by @dependabot[bot])
- (deps) bump zerocopy from 0.8.48 to 0.8.50 (#1847) (by @dependabot[bot])
- (deps) bump google-cloud-aiplatform-v1 from 1.10.0 to 1.11.0 (#1846) (by @dependabot[bot])
- (deps) bump serde_json from 1.0.149 to 1.0.150 (#1845) (by @dependabot[bot])
- (deps) bump tonic from 0.14.5 to 0.14.6 (#1844) (by @dependabot[bot])
- Fix parsing of streamed function-call argument deltas (#1828) (by @geraschenko) - #1828
- (deps) port dependency bumps and Rust 1.91 (#1842) (by @gold-silver-copper)
- (deps) bump quick-xml from 0.39.4 to 0.40.1 (#1818) (by @dependabot[bot])
- (deps) bump google-cloud-auth from 1.9.0 to 1.10.0 (#1817) (by @dependabot[bot])
- Stabilize MongoDB vector search test (#1841) (by @gold-silver-copper) - #1841
- fix VT Code line grammar in README (#1824) (by @Shaurya-Sethi) - #1824
- [codex] Validate model tool calls (#1823) (by @gold-silver-copper) - #1823
- [codex] apply Anthropic cache control to tools (#1815) (by @gold-silver-copper) - #1815
- (deps) bump tokio-tungstenite from 0.23.1 to 0.28.0 (#1784) (by @dependabot[bot])
- (deps) bump rmcp from 1.6.0 to 1.7.0 (#1783) (by @dependabot[bot])
- (deps) bump tokio from 1.52.1 to 1.52.3 (#1782) (by @dependabot[bot])
- Expose per-completion-call usage in agent responses (#1787) (by @gold-silver-copper) - #1787
- (gemini) add streaming metadata cassettes (#1777) (by @gold-silver-copper)
- Add replayable provider cassette tests (#1769) (by @gold-silver-copper) - #1769
- @dependabot[bot]
- @geraschenko
- @tomasz-feliksik
- @gold-silver-copper
- @abhicris
- @Shaurya-Sethi
- @mateobelanger
- @temrjan
0.37.0 - 2026-05-13
- (openrouter) add transcription (STT) and audio generation (TTS) support (#1757) (by @fversaci)
- (rig-bedrock) add structured output support via Converse API (#1667) (by @jdwil)
- (memory) Rig-managed conversation memory + rig-memory companion crate (#1702) (by @ForeverAngry)
- add copilot model listing (#1700) (by @BigtoC) - #1700
- (gemini) Token usage correctness for posthog llm analytics (#1761) (by @mateobelanger)
- (core) [breaking] make Chat append messages to caller history (#1733) (by @gold-silver-copper)
- Clean up root facade features and integration docs (#1764) (by @gold-silver-copper) - #1764
- fix "a ancient" grammar in glarb-glarb sample text (#1755) (by @abhicris) - #1755
- (deps) bump lopdf from 0.36.0 to 0.40.0 (#1754) (by @dependabot[bot])
- (deps) bump quick-xml from 0.39.2 to 0.39.4 (#1752) (by @dependabot[bot])
- (deps) bump tonic-build from 0.14.5 to 0.14.6 (#1751) (by @dependabot[bot])
- Move reusable test doubles into rig_core::test_utils (#1745) (by @gold-silver-copper) - #1745
- workspace and docs cleanup (#1742) (by @gold-silver-copper) - #1742
- openrouter vars (#1741) (by @gold-silver-copper) - #1741
- Add provider file ID support for document inputs (#1740) (by @gold-silver-copper) - #1740
- add smoke test for completion across all Copilot models (#1730) (by @BigtoC) - #1730
- bump dependencies (#1728) (by @gold-silver-copper) - #1728
- remove needless files (#1715) (by @gold-silver-copper) - #1715
- AGENTS.MD, CONTRIBUTING.MD, and docs (#1714) (by @gold-silver-copper) - #1714
- Add Bedrock integration tests (#1707) (by @gold-silver-copper) - #1707
- @gold-silver-copper
- @fversaci
- @mateobelanger
- @abhicris
- @jdwil
- @dependabot[bot]
- @ForeverAngry
- @BigtoC