@sqlrooms/ai-core 0.26.0-rc.2 → 0.26.0-rc.3

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 (48) hide show
  1. package/README.md +238 -348
  2. package/dist/AiSlice.d.ts +37 -7
  3. package/dist/AiSlice.d.ts.map +1 -1
  4. package/dist/AiSlice.js +203 -147
  5. package/dist/AiSlice.js.map +1 -1
  6. package/dist/chatTransport.d.ts +48 -0
  7. package/dist/chatTransport.d.ts.map +1 -0
  8. package/dist/chatTransport.js +270 -0
  9. package/dist/chatTransport.js.map +1 -0
  10. package/dist/components/AiThinkingDots.d.ts +10 -0
  11. package/dist/components/AiThinkingDots.d.ts.map +1 -0
  12. package/dist/components/AiThinkingDots.js +18 -0
  13. package/dist/components/AiThinkingDots.js.map +1 -0
  14. package/dist/components/AnalysisResult.d.ts +1 -2
  15. package/dist/components/AnalysisResult.d.ts.map +1 -1
  16. package/dist/components/AnalysisResult.js +48 -18
  17. package/dist/components/AnalysisResult.js.map +1 -1
  18. package/dist/components/AnalysisResultsContainer.d.ts.map +1 -1
  19. package/dist/components/AnalysisResultsContainer.js +5 -10
  20. package/dist/components/AnalysisResultsContainer.js.map +1 -1
  21. package/dist/components/QueryControls.d.ts.map +1 -1
  22. package/dist/components/QueryControls.js +6 -3
  23. package/dist/components/QueryControls.js.map +1 -1
  24. package/dist/components/ToolCallInfo.d.ts +25 -0
  25. package/dist/components/ToolCallInfo.d.ts.map +1 -0
  26. package/dist/components/ToolCallInfo.js +32 -0
  27. package/dist/components/ToolCallInfo.js.map +1 -0
  28. package/dist/components/tools/ToolResult.d.ts +12 -7
  29. package/dist/components/tools/ToolResult.d.ts.map +1 -1
  30. package/dist/components/tools/ToolResult.js +8 -6
  31. package/dist/components/tools/ToolResult.js.map +1 -1
  32. package/dist/hooks/useAiChat.d.ts +60 -0
  33. package/dist/hooks/useAiChat.d.ts.map +1 -0
  34. package/dist/hooks/useAiChat.js +92 -0
  35. package/dist/hooks/useAiChat.js.map +1 -0
  36. package/dist/index.d.ts +3 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +3 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/utils.d.ts +23 -1
  41. package/dist/utils.d.ts.map +1 -1
  42. package/dist/utils.js +24 -0
  43. package/dist/utils.js.map +1 -1
  44. package/package.json +15 -14
  45. package/dist/analysis.d.ts +0 -51
  46. package/dist/analysis.d.ts.map +0 -1
  47. package/dist/analysis.js +0 -43
  48. package/dist/analysis.js.map +0 -1
package/README.md CHANGED
@@ -19,48 +19,12 @@ npm install @sqlrooms/ai
19
19
  yarn add @sqlrooms/ai
20
20
  ```
21
21
 
22
- Since version 0.8.2, you will need to install the LLM providers you want to use. For example, to use OpenAI, you can install the `@ai-sdk/openai` package:
23
-
24
- ```bash
25
- npm install @ai-sdk/openai
26
- ```
27
-
28
- Google LLM provider:
29
-
30
- ```bash
31
- npm install @ai-sdk/google
32
- ```
33
-
34
- Anthropic LLM provider:
35
-
36
- ```bash
37
- npm install @ai-sdk/anthropic
38
- ```
39
-
40
- DeepSeek LLM provider:
41
-
42
- ```bash
43
- npm install @ai-sdk/deepseek
44
- ```
45
-
46
- XAI LLM provider:
47
-
48
- ```bash
49
- npm install @ai-sdk/xai
50
- ```
51
-
52
- ollama LLM provider:
53
-
54
- ```bash
55
- npm install ollama-ai-provider-v2
56
- ```
57
-
58
22
  ## Basic Usage
59
23
 
60
- ### Setting Up AI Integration
24
+ ### Setting Up SqlRooms AI Chat for Browser-only application
61
25
 
62
26
  ```tsx
63
- import {createAiSlice} from '@sqlrooms/ai';
27
+ import {createAiSlice, createAiSettingsSlice} from '@sqlrooms/ai';
64
28
  import {createRoomStore} from '@sqlrooms/room-shell';
65
29
 
66
30
  // Create a room store with AI capabilities
@@ -71,22 +35,22 @@ const {roomStore, useRoomStore} = createRoomStore({
71
35
  // Your room configuration
72
36
  },
73
37
  }),
38
+ // Ai model config slice
39
+ ...createAiSettingsSlice({})(set, get, store),
74
40
  // Add AI slice
75
41
  ...createAiSlice({
76
- getApiKey: (modelProvider) => {
77
- // Return API key for the specified model provider
78
- return process.env.OPENAI_API_KEY || '';
42
+ getInstructions: () => {
43
+ return `You are an AI assistant that can answer questions and help with tasks.`;
79
44
  },
80
45
  initialAnalysisPrompt: 'What insights can you provide from my data?',
81
- // Optional: Add custom tools
82
- customTools: {
83
- // Your custom tools
46
+ tools: {
47
+ // Your tools
84
48
  },
85
- // Optional: Custom instructions for the AI
86
- getInstructions: (tablesSchema) => {
87
- return `Analyze the following tables: ${tablesSchema.map((t) => t.name).join(', ')}`;
49
+ getInstructions: () => {
50
+ // add custom instructions here
51
+ return createDefaultAiInstructions(store);
88
52
  },
89
- }),
53
+ })(set, get, store),
90
54
  });
91
55
 
92
56
  function MyApp() {
@@ -98,311 +62,205 @@ function MyApp() {
98
62
  }
99
63
  ```
100
64
 
101
- ### Advanced Store Configuration
65
+ ### Setting Up SqlRooms AI Chat for Server-side application
102
66
 
103
- For more complex applications, you can combine multiple slices:
67
+ - api/chat/route.ts
104
68
 
105
- ```tsx
106
- import {createAiSlice} from '@sqlrooms/ai';
107
- import {
108
- createSqlEditorSlice,
109
- createDefaultSqlEditorConfig,
110
- } from '@sqlrooms/sql-editor';
111
- import {createRoomStore, createRoomShellSlice} from '@sqlrooms/room-shell';
112
-
113
- // Define your application state type
114
- export type RoomState = RoomState<RoomConfig> &
115
- AiSliceState &
116
- SqlEditorSliceState;
117
-
118
- // Create the store with multiple slices
119
- export const {roomStore, useRoomStore} = createRoomStore<RoomConfig, RoomState>(
120
- (set, get, store) => ({
121
- // Base room slice
122
- ...createRoomShellSlice({
123
- config: {
124
- ...createDefaultSqlEditorConfig(),
125
- },
126
- }),
127
- // AI slice
128
- ...createAiSlice({
129
- config: {
130
- // Optional: Pre-configured AI sessions
131
- sessions: [
132
- {
133
- id: 'default-session',
134
- name: 'Default Analysis',
135
- modelProvider: 'openai',
136
- model: 'gpt-4o',
137
- analysisResults: [],
138
- createdAt: new Date(),
139
- },
140
- ],
141
- currentSessionId: 'default-session',
142
- }
143
- getApiKey: (modelProvider) => {
144
- // Return API key based on provider
145
- return apiKeys[modelProvider] || '';
146
- },
147
- // Custom tools and instructions
148
- }),
149
- // SQL Editor slice
150
- ...createSqlEditorSlice(),
151
- }),
152
- );
153
- ```
154
-
155
- ### Using AI Query Controls
69
+ ```typescript
70
+ export async function POST(req: Request) {
71
+ const {messages} = await req.json();
156
72
 
157
- ```tsx
158
- import {QueryControls} from '@sqlrooms/ai';
73
+ const stream = createUIMessageStream({
74
+ execute: async ({writer}) => {
75
+ const result = streamText({
76
+ model: openai('gpt-4.1'),
77
+ system: systemPrompt,
78
+ messages,
79
+ tools: {
80
+ // Your tools: remove exeucte for client tools so they run on the client side
81
+ },
82
+ });
83
+ writer.merge(result.toUIMessageStream({originalMessages: messages}));
84
+ },
85
+ });
159
86
 
160
- function AiQueryPanel() {
161
- return (
162
- <div className="rounded-lg border p-4">
163
- <h2 className="mb-4 text-xl font-bold">Ask AI</h2>
164
- <QueryControls
165
- placeholder="Ask a question about your data..."
166
- onSubmit={(query) => console.log('Processing query:', query)}
167
- />
168
- </div>
169
- );
87
+ return stream.toUIMessageStreamResponse();
170
88
  }
171
89
  ```
172
90
 
173
- ### Displaying Analysis Results
174
-
175
- ```tsx
176
- import {AnalysisResultsContainer, AnalysisResult} from '@sqlrooms/ai';
91
+ - page.tsx
177
92
 
178
- function AnalysisPanel() {
179
- // Get the current session and its analysis results
180
- const currentSession = useRoomStore((state) => state.ai.getCurrentSession());
181
- const analysisResults = currentSession?.analysisResults || [];
93
+ ```typescript
94
+ const {roomStore, useRoomStore} = createRoomStore({
95
+ ...createRoomShellSlice({
96
+ // Your room configuration
97
+ })(set, get, store),
98
+ ...createAiSettingsSlice({
99
+ // Your AI settings
100
+ })(set, get, store),
101
+ ...createAiSlice({
102
+ chatEndPoint: '/api/chat', // Point to the server-side endpoint
103
+ tools: {
104
+ // Your tools
105
+ },
106
+ })(set, get, store),
107
+ });
182
108
 
109
+ function MyApp() {
183
110
  return (
184
- <div className="rounded-lg border p-4">
185
- <h2 className="mb-4 text-xl font-bold">AI Analysis</h2>
186
- <AnalysisResultsContainer>
187
- {analysisResults.map((result) => (
188
- <AnalysisResult key={result.id} result={result} />
189
- ))}
190
- </AnalysisResultsContainer>
191
- </div>
111
+ <RoomStateProvider roomStore={roomStore}>
112
+ <MyDataApp />
113
+ </RoomStateProvider>
192
114
  );
193
115
  }
194
116
  ```
195
117
 
196
- ### Working with AI State
197
-
198
- ```tsx
199
- function AiStatusIndicator() {
200
- const isRunningAnalysis = useRoomStore((state) => state.ai.isRunningAnalysis);
201
- const analysisPrompt = useRoomStore((state) => state.ai.analysisPrompt);
202
- const currentSession = useRoomStore((state) => state.ai.getCurrentSession());
203
- const lastResult =
204
- currentSession?.analysisResults[currentSession.analysisResults.length - 1];
205
-
206
- if (isRunningAnalysis) {
207
- return <div>AI is analyzing your data...</div>;
208
- }
209
-
210
- if (lastResult?.errorMessage) {
211
- return <div>Error: {lastResult.errorMessage.message}</div>;
212
- }
213
-
214
- if (analysisPrompt) {
215
- return <div>Last query: "{analysisPrompt}"</div>;
216
- }
217
-
218
- return <div>Ask AI a question about your data</div>;
219
- }
220
- ```
221
-
222
- ## AiSlice API Reference
223
-
224
- The AiSlice provides a comprehensive set of state fields and methods for managing AI interactions in your application.
225
-
226
- ### State Fields
227
-
228
- #### `analysisPrompt`
229
-
230
- The current prompt text entered by the user for analysis.
231
-
232
- ```tsx
233
- const prompt = useRoomStore((state) => state.ai.analysisPrompt);
234
- ```
235
-
236
- #### `isRunningAnalysis`
237
-
238
- Boolean flag indicating whether an analysis is currently in progress.
239
-
240
- ```tsx
241
- const isRunning = useRoomStore((state) => state.ai.isRunningAnalysis);
242
- ```
243
-
244
- #### `tools`
245
-
246
- Record of available AI tools that can be used during analysis.
247
-
248
- ```tsx
249
- const availableTools = useRoomStore((state) => state.ai.tools);
250
- ```
251
-
252
- #### `analysisAbortController`
253
-
254
- Optional AbortController instance that can be used to cancel an ongoing analysis.
255
-
256
- ```tsx
257
- const abortController = useRoomStore(
258
- (state) => state.ai.analysisAbortController,
259
- );
260
- ```
261
-
262
- ### Methods
263
-
264
- #### `setAnalysisPrompt(prompt: string)`
265
-
266
- Sets the current analysis prompt text.
267
-
268
- ```tsx
269
- const setPrompt = useRoomStore((state) => state.ai.setAnalysisPrompt);
270
- setPrompt('Analyze sales trends for the last quarter');
271
- ```
272
-
273
- #### `startAnalysis()`
118
+ See [ai-nextjs](https://github.com/sqlrooms/sqlrooms/tree/main/examples/ai-nextjs) for a complete example.
274
119
 
275
- Starts the analysis process using the current prompt.
276
-
277
- ```tsx
278
- const startAnalysis = useRoomStore((state) => state.ai.startAnalysis);
279
- await startAnalysis();
280
- ```
281
-
282
- #### `cancelAnalysis()`
120
+ ## Data Structure
283
121
 
284
- Cancels any ongoing analysis.
122
+ The basic data structure of the AI package is:
285
123
 
286
- ```tsx
287
- const cancelAnalysis = useRoomStore((state) => state.ai.cancelAnalysis);
288
- cancelAnalysis();
124
+ ```ts
125
+ ai: {
126
+ sessions: [
127
+ {
128
+ id: string, // CUID2 identifier
129
+ name: string, // Session display name
130
+ modelProvider: string, // e.g., 'openai', 'anthropic'
131
+ model: string, // e.g., 'gpt-4o', 'claude-3-5-sonnet'
132
+ createdAt: Date,
133
+ // Primary storage: Full conversation history (AI SDK v5 format)
134
+ uiMessages: UIMessage[],
135
+ // Secondary storage: Error messages and legacy compatibility
136
+ analysisResults: AnalysisResult[],
137
+ // Tool execution data
138
+ toolAdditionalData: Record<string, unknown>,
139
+ },
140
+ ],
141
+ currentSessionId: string,
142
+ }
289
143
  ```
290
144
 
291
- #### `setAiModel(modelProvider: string, model: string)`
145
+ ### Session Schema
292
146
 
293
- Sets the AI model and provider for the current session.
147
+ Each session contains:
294
148
 
295
- ```tsx
296
- const setModel = useRoomStore((state) => state.ai.setAiModel);
297
- setModel('openai', 'gpt-4o');
298
- ```
299
-
300
- #### `createSession(name?: string, modelProvider?: string, model?: string)`
149
+ #### `uiMessages` - Complete Chat History
301
150
 
302
- Creates a new analysis session with optional name and model settings.
151
+ The `uiMessages` array stores the complete, flat conversation history using the Vercel AI SDK v5 `UIMessage` format. This includes:
303
152
 
304
- ```tsx
305
- const createSession = useRoomStore((state) => state.ai.createSession);
306
- createSession('Financial Analysis', 'openai', 'gpt-4o');
307
- ```
153
+ - User messages
154
+ - Assistant messages
155
+ - Tool call messages
156
+ - All message parts (text, tool invocations, etc.)
308
157
 
309
- #### `switchSession(sessionId: string)`
158
+ This is the **primary data structure** and serves as:
310
159
 
311
- Switches to a different analysis session by ID.
160
+ - The full context for AI model interactions
161
+ - The source for displaying conversation history
162
+ - The base for reconstructing analysis results
312
163
 
313
164
  ```tsx
314
- const switchSession = useRoomStore((state) => state.ai.switchSession);
315
- switchSession('session-123');
165
+ // Example: Accessing UI messages
166
+ const currentSession = useRoomStore((state) => state.ai.getCurrentSession());
167
+ const messages = currentSession?.uiMessages || [];
316
168
  ```
317
169
 
318
- #### `renameSession(sessionId: string, name: string)`
319
-
320
- Renames an existing analysis session.
170
+ #### `analysisResults` - Structured Analysis View
321
171
 
322
- ```tsx
323
- const renameSession = useRoomStore((state) => state.ai.renameSession);
324
- renameSession('session-123', 'Q4 Sales Analysis');
325
- ```
172
+ The `analysisResults` array is a **derived structure** that organizes messages into user prompt → AI response pairs. It primarily serves to:
326
173
 
327
- #### `deleteSession(sessionId: string)`
174
+ - Store error messages that occur during analysis
175
+ - Provide backward compatibility with legacy data
176
+ - Offer a simplified view of analysis history
328
177
 
329
- Deletes an analysis session by ID.
178
+ Analysis results are dynamically generated from `uiMessages` using the `transformMessagesToAnalysisResults` utility function.
330
179
 
331
- ```tsx
332
- const deleteSession = useRoomStore((state) => state.ai.deleteSession);
333
- deleteSession('session-123');
180
+ ```ts
181
+ type AnalysisResult = {
182
+ id: string; // Matches the UIMessage.id
183
+ prompt: string; // User's question/request
184
+ errorMessage?: ErrorMessageSchema; // Error if analysis failed
185
+ isCompleted: boolean; // Whether AI finished responding
186
+ };
334
187
  ```
335
188
 
336
- #### `getCurrentSession()`
337
-
338
- Returns the current active analysis session.
189
+ #### `toolAdditionalData` - Rich Tool Outputs
339
190
 
340
- ```tsx
341
- const currentSession = useRoomStore((state) => state.ai.getCurrentSession());
342
- ```
191
+ Each session also maintains a `toolAdditionalData` object that stores additional data from tool executions, keyed by `toolCallId`. This data is used for:
343
192
 
344
- #### `deleteAnalysisResult(sessionId: string, resultId: string)`
193
+ - Rendering tool-specific UI components
194
+ - Passing data between tool calls
195
+ - Preserving rich data that doesn't go back to the LLM
345
196
 
346
- Deletes a specific analysis result from a session.
197
+ ```ts
198
+ type ToolAdditionalData = Record<string, unknown>;
347
199
 
348
- ```tsx
349
- const deleteResult = useRoomStore((state) => state.ai.deleteAnalysisResult);
350
- deleteResult('session-123', 'result-456');
200
+ // Example: Storing tool additional data
201
+ const setToolData = useRoomStore((state) => state.ai.setSessionToolAdditionalData);
202
+ setToolData(sessionId, toolCallId, {chartData: [...]});
351
203
  ```
352
204
 
353
- #### `findToolComponent(toolName: string)`
354
-
355
- Finds the React component associated with a specific tool.
205
+ ## Tools
356
206
 
357
- ```tsx
358
- const ChartComponent = useRoomStore((state) =>
359
- state.ai.findToolComponent('chart'),
360
- );
361
- ```
207
+ In AI package, we provide a OpenAssistantTool type that supports not only `execute` function, but also `context` object and `component` object:
362
208
 
363
- ## Data Structure
209
+ - `execute` needs to return
210
+ - llmResult: the result send back to LLM (no raw data)
211
+ - additionalData: the data will be used by `component` and next `tool`
212
+ - `context`
213
+ - provide e.g. runtime or async data for `execute`
214
+ - `execute` can access `context` via `options.context`
215
+ - `component`
216
+ - use `additionalData` to render a React component for this `tool`
364
217
 
365
- The basic data structure of the AI package is:
218
+ For example, the `weather` tool is defined as follows:
366
219
 
367
220
  ```ts
368
- ai: {
369
- sessions: [
370
- {
371
- id: defaultSessionId,
372
- name: 'Default Session',
373
- modelProvider: 'openai',
374
- model: 'gpt-4o-mini',
375
- analysisResults: [],
376
- createdAt: new Date(),
221
+ const weatherTool: OpenAssistantTool = {
222
+ name: 'weather',
223
+ description: 'Get the weather in a city from a weather station',
224
+ parameters: z.object({cityName: z.string()}),
225
+ execute: async ({cityName}, options) => {
226
+ const getStation = options.context?.getStation;
227
+ const station = getStation ? await getStation(cityName) : null;
228
+ return {
229
+ llmResult: {
230
+ success: true,
231
+ details: `The weather in ${cityName} is sunny from weather station ${station}.`,
232
+ },
233
+ additionalData: {
234
+ weather: 'sunny',
235
+ station,
236
+ },
237
+ };
238
+ },
239
+ context: {
240
+ getStation: async (cityName: string) => {
241
+ const stations = {
242
+ 'New York': '123',
243
+ 'Los Angeles': '456',
244
+ Chicago: '789',
245
+ };
246
+ return stations[cityName];
377
247
  },
378
- ],
379
- currentSessionId: defaultSessionId,
380
- }
248
+ },
249
+ component: WeatherStationComponent,
250
+ };
381
251
  ```
382
252
 
383
- Each session has a `analysisResults` which is an array of `AnalysisResult`. Each `AnalysisResult` has the following properties:
384
-
385
- - `id`: The unique identifier for the analysis result
386
- - `prompt`: The user prompt that was used to generate the analysis result
387
- - `streamMessage`: The stream message from the LLM
388
- - `errorMessage`: The error message from the LLM
389
- - `isCompleted`: Whether the analysis result has been completed
390
-
391
- For each user prompt, the LLM will run multiple tools (e.g. `query`, `chart`) and return the result as the `streamMessage`. The structure of the `streamMessage` is as follows:
392
-
393
- - `text`: the final response from the LLM (streamable)
394
- - `reasoning`: the reasoning of the LLM (only for reason models)
395
- - `toolCallMessages`: the message array of the tool calls executed by the LLM
253
+ ### Tool Execution Flow
396
254
 
397
- Each `toolCallMessages` has the following properties:
255
+ 1. User sends a prompt → creates a user `UIMessage`
256
+ 2. AI processes and may call tools → creates assistant `UIMessage` with tool invocations
257
+ 3. Tools execute and return:
258
+ - `llmResult`: Text summary sent back to the LLM
259
+ - `additionalData`: Rich data stored in `toolAdditionalData` for UI rendering
260
+ 4. AI responds with final answer → creates assistant `UIMessage` with text
261
+ 5. On completion: `uiMessages` updated, `analysisResult` created with user message ID
398
262
 
399
- - `toolName`: the name of the tool
400
- - `toolCallId`: the id of the tool call
401
- - `args`: the arguments of the tool call
402
- - `llmResult`: the result from the execution of the tool, which will be sent back to the LLM as response.
403
- - `additionalData`: the additional data of the tool, which can be used to pass the output of the tool to next tool call or the component for rendering.
404
-
405
- ## Rendering
263
+ ### Rendering Tool Results
406
264
 
407
265
  ```text
408
266
  |--------------------------------|
@@ -411,71 +269,103 @@ Each `toolCallMessages` has the following properties:
411
269
  | |--------------------------| |
412
270
  | | AnalysisResult | |
413
271
  | | | |
414
- | | streamMessage | |
272
+ | | ErrorMessage | |
273
+ | | ------------ | |
274
+ | | UIMessage | |
415
275
  | | | |
416
276
  | | |---------------------| | |
417
- | | | Tools | | |
277
+ | | | Parts | | |
418
278
  | | |---------------------| | |
419
279
  | | | |---------------| | | |
420
- | | | |ToolCallMessage| | | |
421
- | | | |---------------| | | |
280
+ | | | |TextPart | | | |
422
281
  | | | |---------------| | | |
423
- | | | |ToolCallMessage| | | |
282
+ | | | |ToolPart | | | |
424
283
  | | | |---------------| | | |
425
284
  | | | ... | | |
426
285
  | | |---------------------| | |
427
286
  | | | |
428
- | | text | |
429
287
  | |--------------------------| |
430
288
  |--------------------------------|
431
289
  ```
432
290
 
433
- ## Tools
291
+ ### Transfer Additional Tool Output Data to Client
434
292
 
435
- In AI package, we provide a tool() to allow creating function tool for LLM to use. It is an extension of the `tool` from `vercel ai sdk`, and it supports not only `execute` function, but also `context` object and `component` object:
436
293
 
437
- - `execute` needs to return
438
- - llmResult: the result send back to LLM (no raw data)
439
- - additionalData: the data will be used by `component` and next `tool`
440
- - `context`
441
- - provide e.g. runtime or async data for `execute`
442
- - `execute` can access `context` via `options.context`
443
- - `component`
444
- - use `additionalData` to render a React component for this `tool`
294
+ #### The Problem
445
295
 
446
- For example, the `query` tool is defined as follows:
296
+ When tools execute, they often generate additional data (like detailed search results, charts, metadata) that needs to be sent to the client for UI rendering, but should NOT be included in the conversation history sent back to the LLM.
447
297
 
448
- ```ts
449
- const functions = {
450
- weather: tool({
451
- description: 'Get the weather in a city from a weather station',
452
- parameters: z.object({cityName: z.string()}),
453
- execute: async ({cityName}, options) => {
454
- const getStation = options.context?.getStation;
455
- const station = getStation ? await getStation(cityName) : null;
456
- return {
457
- llmResult: `The weather in ${cityName} is sunny from weather station ${station}.`,
458
- additionalData: {
459
- weather: 'sunny',
460
- station,
461
- },
462
- };
463
- },
464
- context: {
465
- getStation: async (cityName: string) => {
466
- const stations = {
467
- 'New York': '123',
468
- 'Los Angeles': '456',
469
- Chicago: '789',
470
- };
471
- return stations[cityName];
472
- },
473
- },
474
- component: WeatherStation,
475
- }),
476
- };
298
+ If the tool execution is done on the server side, the additional data needs to be transferred to the client side for UI rendering. We use the `data-tool-additional-output` data part type to transfer the additional data to the client.
299
+
300
+ #### Using `transient: true`
301
+
302
+ The AI SDK v5 provides a built-in solution through the `transient` flag on data parts. When you write a data part with `transient: true`, the SDK automatically prevents it from being added to the message history.
303
+
304
+ ##### Backend Implementation (route.ts)
305
+
306
+ ```typescript
307
+ writer.write({
308
+ type: 'data-tool-additional-output',
309
+ transient: true, // Won't be added to message history
310
+ data: {
311
+ toolCallId: chunk.toolCallId,
312
+ toolName: chunk.toolName,
313
+ output: getToolAdditionalData(chunk.toolCallId),
314
+ timestamp: new Date().toISOString(),
315
+ },
316
+ });
317
+ ```
318
+
319
+ #### The Flow
320
+
321
+ ```text
322
+ Backend (route.ts)
323
+
324
+ ├─> Tool executes
325
+ │ └─> writer.write({
326
+ │ type: 'data-tool-additional-output',
327
+ │ transient: true, // ✅ SDK handles exclusion
328
+ │ data: {...}
329
+ │ })
330
+
331
+
332
+ Client receives stream
333
+
334
+ ├─> onData callback
335
+ │ └─> setSessionToolAdditionalData() ✅ Stores in toolAdditionalData
336
+
337
+ └─> messages array ✅ Automatically excludes transient data parts
338
+
339
+ Session Storage (clean) → AI SDK → UI Display
340
+
341
+ Session Storage
342
+
343
+ Backend/LLM
477
344
  ```
478
345
 
346
+ #### Benefits of This Approach
347
+
348
+ 1. **✅ Clean Conversation History**: Transient data parts never appear in message history
349
+ 2. **✅ Efficient Token Usage**: No unnecessary data sent to the LLM
350
+ 3. **✅ Proper Data Storage**: Tool data is stored separately in `toolAdditionalData`
351
+ 4. **✅ UI Flexibility**: Components can access tool data via `toolAdditionalData[toolCallId]`
352
+ 5. **✅ Simple & Native**: Uses built-in SDK feature, no custom utilities needed
353
+ 6. **✅ Maintainable**: Follows SDK conventions and patterns
354
+ 7. **✅ No Manual Filtering**: SDK handles exclusion automatically
355
+
356
+ #### Usage in Components
357
+
358
+ To access the additional tool data in your components:
359
+
360
+ ```tsx
361
+ const currentSession = useRoomStore((state) => state.ai.getCurrentSession());
362
+ const toolData = currentSession?.toolAdditionalData?.[toolCallId];
363
+ ```
364
+
365
+ #### Alternative Considered: Message Annotations
366
+
367
+ AI SDK v5 supports message annotations, but these are still part of the message structure. The `transient` flag is specifically designed for data that should only be sent once and not persist in conversation history.
368
+
479
369
  ## Advanced Features
480
370
 
481
371
  - **Custom AI Tools**: Define custom tools for AI to use with the tool() function