@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 +1 @@
1
- {"version":3,"file":"useAiChat.js","sourceRoot":"","sources":["../../src/hooks/useAiChat.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useMemo} from 'react';\nimport {useChat} from '@ai-sdk/react';\nimport {\n DefaultChatTransport,\n lastAssistantMessageIsCompleteWithToolCalls,\n} from 'ai';\nimport type {UIMessage} from 'ai';\nimport {useStoreWithAi} from '../AiSlice';\n\nexport type AddToolResult = (\n options:\n | {tool: string; toolCallId: string; output: unknown}\n | {\n tool: string;\n toolCallId: string;\n state: 'output-error';\n errorText: string;\n },\n) => void;\n\n/**\n * Custom hook that provides AI chat functionality with automatic transport setup,\n * message syncing, and tool call handling.\n *\n * This hook encapsulates all the logic needed to integrate the AI SDK's useChat\n * with the AI slice state management.\n *\n * @returns An object containing messages and sendMessage from useChat\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const {messages, sendMessage} = useAiChat();\n *\n * const handleSubmit = () => {\n * sendMessage({text: 'Hello!'});\n * };\n *\n * return (\n * <div>\n * {messages.map(msg => <div key={msg.id}>{msg.content}</div>)}\n * </div>\n * );\n * }\n * ```\n */\nexport function useAiChat() {\n // Get current session and configuration\n const currentSession = useStoreWithAi((s) => s.ai.getCurrentSession());\n const sessionId = currentSession?.id;\n const model = currentSession?.model;\n // Use messagesRevision to force reset only when messages are explicitly deleted\n const messagesRevision = currentSession?.messagesRevision ?? 0;\n\n // Get chat transport configuration\n const getLocalChatTransport = useStoreWithAi(\n (s) => s.ai.getLocalChatTransport,\n );\n const getRemoteChatTransport = useStoreWithAi(\n (s) => s.ai.getRemoteChatTransport,\n );\n const endPoint = useStoreWithAi((s) => s.ai.chatEndPoint);\n const headers = useStoreWithAi((s) => s.ai.chatHeaders);\n\n // Get chat handlers\n const onChatToolCall = useStoreWithAi((s) => s.ai.onChatToolCall);\n const onChatFinish = useStoreWithAi((s) => s.ai.onChatFinish);\n const onChatData = useStoreWithAi((s) => s.ai.onChatData);\n const onChatError = useStoreWithAi((s) => s.ai.onChatError);\n const setSessionUiMessages = useStoreWithAi((s) => s.ai.setSessionUiMessages);\n\n // Create transport (recreate when model changes)\n const transport: DefaultChatTransport<UIMessage> = useMemo(() => {\n // Recreate transport when the model changes\n void model;\n const trimmed = (endPoint || '').trim();\n if (trimmed.length > 0) {\n return getRemoteChatTransport(trimmed, headers);\n }\n return getLocalChatTransport();\n }, [getLocalChatTransport, getRemoteChatTransport, headers, endPoint, model]);\n\n // Setup useChat with all configuration\n // Include messagesRevision in the id to force reset only when messages are explicitly deleted\n // Store addToolResult in a variable that can be captured by the onToolCall closure\n let capturedAddToolResult: AddToolResult;\n\n const {messages, sendMessage, addToolResult} = useChat({\n id: `${sessionId}-${messagesRevision}`,\n transport,\n messages: (currentSession?.uiMessages as unknown as UIMessage[]) ?? [],\n onToolCall: async ({toolCall}: {toolCall: any}) => {\n // Wrap the store's onChatToolCall to provide addToolResult\n // Use the captured addToolResult from the outer scope\n return onChatToolCall?.({toolCall, addToolResult: capturedAddToolResult});\n },\n onFinish: onChatFinish,\n onError: onChatError,\n onData: onChatData,\n // Automatically submit when all tool results are available\n // NOTE: When using sendAutomaticallyWhen, don't use await with addToolResult inside onChatToolCall as it can cause deadlocks.\n sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,\n });\n\n // Capture addToolResult for use in onToolCall\n capturedAddToolResult = addToolResult;\n\n // Sync streaming updates into the store so UiMessages renders incrementally\n useEffect(() => {\n if (!sessionId) return;\n setSessionUiMessages(sessionId, messages as UIMessage[]);\n }, [messages, sessionId, setSessionUiMessages]);\n\n return {\n messages,\n sendMessage,\n };\n}\n"]}
1
+ {"version":3,"file":"useAiChat.js","sourceRoot":"","sources":["../../src/hooks/useAiChat.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useMemo, useRef} from 'react';\nimport {useChat} from '@ai-sdk/react';\nimport {\n DefaultChatTransport,\n lastAssistantMessageIsCompleteWithToolCalls,\n} from 'ai';\nimport type {UIMessage} from 'ai';\nimport {useStoreWithAi} from '../AiSlice';\nimport type {ToolCall} from '../chatTransport';\nimport {completeIncompleteToolCalls} from '../chatTransport';\n\nexport type AddToolResult = (\n options:\n | {tool: string; toolCallId: string; output: unknown}\n | {\n tool: string;\n toolCallId: string;\n state: 'output-error';\n errorText: string;\n },\n) => void;\n\n/**\n * Custom hook that provides AI chat functionality with automatic transport setup,\n * message syncing, and tool call handling.\n *\n * This hook encapsulates all the logic needed to integrate the AI SDK's useChat\n * with the AI slice state management.\n *\n * @returns An object containing messages and sendMessage from useChat\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const {messages, sendMessage} = useAiChat();\n *\n * const handleSubmit = () => {\n * sendMessage({text: 'Hello!'});\n * };\n *\n * return (\n * <div>\n * {messages.map(msg => <div key={msg.id}>{msg.content}</div>)}\n * </div>\n * );\n * }\n * ```\n */\nexport function useAiChat() {\n // Get current session and configuration\n const currentSession = useStoreWithAi((s) => s.ai.getCurrentSession());\n const sessionId = currentSession?.id;\n const model = currentSession?.model;\n // Use messagesRevision to force reset only when messages are explicitly deleted\n const messagesRevision = currentSession?.messagesRevision ?? 0;\n\n // Get chat transport configuration\n const getLocalChatTransport = useStoreWithAi(\n (s) => s.ai.getLocalChatTransport,\n );\n const getRemoteChatTransport = useStoreWithAi(\n (s) => s.ai.getRemoteChatTransport,\n );\n const endPoint = useStoreWithAi((s) => s.ai.chatEndPoint);\n const headers = useStoreWithAi((s) => s.ai.chatHeaders);\n\n // Get chat handlers\n const onChatToolCall = useStoreWithAi((s) => s.ai.onChatToolCall);\n const onChatFinish = useStoreWithAi((s) => s.ai.onChatFinish);\n const onChatData = useStoreWithAi((s) => s.ai.onChatData);\n const onChatError = useStoreWithAi((s) => s.ai.onChatError);\n const setSessionUiMessages = useStoreWithAi((s) => s.ai.setSessionUiMessages);\n const setChatStop = useStoreWithAi((s) => s.ai.setChatStop);\n const setChatSendMessage = useStoreWithAi((s) => s.ai.setChatSendMessage);\n const setAddToolResult = useStoreWithAi((s) => s.ai.setAddToolResult);\n\n // Abort/auto-send guards\n const isAborted = useStoreWithAi(\n (s) => s.ai.analysisAbortController?.signal.aborted ?? false,\n );\n const isAbortedRef = useRef<boolean>(isAborted);\n // Keep a live ref so sendAutomaticallyWhen sees latest abort state even if useChat doesn't reinit\n useEffect(() => {\n isAbortedRef.current = isAborted;\n }, [isAborted]);\n\n // Create transport (recreate when model changes)\n const transport: DefaultChatTransport<UIMessage> = useMemo(() => {\n // Recreate transport when the model changes\n void model;\n const trimmed = (endPoint || '').trim();\n if (trimmed.length > 0) {\n return getRemoteChatTransport(trimmed, headers);\n }\n return getLocalChatTransport();\n }, [getLocalChatTransport, getRemoteChatTransport, headers, endPoint, model]);\n\n // Setup useChat with all configuration\n // Include messagesRevision in the id to force reset only when messages are explicitly deleted\n // Store addToolResult in a ref that can be captured by the onToolCall closure\n const addToolResultRef = useRef<AddToolResult>(null!);\n\n // Gate auto-send when analysis is aborted or cancelled, to prevent unintended follow-ups\n type SendAutoWhenArg = Parameters<\n typeof lastAssistantMessageIsCompleteWithToolCalls\n >[0];\n const shouldAutoSend = (options: SendAutoWhenArg) => {\n if (isAbortedRef.current) return false;\n return lastAssistantMessageIsCompleteWithToolCalls(options);\n };\n\n const initialMessages = useMemo(() => {\n return completeIncompleteToolCalls(\n ((currentSession?.uiMessages as unknown as UIMessage[]) ?? []),\n );\n }, [sessionId, messagesRevision]);\n\n const {messages, sendMessage, addToolResult, stop, status} = useChat({\n id: `${sessionId}-${messagesRevision}`,\n transport,\n messages: initialMessages,\n onToolCall: async (opts) => {\n const {toolCall} = opts as {toolCall: unknown};\n // Wrap the store's onChatToolCall to provide addToolResult\n // Use the captured addToolResult from the ref\n return onChatToolCall?.({\n toolCall: toolCall as ToolCall,\n addToolResult: addToolResultRef.current,\n });\n },\n onFinish: onChatFinish,\n onError: onChatError,\n onData: onChatData,\n // Automatically submit when all tool results are available\n // NOTE: When using sendAutomaticallyWhen, don't use await with addToolResult inside onChatToolCall as it can cause deadlocks.\n sendAutomaticallyWhen: shouldAutoSend,\n });\n\n // Capture addToolResult for use in onToolCall\n addToolResultRef.current = addToolResult;\n\n // If user aborts mid-stream, stop the local chat stream immediately\n useEffect(() => {\n if (isAbortedRef.current && status === 'streaming') {\n stop();\n }\n }, [status, stop, isAborted]);\n\n // Register stop with the store so cancelAnalysis can stop the stream\n useEffect(() => {\n setChatStop?.(stop);\n return () => setChatStop?.(undefined);\n }, [setChatStop, stop]);\n\n // Register sendMessage with the store so it can be accessed from the slice\n useEffect(() => {\n setChatSendMessage?.(sendMessage);\n return () => setChatSendMessage?.(undefined);\n }, [setChatSendMessage, sendMessage]);\n\n // Register addToolResult with the store so it can be accessed from the slice\n useEffect(() => {\n setAddToolResult?.(addToolResult);\n return () => setAddToolResult?.(undefined);\n }, [setAddToolResult, addToolResult]);\n\n // Sync streaming updates into the store so UiMessages renders incrementally\n useEffect(() => {\n if (!sessionId) return;\n setSessionUiMessages(sessionId, messages as UIMessage[]);\n }, [messages, sessionId, setSessionUiMessages]);\n\n return {\n messages,\n sendMessage,\n stop,\n status,\n };\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { UIMessage } from 'ai';
2
+ import type { UIMessagePart } from '@sqlrooms/ai-config';
3
+ /**
4
+ * Custom hook to extract assistant message parts for a given analysis result ID.
5
+ * Handles the case where the analysis result ID matches a user message and finds
6
+ * the corresponding assistant response. Also includes fallback logic for pending
7
+ * results before the real ID is assigned (e.g., during streaming).
8
+ *
9
+ * @param uiMessages - Array of UI messages from the current session
10
+ * @param analysisResultId - The ID of the analysis result (user message ID)
11
+ * @returns Array of message parts from the assistant's response, or empty array if not found
12
+ */
13
+ export declare function useAssistantMessageParts(uiMessages: UIMessage[] | undefined, analysisResultId: string): UIMessagePart[];
14
+ //# sourceMappingURL=useAssistantMessageParts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAssistantMessageParts.d.ts","sourceRoot":"","sources":["../../src/hooks/useAssistantMessageParts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,IAAI,CAAC;AAClC,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,SAAS,EAAE,GAAG,SAAS,EACnC,gBAAgB,EAAE,MAAM,GACvB,aAAa,EAAE,CAkCjB"}
@@ -0,0 +1,44 @@
1
+ import { useMemo } from 'react';
2
+ /**
3
+ * Custom hook to extract assistant message parts for a given analysis result ID.
4
+ * Handles the case where the analysis result ID matches a user message and finds
5
+ * the corresponding assistant response. Also includes fallback logic for pending
6
+ * results before the real ID is assigned (e.g., during streaming).
7
+ *
8
+ * @param uiMessages - Array of UI messages from the current session
9
+ * @param analysisResultId - The ID of the analysis result (user message ID)
10
+ * @returns Array of message parts from the assistant's response, or empty array if not found
11
+ */
12
+ export function useAssistantMessageParts(uiMessages, analysisResultId) {
13
+ return useMemo(() => {
14
+ if (!uiMessages)
15
+ return [];
16
+ // Find the user message with analysisResultId
17
+ let userMessageIndex = uiMessages.findIndex((msg) => msg.id === analysisResultId && msg.role === 'user');
18
+ // If not found (e.g., pending result before onFinish assigns the real ID),
19
+ // fall back to the last user message to enable streaming display.
20
+ if (userMessageIndex === -1) {
21
+ for (let i = uiMessages.length - 1; i >= 0; i--) {
22
+ if (uiMessages[i]?.role === 'user') {
23
+ userMessageIndex = i;
24
+ break;
25
+ }
26
+ }
27
+ if (userMessageIndex === -1)
28
+ return [];
29
+ }
30
+ // Find the next assistant message after this user message
31
+ for (let i = userMessageIndex + 1; i < uiMessages.length; i++) {
32
+ const msg = uiMessages[i];
33
+ if (msg?.role === 'assistant') {
34
+ return msg.parts;
35
+ }
36
+ if (msg?.role === 'user') {
37
+ // Hit next user message without finding assistant response
38
+ break;
39
+ }
40
+ }
41
+ return [];
42
+ }, [uiMessages, analysisResultId]);
43
+ }
44
+ //# sourceMappingURL=useAssistantMessageParts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAssistantMessageParts.js","sourceRoot":"","sources":["../../src/hooks/useAssistantMessageParts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,OAAO,CAAC;AAI9B;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CACtC,UAAmC,EACnC,gBAAwB;IAExB,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,IAAI,CAAC,UAAU;YAAE,OAAO,EAAE,CAAC;QAE3B,8CAA8C;QAC9C,IAAI,gBAAgB,GAAG,UAAU,CAAC,SAAS,CACzC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,gBAAgB,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,CAC5D,CAAC;QAEF,2EAA2E;QAC3E,kEAAkE;QAClE,IAAI,gBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC;YAC5B,KAAK,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAChD,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;oBACnC,gBAAgB,GAAG,CAAC,CAAC;oBACrB,MAAM;gBACR,CAAC;YACH,CAAC;YACD,IAAI,gBAAgB,KAAK,CAAC,CAAC;gBAAE,OAAO,EAAE,CAAC;QACzC,CAAC;QAED,0DAA0D;QAC1D,KAAK,IAAI,CAAC,GAAG,gBAAgB,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9D,MAAM,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,GAAG,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;gBAC9B,OAAO,GAAG,CAAC,KAAK,CAAC;YACnB,CAAC;YACD,IAAI,GAAG,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;gBACzB,2DAA2D;gBAC3D,MAAM;YACR,CAAC;QACH,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import {useMemo} from 'react';\nimport type {UIMessage} from 'ai';\nimport type {UIMessagePart} from '@sqlrooms/ai-config';\n\n/**\n * Custom hook to extract assistant message parts for a given analysis result ID.\n * Handles the case where the analysis result ID matches a user message and finds\n * the corresponding assistant response. Also includes fallback logic for pending\n * results before the real ID is assigned (e.g., during streaming).\n *\n * @param uiMessages - Array of UI messages from the current session\n * @param analysisResultId - The ID of the analysis result (user message ID)\n * @returns Array of message parts from the assistant's response, or empty array if not found\n */\nexport function useAssistantMessageParts(\n uiMessages: UIMessage[] | undefined,\n analysisResultId: string,\n): UIMessagePart[] {\n return useMemo(() => {\n if (!uiMessages) return [];\n\n // Find the user message with analysisResultId\n let userMessageIndex = uiMessages.findIndex(\n (msg) => msg.id === analysisResultId && msg.role === 'user',\n );\n\n // If not found (e.g., pending result before onFinish assigns the real ID),\n // fall back to the last user message to enable streaming display.\n if (userMessageIndex === -1) {\n for (let i = uiMessages.length - 1; i >= 0; i--) {\n if (uiMessages[i]?.role === 'user') {\n userMessageIndex = i;\n break;\n }\n }\n if (userMessageIndex === -1) return [];\n }\n\n // Find the next assistant message after this user message\n for (let i = userMessageIndex + 1; i < uiMessages.length; i++) {\n const msg = uiMessages[i];\n if (msg?.role === 'assistant') {\n return msg.parts;\n }\n if (msg?.role === 'user') {\n // Hit next user message without finding assistant response\n break;\n }\n }\n return [];\n }, [uiMessages, analysisResultId]);\n}\n\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"useScrollToBottom.d.ts","sourceRoot":"","sources":["../../src/hooks/useScrollToBottom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAE/E,UAAU,oBAAoB,CAAC,CAAC,SAAS,WAAW,GAAG,IAAI;IACzD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,WAAW,GAAG,IAAI,EAAE;AAC9D;;;GAGG;AACH,aAAa,EACb,YAAY,EACZ,MAAM;AACN;;;GAGG;AACH,mBAA2B,GAC5B,EAAE;IACD,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAClC,MAAM,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,oBAAoB,CAAC,CAAC,CAAC,CA+E1B"}
1
+ {"version":3,"file":"useScrollToBottom.d.ts","sourceRoot":"","sources":["../../src/hooks/useScrollToBottom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAE/E,UAAU,oBAAoB,CAAC,CAAC,SAAS,WAAW,GAAG,IAAI;IACzD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,WAAW,GAAG,IAAI,EAAE;AAC9D;;;GAGG;AACH,aAAa,EACb,YAAY,EACZ,MAAM;AACN;;;GAGG;AACH,mBAA2B,GAC5B,EAAE;IACD,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAClC,MAAM,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAgF1B"}
@@ -76,7 +76,8 @@ dataToObserve, containerRef, endRef,
76
76
  scrollOnInitialLoad = false, }) {
77
77
  const [showScrollButton, setShowButton] = useState(false);
78
78
  // Track if user was at bottom before content changes
79
- const wasAtBottomRef = useRef(scrollOnInitialLoad);
79
+ // Start as true since we're initially at the bottom
80
+ const wasAtBottomRef = useRef(true);
80
81
  // Track if this is the initial load
81
82
  const isInitialLoadRef = useRef(true);
82
83
  // Check if the container is scrolled to the bottom
@@ -126,14 +127,14 @@ scrollOnInitialLoad = false, }) {
126
127
  clearTimeout(timeoutId);
127
128
  };
128
129
  }, [containerRef, onScroll]);
129
- const scrollToBottom = () => {
130
+ const scrollToBottom = useCallback(() => {
130
131
  if (containerRef.current) {
131
132
  containerRef.current.scrollTo({
132
133
  top: containerRef.current.scrollHeight,
133
134
  behavior: 'smooth',
134
135
  });
135
136
  }
136
- };
137
+ }, [containerRef]);
137
138
  return {
138
139
  showScrollButton,
139
140
  scrollToBottom,
@@ -1 +1 @@
1
- {"version":3,"file":"useScrollToBottom.js","sourceRoot":"","sources":["../../src/hooks/useScrollToBottom.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useRef, type RefObject, useState, useCallback} from 'react';\n\ninterface ScrollToBottomResult<T extends HTMLElement | null> {\n showScrollButton: boolean;\n scrollToBottom: () => void;\n}\n\n/**\n * Only show button and auto-scroll if we're scrolled up more\n * than {AT_BOTTOM_TOLERANCE}px from the bottom.\n */\nconst AT_BOTTOM_TOLERANCE = 100;\n\n/**\n * A React hook that provides automatic scrolling behavior for containers with dynamic content.\n *\n * This hook helps manage scroll behavior in containers where content is being added dynamically,\n * such as chat interfaces or logs. It automatically scrolls to the bottom when new content is added\n * if the user was already at the bottom, and provides a function to manually scroll to the bottom.\n *\n * @template T - The type of HTMLElement for the container and end references\n *\n * @param options - Configuration options\n * @param options.dataToObserve - The data to observe for changes (messages, items, etc.)\n * @param options.containerRef - Reference to the scrollable container element\n * @param options.endRef - Reference to an element at the end of the content\n * @param options.scrollOnInitialLoad - Whether to scroll to bottom on initial load (default: true)\n *\n * @returns An object containing:\n * - showScrollButton: Boolean indicating if the \"scroll to bottom\" button should be shown\n * - scrollToBottom: Function to programmatically scroll to the bottom\n *\n * @example\n * ```tsx\n * import { useRef } from 'react';\n * import { useScrollToBottom } from './use-scroll-to-bottom';\n *\n * function ChatContainer({ messages }) {\n * const containerRef = useRef<HTMLDivElement>(null);\n * const endRef = useRef<HTMLDivElement>(null);\n *\n * const { showScrollButton, scrollToBottom } = useScrollToBottom({\n * dataToObserve: messages,\n * containerRef,\n * endRef,\n * scrollOnInitialLoad: false // Disable scrolling on initial load\n * });\n *\n * return (\n * <div className=\"relative h-[500px]\">\n * <div ref={containerRef} className=\"h-full overflow-y-auto p-4\">\n * {messages.map((message) => (\n * <div key={message.id} className=\"mb-4\">\n * {message.text}\n * </div>\n * ))}\n * <div ref={endRef} />\n * </div>\n *\n * {showScrollButton && (\n * <button\n * onClick={scrollToBottom}\n * className=\"absolute bottom-4 right-4 rounded-full bg-blue-500 p-2\"\n * >\n * ↓\n * </button>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useScrollToBottom<T extends HTMLElement | null>({\n /**\n * The data to observe. Can be an array of items or a single item.\n * When the data changes, the hook will scroll to the bottom of the container.\n */\n dataToObserve,\n containerRef,\n endRef,\n /**\n * Whether to scroll to bottom on initial load.\n * @default false\n */\n scrollOnInitialLoad = false,\n}: {\n dataToObserve: unknown;\n containerRef: RefObject<T | null>;\n endRef: RefObject<T | null>;\n scrollOnInitialLoad?: boolean;\n}): ScrollToBottomResult<T> {\n const [showScrollButton, setShowButton] = useState(false);\n\n // Track if user was at bottom before content changes\n const wasAtBottomRef = useRef(scrollOnInitialLoad);\n\n // Track if this is the initial load\n const isInitialLoadRef = useRef(true);\n\n // Check if the container is scrolled to the bottom\n const checkIfAtBottom = useCallback((container: T) => {\n if (!container) return false;\n const {scrollTop, scrollHeight, clientHeight} = container;\n return scrollHeight - scrollTop - clientHeight <= AT_BOTTOM_TOLERANCE;\n }, []);\n\n // Extracted reusable handleScroll function\n const onScroll = useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const isAtBottom = checkIfAtBottom(container);\n\n // Update wasAtBottom state for next content change\n wasAtBottomRef.current = isAtBottom;\n\n // Show button only if not at bottom\n setShowButton(!isAtBottom);\n }, [checkIfAtBottom, containerRef]);\n\n // Handle new content being added\n useEffect(() => {\n if (!dataToObserve) return;\n\n const container = containerRef.current;\n const end = endRef.current;\n\n if (container && end && wasAtBottomRef.current) {\n // Only scroll if this is not the initial load or if scrollOnInitialLoad is true\n if (!isInitialLoadRef.current || scrollOnInitialLoad) {\n end.scrollIntoView({behavior: 'instant', block: 'end'});\n }\n }\n\n // Mark that initial load is complete\n isInitialLoadRef.current = false;\n\n // After content change, check scroll position again\n onScroll();\n }, [containerRef, dataToObserve, endRef, onScroll, scrollOnInitialLoad]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n container.addEventListener('scroll', onScroll);\n\n // Initial check with a slight delay to ensure proper measurement\n const timeoutId = setTimeout(onScroll, 100);\n\n return () => {\n container.removeEventListener('scroll', onScroll);\n clearTimeout(timeoutId);\n };\n }, [containerRef, onScroll]);\n\n const scrollToBottom = () => {\n if (containerRef.current) {\n containerRef.current.scrollTo({\n top: containerRef.current.scrollHeight,\n behavior: 'smooth',\n });\n }\n };\n\n return {\n showScrollButton,\n scrollToBottom,\n };\n}\n"]}
1
+ {"version":3,"file":"useScrollToBottom.js","sourceRoot":"","sources":["../../src/hooks/useScrollToBottom.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useRef, type RefObject, useState, useCallback} from 'react';\n\ninterface ScrollToBottomResult<T extends HTMLElement | null> {\n showScrollButton: boolean;\n scrollToBottom: () => void;\n}\n\n/**\n * Only show button and auto-scroll if we're scrolled up more\n * than {AT_BOTTOM_TOLERANCE}px from the bottom.\n */\nconst AT_BOTTOM_TOLERANCE = 100;\n\n/**\n * A React hook that provides automatic scrolling behavior for containers with dynamic content.\n *\n * This hook helps manage scroll behavior in containers where content is being added dynamically,\n * such as chat interfaces or logs. It automatically scrolls to the bottom when new content is added\n * if the user was already at the bottom, and provides a function to manually scroll to the bottom.\n *\n * @template T - The type of HTMLElement for the container and end references\n *\n * @param options - Configuration options\n * @param options.dataToObserve - The data to observe for changes (messages, items, etc.)\n * @param options.containerRef - Reference to the scrollable container element\n * @param options.endRef - Reference to an element at the end of the content\n * @param options.scrollOnInitialLoad - Whether to scroll to bottom on initial load (default: true)\n *\n * @returns An object containing:\n * - showScrollButton: Boolean indicating if the \"scroll to bottom\" button should be shown\n * - scrollToBottom: Function to programmatically scroll to the bottom\n *\n * @example\n * ```tsx\n * import { useRef } from 'react';\n * import { useScrollToBottom } from './use-scroll-to-bottom';\n *\n * function ChatContainer({ messages }) {\n * const containerRef = useRef<HTMLDivElement>(null);\n * const endRef = useRef<HTMLDivElement>(null);\n *\n * const { showScrollButton, scrollToBottom } = useScrollToBottom({\n * dataToObserve: messages,\n * containerRef,\n * endRef,\n * scrollOnInitialLoad: false // Disable scrolling on initial load\n * });\n *\n * return (\n * <div className=\"relative h-[500px]\">\n * <div ref={containerRef} className=\"h-full overflow-y-auto p-4\">\n * {messages.map((message) => (\n * <div key={message.id} className=\"mb-4\">\n * {message.text}\n * </div>\n * ))}\n * <div ref={endRef} />\n * </div>\n *\n * {showScrollButton && (\n * <button\n * onClick={scrollToBottom}\n * className=\"absolute bottom-4 right-4 rounded-full bg-blue-500 p-2\"\n * >\n * ↓\n * </button>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useScrollToBottom<T extends HTMLElement | null>({\n /**\n * The data to observe. Can be an array of items or a single item.\n * When the data changes, the hook will scroll to the bottom of the container.\n */\n dataToObserve,\n containerRef,\n endRef,\n /**\n * Whether to scroll to bottom on initial load.\n * @default false\n */\n scrollOnInitialLoad = false,\n}: {\n dataToObserve: unknown;\n containerRef: RefObject<T | null>;\n endRef: RefObject<T | null>;\n scrollOnInitialLoad?: boolean;\n}): ScrollToBottomResult<T> {\n const [showScrollButton, setShowButton] = useState(false);\n\n // Track if user was at bottom before content changes\n // Start as true since we're initially at the bottom\n const wasAtBottomRef = useRef(true);\n\n // Track if this is the initial load\n const isInitialLoadRef = useRef(true);\n\n // Check if the container is scrolled to the bottom\n const checkIfAtBottom = useCallback((container: T) => {\n if (!container) return false;\n const {scrollTop, scrollHeight, clientHeight} = container;\n return scrollHeight - scrollTop - clientHeight <= AT_BOTTOM_TOLERANCE;\n }, []);\n\n // Extracted reusable handleScroll function\n const onScroll = useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const isAtBottom = checkIfAtBottom(container);\n\n // Update wasAtBottom state for next content change\n wasAtBottomRef.current = isAtBottom;\n\n // Show button only if not at bottom\n setShowButton(!isAtBottom);\n }, [checkIfAtBottom, containerRef]);\n\n // Handle new content being added\n useEffect(() => {\n if (!dataToObserve) return;\n\n const container = containerRef.current;\n const end = endRef.current;\n\n if (container && end && wasAtBottomRef.current) {\n // Only scroll if this is not the initial load or if scrollOnInitialLoad is true\n if (!isInitialLoadRef.current || scrollOnInitialLoad) {\n end.scrollIntoView({behavior: 'instant', block: 'end'});\n }\n }\n\n // Mark that initial load is complete\n isInitialLoadRef.current = false;\n\n // After content change, check scroll position again\n onScroll();\n }, [containerRef, dataToObserve, endRef, onScroll, scrollOnInitialLoad]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n container.addEventListener('scroll', onScroll);\n\n // Initial check with a slight delay to ensure proper measurement\n const timeoutId = setTimeout(onScroll, 100);\n\n return () => {\n container.removeEventListener('scroll', onScroll);\n clearTimeout(timeoutId);\n };\n }, [containerRef, onScroll]);\n\n const scrollToBottom = useCallback(() => {\n if (containerRef.current) {\n containerRef.current.scrollTo({\n top: containerRef.current.scrollHeight,\n behavior: 'smooth',\n });\n }\n }, [containerRef]);\n\n return {\n showScrollButton,\n scrollToBottom,\n };\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import type { UIMessagePart } from '@sqlrooms/ai-config';
3
+ /**
4
+ * Type for a grouped message part (from useToolGrouping hook)
5
+ */
6
+ export type ToolGroup = {
7
+ type: 'text' | 'reasoning' | 'tool-group';
8
+ parts: UIMessagePart[];
9
+ startIndex: number;
10
+ title?: React.ReactNode;
11
+ /** Whether the ReasoningBox should be expanded by default (default: false) */
12
+ defaultExpanded?: boolean;
13
+ };
14
+ /**
15
+ * Custom hook to group consecutive tool parts and generate titles
16
+ * @param uiMessageParts - Array of UI message parts from the assistant
17
+ * @param containerWidth - Width of the container in pixels (for calculating truncation)
18
+ * @param exclude - Array of tool names that should not be grouped and must be rendered separately
19
+ * @param toolAdditionalData - Additional data for tool calls (e.g., agent tool execution details)
20
+ * @returns Grouped parts with generated titles for tool groups
21
+ */
22
+ export declare function useToolGrouping(uiMessageParts: UIMessagePart[], containerWidth?: number, exclude?: string[], toolAdditionalData?: Record<string, unknown>): ToolGroup[];
23
+ //# sourceMappingURL=useToolGrouping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useToolGrouping.d.ts","sourceRoot":"","sources":["../../src/hooks/useToolGrouping.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgB,MAAM,OAAO,CAAC;AAGrC,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,YAAY,CAAC;IAC1C,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,cAAc,EAAE,aAAa,EAAE,EAC/B,cAAc,GAAE,MAAU,EAC1B,OAAO,GAAE,MAAM,EAAO,EACtB,kBAAkB,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAC/C,SAAS,EAAE,CAiJb"}
@@ -0,0 +1,290 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { MapIcon, BarChart3Icon } from 'lucide-react';
4
+ import { isTextPart, isReasoningPart, isToolPart } from '../utils';
5
+ /**
6
+ * Custom hook to group consecutive tool parts and generate titles
7
+ * @param uiMessageParts - Array of UI message parts from the assistant
8
+ * @param containerWidth - Width of the container in pixels (for calculating truncation)
9
+ * @param exclude - Array of tool names that should not be grouped and must be rendered separately
10
+ * @param toolAdditionalData - Additional data for tool calls (e.g., agent tool execution details)
11
+ * @returns Grouped parts with generated titles for tool groups
12
+ */
13
+ export function useToolGrouping(uiMessageParts, containerWidth = 0, exclude = [], toolAdditionalData = {}) {
14
+ return useMemo(() => {
15
+ if (!uiMessageParts.length)
16
+ return [];
17
+ // Helper function to check if a part is a tool part (including dynamic-tool)
18
+ const isAnyToolPart = (part) => {
19
+ if (!part)
20
+ return false;
21
+ if (isToolPart(part))
22
+ return true;
23
+ // Also check for dynamic-tool type
24
+ return typeof part.type === 'string' && part.type === 'dynamic-tool';
25
+ };
26
+ // Helper function to check if a tool part should be excluded from grouping
27
+ const isExcludedTool = (part) => {
28
+ if (!isAnyToolPart(part))
29
+ return false;
30
+ const toolName = getToolName(part);
31
+ return exclude.includes(toolName);
32
+ };
33
+ const groups = [];
34
+ let i = 0;
35
+ while (i < uiMessageParts.length) {
36
+ const part = uiMessageParts[i];
37
+ if (!part) {
38
+ i++;
39
+ continue;
40
+ }
41
+ if (isTextPart(part)) {
42
+ groups.push({
43
+ type: 'text',
44
+ parts: [part],
45
+ startIndex: i,
46
+ });
47
+ i++;
48
+ }
49
+ else if (isReasoningPart(part)) {
50
+ groups.push({
51
+ type: 'reasoning',
52
+ parts: [part],
53
+ startIndex: i,
54
+ });
55
+ i++;
56
+ }
57
+ else if (isAnyToolPart(part)) {
58
+ // If this tool is excluded, render it separately and expand by default
59
+ if (isExcludedTool(part)) {
60
+ groups.push({
61
+ type: 'tool-group',
62
+ parts: [part],
63
+ startIndex: i,
64
+ title: generateToolGroupTitle([part], false, containerWidth, toolAdditionalData),
65
+ defaultExpanded: true, // Excluded tools are expanded by default
66
+ });
67
+ i++;
68
+ continue;
69
+ }
70
+ // Collect consecutive tool parts (including dynamic-tool)
71
+ // Skip over step-start and other metadata parts between tool calls
72
+ // But exclude tools that are in the exclude list
73
+ const toolParts = [part];
74
+ let j = i + 1;
75
+ while (j < uiMessageParts.length) {
76
+ const nextPart = uiMessageParts[j];
77
+ if (!nextPart) {
78
+ j++;
79
+ continue;
80
+ }
81
+ // If it's a tool part, check if it should be excluded
82
+ if (isAnyToolPart(nextPart)) {
83
+ // If excluded, stop grouping here
84
+ if (isExcludedTool(nextPart)) {
85
+ break;
86
+ }
87
+ toolParts.push(nextPart);
88
+ j++;
89
+ }
90
+ // If it's a step-start or step-finish, skip it but continue looking for more tools
91
+ else if (typeof nextPart.type === 'string' &&
92
+ (nextPart.type === 'step-start' ||
93
+ nextPart.type.startsWith('step-'))) {
94
+ j++;
95
+ }
96
+ // If it's text or reasoning, stop grouping
97
+ else if (isTextPart(nextPart) || isReasoningPart(nextPart)) {
98
+ break;
99
+ }
100
+ // Unknown part type, skip it
101
+ else {
102
+ j++;
103
+ }
104
+ }
105
+ // Check if the next group (before any text/reasoning) has more tool parts
106
+ // If there's a text or reasoning part before the next tool, this reasoning chain is complete
107
+ let hasMoreToolsAfter = false;
108
+ for (let k = j; k < uiMessageParts.length; k++) {
109
+ const nextPart = uiMessageParts[k];
110
+ if (!nextPart)
111
+ continue;
112
+ // If we hit a text or reasoning part, the reasoning chain is broken
113
+ if (isTextPart(nextPart) || isReasoningPart(nextPart)) {
114
+ hasMoreToolsAfter = false;
115
+ break;
116
+ }
117
+ // If we find another tool part, reasoning continues (but only if it's not excluded)
118
+ if (isAnyToolPart(nextPart) && !isExcludedTool(nextPart)) {
119
+ hasMoreToolsAfter = true;
120
+ break;
121
+ }
122
+ // Otherwise (step-start, etc.), keep looking
123
+ }
124
+ // Generate title for this tool group
125
+ const title = generateToolGroupTitle(toolParts, hasMoreToolsAfter, containerWidth, toolAdditionalData);
126
+ groups.push({
127
+ type: 'tool-group',
128
+ parts: toolParts,
129
+ startIndex: i,
130
+ title,
131
+ });
132
+ i = j;
133
+ }
134
+ else {
135
+ i++;
136
+ }
137
+ }
138
+ return groups;
139
+ }, [uiMessageParts, containerWidth, exclude, toolAdditionalData]);
140
+ }
141
+ /**
142
+ * Extract tool name from a tool part
143
+ */
144
+ function getToolName(part) {
145
+ if (typeof part.type === 'string' && part.type === 'dynamic-tool') {
146
+ return (part.toolName || 'unknown');
147
+ }
148
+ if (isToolPart(part)) {
149
+ return typeof part.type === 'string'
150
+ ? part.type.replace(/^tool-/, '') || 'unknown'
151
+ : 'unknown';
152
+ }
153
+ return 'unknown';
154
+ }
155
+ /**
156
+ * Get icon component based on tool name
157
+ */
158
+ function getToolIcon(toolName) {
159
+ if (toolName === 'createMapLayer') {
160
+ return _jsx(MapIcon, { className: "h-3 w-3 shrink-0 text-blue-500" });
161
+ }
162
+ if (toolName === 'chart') {
163
+ return _jsx(BarChart3Icon, { className: "h-3 w-3 shrink-0 text-green-500" });
164
+ }
165
+ return null;
166
+ }
167
+ /**
168
+ * Generate a dynamic title for a tool group based on completion status and reasoning
169
+ * @param toolParts - The tool parts in this group
170
+ * @param hasMoreToolsAfter - Whether there are more tool calls after this group
171
+ * @param containerWidth - Width of the container in pixels (for calculating truncation)
172
+ * @param toolAdditionalData - Additional data for tool calls (e.g., agent tool execution details)
173
+ */
174
+ function generateToolGroupTitle(toolParts, hasMoreToolsAfter, containerWidth, toolAdditionalData = {}) {
175
+ // Filter to only tool parts
176
+ const actualToolParts = toolParts.filter((p) => p &&
177
+ (isToolPart(p) ||
178
+ (typeof p.type === 'string' && p.type === 'dynamic-tool')));
179
+ if (actualToolParts.length === 0)
180
+ return 'Thought';
181
+ // Check if all tools in this group are completed
182
+ const allCompleted = actualToolParts.every((p) => {
183
+ const state = p.state;
184
+ return state === 'output-available' || state === 'output-error';
185
+ });
186
+ const toolCount = actualToolParts.length;
187
+ // Collect all unique tool names to determine icons
188
+ const toolNames = new Set(actualToolParts
189
+ .map((p) => getToolName(p))
190
+ .filter((name) => name !== 'unknown'));
191
+ // Check if any of the tools are agent tools
192
+ const hasAgentTool = Array.from(toolNames).some((name) => name.startsWith('agent-'));
193
+ // Check if we have both 'createMapLayer' and 'chart' tools
194
+ const hasMapLayer = toolNames.has('createMapLayer');
195
+ const hasChart = toolNames.has('chart');
196
+ // Generate icons - show both if both types are present
197
+ let icon = null;
198
+ if (hasMapLayer && hasChart) {
199
+ icon = (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx(MapIcon, { className: "h-3 w-3 shrink-0 text-blue-500" }), _jsx(BarChart3Icon, { className: "h-3 w-3 shrink-0 text-green-500" })] }));
200
+ }
201
+ else if (hasMapLayer) {
202
+ icon = _jsx(MapIcon, { className: "h-3 w-3 shrink-0 text-blue-500" });
203
+ }
204
+ else if (hasChart) {
205
+ icon = _jsx(BarChart3Icon, { className: "h-3 w-3 shrink-0 text-green-500" });
206
+ }
207
+ else {
208
+ // Fallback to first tool icon if neither map nor chart
209
+ const firstToolPart = actualToolParts[0];
210
+ const firstToolName = firstToolPart
211
+ ? getToolName(firstToolPart)
212
+ : 'unknown';
213
+ icon = getToolIcon(firstToolName);
214
+ }
215
+ // Show "Thinking..." if:
216
+ // 1. Tools are not completed yet, OR
217
+ // 2. Tools are completed but there are more tool calls coming after
218
+ const isStillThinking = !allCompleted || hasMoreToolsAfter;
219
+ if (isStillThinking) {
220
+ // For active thinking, show reasoning text if available
221
+ const lastToolPart = actualToolParts[actualToolParts.length - 1];
222
+ const reasoning = lastToolPart
223
+ ? lastToolPart.input?.reasoning
224
+ : undefined;
225
+ // Use different base title for agent tools
226
+ let baseTitle;
227
+ if (hasAgentTool) {
228
+ // Extract agent tool names from toolAdditionalData
229
+ const agentToolNames = [];
230
+ for (const toolPart of actualToolParts) {
231
+ const toolCallId = toolPart.toolCallId;
232
+ if (toolCallId && toolAdditionalData[toolCallId]) {
233
+ const agentData = toolAdditionalData[toolCallId];
234
+ if (agentData?.agentToolCalls) {
235
+ // Get currently executing or pending agent tools
236
+ const executingTools = agentData.agentToolCalls
237
+ .filter((call) => call.state === 'pending' || call.state === 'success')
238
+ .map((call) => call.toolName);
239
+ agentToolNames.push(...executingTools);
240
+ }
241
+ }
242
+ }
243
+ // Remove duplicates
244
+ const uniqueToolNames = Array.from(new Set(agentToolNames));
245
+ if (uniqueToolNames.length > 0) {
246
+ const toolNamesList = uniqueToolNames.join(', ');
247
+ baseTitle =
248
+ uniqueToolNames.length === 1
249
+ ? `Agent is calling: ${toolNamesList}...`
250
+ : `Agent is calling: ${toolNamesList}...`;
251
+ }
252
+ else {
253
+ baseTitle =
254
+ toolCount === 1
255
+ ? 'Agent tool is executing...'
256
+ : `Agent tools are executing... (${toolCount} tools)`;
257
+ }
258
+ }
259
+ else {
260
+ baseTitle =
261
+ toolCount === 1 ? 'Thinking...' : `Thinking... (${toolCount} tools)`;
262
+ }
263
+ // Calculate max reasoning length based on container width
264
+ // Estimate: average character width ~7px, reserve space for icon (~24px), padding (~16px), and base text
265
+ const baseTextWidth = baseTitle.length * 7; // Rough estimate for "Thinking..." or "Thinking... (X tools)"
266
+ const iconWidth = 24; // Space for icon(s)
267
+ const padding = 16; // Padding and gaps
268
+ const availableWidth = containerWidth > 0
269
+ ? containerWidth - baseTextWidth - iconWidth - padding
270
+ : 0;
271
+ // Estimate characters that fit: divide by average char width (~7px), with a minimum of 20 and max of 150
272
+ // Removed hard cap of 60 to allow scaling with container width
273
+ const maxReasoningLength = containerWidth > 0
274
+ ? Math.max(20, Math.min(150, Math.floor(availableWidth / 7)))
275
+ : 40; // Fallback to 40 if width not available
276
+ const truncatedReasoning = reasoning && reasoning.length > maxReasoningLength
277
+ ? `${reasoning.substring(0, maxReasoningLength).toLowerCase()}...`
278
+ : reasoning?.toLowerCase();
279
+ const titleText = truncatedReasoning
280
+ ? `${baseTitle} ${truncatedReasoning}`
281
+ : baseTitle;
282
+ return (_jsxs("span", { className: "flex w-full items-center justify-between", children: [_jsx("span", { className: "truncate", children: titleText }), icon] }));
283
+ }
284
+ else {
285
+ // For completed thoughts (and no more tools after), just show tool count without reasoning
286
+ const titleText = toolCount === 1 ? 'Thought (1 tool)' : `Thought (${toolCount} tools)`;
287
+ return (_jsxs("span", { className: "flex w-full items-center justify-between", children: [_jsx("span", { className: "truncate", children: titleText }), icon] }));
288
+ }
289
+ }
290
+ //# sourceMappingURL=useToolGrouping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useToolGrouping.js","sourceRoot":"","sources":["../../src/hooks/useToolGrouping.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useMemo} from 'react';\nimport {MapIcon, BarChart3Icon} from 'lucide-react';\nimport {isTextPart, isReasoningPart, isToolPart} from '../utils';\nimport type {UIMessagePart} from '@sqlrooms/ai-config';\n\n/**\n * Type for a grouped message part (from useToolGrouping hook)\n */\nexport type ToolGroup = {\n type: 'text' | 'reasoning' | 'tool-group';\n parts: UIMessagePart[];\n startIndex: number;\n title?: React.ReactNode;\n /** Whether the ReasoningBox should be expanded by default (default: false) */\n defaultExpanded?: boolean;\n};\n\n/**\n * Custom hook to group consecutive tool parts and generate titles\n * @param uiMessageParts - Array of UI message parts from the assistant\n * @param containerWidth - Width of the container in pixels (for calculating truncation)\n * @param exclude - Array of tool names that should not be grouped and must be rendered separately\n * @param toolAdditionalData - Additional data for tool calls (e.g., agent tool execution details)\n * @returns Grouped parts with generated titles for tool groups\n */\nexport function useToolGrouping(\n uiMessageParts: UIMessagePart[],\n containerWidth: number = 0,\n exclude: string[] = [],\n toolAdditionalData: Record<string, unknown> = {},\n): ToolGroup[] {\n return useMemo(() => {\n if (!uiMessageParts.length) return [];\n\n // Helper function to check if a part is a tool part (including dynamic-tool)\n const isAnyToolPart = (part: UIMessagePart | undefined): boolean => {\n if (!part) return false;\n if (isToolPart(part)) return true;\n // Also check for dynamic-tool type\n return typeof part.type === 'string' && part.type === 'dynamic-tool';\n };\n\n // Helper function to check if a tool part should be excluded from grouping\n const isExcludedTool = (part: UIMessagePart): boolean => {\n if (!isAnyToolPart(part)) return false;\n const toolName = getToolName(part);\n return exclude.includes(toolName);\n };\n\n const groups: ToolGroup[] = [];\n\n let i = 0;\n while (i < uiMessageParts.length) {\n const part = uiMessageParts[i];\n if (!part) {\n i++;\n continue;\n }\n\n if (isTextPart(part)) {\n groups.push({\n type: 'text',\n parts: [part],\n startIndex: i,\n });\n i++;\n } else if (isReasoningPart(part)) {\n groups.push({\n type: 'reasoning',\n parts: [part],\n startIndex: i,\n });\n i++;\n } else if (isAnyToolPart(part)) {\n // If this tool is excluded, render it separately and expand by default\n if (isExcludedTool(part)) {\n groups.push({\n type: 'tool-group',\n parts: [part],\n startIndex: i,\n title: generateToolGroupTitle(\n [part],\n false,\n containerWidth,\n toolAdditionalData,\n ),\n defaultExpanded: true, // Excluded tools are expanded by default\n });\n i++;\n continue;\n }\n\n // Collect consecutive tool parts (including dynamic-tool)\n // Skip over step-start and other metadata parts between tool calls\n // But exclude tools that are in the exclude list\n const toolParts: UIMessagePart[] = [part];\n let j = i + 1;\n while (j < uiMessageParts.length) {\n const nextPart = uiMessageParts[j];\n if (!nextPart) {\n j++;\n continue;\n }\n\n // If it's a tool part, check if it should be excluded\n if (isAnyToolPart(nextPart)) {\n // If excluded, stop grouping here\n if (isExcludedTool(nextPart)) {\n break;\n }\n toolParts.push(nextPart);\n j++;\n }\n // If it's a step-start or step-finish, skip it but continue looking for more tools\n else if (\n typeof nextPart.type === 'string' &&\n (nextPart.type === 'step-start' ||\n nextPart.type.startsWith('step-'))\n ) {\n j++;\n }\n // If it's text or reasoning, stop grouping\n else if (isTextPart(nextPart) || isReasoningPart(nextPart)) {\n break;\n }\n // Unknown part type, skip it\n else {\n j++;\n }\n }\n\n // Check if the next group (before any text/reasoning) has more tool parts\n // If there's a text or reasoning part before the next tool, this reasoning chain is complete\n let hasMoreToolsAfter = false;\n for (let k = j; k < uiMessageParts.length; k++) {\n const nextPart = uiMessageParts[k];\n if (!nextPart) continue;\n\n // If we hit a text or reasoning part, the reasoning chain is broken\n if (isTextPart(nextPart) || isReasoningPart(nextPart)) {\n hasMoreToolsAfter = false;\n break;\n }\n\n // If we find another tool part, reasoning continues (but only if it's not excluded)\n if (isAnyToolPart(nextPart) && !isExcludedTool(nextPart)) {\n hasMoreToolsAfter = true;\n break;\n }\n\n // Otherwise (step-start, etc.), keep looking\n }\n\n // Generate title for this tool group\n const title = generateToolGroupTitle(\n toolParts,\n hasMoreToolsAfter,\n containerWidth,\n toolAdditionalData,\n );\n\n groups.push({\n type: 'tool-group',\n parts: toolParts,\n startIndex: i,\n title,\n });\n i = j;\n } else {\n i++;\n }\n }\n\n return groups;\n }, [uiMessageParts, containerWidth, exclude, toolAdditionalData]);\n}\n\n/**\n * Extract tool name from a tool part\n */\nfunction getToolName(part: UIMessagePart): string {\n if (typeof part.type === 'string' && part.type === 'dynamic-tool') {\n return ((part as any).toolName || 'unknown') as string;\n }\n if (isToolPart(part)) {\n return typeof part.type === 'string'\n ? part.type.replace(/^tool-/, '') || 'unknown'\n : 'unknown';\n }\n return 'unknown';\n}\n\n/**\n * Get icon component based on tool name\n */\nfunction getToolIcon(toolName: string): React.ReactNode | null {\n if (toolName === 'createMapLayer') {\n return <MapIcon className=\"h-3 w-3 shrink-0 text-blue-500\" />;\n }\n if (toolName === 'chart') {\n return <BarChart3Icon className=\"h-3 w-3 shrink-0 text-green-500\" />;\n }\n return null;\n}\n\n/**\n * Generate a dynamic title for a tool group based on completion status and reasoning\n * @param toolParts - The tool parts in this group\n * @param hasMoreToolsAfter - Whether there are more tool calls after this group\n * @param containerWidth - Width of the container in pixels (for calculating truncation)\n * @param toolAdditionalData - Additional data for tool calls (e.g., agent tool execution details)\n */\nfunction generateToolGroupTitle(\n toolParts: UIMessagePart[],\n hasMoreToolsAfter: boolean,\n containerWidth: number,\n toolAdditionalData: Record<string, unknown> = {},\n): React.ReactNode {\n // Filter to only tool parts\n const actualToolParts = toolParts.filter(\n (p) =>\n p &&\n (isToolPart(p) ||\n (typeof p.type === 'string' && p.type === 'dynamic-tool')),\n );\n\n if (actualToolParts.length === 0) return 'Thought';\n\n // Check if all tools in this group are completed\n const allCompleted = actualToolParts.every((p) => {\n const state = (p as any).state;\n return state === 'output-available' || state === 'output-error';\n });\n\n const toolCount = actualToolParts.length;\n\n // Collect all unique tool names to determine icons\n const toolNames = new Set(\n actualToolParts\n .map((p) => getToolName(p))\n .filter((name) => name !== 'unknown'),\n );\n\n // Check if any of the tools are agent tools\n const hasAgentTool = Array.from(toolNames).some((name) =>\n name.startsWith('agent-'),\n );\n\n // Check if we have both 'createMapLayer' and 'chart' tools\n const hasMapLayer = toolNames.has('createMapLayer');\n const hasChart = toolNames.has('chart');\n\n // Generate icons - show both if both types are present\n let icon: React.ReactNode | null = null;\n if (hasMapLayer && hasChart) {\n icon = (\n <span className=\"flex items-center gap-1\">\n <MapIcon className=\"h-3 w-3 shrink-0 text-blue-500\" />\n <BarChart3Icon className=\"h-3 w-3 shrink-0 text-green-500\" />\n </span>\n );\n } else if (hasMapLayer) {\n icon = <MapIcon className=\"h-3 w-3 shrink-0 text-blue-500\" />;\n } else if (hasChart) {\n icon = <BarChart3Icon className=\"h-3 w-3 shrink-0 text-green-500\" />;\n } else {\n // Fallback to first tool icon if neither map nor chart\n const firstToolPart = actualToolParts[0];\n const firstToolName = firstToolPart\n ? getToolName(firstToolPart)\n : 'unknown';\n icon = getToolIcon(firstToolName);\n }\n\n // Show \"Thinking...\" if:\n // 1. Tools are not completed yet, OR\n // 2. Tools are completed but there are more tool calls coming after\n const isStillThinking = !allCompleted || hasMoreToolsAfter;\n\n if (isStillThinking) {\n // For active thinking, show reasoning text if available\n const lastToolPart = actualToolParts[actualToolParts.length - 1];\n const reasoning = lastToolPart\n ? ((lastToolPart as any).input?.reasoning as string | undefined)\n : undefined;\n\n // Use different base title for agent tools\n let baseTitle: string;\n if (hasAgentTool) {\n // Extract agent tool names from toolAdditionalData\n const agentToolNames: string[] = [];\n for (const toolPart of actualToolParts) {\n const toolCallId = (toolPart as any).toolCallId;\n if (toolCallId && toolAdditionalData[toolCallId]) {\n const agentData = toolAdditionalData[toolCallId] as {\n agentToolCalls?: Array<{toolName: string; state: string}>;\n };\n if (agentData?.agentToolCalls) {\n // Get currently executing or pending agent tools\n const executingTools = agentData.agentToolCalls\n .filter(\n (call) => call.state === 'pending' || call.state === 'success',\n )\n .map((call) => call.toolName);\n agentToolNames.push(...executingTools);\n }\n }\n }\n\n // Remove duplicates\n const uniqueToolNames = Array.from(new Set(agentToolNames));\n\n if (uniqueToolNames.length > 0) {\n const toolNamesList = uniqueToolNames.join(', ');\n baseTitle =\n uniqueToolNames.length === 1\n ? `Agent is calling: ${toolNamesList}...`\n : `Agent is calling: ${toolNamesList}...`;\n } else {\n baseTitle =\n toolCount === 1\n ? 'Agent tool is executing...'\n : `Agent tools are executing... (${toolCount} tools)`;\n }\n } else {\n baseTitle =\n toolCount === 1 ? 'Thinking...' : `Thinking... (${toolCount} tools)`;\n }\n\n // Calculate max reasoning length based on container width\n // Estimate: average character width ~7px, reserve space for icon (~24px), padding (~16px), and base text\n const baseTextWidth = baseTitle.length * 7; // Rough estimate for \"Thinking...\" or \"Thinking... (X tools)\"\n const iconWidth = 24; // Space for icon(s)\n const padding = 16; // Padding and gaps\n const availableWidth =\n containerWidth > 0\n ? containerWidth - baseTextWidth - iconWidth - padding\n : 0;\n // Estimate characters that fit: divide by average char width (~7px), with a minimum of 20 and max of 150\n // Removed hard cap of 60 to allow scaling with container width\n const maxReasoningLength =\n containerWidth > 0\n ? Math.max(20, Math.min(150, Math.floor(availableWidth / 7)))\n : 40; // Fallback to 40 if width not available\n\n const truncatedReasoning =\n reasoning && reasoning.length > maxReasoningLength\n ? `${reasoning.substring(0, maxReasoningLength).toLowerCase()}...`\n : reasoning?.toLowerCase();\n\n const titleText = truncatedReasoning\n ? `${baseTitle} ${truncatedReasoning}`\n : baseTitle;\n return (\n <span className=\"flex w-full items-center justify-between\">\n <span className=\"truncate\">{titleText}</span>\n {icon}\n </span>\n );\n } else {\n // For completed thoughts (and no more tools after), just show tool count without reasoning\n const titleText =\n toolCount === 1 ? 'Thought (1 tool)' : `Thought (${toolCount} tools)`;\n return (\n <span className=\"flex w-full items-center justify-between\">\n <span className=\"truncate\">{titleText}</span>\n {icon}\n </span>\n );\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export { AnalysisResultsContainer } from './components/AnalysisResultsContainer'
8
8
  export { AnalysisResult } from './components/AnalysisResult';
9
9
  export { useScrollToBottom } from './hooks/useScrollToBottom';
10
10
  export { useAiChat } from './hooks/useAiChat';
11
+ export { PromptSuggestions } from './components/PromptSuggestions';
11
12
  export { ModelSelector } from './components/ModelSelector';
12
13
  export { SessionControls } from './components/SessionControls';
13
14
  export { QueryControls } from './components/QueryControls';
@@ -20,4 +21,8 @@ export { ToolErrorMessage } from './components/tools/ToolErrorMessage';
20
21
  export { ToolCallInfo } from './components/ToolCallInfo';
21
22
  export { AiSliceConfig, createDefaultAiConfig } from '@sqlrooms/ai-config';
22
23
  export { AiThinkingDots } from './components/AiThinkingDots';
24
+ export { cleanupPendingAnalysisResults, ToolAbortError } from './utils';
25
+ export { convertToAiSDKTools, completeIncompleteToolCalls, } from './chatTransport';
26
+ export { processAgentStream, updateAgentToolCallData } from './agents/AgentUtils';
27
+ export type { AgentStreamResult, UIMessageChunk, AgentToolCall, AgentToolCallAdditionalData, } from './agents/AgentUtils';
23
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,aAAa,EAAE,cAAc,EAAC,MAAM,WAAW,CAAC;AAExD,YAAY,EAAC,YAAY,EAAC,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAC,wBAAwB,EAAC,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAC,iBAAiB,EAAC,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,mBAAmB,EAAC,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAAC,cAAc,EAAC,MAAM,qCAAqC,CAAC;AACnE,OAAO,EAAC,eAAe,EAAC,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAC,YAAY,EAAC,MAAM,mCAAmC,CAAC;AAC/D,YAAY,EAAC,WAAW,EAAC,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAC,gBAAgB,EAAC,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AAEvD,OAAO,EAAC,aAAa,EAAE,qBAAqB,EAAC,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,aAAa,EAAE,cAAc,EAAC,MAAM,WAAW,CAAC;AAExD,YAAY,EAAC,YAAY,EAAC,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAC,wBAAwB,EAAC,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAC,iBAAiB,EAAC,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAC,iBAAiB,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,mBAAmB,EAAC,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAAC,cAAc,EAAC,MAAM,qCAAqC,CAAC;AACnE,OAAO,EAAC,eAAe,EAAC,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAC,YAAY,EAAC,MAAM,mCAAmC,CAAC;AAC/D,YAAY,EAAC,WAAW,EAAC,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAC,gBAAgB,EAAC,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AAEvD,OAAO,EAAC,aAAa,EAAE,qBAAqB,EAAC,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAC,6BAA6B,EAAE,cAAc,EAAC,MAAM,SAAS,CAAC;AACtE,OAAO,EACL,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAC,kBAAkB,EAAE,uBAAuB,EAAC,MAAM,qBAAqB,CAAC;AAChF,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ export { AnalysisResultsContainer } from './components/AnalysisResultsContainer'
7
7
  export { AnalysisResult } from './components/AnalysisResult';
8
8
  export { useScrollToBottom } from './hooks/useScrollToBottom';
9
9
  export { useAiChat } from './hooks/useAiChat';
10
+ export { PromptSuggestions } from './components/PromptSuggestions';
10
11
  export { ModelSelector } from './components/ModelSelector';
11
12
  export { SessionControls } from './components/SessionControls';
12
13
  export { QueryControls } from './components/QueryControls';
@@ -18,4 +19,7 @@ export { ToolErrorMessage } from './components/tools/ToolErrorMessage';
18
19
  export { ToolCallInfo } from './components/ToolCallInfo';
19
20
  export { AiSliceConfig, createDefaultAiConfig } from '@sqlrooms/ai-config';
20
21
  export { AiThinkingDots } from './components/AiThinkingDots';
22
+ export { cleanupPendingAnalysisResults, ToolAbortError } from './utils';
23
+ export { convertToAiSDKTools, completeIncompleteToolCalls, } from './chatTransport';
24
+ export { processAgentStream, updateAgentToolCallData } from './agents/AgentUtils';
21
25
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,aAAa,EAAE,cAAc,EAAC,MAAM,WAAW,CAAC;AAGxD,OAAO,EAAC,wBAAwB,EAAC,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAC,iBAAiB,EAAC,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,mBAAmB,EAAC,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAAC,cAAc,EAAC,MAAM,qCAAqC,CAAC;AACnE,OAAO,EAAC,eAAe,EAAC,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAC,YAAY,EAAC,MAAM,mCAAmC,CAAC;AAE/D,OAAO,EAAC,gBAAgB,EAAC,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AAEvD,OAAO,EAAC,aAAa,EAAE,qBAAqB,EAAC,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC","sourcesContent":["/**\n * {@include ../README.md}\n * @packageDocumentation\n */\n\nexport {createAiSlice, useStoreWithAi} from './AiSlice';\n\nexport type {AiSliceState} from './AiSlice';\nexport {AnalysisResultsContainer} from './components/AnalysisResultsContainer';\nexport {AnalysisResult} from './components/AnalysisResult';\nexport {useScrollToBottom} from './hooks/useScrollToBottom';\nexport {useAiChat} from './hooks/useAiChat';\n\nexport {ModelSelector} from './components/ModelSelector';\nexport {SessionControls} from './components/SessionControls';\nexport {QueryControls} from './components/QueryControls';\nexport {DeleteSessionDialog} from './components/session/DeleteSessionDialog';\nexport {SessionActions} from './components/session/SessionActions';\nexport {SessionDropdown} from './components/session/SessionDropdown';\nexport {SessionTitle} from './components/session/SessionTitle';\nexport type {SessionType} from './components/session/SessionType';\nexport {ToolErrorMessage} from './components/tools/ToolErrorMessage';\nexport {ToolCallInfo} from './components/ToolCallInfo';\n\nexport {AiSliceConfig, createDefaultAiConfig} from '@sqlrooms/ai-config';\nexport {AiThinkingDots} from './components/AiThinkingDots';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,aAAa,EAAE,cAAc,EAAC,MAAM,WAAW,CAAC;AAGxD,OAAO,EAAC,wBAAwB,EAAC,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAC,iBAAiB,EAAC,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAC,iBAAiB,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,mBAAmB,EAAC,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAAC,cAAc,EAAC,MAAM,qCAAqC,CAAC;AACnE,OAAO,EAAC,eAAe,EAAC,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAC,YAAY,EAAC,MAAM,mCAAmC,CAAC;AAE/D,OAAO,EAAC,gBAAgB,EAAC,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AAEvD,OAAO,EAAC,aAAa,EAAE,qBAAqB,EAAC,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAC,6BAA6B,EAAE,cAAc,EAAC,MAAM,SAAS,CAAC;AACtE,OAAO,EACL,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAC,kBAAkB,EAAE,uBAAuB,EAAC,MAAM,qBAAqB,CAAC","sourcesContent":["/**\n * {@include ../README.md}\n * @packageDocumentation\n */\n\nexport {createAiSlice, useStoreWithAi} from './AiSlice';\n\nexport type {AiSliceState} from './AiSlice';\nexport {AnalysisResultsContainer} from './components/AnalysisResultsContainer';\nexport {AnalysisResult} from './components/AnalysisResult';\nexport {useScrollToBottom} from './hooks/useScrollToBottom';\nexport {useAiChat} from './hooks/useAiChat';\n\nexport {PromptSuggestions} from './components/PromptSuggestions';\nexport {ModelSelector} from './components/ModelSelector';\nexport {SessionControls} from './components/SessionControls';\nexport {QueryControls} from './components/QueryControls';\nexport {DeleteSessionDialog} from './components/session/DeleteSessionDialog';\nexport {SessionActions} from './components/session/SessionActions';\nexport {SessionDropdown} from './components/session/SessionDropdown';\nexport {SessionTitle} from './components/session/SessionTitle';\nexport type {SessionType} from './components/session/SessionType';\nexport {ToolErrorMessage} from './components/tools/ToolErrorMessage';\nexport {ToolCallInfo} from './components/ToolCallInfo';\n\nexport {AiSliceConfig, createDefaultAiConfig} from '@sqlrooms/ai-config';\nexport {AiThinkingDots} from './components/AiThinkingDots';\nexport {cleanupPendingAnalysisResults, ToolAbortError} from './utils';\nexport {\n convertToAiSDKTools,\n completeIncompleteToolCalls,\n} from './chatTransport';\n\nexport {processAgentStream, updateAgentToolCallData} from './agents/AgentUtils';\nexport type {\n AgentStreamResult,\n UIMessageChunk,\n AgentToolCall,\n AgentToolCallAdditionalData,\n} from './agents/AgentUtils';\n"]}