@amjadiqbal/tanstack-ai-sdk-bridge 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.1.2] - 2026-10-23
9
+
10
+ ### Changed
11
+
12
+ - Positioned as a migration stepping stone, following TanStack AI maintainers' feedback ([TanStack/ai#1884](https://github.com/TanStack/ai/pull/1884)): the README now starts with TanStack AI's migration guide, a table of the native adapter for each tested provider, a third-party notice, and the list of what the bridge loses versus a native adapter.
13
+ - **Peer range narrowed to `@tanstack/ai >=1.0.0 <1.1.0`** (the versions tested), because the bridge imports `@tanstack/ai/adapter-internals`, which has no stability promise. Newer TanStack AI versions are flagged at install time until a tested release widens the range.
14
+ - `aiSdkSpeech`: Ogg audio is reported as `opus` only when the stream is Opus, otherwise as `ogg`; an `audio/ogg` header maps to `ogg`, and the content type for `opus` and `ogg` is `audio/ogg`.
15
+
16
+ ### Documentation
17
+
18
+ - The `application/octet-stream` fallback for speech `contentType` is documented.
19
+
8
20
  ## [0.1.1] - 2026-10-23
9
21
 
10
22
  ### Fixed
package/README.md CHANGED
@@ -5,14 +5,35 @@
5
5
  </picture>
6
6
  </p>
7
7
 
8
- # TanStack AI SDK Bridge: use Vercel AI SDK providers with TanStack AI
8
+ # TanStack AI SDK Bridge: a migration stepping stone from Vercel AI SDK providers to TanStack AI
9
9
 
10
- Use any Vercel AI SDK provider (`@ai-sdk/*` and community providers) as a [TanStack AI](https://tanstack.com/ai) adapter. The bridge reads the AI SDK provider spec (v2, v3 and v4), so a provider package works with `chat()`, `embed()`, `generateTranscription()`, `generateSpeech()`, `generateImage()` and `rerank()` without a dedicated TanStack adapter. It is also the shortest path for moving an AI SDK app to TanStack AI: keep your provider setup and change the rest step by step.
10
+ Run your existing Vercel AI SDK provider setup (`@ai-sdk/*` and community providers) inside [TanStack AI](https://tanstack.com/ai) **while you move to native TanStack AI adapters**. Wrap a model, e.g. `aiSdkText(deepseek('deepseek-chat'))`, and `chat()`, `embed()`, `generateTranscription()`, `generateSpeech()`, `generateImage()` and `rerank()` accept it. You can move your server routes and client to TanStack AI first, ship, and then swap each provider for its native adapter.
11
11
 
12
- Tested with: `@ai-sdk/deepseek`, `@ai-sdk/anthropic`, `@ai-sdk/openai`, `@ai-sdk/togetherai`, `@ai-sdk/cerebras`, `@ai-sdk/fireworks`, `@ai-sdk/cohere` (rerank), `ollama-ai-provider-v2` and `@ai-sdk/openai-compatible` 1.x, 2.x and 3.x (AI SDK 5, 6 and 7). See [What is tested](#what-is-tested).
12
+ **Use native TanStack AI adapters wherever one exists.** Start from TanStack AI's [migration guide from the Vercel AI SDK](https://tanstack.com/ai/latest/docs/migration/migration-from-vercel-ai) and its [adapter list](https://tanstack.com/ai/latest/docs/adapters/openai). For the providers this package was tested with:
13
+
14
+ | AI SDK provider | Native TanStack AI adapter |
15
+ |---|---|
16
+ | `@ai-sdk/openai` | `openaiText`, `openaiEmbedding`, `openaiImage`, `openaiSpeech`, `openaiTranscription` from `@tanstack/ai-openai` |
17
+ | `@ai-sdk/anthropic` | `anthropicText` from `@tanstack/ai-anthropic` |
18
+ | `ollama-ai-provider-v2` | `ollamaText`, `ollamaEmbedding` from `@tanstack/ai-ollama` |
19
+ | `@ai-sdk/cohere` | `cohereRerank`, `cohereEmbedding` from `@tanstack/ai-cohere` |
20
+ | `@ai-sdk/deepseek`, `@ai-sdk/togetherai`, `@ai-sdk/cerebras`, `@ai-sdk/fireworks`, `@ai-sdk/openai-compatible` | `openaiCompatible` from `@tanstack/ai-openai/compatible` ([docs](https://tanstack.com/ai/latest/docs/adapters/openai-compatible)) |
21
+
22
+ > [!IMPORTANT]
23
+ > **Third-party package.** It is not made, listed or endorsed by TanStack; TanStack AI's maintainers [declined to list it](https://github.com/TanStack/ai/pull/1884) in their docs. Problems with a bridged provider are this package's problems, not TanStack AI's: please [open an issue here](https://github.com/amjadiqbal/tanstack-ai-sdk-bridge/issues).
24
+
25
+ ### What you lose versus a native adapter
26
+
27
+ - **No per-model typing**: `modelOptions` and input modalities are not typed per model; you get one generic set of AI SDK call settings.
28
+ - **Reasoning only on AI SDK 7 providers** (spec v4). On AI SDK 5 and 6 providers `chat({ reasoning })` is not sent; reasoning output still streams if the provider returns it.
29
+ - **No provider-executed tools** (a provider's own web search, code execution and the like); only your function tools.
30
+ - **No reasoning signatures across turns**: signed or redacted thinking is not sent back, so providers that need it in later turns get plain text or nothing.
31
+ - **No `wrapFetch`**: TanStack AI's fetch middleware stack does not apply; pass a custom `fetch` to the provider factory instead.
32
+ - **No streaming structured output**: `outputSchema` runs one non-streaming call after the agent loop.
33
+ - **Depends on TanStack AI internals**: it imports `@tanstack/ai/adapter-internals`, which has **no stability promise**. The peer range is pinned to the versions tested (`@tanstack/ai >=1.0.0 <1.1.0`), so an untested TanStack AI version is flagged at install time (npm reports a peer conflict, pnpm a warning) instead of surfacing as a run-time error. A release that widens the range follows after testing.
13
34
 
14
35
  [![npm](https://img.shields.io/npm/v/@amjadiqbal/tanstack-ai-sdk-bridge?style=flat-square)](https://www.npmjs.com/package/@amjadiqbal/tanstack-ai-sdk-bridge)
15
- [![TanStack AI](https://img.shields.io/badge/TanStack%20AI-1.x-8B5CF6?style=flat-square)](https://tanstack.com/ai)
36
+ [![TanStack AI](https://img.shields.io/badge/TanStack%20AI-1.0.x-8B5CF6?style=flat-square)](https://tanstack.com/ai)
16
37
  [![License](https://img.shields.io/npm/l/@amjadiqbal/tanstack-ai-sdk-bridge?style=flat-square)](LICENSE.md)
17
38
 
18
39
  ```ts
@@ -20,11 +41,13 @@ import { chat } from '@tanstack/ai'
20
41
  import { aiSdkText } from '@amjadiqbal/tanstack-ai-sdk-bridge'
21
42
  import { deepseek } from '@ai-sdk/deepseek'
22
43
 
44
+ // Step 1 of a migration: keep the AI SDK provider, move to TanStack AI's chat().
23
45
  const stream = chat({
24
46
  adapter: aiSdkText(deepseek('deepseek-chat')),
25
47
  messages: [{ role: 'user', content: 'Hello!' }],
26
48
  threadId: 'thread-1',
27
49
  })
50
+ // Step 2: swap to the native adapter (openaiCompatible from @tanstack/ai-openai/compatible for DeepSeek).
28
51
  ```
29
52
 
30
53
  - **Text**: streaming chat, tool calls (TanStack's agent loop runs your tools), reasoning, structured output (`outputSchema`), token usage, abort (see [Abort](#abort)).
@@ -32,13 +55,15 @@ const stream = chat({
32
55
  - **AI SDK 5, 6 and 7 providers**: provider spec v2, v3 and v4 are all read.
33
56
  - **No AI SDK runtime code**: the built package imports only `@tanstack/ai`. `@ai-sdk/provider` is a dependency because the published type declarations use its model types; nothing from it runs. No `ai` package needed.
34
57
 
58
+ Tested with (local wire-level mock, no live APIs): `@ai-sdk/deepseek`, `@ai-sdk/anthropic`, `@ai-sdk/openai`, `@ai-sdk/togetherai`, `@ai-sdk/cerebras`, `@ai-sdk/fireworks`, `@ai-sdk/cohere` (rerank), `ollama-ai-provider-v2` and `@ai-sdk/openai-compatible` 1.x, 2.x and 3.x (AI SDK 5, 6 and 7). See [What is tested](#what-is-tested).
59
+
35
60
  ## Install
36
61
 
37
62
  ```bash
38
- npm i @amjadiqbal/tanstack-ai-sdk-bridge @tanstack/ai
63
+ npm i @amjadiqbal/tanstack-ai-sdk-bridge @tanstack/ai@1.0
39
64
  ```
40
65
 
41
- Then the AI SDK provider you want, for example `npm i @ai-sdk/deepseek`. Requires `@tanstack/ai` 1.x and Node 20+.
66
+ Then the AI SDK provider you already use, for example `npm i @ai-sdk/deepseek`. Requires `@tanstack/ai` 1.0.x (tested: 1.0.0) and Node 20+.
42
67
 
43
68
  ## Usage
44
69
 
@@ -179,7 +204,7 @@ Activity-specific `modelOptions`, on top of `providerOptions` and `headers`:
179
204
  | Adapter | `modelOptions` | Notes |
180
205
  |---|---|---|
181
206
  | `aiSdkTranscription` | `mediaType` | Media type of the audio when it can't be read from a `Blob`/`File` type or a data URL. Default `audio/mpeg`. `language`, `prompt` and `responseFormat` are not sent (the AI SDK spec has no such fields); the result has `text`, plus `segments`, `language` and `duration` when the provider returns them. An `http(s)` URL as `audio` is downloaded with the global `fetch`, not the provider's `fetch`. |
182
- | `aiSdkSpeech` | `instructions`, `language` | Sent as the AI SDK `instructions` and `language` call options. The result's `format` and `contentType` come from the provider's `content-type` header, else from the audio bytes (MP3, WAV, Ogg, FLAC, AAC), else the `format` you asked for, else `unknown`. |
207
+ | `aiSdkSpeech` | `instructions`, `language` | Sent as the AI SDK `instructions` and `language` call options. The result's `format` and `contentType` come from the provider's `content-type` header, else from the audio bytes (MP3, WAV, FLAC, AAC, and Ogg: `opus` when the Ogg stream is Opus, else `ogg`), else the `format` you asked for, else `unknown`. When the format is `unknown` (or a format with no known media type), `contentType` is `application/octet-stream`. |
183
208
  | `aiSdkImage` | `aspectRatio`, `seed`, `mask` | Sent as the AI SDK call options. `mask` is an AI SDK image file (`{ type: 'file', mediaType, data }` or `{ type: 'url', url }`). Image parts in the prompt become input `files`. Input images and masks need a spec v3/v4 model; v2 models throw. When the model does not report `maxImagesPerCall`, one image is requested per call (the AI SDK's own default) until `numberOfImages` is reached. |
184
209
 
185
210
  Every function takes an optional second argument `{ name }`, the adapter name TanStack AI shows in devtools and usage. It defaults to the provider id before the first dot (`deepseek.chat` becomes `deepseek`).
@@ -194,7 +219,7 @@ Every function takes an optional second argument `{ name }`, the adapter name Ta
194
219
 
195
220
  ## Migrating from the Vercel AI SDK
196
221
 
197
- TanStack's own [migration guide](https://tanstack.com/ai/latest/docs/migration/migration-from-vercel-ai) swaps your provider packages for TanStack adapters. With the bridge you can skip that step, and keep providers that have no TanStack adapter:
222
+ Follow TanStack's own [migration guide](https://tanstack.com/ai/latest/docs/migration/migration-from-vercel-ai); it ends with your provider packages swapped for native TanStack AI adapters. The bridge lets you do that last step later, one provider at a time:
198
223
 
199
224
  | Vercel AI SDK | TanStack AI with the bridge |
200
225
  |---|---|
@@ -209,7 +234,7 @@ TanStack's own [migration guide](https://tanstack.com/ai/latest/docs/migration/m
209
234
  | `useChat` from `@ai-sdk/react` | `useChat` from `@tanstack/ai-react` |
210
235
  | `result.toUIMessageStreamResponse()` | `toServerSentEventsResponse(stream)` |
211
236
 
212
- A practical order: move the server route and client first with the bridge, ship, then replace a bridged provider with a native TanStack adapter later if one exists and you need its typed per-model options.
237
+ Order: move the server route and client first with the bridge, ship, then replace each bridged provider with its native TanStack AI adapter (see the table at the top), and remove this package when none is left.
213
238
 
214
239
  ## What is tested
215
240
 
@@ -233,13 +258,13 @@ Everything below ran on 2026-10-23 with `@tanstack/ai` 1.0.0. **No live provider
233
258
 
234
259
  ## Limits
235
260
 
236
- - **Provider-executed tools** (an AI SDK provider's own web search, code execution and the like) are not forwarded; function tools are.
237
- - **Reasoning signatures** are not sent back, so providers that need signed thinking blocks in later turns (Anthropic extended thinking with tools) receive the reasoning as plain text or drop it.
238
- - `chat({ wrapFetch })` is not applied; pass a custom `fetch` to the provider factory (`createDeepSeek({ fetch })`).
261
+ The [What you lose versus a native adapter](#what-you-lose-versus-a-native-adapter) list at the top, plus:
262
+
263
+
239
264
  - Provider file handles (`{ type: 'file' }` sources) are rejected; send images and documents as data or URLs.
240
265
  - Transcription `language`, `prompt` and `responseFormat` are not sent; see the options table.
241
266
  - Embeddings cannot be aborted (TanStack AI's `embed()` has no signal).
242
- - **TanStack AI internals:** like TanStack's own adapters, the bridge imports helpers (`transformMessagesForReplay`, `tanstackMetadata`, `toRunErrorPayload`) from `@tanstack/ai/adapter-internals`, the subpath TanStack's [community adapter guide](https://tanstack.com/ai/latest/docs/community-adapters/guide) points adapters to. It is not covered by the same stability promise as the main entry. Tested with `@tanstack/ai` 1.0.0; the peer range is `^1.0.0`. If a 1.x release breaks it, please open an issue.
267
+ - **TanStack AI internals:** the bridge imports `transformMessagesForReplay`, `tanstackMetadata` and `toRunErrorPayload` from `@tanstack/ai/adapter-internals`. That subpath has no stability promise (TanStack AI's maintainers say so), so the peer range is `>=1.0.0 <1.1.0`, the versions tested. Newer TanStack AI releases are flagged at install time until a tested release widens the range.
243
268
  - AI SDK reranking models report no token usage, so `rerank()` usage is zero.
244
269
  - Video, audio generation and realtime models are not bridged yet.
245
270
 
package/dist/index.d.ts CHANGED
@@ -104,7 +104,8 @@ interface AiSdkSpeechModelOptions extends AiSdkCallOptions {
104
104
  /**
105
105
  * TanStack AI text-to-speech adapter around an AI SDK speech model.
106
106
  * `format` in the result is what the provider returned (content-type header or audio bytes),
107
- * falling back to the requested format, else `unknown`.
107
+ * falling back to the requested format, else `unknown`. `contentType` is the header value, else the media type of the
108
+ * format, else `application/octet-stream`.
108
109
  */
109
110
  export declare class AiSdkSpeechAdapter<TModel extends string = string> extends BaseTTSAdapter<TModel, AiSdkSpeechModelOptions> {
110
111
  readonly name: string;
package/dist/index.js CHANGED
@@ -301,7 +301,8 @@ const SPECS$2 = [
301
301
  ];
302
302
  const CONTENT_TYPES = {
303
303
  mp3: "audio/mpeg",
304
- opus: "audio/opus",
304
+ opus: "audio/ogg",
305
+ ogg: "audio/ogg",
305
306
  aac: "audio/aac",
306
307
  flac: "audio/flac",
307
308
  wav: "audio/wav",
@@ -314,7 +315,7 @@ const FORMATS_BY_TYPE = {
314
315
  "audio/x-wav": "wav",
315
316
  "audio/wave": "wav",
316
317
  "audio/opus": "opus",
317
- "audio/ogg": "opus",
318
+ "audio/ogg": "ogg",
318
319
  "audio/aac": "aac",
319
320
  "audio/flac": "flac",
320
321
  "audio/x-flac": "flac",
@@ -328,11 +329,11 @@ function headerValue(headers, name) {
328
329
  function formatFromContentType(type) {
329
330
  return type ? FORMATS_BY_TYPE[type.toLowerCase()] : void 0;
330
331
  }
331
- /** Recognises MP3, WAV, Ogg/Opus, FLAC and ADTS AAC from their first bytes. */
332
+ /** Recognises MP3, WAV, Ogg (Opus or other), FLAC and ADTS AAC from their first bytes. */
332
333
  function formatFromBytes(bytes) {
333
334
  const ascii = (start, text) => [...text].every((char, i) => bytes[start + i] === char.charCodeAt(0));
334
335
  if (ascii(0, "RIFF") && ascii(8, "WAVE")) return "wav";
335
- if (ascii(0, "OggS")) return "opus";
336
+ if (ascii(0, "OggS")) return ascii(27 + (bytes[26] ?? 0), "OpusHead") ? "opus" : "ogg";
336
337
  if (ascii(0, "fLaC")) return "flac";
337
338
  if (ascii(0, "ID3")) return "mp3";
338
339
  const b0 = bytes[0] ?? 0;
@@ -342,7 +343,8 @@ function formatFromBytes(bytes) {
342
343
  /**
343
344
  * TanStack AI text-to-speech adapter around an AI SDK speech model.
344
345
  * `format` in the result is what the provider returned (content-type header or audio bytes),
345
- * falling back to the requested format, else `unknown`.
346
+ * falling back to the requested format, else `unknown`. `contentType` is the header value, else the media type of the
347
+ * format, else `application/octet-stream`.
346
348
  */
347
349
  var AiSdkSpeechAdapter = class extends BaseTTSAdapter {
348
350
  name;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@amjadiqbal/tanstack-ai-sdk-bridge",
3
- "version": "0.1.1",
4
- "description": "Use any Vercel AI SDK provider (@ai-sdk/* and community providers) as a TanStack AI adapter: streaming chat with tools, reasoning and structured output, plus embeddings, transcription, speech, image generation and rerank. Supports AI SDK provider spec v2, v3 and v4.",
3
+ "version": "0.1.2",
4
+ "description": "Third-party migration stepping stone: run Vercel AI SDK provider models (@ai-sdk/* and community providers) inside TanStack AI while you move to native TanStack AI adapters. Chat with tools, reasoning and structured output, plus embeddings, transcription, speech, image and rerank. Not affiliated with TanStack.",
5
5
  "keywords": [
6
6
  "tanstack",
7
7
  "tanstack-ai",
@@ -57,7 +57,7 @@
57
57
  "@ai-sdk/provider": "^4.0.32"
58
58
  },
59
59
  "peerDependencies": {
60
- "@tanstack/ai": "^1.0.0"
60
+ "@tanstack/ai": ">=1.0.0 <1.1.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@ai-sdk/anthropic": "4.0.86",