- Update model metadata from OpenRouter API (#581)
- Updated dependencies [
2e0e2eb]:- @tanstack/ai@0.19.0
- @tanstack/ai-client@0.11.0
- @tanstack/openai-base@0.3.2
- Updated dependencies [
a9d1916,e810153]:- @tanstack/ai@0.18.0
- @tanstack/ai-client@0.10.0
- @tanstack/openai-base@0.3.1
-
Streaming structured output across the OpenAI-compatible providers, an OpenAI Chat Completions sibling adapter, a summarize-subsystem unification, and the decoupling of
@tanstack/ai-openrouterfrom the shared OpenAI base. (#527)- New
chat({ outputSchema, stream: true })overload returningStructuredOutputStream<InferSchemaType<TSchema>>. The stream yields raw JSON deltas viaTEXT_MESSAGE_CONTENTplus a terminalCUSTOMstructured-output.completeevent whosevalue.objectis typed against the caller's schema with no helper or cast required. StructuredOutputStream<T>is a discriminated union over three taggedCUSTOMvariants —structured-output.complete<T>,approval-requested, andtool-input-available(newApprovalRequestedEvent/ToolInputAvailableEventinterfaces exported from@tanstack/ai). Narrowing onchunk.type === 'CUSTOM' && chunk.name === '<literal>'resolveschunk.valueto the exact shape per variant. The bareCustomEvent(withvalue: any) is deliberately excluded to keep the narrow from collapsing toany; user-emitted events via theemitCustomEventcontext API still flow at runtime and are documented as a small residual gap.- Activity-layer hardening: always-finalise after the stream loop (no silent hangs on missing
finishReason), typedRUN_ERRORon empty content, mid-stream provider errors terminate cleanly, schema-validation failures carryrunId / model / timestamp. fallbackStructuredOutputStreamin the activity layer is the single source of truth for adapters that don't implementstructuredOutputStreamnatively;BaseTextAdapterno longer ships a default.ChatStreamSummarizeAdapter.summarizeStreamaccumulates summary text and emits a terminalCUSTOMgeneration:resultevent before the finalRUN_FINISHED. FixesuseSummarizenever populatingresultover streaming connections (the client only setsresulton that specific CUSTOM event).SummarizationOptionsis now generic inTProviderOptionsandmodelOptionsis plumbed through end-to-end (previously silently dropped byrunSummarize/runStreamingSummarize).
useChat(React/Vue/Solid) andcreateChat(Svelte) now accept anoutputSchemaoption mirroringchat({ outputSchema })on the server. When supplied, the hook's return adds two managed reactive fields:partial— the live progressive object, typedDeepPartial<InferSchemaType<typeof outputSchema>>. Updated fromTEXT_MESSAGE_CONTENTdeltas viaparsePartialJSON. Resets on every new run.final— the validated terminal payload from thestructured-output.completeevent, typedInferSchemaType<typeof outputSchema> | null.nulluntil the run completes.
Both fields are typed against the schema with no helper or cast — each hook is generic on
TSchemaand conditionally adds the fields to the return type. WithoutoutputSchema, the return type is unchanged. Works the same for streaming and non-streaming endpoints — for non-streaming,partialstays{}andfinalsnaps when the single terminal event arrives. Reasoning text and tool calls aren't surfaced as separate hook fields — they're already onmessages[…].parts(asThinkingPart,ToolCallPart,ToolResultPart), same as a normal chat. WhenoutputSchemais set, the assistant'sTextPartcontains the raw JSON the model produced; filtertextparts out of your message renderer and let the structured view (driven bypartial/final) replace it.Reactivity primitive per framework:
Framework partialtypefinaltypeReact ( @tanstack/ai-react)DeepPartial<T>(plain state)T | null(plain state)Vue ( @tanstack/ai-vue)Readonly<ShallowRef<DeepPartial<T>>>Readonly<ShallowRef<T | null>>Solid ( @tanstack/ai-solid)Accessor<DeepPartial<T>>Accessor<T | null>Svelte ( @tanstack/ai-svelte)readonly partial: DeepPartial<T>(rune-backed getter)readonly final: T | null(rune-backed getter)DeepPartial<T>is exported from each framework package for callers who want to annotate handlers explicitly.-
Package renamed from
@tanstack/ai-openai-compatible(which remains published for pinned lockfiles but receives no further updates). Imports change:- import { OpenAICompatibleChatCompletionsTextAdapter } from '@tanstack/ai-openai-compatible' + import { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base' - import { OpenAICompatibleResponsesTextAdapter } from '@tanstack/ai-openai-compatible' + import { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base'
-
Centralised
structuredOutputStreamon both bases. Chat Completions usesresponse_format: { type: 'json_schema', strict: true }+stream: true; Responses usestext.format: { type: 'json_schema', strict: true }+stream: true. Subclasses (ai-openai,ai-grok,ai-groq) inherit it; OpenRouter implements its own (see below). -
Base now adopts the
openaiSDK directly and imports types fromopenai/resources/.... The previously-vendored ~720 LOC of wire-format types (ChatCompletion,ResponseStreamEvent, etc.) is removed; consumers that imported wire types from the package should import them from the openai SDK instead. The abstractcallChatCompletion*/callResponse*hooks are gone — the base constructor now takes a pre-builtOpenAIclient (new OpenAIBaseChatCompletionsTextAdapter(model, name, openaiClient)) and callsclient.chat.completions.create/client.responses.createitself. -
New protected
isAbortError(error)hook duck-types abort detection soRUN_ERROR { code: 'aborted' }is emitted consistently across SDK error types — subclasses with proprietary error classes (e.g.@openrouter/sdk'sRequestAbortedError) override. -
Per-chunk
logger.provider(...)debug logging now fires insidestructuredOutputStreamloops, matching the existing pattern inchatStreamfor end-to-end introspection in debug mode.
The other extension hooks (
extractReasoning,extractTextFromResponse,processStreamChunks,makeStructuredOutputCompatible,transformStructuredOutput,mapOptionsToRequest,convertMessage) remain. Groq'sprocessStreamChunksandmakeStructuredOutputCompatibleoverrides (forx_groq.usagepromotion and Groq's structured-output schema quirks) are unchanged.Adapter API Reasoning surface @tanstack/ai-openaiopenaiTextResponses response.reasoning_text.delta+response.reasoning_summary_text.delta(requiresreasoning.summary: 'auto')@tanstack/ai-openaiopenaiChatCompletions(new)Chat Completions reasoning emitted silently — Chat Completions has no reasoning.summaryopt-in@tanstack/ai-grokgrokTextChat Completions delta.reasoning_content(DeepSeek convention; not typed by OpenAI SDK)@tanstack/ai-groqgroqTextChat Completions delta.reasoning(requiresreasoning_format: 'parsed'; not typed by groq-sdk)@tanstack/ai-openrouteropenRouterTextChat Completions delta.reasoningDetails(camelCase)@tanstack/ai-openrouteropenRouterResponsesText(beta)Responses (beta) response.reasoning_text.delta+response.reasoning_summary_text.deltavianormalizeStreamEventAll six emit the contractual
REASONING_*lifecycle (REASONING_START→REASONING_MESSAGE_START→REASONING_MESSAGE_CONTENTdeltas →REASONING_MESSAGE_END→REASONING_END) and close it beforeTEXT_MESSAGE_START. Accumulated reasoning is also surfaced onstructured-output.complete.value.reasoningfor consumers that only subscribe to the terminal event. OpenRouter SDK's proprietaryRequestAbortedErroris mapped (alongside DOMAbortError) tocode: 'aborted'in the two openrouter adapters.@tanstack/ai-openaialso exports a newOpenAIChatCompletionsTextAdapter/openaiChatCompletions/createOpenaiChatCompletionsfactory — a sibling to the existing Responses adapter for callers who want the older/v1/chat/completionswire format against the OpenAI SDK.OpenRouter ships its own SDK (
@openrouter/sdk) with a camelCase shape, so inheriting from the OpenAI-shaped base forced a snake_case ↔ camelCase round-trip on every request and stream event. ai-openrouter now extendsBaseTextAdapterdirectly and inlines its own stream processors (OpenRouterTextAdapterfor chat-completions,OpenRouterResponsesTextAdapterfor the Responses beta), reading OpenRouter's camelCase types natively. The@tanstack/openai-baseandopenaidependencies are removed from ai-openrouter; only@openrouter/sdk,@tanstack/ai, and@tanstack/ai-utilsremain. The ~300 LOC of inbound/outbound shape converters (toOpenRouterRequest,toChatCompletion,adaptOpenRouterStreamChunks,toSnakeResponseResult, …) are gone. Internal: duck-typedas { ... }casts on stream chunks inOpenRouterResponsesTextAdapterare replaced with direct narrowing via the SDK's discriminated unions.Public OpenRouter API is unchanged:
openRouterText,openRouterResponsesText,createOpenRouterText,createOpenRouterResponsesText, the OpenRouter tool factories, provider routing surface (provider,models,plugins,variant,transforms), app attribution headers (httpReferer,appTitle),:variantmodel suffixing,RequestAbortedErrorpropagation, and the OpenRouter-specific structured-output null-preservation all behave the same.ai-ollamaremains onBaseTextAdapterdirectly — its native API uses a different wire format from Chat Completions and was never on the shared base.Anthropic, Gemini, Ollama, and OpenRouter previously each shipped a bespoke 200–300 LOC summarize adapter. They now construct a
ChatStreamSummarizeAdapter(formerlyChatStreamWrapperAdapter, renamed and exported from@tanstack/ai/activities) wrapping their own text adapter, matching the existing OpenAI/Grok pattern. Removes ~600 LOC of duplicated logic across the six providers and ensures behavioural parity.Bespoke
*SummarizeProviderOptionsinterfaces (e.g.OpenAISummarizeProviderOptions,AnthropicSummarizeProviderOptions,GeminiSummarizeProviderOptions,OllamaSummarizeProviderOptions,OpenRouterSummarizeProviderOptions) are removed from the provider packages' public exports. Consumers who imported them should switch to inferring the type from the adapter (InferTextProviderOptions<typeof adapter>) or remove the explicit annotation (it'll be inferred from the adapter argument).SummarizeAdapterinterface methods are now generic inTProviderOptions.summarizeandsummarizeStreampreviously tookSummarizationOptions(defaulted, somodelOptionswas effectivelyRecord<string, any>regardless of the adapter's typed shape). They now takeSummarizationOptions<TProviderOptions>. Source-compatible for callers that didn't specify the generic; type-tighter for implementers and downstream consumers.SummarizationOptions,SummarizeAdapter,BaseSummarizeAdapter, andChatStreamSummarizeAdapterpreviously had a mixedRecord<string, any>/Record<string, unknown>/objectset of defaults forTProviderOptions; they now uniformly default toRecord<string, unknown>. - New
- Updated dependencies [
98979f7,02527c2]:- @tanstack/ai@0.17.0
- @tanstack/openai-base@0.3.0
- @tanstack/ai-client@0.9.2
- Updated dependencies [
87f305c]:- @tanstack/ai@0.16.0
- @tanstack/ai-client@0.9.1
- @tanstack/openai-base@0.2.1
-
Internal refactor: every provider now delegates
getApiKeyFromEnv/generateId/transformNullsToUndefined/ModelMetahelpers to the new@tanstack/ai-utilspackage.ai-openaiandai-grokadditionally inherit OpenAI-compatible adapter base classes (Chat Completions / Responses text, image, summarize, transcription, TTS, video) from the new@tanstack/openai-basepackage;ai-groqkeeps its ownBaseTextAdapter-derived text adapter (Groq uses thegroq-sdk, not the OpenAI SDK) but consumes@tanstack/openai-base's schema converter and tool converters. The remaining providers (ai-anthropic,ai-gemini,ai-ollama,ai-openrouter,ai-fal,ai-elevenlabs) only consume@tanstack/ai-utilsbecause they speak provider-native protocols, not OpenAI-compatible ones. No breaking changes — all public APIs remain identical. (#409) -
Updated dependencies [
27c9aeb,27c9aeb]:- @tanstack/ai-utils@0.2.0
- @tanstack/openai-base@0.2.0
- Updated dependencies [
a4e2c55,82078bd,b2d3cc1,13cceae]:- @tanstack/ai@0.15.0
- @tanstack/ai-client@0.9.0
-
refactor(ai, ai-openai): narrow error handling before logging (#465)
catch (error: any)sites instream-to-response.ts,activities/stream-generation-result.ts, andactivities/generateVideo/index.tsare now narrowed tounknownand funnel through a sharedtoRunErrorPayload(error, fallback)helper that extractsmessage/codewithout leaking the original error object (which can carry request state from an SDK).Replaced four
console.errorcalls in the OpenAI text adapter'schatStreamcatch block that dumped the full error object to stdout. SDK errors can carry the original request including auth headers, so the library now logs only the narrowed{ message, code }payload via the internal logger — any user-supplied logger receives the sanitized shape, not the raw SDK error. -
Tighten
GeneratedImageandGeneratedAudioto enforce exactly one ofurlorb64Jsonvia a mutually-exclusiveGeneratedMediaSourceunion. (#463)Both types previously declared
url?andb64Json?as independently optional, which allowed meaningless{}values and objects that set both fields. They now require exactly one:type GeneratedMediaSource = | { url: string; b64Json?: never } | { b64Json: string; url?: never }
Existing read patterns like
img.url || \data:image/png;base64,${img.b64Json}`continue to work unchanged. The only runtime-visible change is that the@tanstack/ai-openrouterand@tanstack/ai-falimage adapters no longer populateurlwith a synthesizeddata:image/png;base64,...URI when the provider returns base64 — they return{ b64Json }only. Consumers that want a data URI should build it fromb64Json` at render time. -
Updated dependencies [
54523f5,54523f5,af9eb7b,008f015,54523f5]:- @tanstack/ai@0.14.0
- @tanstack/ai-client@0.8.0
-
Wire each adapter's text, summarize, image, speech, transcription, and video paths through the new
InternalLoggerfrom@tanstack/ai/adapter-internals:logger.request(...)before each SDK call,logger.provider(...)for every chunk received, andlogger.errors(...)in catch blocks. Migrates all pre-existing ad-hocconsole.*calls in adapter catch blocks (including the OpenAI and ElevenLabs realtime adapters) onto the structured logger. No adapter factory or config-shape changes. (#467) -
Updated dependencies [
c1fd96f]:- @tanstack/ai@0.13.0
- @tanstack/ai-client@0.7.14
- Expose provider-tool factories (
webSearchTool,webSearchPreviewTool,fileSearchTool,imageGenerationTool,codeInterpreterTool,mcpTool,computerUseTool,localShellTool,shellTool,applyPatchTool,customTool) on a new/toolssubpath. Each factory returns a branded type (e.g.OpenAIWebSearchTool) gated against the selected model'ssupports.toolslist.supports.toolswas expanded to includeweb_search_preview,local_shell,shell,apply_patch. Existing factory signatures and runtime behavior are unchanged. (#466)
- Updated dependencies [
e32583e]:- @tanstack/ai@0.12.0
- @tanstack/ai-client@0.7.13
-
Align stream output with
@tanstack/ai's AG-UI-compliant event shapes: emitREASONING_*events alongsideSTEP_*, threadthreadId/runIdthroughRUN_STARTED/RUN_FINISHED, and return flatRunErrorEventshape. Cast raw events through an internalasChunkhelper so they line up with the re-exported@ag-ui/coreEventTypeenum. No changes to adapter factory signatures or config shapes. (#474) -
Updated dependencies [
12d43e5,12d43e5,1d6f3be]:- @tanstack/ai@0.11.0
- @tanstack/ai-client@0.7.11
-
fix(ai, ai-openai, ai-gemini, ai-ollama): normalize null tool input to empty object (#430)
When a model produces a
tool_useblock with no input,JSON.parse('null')returnsnullwhich fails Zod schema validation and silently kills the agent loop. Normalize null/non-object parsed tool input to{}inexecuteToolCalls,ToolCallManager.completeToolCall,ToolCallManager.executeTools, and the OpenAI/Gemini/Ollama adapterTOOL_CALL_ENDemissions. The Anthropic adapter already had this fix. -
Updated dependencies [
c780bc1]:- @tanstack/ai@0.10.3
- @tanstack/ai-client@0.7.10
- Update model metadata from OpenRouter API (#433)
-
Add code mode and isolate packages for secure AI code execution (#362)
Also includes fixes for Ollama tool call argument streaming and usage reporting, OpenAI realtime adapter handling of missing call_id/item_id, realtime client guards for missing toolCallId, and new DevtoolsChatMiddleware type export from ai-event-client.
-
Updated dependencies [
54abae0]:- @tanstack/ai@0.10.0
- @tanstack/ai-client@0.7.7
- Updated dependencies [
842e119]:- @tanstack/ai@0.9.0
- @tanstack/ai-client@0.7.3
- Updated dependencies [
f62eeb0]:- @tanstack/ai@0.8.0
- @tanstack/ai-client@0.7.1
-
feat: add realtime voice chat with OpenAI and ElevenLabs adapters (#300)
Adds realtime voice/text chat capabilities:
- @tanstack/ai:
realtimeToken()function and shared realtime types (RealtimeToken,RealtimeMessage,RealtimeSessionConfig,RealtimeStatus,RealtimeMode,AudioVisualization, events, and error types) - @tanstack/ai-client: Framework-agnostic
RealtimeClientclass with connection lifecycle, audio I/O, message state management, tool execution, andRealtimeAdapter/RealtimeConnectioninterfaces - @tanstack/ai-openai:
openaiRealtime()client adapter (WebRTC) andopenaiRealtimeToken()server token adapter with support for semantic VAD, multiple voices, and all realtime models - @tanstack/ai-elevenlabs:
elevenlabsRealtime()client adapter (WebSocket) andelevenlabsRealtimeToken()server token adapter for ElevenLabs conversational AI agents - @tanstack/ai-react:
useRealtimeChat()hook with reactive state for status, mode, messages, pending transcripts, audio visualization levels, VAD control, text/image input, and interruptions - Docs: Realtime Voice Chat guide and full API reference for all realtime classes, interfaces, functions, and type aliases
- @tanstack/ai:
- Updated dependencies [
86be1c8]:- @tanstack/ai@0.7.0
- @tanstack/ai-client@0.7.0
-
re-release adapter packages (#263)
-
add multiple modalities support to the client (#263)
-
Updated dependencies [
0158d14]:- @tanstack/ai@0.4.0
-
feat: Add AG-UI protocol events to streaming system (#244)
All text adapters now emit AG-UI protocol events only:
RUN_STARTED/RUN_FINISHED- Run lifecycle eventsTEXT_MESSAGE_START/TEXT_MESSAGE_CONTENT/TEXT_MESSAGE_END- Text message streamingTOOL_CALL_START/TOOL_CALL_ARGS/TOOL_CALL_END- Tool call streaming
Only AG-UI event types are supported; previous legacy chunk formats (
content,tool_call,done, etc.) are no longer accepted.
- Updated dependencies [
e52135f]:- @tanstack/ai@0.3.0
- allows additional configuration options when creating an openAI client (#245)
-
Fix up model names for OpenAI and release the new response APIs (#188)
-
fix up readmes (#188)
-
Updated dependencies [
181e0ac,181e0ac]:- @tanstack/ai@0.2.1
- Updated dependencies [
c5df33c]:- @tanstack/ai@0.2.0
- add support for gpt 5.2 models (#166)
- Split up adapters for better tree shaking into separate functionalities (#137)
- Updated dependencies [
8d77614]:- @tanstack/ai@0.1.0
-
Fix reasoning token streaming for
gpt-5-miniandgpt-5-nanomodels (#94)- Added
OpenAIReasoningOptionsto type definitions forgpt-5-miniandgpt-5-nanomodels - Fixed
summaryoption placement inOpenAIReasoningOptions(moved insidereasoningobject to match OpenAI SDK) - Added handler for
response.reasoning_summary_text.deltaevents to stream reasoning summaries - Added model-specific
reasoning.summarytypes:conciseonly available forcomputer-use-preview - Added
OpenAIReasoningOptionsWithConciseforcomputer-use-previewmodel
- Added
-
Updated dependencies [
52c3172]:- @tanstack/ai@0.0.3