@sqlrooms/ai-core 0.26.0-rc.6 → 0.26.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 (85) hide show
  1. package/README.md +171 -2
  2. package/dist/AiSlice.d.ts +35 -5
  3. package/dist/AiSlice.d.ts.map +1 -1
  4. package/dist/AiSlice.js +148 -14
  5. package/dist/AiSlice.js.map +1 -1
  6. package/dist/agents/AgentUtils.d.ts +61 -0
  7. package/dist/agents/AgentUtils.d.ts.map +1 -0
  8. package/dist/agents/AgentUtils.js +90 -0
  9. package/dist/agents/AgentUtils.js.map +1 -0
  10. package/dist/chatTransport.d.ts +17 -4
  11. package/dist/chatTransport.d.ts.map +1 -1
  12. package/dist/chatTransport.js +244 -19
  13. package/dist/chatTransport.js.map +1 -1
  14. package/dist/components/AnalysisAnswer.d.ts +2 -0
  15. package/dist/components/AnalysisAnswer.d.ts.map +1 -1
  16. package/dist/components/AnalysisAnswer.js +4 -2
  17. package/dist/components/AnalysisAnswer.js.map +1 -1
  18. package/dist/components/AnalysisResult.d.ts +7 -0
  19. package/dist/components/AnalysisResult.d.ts.map +1 -1
  20. package/dist/components/AnalysisResult.js +42 -42
  21. package/dist/components/AnalysisResult.js.map +1 -1
  22. package/dist/components/AnalysisResultsContainer.d.ts +4 -0
  23. package/dist/components/AnalysisResultsContainer.d.ts.map +1 -1
  24. package/dist/components/AnalysisResultsContainer.js +11 -4
  25. package/dist/components/AnalysisResultsContainer.js.map +1 -1
  26. package/dist/components/DeleteConfirmationDialog.d.ts +14 -0
  27. package/dist/components/DeleteConfirmationDialog.d.ts.map +1 -0
  28. package/dist/components/DeleteConfirmationDialog.js +6 -0
  29. package/dist/components/DeleteConfirmationDialog.js.map +1 -0
  30. package/dist/components/GroupedMessageParts.d.ts +25 -0
  31. package/dist/components/GroupedMessageParts.d.ts.map +1 -0
  32. package/dist/components/GroupedMessageParts.js +34 -0
  33. package/dist/components/GroupedMessageParts.js.map +1 -0
  34. package/dist/components/MessagePartsList.d.ts +23 -0
  35. package/dist/components/MessagePartsList.d.ts.map +1 -0
  36. package/dist/components/MessagePartsList.js +27 -0
  37. package/dist/components/MessagePartsList.js.map +1 -0
  38. package/dist/components/PromptSuggestions.d.ts +32 -0
  39. package/dist/components/PromptSuggestions.d.ts.map +1 -0
  40. package/dist/components/PromptSuggestions.js +69 -0
  41. package/dist/components/PromptSuggestions.js.map +1 -0
  42. package/dist/components/QueryControls.d.ts.map +1 -1
  43. package/dist/components/QueryControls.js +11 -4
  44. package/dist/components/QueryControls.js.map +1 -1
  45. package/dist/components/ReasoningBox.d.ts +21 -0
  46. package/dist/components/ReasoningBox.d.ts.map +1 -0
  47. package/dist/components/ReasoningBox.js +29 -0
  48. package/dist/components/ReasoningBox.js.map +1 -0
  49. package/dist/components/ToolCallInfo.d.ts.map +1 -1
  50. package/dist/components/ToolCallInfo.js +1 -11
  51. package/dist/components/ToolCallInfo.js.map +1 -1
  52. package/dist/components/ToolPartRenderer.d.ts +20 -0
  53. package/dist/components/ToolPartRenderer.d.ts.map +1 -0
  54. package/dist/components/ToolPartRenderer.js +85 -0
  55. package/dist/components/ToolPartRenderer.js.map +1 -0
  56. package/dist/components/tools/ToolErrorMessage.d.ts.map +1 -1
  57. package/dist/components/tools/ToolErrorMessage.js +1 -2
  58. package/dist/components/tools/ToolErrorMessage.js.map +1 -1
  59. package/dist/components/tools/ToolResult.d.ts.map +1 -1
  60. package/dist/components/tools/ToolResult.js +1 -1
  61. package/dist/components/tools/ToolResult.js.map +1 -1
  62. package/dist/hooks/useAiChat.d.ts +2 -0
  63. package/dist/hooks/useAiChat.d.ts.map +1 -1
  64. package/dist/hooks/useAiChat.js +56 -10
  65. package/dist/hooks/useAiChat.js.map +1 -1
  66. package/dist/hooks/useAssistantMessageParts.d.ts +14 -0
  67. package/dist/hooks/useAssistantMessageParts.d.ts.map +1 -0
  68. package/dist/hooks/useAssistantMessageParts.js +44 -0
  69. package/dist/hooks/useAssistantMessageParts.js.map +1 -0
  70. package/dist/hooks/useScrollToBottom.d.ts.map +1 -1
  71. package/dist/hooks/useScrollToBottom.js +4 -3
  72. package/dist/hooks/useScrollToBottom.js.map +1 -1
  73. package/dist/hooks/useToolGrouping.d.ts +23 -0
  74. package/dist/hooks/useToolGrouping.d.ts.map +1 -0
  75. package/dist/hooks/useToolGrouping.js +290 -0
  76. package/dist/hooks/useToolGrouping.js.map +1 -0
  77. package/dist/index.d.ts +5 -0
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +4 -0
  80. package/dist/index.js.map +1 -1
  81. package/dist/utils.d.ts +41 -1
  82. package/dist/utils.d.ts.map +1 -1
  83. package/dist/utils.js +91 -0
  84. package/dist/utils.js.map +1 -1
  85. package/package.json +8 -8
@@ -1,8 +1,72 @@
1
- import { DefaultChatTransport, convertToModelMessages, streamText, } from 'ai';
1
+ import { DefaultChatTransport, convertToModelMessages, streamText, lastAssistantMessageIsCompleteWithToolCalls, } from 'ai';
2
2
  import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
3
3
  import { convertToVercelAiToolV5 } from '@openassistant/utils';
4
4
  import { produce } from 'immer';
5
5
  import { getErrorMessageForDisplay } from '@sqlrooms/utils';
6
+ import { ToolAbortError } from './utils';
7
+ /**
8
+ * Validates and completes UIMessages to ensure all tool-call parts have corresponding tool-result parts.
9
+ * This is important when canceling with AbortController, which may leave incomplete tool-calls.
10
+ * Assumes sequential tool execution (only one tool runs at a time).
11
+ *
12
+ * @param messages - The messages to validate and complete
13
+ * @returns Cleaned messages with completed tool-call/result pairs
14
+ */
15
+ export function completeIncompleteToolCalls(messages) {
16
+ return messages.map((message) => {
17
+ if (message.role !== 'assistant' || !message.parts) {
18
+ return message;
19
+ }
20
+ const isToolPart = (part) => {
21
+ if (typeof part !== 'object' || part === null)
22
+ return false;
23
+ const p = part;
24
+ const typeVal = typeof p.type === 'string' ? p.type : undefined;
25
+ return (!!typeVal &&
26
+ 'toolCallId' in p &&
27
+ (typeVal === 'dynamic-tool' || typeVal.startsWith('tool-')));
28
+ };
29
+ const updatedParts = [...message.parts];
30
+ let sawAnyTool = false;
31
+ for (let i = updatedParts.length - 1; i >= 0; i--) {
32
+ const current = updatedParts[i];
33
+ if (!isToolPart(current)) {
34
+ // Stop once we exit the trailing tool region
35
+ if (sawAnyTool)
36
+ break;
37
+ continue;
38
+ }
39
+ sawAnyTool = true;
40
+ const toolPart = current;
41
+ const hasOutput = toolPart.state?.startsWith('output');
42
+ if (hasOutput) {
43
+ // Completed tool; continue checking earlier parts just in case
44
+ continue;
45
+ }
46
+ // Synthesize a completed error result for the incomplete tool call
47
+ const base = {
48
+ toolCallId: toolPart.toolCallId,
49
+ state: 'output-error',
50
+ input: toolPart.input ?? {},
51
+ errorText: 'Operation cancelled by user',
52
+ providerExecuted: false,
53
+ };
54
+ const syntheticPart = toolPart.type === 'dynamic-tool'
55
+ ? {
56
+ type: 'dynamic-tool',
57
+ toolName: toolPart.toolName || 'unknown',
58
+ ...base,
59
+ }
60
+ : { type: toolPart.type, ...base };
61
+ updatedParts[i] =
62
+ syntheticPart;
63
+ }
64
+ return {
65
+ ...message,
66
+ parts: updatedParts,
67
+ };
68
+ });
69
+ }
6
70
  /**
7
71
  * Creates a handler for tool completion that updates the tool additional data in the store
8
72
  */
@@ -19,7 +83,7 @@ function createOnToolCompletedHandler(store) {
19
83
  /**
20
84
  * Converts OpenAssistant tools to Vercel AI SDK tools with onToolCompleted handler
21
85
  */
22
- function convertToAiSDKTools(tools, onToolCompleted) {
86
+ export function convertToAiSDKTools(tools, onToolCompleted) {
23
87
  return Object.entries(tools || {}).reduce((acc, [name, tool]) => {
24
88
  acc[name] = convertToVercelAiToolV5({
25
89
  ...tool,
@@ -48,15 +112,32 @@ export function createLocalChatTransportFactory({ store, defaultProvider, defaul
48
112
  });
49
113
  model = openai.chatModel(modelId);
50
114
  }
115
+ // Parse caller-supplied body defensively to avoid breaking the stream
51
116
  const body = init?.body;
52
- const parsed = body ? JSON.parse(body) : {};
53
- const messagesCopy = JSON.parse(JSON.stringify(parsed.messages || []));
117
+ let parsed = {};
118
+ try {
119
+ parsed = body ? JSON.parse(body) : {};
120
+ }
121
+ catch {
122
+ parsed = {};
123
+ }
124
+ const parsedObj = parsed || {};
125
+ const messagesCopy = Array.isArray(parsedObj.messages)
126
+ ? parsedObj.messages
127
+ : [];
54
128
  const onToolCompleted = createOnToolCompletedHandler(store);
55
129
  const tools = convertToAiSDKTools(state.ai.tools || {}, onToolCompleted);
130
+ // Remove execute from tools for the model call so tool invocations are
131
+ // handled exclusively by onChatToolCall. convertToAiSDKTools is expected
132
+ // to return fresh tool objects; if that ever changes, clone before mutate.
133
+ Object.values(tools).forEach((tool) => {
134
+ tool.execute = undefined;
135
+ });
56
136
  // get system instructions dynamically at request time to ensure fresh table schema
57
137
  const systemInstructions = getInstructions();
58
138
  const result = streamText({
59
139
  model,
140
+ // Ensure we always pass an array of messages
60
141
  messages: convertToModelMessages(messagesCopy),
61
142
  tools,
62
143
  system: systemInstructions,
@@ -75,11 +156,20 @@ export function createRemoteChatTransportFactory(params) {
75
156
  const currentSession = state.ai.getCurrentSession();
76
157
  const modelProvider = currentSession?.modelProvider || params.defaultProvider;
77
158
  const model = currentSession?.model || params.defaultModel;
78
- // Parse the existing body and add model information
159
+ // Parse the existing body and add model information (defensive parsing)
79
160
  const body = init?.body;
80
- const parsed = body ? JSON.parse(body) : {};
161
+ let parsed = {};
162
+ try {
163
+ parsed = body ? JSON.parse(body) : {};
164
+ }
165
+ catch {
166
+ parsed = {};
167
+ }
168
+ const parsedObj = typeof parsed === 'object' && parsed !== null
169
+ ? parsed
170
+ : {};
81
171
  const enhancedBody = {
82
- ...parsed,
172
+ ...parsedObj,
83
173
  modelProvider,
84
174
  model,
85
175
  };
@@ -104,14 +194,30 @@ export function createChatHandlers({ store }) {
104
194
  try {
105
195
  // handle client tools
106
196
  const state = store.getState();
197
+ // Check if the stream was aborted before executing tool
198
+ if (state.ai.analysisAbortController?.signal.aborted) {
199
+ if (addToolResult) {
200
+ addToolResult({
201
+ tool: toolName,
202
+ toolCallId,
203
+ state: 'output-error',
204
+ errorText: 'Operation cancelled by user',
205
+ });
206
+ }
207
+ return;
208
+ }
107
209
  const onToolCompleted = createOnToolCompletedHandler(store);
108
210
  const tools = convertToAiSDKTools(state.ai.tools || {}, onToolCompleted);
109
211
  // find tool from tools using toolName
110
212
  const tool = tools[toolName];
111
- if (tool && tool.execute) {
213
+ if (tool && state.ai.tools[toolName]?.execute && tool.execute) {
214
+ // Always provide a defined messages array to the tool runtime
215
+ const sessionMessages = (state.ai.getCurrentSession()?.uiMessages ??
216
+ []);
112
217
  const llmResult = await tool.execute(input, {
113
218
  toolCallId,
114
- messages: [],
219
+ messages: convertToModelMessages(sessionMessages),
220
+ abortSignal: state.ai.analysisAbortController?.signal,
115
221
  });
116
222
  if (addToolResult) {
117
223
  // Note: When using sendAutomaticallyWhen, avoid awaiting addToolResult to prevent deadlocks
@@ -122,21 +228,54 @@ export function createChatHandlers({ store }) {
122
228
  });
123
229
  }
124
230
  }
231
+ else {
232
+ // Tool has no execute function - wait for UI component to call addToolResult
233
+ // Check if there's a ToolComponent for this tool
234
+ const hasToolComponent = !!state.ai.findToolComponent(toolName);
235
+ if (hasToolComponent && state.ai.waitForToolResult) {
236
+ try {
237
+ // Wait for the UI component to call addToolResult
238
+ await state.ai.waitForToolResult(toolCallId, state.ai.analysisAbortController?.signal);
239
+ }
240
+ catch (error) {
241
+ // If waiting was cancelled or failed, ensure we add an error result
242
+ if (addToolResult && error instanceof Error) {
243
+ addToolResult({
244
+ tool: toolName,
245
+ toolCallId,
246
+ state: 'output-error',
247
+ errorText: error.message,
248
+ });
249
+ }
250
+ // Re-throw to let the outer catch handle it
251
+ throw error;
252
+ }
253
+ }
254
+ // If no ToolComponent, we still return (no-op) - the UI won't render anything
255
+ // and the tool call will remain incomplete, which is fine for error handling
256
+ }
125
257
  }
126
258
  catch (error) {
259
+ // Check if this is an abort error
260
+ const isAbortError = error instanceof ToolAbortError;
127
261
  if (addToolResult) {
128
262
  addToolResult({
129
263
  tool: toolName,
130
264
  toolCallId,
131
265
  state: 'output-error',
132
- errorText: getErrorMessageForDisplay(error),
266
+ errorText: isAbortError
267
+ ? 'Operation cancelled by user'
268
+ : getErrorMessageForDisplay(error),
133
269
  });
134
270
  }
135
271
  }
136
272
  },
273
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
137
274
  onChatData: (dataPart) => {
138
- // Handle additional tool output data from the backend
139
- if (dataPart.type === 'data-tool-additional-output') {
275
+ // Handle additional tool output data from the backend (defensive guards)
276
+ if (dataPart.type === 'data-tool-additional-output' &&
277
+ dataPart.data &&
278
+ dataPart.data.toolCallId != null) {
140
279
  const { toolCallId, output } = dataPart.data;
141
280
  // Store the additional data in the session
142
281
  const currentSessionId = store.getState().ai.config.currentSessionId;
@@ -152,14 +291,69 @@ export function createChatHandlers({ store }) {
152
291
  const currentSessionId = store.getState().ai.config.currentSessionId;
153
292
  if (!currentSessionId)
154
293
  return;
155
- store.getState().ai.setSessionUiMessages(currentSessionId, messages);
294
+ // If the analysis has been aborted, force-complete and clean up immediately
295
+ const aborted = !!store.getState().ai.analysisAbortController?.signal.aborted;
296
+ if (aborted) {
297
+ // If messages are empty (possible when stopping immediately), fall back to existing session messages
298
+ const sessionMessages = store.getState().ai.getCurrentSession()
299
+ ?.uiMessages || [];
300
+ const sourceMessages = messages && messages.length > 0 ? messages : sessionMessages;
301
+ const completedMessages = completeIncompleteToolCalls(sourceMessages);
302
+ store
303
+ .getState()
304
+ .ai.setSessionUiMessages(currentSessionId, completedMessages);
305
+ // Ensure an analysis result exists and is marked as cancelled
306
+ store.setState((state) => produce(state, (draft) => {
307
+ draft.ai.isRunningAnalysis = false;
308
+ draft.ai.analysisAbortController = undefined;
309
+ const targetSession = draft.ai.config.sessions.find((s) => s.id === currentSessionId);
310
+ if (!targetSession)
311
+ return;
312
+ // Find the last user message
313
+ const lastUserMessage = completedMessages
314
+ .filter((msg) => msg.role === 'user')
315
+ .slice(-1)[0];
316
+ if (!lastUserMessage)
317
+ return;
318
+ const promptText = lastUserMessage.parts
319
+ .filter((part) => part.type === 'text')
320
+ .map((part) => part.text)
321
+ .join('');
322
+ const pendingIndex = targetSession.analysisResults.findIndex((result) => result.id === '__pending__');
323
+ if (pendingIndex !== -1) {
324
+ targetSession.analysisResults[pendingIndex] = {
325
+ id: lastUserMessage.id,
326
+ prompt: promptText,
327
+ errorMessage: { error: 'Operation cancelled by user' },
328
+ isCompleted: true,
329
+ };
330
+ }
331
+ else {
332
+ const existing = targetSession.analysisResults.find((r) => r.id === lastUserMessage.id);
333
+ if (!existing) {
334
+ targetSession.analysisResults.push({
335
+ id: lastUserMessage.id,
336
+ prompt: promptText,
337
+ errorMessage: { error: 'Operation cancelled by user' },
338
+ isCompleted: true,
339
+ });
340
+ }
341
+ }
342
+ }));
343
+ return;
344
+ }
345
+ // Complete any incomplete tool-calls before saving (can happen with AbortController)
346
+ const completedMessages = completeIncompleteToolCalls(messages);
347
+ store
348
+ .getState()
349
+ .ai.setSessionUiMessages(currentSessionId, completedMessages);
156
350
  // Create or update analysis result with the user message ID for proper correlation
157
351
  store.setState((state) => produce(state, (draft) => {
158
352
  const targetSession = draft.ai.config.sessions.find((s) => s.id === currentSessionId);
159
353
  if (!targetSession)
160
354
  return;
161
355
  // Find the last user message to get its ID and prompt
162
- const lastUserMessage = messages
356
+ const lastUserMessage = completedMessages
163
357
  .filter((msg) => msg.role === 'user')
164
358
  .slice(-1)[0];
165
359
  if (lastUserMessage) {
@@ -192,11 +386,38 @@ export function createChatHandlers({ store }) {
192
386
  }
193
387
  }
194
388
  }));
195
- store.setState((state) => produce(state, (draft) => {
196
- draft.ai.isRunningAnalysis = false;
197
- draft.ai.analysisPrompt = '';
198
- draft.ai.analysisAbortController = undefined;
199
- }));
389
+ // Determine if SDK wants to auto-send a follow-up turn (i.e., more steps pending)
390
+ const shouldAutoSendNext = lastAssistantMessageIsCompleteWithToolCalls({
391
+ messages: completedMessages,
392
+ });
393
+ // Step-aware completion: look only at parts after the most recent step-start
394
+ const lastMessage = completedMessages[completedMessages.length - 1];
395
+ const isLastMessageAssistant = lastMessage?.role === 'assistant';
396
+ let tailHasTool = false;
397
+ if (isLastMessageAssistant) {
398
+ const parts = lastMessage?.parts ?? [];
399
+ let lastStepStartIndex = -1;
400
+ for (let i = parts.length - 1; i >= 0; i--) {
401
+ if (parts[i]?.type === 'step-start') {
402
+ lastStepStartIndex = i;
403
+ break;
404
+ }
405
+ }
406
+ const tailParts = parts.slice(lastStepStartIndex + 1);
407
+ tailHasTool = tailParts.some((part) => typeof part?.type === 'string' &&
408
+ (part.type.startsWith('tool-') || part.type === 'dynamic-tool'));
409
+ }
410
+ // End analysis when there is no autosend and there are no pending tool parts
411
+ // even if the assistant didn't emit additional text (e.g., tool-only tails).
412
+ const shouldEndAnalysis = (isLastMessageAssistant && !shouldAutoSendNext && !tailHasTool) ||
413
+ (!shouldAutoSendNext && !isLastMessageAssistant);
414
+ if (shouldEndAnalysis) {
415
+ store.setState((state) => produce(state, (draft) => {
416
+ draft.ai.isRunningAnalysis = false;
417
+ draft.ai.analysisPrompt = '';
418
+ draft.ai.analysisAbortController = undefined;
419
+ }));
420
+ }
200
421
  }
201
422
  catch (err) {
202
423
  console.error('onChatFinish error:', err);
@@ -215,6 +436,10 @@ export function createChatHandlers({ store }) {
215
436
  return;
216
437
  const targetSession = draft.ai.config.sessions.find((s) => s.id === currentSessionId);
217
438
  if (targetSession) {
439
+ // Ensure message structure is valid even on errors
440
+ const existingMessages = (targetSession.uiMessages ||
441
+ []);
442
+ targetSession.uiMessages = completeIncompleteToolCalls(existingMessages);
218
443
  // Find the last user message to create analysis result with correct ID
219
444
  const uiMessages = targetSession.uiMessages;
220
445
  const lastUserMessage = uiMessages
@@ -1 +1 @@
1
- {"version":3,"file":"chatTransport.js","sourceRoot":"","sources":["../src/chatTransport.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DefaultChatTransport,\n UIMessage,\n convertToModelMessages,\n streamText,\n} from 'ai';\nimport type {LanguageModel, ToolSet} from 'ai';\nimport {createOpenAICompatible} from '@ai-sdk/openai-compatible';\nimport {convertToVercelAiToolV5, OpenAssistantTool} from '@openassistant/utils';\nimport {produce} from 'immer';\nimport {getErrorMessageForDisplay} from '@sqlrooms/utils';\nimport type {AiSliceState} from './AiSlice';\nimport type {AnalysisSessionSchema} from '@sqlrooms/ai-config';\nimport {AddToolResult} from './hooks/useAiChat';\nimport type {StoreApi} from '@sqlrooms/room-store';\n\ntype ToolCall = {\n input: string;\n toolCallId: string;\n toolName: string;\n type: 'tool-input-available';\n};\n\nexport type ChatTransportConfig = {\n store: StoreApi<AiSliceState>;\n defaultProvider: string;\n defaultModel: string;\n apiKey: string;\n baseUrl?: string;\n headers?: Record<string, string>;\n getInstructions: () => string;\n /**\n * Optional: supply a pre-configured custom model.\n * e.g. import {xai} from \"@ai-sdk/xai\";\n * getCustomModel: () => xai('grok-4')\n * If provided, this model will be used instead of the default OpenAI-compatible client.\n */\n getCustomModel?: () => LanguageModel | undefined;\n};\n\n/**\n * Creates a handler for tool completion that updates the tool additional data in the store\n */\nfunction createOnToolCompletedHandler(store: StoreApi<AiSliceState>) {\n return (toolCallId: string, additionalData: unknown) => {\n const sessionId = store.getState().ai.config.currentSessionId;\n if (!sessionId) return;\n\n store\n .getState()\n .ai.setSessionToolAdditionalData(sessionId, toolCallId, additionalData);\n };\n}\n\n/**\n * Converts OpenAssistant tools to Vercel AI SDK tools with onToolCompleted handler\n */\nfunction convertToAiSDKTools(\n tools: Record<string, OpenAssistantTool>,\n onToolCompleted: (toolCallId: string, additionalData: unknown) => void,\n): ToolSet {\n return Object.entries(tools || {}).reduce(\n (acc: ToolSet, [name, tool]: [string, OpenAssistantTool]) => {\n acc[name] = convertToVercelAiToolV5({\n ...tool,\n onToolCompleted,\n });\n return acc;\n },\n {},\n );\n}\n\nexport function createLocalChatTransportFactory({\n store,\n defaultProvider,\n defaultModel,\n apiKey,\n baseUrl,\n headers,\n getInstructions,\n getCustomModel,\n}: ChatTransportConfig) {\n return () => {\n const fetchImpl = async (_input: RequestInfo | URL, init?: RequestInit) => {\n // Resolve provider/model and client at call time to pick up latest settings\n const state = store.getState();\n const currentSession = state.ai.getCurrentSession();\n const provider = currentSession?.modelProvider || defaultProvider;\n const modelId = currentSession?.model || defaultModel;\n\n // Prefer a user-supplied model if available\n let model: LanguageModel | undefined = getCustomModel?.();\n\n // Fallback to OpenAI-compatible if no custom model provided\n if (!model) {\n const openai = createOpenAICompatible({\n apiKey,\n name: provider,\n baseURL: baseUrl || 'https://api.openai.com/v1',\n headers,\n });\n model = openai.chatModel(modelId);\n }\n\n const body = init?.body as string;\n const parsed = body ? JSON.parse(body) : {};\n const messagesCopy = JSON.parse(JSON.stringify(parsed.messages || []));\n\n const onToolCompleted = createOnToolCompletedHandler(store);\n const tools = convertToAiSDKTools(state.ai.tools || {}, onToolCompleted);\n\n // get system instructions dynamically at request time to ensure fresh table schema\n const systemInstructions = getInstructions();\n\n const result = streamText({\n model,\n messages: convertToModelMessages(messagesCopy),\n tools,\n system: systemInstructions,\n abortSignal: state.ai.analysisAbortController?.signal,\n });\n\n return result.toUIMessageStreamResponse();\n };\n\n return new DefaultChatTransport({fetch: fetchImpl});\n };\n}\n\nexport function createRemoteChatTransportFactory(params: {\n store: StoreApi<AiSliceState>;\n defaultProvider: string;\n defaultModel: string;\n}) {\n return (endpoint: string, headers?: Record<string, string>) => {\n const fetchImpl = async (input: RequestInfo | URL, init?: RequestInit) => {\n // Get current session's model and provider at request time\n const state = params.store.getState();\n const currentSession = state.ai.getCurrentSession();\n const modelProvider =\n currentSession?.modelProvider || params.defaultProvider;\n const model = currentSession?.model || params.defaultModel;\n\n // Parse the existing body and add model information\n const body = init?.body as string;\n const parsed = body ? JSON.parse(body) : {};\n\n const enhancedBody = {\n ...parsed,\n modelProvider,\n model,\n };\n\n // Make the request with enhanced body\n return fetch(input, {\n ...init,\n body: JSON.stringify(enhancedBody),\n });\n };\n\n return new DefaultChatTransport({\n api: endpoint,\n credentials: 'include',\n headers,\n fetch: fetchImpl,\n });\n };\n}\n\nexport function createChatHandlers({store}: {store: StoreApi<AiSliceState>}) {\n return {\n onChatToolCall: async ({\n toolCall,\n addToolResult,\n }: {\n toolCall: ToolCall;\n addToolResult?: AddToolResult;\n }) => {\n const {input, toolCallId, toolName} = toolCall;\n try {\n // handle client tools\n const state = store.getState();\n\n const onToolCompleted = createOnToolCompletedHandler(store);\n const tools = convertToAiSDKTools(\n state.ai.tools || {},\n onToolCompleted,\n );\n\n // find tool from tools using toolName\n const tool = tools[toolName];\n if (tool && tool.execute) {\n const llmResult = await tool.execute(input, {\n toolCallId,\n messages: [],\n });\n\n if (addToolResult) {\n // Note: When using sendAutomaticallyWhen, avoid awaiting addToolResult to prevent deadlocks\n addToolResult({\n tool: toolName,\n toolCallId,\n output: llmResult,\n });\n }\n }\n } catch (error) {\n if (addToolResult) {\n addToolResult({\n tool: toolName,\n toolCallId,\n state: 'output-error',\n errorText: getErrorMessageForDisplay(error),\n });\n }\n }\n },\n onChatData: (dataPart: any) => {\n // Handle additional tool output data from the backend\n if (dataPart.type === 'data-tool-additional-output') {\n const {toolCallId, output} = dataPart.data;\n\n // Store the additional data in the session\n const currentSessionId = store.getState().ai.config.currentSessionId;\n if (currentSessionId) {\n store\n .getState()\n .ai.setSessionToolAdditionalData(\n currentSessionId,\n toolCallId,\n output,\n );\n }\n }\n },\n onChatFinish: ({messages}: {messages: UIMessage[]}) => {\n try {\n const currentSessionId = store.getState().ai.config.currentSessionId;\n if (!currentSessionId) return;\n\n store.getState().ai.setSessionUiMessages(currentSessionId, messages);\n\n // Create or update analysis result with the user message ID for proper correlation\n store.setState((state: AiSliceState) =>\n produce(state, (draft: AiSliceState) => {\n const targetSession = draft.ai.config.sessions.find(\n (s: AnalysisSessionSchema) => s.id === currentSessionId,\n );\n if (!targetSession) return;\n\n // Find the last user message to get its ID and prompt\n const lastUserMessage = messages\n .filter((msg) => msg.role === 'user')\n .slice(-1)[0];\n\n if (lastUserMessage) {\n // Extract text content from user message\n const promptText = lastUserMessage.parts\n .filter((part) => part.type === 'text')\n .map((part) => (part as {text: string}).text)\n .join('');\n\n // Check if there's a pending analysis result\n const pendingIndex = targetSession.analysisResults.findIndex(\n (result: any) => result.id === '__pending__',\n );\n\n if (pendingIndex !== -1) {\n // Update the pending result with actual data\n targetSession.analysisResults[pendingIndex] = {\n id: lastUserMessage.id,\n prompt: promptText,\n isCompleted: true,\n };\n } else {\n // Check if analysis result already exists for this user message\n const existingResult = targetSession.analysisResults.find(\n (result: any) => result.id === lastUserMessage.id,\n );\n\n if (!existingResult) {\n // Create analysis result with the same ID as the user message\n targetSession.analysisResults.push({\n id: lastUserMessage.id,\n prompt: promptText,\n isCompleted: true,\n });\n }\n }\n }\n }),\n );\n store.setState((state: AiSliceState) =>\n produce(state, (draft: AiSliceState) => {\n draft.ai.isRunningAnalysis = false;\n draft.ai.analysisPrompt = '';\n draft.ai.analysisAbortController = undefined;\n }),\n );\n } catch (err) {\n console.error('onChatFinish error:', err);\n throw err;\n }\n },\n onChatError: (error: unknown) => {\n try {\n let errMsg = getErrorMessageForDisplay(error);\n if (!errMsg || errMsg.trim().length === 0) {\n errMsg = 'Unknown error';\n }\n const currentSessionId = store.getState().ai.config.currentSessionId;\n store.setState((state: AiSliceState) =>\n produce(state, (draft: AiSliceState) => {\n if (!currentSessionId) return;\n const targetSession = draft.ai.config.sessions.find(\n (s: AnalysisSessionSchema) => s.id === currentSessionId,\n );\n if (targetSession) {\n // Find the last user message to create analysis result with correct ID\n const uiMessages = targetSession.uiMessages as UIMessage[];\n const lastUserMessage = uiMessages\n .filter((msg) => msg.role === 'user')\n .slice(-1)[0];\n\n if (lastUserMessage) {\n // Extract text content from user message\n const promptText = lastUserMessage.parts\n .filter((part: any) => part.type === 'text')\n .map((part: any) => (part as {text: string}).text)\n .join('');\n\n // Check if there's a pending analysis result\n const pendingIndex = targetSession.analysisResults.findIndex(\n (result: any) => result.id === '__pending__',\n );\n\n if (pendingIndex !== -1) {\n // Update the pending result with error\n targetSession.analysisResults[pendingIndex] = {\n id: lastUserMessage.id,\n prompt: promptText,\n errorMessage: {error: errMsg},\n isCompleted: true,\n };\n } else {\n // Check if analysis result already exists for this user message\n const existingResult = targetSession.analysisResults.find(\n (result: any) => result.id === lastUserMessage.id,\n );\n\n if (!existingResult) {\n // Create analysis result with the same ID as the user message\n targetSession.analysisResults.push({\n id: lastUserMessage.id,\n prompt: promptText,\n errorMessage: {error: errMsg},\n isCompleted: true,\n });\n } else {\n // Update existing result with error message\n existingResult.errorMessage = {error: errMsg};\n }\n }\n }\n }\n draft.ai.isRunningAnalysis = false;\n draft.ai.analysisAbortController = undefined;\n }),\n );\n } catch (err) {\n console.error('Failed to store chat error:', err);\n throw err;\n }\n },\n };\n}\n"]}
1
+ {"version":3,"file":"chatTransport.js","sourceRoot":"","sources":["../src/chatTransport.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DefaultChatTransport,\n UIMessage,\n convertToModelMessages,\n streamText,\n lastAssistantMessageIsCompleteWithToolCalls,\n} from 'ai';\nimport type {DataUIPart, LanguageModel, ToolSet} from 'ai';\nimport {createOpenAICompatible} from '@ai-sdk/openai-compatible';\nimport {convertToVercelAiToolV5, OpenAssistantTool} from '@openassistant/utils';\nimport {produce} from 'immer';\nimport {getErrorMessageForDisplay} from '@sqlrooms/utils';\nimport type {AiSliceState} from './AiSlice';\nimport type {AnalysisSessionSchema} from '@sqlrooms/ai-config';\nimport {AddToolResult} from './hooks/useAiChat';\nimport type {StoreApi} from '@sqlrooms/room-store';\nimport {ToolAbortError} from './utils';\n\n/**\n * Validates and completes UIMessages to ensure all tool-call parts have corresponding tool-result parts.\n * This is important when canceling with AbortController, which may leave incomplete tool-calls.\n * Assumes sequential tool execution (only one tool runs at a time).\n *\n * @param messages - The messages to validate and complete\n * @returns Cleaned messages with completed tool-call/result pairs\n */\nexport function completeIncompleteToolCalls(\n messages: UIMessage[],\n): UIMessage[] {\n return messages.map((message) => {\n if (message.role !== 'assistant' || !message.parts) {\n return message;\n }\n\n // Walk backward and complete any TRAILING tool parts that lack output.\n // This covers multi-tool-step aborts where several tool calls were started\n // but the stream was cancelled before the outputs were emitted.\n type ToolPart = {\n type: string;\n toolCallId: string;\n toolName?: string;\n input?: unknown;\n state?: string;\n };\n const isToolPart = (part: unknown): part is ToolPart => {\n if (typeof part !== 'object' || part === null) return false;\n const p = part as Record<string, unknown> & {type?: unknown};\n const typeVal =\n typeof p.type === 'string' ? (p.type as string) : undefined;\n return (\n !!typeVal &&\n 'toolCallId' in p &&\n (typeVal === 'dynamic-tool' || typeVal.startsWith('tool-'))\n );\n };\n\n const updatedParts = [...message.parts];\n let sawAnyTool = false;\n for (let i = updatedParts.length - 1; i >= 0; i--) {\n const current = updatedParts[i] as unknown;\n if (!isToolPart(current)) {\n // Stop once we exit the trailing tool region\n if (sawAnyTool) break;\n continue;\n }\n sawAnyTool = true;\n const toolPart = current as ToolPart;\n const hasOutput = toolPart.state?.startsWith('output');\n if (hasOutput) {\n // Completed tool; continue checking earlier parts just in case\n continue;\n }\n\n // Synthesize a completed error result for the incomplete tool call\n const base = {\n toolCallId: toolPart.toolCallId,\n state: 'output-error' as const,\n input: toolPart.input ?? {},\n errorText: 'Operation cancelled by user',\n providerExecuted: false,\n };\n\n const syntheticPart =\n toolPart.type === 'dynamic-tool'\n ? {\n type: 'dynamic-tool' as const,\n toolName: toolPart.toolName || 'unknown',\n ...base,\n }\n : {type: toolPart.type as string, ...base};\n\n updatedParts[i] =\n syntheticPart as unknown as (typeof message.parts)[number];\n }\n\n return {\n ...message,\n parts: updatedParts,\n };\n });\n}\n\nexport type ToolCall = {\n input: string;\n toolCallId: string;\n toolName: string;\n type: 'tool-input-available';\n};\n\nexport type ChatTransportConfig = {\n store: StoreApi<AiSliceState>;\n defaultProvider: string;\n defaultModel: string;\n apiKey: string;\n baseUrl?: string;\n headers?: Record<string, string>;\n getInstructions: () => string;\n /**\n * Optional: supply a pre-configured custom model.\n * e.g. import {xai} from \"@ai-sdk/xai\";\n * getCustomModel: () => xai('grok-4')\n * If provided, this model will be used instead of the default OpenAI-compatible client.\n */\n getCustomModel?: () => LanguageModel | undefined;\n};\n\n/**\n * Creates a handler for tool completion that updates the tool additional data in the store\n */\nfunction createOnToolCompletedHandler(store: StoreApi<AiSliceState>) {\n return (toolCallId: string, additionalData: unknown) => {\n const sessionId = store.getState().ai.config.currentSessionId;\n if (!sessionId) return;\n\n store\n .getState()\n .ai.setSessionToolAdditionalData(sessionId, toolCallId, additionalData);\n };\n}\n\n/**\n * Converts OpenAssistant tools to Vercel AI SDK tools with onToolCompleted handler\n */\nexport function convertToAiSDKTools(\n tools: Record<string, OpenAssistantTool>,\n onToolCompleted?: (toolCallId: string, additionalData: unknown) => void,\n): ToolSet {\n return Object.entries(tools || {}).reduce(\n (acc: ToolSet, [name, tool]: [string, OpenAssistantTool]) => {\n acc[name] = convertToVercelAiToolV5({\n ...tool,\n onToolCompleted,\n });\n return acc;\n },\n {},\n );\n}\n\nexport function createLocalChatTransportFactory({\n store,\n defaultProvider,\n defaultModel,\n apiKey,\n baseUrl,\n headers,\n getInstructions,\n getCustomModel,\n}: ChatTransportConfig) {\n return () => {\n const fetchImpl = async (_input: RequestInfo | URL, init?: RequestInit) => {\n // Resolve provider/model and client at call time to pick up latest settings\n const state = store.getState();\n const currentSession = state.ai.getCurrentSession();\n const provider = currentSession?.modelProvider || defaultProvider;\n const modelId = currentSession?.model || defaultModel;\n\n // Prefer a user-supplied model if available\n let model: LanguageModel | undefined = getCustomModel?.();\n\n // Fallback to OpenAI-compatible if no custom model provided\n if (!model) {\n const openai = createOpenAICompatible({\n apiKey,\n name: provider,\n baseURL: baseUrl || 'https://api.openai.com/v1',\n headers,\n });\n model = openai.chatModel(modelId);\n }\n\n // Parse caller-supplied body defensively to avoid breaking the stream\n const body = init?.body as string;\n let parsed: unknown = {};\n try {\n parsed = body ? JSON.parse(body) : {};\n } catch {\n parsed = {};\n }\n const parsedObj = (parsed as {messages?: unknown}) || {};\n const messagesCopy = Array.isArray(parsedObj.messages)\n ? (parsedObj.messages as UIMessage[])\n : [];\n\n const onToolCompleted = createOnToolCompletedHandler(store);\n const tools = convertToAiSDKTools(state.ai.tools || {}, onToolCompleted);\n // Remove execute from tools for the model call so tool invocations are\n // handled exclusively by onChatToolCall. convertToAiSDKTools is expected\n // to return fresh tool objects; if that ever changes, clone before mutate.\n Object.values(tools).forEach((tool) => {\n tool.execute = undefined;\n });\n\n // get system instructions dynamically at request time to ensure fresh table schema\n const systemInstructions = getInstructions();\n\n const result = streamText({\n model,\n // Ensure we always pass an array of messages\n messages: convertToModelMessages(messagesCopy),\n tools,\n system: systemInstructions,\n abortSignal: state.ai.analysisAbortController?.signal,\n });\n\n return result.toUIMessageStreamResponse();\n };\n\n return new DefaultChatTransport({fetch: fetchImpl});\n };\n}\n\nexport function createRemoteChatTransportFactory(params: {\n store: StoreApi<AiSliceState>;\n defaultProvider: string;\n defaultModel: string;\n}) {\n return (endpoint: string, headers?: Record<string, string>) => {\n const fetchImpl = async (input: RequestInfo | URL, init?: RequestInit) => {\n // Get current session's model and provider at request time\n const state = params.store.getState();\n const currentSession = state.ai.getCurrentSession();\n const modelProvider =\n currentSession?.modelProvider || params.defaultProvider;\n const model = currentSession?.model || params.defaultModel;\n\n // Parse the existing body and add model information (defensive parsing)\n const body = init?.body as string;\n let parsed: unknown = {};\n try {\n parsed = body ? JSON.parse(body) : {};\n } catch {\n parsed = {};\n }\n\n const parsedObj =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n const enhancedBody = {\n ...parsedObj,\n modelProvider,\n model,\n };\n\n // Make the request with enhanced body\n return fetch(input, {\n ...init,\n body: JSON.stringify(enhancedBody),\n });\n };\n\n return new DefaultChatTransport({\n api: endpoint,\n credentials: 'include',\n headers,\n fetch: fetchImpl,\n });\n };\n}\n\nexport function createChatHandlers({store}: {store: StoreApi<AiSliceState>}) {\n return {\n onChatToolCall: async ({\n toolCall,\n addToolResult,\n }: {\n toolCall: ToolCall;\n addToolResult?: AddToolResult;\n }) => {\n const {input, toolCallId, toolName} = toolCall;\n try {\n // handle client tools\n const state = store.getState();\n\n // Check if the stream was aborted before executing tool\n if (state.ai.analysisAbortController?.signal.aborted) {\n if (addToolResult) {\n addToolResult({\n tool: toolName,\n toolCallId,\n state: 'output-error',\n errorText: 'Operation cancelled by user',\n });\n }\n return;\n }\n\n const onToolCompleted = createOnToolCompletedHandler(store);\n const tools = convertToAiSDKTools(\n state.ai.tools || {},\n onToolCompleted,\n );\n\n // find tool from tools using toolName\n const tool = tools[toolName];\n if (tool && state.ai.tools[toolName]?.execute && tool.execute) {\n // Always provide a defined messages array to the tool runtime\n const sessionMessages = (state.ai.getCurrentSession()?.uiMessages ??\n []) as UIMessage[];\n const llmResult = await tool.execute(input, {\n toolCallId,\n messages: convertToModelMessages(sessionMessages),\n abortSignal: state.ai.analysisAbortController?.signal,\n });\n\n if (addToolResult) {\n // Note: When using sendAutomaticallyWhen, avoid awaiting addToolResult to prevent deadlocks\n addToolResult({\n tool: toolName,\n toolCallId,\n output: llmResult,\n });\n }\n } else {\n // Tool has no execute function - wait for UI component to call addToolResult\n // Check if there's a ToolComponent for this tool\n const hasToolComponent = !!state.ai.findToolComponent(toolName);\n if (hasToolComponent && state.ai.waitForToolResult) {\n try {\n // Wait for the UI component to call addToolResult\n await state.ai.waitForToolResult(\n toolCallId,\n state.ai.analysisAbortController?.signal,\n );\n } catch (error) {\n // If waiting was cancelled or failed, ensure we add an error result\n if (addToolResult && error instanceof Error) {\n addToolResult({\n tool: toolName,\n toolCallId,\n state: 'output-error',\n errorText: error.message,\n });\n }\n // Re-throw to let the outer catch handle it\n throw error;\n }\n }\n // If no ToolComponent, we still return (no-op) - the UI won't render anything\n // and the tool call will remain incomplete, which is fine for error handling\n }\n } catch (error) {\n // Check if this is an abort error\n const isAbortError = error instanceof ToolAbortError;\n\n if (addToolResult) {\n addToolResult({\n tool: toolName,\n toolCallId,\n state: 'output-error',\n errorText: isAbortError\n ? 'Operation cancelled by user'\n : getErrorMessageForDisplay(error),\n });\n }\n }\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChatData: (dataPart: DataUIPart<any>) => {\n // Handle additional tool output data from the backend (defensive guards)\n if (\n dataPart.type === 'data-tool-additional-output' &&\n dataPart.data &&\n (dataPart.data as {toolCallId?: unknown}).toolCallId != null\n ) {\n const {toolCallId, output} = dataPart.data as {\n toolCallId: string;\n output: unknown;\n };\n\n // Store the additional data in the session\n const currentSessionId = store.getState().ai.config.currentSessionId;\n if (currentSessionId) {\n store\n .getState()\n .ai.setSessionToolAdditionalData(\n currentSessionId,\n toolCallId,\n output,\n );\n }\n }\n },\n onChatFinish: ({messages}: {messages: UIMessage[]}) => {\n try {\n const currentSessionId = store.getState().ai.config.currentSessionId;\n if (!currentSessionId) return;\n\n // If the analysis has been aborted, force-complete and clean up immediately\n const aborted =\n !!store.getState().ai.analysisAbortController?.signal.aborted;\n if (aborted) {\n // If messages are empty (possible when stopping immediately), fall back to existing session messages\n const sessionMessages =\n (store.getState().ai.getCurrentSession()\n ?.uiMessages as UIMessage[]) || [];\n const sourceMessages =\n messages && messages.length > 0 ? messages : sessionMessages;\n\n const completedMessages = completeIncompleteToolCalls(sourceMessages);\n store\n .getState()\n .ai.setSessionUiMessages(currentSessionId, completedMessages);\n\n // Ensure an analysis result exists and is marked as cancelled\n store.setState((state: AiSliceState) =>\n produce(state, (draft: AiSliceState) => {\n draft.ai.isRunningAnalysis = false;\n draft.ai.analysisAbortController = undefined;\n\n const targetSession = draft.ai.config.sessions.find(\n (s: AnalysisSessionSchema) => s.id === currentSessionId,\n );\n if (!targetSession) return;\n\n // Find the last user message\n const lastUserMessage = completedMessages\n .filter((msg) => msg.role === 'user')\n .slice(-1)[0];\n if (!lastUserMessage) return;\n\n const promptText = lastUserMessage.parts\n .filter((part) => part.type === 'text')\n .map((part) => (part as {text: string}).text)\n .join('');\n\n const pendingIndex = targetSession.analysisResults.findIndex(\n (result) => result.id === '__pending__',\n );\n\n if (pendingIndex !== -1) {\n targetSession.analysisResults[pendingIndex] = {\n id: lastUserMessage.id,\n prompt: promptText,\n errorMessage: {error: 'Operation cancelled by user'},\n isCompleted: true,\n };\n } else {\n const existing = targetSession.analysisResults.find(\n (r) => r.id === lastUserMessage.id,\n );\n if (!existing) {\n targetSession.analysisResults.push({\n id: lastUserMessage.id,\n prompt: promptText,\n errorMessage: {error: 'Operation cancelled by user'},\n isCompleted: true,\n });\n }\n }\n }),\n );\n return;\n }\n\n // Complete any incomplete tool-calls before saving (can happen with AbortController)\n const completedMessages = completeIncompleteToolCalls(messages);\n\n store\n .getState()\n .ai.setSessionUiMessages(currentSessionId, completedMessages);\n\n // Create or update analysis result with the user message ID for proper correlation\n store.setState((state: AiSliceState) =>\n produce(state, (draft: AiSliceState) => {\n const targetSession = draft.ai.config.sessions.find(\n (s: AnalysisSessionSchema) => s.id === currentSessionId,\n );\n if (!targetSession) return;\n\n // Find the last user message to get its ID and prompt\n const lastUserMessage = completedMessages\n .filter((msg) => msg.role === 'user')\n .slice(-1)[0];\n\n if (lastUserMessage) {\n // Extract text content from user message\n const promptText = lastUserMessage.parts\n .filter((part) => part.type === 'text')\n .map((part) => (part as {text: string}).text)\n .join('');\n\n // Check if there's a pending analysis result\n const pendingIndex = targetSession.analysisResults.findIndex(\n (result) => result.id === '__pending__',\n );\n\n if (pendingIndex !== -1) {\n // Update the pending result with actual data\n targetSession.analysisResults[pendingIndex] = {\n id: lastUserMessage.id,\n prompt: promptText,\n isCompleted: true,\n };\n } else {\n // Check if analysis result already exists for this user message\n const existingResult = targetSession.analysisResults.find(\n (result) => result.id === lastUserMessage.id,\n );\n\n if (!existingResult) {\n // Create analysis result with the same ID as the user message\n targetSession.analysisResults.push({\n id: lastUserMessage.id,\n prompt: promptText,\n isCompleted: true,\n });\n }\n }\n }\n }),\n );\n\n // Determine if SDK wants to auto-send a follow-up turn (i.e., more steps pending)\n const shouldAutoSendNext = lastAssistantMessageIsCompleteWithToolCalls({\n messages: completedMessages,\n });\n\n // Step-aware completion: look only at parts after the most recent step-start\n const lastMessage = completedMessages[completedMessages.length - 1];\n const isLastMessageAssistant = lastMessage?.role === 'assistant';\n let tailHasTool = false;\n if (isLastMessageAssistant) {\n const parts = lastMessage?.parts ?? [];\n let lastStepStartIndex = -1;\n for (let i = parts.length - 1; i >= 0; i--) {\n if (parts[i]?.type === 'step-start') {\n lastStepStartIndex = i;\n break;\n }\n }\n const tailParts = parts.slice(lastStepStartIndex + 1);\n tailHasTool = tailParts.some(\n (part) =>\n typeof part?.type === 'string' &&\n (part.type.startsWith('tool-') || part.type === 'dynamic-tool'),\n );\n }\n\n // End analysis when there is no autosend and there are no pending tool parts\n // even if the assistant didn't emit additional text (e.g., tool-only tails).\n const shouldEndAnalysis =\n (isLastMessageAssistant && !shouldAutoSendNext && !tailHasTool) ||\n (!shouldAutoSendNext && !isLastMessageAssistant);\n\n if (shouldEndAnalysis) {\n store.setState((state: AiSliceState) =>\n produce(state, (draft: AiSliceState) => {\n draft.ai.isRunningAnalysis = false;\n draft.ai.analysisPrompt = '';\n draft.ai.analysisAbortController = undefined;\n }),\n );\n }\n } catch (err) {\n console.error('onChatFinish error:', err);\n throw err;\n }\n },\n onChatError: (error: unknown) => {\n try {\n let errMsg = getErrorMessageForDisplay(error);\n if (!errMsg || errMsg.trim().length === 0) {\n errMsg = 'Unknown error';\n }\n const currentSessionId = store.getState().ai.config.currentSessionId;\n store.setState((state: AiSliceState) =>\n produce(state, (draft: AiSliceState) => {\n if (!currentSessionId) return;\n const targetSession = draft.ai.config.sessions.find(\n (s: AnalysisSessionSchema) => s.id === currentSessionId,\n );\n if (targetSession) {\n // Ensure message structure is valid even on errors\n const existingMessages = (targetSession.uiMessages ||\n []) as UIMessage[];\n targetSession.uiMessages = completeIncompleteToolCalls(\n existingMessages,\n ) as unknown as AnalysisSessionSchema['uiMessages'];\n\n // Find the last user message to create analysis result with correct ID\n const uiMessages = targetSession.uiMessages as UIMessage[];\n const lastUserMessage = uiMessages\n .filter((msg) => msg.role === 'user')\n .slice(-1)[0];\n\n if (lastUserMessage) {\n // Extract text content from user message\n const promptText = lastUserMessage.parts\n .filter((part) => part.type === 'text')\n .map((part) => (part as {text: string}).text)\n .join('');\n\n // Check if there's a pending analysis result\n const pendingIndex = targetSession.analysisResults.findIndex(\n (result) => result.id === '__pending__',\n );\n\n if (pendingIndex !== -1) {\n // Update the pending result with error\n targetSession.analysisResults[pendingIndex] = {\n id: lastUserMessage.id,\n prompt: promptText,\n errorMessage: {error: errMsg},\n isCompleted: true,\n };\n } else {\n // Check if analysis result already exists for this user message\n const existingResult = targetSession.analysisResults.find(\n (result) => result.id === lastUserMessage.id,\n );\n\n if (!existingResult) {\n // Create analysis result with the same ID as the user message\n targetSession.analysisResults.push({\n id: lastUserMessage.id,\n prompt: promptText,\n errorMessage: {error: errMsg},\n isCompleted: true,\n });\n } else {\n // Update existing result with error message\n existingResult.errorMessage = {error: errMsg};\n }\n }\n }\n }\n draft.ai.isRunningAnalysis = false;\n draft.ai.analysisAbortController = undefined;\n }),\n );\n } catch (err) {\n console.error('Failed to store chat error:', err);\n throw err;\n }\n },\n };\n}\n"]}
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
+ import { Components } from 'react-markdown';
2
3
  type AnalysisAnswerProps = {
3
4
  content: string;
4
5
  isAnswer: boolean;
6
+ customMarkdownComponents?: Partial<Components>;
5
7
  };
6
8
  /**
7
9
  * Renders an analysis answer with markdown content of the final streaming response.
@@ -1 +1 @@
1
- {"version":3,"file":"AnalysisAnswer.d.ts","sourceRoot":"","sources":["../../src/components/AnalysisAnswer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAS5D,KAAK,mBAAmB,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AA6GF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,iDA0EzB,CAAC"}
1
+ {"version":3,"file":"AnalysisAnswer.d.ts","sourceRoot":"","sources":["../../src/components/AnalysisAnswer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAC5D,OAAiB,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAQpD,KAAK,mBAAmB,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,wBAAwB,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAChD,CAAC;AA6GF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,iDA4EzB,CAAC"}
@@ -62,6 +62,7 @@ ThinkBlock.displayName = 'ThinkBlock';
62
62
  * @returns {JSX.Element} The rendered answer tool call
63
63
  */
64
64
  export const AnalysisAnswer = React.memo(function AnalysisAnswer(props) {
65
+ const { content, isAnswer, customMarkdownComponents } = props;
65
66
  const [expandedThink, setExpandedThink] = useState(new Set());
66
67
  const toggleThinkExpansion = useCallback((content) => {
67
68
  setExpandedThink((prev) => {
@@ -76,7 +77,7 @@ export const AnalysisAnswer = React.memo(function AnalysisAnswer(props) {
76
77
  });
77
78
  }, []);
78
79
  // Memoize content processing to avoid recalculation on every render
79
- const { processedContent, thinkContents } = useMemo(() => processContent(props.content), [props.content]);
80
+ const { processedContent, thinkContents } = useMemo(() => processContent(content), [content]);
80
81
  // Memoize the think-block component to prevent unnecessary re-renders
81
82
  const thinkBlockComponent = useCallback((thinkBlock) => {
82
83
  try {
@@ -94,9 +95,10 @@ export const AnalysisAnswer = React.memo(function AnalysisAnswer(props) {
94
95
  return null;
95
96
  }
96
97
  }, [thinkContents, expandedThink, toggleThinkExpansion]);
97
- return (_jsx("div", { className: "flex flex-col gap-5", children: _jsx(MessageContainer, { isSuccess: true, type: props.isAnswer ? 'answer' : 'thinking', content: props, children: _jsx("div", { className: "prose dark:prose-invert max-w-none text-sm", children: _jsx(Markdown, { remarkPlugins: [remarkGfm], rehypePlugins: [rehypeRaw], components: {
98
+ return (_jsx("div", { className: "flex flex-col gap-5", children: _jsx(MessageContainer, { isSuccess: true, type: isAnswer ? 'answer' : 'thinking', content: { content, isAnswer }, children: _jsx("div", { className: "prose dark:prose-invert max-w-none text-sm", children: _jsx(Markdown, { remarkPlugins: [remarkGfm], rehypePlugins: [rehypeRaw], components: {
98
99
  // @ts-expect-error - Custom HTML element not in react-markdown types
99
100
  'think-block': thinkBlockComponent,
101
+ ...customMarkdownComponents,
100
102
  }, children: processedContent }) }) }) }));
101
103
  });
102
104
  //# sourceMappingURL=AnalysisAnswer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnalysisAnswer.js","sourceRoot":"","sources":["../../src/components/AnalysisAnswer.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useState, useCallback, useMemo} from 'react';\nimport Markdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport rehypeRaw from 'rehype-raw';\nimport {truncate} from '@sqlrooms/utils';\nimport {MessageContainer} from './MessageContainer';\nimport {BrainIcon} from 'lucide-react';\nimport {cn} from '@sqlrooms/ui';\n\ntype AnalysisAnswerProps = {\n content: string;\n isAnswer: boolean;\n};\n\ntype ThinkContent = {\n content: string;\n isComplete: boolean;\n index: number;\n};\n\n// Constants moved outside component to prevent recreation\nconst THINK_WORD_LIMIT = 10;\nconst COMPLETE_THINK_REGEX = /<think>([\\s\\S]*?)<\\/think>/g;\nconst INCOMPLETE_THINK_REGEX = /<think>([\\s\\S]*)$/;\n\n/**\n * Processes content and extracts think content in one pass\n */\nconst processContent = (\n originalContent: string,\n): {\n processedContent: string;\n thinkContents: ThinkContent[];\n} => {\n const thinkContents: ThinkContent[] = [];\n let processedContent = originalContent;\n let index = 0;\n\n // Replace complete think tags\n processedContent = processedContent.replace(\n COMPLETE_THINK_REGEX,\n (match, content) => {\n if (content) {\n thinkContents.push({\n content: content.trim(),\n isComplete: true,\n index: index++,\n });\n return `\\n\\n<think-block data-index=\"${index - 1}\"></think-block>\\n\\n`;\n }\n return match;\n },\n );\n\n // Replace incomplete think tags (no closing tag)\n processedContent = processedContent.replace(\n INCOMPLETE_THINK_REGEX,\n (match, content) => {\n if (content) {\n thinkContents.push({\n content: content.trim(),\n isComplete: false,\n index: index++,\n });\n return `\\n\\n<think-block data-index=\"${index - 1}\"></think-block>\\n\\n`;\n }\n return match;\n },\n );\n\n return {processedContent, thinkContents};\n};\n\n/**\n * ThinkBlock component for rendering individual think blocks\n */\nconst ThinkBlock = React.memo<{\n className?: string;\n thinkContent: ThinkContent;\n isExpanded: boolean;\n onToggleExpansion: (content: string) => void;\n}>(({thinkContent, isExpanded, onToggleExpansion, className}) => {\n const {content, isComplete, index} = thinkContent;\n\n const displayText =\n isComplete && !isExpanded ? truncate(content, THINK_WORD_LIMIT) : content;\n const needsTruncation =\n isComplete && content.split(' ').length > THINK_WORD_LIMIT;\n\n return (\n <span\n key={`think-${index}`}\n className={cn(\n 'inline-block rounded-lg px-3 py-2 text-xs font-normal text-gray-100 dark:text-gray-400',\n isExpanded && 'bg-gray-50 dark:bg-gray-800/50',\n className,\n )}\n >\n <span className=\"inline-flex items-start gap-2\">\n <span className=\"text-gray-400\">\n <BrainIcon\n className=\"mb-1 inline-block opacity-60 grayscale\"\n size={12}\n />{' '}\n {displayText}\n </span>\n </span>{' '}\n {needsTruncation && (\n <button\n onClick={() => onToggleExpansion(content)}\n className=\"text-xs text-gray-500 underline hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300\"\n >\n {isExpanded ? 'Show less' : 'Show more thinking'}\n </button>\n )}\n </span>\n );\n});\n\nThinkBlock.displayName = 'ThinkBlock';\n\n/**\n * Renders an analysis answer with markdown content of the final streaming response.\n * Supports streaming think content that may arrive in chunks (e.g., \"<think>Hello\" before \"</think>\").\n *\n * @param {AnalysisAnswerProps} props - The component props. See {@link AnalysisAnswerProps} for more details.\n * @returns {JSX.Element} The rendered answer tool call\n */\nexport const AnalysisAnswer = React.memo(function AnalysisAnswer(\n props: AnalysisAnswerProps,\n) {\n const [expandedThink, setExpandedThink] = useState<Set<string>>(new Set());\n\n const toggleThinkExpansion = useCallback((content: string) => {\n setExpandedThink((prev) => {\n const newExpanded = new Set(prev);\n if (newExpanded.has(content)) {\n newExpanded.delete(content);\n } else {\n newExpanded.add(content);\n }\n return newExpanded;\n });\n }, []);\n\n // Memoize content processing to avoid recalculation on every render\n const {processedContent, thinkContents} = useMemo(\n () => processContent(props.content),\n [props.content],\n );\n\n // Memoize the think-block component to prevent unnecessary re-renders\n const thinkBlockComponent = useCallback(\n (thinkBlock: any) => {\n try {\n const index = parseInt(thinkBlock.props?.['data-index'] || '0', 10);\n const thinkContent = thinkContents[index];\n\n if (!thinkContent) {\n console.warn(`Think content not found for index: ${index}`);\n return null;\n }\n\n const isExpanded = expandedThink.has(thinkContent.content);\n\n return (\n <ThinkBlock\n thinkContent={thinkContent}\n isExpanded={isExpanded}\n onToggleExpansion={toggleThinkExpansion}\n />\n );\n } catch (error) {\n console.error('Error rendering think block:', error);\n return null;\n }\n },\n [thinkContents, expandedThink, toggleThinkExpansion],\n );\n\n return (\n <div className=\"flex flex-col gap-5\">\n <MessageContainer\n isSuccess={true}\n type={props.isAnswer ? 'answer' : 'thinking'}\n content={props}\n >\n <div className=\"prose dark:prose-invert max-w-none text-sm\">\n <Markdown\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeRaw]}\n components={{\n // @ts-expect-error - Custom HTML element not in react-markdown types\n 'think-block': thinkBlockComponent,\n }}\n >\n {processedContent}\n </Markdown>\n </div>\n </MessageContainer>\n </div>\n );\n});\n"]}
1
+ {"version":3,"file":"AnalysisAnswer.js","sourceRoot":"","sources":["../../src/components/AnalysisAnswer.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useState, useCallback, useMemo} from 'react';\nimport Markdown, {Components} from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport rehypeRaw from 'rehype-raw';\nimport {truncate} from '@sqlrooms/utils';\nimport {MessageContainer} from './MessageContainer';\nimport {BrainIcon} from 'lucide-react';\nimport {cn} from '@sqlrooms/ui';\n\ntype AnalysisAnswerProps = {\n content: string;\n isAnswer: boolean;\n customMarkdownComponents?: Partial<Components>;\n};\n\ntype ThinkContent = {\n content: string;\n isComplete: boolean;\n index: number;\n};\n\n// Constants moved outside component to prevent recreation\nconst THINK_WORD_LIMIT = 10;\nconst COMPLETE_THINK_REGEX = /<think>([\\s\\S]*?)<\\/think>/g;\nconst INCOMPLETE_THINK_REGEX = /<think>([\\s\\S]*)$/;\n\n/**\n * Processes content and extracts think content in one pass\n */\nconst processContent = (\n originalContent: string,\n): {\n processedContent: string;\n thinkContents: ThinkContent[];\n} => {\n const thinkContents: ThinkContent[] = [];\n let processedContent = originalContent;\n let index = 0;\n\n // Replace complete think tags\n processedContent = processedContent.replace(\n COMPLETE_THINK_REGEX,\n (match, content) => {\n if (content) {\n thinkContents.push({\n content: content.trim(),\n isComplete: true,\n index: index++,\n });\n return `\\n\\n<think-block data-index=\"${index - 1}\"></think-block>\\n\\n`;\n }\n return match;\n },\n );\n\n // Replace incomplete think tags (no closing tag)\n processedContent = processedContent.replace(\n INCOMPLETE_THINK_REGEX,\n (match, content) => {\n if (content) {\n thinkContents.push({\n content: content.trim(),\n isComplete: false,\n index: index++,\n });\n return `\\n\\n<think-block data-index=\"${index - 1}\"></think-block>\\n\\n`;\n }\n return match;\n },\n );\n\n return {processedContent, thinkContents};\n};\n\n/**\n * ThinkBlock component for rendering individual think blocks\n */\nconst ThinkBlock = React.memo<{\n className?: string;\n thinkContent: ThinkContent;\n isExpanded: boolean;\n onToggleExpansion: (content: string) => void;\n}>(({thinkContent, isExpanded, onToggleExpansion, className}) => {\n const {content, isComplete, index} = thinkContent;\n\n const displayText =\n isComplete && !isExpanded ? truncate(content, THINK_WORD_LIMIT) : content;\n const needsTruncation =\n isComplete && content.split(' ').length > THINK_WORD_LIMIT;\n\n return (\n <span\n key={`think-${index}`}\n className={cn(\n 'inline-block rounded-lg px-3 py-2 text-xs font-normal text-gray-100 dark:text-gray-400',\n isExpanded && 'bg-gray-50 dark:bg-gray-800/50',\n className,\n )}\n >\n <span className=\"inline-flex items-start gap-2\">\n <span className=\"text-gray-400\">\n <BrainIcon\n className=\"mb-1 inline-block opacity-60 grayscale\"\n size={12}\n />{' '}\n {displayText}\n </span>\n </span>{' '}\n {needsTruncation && (\n <button\n onClick={() => onToggleExpansion(content)}\n className=\"text-xs text-gray-500 underline hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300\"\n >\n {isExpanded ? 'Show less' : 'Show more thinking'}\n </button>\n )}\n </span>\n );\n});\n\nThinkBlock.displayName = 'ThinkBlock';\n\n/**\n * Renders an analysis answer with markdown content of the final streaming response.\n * Supports streaming think content that may arrive in chunks (e.g., \"<think>Hello\" before \"</think>\").\n *\n * @param {AnalysisAnswerProps} props - The component props. See {@link AnalysisAnswerProps} for more details.\n * @returns {JSX.Element} The rendered answer tool call\n */\nexport const AnalysisAnswer = React.memo(function AnalysisAnswer(\n props: AnalysisAnswerProps,\n) {\n const {content, isAnswer, customMarkdownComponents} = props;\n const [expandedThink, setExpandedThink] = useState<Set<string>>(new Set());\n\n const toggleThinkExpansion = useCallback((content: string) => {\n setExpandedThink((prev) => {\n const newExpanded = new Set(prev);\n if (newExpanded.has(content)) {\n newExpanded.delete(content);\n } else {\n newExpanded.add(content);\n }\n return newExpanded;\n });\n }, []);\n\n // Memoize content processing to avoid recalculation on every render\n const {processedContent, thinkContents} = useMemo(\n () => processContent(content),\n [content],\n );\n\n // Memoize the think-block component to prevent unnecessary re-renders\n const thinkBlockComponent = useCallback(\n (thinkBlock: any) => {\n try {\n const index = parseInt(thinkBlock.props?.['data-index'] || '0', 10);\n const thinkContent = thinkContents[index];\n\n if (!thinkContent) {\n console.warn(`Think content not found for index: ${index}`);\n return null;\n }\n\n const isExpanded = expandedThink.has(thinkContent.content);\n\n return (\n <ThinkBlock\n thinkContent={thinkContent}\n isExpanded={isExpanded}\n onToggleExpansion={toggleThinkExpansion}\n />\n );\n } catch (error) {\n console.error('Error rendering think block:', error);\n return null;\n }\n },\n [thinkContents, expandedThink, toggleThinkExpansion],\n );\n\n return (\n <div className=\"flex flex-col gap-5\">\n <MessageContainer\n isSuccess={true}\n type={isAnswer ? 'answer' : 'thinking'}\n content={{content, isAnswer}}\n >\n <div className=\"prose dark:prose-invert max-w-none text-sm\">\n <Markdown\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeRaw]}\n components={{\n // @ts-expect-error - Custom HTML element not in react-markdown types\n 'think-block': thinkBlockComponent,\n ...customMarkdownComponents,\n }}\n >\n {processedContent}\n </Markdown>\n </div>\n </MessageContainer>\n </div>\n );\n});\n"]}
@@ -1,10 +1,17 @@
1
1
  import { AnalysisResultSchema } from '@sqlrooms/ai-config';
2
+ import { Components } from 'react-markdown';
2
3
  /**
3
4
  * Props for the AnalysisResult component
4
5
  * @property {AnalysisResultSchema} result - The result of the analysis containing prompt, tool calls, and analysis data
6
+ * @property {boolean} enableReasoningBox - Whether to group consecutive tool parts into a collapsible ReasoningBox
7
+ * @property {Partial<Components>} customMarkdownComponents - Optional custom components for markdown rendering
8
+ * @property {string[]} userTools - Array of tool names that should not be grouped and must be rendered separately
5
9
  */
6
10
  type AnalysisResultProps = {
7
11
  analysisResult: AnalysisResultSchema;
12
+ enableReasoningBox?: boolean;
13
+ customMarkdownComponents?: Partial<Components>;
14
+ userTools?: string[];
8
15
  };
9
16
  /**
10
17
  * Component that displays the results of an AI analysis.
@@ -1 +1 @@
1
- {"version":3,"file":"AnalysisResult.d.ts","sourceRoot":"","sources":["../../src/components/AnalysisResult.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAoBzD;;;GAGG;AACH,KAAK,mBAAmB,GAAG;IACzB,cAAc,EAAE,oBAAoB,CAAC;CACtC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA+JxD,CAAC"}
1
+ {"version":3,"file":"AnalysisResult.d.ts","sourceRoot":"","sources":["../../src/components/AnalysisResult.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAIzD,OAAO,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAU1C;;;;;;GAMG;AACH,KAAK,mBAAmB,GAAG;IACzB,cAAc,EAAE,oBAAoB,CAAC;IACrC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA2HxD,CAAC"}