@tanstack/ai-openrouter
1.0.0 · 22 Oct 2026MIT69.2k / weekRepositoryHomepagenpmAtom feed
Latest releases
All 98 releases- 1.x · latest line
1.0.0
22 Oct 2026- breaking
TanStack AI 1.0.0. This release removes every API that was deprecated in 0.x. Read the upgrade guide before you upgrade: tanstack.com/ai/latest/docs/migration/v1
The TypeSafe, Ollaya, OpenRouter and Vercel Gateway evaluate adapters now set
statuson the error they throw for an error response.statusis the HTTP status code, so you can tell a rate limit (429) from a bad key (401) without parsing the message. The message does not change.if (error instanceof Error && 'status' in error && error.status === 429) { // wait, then call decide() again }Update model metadata from OpenRouter API
Major Changes
- breaking
TanStack AI 1.0.0. This release removes every API that was deprecated in 0.x. Read the upgrade guide before you upgrade: tanstack.com/ai/latest/docs/migration/v1
Removed all
@deprecatedexports, options, fields, and type aliases. Each one has a replacement that already shipped in 0.x.Removed
addToolApprovalResponse()fromChatClientand every chat hook. Resolve the boundtool-approvalinterrupt withresolveInterruptinstead. The devtools eventtools:approval:respondedis also removed.Removed the old wire formats: the CUSTOM
approval-requestedandtool-input-availableevents, the[DONE]SSE line, thedatacopy offorwardedProps, pre-binding interrupt descriptors and theirapproval_requiredandclient_tool_inputreasons, and the STEP events that adapters sent next toREASONING_*events. Use a 1.0 client with a 1.0 server.Removed the
toolNamealias fromTOOL_CALL_STARTchunks. ReadtoolCallNameinstead.threadIdis now required onuseChatand every other chat hook,ChatClient, the generation hooks and clients,chat(),summarize(), the generation functions,codeModeWithSnippets(),selectRelevantSnippets(),CompactionContext, andSummarizeInput. TanStack AI no longer makes a thread id for you. Pass a stable id for each conversation.createChatHookfactory options no longer takethreadId: pass it on each hook call. Octane generation hooks takethreadIdinstead ofid.@tanstack/ai-sandboxremovesresolveDurableThreadIdandDurableThreadIdRequiredError.The published
@tanstack/ai-octane/uitypes are now strict.UI.Messages,UI.Message,UI.Part, andUI.Interruptstype their render children.ToolPropstypespart.inputandpart.outputfor the named tool.toolsComponentsandinterruptsComponentsare required when the chat options declare tools or named interrupts. Code that relied on the old loose types can fail to compile.A connection adapter's
connect()andsend()need the run context (withthreadId) as their fourth argument.ChatClientand the generation clients pass it.@tanstack/ai-sandbox:createToolBridgeCore(),startHostToolBridge(), aToolBridgeProvisioner, andexecuteHostTool()take a requiredthreadId. Bridged tools get it in their context.@tanstack/ai-sandbox: the harness tool bridge andexecuteHostTool()refuse a tool withneedsApproval. Before, the tool ran with no approval. RemoveneedsApproval, or run the tool with an HTTP adapter.stream()andrpcStream()pass the AG-UIRunAgentInputfor the run as the fourth factory argument, the same body the HTTP adapters send. Read it on the server withchatParamsFromRequestBody()orgenerationParamsFromBody(), sothreadId,runId, and interruptresumereach a server function.RunAgentInputis exported from@tanstack/ai/client.A generation
fetchergetsthreadIdandrunIdin its second argument, next tosignalandheaders. Pass them to the server generation call.generationParamsFromBody()andgenerationParamsFromRequest()read the generation input fromforwardedProps. A server that readsbody.datagetsundefined. Readbody.forwardedPropsinstead. They also requirethreadIdandrunIdand return them as strings, the same aschatParamsFromRequestBody()and the AG-UIRunAgentInput. A body that holds only the input is rejected.Removed the readers for data saved in old 0.x formats (old Cloudflare run-log records, a bare
UIMessagefrom client persistence, artifact records withoutblobKey).Removed provider models that are shut down.
@tanstack/ai-react-ui,@tanstack/ai-solid-ui, and@tanstack/ai-vue-uiget no 1.0 release. Import from@tanstack/ai-react/ui,@tanstack/ai-solid/ui, or@tanstack/ai-vue/ui.
Minor Changes
The TypeSafe, Ollaya, OpenRouter and Vercel Gateway evaluate adapters now set
statuson the error they throw for an error response.statusis the HTTP status code, so you can tell a rate limit (429) from a bad key (401) without parsing the message. The message does not change.if (error instanceof Error && 'status' in error && error.status === 429) { // wait, then call decide() again }
Patch Changes
Update model metadata from OpenRouter API
- 0.23.x · maintenance
0.23.1
21 Oct 2026Only updates a dependency:@tanstack/ai 0.71.0
0.23.0
19 Oct 2026Structured output sends
response_format: { type: "json_object" }for OpenRouter and Groq models that declare a response format without strict structured outputs. These models reject strictjson_schema. In JSON mode the adapter adds the schema as a system prompt.await chat({ outputSchema })checks the reply when the schema is a Standard Schema, such as Zod. Other models keep strictjson_schema.modelOptionscan setjson_objectfor any model. On the OpenAI-base adapters (OpenAI, Grok, Groq and others), ajson_objectinmodelOptionswas replaced by strictjson_schema. It is now sent as given, with the schema in a system prompt. In JSON mode,supportsCombinedToolsAndSchema()returnsfalse. Tools and the schema then go out in separate requests, so the tool-calling turns do not get the schema prompt.OpenRouter catalog ids with a
:variant, such asgoogle/gemma-4-26b-a4b-it:free, now match their own catalog entry. Before, only the id without the variant was checked.Keep the provider's raw tool argument string for replay.
Minor Changes
Structured output sends
response_format: { type: "json_object" }for OpenRouter and Groq models that declare a response format without strict structured outputs. These models reject strictjson_schema. In JSON mode the adapter adds the schema as a system prompt.await chat({ outputSchema })checks the reply when the schema is a Standard Schema, such as Zod. Other models keep strictjson_schema.modelOptionscan setjson_objectfor any model. On the OpenAI-base adapters (OpenAI, Grok, Groq and others), ajson_objectinmodelOptionswas replaced by strictjson_schema. It is now sent as given, with the schema in a system prompt. In JSON mode,supportsCombinedToolsAndSchema()returnsfalse. Tools and the schema then go out in separate requests, so the tool-calling turns do not get the schema prompt.OpenRouter catalog ids with a
:variant, such asgoogle/gemma-4-26b-a4b-it:free, now match their own catalog entry. Before, only the id without the variant was checked.
Patch Changes
Keep the provider's raw tool argument string for replay.
Adapters now send the raw argument string as
argsonTOOL_CALL_END.The tool call in the history keeps that exact string when it is the same JSON value as the parsed input, so the next request sends the provider's own bytes back (for example
1.50, or an id pastNumber.MAX_SAFE_INTEGER). When the parsed input differs, only the changed values are written again.The
TOOL_CALL_ENDthat reaches the client carries the same string inmetadata.tanstack.args, so the client history matches the server history. It no longer also carriesmetadata.tanstack.input: in-processchat()andStreamProcessorparseinputfromargs. Run the client and the server on the same release: an older client ignoresargs, so it builds the tool input from the streamed deltas only, and a tool call with no deltas (for example an Anthropic server tool) gets no input.History converters for Bedrock and the Gemini Interactions API send malformed tool arguments as
{}instead of failing the request.Gemini Interactions: the initial
step.argumentssnapshot goes out as aTOOL_CALL_ARGSdelta only when no argument deltas follow, so the deltas always add up to the final arguments.Malformed tool arguments are no longer turned into
{}by an adapter. The tool gets a "Failed to parse tool arguments as JSON" error that the model can repair. Mistral still ends the run with an error on malformed arguments, as before.
- 0.22.x · maintenance
0.22.0
13 Oct 2026Text adapters now set one stable code on
RUN_ERRORwhen the input does not fit in the model's context window:code: 'context_overflow'.@tanstack/aiexports it asCONTEXT_OVERFLOW_CODE. The provider's own text stays inmessage.if (chunk.type === 'RUN_ERROR' && chunk.code === CONTEXT_OVERFLOW_CODE) { // shorten the messages, then call chat() again }These adapters set it: OpenAI and the other OpenAI-compatible adapters (from
code: 'context_length_exceeded'), Anthropic, Gemini, Bedrock (Converse, Chat Completions, and Responses), OpenRouter, Grok, Cloudflare Workers AI, and BytePlus.Changes to code that reads the old values:
Add
openrouterSignIn()at@tanstack/ai-openrouter/pkce: a local app, such as a CLI, signs the user in to OpenRouter in the browser and gets an API key, with no key to copy.Prompt caching is now on by default. Each
chat()call asks the provider to cache the stable start of the request (system prompt, tools, and earlier messages), so repeated requests cost less and answer sooner.
Minor Changes
Text adapters now set one stable code on
RUN_ERRORwhen the input does not fit in the model's context window:code: 'context_overflow'.@tanstack/aiexports it asCONTEXT_OVERFLOW_CODE. The provider's own text stays inmessage.if (chunk.type === 'RUN_ERROR' && chunk.code === CONTEXT_OVERFLOW_CODE) { // shorten the messages, then call chat() again }These adapters set it: OpenAI and the other OpenAI-compatible adapters (from
code: 'context_length_exceeded'), Anthropic, Gemini, Bedrock (Converse, Chat Completions, and Responses), OpenRouter, Grok, Cloudflare Workers AI, and BytePlus.Changes to code that reads the old values:
OpenAI-compatible adapters gave
code: 'context_length_exceeded'. They now give'context_overflow'.rawEventstill has the provider's code.Anthropic, Grok, OpenRouter, and BytePlus gave the HTTP status or a general bad-request code for this error. Gemini and Bedrock Converse gave no code.
The Anthropic adapter now sends
RUN_ERRORwith this code when the model stops withstop_reason: 'model_context_window_exceeded'. Before, it sentRUN_FINISHEDwithfinishReason: 'stop', so a cut-off answer looked complete.
Add
openrouterSignIn()at@tanstack/ai-openrouter/pkce: a local app, such as a CLI, signs the user in to OpenRouter in the browser and gets an API key, with no key to copy.Prompt caching is now on by default. Each
chat()call asks the provider to cache the stable start of the request (system prompt, tools, and earlier messages), so repeated requests cost less and answer sooner.New
chat()optionpromptCache:'none' | 'short' | 'long', or{ retention, key }. The default is'short'. The key ispromptCache.key, else thethreadIdorconversationIdthat you give. AthreadIdthatchat()makes up is never the key.To turn it off, pass
promptCache: 'none'. Do this for a large prompt that you send to Claude one time only, because Claude bills the cache write.A middleware can change it for the next model call:
onConfiggetsconfig.promptCacheand can return a new value.Each adapter sends its own fields:
Claude on Anthropic, Bedrock Converse, and OpenRouter gets cache markers.
'long'asks for the 1-hour TTL.OpenAI (Responses and Chat Completions) gets
prompt_cache_key, andprompt_cache_retentionorprompt_cache_optionson'long'.Mistral gets
prompt_cache_key, and usage now reports cache reads.OpenRouter gets
sessionId.
A manual
cache_control,cachePoint,prompt_cache_key, orsessionIdwins over the automatic value.New types:
PromptCacheRetention,PromptCacheOptions, andResolvedPromptCache. Adapters readTextOptions.promptCache.
Set reasoning with
chat({ reasoning })on both OpenRouter adapters.The chat adapter sends
reasoning.effort, andnoneforoff. Its request has no budget field, so its types take nobudgetTokens.The Responses adapter sends
reasoning.effort,summary: 'auto'when the summary is on, andmaxTokensforbudgetTokens.Each model has its reasoning data in
model-meta.ts.OpenRouterModelReasoningByNameandOPENROUTER_MODEL_REASONINGcome from it.
Breaking:
reasoningis no longer inmodelOptions, andReasoningOptionsis removed.// Before chat({ adapter: openRouterText('openai/gpt-5.5'), messages, modelOptions: { reasoning: { enabled: false } }, }) // After chat({ adapter: openRouterText('openai/gpt-5.5'), messages, reasoning: 'off', })
Patch Changes
Fix three gaps in the OpenRouter Responses adapter (
openRouterResponsesText):Usage now has
promptTokensDetails.cachedTokenswhen the response reports cached input tokens.An output item that the OpenRouter SDK cannot parse (the SDK gives it as
{ type: 'UNKNOWN', isUnknown: true, raw }) is now read from its raw item, in the stream and instructuredOutput. Before this fix, that function call or message was lost.The
namespaceof a function call is now kept in the tool call metadata and sent back when the call is replayed.
chat({ retry })now retries an OpenRouter provider error that arrives in the stream. OpenRouter can send status 200 before the provider fails. The error then comes as an error chunk that the SDK cannot retry. When the code of that chunk is inretryCodes(default['5XX'], add'429'to retry a rate limit), theRUN_ERRORof the Chat Completions adapter now hasretryAfterMsset to theinitialIntervalofretryConfig(default 500).chat()retries it only when the call streamed no output yet. An error after part of the answer is not retried.retryConfig: { strategy: 'none' }turns this off.Keep a schema property or a tool argument named
__proto__. Before this fix, code that copied it without[key] = valuewent to the__proto__setter instead, so the key was lost.@tanstack/ai-utils:transformNullsToUndefinedandundoNullWideningkeep the key.@tanstack/openai-base: the strict schema and its null-widening map keep the key.@tanstack/ai-mistral: the null-widening map keeps the key.@tanstack/ai-openrouter: the Responses tool input normalizer keeps the key.@tanstack/ai-gemini: the Interactions adapter keeps the key in tool parameters.@tanstack/ai-bedrock: the Converse adapter puts the tool schemas and tool inputs back into the request body after the AWS SDK serializes it, because the SDK drops the key.
Keep a conversation valid when the next request goes to a different model.
Each assistant message records
metadata.tanstack.source({ provider, api, model }), andresponseIdand the resolvedmodelwhen the provider sends them. A failed or aborted answer getsstopReason: 'error'or'aborted'.RUN_FINISHEDhas a newresponseIdfield.TextAdapterhas new optionalproviderandapifields.When the next request goes to a different source, the adapter removes signatures and redacted thinking, turns readable thinking into text, and rewrites tool call IDs to the shape the target API accepts. Each call stays paired with its result.
Every request drops failed and aborted assistant batches and adds a
No result providedresult for an unanswered tool call. The saved history does not change.RUN_FINISHEDandRUN_ERRORmetadata now merge onto the assistant messages of that model call inStreamProcessor.Anthropic: the new
allowEmptySignatureoption (defaultfalse) replays unsigned thinking from the same source, for gateways. The newprovideroption names a gateway.Azure OpenAI reports the
azure-openai-responsesAPI.Gemini, Mistral, and Bedrock Converse send same-source thinking back to the model, in block order.
withPersistencewrites the source and the stop reason onto streaming snapshot rows.Adapter authors get
transformMessagesForReplay()andhashToolCallId()from@tanstack/ai/adapter-internals.
Send
chat({ toolChoice })to each provider.Anthropic and Bedrock Converse. Claude Fable 5.1, Mythos 5.1, Opus 5.5, and Sonnet 5.5 reject a forced tool, and every Claude model rejects one while thinking is on. On those,
'required'and a named tool fall back toauto.Bedrock Converse has no
none.'none'sends no tool config. Bedrock refuses tool calls in the history without a tool config, so the history goes out as text.Gemini maps the choice to
functionCallingConfig(AUTO,NONE,ANY, andallowedFunctionNames). With only provider tools, such as Google Search, it sends no tool config.OpenAI and Azure OpenAI (Responses), Grok, Mistral, and OpenRouter (Chat Completions and Responses) send their own
tool_choiceshape.
Images in tool results now reach the model on Chat Completions and on OpenRouter Responses.
Chat Completions adapters (OpenAI and the adapters built on
@tanstack/openai-base, and OpenRouter chat) send the tool text in the tool message and the images in one user message after the tool results. Before this fix, OpenAI sent the parts as a JSON string in the tool text, and OpenRouter dropped the images.OpenRouter Responses sends the images as
input_imageparts of the function call output. Before this fix, it dropped them.On Chat Completions, a result with only images gets the tool text
(see attached image).A model without image input gets no images. A result with only images then has the text
(image omitted: this model has no image input). The OpenAI Responses adapter now uses this text too.Other media (audio, video, documents) in a tool result with images is not sent on these adapters.
OpenRouter chat with automatic prompt caching on an
anthropic/*model puts the cache marker on the tool result before that image message, because the image message has no text block.
- 0.21.x · maintenance
0.21.5
9 Oct 2026Only updates a dependency:@tanstack/ai 0.68.0
Dependency tracking · free for 3 projects
Know when your own dependencies move
@tanstack/ai-openrouter shipped a new major in the last 12 months, among 98 releases. Inlinr shows which of your projects are behind, and what the upgrade breaks.
Advisories reach you
An email when a critical or high advisory affects a project, before the next install.
Upgrades checked first
Peer ranges, Node, ESM-only and types changes read before you bump.
Only what matters
Majors and breaking notes stay; patches and sibling bumps fold into one line.
Registry facts last checked 22 Oct 2026. Releases and advisories last read 23 Oct 2026.