@sqlrooms/ai-core 0.27.0-rc.0 → 0.27.0-rc.2

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 (92) hide show
  1. package/README.md +2 -2
  2. package/dist/AiSlice.d.ts +33 -46
  3. package/dist/AiSlice.d.ts.map +1 -1
  4. package/dist/AiSlice.js +183 -91
  5. package/dist/AiSlice.js.map +1 -1
  6. package/dist/agents/AgentUtils.d.ts +19 -11
  7. package/dist/agents/AgentUtils.d.ts.map +1 -1
  8. package/dist/agents/AgentUtils.js +95 -57
  9. package/dist/agents/AgentUtils.js.map +1 -1
  10. package/dist/chatTransport.d.ts +13 -18
  11. package/dist/chatTransport.d.ts.map +1 -1
  12. package/dist/chatTransport.js +139 -203
  13. package/dist/chatTransport.js.map +1 -1
  14. package/dist/components/AnalysisResult.d.ts +1 -1
  15. package/dist/components/AnalysisResult.d.ts.map +1 -1
  16. package/dist/components/AnalysisResult.js +8 -27
  17. package/dist/components/AnalysisResult.js.map +1 -1
  18. package/dist/components/AnalysisResultsContainer.d.ts +1 -1
  19. package/dist/components/AnalysisResultsContainer.d.ts.map +1 -1
  20. package/dist/components/AnalysisResultsContainer.js +7 -5
  21. package/dist/components/AnalysisResultsContainer.js.map +1 -1
  22. package/dist/components/Chat.d.ts +13 -0
  23. package/dist/components/Chat.d.ts.map +1 -0
  24. package/dist/components/Chat.js +17 -0
  25. package/dist/components/Chat.js.map +1 -0
  26. package/dist/components/GroupedMessageParts.d.ts +2 -0
  27. package/dist/components/GroupedMessageParts.d.ts.map +1 -1
  28. package/dist/components/GroupedMessageParts.js +9 -3
  29. package/dist/components/GroupedMessageParts.js.map +1 -1
  30. package/dist/components/MessagePartsList.d.ts +2 -0
  31. package/dist/components/MessagePartsList.d.ts.map +1 -1
  32. package/dist/components/MessagePartsList.js +4 -4
  33. package/dist/components/MessagePartsList.js.map +1 -1
  34. package/dist/components/PromptSuggestions.d.ts.map +1 -1
  35. package/dist/components/PromptSuggestions.js +8 -5
  36. package/dist/components/PromptSuggestions.js.map +1 -1
  37. package/dist/components/QueryControls.d.ts.map +1 -1
  38. package/dist/components/QueryControls.js +26 -19
  39. package/dist/components/QueryControls.js.map +1 -1
  40. package/dist/components/SessionChatManager.d.ts +9 -0
  41. package/dist/components/SessionChatManager.d.ts.map +1 -0
  42. package/dist/components/SessionChatManager.js +27 -0
  43. package/dist/components/SessionChatManager.js.map +1 -0
  44. package/dist/components/SessionChatProvider.d.ts +13 -0
  45. package/dist/components/SessionChatProvider.d.ts.map +1 -0
  46. package/dist/components/SessionChatProvider.js +32 -0
  47. package/dist/components/SessionChatProvider.js.map +1 -0
  48. package/dist/components/SessionControls.d.ts +0 -1
  49. package/dist/components/SessionControls.d.ts.map +1 -1
  50. package/dist/components/SessionControls.js +4 -2
  51. package/dist/components/SessionControls.js.map +1 -1
  52. package/dist/components/ToolPartRenderer.d.ts +3 -1
  53. package/dist/components/ToolPartRenderer.d.ts.map +1 -1
  54. package/dist/components/ToolPartRenderer.js +2 -3
  55. package/dist/components/ToolPartRenderer.js.map +1 -1
  56. package/dist/components/tools/ToolErrorMessage.d.ts +1 -30
  57. package/dist/components/tools/ToolErrorMessage.d.ts.map +1 -1
  58. package/dist/components/tools/ToolErrorMessage.js +18 -14
  59. package/dist/components/tools/ToolErrorMessage.js.map +1 -1
  60. package/dist/components/tools/ToolResult.d.ts.map +1 -1
  61. package/dist/components/tools/ToolResult.js +5 -1
  62. package/dist/components/tools/ToolResult.js.map +1 -1
  63. package/dist/constants.d.ts +8 -0
  64. package/dist/constants.d.ts.map +1 -1
  65. package/dist/constants.js +8 -0
  66. package/dist/constants.js.map +1 -1
  67. package/dist/hooks/useScrollToBottom.d.ts +1 -1
  68. package/dist/hooks/useScrollToBottom.js +1 -1
  69. package/dist/hooks/useScrollToBottom.js.map +1 -1
  70. package/dist/hooks/useSessionChat.d.ts +38 -0
  71. package/dist/hooks/useSessionChat.d.ts.map +1 -0
  72. package/dist/hooks/{useAiChat.js → useSessionChat.js} +50 -46
  73. package/dist/hooks/useSessionChat.js.map +1 -0
  74. package/dist/index.d.ts +4 -2
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +5 -2
  77. package/dist/index.js.map +1 -1
  78. package/dist/types.d.ts +21 -4
  79. package/dist/types.d.ts.map +1 -1
  80. package/dist/types.js.map +1 -1
  81. package/dist/utils.d.ts +28 -1
  82. package/dist/utils.d.ts.map +1 -1
  83. package/dist/utils.js +113 -1
  84. package/dist/utils.js.map +1 -1
  85. package/package.json +7 -7
  86. package/dist/components/DeleteConfirmationDialog.d.ts +0 -14
  87. package/dist/components/DeleteConfirmationDialog.d.ts.map +0 -1
  88. package/dist/components/DeleteConfirmationDialog.js +0 -6
  89. package/dist/components/DeleteConfirmationDialog.js.map +0 -1
  90. package/dist/hooks/useAiChat.d.ts +0 -39
  91. package/dist/hooks/useAiChat.d.ts.map +0 -1
  92. package/dist/hooks/useAiChat.js.map +0 -1
package/README.md CHANGED
@@ -43,7 +43,7 @@ const {roomStore, useRoomStore} = createRoomStore({
43
43
  getInstructions: () => {
44
44
  return `You are an AI assistant that can answer questions and help with tasks.`;
45
45
  },
46
- initialAnalysisPrompt: 'What insights can you provide from my data?',
46
+ initialPrompt: 'What insights can you provide from my data?',
47
47
  tools: {
48
48
  // Your tools
49
49
  },
@@ -464,7 +464,7 @@ createAiSlice({
464
464
  The `processAgentStream` function handles the complexity of integrating agent execution into the main conversation:
465
465
 
466
466
  ```typescript
467
- await processAgentStream(agentResult, store, parentToolCallId)
467
+ await processAgentStream(agentResult, store, parentToolCallId, abortSignal)
468
468
  ```
469
469
 
470
470
  **What it handles:**
package/dist/AiSlice.d.ts CHANGED
@@ -1,42 +1,33 @@
1
1
  import { AiSliceConfig, AnalysisResultSchema, AnalysisSessionSchema } from '@sqlrooms/ai-config';
2
2
  import { type StateCreator } from '@sqlrooms/room-store';
3
- import { UIMessage, DefaultChatTransport, LanguageModel, UITools, ChatOnDataCallback, UIDataTypes } from 'ai';
3
+ import { UIMessage, DefaultChatTransport, LanguageModel, ChatOnDataCallback } from 'ai';
4
4
  import { ToolCall } from './chatTransport';
5
5
  import { OpenAssistantToolSet } from '@openassistant/utils';
6
- import { AddToolResult } from './types';
7
- type ExtendedChatOnToolCallCallback = (args: {
8
- toolCall: ToolCall;
9
- addToolResult?: AddToolResult;
10
- }) => Promise<void> | void;
6
+ import type { AddToolResult, AiChatSendMessage, GetProviderOptions } from './types';
11
7
  export type AiSliceState = {
12
8
  ai: {
13
9
  config: AiSliceConfig;
14
- analysisPrompt: string;
15
- isRunningAnalysis: boolean;
16
10
  promptSuggestionsVisible: boolean;
17
11
  tools: OpenAssistantToolSet;
18
- analysisAbortController?: AbortController;
12
+ getProviderOptions?: GetProviderOptions;
19
13
  setConfig: (config: AiSliceConfig) => void;
20
14
  setPromptSuggestionsVisible: (visible: boolean) => void;
21
- /** Latest stop function from useChat to immediately halt local streaming */
22
- chatStop?: () => void;
23
- /** Register/replace the current chat stop function */
24
- setChatStop: (stop: (() => void) | undefined) => void;
25
- /** Latest sendMessage function from useChat to send messages */
26
- chatSendMessage?: (message: {
27
- text: string;
28
- }) => void;
29
- /** Register/replace the current chat sendMessage function */
30
- setChatSendMessage: (sendMessage: ((message: {
31
- text: string;
32
- }) => void) | undefined) => void;
33
- /** Latest addToolResult function from useChat to add tool results */
34
- addToolResult?: AddToolResult;
35
- /** Register/replace the current addToolResult function */
36
- setAddToolResult: (addToolResult: AddToolResult | undefined) => void;
37
- /** Wait for a tool result to be added by UI component */
38
- waitForToolResult: (toolCallId: string, abortSignal?: AbortSignal) => Promise<void>;
39
- setAnalysisPrompt: (prompt: string) => void;
15
+ getAbortController: (sessionId: string) => AbortController | undefined;
16
+ setAbortController: (sessionId: string, controller: AbortController | undefined) => void;
17
+ setChatStop: (sessionId: string, stop: (() => void) | undefined) => void;
18
+ getChatStop: (sessionId: string) => (() => void) | undefined;
19
+ setChatSendMessage: (sessionId: string, sendMessage: AiChatSendMessage | undefined) => void;
20
+ getChatSendMessage: (sessionId: string) => AiChatSendMessage | undefined;
21
+ setAddToolResult: (sessionId: string, addToolResult: AddToolResult | undefined) => void;
22
+ getAddToolResult: (sessionId: string) => AddToolResult | undefined;
23
+ waitForToolResult: (sessionId: string, toolCallId: string, abortSignal?: AbortSignal) => Promise<void>;
24
+ /** Map toolCallId -> sessionId for long-running tool streams (e.g. agent tools) */
25
+ setToolCallSession: (toolCallId: string, sessionId: string | undefined) => void;
26
+ getToolCallSession: (toolCallId: string) => string | undefined;
27
+ setPrompt: (sessionId: string, prompt: string) => void;
28
+ getPrompt: (sessionId: string) => string;
29
+ setIsRunning: (sessionId: string, isRunning: boolean) => void;
30
+ getIsRunning: (sessionId: string) => boolean;
40
31
  addAnalysisResult: (message: UIMessage) => void;
41
32
  sendPrompt: (prompt: string, options?: {
42
33
  systemInstructions?: string;
@@ -46,10 +37,8 @@ export type AiSliceState = {
46
37
  abortSignal?: AbortSignal;
47
38
  useTools?: boolean;
48
39
  }) => Promise<string>;
49
- startAnalysis: (sendMessage: (message: {
50
- text: string;
51
- }) => void) => Promise<void>;
52
- cancelAnalysis: () => void;
40
+ startAnalysis: (sessionId: string) => Promise<void>;
41
+ cancelAnalysis: (sessionId: string) => void;
53
42
  setAiModel: (modelProvider: string, model: string) => void;
54
43
  createSession: (name?: string, modelProvider?: string, model?: string) => void;
55
44
  switchSession: (sessionId: string) => void;
@@ -66,19 +55,23 @@ export type AiSliceState = {
66
55
  getBaseUrlFromSettings: () => string | undefined;
67
56
  getMaxStepsFromSettings: () => number;
68
57
  getFullInstructions: () => string;
69
- getLocalChatTransport: () => DefaultChatTransport<UIMessage>;
58
+ getLocalChatTransport: (sessionId: string) => DefaultChatTransport<UIMessage>;
70
59
  /** Optional remote endpoint to use for chat; if empty, local transport is used */
71
60
  chatEndPoint: string;
72
61
  chatHeaders: Record<string, string>;
73
- getRemoteChatTransport: (endpoint: string, headers?: Record<string, string>) => DefaultChatTransport<UIMessage>;
74
- onChatToolCall: ExtendedChatOnToolCallCallback;
75
- onChatData: ChatOnDataCallback<UIMessage<unknown, UIDataTypes, UITools>>;
62
+ getRemoteChatTransport: (sessionId: string, endpoint: string, headers?: Record<string, string>) => DefaultChatTransport<UIMessage>;
76
63
  onChatFinish: (args: {
77
- message: UIMessage;
64
+ sessionId: string;
78
65
  messages: UIMessage[];
79
66
  isError?: boolean;
80
67
  }) => void;
81
- onChatError: (error: unknown) => void;
68
+ onChatToolCall: (args: {
69
+ sessionId: string;
70
+ toolCall: ToolCall;
71
+ addToolResult?: AddToolResult;
72
+ }) => Promise<void> | void;
73
+ onChatData: (sessionId: string, dataPart: Parameters<ChatOnDataCallback<UIMessage>>[0]) => void;
74
+ onChatError: (sessionId: string, error: unknown) => void;
82
75
  };
83
76
  };
84
77
  /**
@@ -86,19 +79,14 @@ export type AiSliceState = {
86
79
  */
87
80
  export interface AiSliceOptions {
88
81
  config?: Partial<AiSliceConfig>;
89
- /** Initial prompt to display in the analysis input */
90
- initialAnalysisPrompt?: string;
91
- /** Tools to add to the AI assistant */
82
+ initialPrompt?: string;
92
83
  tools: OpenAssistantToolSet;
93
- /**
94
- * Function to get custom instructions for the AI assistant
95
- * @returns The instructions string to use
96
- */
97
84
  getInstructions: () => string;
98
85
  defaultProvider?: string;
99
86
  defaultModel?: string;
100
87
  /** Provide a pre-configured model client for a provider (e.g., Azure). */
101
88
  getCustomModel?: () => LanguageModel | undefined;
89
+ getProviderOptions?: GetProviderOptions;
102
90
  maxSteps?: number;
103
91
  getApiKey?: (modelProvider: string) => string;
104
92
  getBaseUrl?: () => string;
@@ -109,5 +97,4 @@ export interface AiSliceOptions {
109
97
  }
110
98
  export declare function createAiSlice(params: AiSliceOptions): StateCreator<AiSliceState>;
111
99
  export declare function useStoreWithAi<T>(selector: (state: AiSliceState) => T): T;
112
- export {};
113
100
  //# sourceMappingURL=AiSlice.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiSlice.d.ts","sourceRoot":"","sources":["../src/AiSlice.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AiSlice.d.ts","sourceRoot":"","sources":["../src/AiSlice.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EAEtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,SAAS,EACT,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAEnB,MAAM,IAAI,CAAC;AACZ,OAAO,EAIL,QAAQ,EAET,MAAM,iBAAiB,CAAC;AAUzB,OAAO,EAAC,oBAAoB,EAAC,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAQjB,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE;QACF,MAAM,EAAE,aAAa,CAAC;QACtB,wBAAwB,EAAE,OAAO,CAAC;QAClC,KAAK,EAAE,oBAAoB,CAAC;QAC5B,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;QACxC,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;QAC3C,2BAA2B,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;QACxD,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;QACvE,kBAAkB,EAAE,CAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,eAAe,GAAG,SAAS,KACpC,IAAI,CAAC;QACV,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,KAAK,IAAI,CAAC;QACzE,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;QAC7D,kBAAkB,EAAE,CAClB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,iBAAiB,GAAG,SAAS,KACvC,IAAI,CAAC;QACV,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,iBAAiB,GAAG,SAAS,CAAC;QACzE,gBAAgB,EAAE,CAChB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,aAAa,GAAG,SAAS,KACrC,IAAI,CAAC;QACV,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;QACnE,iBAAiB,EAAE,CACjB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,WAAW,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;QACnB,mFAAmF;QACnF,kBAAkB,EAAE,CAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAAG,SAAS,KAC1B,IAAI,CAAC;QACV,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;QAC/D,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACvD,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;QACzC,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;QAC9D,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;QAC7C,iBAAiB,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,IAAI,CAAC;QAChD,UAAU,EAAE,CACV,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;YACR,kBAAkB,CAAC,EAAE,MAAM,CAAC;YAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;YACvB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,WAAW,CAAC,EAAE,WAAW,CAAC;YAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;SACpB,KACE,OAAO,CAAC,MAAM,CAAC,CAAC;QACrB,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QACpD,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5C,UAAU,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;QAC3D,aAAa,EAAE,CACb,IAAI,CAAC,EAAE,MAAM,EACb,aAAa,CAAC,EAAE,MAAM,EACtB,KAAK,CAAC,EAAE,MAAM,KACX,IAAI,CAAC;QACV,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;QAC3C,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QACzD,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;QAC3C,iBAAiB,EAAE,MAAM,qBAAqB,GAAG,SAAS,CAAC;QAC3D,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;QAC3E,4BAA4B,EAAE,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,OAAO,KACpB,IAAI,CAAC;QACV,kBAAkB,EAAE,MAAM,oBAAoB,EAAE,GAAG,SAAS,CAAC;QAC7D,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;QACpE,wBAAwB,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,SAAS,CAAC,OAAO,CAAC,CAAC;QAC3E,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,KAAK,CAAC,aAAa,GAAG,SAAS,CAAC;QACzE,qBAAqB,EAAE,MAAM,MAAM,CAAC;QACpC,sBAAsB,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;QACjD,uBAAuB,EAAE,MAAM,MAAM,CAAC;QACtC,mBAAmB,EAAE,MAAM,MAAM,CAAC;QAClC,qBAAqB,EAAE,CACrB,SAAS,EAAE,MAAM,KACd,oBAAoB,CAAC,SAAS,CAAC,CAAC;QACrC,kFAAkF;QAClF,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpC,sBAAsB,EAAE,CACtB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC7B,oBAAoB,CAAC,SAAS,CAAC,CAAC;QACrC,YAAY,EAAE,CAAC,IAAI,EAAE;YACnB,SAAS,EAAE,MAAM,CAAC;YAClB,QAAQ,EAAE,SAAS,EAAE,CAAC;YACtB,OAAO,CAAC,EAAE,OAAO,CAAC;SACnB,KAAK,IAAI,CAAC;QACX,cAAc,EAAE,CAAC,IAAI,EAAE;YACrB,SAAS,EAAE,MAAM,CAAC;YAClB,QAAQ,EAAE,QAAQ,CAAC;YACnB,aAAa,CAAC,EAAE,aAAa,CAAC;SAC/B,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAC3B,UAAU,EAAE,CACV,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,UAAU,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,KACnD,IAAI,CAAC;QACV,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;KAC1D,CAAC;CACH,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,oBAAoB,CAAC;IAC5B,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,aAAa,GAAG,SAAS,CAAC;IACjD,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1B,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,GACrB,YAAY,CAAC,YAAY,CAAC,CA+1B5B;AAYD,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,CAAC,GAAG,CAAC,CAEzE"}
package/dist/AiSlice.js CHANGED
@@ -3,13 +3,13 @@ import { createDefaultAiConfig, } from '@sqlrooms/ai-config';
3
3
  import { createSlice, useBaseRoomStore, } from '@sqlrooms/room-store';
4
4
  import { produce } from 'immer';
5
5
  import { generateText, } from 'ai';
6
- import { createChatHandlers, createLocalChatTransportFactory, createRemoteChatTransportFactory, convertToAiSDKTools, completeIncompleteToolCalls, } from './chatTransport';
7
- import { AI_DEFAULT_TEMPERATURE } from './constants';
6
+ import { createChatHandlers, createLocalChatTransportFactory, createRemoteChatTransportFactory, convertToAiSDKTools, } from './chatTransport';
7
+ import { ABORT_EVENT, AI_DEFAULT_TEMPERATURE, ANALYSIS_CANCELLED, ANALYSIS_PENDING_ID, SESSION_DELETED, TOOL_CALL_CANCELLED, } from './constants';
8
8
  import { hasAiSettingsConfig } from './hasAiSettingsConfig';
9
- import { cleanupPendingAnalysisResults } from './utils';
9
+ import { cleanupPendingAnalysisResults, ToolAbortError, fixIncompleteToolCalls, } from './utils';
10
10
  import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
11
11
  export function createAiSlice(params) {
12
- const { initialAnalysisPrompt = '', tools, getApiKey, getBaseUrl, maxSteps = 50, getInstructions, defaultProvider = 'openai', defaultModel = 'gpt-4.1', getCustomModel, chatEndPoint = '', chatHeaders = {}, } = params;
12
+ const { initialPrompt = '', tools, getApiKey, getBaseUrl, maxSteps = 50, getInstructions, defaultProvider = 'openai', defaultModel = 'gpt-4.1', getCustomModel, getProviderOptions, chatEndPoint = '', chatHeaders = {}, } = params;
13
13
  return createSlice((set, get, store) => {
14
14
  // Clean up pending analysis results from persisted config
15
15
  const cleanedConfig = params.config?.sessions
@@ -18,7 +18,7 @@ export function createAiSlice(params) {
18
18
  sessions: params.config.sessions.map((session) => {
19
19
  const cleaned = cleanupPendingAnalysisResults(session);
20
20
  const completedUiMessages = Array.isArray(cleaned.uiMessages)
21
- ? completeIncompleteToolCalls(cleaned.uiMessages || [])
21
+ ? fixIncompleteToolCalls(cleaned.uiMessages || [])
22
22
  : [];
23
23
  return {
24
24
  ...cleaned,
@@ -27,8 +27,13 @@ export function createAiSlice(params) {
27
27
  }),
28
28
  }
29
29
  : params.config;
30
- // Create a persistent Map for pending tool call resolvers (outside of immer draft)
30
+ // Create persistent Maps (outside of immer draft)
31
31
  const pendingToolCallResolvers = new Map();
32
+ const toolCallToSessionId = new Map();
33
+ const sessionAbortControllers = new Map();
34
+ const sessionChatStops = new Map();
35
+ const sessionChatSendMessages = new Map();
36
+ const sessionAddToolResults = new Map();
32
37
  // Initialize base config and ensure the initial session respects default provider/model
33
38
  const baseConfig = createDefaultAiConfig(cleanedConfig);
34
39
  if (!cleanedConfig?.sessions || cleanedConfig.sessions.length === 0) {
@@ -36,55 +41,124 @@ export function createAiSlice(params) {
36
41
  if (firstSession) {
37
42
  firstSession.modelProvider = defaultProvider;
38
43
  firstSession.model = defaultModel;
44
+ firstSession.prompt = initialPrompt;
45
+ firstSession.isRunning = false;
39
46
  }
40
47
  }
41
48
  return {
42
49
  ai: {
43
50
  config: baseConfig,
44
- analysisPrompt: initialAnalysisPrompt,
45
- isRunningAnalysis: false,
46
51
  promptSuggestionsVisible: true,
47
52
  tools,
48
- waitForToolResult: (toolCallId, abortSignal) => {
53
+ getProviderOptions,
54
+ waitForToolResult: (sessionId, toolCallId, abortSignal) => {
55
+ const key = `${sessionId}:${toolCallId}`;
49
56
  return new Promise((resolve, reject) => {
50
57
  // Set up abort handler
51
58
  const abortHandler = () => {
52
- const resolver = pendingToolCallResolvers.get(toolCallId);
59
+ const resolver = pendingToolCallResolvers.get(key);
53
60
  if (resolver) {
54
- pendingToolCallResolvers.delete(toolCallId);
55
- resolver.reject(new Error('Tool call cancelled by user'));
61
+ pendingToolCallResolvers.delete(key);
62
+ resolver.reject(new Error(TOOL_CALL_CANCELLED));
56
63
  }
57
64
  };
58
65
  if (abortSignal) {
59
66
  if (abortSignal.aborted) {
60
- reject(new Error('Tool call cancelled by user'));
67
+ reject(new Error(TOOL_CALL_CANCELLED));
61
68
  return;
62
69
  }
63
- abortSignal.addEventListener('abort', abortHandler, { once: true });
70
+ abortSignal.addEventListener(ABORT_EVENT, abortHandler, {
71
+ once: true,
72
+ });
64
73
  }
65
74
  // Store resolver (overwrites any existing one, which is fine for our use case)
66
- pendingToolCallResolvers.set(toolCallId, {
75
+ pendingToolCallResolvers.set(key, {
67
76
  resolve: () => {
68
77
  if (abortSignal) {
69
- abortSignal.removeEventListener('abort', abortHandler);
78
+ abortSignal.removeEventListener(ABORT_EVENT, abortHandler);
70
79
  }
71
- pendingToolCallResolvers.delete(toolCallId);
80
+ pendingToolCallResolvers.delete(key);
72
81
  resolve();
73
82
  },
74
83
  reject: (error) => {
75
84
  if (abortSignal) {
76
- abortSignal.removeEventListener('abort', abortHandler);
85
+ abortSignal.removeEventListener(ABORT_EVENT, abortHandler);
77
86
  }
78
- pendingToolCallResolvers.delete(toolCallId);
87
+ pendingToolCallResolvers.delete(key);
79
88
  reject(error);
80
89
  },
81
90
  });
82
91
  });
83
92
  },
84
- setChatStop: (stopFn) => {
85
- set((state) => produce(state, (draft) => {
86
- draft.ai.chatStop = stopFn;
87
- }));
93
+ setToolCallSession: (toolCallId, sessionId) => {
94
+ if (!toolCallId)
95
+ return;
96
+ if (sessionId) {
97
+ toolCallToSessionId.set(toolCallId, sessionId);
98
+ }
99
+ else {
100
+ toolCallToSessionId.delete(toolCallId);
101
+ }
102
+ },
103
+ getToolCallSession: (toolCallId) => {
104
+ if (!toolCallId)
105
+ return undefined;
106
+ return toolCallToSessionId.get(toolCallId);
107
+ },
108
+ getAbortController: (sessionId) => {
109
+ return sessionAbortControllers.get(sessionId);
110
+ },
111
+ setAbortController: (sessionId, controller) => {
112
+ if (controller) {
113
+ sessionAbortControllers.set(sessionId, controller);
114
+ }
115
+ else {
116
+ sessionAbortControllers.delete(sessionId);
117
+ }
118
+ },
119
+ setChatStop: (sessionId, stopFn) => {
120
+ if (stopFn) {
121
+ sessionChatStops.set(sessionId, stopFn);
122
+ }
123
+ else {
124
+ sessionChatStops.delete(sessionId);
125
+ }
126
+ },
127
+ getChatStop: (sessionId) => {
128
+ return sessionChatStops.get(sessionId);
129
+ },
130
+ setChatSendMessage: (sessionId, sendMessageFn) => {
131
+ if (sendMessageFn) {
132
+ sessionChatSendMessages.set(sessionId, sendMessageFn);
133
+ }
134
+ else {
135
+ sessionChatSendMessages.delete(sessionId);
136
+ }
137
+ },
138
+ getChatSendMessage: (sessionId) => {
139
+ return sessionChatSendMessages.get(sessionId);
140
+ },
141
+ setAddToolResult: (sessionId, addToolResultFn) => {
142
+ if (addToolResultFn) {
143
+ // Wrap addToolResult to intercept calls and resolve pending promises
144
+ const wrappedAddToolResult = (options) => {
145
+ addToolResultFn(options);
146
+ const key = `${sessionId}:${options.toolCallId}`;
147
+ const resolver = pendingToolCallResolvers.get(key);
148
+ if (resolver) {
149
+ resolver.resolve();
150
+ }
151
+ // Tool is complete (success or error), we can drop toolCall->session mapping.
152
+ toolCallToSessionId.delete(options.toolCallId);
153
+ };
154
+ sessionAddToolResults.set(sessionId, wrappedAddToolResult);
155
+ }
156
+ else {
157
+ sessionAddToolResults.delete(sessionId);
158
+ }
159
+ },
160
+ getAddToolResult: (sessionId) => {
161
+ return sessionAddToolResults.get(sessionId);
88
162
  },
89
163
  setConfig: (config) => {
90
164
  set((state) => produce(state, (draft) => {
@@ -96,33 +170,32 @@ export function createAiSlice(params) {
96
170
  draft.ai.promptSuggestionsVisible = visible;
97
171
  }));
98
172
  },
99
- setChatSendMessage: (sendMessageFn) => {
173
+ setPrompt: (sessionId, prompt) => {
100
174
  set((state) => produce(state, (draft) => {
101
- draft.ai.chatSendMessage = sendMessageFn;
102
- }));
103
- },
104
- setAddToolResult: (addToolResultFn) => {
105
- // Wrap addToolResult to intercept calls and resolve pending promises
106
- const wrappedAddToolResult = addToolResultFn
107
- ? (options) => {
108
- // Call the original addToolResult
109
- addToolResultFn(options);
110
- // Resolve the promise if there's a pending waiter for this toolCallId
111
- const resolver = pendingToolCallResolvers.get(options.toolCallId);
112
- if (resolver) {
113
- resolver.resolve();
114
- }
175
+ const session = draft.ai.config.sessions.find((s) => s.id === sessionId);
176
+ if (session) {
177
+ session.prompt = prompt;
115
178
  }
116
- : undefined;
117
- set((state) => produce(state, (draft) => {
118
- draft.ai.addToolResult = wrappedAddToolResult;
119
179
  }));
120
180
  },
121
- setAnalysisPrompt: (prompt) => {
181
+ getPrompt: (sessionId) => {
182
+ const state = get();
183
+ const session = state.ai.config.sessions.find((s) => s.id === sessionId);
184
+ return session?.prompt || '';
185
+ },
186
+ setIsRunning: (sessionId, isRunning) => {
122
187
  set((state) => produce(state, (draft) => {
123
- draft.ai.analysisPrompt = prompt;
188
+ const session = draft.ai.config.sessions.find((s) => s.id === sessionId);
189
+ if (session) {
190
+ session.isRunning = isRunning;
191
+ }
124
192
  }));
125
193
  },
194
+ getIsRunning: (sessionId) => {
195
+ const state = get();
196
+ const session = state.ai.config.sessions.find((s) => s.id === sessionId);
197
+ return session?.isRunning || false;
198
+ },
126
199
  /**
127
200
  * Set the AI model for the current session
128
201
  * @param model - The model to set
@@ -168,7 +241,7 @@ export function createAiSlice(params) {
168
241
  sessionName = `Session ${formattedDate} at ${formattedTime}`;
169
242
  }
170
243
  set((state) => produce(state, (draft) => {
171
- // Add to AI sessions
244
+ // Add to AI sessions with per-session state
172
245
  draft.ai.config.sessions.unshift({
173
246
  id: newSessionId,
174
247
  name: sessionName,
@@ -181,6 +254,8 @@ export function createAiSlice(params) {
181
254
  uiMessages: [],
182
255
  toolAdditionalData: {},
183
256
  messagesRevision: 0,
257
+ prompt: '',
258
+ isRunning: false,
184
259
  });
185
260
  draft.ai.config.currentSessionId = newSessionId;
186
261
  }));
@@ -205,9 +280,18 @@ export function createAiSlice(params) {
205
280
  }));
206
281
  },
207
282
  /**
208
- * Delete a session
283
+ * Delete a session and clean up its resources
209
284
  */
210
285
  deleteSession: (sessionId) => {
286
+ // Clean up per-session state
287
+ const abortController = sessionAbortControllers.get(sessionId);
288
+ if (abortController) {
289
+ abortController.abort(SESSION_DELETED);
290
+ }
291
+ sessionAbortControllers.delete(sessionId);
292
+ sessionChatStops.delete(sessionId);
293
+ sessionChatSendMessages.delete(sessionId);
294
+ sessionAddToolResults.delete(sessionId);
211
295
  set((state) => produce(state, (draft) => {
212
296
  const sessionIndex = draft.ai.config.sessions.findIndex((s) => s.id === sessionId);
213
297
  if (sessionIndex !== -1) {
@@ -333,15 +417,15 @@ export function createAiSlice(params) {
333
417
  sendPrompt: async (prompt, options = {}) => {
334
418
  // One-shot generateText path with explicit abort lifecycle management
335
419
  const state = get();
336
- const currentSession = state.ai.getCurrentSession();
420
+ const currentSession = state.ai.getCurrentSession(); // only used when no model provider is provided
337
421
  const { systemInstructions, modelProvider, modelName, baseUrl, abortSignal, useTools = false, } = options;
422
+ if (abortSignal?.aborted) {
423
+ throw new ToolAbortError(TOOL_CALL_CANCELLED);
424
+ }
338
425
  const provider = modelProvider || currentSession?.modelProvider || defaultProvider;
339
426
  const modelId = modelName || currentSession?.model || defaultModel;
340
- const baseURL = baseUrl ||
341
- state.ai.getBaseUrlFromSettings() ||
342
- 'https://api.openai.com/v1';
427
+ const baseURL = baseUrl ?? state.ai.getBaseUrlFromSettings() ?? '';
343
428
  const tools = state.ai.tools;
344
- // remove execute from tools
345
429
  const toolsWithoutExecute = Object.fromEntries(Object.entries(tools).filter(([, tool]) => !tool.execute));
346
430
  const model = createOpenAICompatible({
347
431
  apiKey: state.ai.getApiKeyFromSettings(),
@@ -362,63 +446,69 @@ export function createAiSlice(params) {
362
446
  return response.text;
363
447
  }
364
448
  catch (error) {
449
+ const errorName = typeof error === 'object' && error && 'name' in error
450
+ ? String(error.name)
451
+ : '';
452
+ if (abortSignal?.aborted || errorName === 'AbortError') {
453
+ throw new ToolAbortError(TOOL_CALL_CANCELLED);
454
+ }
365
455
  console.error('Error generating text:', error);
366
456
  return 'error: can not generate response';
367
457
  }
368
458
  },
369
459
  /**
370
- * Start the analysis
460
+ * Start the analysis for a specific session
371
461
  */
372
- startAnalysis: async (sendMessage) => {
373
- const abortController = new AbortController();
374
- const currentSession = get().ai.getCurrentSession();
375
- if (!currentSession) {
376
- console.error('No current session found');
462
+ startAnalysis: async (sessionId) => {
463
+ const state = get();
464
+ const session = state.ai.config.sessions.find((s) => s.id === sessionId);
465
+ if (!session) {
466
+ console.error('Session not found:', sessionId);
377
467
  return;
378
468
  }
379
- const promptText = get().ai.analysisPrompt;
380
- set((state) => produce(state, (draft) => {
381
- draft.ai.analysisAbortController = abortController;
382
- draft.ai.isRunningAnalysis = true;
383
- draft.ai.analysisPrompt = '';
384
- draft.ai.promptSuggestionsVisible = false;
385
- // Add incomplete analysis result to session immediately for instant UI rendering
386
- const session = draft.ai.config.sessions.find((s) => s.id === currentSession.id);
387
- if (session) {
388
- // Remove any existing pending results (safety check for page refresh scenarios)
389
- session.analysisResults = session.analysisResults.filter((result) => result.id !== '__pending__');
469
+ const sendMessage = state.ai.getChatSendMessage(sessionId);
470
+ if (!sendMessage) {
471
+ console.error('No sendMessage function found for session:', sessionId);
472
+ return;
473
+ }
474
+ const abortController = new AbortController();
475
+ const promptText = session.prompt || '';
476
+ // Store abort controller for this session
477
+ state.ai.setAbortController(sessionId, abortController);
478
+ set((stateToUpdate) => produce(stateToUpdate, (draft) => {
479
+ const draftSession = draft.ai.config.sessions.find((s) => s.id === sessionId);
480
+ if (draftSession) {
481
+ draftSession.isRunning = true;
482
+ draftSession.prompt = '';
483
+ draft.ai.promptSuggestionsVisible = false;
484
+ // Remove any existing pending results
485
+ draftSession.analysisResults =
486
+ draftSession.analysisResults.filter((result) => result.id !== ANALYSIS_PENDING_ID);
390
487
  // Add incomplete analysis result with a temporary ID
391
- // This will be updated in onChatFinish with the actual user message ID
392
- session.analysisResults.push({
393
- id: '__pending__',
488
+ draftSession.analysisResults.push({
489
+ id: ANALYSIS_PENDING_ID,
394
490
  prompt: promptText,
395
491
  isCompleted: false,
396
492
  });
397
493
  }
398
494
  }));
399
- // The pending analysis result will be updated in onChatFinish with the correct message ID
495
+ // Send the message through the session's chat instance
400
496
  sendMessage({ text: promptText });
401
497
  },
402
- cancelAnalysis: () => {
403
- const abortController = get().ai.analysisAbortController;
498
+ cancelAnalysis: (sessionId) => {
499
+ const state = get();
500
+ const abortController = state.ai.getAbortController(sessionId);
501
+ const stopFn = state.ai.getChatStop(sessionId);
404
502
  // Stop local chat streaming immediately if available
405
- try {
406
- get().ai.chatStop?.();
407
- }
408
- catch {
409
- // no-op
410
- }
411
- // Call abort to signal cancellation
412
- // Keep the abort controller in state so that async handlers (onChatToolCall, onChatFinish)
413
- // can check if it was aborted. The onChatFinish handler will clean it up.
414
- abortController?.abort('Analysis cancelled');
415
- set((state) => produce(state, (draft) => {
416
- // Set isRunningAnalysis to false to update UI
417
- draft.ai.isRunningAnalysis = false;
418
- // Keep analysisAbortController so handlers can check signal.aborted
503
+ stopFn?.();
504
+ abortController?.abort(ANALYSIS_CANCELLED);
505
+ set((stateToUpdate) => produce(stateToUpdate, (draft) => {
506
+ const session = draft.ai.config.sessions.find((s) => s.id === sessionId);
507
+ if (session) {
508
+ session.isRunning = false;
509
+ }
510
+ // Keep abort controller so handlers can check signal.aborted
419
511
  // It will be cleared by onChatFinish
420
- // Intentionally preserve any pending analysis result so the
421
- // conversation row remains visible until onChatFinish runs.
422
512
  }));
423
513
  },
424
514
  /**
@@ -539,7 +629,7 @@ export function createAiSlice(params) {
539
629
  // Chat transport configuration
540
630
  chatEndPoint,
541
631
  chatHeaders,
542
- getLocalChatTransport: () => {
632
+ getLocalChatTransport: (sessionId) => {
543
633
  const state = get();
544
634
  return createLocalChatTransportFactory({
545
635
  store,
@@ -549,12 +639,14 @@ export function createAiSlice(params) {
549
639
  baseUrl: state.ai.getBaseUrlFromSettings(),
550
640
  getInstructions: () => store.getState().ai.getFullInstructions(),
551
641
  getCustomModel,
642
+ sessionId,
552
643
  })();
553
644
  },
554
- getRemoteChatTransport: (endpoint, headers) => createRemoteChatTransportFactory({
645
+ getRemoteChatTransport: (sessionId, endpoint, headers) => createRemoteChatTransportFactory({
555
646
  store,
556
647
  defaultProvider,
557
648
  defaultModel,
649
+ sessionId,
558
650
  })(endpoint, headers),
559
651
  ...createChatHandlers({ store }),
560
652
  },