@datalayer/agent-runtimes 0.0.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.
- package/LICENSE +32 -0
- package/README.md +56 -0
- package/lib/App.css +47 -0
- package/lib/App.d.ts +3 -0
- package/lib/App.js +18 -0
- package/lib/components/chat/components/AgentDetails.d.ts +19 -0
- package/lib/components/chat/components/AgentDetails.js +170 -0
- package/lib/components/chat/components/Chat.d.ts +105 -0
- package/lib/components/chat/components/Chat.js +275 -0
- package/lib/components/chat/components/ChatFloating.d.ts +146 -0
- package/lib/components/chat/components/ChatFloating.js +381 -0
- package/lib/components/chat/components/ChatInline.d.ts +42 -0
- package/lib/components/chat/components/ChatInline.js +327 -0
- package/lib/components/chat/components/ChatPopupStandalone.d.ts +110 -0
- package/lib/components/chat/components/ChatPopupStandalone.js +422 -0
- package/lib/components/chat/components/ChatSidebar.d.ts +78 -0
- package/lib/components/chat/components/ChatSidebar.js +224 -0
- package/lib/components/chat/components/ChatStandalone.d.ts +105 -0
- package/lib/components/chat/components/ChatStandalone.js +320 -0
- package/lib/components/chat/components/base/ChatBase.d.ts +285 -0
- package/lib/components/chat/components/base/ChatBase.js +1192 -0
- package/lib/components/chat/components/display/ToolCallDisplay.d.ts +26 -0
- package/lib/components/chat/components/display/ToolCallDisplay.js +225 -0
- package/lib/components/chat/components/display/index.d.ts +6 -0
- package/lib/components/chat/components/display/index.js +10 -0
- package/lib/components/chat/components/elements/ChatHeader.d.ts +43 -0
- package/lib/components/chat/components/elements/ChatHeader.js +67 -0
- package/lib/components/chat/components/elements/ChatInputPrompt.d.ts +26 -0
- package/lib/components/chat/components/elements/ChatInputPrompt.js +145 -0
- package/lib/components/chat/components/elements/ChatMessages.d.ts +36 -0
- package/lib/components/chat/components/elements/ChatMessages.js +195 -0
- package/lib/components/chat/components/elements/FloatingBrandButton.d.ts +34 -0
- package/lib/components/chat/components/elements/FloatingBrandButton.js +88 -0
- package/lib/components/chat/components/elements/MessagePart.d.ts +50 -0
- package/lib/components/chat/components/elements/MessagePart.js +48 -0
- package/lib/components/chat/components/elements/PoweredByTag.d.ts +26 -0
- package/lib/components/chat/components/elements/PoweredByTag.js +33 -0
- package/lib/components/chat/components/elements/ToolApprovalDialog.d.ts +42 -0
- package/lib/components/chat/components/elements/ToolApprovalDialog.js +96 -0
- package/lib/components/chat/components/index.d.ts +21 -0
- package/lib/components/chat/components/index.js +34 -0
- package/lib/components/chat/components/parts/DynamicToolPart.d.ts +20 -0
- package/lib/components/chat/components/parts/DynamicToolPart.js +13 -0
- package/lib/components/chat/components/parts/ReasoningPart.d.ts +17 -0
- package/lib/components/chat/components/parts/ReasoningPart.js +69 -0
- package/lib/components/chat/components/parts/TextPart.d.ts +28 -0
- package/lib/components/chat/components/parts/TextPart.js +103 -0
- package/lib/components/chat/components/parts/ToolPart.d.ts +17 -0
- package/lib/components/chat/components/parts/ToolPart.js +163 -0
- package/lib/components/chat/components/parts/index.d.ts +9 -0
- package/lib/components/chat/components/parts/index.js +13 -0
- package/lib/components/chat/extensions/A2UIExtension.d.ts +87 -0
- package/lib/components/chat/extensions/A2UIExtension.js +312 -0
- package/lib/components/chat/extensions/ExtensionRegistry.d.ts +66 -0
- package/lib/components/chat/extensions/ExtensionRegistry.js +128 -0
- package/lib/components/chat/extensions/MCPUIExtension.d.ts +77 -0
- package/lib/components/chat/extensions/MCPUIExtension.js +212 -0
- package/lib/components/chat/extensions/index.d.ts +9 -0
- package/lib/components/chat/extensions/index.js +12 -0
- package/lib/components/chat/handler.d.ts +20 -0
- package/lib/components/chat/handler.js +56 -0
- package/lib/components/chat/index.d.ts +61 -0
- package/lib/components/chat/index.js +76 -0
- package/lib/components/chat/inference/BaseInferenceProvider.d.ts +57 -0
- package/lib/components/chat/inference/BaseInferenceProvider.js +69 -0
- package/lib/components/chat/inference/DatalayerInferenceProvider.d.ts +83 -0
- package/lib/components/chat/inference/DatalayerInferenceProvider.js +305 -0
- package/lib/components/chat/inference/SelfHostedInferenceProvider.d.ts +54 -0
- package/lib/components/chat/inference/SelfHostedInferenceProvider.js +246 -0
- package/lib/components/chat/inference/index.d.ts +9 -0
- package/lib/components/chat/inference/index.js +12 -0
- package/lib/components/chat/middleware/MiddlewarePipeline.d.ts +118 -0
- package/lib/components/chat/middleware/MiddlewarePipeline.js +255 -0
- package/lib/components/chat/middleware/index.d.ts +7 -0
- package/lib/components/chat/middleware/index.js +10 -0
- package/lib/components/chat/protocols/A2AAdapter.d.ts +79 -0
- package/lib/components/chat/protocols/A2AAdapter.js +388 -0
- package/lib/components/chat/protocols/ACPAdapter.d.ts +161 -0
- package/lib/components/chat/protocols/ACPAdapter.js +504 -0
- package/lib/components/chat/protocols/AGUIAdapter.d.ts +82 -0
- package/lib/components/chat/protocols/AGUIAdapter.js +518 -0
- package/lib/components/chat/protocols/BaseProtocolAdapter.d.ts +75 -0
- package/lib/components/chat/protocols/BaseProtocolAdapter.js +119 -0
- package/lib/components/chat/protocols/VercelAIAdapter.d.ts +77 -0
- package/lib/components/chat/protocols/VercelAIAdapter.js +252 -0
- package/lib/components/chat/protocols/index.d.ts +11 -0
- package/lib/components/chat/protocols/index.js +14 -0
- package/lib/components/chat/store/chatStore.d.ts +158 -0
- package/lib/components/chat/store/chatStore.js +313 -0
- package/lib/components/chat/store/index.d.ts +6 -0
- package/lib/components/chat/store/index.js +10 -0
- package/lib/components/chat/tools/ToolExecutor.d.ts +49 -0
- package/lib/components/chat/tools/ToolExecutor.js +151 -0
- package/lib/components/chat/tools/index.d.ts +8 -0
- package/lib/components/chat/tools/index.js +11 -0
- package/lib/components/chat/types/extension.d.ts +181 -0
- package/lib/components/chat/types/extension.js +46 -0
- package/lib/components/chat/types/index.d.ts +11 -0
- package/lib/components/chat/types/index.js +16 -0
- package/lib/components/chat/types/inference.d.ts +117 -0
- package/lib/components/chat/types/inference.js +5 -0
- package/lib/components/chat/types/message.d.ts +117 -0
- package/lib/components/chat/types/message.js +50 -0
- package/lib/components/chat/types/middleware.d.ts +153 -0
- package/lib/components/chat/types/middleware.js +32 -0
- package/lib/components/chat/types/protocol.d.ts +209 -0
- package/lib/components/chat/types/protocol.js +5 -0
- package/lib/components/chat/types/tool.d.ts +140 -0
- package/lib/components/chat/types/tool.js +23 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +6 -0
- package/lib/components/sparklines/Sparklines.d.ts +16 -0
- package/lib/components/sparklines/Sparklines.js +65 -0
- package/lib/components/sparklines/SparklinesLine.d.ts +8 -0
- package/lib/components/sparklines/SparklinesLine.js +37 -0
- package/lib/components/sparklines/dataProcessing.d.ts +25 -0
- package/lib/components/sparklines/dataProcessing.js +35 -0
- package/lib/components/sparklines/index.d.ts +4 -0
- package/lib/components/sparklines/index.js +7 -0
- package/lib/components/sparklines/types.d.ts +36 -0
- package/lib/components/sparklines/types.js +5 -0
- package/lib/components/ui/accordion.d.ts +7 -0
- package/lib/components/ui/accordion.js +22 -0
- package/lib/components/ui/alert-dialog.d.ts +14 -0
- package/lib/components/ui/alert-dialog.js +43 -0
- package/lib/components/ui/alert.d.ts +9 -0
- package/lib/components/ui/alert.js +24 -0
- package/lib/components/ui/aspect-ratio.d.ts +3 -0
- package/lib/components/ui/aspect-ratio.js +11 -0
- package/lib/components/ui/avatar.d.ts +6 -0
- package/lib/components/ui/avatar.js +18 -0
- package/lib/components/ui/badge.d.ts +9 -0
- package/lib/components/ui/badge.js +22 -0
- package/lib/components/ui/breadcrumb.d.ts +11 -0
- package/lib/components/ui/breadcrumb.js +27 -0
- package/lib/components/ui/button-group.d.ts +11 -0
- package/lib/components/ui/button-group.js +31 -0
- package/lib/components/ui/button.d.ts +13 -0
- package/lib/components/ui/button.js +39 -0
- package/lib/components/ui/calendar.d.ts +8 -0
- package/lib/components/ui/calendar.js +80 -0
- package/lib/components/ui/card.d.ts +9 -0
- package/lib/components/ui/card.js +24 -0
- package/lib/components/ui/carousel.d.ts +19 -0
- package/lib/components/ui/carousel.js +95 -0
- package/lib/components/ui/chart.d.ts +53 -0
- package/lib/components/ui/chart.js +136 -0
- package/lib/components/ui/checkbox.d.ts +4 -0
- package/lib/components/ui/checkbox.js +13 -0
- package/lib/components/ui/collapsible.d.ts +5 -0
- package/lib/components/ui/collapsible.js +17 -0
- package/lib/components/ui/command.d.ts +18 -0
- package/lib/components/ui/command.js +38 -0
- package/lib/components/ui/context-menu.d.ts +25 -0
- package/lib/components/ui/context-menu.js +55 -0
- package/lib/components/ui/dialog.d.ts +15 -0
- package/lib/components/ui/dialog.js +40 -0
- package/lib/components/ui/drawer.d.ts +13 -0
- package/lib/components/ui/drawer.js +39 -0
- package/lib/components/ui/dropdown-menu.d.ts +25 -0
- package/lib/components/ui/dropdown-menu.js +55 -0
- package/lib/components/ui/empty.d.ts +11 -0
- package/lib/components/ui/empty.js +37 -0
- package/lib/components/ui/field.d.ts +24 -0
- package/lib/components/ui/field.js +80 -0
- package/lib/components/ui/form.d.ts +24 -0
- package/lib/components/ui/form.js +63 -0
- package/lib/components/ui/hover-card.d.ts +6 -0
- package/lib/components/ui/hover-card.js +18 -0
- package/lib/components/ui/input-group.d.ts +19 -0
- package/lib/components/ui/input-group.js +69 -0
- package/lib/components/ui/input-otp.d.ts +11 -0
- package/lib/components/ui/input-otp.js +25 -0
- package/lib/components/ui/input.d.ts +3 -0
- package/lib/components/ui/input.js +6 -0
- package/lib/components/ui/item.d.ts +23 -0
- package/lib/components/ui/item.js +66 -0
- package/lib/components/ui/kbd.d.ts +3 -0
- package/lib/components/ui/kbd.js +13 -0
- package/lib/components/ui/label.d.ts +4 -0
- package/lib/components/ui/label.js +12 -0
- package/lib/components/ui/menubar.d.ts +26 -0
- package/lib/components/ui/menubar.js +58 -0
- package/lib/components/ui/navigation-menu.d.ts +14 -0
- package/lib/components/ui/navigation-menu.js +31 -0
- package/lib/components/ui/pagination.d.ts +13 -0
- package/lib/components/ui/pagination.js +29 -0
- package/lib/components/ui/popover.d.ts +7 -0
- package/lib/components/ui/popover.js +21 -0
- package/lib/components/ui/progress.d.ts +4 -0
- package/lib/components/ui/progress.js +12 -0
- package/lib/components/ui/radio-group.d.ts +5 -0
- package/lib/components/ui/radio-group.js +16 -0
- package/lib/components/ui/resizable.d.ts +8 -0
- package/lib/components/ui/resizable.js +19 -0
- package/lib/components/ui/scroll-area.d.ts +5 -0
- package/lib/components/ui/scroll-area.js +17 -0
- package/lib/components/ui/select.d.ts +15 -0
- package/lib/components/ui/select.js +42 -0
- package/lib/components/ui/separator.d.ts +4 -0
- package/lib/components/ui/separator.js +12 -0
- package/lib/components/ui/sheet.d.ts +13 -0
- package/lib/components/ui/sheet.js +44 -0
- package/lib/components/ui/sidebar.d.ts +69 -0
- package/lib/components/ui/sidebar.js +216 -0
- package/lib/components/ui/skeleton.d.ts +2 -0
- package/lib/components/ui/skeleton.js +10 -0
- package/lib/components/ui/slider.d.ts +4 -0
- package/lib/components/ui/slider.js +18 -0
- package/lib/components/ui/sonner.d.ts +3 -0
- package/lib/components/ui/sonner.js +25 -0
- package/lib/components/ui/spinner.d.ts +2 -0
- package/lib/components/ui/spinner.js +11 -0
- package/lib/components/ui/switch.d.ts +4 -0
- package/lib/components/ui/switch.js +12 -0
- package/lib/components/ui/table.d.ts +10 -0
- package/lib/components/ui/table.js +32 -0
- package/lib/components/ui/tabs.d.ts +7 -0
- package/lib/components/ui/tabs.js +21 -0
- package/lib/components/ui/textarea.d.ts +3 -0
- package/lib/components/ui/textarea.js +6 -0
- package/lib/components/ui/toast.d.ts +15 -0
- package/lib/components/ui/toast.js +38 -0
- package/lib/components/ui/toaster.d.ts +1 -0
- package/lib/components/ui/toaster.js +14 -0
- package/lib/components/ui/toggle-group.d.ts +9 -0
- package/lib/components/ui/toggle-group.js +26 -0
- package/lib/components/ui/toggle.d.ts +9 -0
- package/lib/components/ui/toggle.js +30 -0
- package/lib/components/ui/tooltip.d.ts +7 -0
- package/lib/components/ui/tooltip.js +21 -0
- package/lib/components/vercel-ai-elements/artifact.d.ts +23 -0
- package/lib/components/vercel-ai-elements/artifact.js +24 -0
- package/lib/components/vercel-ai-elements/code-block.d.ts +17 -0
- package/lib/components/vercel-ai-elements/code-block.js +94 -0
- package/lib/components/vercel-ai-elements/conversation.d.ts +15 -0
- package/lib/components/vercel-ai-elements/conversation.js +21 -0
- package/lib/components/vercel-ai-elements/loader.d.ts +5 -0
- package/lib/components/vercel-ai-elements/loader.js +8 -0
- package/lib/components/vercel-ai-elements/message.d.ts +46 -0
- package/lib/components/vercel-ai-elements/message.js +109 -0
- package/lib/components/vercel-ai-elements/model-selector.d.ts +35 -0
- package/lib/components/vercel-ai-elements/model-selector.js +22 -0
- package/lib/components/vercel-ai-elements/prompt-input.d.ts +195 -0
- package/lib/components/vercel-ai-elements/prompt-input.js +589 -0
- package/lib/components/vercel-ai-elements/reasoning.d.ts +26 -0
- package/lib/components/vercel-ai-elements/reasoning.js +80 -0
- package/lib/components/vercel-ai-elements/shimmer.d.ts +9 -0
- package/lib/components/vercel-ai-elements/shimmer.js +22 -0
- package/lib/components/vercel-ai-elements/sources.d.ts +12 -0
- package/lib/components/vercel-ai-elements/sources.js +13 -0
- package/lib/components/vercel-ai-elements/suggestion.d.ts +10 -0
- package/lib/components/vercel-ai-elements/suggestion.js +16 -0
- package/lib/components/vercel-ai-elements/tool.d.ts +23 -0
- package/lib/components/vercel-ai-elements/tool.js +52 -0
- package/lib/examples/A2UiRestaurantExample.d.ts +25 -0
- package/lib/examples/A2UiRestaurantExample.js +305 -0
- package/lib/examples/AgUiAgenticExample.d.ts +25 -0
- package/lib/examples/AgUiAgenticExample.js +63 -0
- package/lib/examples/AgUiBackendToolRenderingExample.d.ts +30 -0
- package/lib/examples/AgUiBackendToolRenderingExample.js +103 -0
- package/lib/examples/AgUiHaikuGenUIExample.d.ts +44 -0
- package/lib/examples/AgUiHaikuGenUIExample.js +151 -0
- package/lib/examples/AgUiHumanInTheLoopExample.d.ts +26 -0
- package/lib/examples/AgUiHumanInTheLoopExample.js +220 -0
- package/lib/examples/AgUiSharedStateExample.d.ts +25 -0
- package/lib/examples/AgUiSharedStateExample.js +181 -0
- package/lib/examples/AgUiToolsBasedGenUIExample.d.ts +25 -0
- package/lib/examples/AgUiToolsBasedGenUIExample.js +257 -0
- package/lib/examples/AgentRuntimeCustomExample.d.ts +9 -0
- package/lib/examples/AgentRuntimeCustomExample.js +68 -0
- package/lib/examples/AgentRuntimeLexical2Example.d.ts +42 -0
- package/lib/examples/AgentRuntimeLexical2Example.js +236 -0
- package/lib/examples/AgentRuntimeLexicalExample.d.ts +36 -0
- package/lib/examples/AgentRuntimeLexicalExample.js +260 -0
- package/lib/examples/AgentRuntimeLexicalSidebarExample.d.ts +41 -0
- package/lib/examples/AgentRuntimeLexicalSidebarExample.js +166 -0
- package/lib/examples/AgentRuntimeNotebookExample.d.ts +9 -0
- package/lib/examples/AgentRuntimeNotebookExample.js +148 -0
- package/lib/examples/AgentRuntimeNotebookSidebarExample.d.ts +13 -0
- package/lib/examples/AgentRuntimeNotebookSidebarExample.js +121 -0
- package/lib/examples/AgentRuntimeStandaloneExample.d.ts +21 -0
- package/lib/examples/AgentRuntimeStandaloneExample.js +158 -0
- package/lib/examples/AgentSpaceFormExample.d.ts +22 -0
- package/lib/examples/AgentSpaceFormExample.js +296 -0
- package/lib/examples/AgentSpaceHomeExample.d.ts +8 -0
- package/lib/examples/AgentSpaceHomeExample.js +171 -0
- package/lib/examples/CopilotKitLexicalExample.d.ts +38 -0
- package/lib/examples/CopilotKitLexicalExample.js +161 -0
- package/lib/examples/CopilotKitNotebookExample.d.ts +11 -0
- package/lib/examples/CopilotKitNotebookExample.js +70 -0
- package/lib/examples/DatalayerNotebookExample.d.ts +16 -0
- package/lib/examples/DatalayerNotebookExample.js +99 -0
- package/lib/examples/JupyterCellExample.d.ts +6 -0
- package/lib/examples/JupyterCellExample.js +19 -0
- package/lib/examples/JupyterNotebookExample.d.ts +6 -0
- package/lib/examples/JupyterNotebookExample.js +21 -0
- package/lib/examples/ag-ui/haiku/HaikuDisplay.d.ts +18 -0
- package/lib/examples/ag-ui/haiku/HaikuDisplay.js +110 -0
- package/lib/examples/ag-ui/haiku/InlineHaikuCard.d.ts +39 -0
- package/lib/examples/ag-ui/haiku/InlineHaikuCard.js +117 -0
- package/lib/examples/ag-ui/haiku/index.d.ts +11 -0
- package/lib/examples/ag-ui/haiku/index.js +15 -0
- package/lib/examples/ag-ui/index.d.ts +10 -0
- package/lib/examples/ag-ui/index.js +16 -0
- package/lib/examples/ag-ui/weather/InlineWeatherCard.d.ts +43 -0
- package/lib/examples/ag-ui/weather/InlineWeatherCard.js +180 -0
- package/lib/examples/ag-ui/weather/index.d.ts +9 -0
- package/lib/examples/ag-ui/weather/index.js +13 -0
- package/lib/examples/components/AgentConfiguration.d.ts +50 -0
- package/lib/examples/components/AgentConfiguration.js +115 -0
- package/lib/examples/components/AgentsDataTable.d.ts +13 -0
- package/lib/examples/components/AgentsDataTable.js +74 -0
- package/lib/examples/components/FooterMetrics.d.ts +12 -0
- package/lib/examples/components/FooterMetrics.js +17 -0
- package/lib/examples/components/Header.d.ts +27 -0
- package/lib/examples/components/Header.js +294 -0
- package/lib/examples/components/HeaderControls.d.ts +11 -0
- package/lib/examples/components/HeaderControls.js +24 -0
- package/lib/examples/components/LexicalEditor.d.ts +27 -0
- package/lib/examples/components/LexicalEditor.js +118 -0
- package/lib/examples/components/MainContent.d.ts +19 -0
- package/lib/examples/components/MainContent.js +68 -0
- package/lib/examples/components/MockFileBrowser.d.ts +12 -0
- package/lib/examples/components/MockFileBrowser.js +131 -0
- package/lib/examples/components/Rating.d.ts +14 -0
- package/lib/examples/components/Rating.js +12 -0
- package/lib/examples/components/SessionTabs.d.ts +21 -0
- package/lib/examples/components/SessionTabs.js +11 -0
- package/lib/examples/components/TimeTravel.d.ts +15 -0
- package/lib/examples/components/TimeTravel.js +23 -0
- package/lib/examples/components/index.d.ts +11 -0
- package/lib/examples/components/index.js +14 -0
- package/lib/examples/example-selector.d.ts +22 -0
- package/lib/examples/example-selector.js +62 -0
- package/lib/examples/index.d.ts +21 -0
- package/lib/examples/index.js +25 -0
- package/lib/examples/lexical/editorConfig.d.ts +76 -0
- package/lib/examples/lexical/editorConfig.js +55 -0
- package/lib/examples/lexical/lexical-theme.css +436 -0
- package/lib/examples/lexical/theme.d.ts +61 -0
- package/lib/examples/lexical/theme.js +72 -0
- package/lib/examples/main.d.ts +2 -0
- package/lib/examples/main.js +334 -0
- package/lib/examples/stores/agents/earthquake-detector.ipynb.json +111 -0
- package/lib/examples/stores/agents/earthquake-detector.json +13 -0
- package/lib/examples/stores/agents/earthquake-detector.lexical.json +2988 -0
- package/lib/examples/stores/agents/sales-forecaster.ipynb.json +111 -0
- package/lib/examples/stores/agents/sales-forecaster.json +13 -0
- package/lib/examples/stores/agents/sales-forecaster.lexical.json +2988 -0
- package/lib/examples/stores/agents/social-post-generator.ipynb.json +111 -0
- package/lib/examples/stores/agents/social-post-generator.json +13 -0
- package/lib/examples/stores/agents/social-post-generator.lexical.json +2988 -0
- package/lib/examples/stores/agents/stock-market.ipynb.json +56 -0
- package/lib/examples/stores/agents/stock-market.json +13 -0
- package/lib/examples/stores/agents/stock-market.lexical.json +1026 -0
- package/lib/examples/stores/examplesStore.d.ts +26 -0
- package/lib/examples/stores/examplesStore.js +60 -0
- package/lib/examples/stores/notebooks/Empty.ipynb.json +33 -0
- package/lib/examples/stores/notebooks/IPyWidgetsExample.ipynb.json +101 -0
- package/lib/examples/stores/notebooks/IPyWidgetsExampleWithState.ipynb.json +112 -0
- package/lib/examples/stores/notebooks/Lite.ipynb.json +128 -0
- package/lib/examples/stores/notebooks/Matplotlib.ipynb.json +137 -0
- package/lib/examples/stores/notebooks/NotebookExample1.ipynb.json +126 -0
- package/lib/examples/stores/notebooks/NotebookExample2.ipynb.json +48 -0
- package/lib/examples/stores/notebooks/NotebookOutputs.ipynb.json +49 -0
- package/lib/examples/stores/notebooks/NotebookToCExample.ipynb.json +102 -0
- package/lib/examples/stores/notebooks/OutputIPyWidgetsExample.d.ts +145 -0
- package/lib/examples/stores/notebooks/OutputIPyWidgetsExample.js +153 -0
- package/lib/examples/stores/notebooks/PyGWalker.ipynb.json +55 -0
- package/lib/examples/vercel-ai-elements/VercelAiElementsShowcase.d.ts +12 -0
- package/lib/examples/vercel-ai-elements/VercelAiElementsShowcase.js +69 -0
- package/lib/examples/vercel-ai-elements/components/ArtifactShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/ArtifactShowcase.js +85 -0
- package/lib/examples/vercel-ai-elements/components/CodeBlockShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/CodeBlockShowcase.js +62 -0
- package/lib/examples/vercel-ai-elements/components/ConversationShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/ConversationShowcase.js +51 -0
- package/lib/examples/vercel-ai-elements/components/LoaderShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/LoaderShowcase.js +9 -0
- package/lib/examples/vercel-ai-elements/components/MessageShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/MessageShowcase.js +56 -0
- package/lib/examples/vercel-ai-elements/components/ModelSelectorShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/ModelSelectorShowcase.js +50 -0
- package/lib/examples/vercel-ai-elements/components/PromptInputShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/PromptInputShowcase.js +16 -0
- package/lib/examples/vercel-ai-elements/components/ReasoningShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/ReasoningShowcase.js +72 -0
- package/lib/examples/vercel-ai-elements/components/ShimmerShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/ShimmerShowcase.js +9 -0
- package/lib/examples/vercel-ai-elements/components/SourcesShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/SourcesShowcase.js +43 -0
- package/lib/examples/vercel-ai-elements/components/SuggestionShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/SuggestionShowcase.js +31 -0
- package/lib/examples/vercel-ai-elements/components/ToolShowcase.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/components/ToolShowcase.js +54 -0
- package/lib/examples/vercel-ai-elements/index.d.ts +13 -0
- package/lib/examples/vercel-ai-elements/index.js +17 -0
- package/lib/examples/vercel-ai-elements/main.d.ts +1 -0
- package/lib/examples/vercel-ai-elements/main.js +9 -0
- package/lib/examples/vercel-ai-elements/showcase.css +128 -0
- package/lib/hooks/index.d.ts +68 -0
- package/lib/hooks/index.js +81 -0
- package/lib/hooks/useA2A.d.ts +75 -0
- package/lib/hooks/useA2A.js +368 -0
- package/lib/hooks/useAGUI.d.ts +63 -0
- package/lib/hooks/useAGUI.js +162 -0
- package/lib/hooks/useAcp.d.ts +121 -0
- package/lib/hooks/useAcp.js +459 -0
- package/lib/hooks/useAgents.d.ts +13 -0
- package/lib/hooks/useAgents.js +71 -0
- package/lib/hooks/useChat.d.ts +62 -0
- package/lib/hooks/useChat.js +363 -0
- package/lib/hooks/useKeyboardShortcuts.d.ts +47 -0
- package/lib/hooks/useKeyboardShortcuts.js +153 -0
- package/lib/hooks/useMobile.d.ts +1 -0
- package/lib/hooks/useMobile.js +19 -0
- package/lib/hooks/useNotebookAIAgent.d.ts +8 -0
- package/lib/hooks/useNotebookAIAgent.js +56 -0
- package/lib/hooks/useToast.d.ts +44 -0
- package/lib/hooks/useToast.js +128 -0
- package/lib/hooks/useTools.d.ts +107 -0
- package/lib/hooks/useTools.js +130 -0
- package/lib/hooks/useVercelChat.d.ts +45 -0
- package/lib/hooks/useVercelChat.js +62 -0
- package/lib/index.css +73 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +5 -0
- package/lib/lexical/ChatInlinePlugin.d.ts +21 -0
- package/lib/lexical/ChatInlinePlugin.js +379 -0
- package/lib/lexical/index.d.ts +6 -0
- package/lib/lexical/index.js +10 -0
- package/lib/lib/utils.d.ts +2 -0
- package/lib/lib/utils.js +9 -0
- package/lib/main.d.ts +1 -0
- package/lib/main.js +12 -0
- package/lib/models/AIAgent.d.ts +17 -0
- package/lib/models/AIAgent.js +5 -0
- package/lib/models/index.d.ts +1 -0
- package/lib/models/index.js +5 -0
- package/lib/renderers/a2ui/components/A2UIRenderer.d.ts +7 -0
- package/lib/renderers/a2ui/components/A2UIRenderer.js +102 -0
- package/lib/renderers/a2ui/components/SurfaceRenderer.d.ts +7 -0
- package/lib/renderers/a2ui/components/SurfaceRenderer.js +101 -0
- package/lib/renderers/a2ui/components/content/AudioPlayer.d.ts +9 -0
- package/lib/renderers/a2ui/components/content/AudioPlayer.js +38 -0
- package/lib/renderers/a2ui/components/content/Divider.d.ts +9 -0
- package/lib/renderers/a2ui/components/content/Divider.js +35 -0
- package/lib/renderers/a2ui/components/content/Icon.d.ts +9 -0
- package/lib/renderers/a2ui/components/content/Icon.js +110 -0
- package/lib/renderers/a2ui/components/content/Image.d.ts +9 -0
- package/lib/renderers/a2ui/components/content/Image.js +61 -0
- package/lib/renderers/a2ui/components/content/Text.d.ts +9 -0
- package/lib/renderers/a2ui/components/content/Text.js +64 -0
- package/lib/renderers/a2ui/components/content/Video.d.ts +9 -0
- package/lib/renderers/a2ui/components/content/Video.js +37 -0
- package/lib/renderers/a2ui/components/content/index.d.ts +6 -0
- package/lib/renderers/a2ui/components/content/index.js +25 -0
- package/lib/renderers/a2ui/components/index.d.ts +5 -0
- package/lib/renderers/a2ui/components/index.js +24 -0
- package/lib/renderers/a2ui/components/interactive/Button.d.ts +11 -0
- package/lib/renderers/a2ui/components/interactive/Button.js +71 -0
- package/lib/renderers/a2ui/components/interactive/CheckBox.d.ts +9 -0
- package/lib/renderers/a2ui/components/interactive/CheckBox.js +48 -0
- package/lib/renderers/a2ui/components/interactive/DateTimeInput.d.ts +9 -0
- package/lib/renderers/a2ui/components/interactive/DateTimeInput.js +62 -0
- package/lib/renderers/a2ui/components/interactive/MultipleChoice.d.ts +9 -0
- package/lib/renderers/a2ui/components/interactive/MultipleChoice.js +73 -0
- package/lib/renderers/a2ui/components/interactive/Slider.d.ts +9 -0
- package/lib/renderers/a2ui/components/interactive/Slider.js +53 -0
- package/lib/renderers/a2ui/components/interactive/TextField.d.ts +9 -0
- package/lib/renderers/a2ui/components/interactive/TextField.js +72 -0
- package/lib/renderers/a2ui/components/interactive/index.d.ts +6 -0
- package/lib/renderers/a2ui/components/interactive/index.js +25 -0
- package/lib/renderers/a2ui/components/layout/Card.d.ts +11 -0
- package/lib/renderers/a2ui/components/layout/Card.js +30 -0
- package/lib/renderers/a2ui/components/layout/Column.d.ts +11 -0
- package/lib/renderers/a2ui/components/layout/Column.js +65 -0
- package/lib/renderers/a2ui/components/layout/List.d.ts +11 -0
- package/lib/renderers/a2ui/components/layout/List.js +55 -0
- package/lib/renderers/a2ui/components/layout/Modal.d.ts +11 -0
- package/lib/renderers/a2ui/components/layout/Modal.js +58 -0
- package/lib/renderers/a2ui/components/layout/Row.d.ts +11 -0
- package/lib/renderers/a2ui/components/layout/Row.js +65 -0
- package/lib/renderers/a2ui/components/layout/Tabs.d.ts +11 -0
- package/lib/renderers/a2ui/components/layout/Tabs.js +48 -0
- package/lib/renderers/a2ui/components/layout/index.d.ts +6 -0
- package/lib/renderers/a2ui/components/layout/index.js +25 -0
- package/lib/renderers/a2ui/context/A2UIContext.d.ts +17 -0
- package/lib/renderers/a2ui/context/A2UIContext.js +54 -0
- package/lib/renderers/a2ui/context/ThemeContext.d.ts +20 -0
- package/lib/renderers/a2ui/context/ThemeContext.js +333 -0
- package/lib/renderers/a2ui/hooks/useA2UI.d.ts +36 -0
- package/lib/renderers/a2ui/hooks/useA2UI.js +62 -0
- package/lib/renderers/a2ui/hooks/useDataBinding.d.ts +8 -0
- package/lib/renderers/a2ui/hooks/useDataBinding.js +83 -0
- package/lib/renderers/a2ui/index.d.ts +9 -0
- package/lib/renderers/a2ui/index.js +28 -0
- package/lib/renderers/a2ui/lib/utils.d.ts +11 -0
- package/lib/renderers/a2ui/lib/utils.js +38 -0
- package/lib/renderers/a2ui/types/index.d.ts +17 -0
- package/lib/renderers/a2ui/types/index.js +5 -0
- package/lib/renderers/index.d.ts +1 -0
- package/lib/renderers/index.js +5 -0
- package/lib/state/index.d.ts +1 -0
- package/lib/state/index.js +5 -0
- package/lib/state/substates/AIAgentState.d.ts +11 -0
- package/lib/state/substates/AIAgentState.js +42 -0
- package/lib/state/substates/index.d.ts +1 -0
- package/lib/state/substates/index.js +5 -0
- package/lib/stories/Button.d.ts +15 -0
- package/lib/stories/Button.js +13 -0
- package/lib/stories/Button.stories.d.ts +23 -0
- package/lib/stories/Button.stories.js +48 -0
- package/lib/stories/Cell.stories.d.ts +12 -0
- package/lib/stories/Cell.stories.js +123 -0
- package/lib/stories/Header.d.ts +12 -0
- package/lib/stories/Header.js +8 -0
- package/lib/stories/Header.stories.d.ts +18 -0
- package/lib/stories/Header.stories.js +30 -0
- package/lib/stories/Page.d.ts +3 -0
- package/lib/stories/Page.js +12 -0
- package/lib/stories/Page.stories.d.ts +12 -0
- package/lib/stories/Page.stories.js +28 -0
- package/lib/stories/assets/accessibility.png +0 -0
- package/lib/stories/assets/accessibility.svg +1 -0
- package/lib/stories/assets/addon-library.png +0 -0
- package/lib/stories/assets/assets.png +0 -0
- package/lib/stories/assets/context.png +0 -0
- package/lib/stories/assets/discord.svg +1 -0
- package/lib/stories/assets/docs.png +0 -0
- package/lib/stories/assets/figma-plugin.png +0 -0
- package/lib/stories/assets/github.svg +1 -0
- package/lib/stories/assets/share.png +0 -0
- package/lib/stories/assets/styling.png +0 -0
- package/lib/stories/assets/testing.png +0 -0
- package/lib/stories/assets/theming.png +0 -0
- package/lib/stories/assets/tutorials.svg +1 -0
- package/lib/stories/assets/youtube.svg +1 -0
- package/lib/stories/button.css +35 -0
- package/lib/stories/header.css +37 -0
- package/lib/stories/page.css +73 -0
- package/lib/test-setup.d.ts +1 -0
- package/lib/test-setup.js +80 -0
- package/lib/tools/adapters/agent-runtimes/AgentRuntimesToolAdapter.d.ts +40 -0
- package/lib/tools/adapters/agent-runtimes/AgentRuntimesToolAdapter.js +110 -0
- package/lib/tools/adapters/agent-runtimes/index.d.ts +9 -0
- package/lib/tools/adapters/agent-runtimes/index.js +13 -0
- package/lib/tools/adapters/agent-runtimes/lexicalHooks.d.ts +24 -0
- package/lib/tools/adapters/agent-runtimes/lexicalHooks.js +50 -0
- package/lib/tools/adapters/agent-runtimes/notebookHooks.d.ts +24 -0
- package/lib/tools/adapters/agent-runtimes/notebookHooks.js +51 -0
- package/lib/tools/adapters/copilotkit/CopilotKitToolAdapter.d.ts +73 -0
- package/lib/tools/adapters/copilotkit/CopilotKitToolAdapter.js +244 -0
- package/lib/tools/adapters/copilotkit/index.d.ts +10 -0
- package/lib/tools/adapters/copilotkit/index.js +14 -0
- package/lib/tools/adapters/copilotkit/lexicalHooks.d.ts +27 -0
- package/lib/tools/adapters/copilotkit/lexicalHooks.js +59 -0
- package/lib/tools/adapters/copilotkit/notebookHooks.d.ts +27 -0
- package/lib/tools/adapters/copilotkit/notebookHooks.js +58 -0
- package/lib/tools/adapters/index.d.ts +1 -0
- package/lib/tools/adapters/index.js +5 -0
- package/lib/tools/index.d.ts +6 -0
- package/lib/tools/index.js +18 -0
- package/lib/types.d.ts +5 -0
- package/lib/types.js +5 -0
- package/package.json +327 -0
- package/style/animation/Animation.module.css +174 -0
- package/style/base.css +204 -0
- package/style/index.css +6 -0
- package/style/showcase-vercel-ai.css +137 -0
|
@@ -0,0 +1,1192 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Main ChatBase component.
|
|
8
|
+
* Provides a full chat interface with messages and input.
|
|
9
|
+
* This is the base component used by all other chat container components.
|
|
10
|
+
*
|
|
11
|
+
* Supports multiple modes:
|
|
12
|
+
* 1. Store mode: Uses Zustand store for state management (default)
|
|
13
|
+
* 2. Protocol mode: Connects to backend via AG-UI, A2A, ACP, or Vercel AI protocols
|
|
14
|
+
* 3. Custom mode: Uses onSendMessage prop for custom message handling
|
|
15
|
+
*
|
|
16
|
+
* @module components/chat/components/ChatBase
|
|
17
|
+
*/
|
|
18
|
+
import { useContext } from 'react';
|
|
19
|
+
import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
20
|
+
import { Heading, Text, Spinner, IconButton, Textarea, Button, ActionMenu, ActionList, LabelGroup, Label, } from '@primer/react';
|
|
21
|
+
import { Box } from '@datalayer/primer-addons';
|
|
22
|
+
import { AlertIcon, PlusIcon, TrashIcon, GearIcon, PersonIcon, PaperAirplaneIcon, SquareCircleIcon, ToolsIcon, AiModelIcon, } from '@primer/octicons-react';
|
|
23
|
+
import { AiAgentIcon } from '@datalayer/icons-react';
|
|
24
|
+
import { useQuery, QueryClientContext } from '@tanstack/react-query';
|
|
25
|
+
import { Streamdown } from 'streamdown';
|
|
26
|
+
import { PoweredByTag } from '../elements/PoweredByTag';
|
|
27
|
+
import { requestAPI } from '../../handler';
|
|
28
|
+
import { useChatStore } from '../../store/chatStore';
|
|
29
|
+
import { generateMessageId, createUserMessage, createAssistantMessage, } from '../../types/message';
|
|
30
|
+
import { AGUIAdapter, A2AAdapter, VercelAIAdapter, ACPAdapter, } from '../../protocols';
|
|
31
|
+
import { ToolCallDisplay } from '../display/ToolCallDisplay';
|
|
32
|
+
/**
|
|
33
|
+
* Check if an item is a tool call message
|
|
34
|
+
*/
|
|
35
|
+
function isToolCallMessage(item) {
|
|
36
|
+
return 'type' in item && item.type === 'tool-call';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract text content from a ChatMessage
|
|
40
|
+
*/
|
|
41
|
+
function getMessageText(message) {
|
|
42
|
+
if (typeof message.content === 'string') {
|
|
43
|
+
return message.content;
|
|
44
|
+
}
|
|
45
|
+
// Array of ContentPart - extract text parts
|
|
46
|
+
return message.content
|
|
47
|
+
.filter((part) => part.type === 'text')
|
|
48
|
+
.map(part => part.text)
|
|
49
|
+
.join('');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create protocol adapter based on configuration
|
|
53
|
+
*/
|
|
54
|
+
function createProtocolAdapter(config) {
|
|
55
|
+
const adapterConfig = {
|
|
56
|
+
type: config.type,
|
|
57
|
+
baseUrl: config.endpoint,
|
|
58
|
+
authToken: config.authToken,
|
|
59
|
+
agentId: config.agentId,
|
|
60
|
+
...config.options,
|
|
61
|
+
};
|
|
62
|
+
switch (config.type) {
|
|
63
|
+
case 'ag-ui':
|
|
64
|
+
return new AGUIAdapter(adapterConfig);
|
|
65
|
+
case 'a2a':
|
|
66
|
+
return new A2AAdapter(adapterConfig);
|
|
67
|
+
case 'vercel-ai':
|
|
68
|
+
return new VercelAIAdapter(adapterConfig);
|
|
69
|
+
case 'acp':
|
|
70
|
+
return new ACPAdapter(adapterConfig);
|
|
71
|
+
default:
|
|
72
|
+
console.warn(`[ChatBase] Unknown protocol type: ${config.type}`);
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Hook to safely use query when QueryClient is available
|
|
78
|
+
* Returns null if no QueryClientProvider is present
|
|
79
|
+
*/
|
|
80
|
+
function useConfigQuery(enabled, configEndpoint, authToken) {
|
|
81
|
+
const queryClient = useContext(QueryClientContext);
|
|
82
|
+
// If no QueryClient is available, return a mock result
|
|
83
|
+
if (!queryClient) {
|
|
84
|
+
return {
|
|
85
|
+
data: undefined,
|
|
86
|
+
isLoading: false,
|
|
87
|
+
isError: false,
|
|
88
|
+
error: null,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
92
|
+
return useQuery({
|
|
93
|
+
queryFn: async () => {
|
|
94
|
+
// If configEndpoint is provided, use direct fetch (for FastAPI)
|
|
95
|
+
if (configEndpoint) {
|
|
96
|
+
const headers = {
|
|
97
|
+
'Content-Type': 'application/json',
|
|
98
|
+
};
|
|
99
|
+
if (authToken) {
|
|
100
|
+
headers['Authorization'] = `Bearer ${authToken}`;
|
|
101
|
+
}
|
|
102
|
+
const response = await fetch(configEndpoint, { headers });
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
throw new Error(`Config fetch failed: ${response.statusText}`);
|
|
105
|
+
}
|
|
106
|
+
return response.json();
|
|
107
|
+
}
|
|
108
|
+
// Otherwise use Jupyter requestAPI
|
|
109
|
+
return requestAPI('configure');
|
|
110
|
+
},
|
|
111
|
+
queryKey: ['models', configEndpoint || 'jupyter'],
|
|
112
|
+
enabled,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* ChatBase component - Universal chat panel supporting store, protocol, and custom modes
|
|
117
|
+
*/
|
|
118
|
+
export function ChatBase({ title, showHeader = false, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, className, loadingState, headerActions,
|
|
119
|
+
// Mode selection
|
|
120
|
+
useStore: useStoreMode = true, protocol, onSendMessage, enableStreaming = false,
|
|
121
|
+
// Extended props
|
|
122
|
+
brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderToolResult, footerContent, headerContent, children, borderRadius, backgroundColor, border, boxShadow, compact = false, placeholder, description = 'Start a conversation with the AI agent.', onStateUpdate, onNewChat, onClear, onMessagesChange, autoFocus = false, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, focusTrigger, frontendTools, }) {
|
|
123
|
+
// Store (optional for message persistence)
|
|
124
|
+
const clearStoreMessages = useChatStore(state => state.clearMessages);
|
|
125
|
+
// Component state
|
|
126
|
+
const [displayItems, setDisplayItems] = useState([]);
|
|
127
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
128
|
+
const [isStreaming, setIsStreaming] = useState(false);
|
|
129
|
+
const [error, setError] = useState(null);
|
|
130
|
+
const [input, setInput] = useState('');
|
|
131
|
+
// Model and tools state
|
|
132
|
+
const [selectedModel, setSelectedModel] = useState('');
|
|
133
|
+
// Note: enabledTools is used for backend-defined tools from config query
|
|
134
|
+
// Frontend tools are passed via frontendTools prop
|
|
135
|
+
const [enabledTools, setEnabledTools] = useState([]);
|
|
136
|
+
void enabledTools; // Suppress unused warning - may be used in future
|
|
137
|
+
// Config query (for protocols that support it)
|
|
138
|
+
// Safely handles missing QueryClientProvider
|
|
139
|
+
const configQuery = useConfigQuery(Boolean(protocol?.enableConfigQuery), protocol?.configEndpoint, protocol?.authToken);
|
|
140
|
+
// Refs
|
|
141
|
+
const adapterRef = useRef(null);
|
|
142
|
+
const unsubscribeRef = useRef(null);
|
|
143
|
+
const toolCallsRef = useRef(new Map());
|
|
144
|
+
const currentAssistantMessageRef = useRef(null);
|
|
145
|
+
const threadIdRef = useRef(generateMessageId());
|
|
146
|
+
const messagesEndRef = useRef(null);
|
|
147
|
+
const inputRef = useRef(null);
|
|
148
|
+
const abortControllerRef = useRef(null);
|
|
149
|
+
// Auto-focus input on mount
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
if (autoFocus && inputRef.current) {
|
|
152
|
+
// Small delay to ensure the component is fully rendered
|
|
153
|
+
const timeoutId = setTimeout(() => {
|
|
154
|
+
inputRef.current?.focus();
|
|
155
|
+
}, 100);
|
|
156
|
+
return () => clearTimeout(timeoutId);
|
|
157
|
+
}
|
|
158
|
+
}, [autoFocus]);
|
|
159
|
+
// Refocus input when focusTrigger changes
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (focusTrigger !== undefined && focusTrigger > 0 && inputRef.current) {
|
|
162
|
+
// Small delay to ensure any layout changes have completed
|
|
163
|
+
const timeoutId = setTimeout(() => {
|
|
164
|
+
inputRef.current?.focus();
|
|
165
|
+
}, 150);
|
|
166
|
+
return () => clearTimeout(timeoutId);
|
|
167
|
+
}
|
|
168
|
+
}, [focusTrigger]);
|
|
169
|
+
// Track previous loading state to detect when loading completes
|
|
170
|
+
const wasLoadingRef = useRef(false);
|
|
171
|
+
// Refocus input when loading completes
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (wasLoadingRef.current && !isLoading && inputRef.current) {
|
|
174
|
+
// Small delay to ensure the input is fully enabled
|
|
175
|
+
const timeoutId = setTimeout(() => {
|
|
176
|
+
inputRef.current?.focus();
|
|
177
|
+
}, 50);
|
|
178
|
+
return () => clearTimeout(timeoutId);
|
|
179
|
+
}
|
|
180
|
+
wasLoadingRef.current = isLoading;
|
|
181
|
+
}, [isLoading]);
|
|
182
|
+
// Auto-resize textarea based on content
|
|
183
|
+
const adjustTextareaHeight = useCallback(() => {
|
|
184
|
+
const textarea = inputRef.current;
|
|
185
|
+
if (textarea) {
|
|
186
|
+
// Reset height to auto to get proper scrollHeight
|
|
187
|
+
textarea.style.height = 'auto';
|
|
188
|
+
// Set height to scrollHeight, capped at maxHeight (120px)
|
|
189
|
+
const maxHeight = 120;
|
|
190
|
+
const newHeight = Math.min(textarea.scrollHeight, maxHeight);
|
|
191
|
+
textarea.style.height = `${newHeight}px`;
|
|
192
|
+
// Add overflow if content exceeds maxHeight
|
|
193
|
+
textarea.style.overflowY =
|
|
194
|
+
textarea.scrollHeight > maxHeight ? 'auto' : 'hidden';
|
|
195
|
+
}
|
|
196
|
+
}, []);
|
|
197
|
+
// Adjust textarea height when input changes
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
adjustTextareaHeight();
|
|
200
|
+
}, [input, adjustTextareaHeight]);
|
|
201
|
+
// Initialize model and tools when config is available
|
|
202
|
+
useEffect(() => {
|
|
203
|
+
if (configQuery.data && !selectedModel) {
|
|
204
|
+
const firstModel = configQuery.data.models[0];
|
|
205
|
+
if (firstModel) {
|
|
206
|
+
setSelectedModel(firstModel.id);
|
|
207
|
+
const allToolIds = configQuery.data.builtinTools?.map(tool => tool.id) || [];
|
|
208
|
+
setEnabledTools(allToolIds);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}, [configQuery.data, selectedModel]);
|
|
212
|
+
// Load messages from store on mount when useStoreMode is enabled
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
if (useStoreMode) {
|
|
215
|
+
const storeMessages = useChatStore.getState().messages;
|
|
216
|
+
if (storeMessages.length > 0) {
|
|
217
|
+
setDisplayItems(storeMessages);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}, [useStoreMode]);
|
|
221
|
+
// Derived state
|
|
222
|
+
const messages = displayItems.filter((item) => !isToolCallMessage(item));
|
|
223
|
+
const ready = true;
|
|
224
|
+
// Notify parent when messages change
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
onMessagesChange?.(messages);
|
|
227
|
+
}, [messages, onMessagesChange]);
|
|
228
|
+
// Padding based on compact mode
|
|
229
|
+
const padding = compact ? 2 : 3;
|
|
230
|
+
// Default avatar config
|
|
231
|
+
const defaultAvatarConfig = {
|
|
232
|
+
userAvatar: _jsx(PersonIcon, { size: 16 }),
|
|
233
|
+
assistantAvatar: _jsx(AiAgentIcon, { colored: true, size: 16 }),
|
|
234
|
+
showAvatars: true,
|
|
235
|
+
avatarSize: 32,
|
|
236
|
+
userAvatarBg: 'neutral.muted',
|
|
237
|
+
assistantAvatarBg: 'accent.emphasis',
|
|
238
|
+
...avatarConfig,
|
|
239
|
+
};
|
|
240
|
+
// Initialize protocol adapter
|
|
241
|
+
useEffect(() => {
|
|
242
|
+
if (!protocol)
|
|
243
|
+
return;
|
|
244
|
+
const adapter = createProtocolAdapter(protocol);
|
|
245
|
+
if (!adapter)
|
|
246
|
+
return;
|
|
247
|
+
adapterRef.current = adapter;
|
|
248
|
+
// Subscribe to protocol events
|
|
249
|
+
unsubscribeRef.current = adapter.subscribe((event) => {
|
|
250
|
+
switch (event.type) {
|
|
251
|
+
case 'message':
|
|
252
|
+
// Update or create assistant message
|
|
253
|
+
if (event.message) {
|
|
254
|
+
const incomingId = event.message.id;
|
|
255
|
+
const currentId = currentAssistantMessageRef.current?.id;
|
|
256
|
+
const isNewMessage = !currentId || (incomingId && incomingId !== currentId);
|
|
257
|
+
if (currentAssistantMessageRef.current && !isNewMessage) {
|
|
258
|
+
// Update existing message (same ID)
|
|
259
|
+
setDisplayItems(prev => {
|
|
260
|
+
const newItems = [...prev];
|
|
261
|
+
const idx = newItems.findIndex(item => !isToolCallMessage(item) &&
|
|
262
|
+
item.id === currentAssistantMessageRef.current?.id);
|
|
263
|
+
if (idx >= 0 && !isToolCallMessage(newItems[idx])) {
|
|
264
|
+
newItems[idx] = {
|
|
265
|
+
...newItems[idx],
|
|
266
|
+
content: event.message?.content ?? '',
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return newItems;
|
|
270
|
+
});
|
|
271
|
+
// Update message in store
|
|
272
|
+
if (useStoreMode && currentAssistantMessageRef.current) {
|
|
273
|
+
useChatStore
|
|
274
|
+
.getState()
|
|
275
|
+
.updateMessage(currentAssistantMessageRef.current.id, {
|
|
276
|
+
content: event.message?.content ?? '',
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
// Create new assistant message (new ID or first message)
|
|
282
|
+
const content = event.message.content;
|
|
283
|
+
const contentStr = typeof content === 'string' ? content : (content ?? '');
|
|
284
|
+
const newMessage = createAssistantMessage(typeof contentStr === 'string' ? contentStr : '');
|
|
285
|
+
newMessage.id = event.message.id || newMessage.id;
|
|
286
|
+
currentAssistantMessageRef.current = newMessage;
|
|
287
|
+
setDisplayItems(prev => [...prev, newMessage]);
|
|
288
|
+
// Add message to store
|
|
289
|
+
if (useStoreMode) {
|
|
290
|
+
useChatStore.getState().addMessage(newMessage);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
case 'tool-call':
|
|
296
|
+
// Handle tool call start or update
|
|
297
|
+
if (event.toolCall) {
|
|
298
|
+
const toolCallId = event.toolCall.toolCallId || generateMessageId();
|
|
299
|
+
const toolName = event.toolCall.toolName;
|
|
300
|
+
const args = event.toolCall.args || {};
|
|
301
|
+
// Check if this tool call already exists (update args)
|
|
302
|
+
if (toolCallsRef.current.has(toolCallId)) {
|
|
303
|
+
const existingToolCall = toolCallsRef.current.get(toolCallId);
|
|
304
|
+
if (existingToolCall) {
|
|
305
|
+
// Merge new args with existing (update from TOOL_CALL_END)
|
|
306
|
+
const updatedToolCall = {
|
|
307
|
+
...existingToolCall,
|
|
308
|
+
args: {
|
|
309
|
+
...existingToolCall.args,
|
|
310
|
+
...args,
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
toolCallsRef.current.set(toolCallId, updatedToolCall);
|
|
314
|
+
// Always update displayItems (default ToolCallDisplay will be used if no custom renderer)
|
|
315
|
+
setDisplayItems(prev => prev.map(item => isToolCallMessage(item) && item.toolCallId === toolCallId
|
|
316
|
+
? updatedToolCall
|
|
317
|
+
: item));
|
|
318
|
+
// Check if this is a frontend tool and we have complete args
|
|
319
|
+
// Execute the tool handler if available
|
|
320
|
+
const frontendTool = frontendTools?.find(t => t.name === toolName);
|
|
321
|
+
const toolHandler = frontendTool?.handler;
|
|
322
|
+
if (toolHandler && Object.keys(args).length > 0) {
|
|
323
|
+
// Execute frontend tool
|
|
324
|
+
(async () => {
|
|
325
|
+
try {
|
|
326
|
+
const result = await toolHandler(updatedToolCall.args);
|
|
327
|
+
// Send result back to adapter
|
|
328
|
+
if (adapterRef.current) {
|
|
329
|
+
await adapterRef.current.sendToolResult(toolCallId, {
|
|
330
|
+
toolCallId,
|
|
331
|
+
success: true,
|
|
332
|
+
result,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
// Update tool call with result
|
|
336
|
+
const completedToolCall = {
|
|
337
|
+
...updatedToolCall,
|
|
338
|
+
result,
|
|
339
|
+
status: 'complete',
|
|
340
|
+
};
|
|
341
|
+
toolCallsRef.current.set(toolCallId, completedToolCall);
|
|
342
|
+
setDisplayItems(prev => prev.map(item => isToolCallMessage(item) &&
|
|
343
|
+
item.toolCallId === toolCallId
|
|
344
|
+
? completedToolCall
|
|
345
|
+
: item));
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
console.error('[ChatBase] Frontend tool execution error:', err);
|
|
349
|
+
const errorToolCall = {
|
|
350
|
+
...updatedToolCall,
|
|
351
|
+
status: 'error',
|
|
352
|
+
error: err.message,
|
|
353
|
+
};
|
|
354
|
+
toolCallsRef.current.set(toolCallId, errorToolCall);
|
|
355
|
+
setDisplayItems(prev => prev.map(item => isToolCallMessage(item) &&
|
|
356
|
+
item.toolCallId === toolCallId
|
|
357
|
+
? errorToolCall
|
|
358
|
+
: item));
|
|
359
|
+
}
|
|
360
|
+
})();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
// New tool call - add it
|
|
366
|
+
const toolCallMsg = {
|
|
367
|
+
id: `tool-${toolCallId}`,
|
|
368
|
+
type: 'tool-call',
|
|
369
|
+
toolCallId,
|
|
370
|
+
toolName,
|
|
371
|
+
args,
|
|
372
|
+
status: 'executing',
|
|
373
|
+
};
|
|
374
|
+
toolCallsRef.current.set(toolCallId, toolCallMsg);
|
|
375
|
+
// Always add to displayItems (default ToolCallDisplay will be used if no custom renderer)
|
|
376
|
+
setDisplayItems(prev => [...prev, toolCallMsg]);
|
|
377
|
+
// Execute frontend tool if available and args are present
|
|
378
|
+
const frontendTool = frontendTools?.find(t => t.name === toolName);
|
|
379
|
+
const toolHandler = frontendTool?.handler;
|
|
380
|
+
if (toolHandler && Object.keys(args).length > 0) {
|
|
381
|
+
(async () => {
|
|
382
|
+
try {
|
|
383
|
+
const result = await toolHandler(args);
|
|
384
|
+
// Send result back to adapter
|
|
385
|
+
if (adapterRef.current) {
|
|
386
|
+
await adapterRef.current.sendToolResult(toolCallId, {
|
|
387
|
+
toolCallId,
|
|
388
|
+
success: true,
|
|
389
|
+
result,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
// Update tool call with result
|
|
393
|
+
const completedToolCall = {
|
|
394
|
+
...toolCallMsg,
|
|
395
|
+
result,
|
|
396
|
+
status: 'complete',
|
|
397
|
+
};
|
|
398
|
+
toolCallsRef.current.set(toolCallId, completedToolCall);
|
|
399
|
+
setDisplayItems(prev => prev.map(item => isToolCallMessage(item) &&
|
|
400
|
+
item.toolCallId === toolCallId
|
|
401
|
+
? completedToolCall
|
|
402
|
+
: item));
|
|
403
|
+
}
|
|
404
|
+
catch (err) {
|
|
405
|
+
console.error('[ChatBase] Frontend tool execution error:', err);
|
|
406
|
+
const errorToolCall = {
|
|
407
|
+
...toolCallMsg,
|
|
408
|
+
status: 'error',
|
|
409
|
+
error: err.message,
|
|
410
|
+
};
|
|
411
|
+
toolCallsRef.current.set(toolCallId, errorToolCall);
|
|
412
|
+
setDisplayItems(prev => prev.map(item => isToolCallMessage(item) &&
|
|
413
|
+
item.toolCallId === toolCallId
|
|
414
|
+
? errorToolCall
|
|
415
|
+
: item));
|
|
416
|
+
}
|
|
417
|
+
})();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
break;
|
|
422
|
+
case 'tool-result':
|
|
423
|
+
// Handle tool result - always update status even without custom renderToolResult
|
|
424
|
+
if (event.toolResult) {
|
|
425
|
+
const toolCallId = event.toolResult.toolCallId;
|
|
426
|
+
if (toolCallId && toolCallsRef.current.has(toolCallId)) {
|
|
427
|
+
const existingToolCall = toolCallsRef.current.get(toolCallId);
|
|
428
|
+
if (existingToolCall) {
|
|
429
|
+
// Check if this is a human-in-the-loop tool (has steps in args)
|
|
430
|
+
// If so, keep status as 'executing' until user responds
|
|
431
|
+
const isHumanInTheLoop = existingToolCall.args &&
|
|
432
|
+
'steps' in existingToolCall.args &&
|
|
433
|
+
Array.isArray(existingToolCall.args.steps);
|
|
434
|
+
const updatedToolCall = {
|
|
435
|
+
...existingToolCall,
|
|
436
|
+
result: event.toolResult.result,
|
|
437
|
+
// Keep executing for HITL, otherwise mark complete/error
|
|
438
|
+
status: event.toolResult.error
|
|
439
|
+
? 'error'
|
|
440
|
+
: isHumanInTheLoop
|
|
441
|
+
? 'executing'
|
|
442
|
+
: 'complete',
|
|
443
|
+
error: event.toolResult.error,
|
|
444
|
+
};
|
|
445
|
+
toolCallsRef.current.set(toolCallId, updatedToolCall);
|
|
446
|
+
setDisplayItems(prev => prev.map(item => isToolCallMessage(item) && item.toolCallId === toolCallId
|
|
447
|
+
? updatedToolCall
|
|
448
|
+
: item));
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
break;
|
|
453
|
+
case 'state-update':
|
|
454
|
+
onStateUpdate?.(event.data);
|
|
455
|
+
// When we receive a state update, mark the last executing tool as complete
|
|
456
|
+
// This handles tools that return state events (STATE_SNAPSHOT/STATE_DELTA) instead of TOOL_CALL_RESULT
|
|
457
|
+
if (event.data) {
|
|
458
|
+
// Find any tool calls that are still in 'executing' status
|
|
459
|
+
const executingToolCalls = Array.from(toolCallsRef.current.entries()).filter(([_, tc]) => tc.status === 'executing');
|
|
460
|
+
// Mark the most recent executing tool as complete
|
|
461
|
+
if (executingToolCalls.length > 0) {
|
|
462
|
+
const [lastToolCallId, existingToolCall] = executingToolCalls[executingToolCalls.length - 1];
|
|
463
|
+
// Check if this is NOT a human-in-the-loop tool
|
|
464
|
+
const isHumanInTheLoop = existingToolCall.args &&
|
|
465
|
+
'steps' in existingToolCall.args &&
|
|
466
|
+
Array.isArray(existingToolCall.args.steps);
|
|
467
|
+
if (!isHumanInTheLoop) {
|
|
468
|
+
// Extract result from state data if available
|
|
469
|
+
const stateData = event.data;
|
|
470
|
+
const result = stateData.weather ??
|
|
471
|
+
stateData.result ??
|
|
472
|
+
stateData.toolResult ??
|
|
473
|
+
stateData;
|
|
474
|
+
const updatedToolCall = {
|
|
475
|
+
...existingToolCall,
|
|
476
|
+
result,
|
|
477
|
+
status: 'complete',
|
|
478
|
+
};
|
|
479
|
+
toolCallsRef.current.set(lastToolCallId, updatedToolCall);
|
|
480
|
+
setDisplayItems(prev => prev.map(item => isToolCallMessage(item) &&
|
|
481
|
+
item.toolCallId === lastToolCallId
|
|
482
|
+
? updatedToolCall
|
|
483
|
+
: item));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
break;
|
|
488
|
+
case 'error':
|
|
489
|
+
console.error('[ChatBase] Protocol error:', event.error);
|
|
490
|
+
setError(event.error || new Error('Unknown error'));
|
|
491
|
+
setIsLoading(false);
|
|
492
|
+
setIsStreaming(false);
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
// Connect to protocol
|
|
497
|
+
adapter.connect().catch(console.error);
|
|
498
|
+
return () => {
|
|
499
|
+
unsubscribeRef.current?.();
|
|
500
|
+
adapterRef.current?.disconnect();
|
|
501
|
+
};
|
|
502
|
+
}, [protocol, renderToolResult, onStateUpdate, useStoreMode]);
|
|
503
|
+
// Auto-scroll to bottom
|
|
504
|
+
useEffect(() => {
|
|
505
|
+
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
506
|
+
}, [displayItems]);
|
|
507
|
+
// Handle sending message in protocol mode or custom mode
|
|
508
|
+
const handleSend = useCallback(async () => {
|
|
509
|
+
if (!input.trim() || isLoading)
|
|
510
|
+
return;
|
|
511
|
+
// Need either an adapter (protocol mode) or onSendMessage handler (custom mode)
|
|
512
|
+
if (!adapterRef.current && !onSendMessage)
|
|
513
|
+
return;
|
|
514
|
+
const messageContent = input.trim();
|
|
515
|
+
const userMessage = createUserMessage(messageContent);
|
|
516
|
+
const currentMessages = displayItems.filter((item) => !isToolCallMessage(item));
|
|
517
|
+
const allMessages = [...currentMessages, userMessage];
|
|
518
|
+
setDisplayItems(prev => [...prev, userMessage]);
|
|
519
|
+
setInput('');
|
|
520
|
+
setIsLoading(true);
|
|
521
|
+
setIsStreaming(true);
|
|
522
|
+
setError(null);
|
|
523
|
+
currentAssistantMessageRef.current = null;
|
|
524
|
+
// Persist user message to store if enabled
|
|
525
|
+
if (useStoreMode) {
|
|
526
|
+
useChatStore.getState().addMessage(userMessage);
|
|
527
|
+
}
|
|
528
|
+
try {
|
|
529
|
+
if (onSendMessage) {
|
|
530
|
+
// Custom mode: use the provided message handler
|
|
531
|
+
if (enableStreaming) {
|
|
532
|
+
// Streaming mode: create assistant message placeholder and stream updates
|
|
533
|
+
const assistantMessageId = generateMessageId();
|
|
534
|
+
const assistantMessage = createAssistantMessage('');
|
|
535
|
+
assistantMessage.id = assistantMessageId;
|
|
536
|
+
setDisplayItems(prev => [...prev, assistantMessage]);
|
|
537
|
+
currentAssistantMessageRef.current = assistantMessage;
|
|
538
|
+
if (useStoreMode) {
|
|
539
|
+
useChatStore.getState().addMessage(assistantMessage);
|
|
540
|
+
useChatStore.getState().startStreaming(assistantMessageId);
|
|
541
|
+
}
|
|
542
|
+
// Create abort controller for cancellation
|
|
543
|
+
abortControllerRef.current = new AbortController();
|
|
544
|
+
await onSendMessage(messageContent, allMessages, {
|
|
545
|
+
onChunk: (chunk) => {
|
|
546
|
+
// Append chunk to the assistant message
|
|
547
|
+
setDisplayItems(prev => prev.map(item => item.id === assistantMessageId
|
|
548
|
+
? {
|
|
549
|
+
...item,
|
|
550
|
+
content: item.content + chunk,
|
|
551
|
+
}
|
|
552
|
+
: item));
|
|
553
|
+
if (useStoreMode) {
|
|
554
|
+
useChatStore
|
|
555
|
+
.getState()
|
|
556
|
+
.appendToStream(assistantMessageId, chunk);
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
onComplete: (fullResponse) => {
|
|
560
|
+
// Update assistant message with final content
|
|
561
|
+
setDisplayItems(prev => prev.map(item => item.id === assistantMessageId
|
|
562
|
+
? { ...item, content: fullResponse }
|
|
563
|
+
: item));
|
|
564
|
+
if (useStoreMode) {
|
|
565
|
+
useChatStore
|
|
566
|
+
.getState()
|
|
567
|
+
.updateMessage(assistantMessageId, { content: fullResponse });
|
|
568
|
+
useChatStore.getState().stopStreaming();
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
onError: (error) => {
|
|
572
|
+
// Update assistant message with error
|
|
573
|
+
const errorContent = `Error: ${error.message}`;
|
|
574
|
+
setDisplayItems(prev => prev.map(item => item.id === assistantMessageId
|
|
575
|
+
? { ...item, content: errorContent }
|
|
576
|
+
: item));
|
|
577
|
+
if (useStoreMode) {
|
|
578
|
+
useChatStore
|
|
579
|
+
.getState()
|
|
580
|
+
.updateMessage(assistantMessageId, { content: errorContent });
|
|
581
|
+
useChatStore.getState().stopStreaming();
|
|
582
|
+
}
|
|
583
|
+
setError(error);
|
|
584
|
+
},
|
|
585
|
+
signal: abortControllerRef.current.signal,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
// Non-streaming mode: wait for full response
|
|
590
|
+
const response = await onSendMessage(messageContent, allMessages);
|
|
591
|
+
if (response) {
|
|
592
|
+
const assistantMessage = createAssistantMessage(response);
|
|
593
|
+
setDisplayItems(prev => [...prev, assistantMessage]);
|
|
594
|
+
if (useStoreMode) {
|
|
595
|
+
useChatStore.getState().addMessage(assistantMessage);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
else if (adapterRef.current) {
|
|
601
|
+
// Protocol mode: use the adapter
|
|
602
|
+
// Convert frontend tools to AG-UI format (only serializable properties)
|
|
603
|
+
const toolsForRequest = (frontendTools || []).map(tool => ({
|
|
604
|
+
name: tool.name,
|
|
605
|
+
description: tool.description,
|
|
606
|
+
parameters: tool.parameters || { type: 'object', properties: {} },
|
|
607
|
+
}));
|
|
608
|
+
if (toolsForRequest.length > 0) {
|
|
609
|
+
console.log('[ChatBase] Sending tools to AG-UI:', toolsForRequest.map(t => t.name));
|
|
610
|
+
}
|
|
611
|
+
await adapterRef.current.sendMessage(userMessage, {
|
|
612
|
+
threadId: threadIdRef.current,
|
|
613
|
+
messages: allMessages,
|
|
614
|
+
...(selectedModel && { model: selectedModel }),
|
|
615
|
+
tools: toolsForRequest,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
catch (err) {
|
|
620
|
+
if (err.name !== 'AbortError') {
|
|
621
|
+
console.error('[ChatBase] Send error:', err);
|
|
622
|
+
const errorMessage = createAssistantMessage(`Error: ${err.message}`);
|
|
623
|
+
setDisplayItems(prev => [...prev, errorMessage]);
|
|
624
|
+
setError(err);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
finally {
|
|
628
|
+
setIsLoading(false);
|
|
629
|
+
setIsStreaming(false);
|
|
630
|
+
currentAssistantMessageRef.current = null;
|
|
631
|
+
abortControllerRef.current = null;
|
|
632
|
+
}
|
|
633
|
+
}, [
|
|
634
|
+
input,
|
|
635
|
+
isLoading,
|
|
636
|
+
displayItems,
|
|
637
|
+
selectedModel,
|
|
638
|
+
frontendTools,
|
|
639
|
+
useStoreMode,
|
|
640
|
+
onSendMessage,
|
|
641
|
+
enableStreaming,
|
|
642
|
+
]);
|
|
643
|
+
// Handle stop
|
|
644
|
+
const handleStop = useCallback(() => {
|
|
645
|
+
// Abort custom mode request
|
|
646
|
+
abortControllerRef.current?.abort();
|
|
647
|
+
// Disconnect protocol adapter
|
|
648
|
+
adapterRef.current?.disconnect();
|
|
649
|
+
// Stop streaming in store
|
|
650
|
+
if (useStoreMode) {
|
|
651
|
+
useChatStore.getState().stopStreaming();
|
|
652
|
+
}
|
|
653
|
+
setIsLoading(false);
|
|
654
|
+
setIsStreaming(false);
|
|
655
|
+
}, [useStoreMode]);
|
|
656
|
+
// Handle key press
|
|
657
|
+
const handleKeyDown = useCallback((e) => {
|
|
658
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
659
|
+
e.preventDefault();
|
|
660
|
+
handleSend();
|
|
661
|
+
}
|
|
662
|
+
}, [handleSend]);
|
|
663
|
+
// Handle new chat
|
|
664
|
+
const handleNewChat = useCallback(() => {
|
|
665
|
+
setDisplayItems([]);
|
|
666
|
+
toolCallsRef.current.clear();
|
|
667
|
+
setInput('');
|
|
668
|
+
threadIdRef.current = generateMessageId();
|
|
669
|
+
if (useStoreMode) {
|
|
670
|
+
clearStoreMessages();
|
|
671
|
+
}
|
|
672
|
+
onNewChat?.();
|
|
673
|
+
headerButtons?.onNewChat?.();
|
|
674
|
+
}, [clearStoreMessages, onNewChat, headerButtons, useStoreMode]);
|
|
675
|
+
// Handle clear
|
|
676
|
+
const handleClear = useCallback(() => {
|
|
677
|
+
if (window.confirm('Clear all messages?')) {
|
|
678
|
+
setDisplayItems([]);
|
|
679
|
+
toolCallsRef.current.clear();
|
|
680
|
+
if (useStoreMode) {
|
|
681
|
+
clearStoreMessages();
|
|
682
|
+
}
|
|
683
|
+
onClear?.();
|
|
684
|
+
headerButtons?.onClear?.();
|
|
685
|
+
}
|
|
686
|
+
}, [clearStoreMessages, onClear, headerButtons, useStoreMode]);
|
|
687
|
+
// Not ready yet (store mode only)
|
|
688
|
+
if (!ready) {
|
|
689
|
+
return (_jsx(Box, { className: className, sx: {
|
|
690
|
+
display: 'flex',
|
|
691
|
+
flexDirection: 'column',
|
|
692
|
+
alignItems: 'center',
|
|
693
|
+
justifyContent: 'center',
|
|
694
|
+
height: '100%',
|
|
695
|
+
p: 4,
|
|
696
|
+
borderRadius: borderRadius,
|
|
697
|
+
bg: backgroundColor || 'canvas.default',
|
|
698
|
+
border: border,
|
|
699
|
+
boxShadow: boxShadow,
|
|
700
|
+
}, children: loadingState || (_jsxs(_Fragment, { children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { mt: 3, color: 'fg.muted' }, children: "Initializing chat..." })] })) }));
|
|
701
|
+
}
|
|
702
|
+
// Render header with buttons
|
|
703
|
+
const renderHeader = () => {
|
|
704
|
+
if (!showHeader)
|
|
705
|
+
return null;
|
|
706
|
+
return (_jsx(Box, { sx: {
|
|
707
|
+
display: 'flex',
|
|
708
|
+
flexDirection: 'column',
|
|
709
|
+
borderBottom: '1px solid',
|
|
710
|
+
borderColor: 'border.default',
|
|
711
|
+
}, children: _jsxs(Box, { sx: {
|
|
712
|
+
display: 'flex',
|
|
713
|
+
alignItems: 'center',
|
|
714
|
+
justifyContent: 'space-between',
|
|
715
|
+
p: padding,
|
|
716
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [brandIcon || _jsx(AiAgentIcon, { colored: true, size: 20 }), title && (_jsx(Heading, { as: "h3", sx: { fontSize: 2, fontWeight: 'semibold' }, children: title })), headerContent] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [headerButtons?.showNewChat && (_jsx(IconButton, { icon: PlusIcon, "aria-label": "New chat", variant: "invisible", size: "small", onClick: handleNewChat })), headerButtons?.showClear && messages.length > 0 && (_jsx(IconButton, { icon: TrashIcon, "aria-label": "Clear messages", variant: "invisible", size: "small", onClick: handleClear })), headerButtons?.showSettings && (_jsx(IconButton, { icon: GearIcon, "aria-label": "Settings", variant: "invisible", size: "small", onClick: headerButtons.onSettings })), headerActions] })] }) }));
|
|
717
|
+
};
|
|
718
|
+
// Render empty state
|
|
719
|
+
const renderEmptyState = () => {
|
|
720
|
+
if (emptyState?.render) {
|
|
721
|
+
return emptyState.render();
|
|
722
|
+
}
|
|
723
|
+
// Handler for suggestion clicks
|
|
724
|
+
const handleSuggestionClick = (suggestion) => {
|
|
725
|
+
if (submitOnSuggestionClick) {
|
|
726
|
+
// Auto-submit the suggestion message
|
|
727
|
+
const userMessage = {
|
|
728
|
+
id: generateMessageId(),
|
|
729
|
+
role: 'user',
|
|
730
|
+
content: suggestion.message,
|
|
731
|
+
createdAt: new Date(),
|
|
732
|
+
};
|
|
733
|
+
setDisplayItems(prev => [...prev, userMessage]);
|
|
734
|
+
setIsLoading(true);
|
|
735
|
+
setIsStreaming(true);
|
|
736
|
+
// Convert frontend tools to AG-UI format (same as regular message send)
|
|
737
|
+
const toolsForSuggestion = (frontendTools || []).map(tool => ({
|
|
738
|
+
name: tool.name,
|
|
739
|
+
description: tool.description,
|
|
740
|
+
parameters: tool.parameters || { type: 'object', properties: {} },
|
|
741
|
+
}));
|
|
742
|
+
adapterRef.current
|
|
743
|
+
?.sendMessage(userMessage, {
|
|
744
|
+
threadId: threadIdRef.current,
|
|
745
|
+
messages: [userMessage],
|
|
746
|
+
tools: toolsForSuggestion,
|
|
747
|
+
})
|
|
748
|
+
.catch(err => {
|
|
749
|
+
console.error('[ChatBase] Suggestion send error:', err);
|
|
750
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
751
|
+
})
|
|
752
|
+
.finally(() => {
|
|
753
|
+
setIsLoading(false);
|
|
754
|
+
setIsStreaming(false);
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
// Just fill the input without submitting
|
|
759
|
+
setInput(suggestion.message);
|
|
760
|
+
setTimeout(() => {
|
|
761
|
+
inputRef.current?.focus();
|
|
762
|
+
}, 0);
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
return (_jsxs(Box, { sx: {
|
|
766
|
+
display: 'flex',
|
|
767
|
+
flexDirection: 'column',
|
|
768
|
+
alignItems: 'center',
|
|
769
|
+
justifyContent: 'center',
|
|
770
|
+
height: '100%',
|
|
771
|
+
p: 4,
|
|
772
|
+
color: 'fg.muted',
|
|
773
|
+
textAlign: 'center',
|
|
774
|
+
gap: 2,
|
|
775
|
+
}, children: [emptyState?.icon || brandIcon || _jsx(AiAgentIcon, { colored: true, size: 48 }), _jsx(Text, { sx: { fontSize: 2 }, children: emptyState?.title || 'Start a conversation' }), (emptyState?.subtitle || description) && (_jsx(Text, { sx: { fontSize: 1 }, children: emptyState?.subtitle || description })), suggestions && suggestions.length > 0 && (_jsx(LabelGroup, { sx: { mt: 2, justifyContent: 'center' }, children: suggestions.map((suggestion, index) => (_jsx(Label, { variant: "accent", sx: {
|
|
776
|
+
cursor: 'pointer',
|
|
777
|
+
'&:hover': {
|
|
778
|
+
bg: 'accent.muted',
|
|
779
|
+
},
|
|
780
|
+
}, onClick: () => handleSuggestionClick(suggestion), children: suggestion.title }, index))) }))] }));
|
|
781
|
+
};
|
|
782
|
+
// Render protocol mode messages with tool call support
|
|
783
|
+
const renderProtocolMessages = () => {
|
|
784
|
+
if (displayItems.length === 0) {
|
|
785
|
+
return renderEmptyState();
|
|
786
|
+
}
|
|
787
|
+
// Create respond callback for a tool call (human-in-the-loop)
|
|
788
|
+
const createRespondCallback = (toolCallId) => {
|
|
789
|
+
return async (result) => {
|
|
790
|
+
// Update tool call status to complete with the user's response
|
|
791
|
+
const existingToolCall = toolCallsRef.current.get(toolCallId);
|
|
792
|
+
if (existingToolCall && existingToolCall.status === 'executing') {
|
|
793
|
+
const updatedToolCall = {
|
|
794
|
+
...existingToolCall,
|
|
795
|
+
result,
|
|
796
|
+
status: 'complete',
|
|
797
|
+
};
|
|
798
|
+
toolCallsRef.current.set(toolCallId, updatedToolCall);
|
|
799
|
+
setDisplayItems(prev => prev.map(item => isToolCallMessage(item) && item.toolCallId === toolCallId
|
|
800
|
+
? updatedToolCall
|
|
801
|
+
: item));
|
|
802
|
+
// Send the user's response back to the agent as a new message
|
|
803
|
+
// This continues the conversation with the HITL response
|
|
804
|
+
if (adapterRef.current) {
|
|
805
|
+
// Format the response as a clear text message the agent can understand
|
|
806
|
+
let responseText;
|
|
807
|
+
if (typeof result === 'string') {
|
|
808
|
+
responseText = result;
|
|
809
|
+
}
|
|
810
|
+
else if (result &&
|
|
811
|
+
typeof result === 'object' &&
|
|
812
|
+
'accepted' in result) {
|
|
813
|
+
const hitlResult = result;
|
|
814
|
+
if (hitlResult.accepted) {
|
|
815
|
+
const stepDescriptions = hitlResult.steps?.map(s => s.description).join(', ') || '';
|
|
816
|
+
responseText = stepDescriptions
|
|
817
|
+
? `I confirm and approve the following steps: ${stepDescriptions}`
|
|
818
|
+
: 'I confirm and approve the plan.';
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
responseText =
|
|
822
|
+
'I reject this plan. Please suggest something else.';
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
responseText = JSON.stringify(result, null, 2);
|
|
827
|
+
}
|
|
828
|
+
// Create a user message with the HITL response
|
|
829
|
+
const userMessage = {
|
|
830
|
+
id: generateMessageId(),
|
|
831
|
+
role: 'user',
|
|
832
|
+
content: responseText,
|
|
833
|
+
createdAt: new Date(),
|
|
834
|
+
};
|
|
835
|
+
setIsLoading(true);
|
|
836
|
+
setIsStreaming(true);
|
|
837
|
+
try {
|
|
838
|
+
// Get all chat messages for context
|
|
839
|
+
const allMessages = displayItems.filter((item) => !isToolCallMessage(item));
|
|
840
|
+
await adapterRef.current.sendMessage(userMessage, {
|
|
841
|
+
threadId: threadIdRef.current,
|
|
842
|
+
messages: [...allMessages, userMessage],
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
catch (err) {
|
|
846
|
+
console.error('[ChatBase] HITL respond error:', err);
|
|
847
|
+
}
|
|
848
|
+
finally {
|
|
849
|
+
setIsLoading(false);
|
|
850
|
+
setIsStreaming(false);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
};
|
|
856
|
+
// Check if there are tool calls being rendered
|
|
857
|
+
// This is used to hide duplicate assistant text when UI is shown
|
|
858
|
+
const renderedToolCallIds = new Set();
|
|
859
|
+
displayItems.forEach(item => {
|
|
860
|
+
if (isToolCallMessage(item)) {
|
|
861
|
+
if (renderToolResult) {
|
|
862
|
+
// Check if custom renderer produces a rendered UI
|
|
863
|
+
const rendered = renderToolResult({
|
|
864
|
+
toolCallId: item.toolCallId,
|
|
865
|
+
toolName: item.toolName,
|
|
866
|
+
name: item.toolName,
|
|
867
|
+
args: item.args,
|
|
868
|
+
result: item.result,
|
|
869
|
+
status: item.status,
|
|
870
|
+
error: item.error,
|
|
871
|
+
});
|
|
872
|
+
if (rendered !== null && rendered !== undefined) {
|
|
873
|
+
renderedToolCallIds.add(item.toolCallId);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
// Default display always renders tool calls
|
|
878
|
+
renderedToolCallIds.add(item.toolCallId);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
const hasRenderedToolCall = renderedToolCallIds.size > 0;
|
|
883
|
+
return (_jsxs(_Fragment, { children: [displayItems.map((item, index) => {
|
|
884
|
+
// Render tool call
|
|
885
|
+
if (isToolCallMessage(item)) {
|
|
886
|
+
// Only provide respond callback when status is 'executing'
|
|
887
|
+
const respond = item.status === 'executing'
|
|
888
|
+
? createRespondCallback(item.toolCallId)
|
|
889
|
+
: undefined;
|
|
890
|
+
// Use custom renderToolResult if provided, otherwise use default display
|
|
891
|
+
const toolUI = renderToolResult ? (renderToolResult({
|
|
892
|
+
toolCallId: item.toolCallId,
|
|
893
|
+
toolName: item.toolName,
|
|
894
|
+
name: item.toolName,
|
|
895
|
+
args: item.args,
|
|
896
|
+
result: item.result,
|
|
897
|
+
status: item.status,
|
|
898
|
+
error: item.error,
|
|
899
|
+
respond,
|
|
900
|
+
})) : (_jsx(ToolCallDisplay, { toolCallId: item.toolCallId, toolName: item.toolName, args: item.args, result: item.result, status: item.status, error: item.error }));
|
|
901
|
+
// Skip if custom render returns null/undefined
|
|
902
|
+
if (toolUI === null || toolUI === undefined)
|
|
903
|
+
return null;
|
|
904
|
+
return (_jsx(Box, { sx: {
|
|
905
|
+
display: 'flex',
|
|
906
|
+
flexDirection: 'column',
|
|
907
|
+
alignItems: 'flex-start',
|
|
908
|
+
maxWidth: '95%',
|
|
909
|
+
px: padding,
|
|
910
|
+
py: 1,
|
|
911
|
+
}, children: toolUI }, item.id));
|
|
912
|
+
}
|
|
913
|
+
// Render regular chat message
|
|
914
|
+
const message = item;
|
|
915
|
+
const isUser = message.role === 'user';
|
|
916
|
+
// Skip assistant messages when hideMessagesAfterToolUI is enabled and there's a rendered tool call
|
|
917
|
+
if (!isUser && hideMessagesAfterToolUI && hasRenderedToolCall) {
|
|
918
|
+
// Get message text safely
|
|
919
|
+
const messageText = getMessageText(message);
|
|
920
|
+
// Check if this assistant message follows a rendered tool call
|
|
921
|
+
const prevIndex = index - 1;
|
|
922
|
+
if (prevIndex >= 0) {
|
|
923
|
+
const prevItem = displayItems[prevIndex];
|
|
924
|
+
if (isToolCallMessage(prevItem) &&
|
|
925
|
+
renderedToolCallIds.has(prevItem.toolCallId)) {
|
|
926
|
+
// Skip this assistant message as it describes the tool result
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
// Also check for HITL-specific patterns (step descriptions)
|
|
931
|
+
const hitlToolCall = displayItems.find(item => isToolCallMessage(item) &&
|
|
932
|
+
renderedToolCallIds.has(item.toolCallId) &&
|
|
933
|
+
item.args &&
|
|
934
|
+
'steps' in item.args &&
|
|
935
|
+
Array.isArray(item.args.steps));
|
|
936
|
+
if (hitlToolCall && messageText) {
|
|
937
|
+
const steps = hitlToolCall.args.steps;
|
|
938
|
+
// Check if message contains step descriptions or step-like patterns
|
|
939
|
+
const hasStepContent = steps.some(step => step.description &&
|
|
940
|
+
messageText
|
|
941
|
+
.toLowerCase()
|
|
942
|
+
.includes(step.description.toLowerCase().slice(0, 20))) ||
|
|
943
|
+
// Also hide if message contains numbered list patterns
|
|
944
|
+
/^\s*(\d+\.\s|[-*]\s|\*\*)/.test(messageText) ||
|
|
945
|
+
messageText.includes('**Enabled**') ||
|
|
946
|
+
messageText.includes('steps below');
|
|
947
|
+
if (hasStepContent) {
|
|
948
|
+
return null;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
return (_jsx(Box, { sx: {
|
|
953
|
+
display: 'flex',
|
|
954
|
+
flexDirection: 'column',
|
|
955
|
+
alignItems: isUser ? 'flex-end' : 'flex-start',
|
|
956
|
+
px: padding,
|
|
957
|
+
py: 1,
|
|
958
|
+
}, children: _jsxs(Box, { sx: {
|
|
959
|
+
display: 'flex',
|
|
960
|
+
gap: 2,
|
|
961
|
+
flexDirection: isUser ? 'row-reverse' : 'row',
|
|
962
|
+
alignItems: 'flex-start',
|
|
963
|
+
}, children: [defaultAvatarConfig.showAvatars && (_jsx(Box, { sx: {
|
|
964
|
+
width: defaultAvatarConfig.avatarSize,
|
|
965
|
+
height: defaultAvatarConfig.avatarSize,
|
|
966
|
+
borderRadius: '50%',
|
|
967
|
+
bg: isUser
|
|
968
|
+
? defaultAvatarConfig.userAvatarBg
|
|
969
|
+
: defaultAvatarConfig.assistantAvatarBg,
|
|
970
|
+
color: isUser ? 'fg.default' : 'fg.onEmphasis',
|
|
971
|
+
display: 'flex',
|
|
972
|
+
alignItems: 'center',
|
|
973
|
+
justifyContent: 'center',
|
|
974
|
+
flexShrink: 0,
|
|
975
|
+
}, children: isUser
|
|
976
|
+
? defaultAvatarConfig.userAvatar
|
|
977
|
+
: defaultAvatarConfig.assistantAvatar })), _jsx(Box, { sx: {
|
|
978
|
+
maxWidth: '85%',
|
|
979
|
+
p: 2,
|
|
980
|
+
borderRadius: 2,
|
|
981
|
+
backgroundColor: isUser ? 'accent.emphasis' : '#f6f8fa',
|
|
982
|
+
color: isUser ? 'fg.onEmphasis' : 'fg.default',
|
|
983
|
+
// Streamdown code block styling
|
|
984
|
+
// Code block container
|
|
985
|
+
'& [data-streamdown="code-block"]': {
|
|
986
|
+
borderRadius: '8px',
|
|
987
|
+
border: '1px solid',
|
|
988
|
+
borderColor: 'border.default',
|
|
989
|
+
overflow: 'hidden',
|
|
990
|
+
my: 2,
|
|
991
|
+
},
|
|
992
|
+
// Code block header with language label and buttons
|
|
993
|
+
'& [data-streamdown="code-block-header"]': {
|
|
994
|
+
display: 'flex',
|
|
995
|
+
alignItems: 'center',
|
|
996
|
+
justifyContent: 'space-between',
|
|
997
|
+
backgroundColor: '#e1e4e8',
|
|
998
|
+
padding: '8px 12px',
|
|
999
|
+
fontSize: '12px',
|
|
1000
|
+
color: '#586069',
|
|
1001
|
+
},
|
|
1002
|
+
// Style the buttons in the header
|
|
1003
|
+
'& [data-streamdown="code-block-header"] button': {
|
|
1004
|
+
background: 'none',
|
|
1005
|
+
border: 'none',
|
|
1006
|
+
cursor: 'pointer',
|
|
1007
|
+
padding: '4px',
|
|
1008
|
+
color: '#586069',
|
|
1009
|
+
borderRadius: '4px',
|
|
1010
|
+
'&:hover': {
|
|
1011
|
+
backgroundColor: 'rgba(0,0,0,0.1)',
|
|
1012
|
+
color: '#24292e',
|
|
1013
|
+
},
|
|
1014
|
+
},
|
|
1015
|
+
// Code block body
|
|
1016
|
+
'& [data-streamdown="code-block-body"]': {
|
|
1017
|
+
backgroundColor: '#f6f8fa',
|
|
1018
|
+
padding: '12px',
|
|
1019
|
+
margin: 0,
|
|
1020
|
+
overflow: 'auto',
|
|
1021
|
+
fontSize: '13px',
|
|
1022
|
+
lineHeight: 1.5,
|
|
1023
|
+
},
|
|
1024
|
+
// Make each line display as a block for line breaks
|
|
1025
|
+
'& [data-streamdown="code-block-body"] code': {
|
|
1026
|
+
display: 'block',
|
|
1027
|
+
fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
|
|
1028
|
+
},
|
|
1029
|
+
'& [data-streamdown="code-block-body"] code > span.block': {
|
|
1030
|
+
display: 'block',
|
|
1031
|
+
},
|
|
1032
|
+
'& [data-streamdown="code-block-body"] code > span': {
|
|
1033
|
+
display: 'block',
|
|
1034
|
+
},
|
|
1035
|
+
// General pre/code styling fallback
|
|
1036
|
+
'& pre': {
|
|
1037
|
+
whiteSpace: 'pre-wrap',
|
|
1038
|
+
wordBreak: 'break-word',
|
|
1039
|
+
overflowX: 'auto',
|
|
1040
|
+
margin: 0,
|
|
1041
|
+
},
|
|
1042
|
+
'& pre code': {
|
|
1043
|
+
whiteSpace: 'pre-wrap',
|
|
1044
|
+
},
|
|
1045
|
+
'& code': {
|
|
1046
|
+
fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
|
|
1047
|
+
},
|
|
1048
|
+
}, children: isUser ? (_jsx(Text, { sx: {
|
|
1049
|
+
fontSize: 1,
|
|
1050
|
+
whiteSpace: 'pre-wrap',
|
|
1051
|
+
wordBreak: 'break-word',
|
|
1052
|
+
}, children: getMessageText(message) })) : (_jsx(Box, { sx: { fontSize: 1, lineHeight: 1.5 }, children: _jsx(Streamdown, { children: getMessageText(message) || (isStreaming ? '...' : '') }) })) })] }) }, message.id));
|
|
1053
|
+
}), (isLoading || isStreaming) && (_jsx(Box, { sx: {
|
|
1054
|
+
display: 'flex',
|
|
1055
|
+
alignItems: 'flex-start',
|
|
1056
|
+
px: padding,
|
|
1057
|
+
py: 1,
|
|
1058
|
+
}, children: _jsxs(Box, { sx: {
|
|
1059
|
+
display: 'flex',
|
|
1060
|
+
gap: 2,
|
|
1061
|
+
alignItems: 'flex-start',
|
|
1062
|
+
}, children: [defaultAvatarConfig.showAvatars && (_jsx(Box, { sx: {
|
|
1063
|
+
width: defaultAvatarConfig.avatarSize,
|
|
1064
|
+
height: defaultAvatarConfig.avatarSize,
|
|
1065
|
+
borderRadius: '50%',
|
|
1066
|
+
bg: defaultAvatarConfig.assistantAvatarBg,
|
|
1067
|
+
color: 'fg.onEmphasis',
|
|
1068
|
+
display: 'flex',
|
|
1069
|
+
alignItems: 'center',
|
|
1070
|
+
justifyContent: 'center',
|
|
1071
|
+
flexShrink: 0,
|
|
1072
|
+
}, children: defaultAvatarConfig.assistantAvatar })), _jsxs(Box, { sx: {
|
|
1073
|
+
display: 'flex',
|
|
1074
|
+
alignItems: 'center',
|
|
1075
|
+
gap: '4px',
|
|
1076
|
+
p: 2,
|
|
1077
|
+
borderRadius: 2,
|
|
1078
|
+
bg: 'canvas.subtle',
|
|
1079
|
+
minHeight: '32px',
|
|
1080
|
+
}, children: [_jsx(Box, { sx: {
|
|
1081
|
+
width: '8px',
|
|
1082
|
+
height: '8px',
|
|
1083
|
+
borderRadius: '50%',
|
|
1084
|
+
bg: 'fg.muted',
|
|
1085
|
+
animation: 'typingPulse 1.4s ease-in-out infinite',
|
|
1086
|
+
'@keyframes typingPulse': {
|
|
1087
|
+
'0%, 60%, 100%': {
|
|
1088
|
+
transform: 'scale(0.6)',
|
|
1089
|
+
opacity: 0.4,
|
|
1090
|
+
},
|
|
1091
|
+
'30%': {
|
|
1092
|
+
transform: 'scale(1)',
|
|
1093
|
+
opacity: 1,
|
|
1094
|
+
},
|
|
1095
|
+
},
|
|
1096
|
+
} }), _jsx(Box, { sx: {
|
|
1097
|
+
width: '8px',
|
|
1098
|
+
height: '8px',
|
|
1099
|
+
borderRadius: '50%',
|
|
1100
|
+
bg: 'fg.muted',
|
|
1101
|
+
animation: 'typingPulse 1.4s ease-in-out infinite',
|
|
1102
|
+
animationDelay: '0.2s',
|
|
1103
|
+
'@keyframes typingPulse': {
|
|
1104
|
+
'0%, 60%, 100%': {
|
|
1105
|
+
transform: 'scale(0.6)',
|
|
1106
|
+
opacity: 0.4,
|
|
1107
|
+
},
|
|
1108
|
+
'30%': {
|
|
1109
|
+
transform: 'scale(1)',
|
|
1110
|
+
opacity: 1,
|
|
1111
|
+
},
|
|
1112
|
+
},
|
|
1113
|
+
} }), _jsx(Box, { sx: {
|
|
1114
|
+
width: '8px',
|
|
1115
|
+
height: '8px',
|
|
1116
|
+
borderRadius: '50%',
|
|
1117
|
+
bg: 'fg.muted',
|
|
1118
|
+
animation: 'typingPulse 1.4s ease-in-out infinite',
|
|
1119
|
+
animationDelay: '0.4s',
|
|
1120
|
+
'@keyframes typingPulse': {
|
|
1121
|
+
'0%, 60%, 100%': {
|
|
1122
|
+
transform: 'scale(0.6)',
|
|
1123
|
+
opacity: 0.4,
|
|
1124
|
+
},
|
|
1125
|
+
'30%': {
|
|
1126
|
+
transform: 'scale(1)',
|
|
1127
|
+
opacity: 1,
|
|
1128
|
+
},
|
|
1129
|
+
},
|
|
1130
|
+
} })] })] }) })), _jsx("div", { ref: messagesEndRef })] }));
|
|
1131
|
+
};
|
|
1132
|
+
// Render protocol mode input
|
|
1133
|
+
const renderProtocolInput = () => {
|
|
1134
|
+
const availableTools = configQuery.data?.builtinTools || [];
|
|
1135
|
+
const models = configQuery.data?.models || [];
|
|
1136
|
+
return (_jsxs(Box, { children: [_jsx(Box, { sx: {
|
|
1137
|
+
p: padding,
|
|
1138
|
+
borderTop: '1px solid',
|
|
1139
|
+
borderColor: 'border.default',
|
|
1140
|
+
bg: 'canvas.subtle',
|
|
1141
|
+
}, children: _jsxs(Box, { sx: { display: 'flex', gap: 2, alignItems: 'flex-end' }, children: [_jsx(Textarea, { ref: inputRef, value: input, onChange: e => {
|
|
1142
|
+
setInput(e.target.value);
|
|
1143
|
+
// Height adjustment happens via useEffect watching input
|
|
1144
|
+
}, onKeyDown: handleKeyDown, placeholder: placeholder || 'Type a message...', disabled: isLoading, sx: {
|
|
1145
|
+
flex: 1,
|
|
1146
|
+
resize: 'none',
|
|
1147
|
+
minHeight: '40px',
|
|
1148
|
+
maxHeight: '120px',
|
|
1149
|
+
overflow: 'hidden',
|
|
1150
|
+
transition: 'height 0.1s ease-out',
|
|
1151
|
+
}, rows: 1 }), isLoading ? (_jsx(IconButton, { icon: SquareCircleIcon, "aria-label": "Stop", onClick: handleStop, sx: { alignSelf: 'flex-end' } })) : (_jsx(IconButton, { icon: PaperAirplaneIcon, "aria-label": "Send", onClick: handleSend, disabled: !input.trim(), sx: { alignSelf: 'flex-end' } }))] }) }), (showModelSelector || showToolsMenu) && configQuery.data && (_jsxs(Box, { sx: {
|
|
1152
|
+
display: 'flex',
|
|
1153
|
+
gap: 2,
|
|
1154
|
+
px: padding,
|
|
1155
|
+
py: 1,
|
|
1156
|
+
borderTop: '1px solid',
|
|
1157
|
+
borderColor: 'border.default',
|
|
1158
|
+
alignItems: 'center',
|
|
1159
|
+
bg: 'canvas.subtle',
|
|
1160
|
+
}, children: [showToolsMenu && (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(IconButton, { icon: ToolsIcon, "aria-label": "Tools", variant: "invisible", size: "small" }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "start", children: _jsx(ActionList, { children: _jsx(ActionList.Group, { title: "Available Tools", children: availableTools.length > 0 ? (availableTools.map(tool => (_jsxs(ActionList.Item, { disabled: true, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(Box, { sx: {
|
|
1161
|
+
width: 8,
|
|
1162
|
+
height: 8,
|
|
1163
|
+
borderRadius: '50%',
|
|
1164
|
+
backgroundColor: 'success.emphasis',
|
|
1165
|
+
} }) }), tool.name] }, tool.id)))) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted', fontStyle: 'italic' }, children: "No tools available" }) })) }) }) })] })), showModelSelector && models.length > 0 && selectedModel && (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { type: "button", variant: "invisible", size: "small", leadingVisual: AiModelIcon, children: _jsx(Text, { sx: { fontSize: 0 }, children: models.find(m => m.id === selectedModel)?.name ||
|
|
1166
|
+
'Select Model' }) }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "end", children: _jsx(ActionList, { selectionVariant: "single", children: models.map(modelItem => (_jsx(ActionList.Item, { selected: selectedModel === modelItem.id, onSelect: () => setSelectedModel(modelItem.id), children: modelItem.name }, modelItem.id))) }) })] }))] }))] }));
|
|
1167
|
+
};
|
|
1168
|
+
return (_jsxs(Box, { className: className, sx: {
|
|
1169
|
+
display: 'flex',
|
|
1170
|
+
flexDirection: 'column',
|
|
1171
|
+
height: '100%',
|
|
1172
|
+
bg: backgroundColor || 'canvas.default',
|
|
1173
|
+
borderRadius: borderRadius,
|
|
1174
|
+
border: border,
|
|
1175
|
+
boxShadow: boxShadow,
|
|
1176
|
+
overflow: 'hidden',
|
|
1177
|
+
}, children: [renderHeader(), showErrors && error && (_jsxs(Box, { sx: {
|
|
1178
|
+
display: 'flex',
|
|
1179
|
+
alignItems: 'center',
|
|
1180
|
+
gap: 2,
|
|
1181
|
+
p: padding,
|
|
1182
|
+
bg: 'danger.subtle',
|
|
1183
|
+
borderBottom: '1px solid',
|
|
1184
|
+
borderColor: 'danger.muted',
|
|
1185
|
+
}, children: [_jsx(AlertIcon, { size: 16 }), _jsx(Text, { sx: { color: 'danger.fg', fontSize: 1 }, children: error.message })] })), _jsx(Box, { sx: { flex: 1, overflow: 'auto', bg: 'canvas.default' }, children: children ? (children) : (_jsx(Box, { sx: {
|
|
1186
|
+
display: 'flex',
|
|
1187
|
+
flexDirection: 'column',
|
|
1188
|
+
minHeight: '100%',
|
|
1189
|
+
bg: 'canvas.default',
|
|
1190
|
+
}, children: renderProtocolMessages() })) }), footerContent, showInput && renderProtocolInput(), showPoweredBy && _jsx(PoweredByTag, { ...poweredByProps })] }));
|
|
1191
|
+
}
|
|
1192
|
+
export default ChatBase;
|