@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 +12 -0
- package/README.md +38 -13
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -5
- package/package.json +3 -3
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:
|
|
8
|
+
# TanStack AI SDK Bridge: a migration stepping stone from Vercel AI SDK providers to TanStack AI
|
|
9
9
|
|
|
10
|
-
|
|
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
|
-
|
|
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
|
[](https://www.npmjs.com/package/@amjadiqbal/tanstack-ai-sdk-bridge)
|
|
15
|
-
[](https://tanstack.com/ai)
|
|
36
|
+
[](https://tanstack.com/ai)
|
|
16
37
|
[](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
|
|
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,
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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:**
|
|
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/
|
|
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": "
|
|
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
|
|
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.
|
|
4
|
-
"description": "
|
|
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": "
|
|
60
|
+
"@tanstack/ai": ">=1.0.0 <1.1.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@ai-sdk/anthropic": "4.0.86",
|