@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.
Files changed (74) hide show
  1. package/README.md +14 -13
  2. package/dist/esm/activities/chat/index.js +5 -3
  3. package/dist/esm/activities/chat/index.js.map +1 -1
  4. package/dist/esm/activities/generateLiveVideo/adapter.d.ts +69 -0
  5. package/dist/esm/activities/generateLiveVideo/adapter.js +23 -0
  6. package/dist/esm/activities/generateLiveVideo/adapter.js.map +1 -0
  7. package/dist/esm/activities/generateLiveVideo/index.d.ts +99 -0
  8. package/dist/esm/activities/generateLiveVideo/index.js +162 -0
  9. package/dist/esm/activities/generateLiveVideo/index.js.map +1 -0
  10. package/dist/esm/activities/generateVideo/index.js +3 -1
  11. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  12. package/dist/esm/activities/generateWorld/adapter.d.ts +69 -0
  13. package/dist/esm/activities/generateWorld/adapter.js +23 -0
  14. package/dist/esm/activities/generateWorld/adapter.js.map +1 -0
  15. package/dist/esm/activities/generateWorld/index.d.ts +99 -0
  16. package/dist/esm/activities/generateWorld/index.js +162 -0
  17. package/dist/esm/activities/generateWorld/index.js.map +1 -0
  18. package/dist/esm/activities/index.d.ts +8 -2
  19. package/dist/esm/activities/index.js +11 -7
  20. package/dist/esm/activities/middleware/types.d.ts +1 -1
  21. package/dist/esm/activities/summarize/chat-stream-summarize.js +2 -1
  22. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
  23. package/dist/esm/byok/define-provider.d.ts +6 -0
  24. package/dist/esm/byok/define-provider.js +2 -1
  25. package/dist/esm/byok/define-provider.js.map +1 -1
  26. package/dist/esm/byok/get-key.d.ts +7 -0
  27. package/dist/esm/byok/get-key.js +8 -1
  28. package/dist/esm/byok/get-key.js.map +1 -1
  29. package/dist/esm/byok/server.d.ts +1 -1
  30. package/dist/esm/byok/server.js +2 -2
  31. package/dist/esm/client.d.ts +4 -2
  32. package/dist/esm/client.js +3 -1
  33. package/dist/esm/client.js.map +1 -1
  34. package/dist/esm/index.d.ts +4 -2
  35. package/dist/esm/index.js +3 -1
  36. package/dist/esm/middlewares/otel.js +3 -1
  37. package/dist/esm/middlewares/otel.js.map +1 -1
  38. package/dist/esm/types.d.ts +112 -0
  39. package/package.json +2 -2
  40. package/skills/ai-core/adapter-configuration/SKILL.md +103 -54
  41. package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +39 -21
  42. package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +5 -0
  43. package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +14 -6
  44. package/skills/ai-core/adapter-configuration/references/grok-adapter.md +33 -25
  45. package/skills/ai-core/adapter-configuration/references/groq-adapter.md +7 -2
  46. package/skills/ai-core/adapter-configuration/references/ollama-adapter.md +25 -12
  47. package/skills/ai-core/adapter-configuration/references/openai-adapter.md +19 -9
  48. package/skills/ai-core/adapter-configuration/references/openrouter-adapter.md +34 -21
  49. package/skills/ai-core/ag-ui-protocol/SKILL.md +16 -10
  50. package/skills/ai-core/chat-experience/SKILL.md +228 -108
  51. package/skills/ai-core/client-persistence/SKILL.md +21 -9
  52. package/skills/ai-core/custom-backend-integration/SKILL.md +86 -52
  53. package/skills/ai-core/debug-logging/SKILL.md +100 -18
  54. package/skills/ai-core/locks/SKILL.md +35 -7
  55. package/skills/ai-core/media-generation/SKILL.md +114 -49
  56. package/skills/ai-core/middleware/SKILL.md +174 -69
  57. package/skills/ai-core/structured-outputs/SKILL.md +99 -49
  58. package/skills/ai-core/tool-calling/SKILL.md +245 -158
  59. package/src/activities/chat/index.ts +6 -7
  60. package/src/activities/generateLiveVideo/adapter.ts +99 -0
  61. package/src/activities/generateLiveVideo/index.ts +339 -0
  62. package/src/activities/generateVideo/index.ts +3 -4
  63. package/src/activities/generateWorld/adapter.ts +96 -0
  64. package/src/activities/generateWorld/index.ts +339 -0
  65. package/src/activities/index.ts +44 -0
  66. package/src/activities/middleware/types.ts +2 -0
  67. package/src/activities/summarize/chat-stream-summarize.ts +2 -0
  68. package/src/byok/define-provider.ts +7 -0
  69. package/src/byok/get-key.ts +18 -0
  70. package/src/byok/server.ts +1 -1
  71. package/src/client.ts +8 -0
  72. package/src/index.ts +8 -0
  73. package/src/middlewares/otel.ts +2 -0
  74. package/src/types.ts +128 -0
@@ -23,34 +23,41 @@ import { grokImage } from '@tanstack/ai-grok'
23
23
 
24
24
  ## Key Chat Models
25
25
 
26
- | Model | Context Window | Notes |
27
- | ----------------------------- | -------------- | ---------------------------- |
28
- | `grok-4-1-fast-reasoning` | 2M | Latest, fast reasoning |
29
- | `grok-4-1-fast-non-reasoning` | 2M | Latest, no reasoning |
30
- | `grok-code-fast-1` | 256K | Code-specialized, reasoning |
31
- | `grok-4` | 256K | Full reasoning, tool calling |
32
- | `grok-4-fast-reasoning` | 2M | Fast reasoning variant |
33
- | `grok-3` | 131K | Previous gen, no reasoning |
34
- | `grok-3-mini` | 131K | Budget reasoning |
35
- | `grok-2-vision-1212` | 32K | Vision input |
36
-
37
- Image model: `grok-2-image-1212`
26
+ | Model | Context Window | Notes |
27
+ | ---------------- | -------------- | -------------------------------------------------------- |
28
+ | `grok-4.6` | 500K | Latest; reasoning, tools, structured output; document in |
29
+ | `grok-4.5` | 500K | Reasoning, tools, structured output; document in |
30
+ | `grok-4.3` | 1M | Reasoning, tools, structured output; text + image in |
31
+ | `grok-build-0.1` | 256K | Code-specialized; `reasoning` option is not accepted |
32
+
33
+ `GROK_CHAT_MODELS` is exactly these four ids. Image models
34
+ (`GROK_IMAGE_MODELS`): `grok-2-image-1212`, `grok-imagine-image`,
35
+ `grok-imagine-image-2.0`, `grok-imagine-image-quality`.
38
36
 
39
37
  ## Provider-Specific modelOptions
40
38
 
41
- Grok uses an OpenAI-compatible API. Options are straightforward:
39
+ Grok speaks the OpenAI **Responses** API (the adapter uses the OpenAI SDK
40
+ against `https://api.x.ai/v1`), so option names follow that API:
42
41
 
43
42
  ```typescript
43
+ import { chat } from '@tanstack/ai'
44
+ import { grokText } from '@tanstack/ai-grok'
45
+
46
+ const messages = [{ role: 'user' as const, content: 'Hello' }]
47
+
44
48
  chat({
45
- adapter: grokText('grok-4'),
49
+ adapter: grokText('grok-4.6'),
46
50
  messages,
47
51
  modelOptions: {
52
+ // Sampling (Responses API names)
48
53
  temperature: 0.7,
49
- max_tokens: 4096,
50
54
  top_p: 0.9,
51
- frequency_penalty: 0.5,
52
- presence_penalty: 0.5,
53
- stop: ['\n\n'],
55
+ max_output_tokens: 4096,
56
+ // Reasoning (reasoning-capable models)
57
+ reasoning: { effort: 'high' }, // 'none' | 'low' | 'medium' | 'high'
58
+ // Response storage (adapter default: false)
59
+ store: false,
60
+ // End-user id for abuse monitoring
54
61
  user: 'user-123',
55
62
  },
56
63
  })
@@ -68,10 +75,11 @@ The adapter uses the OpenAI SDK with xAI's base URL (`https://api.x.ai/v1`).
68
75
  ## Gotchas
69
76
 
70
77
  - Uses the OpenAI SDK under the hood with a custom `baseURL`.
71
- - `grok-4-1-fast-non-reasoning` and `grok-4-fast-non-reasoning` explicitly
72
- do NOT support reasoning. Other grok-4+ models do.
73
- - `grok-2-vision-1212` is the only model with image input support in the
74
- older generation.
75
- - The grok-4-1 fast models have a massive 2M context window.
76
- - Provider options are simpler than OpenAI's (no Responses API features,
77
- no structured outputs config, no metadata).
78
+ - All four chat models support reasoning; `grok-build-0.1` is the exception
79
+ in that it rejects the `reasoning` option (`GrokBuildProviderOptions`).
80
+ - `grok-4.5` / `grok-4.6` accept `text`, `image`, and `document` input;
81
+ `grok-4.3` / `grok-build-0.1` accept `text` and `image`.
82
+ - Provider options are a subset of OpenAI's Responses options:
83
+ `temperature`, `top_p`, `max_output_tokens`, `reasoning`, `store`,
84
+ `include`, `user`. There is no `max_tokens`, `frequency_penalty`,
85
+ `presence_penalty`, `stop`, or `metadata`.
@@ -38,6 +38,11 @@ Guard models: `meta-llama/llama-guard-4-12b`, `meta-llama/llama-prompt-guard-2-8
38
38
  ## Provider-Specific modelOptions
39
39
 
40
40
  ```typescript
41
+ import { chat } from '@tanstack/ai'
42
+ import { groqText } from '@tanstack/ai-groq'
43
+
44
+ const messages = [{ role: 'user' as const, content: 'Hello' }]
45
+
41
46
  chat({
42
47
  adapter: groqText('llama-3.3-70b-versatile'),
43
48
  messages,
@@ -49,7 +54,7 @@ chat({
49
54
  // Response format
50
55
  response_format: {
51
56
  type: 'json_schema',
52
- json_schema: {/* ... */},
57
+ json_schema: { name: 'answer', schema: {/* JSON Schema */} },
53
58
  },
54
59
  // Sampling
55
60
  temperature: 0.7,
@@ -67,7 +72,7 @@ chat({
67
72
  // Citations
68
73
  citation_options: 'enabled',
69
74
  // Documents for context
70
- documents: [{ text: '...' }],
75
+ documents: [{ source: { type: 'text', text: '...' } }],
71
76
  // Search settings (for web search tool)
72
77
  search_settings: {/* SearchSettings */},
73
78
  // Service tier
@@ -24,15 +24,20 @@ import { ollamaText } from '@tanstack/ai-ollama'
24
24
  Ollama runs models locally. The adapter supports a large catalog of models.
25
25
  Key families include:
26
26
 
27
- | Model Family | Example Names | Notes |
28
- | ------------ | -------------------------------- | ----------------------- |
29
- | Llama 4 | `llama4`, `llama4:scout` | Latest Meta models |
30
- | Llama 3.3 | `llama3.3`, `llama3.3:70b` | Strong general purpose |
31
- | Qwen 3 | `qwen3`, `qwen3:32b` | Reasoning capable |
32
- | DeepSeek R1 | `deepseek-r1`, `deepseek-r1:70b` | Reasoning focused |
33
- | Gemma 3 | `gemma3`, `gemma3:27b` | Google's open model |
34
- | Phi 4 | `phi4`, `phi4:14b` | Microsoft's small model |
35
- | Mistral | `mistral`, `mistral-large` | Mistral AI models |
27
+ | Model Family | Example Names | Notes |
28
+ | ------------ | ---------------------------------------- | ----------------------- |
29
+ | Llama 4 | `llama4:latest`, `llama4:16x17b` | Latest Meta models |
30
+ | Llama 3.3 | `llama3.3:latest`, `llama3.3:70b` | Strong general purpose |
31
+ | Qwen 3 | `qwen3:latest`, `qwen3:32b` | Reasoning capable |
32
+ | DeepSeek R1 | `deepseek-r1:latest`, `deepseek-r1:70b` | Reasoning focused |
33
+ | Gemma 3 | `gemma3:latest`, `gemma3:27b` | Google's open model |
34
+ | Phi 4 | `phi4:latest`, `phi4:14b` | Microsoft's small model |
35
+ | Mistral | `mistral:latest`, `mistral-large:latest` | Mistral AI models |
36
+
37
+ Typed ids are always `family:tag` (`OLLAMA_TEXT_MODELS`). `ollamaText()`
38
+ accepts any string, but a bare `llama3.3` falls outside the typed catalog and
39
+ `modelOptions` degrades to the raw Ollama `ChatRequest` (which then demands a
40
+ `model` field). Use `llama3.3:latest`.
36
41
 
37
42
  Models must be pulled first: `ollama pull llama3.3`
38
43
 
@@ -48,8 +53,10 @@ Ollama's own request shape) — `temperature`, `top_p`, and `num_predict`
48
53
  import { chat } from '@tanstack/ai'
49
54
  import { ollamaText } from '@tanstack/ai-ollama'
50
55
 
56
+ const messages = [{ role: 'user' as const, content: 'Hello' }]
57
+
51
58
  const stream = chat({
52
- adapter: ollamaText('llama3.3'),
59
+ adapter: ollamaText('llama3.3:latest'),
53
60
  messages,
54
61
  modelOptions: {
55
62
  options: {
@@ -64,9 +71,15 @@ const stream = chat({
64
71
 
65
72
  ## Configuration
66
73
 
74
+ `ollamaText(model)` takes no config — it reads `OLLAMA_HOST`. To point at
75
+ another server (or pass headers / `baseURL` for a gateway), use
76
+ `createOllamaChat(model, hostOrConfig)`:
77
+
67
78
  ```typescript
68
- // With explicit host
69
- const adapter = ollamaText('llama3.3', {
79
+ import { createOllamaChat } from '@tanstack/ai-ollama'
80
+
81
+ // With explicit host (ollamaText() reads OLLAMA_HOST instead)
82
+ const adapter = createOllamaChat('llama3.3:latest', {
70
83
  host: 'http://my-server:11434',
71
84
  })
72
85
  ```
@@ -28,20 +28,30 @@ import { openaiSpeech } from '@tanstack/ai-openai'
28
28
 
29
29
  ## Key Chat Models
30
30
 
31
- | Model | Context Window | Max Output | Notes |
32
- | --------------------- | -------------- | ---------- | -------------------------------------- |
33
- | `gpt-5.4` | 400K | 128K | Flagship, reasoning, image input |
34
- | `gpt-5.4-pro` | 400K | 128K | Higher reasoning, no structured output |
35
- | `gpt-5.4-chat-latest` | 128K | 16K | Chat-optimized variant |
36
- | `gpt-5.1` | 400K | 128K | Previous flagship, image I/O |
37
- | `gpt-5` | 400K | 128K | Previous gen flagship |
38
- | `gpt-5-mini` | 400K | 128K | Cost-efficient |
31
+ | Model | Context Window | Max Output | Notes |
32
+ | -------------- | -------------- | ---------- | ---------------------------------------------- |
33
+ | `gpt-6-astra` | 1M | 128K | Newest; reasoning, tools, image input |
34
+ | `gpt-5.6` | 1M | 128K | Reasoning, tools, image input |
35
+ | `gpt-5.5` | 1M | 128K | Flagship used in examples; text/image/document |
36
+ | `gpt-5.5-pro` | 1M | 128K | Higher reasoning tier |
37
+ | `gpt-5.4-mini` | 400K | 128K | Cost-efficient (no bare `gpt-5.4` chat id) |
38
+ | `gpt-5.2` | 400K | 128K | Previous flagship; text/image/document |
39
+ | `gpt-5-mini` | 400K | 128K | Budget |
40
+
41
+ `OPENAI_CHAT_MODELS` is the full list (also `gpt-6-astra-pro`, the
42
+ `gpt-5.6-luna/sol/terra` family, `gpt-5.4-nano`, `gpt-5.2-pro`,
43
+ `gpt-5.1`, `gpt-5`, the `o3`/`o4-mini` reasoning models, and `gpt-4.1`/`gpt-4o`).
39
44
 
40
45
  ## Provider-Specific modelOptions
41
46
 
42
47
  ```typescript
48
+ import { chat } from '@tanstack/ai'
49
+ import { openaiText } from '@tanstack/ai-openai'
50
+
51
+ const messages = [{ role: 'user' as const, content: 'Hello' }]
52
+
43
53
  chat({
44
- adapter: openaiText('gpt-5.4'),
54
+ adapter: openaiText('gpt-5.5'),
45
55
  messages,
46
56
  modelOptions: {
47
57
  // Sampling
@@ -38,24 +38,29 @@ the format `provider/model-name`:
38
38
  OpenRouter has unique routing and provider selection options:
39
39
 
40
40
  ```typescript
41
+ import { chat } from '@tanstack/ai'
42
+ import { openRouterText } from '@tanstack/ai-openrouter'
43
+
44
+ const messages = [{ role: 'user' as const, content: 'Hello' }]
45
+
46
+ // Options are narrowed per model from OpenRouter's published metadata —
47
+ // e.g. 'anthropic/claude-sonnet-4' only accepts temperature/topP/
48
+ // maxCompletionTokens/stop/toolChoice/reasoning. This model takes the full set.
41
49
  chat({
42
- adapter: openRouterText('anthropic/claude-sonnet-4'),
50
+ adapter: openRouterText('deepseek/deepseek-v4-pro'),
43
51
  messages,
44
52
  modelOptions: {
45
53
  // Reasoning
46
54
  reasoning: {
47
- effort: 'high', // 'none' | 'minimal' | 'low' | 'medium' | 'high'
48
- max_tokens: 4096,
49
- exclude: false,
55
+ effort: 'high', // 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'
56
+ summary: 'auto',
57
+ // enabled: false — explicit opt-out (normalized to effort: 'none')
50
58
  },
51
59
  // Sampling
52
60
  temperature: 0.7,
53
61
  topP: 0.9,
54
- topK: 40,
55
62
  frequencyPenalty: 0.5,
56
63
  presencePenalty: 0.5,
57
- repetitionPenalty: 1.1,
58
- minP: 0.05,
59
64
  seed: 42,
60
65
  // Token limits
61
66
  maxCompletionTokens: 8192,
@@ -66,12 +71,11 @@ chat({
66
71
  parallelToolCalls: true,
67
72
  // Response format
68
73
  responseFormat: { type: 'json_object' },
69
- // Web search
70
- webSearchOptions: {
71
- search_context_size: 'medium', // 'low' | 'medium' | 'high'
72
- },
73
- // Verbosity
74
- verbosity: 'medium',
74
+ // Routing (available on every model)
75
+ variant: 'nitro', // 'free' | 'nitro' | 'online' | 'exacto' | 'extended' | 'thinking'
76
+ models: ['deepseek/deepseek-v4-flash'], // fallbacks, tried in order
77
+ provider: { order: ['DeepSeek'], allowFallbacks: true },
78
+ plugins: [{ id: 'web' }], // web search
75
79
  // Logprobs
76
80
  logprobs: true,
77
81
  topLogprobs: 5,
@@ -89,11 +93,20 @@ OPENROUTER_API_KEY
89
93
 
90
94
  - Model IDs are `provider/model-name` format (e.g., `openai/gpt-5.2`).
91
95
  - OpenRouter has unique features not found in direct provider adapters:
92
- - `variant` option: `'free'`, `'nitro'`, `'online'`, `'thinking'`, etc.
93
- - `provider` routing preferences (order, fallbacks, data collection policies)
94
- - `transforms: ['middle-out']` for context compression
95
- - `prediction` for latency reduction
96
- - `plugins: [{ id: 'web' }]` for web search
97
- - Uses `camelCase` for option names (e.g., `topP`, `frequencyPenalty`),
98
- unlike OpenAI's `snake_case`.
99
- - `route: 'fallback'` with `models` array tries models in order.
96
+ - `variant` option: `'free'`, `'nitro'`, `'online'`, `'exacto'`,
97
+ `'extended'`, `'thinking'`
98
+ - `provider` routing preferences (`order`, `allowFallbacks`, data
99
+ collection policies — camelCase keys)
100
+ - `models` array of fallback ids, tried in order
101
+ - `plugins: [{ id: 'web' }]` for web search (also `file-parser`,
102
+ `response-healing`, `moderation`, `auto-router`)
103
+ - Uses `camelCase` for option names (e.g., `topP`, `frequencyPenalty`,
104
+ `maxCompletionTokens`), unlike OpenAI's `snake_case`.
105
+ - `reasoning` is `{ effort, summary, enabled }` — there is no
106
+ `max_tokens`/`exclude` inside it; `enabled: false` is normalized to
107
+ `effort: 'none'`.
108
+ - Per-model options are narrowed from OpenRouter's published metadata, so
109
+ keys like `frequencyPenalty`, `seed`, `logprobs`, or `responseFormat` are
110
+ only accepted on models that support them. `topK`, `minP`,
111
+ `repetitionPenalty`, `webSearchOptions`, `verbosity`, `transforms`, and
112
+ `route` are not exposed by the adapter.
@@ -29,7 +29,7 @@ import { openaiText } from '@tanstack/ai-openai'
29
29
  export async function POST(request: Request) {
30
30
  const { messages } = await request.json()
31
31
  const stream = chat({
32
- adapter: openaiText('gpt-5.2'),
32
+ adapter: openaiText('gpt-5.6'),
33
33
  messages,
34
34
  })
35
35
  return toServerSentEventsResponse(stream)
@@ -49,7 +49,7 @@ import {
49
49
  mergeAgentTools,
50
50
  toServerSentEventsResponse,
51
51
  } from '@tanstack/ai'
52
- import { openaiText } from '@tanstack/ai-openai/adapters'
52
+ import { openaiText } from '@tanstack/ai-openai'
53
53
  import { serverTools } from './tools'
54
54
 
55
55
  export async function POST(req: Request) {
@@ -64,7 +64,7 @@ export async function POST(req: Request) {
64
64
  }
65
65
 
66
66
  const stream = chat({
67
- adapter: openaiText('gpt-4o'),
67
+ adapter: openaiText('gpt-5.6'),
68
68
  messages: params.messages,
69
69
  tools: mergeAgentTools(serverTools, params.tools),
70
70
  })
@@ -87,7 +87,7 @@ export async function POST(req: Request) {
87
87
 
88
88
  **Wire format:** Each event is `data: <JSON>\n\n`. Stream ends with `data: [DONE]\n\n`.
89
89
 
90
- ```typescript
90
+ ```typescript group=sse-response
91
91
  import {
92
92
  chat,
93
93
  toServerSentEventsStream,
@@ -95,10 +95,12 @@ import {
95
95
  } from '@tanstack/ai'
96
96
  import { openaiText } from '@tanstack/ai-openai'
97
97
 
98
+ const messages = [{ role: 'user' as const, content: 'Hello' }]
99
+
98
100
  // Option A: Get a ReadableStream (manual Response construction)
99
101
  const abortController = new AbortController()
100
102
  const stream = chat({
101
- adapter: openaiText('gpt-5.2'),
103
+ adapter: openaiText('gpt-5.6'),
102
104
  messages,
103
105
  abortController,
104
106
  })
@@ -127,7 +129,7 @@ const response2 = toServerSentEventsResponse(stream, { abortController })
127
129
 
128
130
  Custom headers merge on top (user headers override defaults):
129
131
 
130
- ```typescript
132
+ ```typescript group=sse-response
131
133
  toServerSentEventsResponse(stream, {
132
134
  headers: {
133
135
  'X-Accel-Buffering': 'no', // Disable nginx buffering
@@ -149,10 +151,12 @@ aborted, the error event is suppressed and the stream closes silently.
149
151
  import { chat, toHttpStream, toHttpResponse } from '@tanstack/ai'
150
152
  import { openaiText } from '@tanstack/ai-openai'
151
153
 
154
+ const messages = [{ role: 'user' as const, content: 'Hello' }]
155
+
152
156
  // Option A: Get a ReadableStream
153
157
  const abortController = new AbortController()
154
158
  const stream = chat({
155
- adapter: openaiText('gpt-5.2'),
159
+ adapter: openaiText('gpt-5.6'),
156
160
  messages,
157
161
  abortController,
158
162
  })
@@ -199,7 +203,7 @@ All events extend `BaseAGUIEvent` which carries `type`, `timestamp`, optional
199
203
  | `STATE_DELTA` | Incremental state update. Carries `delta: Record<string, unknown>`. |
200
204
  | `CUSTOM` | Extension point. Carries `name` (string) and optional `value` (unknown). |
201
205
  | `RUN_FINISHED` | Stream complete. Carries `runId` and `finishReason` (`'stop'` / `'length'` / `'content_filter'` / `'tool_calls'` / `null`). |
202
- | `RUN_ERROR` | Error during stream. Carries optional `runId` and `error: { message, code? }`. |
206
+ | `RUN_ERROR` | Error during stream. Carries `message`, optional `code` and `runId`; a nested `error: { message, code? }` copy is kept too. |
203
207
 
204
208
  **Typical event sequence for a text-only response:**
205
209
 
@@ -235,8 +239,10 @@ no helper, no cast:
235
239
  import { chat } from '@tanstack/ai'
236
240
  import { openaiText } from '@tanstack/ai-openai'
237
241
 
242
+ const messages = [{ role: 'user' as const, content: 'Hello' }]
243
+
238
244
  const stream = chat({
239
- adapter: openaiText('gpt-5.2'),
245
+ adapter: openaiText('gpt-5.6'),
240
246
  messages,
241
247
  })
242
248
 
@@ -284,7 +290,7 @@ causing events to arrive in batches instead of streaming token-by-token.
284
290
 
285
291
  Fix: Set proxy-bypass headers on the response.
286
292
 
287
- ```typescript
293
+ ```typescript group=sse-response
288
294
  toServerSentEventsResponse(stream, {
289
295
  headers: {
290
296
  'X-Accel-Buffering': 'no', // nginx