@tanstack/ai 0.52.3 → 0.54.0
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/README.md +14 -13
- package/dist/esm/activities/chat/index.js +5 -3
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/generateLiveVideo/adapter.d.ts +69 -0
- package/dist/esm/activities/generateLiveVideo/adapter.js +23 -0
- package/dist/esm/activities/generateLiveVideo/adapter.js.map +1 -0
- package/dist/esm/activities/generateLiveVideo/index.d.ts +99 -0
- package/dist/esm/activities/generateLiveVideo/index.js +162 -0
- package/dist/esm/activities/generateLiveVideo/index.js.map +1 -0
- package/dist/esm/activities/generateVideo/index.js +3 -1
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateWorld/adapter.d.ts +69 -0
- package/dist/esm/activities/generateWorld/adapter.js +23 -0
- package/dist/esm/activities/generateWorld/adapter.js.map +1 -0
- package/dist/esm/activities/generateWorld/index.d.ts +99 -0
- package/dist/esm/activities/generateWorld/index.js +162 -0
- package/dist/esm/activities/generateWorld/index.js.map +1 -0
- package/dist/esm/activities/index.d.ts +8 -2
- package/dist/esm/activities/index.js +11 -7
- package/dist/esm/activities/middleware/types.d.ts +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +2 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/byok/define-provider.d.ts +6 -0
- package/dist/esm/byok/define-provider.js +2 -1
- package/dist/esm/byok/define-provider.js.map +1 -1
- package/dist/esm/byok/get-key.d.ts +7 -0
- package/dist/esm/byok/get-key.js +8 -1
- package/dist/esm/byok/get-key.js.map +1 -1
- package/dist/esm/byok/server.d.ts +1 -1
- package/dist/esm/byok/server.js +2 -2
- package/dist/esm/client.d.ts +4 -2
- package/dist/esm/client.js +3 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +4 -2
- package/dist/esm/index.js +3 -1
- package/dist/esm/middlewares/otel.js +3 -1
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/types.d.ts +112 -0
- package/package.json +2 -2
- package/skills/ai-core/adapter-configuration/SKILL.md +103 -54
- package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +39 -21
- package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +5 -0
- package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +14 -6
- package/skills/ai-core/adapter-configuration/references/grok-adapter.md +33 -25
- package/skills/ai-core/adapter-configuration/references/groq-adapter.md +7 -2
- package/skills/ai-core/adapter-configuration/references/ollama-adapter.md +25 -12
- package/skills/ai-core/adapter-configuration/references/openai-adapter.md +19 -9
- package/skills/ai-core/adapter-configuration/references/openrouter-adapter.md +34 -21
- package/skills/ai-core/ag-ui-protocol/SKILL.md +16 -10
- package/skills/ai-core/chat-experience/SKILL.md +228 -108
- package/skills/ai-core/client-persistence/SKILL.md +21 -9
- package/skills/ai-core/custom-backend-integration/SKILL.md +86 -52
- package/skills/ai-core/debug-logging/SKILL.md +100 -18
- package/skills/ai-core/locks/SKILL.md +35 -7
- package/skills/ai-core/media-generation/SKILL.md +114 -49
- package/skills/ai-core/middleware/SKILL.md +174 -69
- package/skills/ai-core/structured-outputs/SKILL.md +99 -49
- package/skills/ai-core/tool-calling/SKILL.md +245 -158
- package/src/activities/chat/index.ts +6 -7
- package/src/activities/generateLiveVideo/adapter.ts +99 -0
- package/src/activities/generateLiveVideo/index.ts +339 -0
- package/src/activities/generateVideo/index.ts +3 -4
- package/src/activities/generateWorld/adapter.ts +96 -0
- package/src/activities/generateWorld/index.ts +339 -0
- package/src/activities/index.ts +44 -0
- package/src/activities/middleware/types.ts +2 -0
- package/src/activities/summarize/chat-stream-summarize.ts +2 -0
- package/src/byok/define-provider.ts +7 -0
- package/src/byok/get-key.ts +18 -0
- package/src/byok/server.ts +1 -1
- package/src/client.ts +8 -0
- package/src/index.ts +8 -0
- package/src/middlewares/otel.ts +2 -0
- package/src/types.ts +128 -0
package/dist/esm/types.d.ts
CHANGED
|
@@ -1865,6 +1865,118 @@ export interface VideoUrlResult {
|
|
|
1865
1865
|
/** Persisted artifact references for generated assets, when available */
|
|
1866
1866
|
artifacts?: Array<PersistedArtifactRef>;
|
|
1867
1867
|
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Options for world generation (live, prompt-steerable sessions).
|
|
1870
|
+
*
|
|
1871
|
+
* @experimental World generation is an experimental feature and may change.
|
|
1872
|
+
*/
|
|
1873
|
+
export interface WorldGenerationOptions<TProviderOptions extends object = object> {
|
|
1874
|
+
/** The model to use for world generation */
|
|
1875
|
+
model: string;
|
|
1876
|
+
/** Natural-language description of the world or scene */
|
|
1877
|
+
prompt: string;
|
|
1878
|
+
/**
|
|
1879
|
+
* Provider mint options. Reactor resolution/seed/audio are browser
|
|
1880
|
+
* `sendCommand` fields, not token-mint fields.
|
|
1881
|
+
*/
|
|
1882
|
+
modelOptions?: TProviderOptions;
|
|
1883
|
+
/**
|
|
1884
|
+
* Internal logger threaded from the generateWorld() entry point. Adapters
|
|
1885
|
+
* must call logger.request() before the SDK call and logger.errors() in
|
|
1886
|
+
* catch blocks.
|
|
1887
|
+
*/
|
|
1888
|
+
logger: InternalLogger;
|
|
1889
|
+
/**
|
|
1890
|
+
* Effective abort signal composed by the activity from caller `abortSignal`
|
|
1891
|
+
* and/or `timeout`. Adapters should forward this to the provider SDK when
|
|
1892
|
+
* supported. Request-specific — never store on a global client config.
|
|
1893
|
+
*/
|
|
1894
|
+
abortSignal?: AbortSignal;
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Result of world generation. JSON-serializable so a server route can return
|
|
1898
|
+
* it to a browser. The browser uses `token` + `model` to open the live
|
|
1899
|
+
* session (set the prompt, start streaming, steer mid-run).
|
|
1900
|
+
*
|
|
1901
|
+
* @experimental World generation is an experimental feature and may change.
|
|
1902
|
+
*/
|
|
1903
|
+
export interface WorldGenerationResult {
|
|
1904
|
+
/** Unique identifier for this generation */
|
|
1905
|
+
id: string;
|
|
1906
|
+
/** Model used for generation (provider connect slug) */
|
|
1907
|
+
model: string;
|
|
1908
|
+
/** Short-lived session token for the client connection */
|
|
1909
|
+
token: string;
|
|
1910
|
+
/** Token expiry as milliseconds since epoch */
|
|
1911
|
+
expiresAt: number;
|
|
1912
|
+
/** Prompt the client should send when it starts the session */
|
|
1913
|
+
prompt: string;
|
|
1914
|
+
/** Session status after the server half finishes */
|
|
1915
|
+
status: 'ready' | 'waiting';
|
|
1916
|
+
/** Provider session id, when the adapter created one */
|
|
1917
|
+
sessionId?: string;
|
|
1918
|
+
/** Token usage / billing, when the adapter can report it */
|
|
1919
|
+
usage?: TokenUsage;
|
|
1920
|
+
}
|
|
1921
|
+
/**
|
|
1922
|
+
* Options for live generation (prompt-steerable video sessions).
|
|
1923
|
+
*
|
|
1924
|
+
* @experimental Live generation is an experimental feature and may change.
|
|
1925
|
+
*/
|
|
1926
|
+
export interface LiveVideoGenerationOptions<TProviderOptions extends object = object> {
|
|
1927
|
+
/** The model to use for live generation */
|
|
1928
|
+
model: string;
|
|
1929
|
+
/** Natural-language description of the shot or scene */
|
|
1930
|
+
prompt: string;
|
|
1931
|
+
/**
|
|
1932
|
+
* Provider mint options. For fal live this is `tokenDuration`. Reactor
|
|
1933
|
+
* resolution/seed/audio are browser `sendCommand` fields.
|
|
1934
|
+
*/
|
|
1935
|
+
modelOptions?: TProviderOptions;
|
|
1936
|
+
/**
|
|
1937
|
+
* Internal logger threaded from the generateLiveVideo() entry point. Adapters
|
|
1938
|
+
* must call logger.request() before the SDK call and logger.errors() in
|
|
1939
|
+
* catch blocks.
|
|
1940
|
+
*/
|
|
1941
|
+
logger: InternalLogger;
|
|
1942
|
+
/**
|
|
1943
|
+
* Effective abort signal composed by the activity from caller `abortSignal`
|
|
1944
|
+
* and/or `timeout`. Adapters should forward this to the provider SDK when
|
|
1945
|
+
* supported. Request-specific — never store on a global client config.
|
|
1946
|
+
*/
|
|
1947
|
+
abortSignal?: AbortSignal;
|
|
1948
|
+
}
|
|
1949
|
+
/**
|
|
1950
|
+
* Result of live generation. JSON-serializable so a server route can return
|
|
1951
|
+
* it to a browser.
|
|
1952
|
+
*
|
|
1953
|
+
* Reactor: connect with `token` and `model` (the connect slug).
|
|
1954
|
+
* fal: `model` is the WMA app id. Open `wma(model)` through a server proxy
|
|
1955
|
+
* that attaches `FAL_KEY`. Do not send `token` as Key credentials.
|
|
1956
|
+
*
|
|
1957
|
+
* @experimental Live generation is an experimental feature and may change.
|
|
1958
|
+
*/
|
|
1959
|
+
export interface LiveVideoGenerationResult {
|
|
1960
|
+
/** Unique identifier for this generation */
|
|
1961
|
+
id: string;
|
|
1962
|
+
/**
|
|
1963
|
+
* Connect id for the browser client. Reactor: `reactor/helios`.
|
|
1964
|
+
* fal: WMA app id `fal-ai/minimax-h3-max-director`.
|
|
1965
|
+
*/
|
|
1966
|
+
model: string;
|
|
1967
|
+
/** Short-lived session token. Reactor uses this to connect. fal does not. */
|
|
1968
|
+
token: string;
|
|
1969
|
+
/** Token expiry as milliseconds since epoch */
|
|
1970
|
+
expiresAt: number;
|
|
1971
|
+
/** Prompt the client should send when it starts the session */
|
|
1972
|
+
prompt: string;
|
|
1973
|
+
/** Session status after the server half finishes */
|
|
1974
|
+
status: 'ready' | 'waiting';
|
|
1975
|
+
/** Provider session id, when the adapter created one */
|
|
1976
|
+
sessionId?: string;
|
|
1977
|
+
/** Token usage / billing, when the adapter can report it */
|
|
1978
|
+
usage?: TokenUsage;
|
|
1979
|
+
}
|
|
1868
1980
|
/**
|
|
1869
1981
|
* Options for text-to-speech generation.
|
|
1870
1982
|
* These are the common options supported across providers.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.0",
|
|
4
4
|
"description": "Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"@ag-ui/core": "0.1.1-canary.beta.0",
|
|
89
89
|
"@standard-schema/spec": "^1.1.0",
|
|
90
90
|
"partial-json": "^0.1.7",
|
|
91
|
-
"@tanstack/ai-event-client": "^0.11.
|
|
91
|
+
"@tanstack/ai-event-client": "^0.11.3",
|
|
92
92
|
"@tanstack/ai-utils": "^0.4.0"
|
|
93
93
|
},
|
|
94
94
|
"peerDependencies": {
|
|
@@ -45,16 +45,20 @@ Create an adapter and use it with `chat()`:
|
|
|
45
45
|
import { chat, toServerSentEventsResponse } from '@tanstack/ai'
|
|
46
46
|
import { openaiText } from '@tanstack/ai-openai'
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
export async function POST(request: Request) {
|
|
49
|
+
const { messages } = await request.json()
|
|
50
|
+
|
|
51
|
+
const stream = chat({
|
|
52
|
+
adapter: openaiText('gpt-5.2'),
|
|
53
|
+
messages,
|
|
54
|
+
modelOptions: {
|
|
55
|
+
temperature: 0.7,
|
|
56
|
+
max_output_tokens: 1000,
|
|
57
|
+
},
|
|
58
|
+
})
|
|
56
59
|
|
|
57
|
-
return toServerSentEventsResponse(stream)
|
|
60
|
+
return toServerSentEventsResponse(stream)
|
|
61
|
+
}
|
|
58
62
|
```
|
|
59
63
|
|
|
60
64
|
The adapter factory function takes the model name as a string literal and an
|
|
@@ -73,18 +77,19 @@ top-level options on `chat()`. See the per-provider table in
|
|
|
73
77
|
Each provider has a dedicated package with tree-shakeable adapter factories.
|
|
74
78
|
The text adapter is the primary one for chat/completions:
|
|
75
79
|
|
|
76
|
-
| Provider | Package | Factory | Env Var
|
|
77
|
-
| ----------------- | -------------------------------- | ------------------------------------------- |
|
|
78
|
-
| OpenAI | `@tanstack/ai-openai` | `openaiText` | `OPENAI_API_KEY`
|
|
79
|
-
| Anthropic | `@tanstack/ai-anthropic` | `anthropicText` | `ANTHROPIC_API_KEY`
|
|
80
|
-
| Gemini | `@tanstack/ai-gemini` | `geminiText` | `GOOGLE_API_KEY` or `GEMINI_API_KEY`
|
|
81
|
-
| Grok (xAI) | `@tanstack/ai-grok` | `grokText` | `XAI_API_KEY`
|
|
82
|
-
| Groq | `@tanstack/ai-groq` | `groqText` | `GROQ_API_KEY`
|
|
83
|
-
| OpenRouter | `@tanstack/ai-openrouter` | `openRouterText` | `OPENROUTER_API_KEY`
|
|
84
|
-
| Ollama | `@tanstack/ai-ollama` | `ollamaText` | `OLLAMA_HOST` (default: `http://localhost:11434`)
|
|
85
|
-
| Bedrock | `@tanstack/ai-bedrock` | `bedrockText` | `BEDROCK_API_KEY` or `AWS_BEARER_TOKEN_BEDROCK`
|
|
86
|
-
| BytePlus | `@tanstack/ai-byteplus` | `byteplusText` | `ARK_API_KEY` (falls back to `BYTEPLUS_API_KEY`)
|
|
87
|
-
| OpenAI-compatible | `@tanstack/ai-openai/compatible` | `openaiCompatible` / `openaiCompatibleText` | provider-specific (passed via `apiKey`)
|
|
80
|
+
| Provider | Package | Factory | Env Var |
|
|
81
|
+
| ----------------- | -------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
82
|
+
| OpenAI | `@tanstack/ai-openai` | `openaiText` | `OPENAI_API_KEY` |
|
|
83
|
+
| Anthropic | `@tanstack/ai-anthropic` | `anthropicText` | `ANTHROPIC_API_KEY` |
|
|
84
|
+
| Gemini | `@tanstack/ai-gemini` | `geminiText` | `GOOGLE_API_KEY` or `GEMINI_API_KEY` |
|
|
85
|
+
| Grok (xAI) | `@tanstack/ai-grok` | `grokText` | `XAI_API_KEY` |
|
|
86
|
+
| Groq | `@tanstack/ai-groq` | `groqText` | `GROQ_API_KEY` |
|
|
87
|
+
| OpenRouter | `@tanstack/ai-openrouter` | `openRouterText` | `OPENROUTER_API_KEY` |
|
|
88
|
+
| Ollama | `@tanstack/ai-ollama` | `ollamaText` | `OLLAMA_HOST` (default: `http://localhost:11434`) |
|
|
89
|
+
| Bedrock | `@tanstack/ai-bedrock` | `bedrockText` | `BEDROCK_API_KEY` or `AWS_BEARER_TOKEN_BEDROCK` |
|
|
90
|
+
| BytePlus | `@tanstack/ai-byteplus` | `byteplusText` | `ARK_API_KEY` (falls back to `BYTEPLUS_API_KEY`) |
|
|
91
|
+
| OpenAI-compatible | `@tanstack/ai-openai/compatible` | `openaiCompatible` / `openaiCompatibleText` | provider-specific (passed via `apiKey`) |
|
|
92
|
+
| Cloudflare | `@tanstack/ai-cloudflare` | `cloudflareText` | `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_API_TOKEN`, or `{ binding: env.AI }` in a Worker |
|
|
88
93
|
|
|
89
94
|
> **BytePlus uses two keys.** `byteplusText` / `byteplusVideo` /
|
|
90
95
|
> `byteplusImage` read `ARK_API_KEY` (ModelArk, `Authorization: Bearer`), but
|
|
@@ -94,7 +99,7 @@ The text adapter is the primary one for chat/completions:
|
|
|
94
99
|
|
|
95
100
|
```typescript
|
|
96
101
|
// Each factory takes model as first arg, optional config as second
|
|
97
|
-
import { openaiText } from '@tanstack/ai-openai'
|
|
102
|
+
import { openaiText, createOpenaiChat } from '@tanstack/ai-openai'
|
|
98
103
|
import { anthropicText } from '@tanstack/ai-anthropic'
|
|
99
104
|
import { geminiText } from '@tanstack/ai-gemini'
|
|
100
105
|
import { grokText } from '@tanstack/ai-grok'
|
|
@@ -108,17 +113,16 @@ import { byteplusText } from '@tanstack/ai-byteplus'
|
|
|
108
113
|
const adapter = openaiText('gpt-5.2')
|
|
109
114
|
const adapter2 = anthropicText('claude-sonnet-4-6')
|
|
110
115
|
const adapter3 = geminiText('gemini-2.5-pro')
|
|
111
|
-
const adapter4 = grokText('grok-4')
|
|
116
|
+
const adapter4 = grokText('grok-4.6')
|
|
112
117
|
const adapter5 = groqText('llama-3.3-70b-versatile')
|
|
113
118
|
const adapter6 = openRouterText('anthropic/claude-sonnet-4')
|
|
114
|
-
const adapter7 = ollamaText('llama3.3')
|
|
119
|
+
const adapter7 = ollamaText('llama3.3:latest')
|
|
115
120
|
const adapter8 = bedrockText('us.anthropic.claude-3-7-sonnet-20250219-v1:0')
|
|
116
121
|
const adapter9 = byteplusText('seed-2-0-lite-260428')
|
|
117
122
|
|
|
118
|
-
// Optional: pass explicit API key
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
})
|
|
123
|
+
// Optional: pass an explicit API key via the create* sibling
|
|
124
|
+
// (the plain factory reads it from the environment)
|
|
125
|
+
const adapterWithKey = createOpenaiChat('gpt-5.2', 'sk-...')
|
|
122
126
|
```
|
|
123
127
|
|
|
124
128
|
`@tanstack/ai-bedrock` (Amazon Bedrock) branches on `config.api`:
|
|
@@ -136,26 +140,32 @@ input or configuration:
|
|
|
136
140
|
|
|
137
141
|
```typescript
|
|
138
142
|
import { chat, toServerSentEventsResponse } from '@tanstack/ai'
|
|
139
|
-
import type {
|
|
143
|
+
import type { ModelMessage } from '@tanstack/ai'
|
|
140
144
|
import { openaiText } from '@tanstack/ai-openai'
|
|
141
145
|
import { anthropicText } from '@tanstack/ai-anthropic'
|
|
142
146
|
import { geminiText } from '@tanstack/ai-gemini'
|
|
143
147
|
|
|
144
148
|
// Define a map of provider+model to adapter factory calls
|
|
145
|
-
const adapters
|
|
149
|
+
const adapters = {
|
|
146
150
|
'openai/gpt-5.2': () => openaiText('gpt-5.2'),
|
|
147
151
|
'anthropic/claude-sonnet-4-6': () => anthropicText('claude-sonnet-4-6'),
|
|
148
152
|
'gemini/gemini-2.5-pro': () => geminiText('gemini-2.5-pro'),
|
|
149
153
|
}
|
|
150
154
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
function isKnownProviderModel(key: string): key is keyof typeof adapters {
|
|
156
|
+
return key in adapters
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function handleChat(
|
|
160
|
+
providerModel: string,
|
|
161
|
+
messages: Array<ModelMessage>,
|
|
162
|
+
) {
|
|
163
|
+
if (!isKnownProviderModel(providerModel)) {
|
|
154
164
|
throw new Error(`Unknown provider/model: ${providerModel}`)
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
const stream = chat({
|
|
158
|
-
adapter:
|
|
168
|
+
adapter: adapters[providerModel](),
|
|
159
169
|
messages,
|
|
160
170
|
})
|
|
161
171
|
|
|
@@ -173,6 +183,10 @@ import { openaiText } from '@tanstack/ai-openai'
|
|
|
173
183
|
import { anthropicText } from '@tanstack/ai-anthropic'
|
|
174
184
|
import { geminiText } from '@tanstack/ai-gemini'
|
|
175
185
|
|
|
186
|
+
const messages = [
|
|
187
|
+
{ role: 'user' as const, content: 'Plan a database migration.' },
|
|
188
|
+
]
|
|
189
|
+
|
|
176
190
|
// OpenAI: reasoning with effort and summary
|
|
177
191
|
const openaiStream = chat({
|
|
178
192
|
adapter: openaiText('gpt-5.2'),
|
|
@@ -198,16 +212,18 @@ const anthropicStream = chat({
|
|
|
198
212
|
},
|
|
199
213
|
})
|
|
200
214
|
|
|
201
|
-
// Anthropic: adaptive thinking (
|
|
215
|
+
// Anthropic: adaptive thinking (Sonnet 5, Fable 5, Opus 4.7+) — depth is
|
|
216
|
+
// tuned with output_config.effort instead of a token budget
|
|
202
217
|
const adaptiveStream = chat({
|
|
203
|
-
adapter: anthropicText('claude-sonnet-
|
|
218
|
+
adapter: anthropicText('claude-sonnet-5'),
|
|
204
219
|
messages,
|
|
205
220
|
modelOptions: {
|
|
206
221
|
max_tokens: 16000,
|
|
207
222
|
thinking: {
|
|
208
223
|
type: 'adaptive',
|
|
224
|
+
display: 'summarized', // stream the reasoning text (default 'omitted')
|
|
209
225
|
},
|
|
210
|
-
effort: 'high', // '
|
|
226
|
+
output_config: { effort: 'high' }, // 'low' | 'medium' | 'high' | 'xhigh' | 'max'
|
|
211
227
|
},
|
|
212
228
|
})
|
|
213
229
|
|
|
@@ -262,6 +278,14 @@ inside `modelOptions` using each provider's **native** key. They are not
|
|
|
262
278
|
top-level fields on `chat()`/`ai()`/`generate()`.
|
|
263
279
|
|
|
264
280
|
```typescript
|
|
281
|
+
import { chat } from '@tanstack/ai'
|
|
282
|
+
import { openaiText } from '@tanstack/ai-openai'
|
|
283
|
+
import { anthropicText } from '@tanstack/ai-anthropic'
|
|
284
|
+
import { geminiText } from '@tanstack/ai-gemini'
|
|
285
|
+
import { ollamaText } from '@tanstack/ai-ollama'
|
|
286
|
+
|
|
287
|
+
const messages = [{ role: 'user' as const, content: 'Hello' }]
|
|
288
|
+
|
|
265
289
|
// OpenAI — native keys
|
|
266
290
|
chat({
|
|
267
291
|
adapter: openaiText('gpt-5.2'),
|
|
@@ -284,8 +308,9 @@ chat({
|
|
|
284
308
|
})
|
|
285
309
|
|
|
286
310
|
// Ollama — NESTED under modelOptions.options
|
|
311
|
+
// (use the `family:tag` id — a bare `llama3.3` falls back to untyped options)
|
|
287
312
|
chat({
|
|
288
|
-
adapter: ollamaText('llama3.3'),
|
|
313
|
+
adapter: ollamaText('llama3.3:latest'),
|
|
289
314
|
messages,
|
|
290
315
|
modelOptions: {
|
|
291
316
|
options: { temperature: 0.7, top_p: 0.9, num_predict: 1000 },
|
|
@@ -300,7 +325,7 @@ Per-provider sampling keys (all live inside `modelOptions`):
|
|
|
300
325
|
| OpenAI | `temperature` | `top_p` | `max_output_tokens` |
|
|
301
326
|
| Anthropic | `temperature` | `top_p` | `max_tokens` |
|
|
302
327
|
| Gemini | `temperature` | `topP` | `maxOutputTokens` |
|
|
303
|
-
| Grok (xAI) | `temperature` | `top_p` | `
|
|
328
|
+
| Grok (xAI) | `temperature` | `top_p` | `max_output_tokens` |
|
|
304
329
|
| Groq | `temperature` | `top_p` | `max_completion_tokens` |
|
|
305
330
|
| OpenRouter (chat) | `temperature` | `topP` | `maxCompletionTokens` |
|
|
306
331
|
| Ollama | `temperature` | `top_p` | `num_predict` (nested in `options`) |
|
|
@@ -325,7 +350,16 @@ some sampling options use provider-native names. Ollama nests all sampling under
|
|
|
325
350
|
Adapters can declare an optional capability method:
|
|
326
351
|
|
|
327
352
|
```ts
|
|
328
|
-
|
|
353
|
+
import { AnthropicTextAdapter } from '@tanstack/ai-anthropic'
|
|
354
|
+
|
|
355
|
+
// The TextAdapter contract:
|
|
356
|
+
// supportsCombinedToolsAndSchema?: (modelOptions?: TProviderOptions) => boolean
|
|
357
|
+
// Subclasses override it to narrow the capability:
|
|
358
|
+
class LegacyPathAnthropic extends AnthropicTextAdapter<'claude-sonnet-4-6'> {
|
|
359
|
+
override supportsCombinedToolsAndSchema(): boolean {
|
|
360
|
+
return false
|
|
361
|
+
}
|
|
362
|
+
}
|
|
329
363
|
```
|
|
330
364
|
|
|
331
365
|
When `true`, the engine wires `outputSchema` into the regular
|
|
@@ -337,16 +371,16 @@ runs.
|
|
|
337
371
|
|
|
338
372
|
Current per-adapter status (#605):
|
|
339
373
|
|
|
340
|
-
| Adapter | Returns
|
|
341
|
-
| -------------------------------------------- |
|
|
342
|
-
| `openaiText` / `openaiChatCompletions` | `true` (all supported models)
|
|
343
|
-
| `anthropicText` | `true` for Claude 4.5+ (gated by `ANTHROPIC_COMBINED_TOOLS_AND_SCHEMA_MODELS`), `false` otherwise
|
|
344
|
-
| `geminiText` | `true` for Gemini 3.x (gated by `GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS`), `false` otherwise
|
|
345
|
-
| `grokText` | `true`
|
|
346
|
-
| `groqText` | `false` (Groq API rejects schema + tools + stream)
|
|
347
|
-
| `openRouterText` / `openRouterResponsesText` | `
|
|
348
|
-
| `ollamaText` | `false` (constrained-decoding vs tool-call grammar conflict)
|
|
349
|
-
| `byteplusText` | Per model — `true` only for the 10 ids in `BYTEPLUS_STRUCTURED_OUTPUT_CHAT_MODELS`, `false` otherwise
|
|
374
|
+
| Adapter | Returns |
|
|
375
|
+
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
376
|
+
| `openaiText` / `openaiChatCompletions` | `true` (all supported models) |
|
|
377
|
+
| `anthropicText` | `true` for Claude 4.5+ (gated by `ANTHROPIC_COMBINED_TOOLS_AND_SCHEMA_MODELS`), `false` otherwise |
|
|
378
|
+
| `geminiText` | `true` for Gemini 3.x (gated by `GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS`), `false` otherwise |
|
|
379
|
+
| `grokText` | `true` (all chat models — inherits the OpenAI Responses base; no per-model gate) |
|
|
380
|
+
| `groqText` | `false` (Groq API rejects schema + tools + stream) |
|
|
381
|
+
| `openRouterText` / `openRouterResponsesText` | Per model — `true` only when the model and every `modelOptions.models` fallback are in `OPENROUTER_COMBINED_TOOLS_AND_SCHEMA_MODELS` |
|
|
382
|
+
| `ollamaText` | `false` (constrained-decoding vs tool-call grammar conflict) |
|
|
383
|
+
| `byteplusText` | Per model — `true` only for the 10 ids in `BYTEPLUS_STRUCTURED_OUTPUT_CHAT_MODELS`, `false` otherwise |
|
|
350
384
|
|
|
351
385
|
Subclasses can override to narrow the capability. When extending an
|
|
352
386
|
adapter for a custom model that doesn't support the combination, return
|
|
@@ -370,7 +404,9 @@ dedicated package required.
|
|
|
370
404
|
|
|
371
405
|
```typescript
|
|
372
406
|
import { openaiCompatible } from '@tanstack/ai-openai/compatible'
|
|
373
|
-
import { createModel } from '@tanstack/ai'
|
|
407
|
+
import { chat, createModel } from '@tanstack/ai'
|
|
408
|
+
|
|
409
|
+
const messages = [{ role: 'user' as const, content: 'Hello' }]
|
|
374
410
|
|
|
375
411
|
// Provider-factory: configure baseURL + apiKey + models ONCE,
|
|
376
412
|
// then select a model per call (the model arg is a type-safe union).
|
|
@@ -399,6 +435,9 @@ For a single model, use the one-shot helper:
|
|
|
399
435
|
|
|
400
436
|
```typescript
|
|
401
437
|
import { openaiCompatibleText } from '@tanstack/ai-openai/compatible'
|
|
438
|
+
import { chat } from '@tanstack/ai'
|
|
439
|
+
|
|
440
|
+
const messages = [{ role: 'user' as const, content: 'Hello' }]
|
|
402
441
|
|
|
403
442
|
chat({
|
|
404
443
|
adapter: openaiCompatibleText('deepseek-chat', {
|
|
@@ -427,13 +466,17 @@ ElevenLabs `baseUrl`/`headers`). The vendor names still work; when both are
|
|
|
427
466
|
set, `baseURL` and `defaultHeaders` win.
|
|
428
467
|
|
|
429
468
|
```typescript
|
|
469
|
+
import { createGeminiChat } from '@tanstack/ai-gemini'
|
|
470
|
+
|
|
430
471
|
const gateway = {
|
|
431
472
|
baseURL: 'https://gateway.example.com/google-ai-studio',
|
|
432
473
|
defaultHeaders: {
|
|
433
474
|
'cf-aig-authorization': `Bearer ${process.env.GATEWAY_TOKEN}`,
|
|
434
475
|
},
|
|
435
476
|
}
|
|
436
|
-
createGeminiChat('gemini-3.8-flash',
|
|
477
|
+
createGeminiChat('gemini-3.8-flash', process.env.GOOGLE_API_KEY!, {
|
|
478
|
+
...gateway,
|
|
479
|
+
})
|
|
437
480
|
```
|
|
438
481
|
|
|
439
482
|
## Common Mistakes
|
|
@@ -443,13 +486,19 @@ createGeminiChat('gemini-3.8-flash', apiKey, { ...gateway })
|
|
|
443
486
|
The legacy `openai()` (and `anthropic()`, etc.) monolithic adapters are
|
|
444
487
|
deprecated. They take the model in `chat()`, not in the factory.
|
|
445
488
|
|
|
446
|
-
```typescript
|
|
447
|
-
// WRONG: Legacy monolithic adapter pattern
|
|
489
|
+
```typescript ignore
|
|
490
|
+
// WRONG: Legacy monolithic adapter pattern (no longer exported)
|
|
448
491
|
import { openai } from '@tanstack/ai-openai'
|
|
449
492
|
chat({ adapter: openai(), model: 'gpt-5.2', messages })
|
|
493
|
+
```
|
|
450
494
|
|
|
495
|
+
```typescript
|
|
451
496
|
// CORRECT: Tree-shakeable adapter, model in factory
|
|
497
|
+
import { chat } from '@tanstack/ai'
|
|
452
498
|
import { openaiText } from '@tanstack/ai-openai'
|
|
499
|
+
|
|
500
|
+
const messages = [{ role: 'user' as const, content: 'Hello' }]
|
|
501
|
+
|
|
453
502
|
chat({ adapter: openaiText('gpt-5.2'), messages })
|
|
454
503
|
```
|
|
455
504
|
|
|
@@ -21,45 +21,58 @@ import { anthropicText } from '@tanstack/ai-anthropic'
|
|
|
21
21
|
|
|
22
22
|
## Key Chat Models
|
|
23
23
|
|
|
24
|
-
| Model
|
|
25
|
-
|
|
|
26
|
-
| `claude-fable-5`
|
|
27
|
-
| `claude-
|
|
28
|
-
| `claude-opus-
|
|
29
|
-
| `claude-opus-
|
|
30
|
-
| `claude-
|
|
31
|
-
| `claude-
|
|
32
|
-
| `claude-
|
|
33
|
-
| `claude-opus-4-
|
|
34
|
-
| `claude-
|
|
35
|
-
| `claude-
|
|
24
|
+
| Model | Context Window | Max Output | Notes |
|
|
25
|
+
| -------------------- | -------------- | ---------- | ------------------------------------------- |
|
|
26
|
+
| `claude-fable-5-1` | 1M | 128K | Newest; thinking always on (adaptive) |
|
|
27
|
+
| `claude-fable-5` | 1M | 128K | Most capable; thinking always on (adaptive) |
|
|
28
|
+
| `claude-opus-5` | 1M | 128K | Opus tier; budget thinking + sampling |
|
|
29
|
+
| `claude-opus-5-fast` | 1M | 128K | Fast-mode Opus 5; same options as opus-5 |
|
|
30
|
+
| `claude-sonnet-5` | 1M | 128K | Best balance; adaptive thinking by default |
|
|
31
|
+
| `claude-opus-4-8` | 1M | 128K | Opus tier; adaptive thinking, no sampling |
|
|
32
|
+
| `claude-opus-4-7` | 1M | 128K | Older Opus; adaptive thinking, no sampling |
|
|
33
|
+
| `claude-opus-4-6` | 200K | 128K | Older Opus, adaptive + budget thinking |
|
|
34
|
+
| `claude-sonnet-4-6` | 1M | 64K | Previous gen balanced, adaptive + budget |
|
|
35
|
+
| `claude-sonnet-4-5` | 200K | 64K | Previous gen balanced |
|
|
36
|
+
| `claude-opus-4-5` | 200K | 32K | Previous gen most capable |
|
|
37
|
+
| `claude-opus-4-1` | 200K | 64K | Deprecated (retires 2026-08-05) |
|
|
38
|
+
| `claude-haiku-4-5` | 200K | 64K | Fast and affordable |
|
|
36
39
|
|
|
37
40
|
Note: Model IDs use the format `claude-sonnet-5`, `claude-opus-4-8`, etc.
|
|
38
|
-
Retired models (Claude 3.x, Sonnet 3.7, Opus 4 / Sonnet 4)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
Retired models (Claude 3.x, Sonnet 3.7, Opus 4 / Sonnet 4) were removed —
|
|
42
|
+
every registered id resolves against the first-party Anthropic API.
|
|
43
|
+
`claude-opus-5-fast` is the only `-fast` id that remains.
|
|
44
|
+
|
|
45
|
+
`output_config.effort` is typed only on the adaptive-era models
|
|
46
|
+
(`claude-opus-4-7`, `claude-opus-4-8`, `claude-sonnet-5`, `claude-fable-5`,
|
|
47
|
+
`claude-fable-5-1`). There is no top-level `effort` option on any model;
|
|
48
|
+
`claude-opus-4-6` / `claude-sonnet-4-6` accept `thinking: { type: 'adaptive' }`
|
|
49
|
+
but no effort knob.
|
|
41
50
|
|
|
42
51
|
## Provider-Specific modelOptions
|
|
43
52
|
|
|
44
53
|
```typescript
|
|
54
|
+
import { chat } from '@tanstack/ai'
|
|
55
|
+
import { anthropicText } from '@tanstack/ai-anthropic'
|
|
56
|
+
|
|
57
|
+
const messages = [{ role: 'user' as const, content: 'Hello' }]
|
|
58
|
+
|
|
45
59
|
chat({
|
|
46
60
|
adapter: anthropicText('claude-sonnet-4-6'),
|
|
47
61
|
messages,
|
|
48
62
|
modelOptions: {
|
|
49
63
|
// Sampling
|
|
50
64
|
temperature: 0.7,
|
|
51
|
-
top_p: 0.9, // cannot be combined with temperature
|
|
65
|
+
// top_p: 0.9, // cannot be combined with temperature
|
|
52
66
|
max_tokens: 16000,
|
|
53
67
|
// Extended thinking (budget-based)
|
|
54
68
|
thinking: {
|
|
55
69
|
type: 'enabled',
|
|
56
70
|
budget_tokens: 8000, // must be >= 1024 and < max_tokens
|
|
57
71
|
},
|
|
58
|
-
// Adaptive thinking (claude-sonnet-4-6, claude-opus-4-6+)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
},
|
|
62
|
-
effort: 'high', // 'max' | 'high' | 'medium' | 'low'
|
|
72
|
+
// Adaptive thinking (claude-sonnet-4-6, claude-opus-4-6+) — the
|
|
73
|
+
// alternative to the budget shape above; effort is tuned via
|
|
74
|
+
// output_config.effort on the adaptive-era models (see below)
|
|
75
|
+
// thinking: { type: 'adaptive' },
|
|
63
76
|
// Service tier
|
|
64
77
|
service_tier: 'auto', // 'auto' | 'standard_only'
|
|
65
78
|
// Stop sequences
|
|
@@ -99,6 +112,11 @@ ANTHROPIC_API_KEY
|
|
|
99
112
|
The per-model types restrict `modelOptions` on the newest models:
|
|
100
113
|
|
|
101
114
|
```typescript
|
|
115
|
+
import { chat } from '@tanstack/ai'
|
|
116
|
+
import { anthropicText } from '@tanstack/ai-anthropic'
|
|
117
|
+
|
|
118
|
+
const messages = [{ role: 'user' as const, content: 'Hello' }]
|
|
119
|
+
|
|
102
120
|
chat({
|
|
103
121
|
adapter: anthropicText('claude-sonnet-5'), // or 'claude-fable-5', 'claude-opus-4-8'
|
|
104
122
|
messages,
|
|
@@ -62,6 +62,11 @@ Media models: `BYTEPLUS_VIDEO_MODELS` (Seedance —
|
|
|
62
62
|
## Provider-Specific modelOptions
|
|
63
63
|
|
|
64
64
|
```typescript
|
|
65
|
+
import { chat } from '@tanstack/ai'
|
|
66
|
+
import { byteplusText } from '@tanstack/ai-byteplus'
|
|
67
|
+
|
|
68
|
+
const messages = [{ role: 'user' as const, content: 'Hello' }]
|
|
69
|
+
|
|
65
70
|
chat({
|
|
66
71
|
adapter: byteplusText('dola-seed-2-1-turbo-260628'),
|
|
67
72
|
messages,
|
|
@@ -38,6 +38,15 @@ Most Gemini text models accept `text`, `image`, `audio`, `video`, and `document`
|
|
|
38
38
|
## Provider-Specific modelOptions
|
|
39
39
|
|
|
40
40
|
```typescript
|
|
41
|
+
import { chat } from '@tanstack/ai'
|
|
42
|
+
import {
|
|
43
|
+
geminiText,
|
|
44
|
+
HarmBlockThreshold,
|
|
45
|
+
HarmCategory,
|
|
46
|
+
} from '@tanstack/ai-gemini'
|
|
47
|
+
|
|
48
|
+
const messages = [{ role: 'user' as const, content: 'Hello' }]
|
|
49
|
+
|
|
41
50
|
chat({
|
|
42
51
|
adapter: geminiText('gemini-2.5-pro'),
|
|
43
52
|
messages,
|
|
@@ -47,15 +56,14 @@ chat({
|
|
|
47
56
|
includeThoughts: true,
|
|
48
57
|
thinkingBudget: 4096,
|
|
49
58
|
},
|
|
50
|
-
// Thinking (level-based, advanced models)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
59
|
+
// Thinking (level-based, advanced models) — the alternative to the
|
|
60
|
+
// budget shape above:
|
|
61
|
+
// thinkingConfig: { thinkingLevel: 'THINKING_LEVEL_HIGH' },
|
|
54
62
|
// Safety settings
|
|
55
63
|
safetySettings: [
|
|
56
64
|
{
|
|
57
|
-
category:
|
|
58
|
-
threshold:
|
|
65
|
+
category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
|
66
|
+
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
|
|
59
67
|
},
|
|
60
68
|
],
|
|
61
69
|
// Tool config
|