@gram-ai/elements 1.27.4 → 1.27.5
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/README.md +72 -60
- package/README.typedoc.md +6 -6
- package/bin/cli.js +74 -74
- package/dist/compat-shims-CO9JXXV4.cjs.map +1 -1
- package/dist/{compat-shims-BPJ7Q68c.js → compat-shims-DxtUrORi.js} +4 -2
- package/dist/compat-shims-DxtUrORi.js.map +1 -0
- package/dist/components/ShareButton/index.d.ts +2 -2
- package/dist/components/assistant-ui/message-feedback.d.ts +1 -1
- package/dist/components/assistant-ui/tooltip-icon-button.d.ts +2 -2
- package/dist/components/ui/avatar.d.ts +2 -2
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/calendar.d.ts +1 -1
- package/dist/components/ui/collapsible.d.ts +1 -1
- package/dist/components/ui/dialog.d.ts +4 -4
- package/dist/components/ui/popover.d.ts +2 -2
- package/dist/components/ui/skeleton.d.ts +1 -1
- package/dist/components/ui/time-range-picker.d.ts +1 -1
- package/dist/components/ui/tool-ui.d.ts +7 -7
- package/dist/components/ui/tooltip.d.ts +2 -2
- package/dist/contexts/ConnectionStatusContext.d.ts +1 -1
- package/dist/elements.cjs +1 -1
- package/dist/elements.js +2 -2
- package/dist/hooks/useDensity.d.ts +73 -73
- package/dist/hooks/useMCPTools.d.ts +1 -1
- package/dist/hooks/useRadius.d.ts +1 -1
- package/dist/{index-BpJstUh1.cjs → index-C4bFBGfl.cjs} +4 -4
- package/dist/{index-BpJstUh1.cjs.map → index-C4bFBGfl.cjs.map} +1 -1
- package/dist/{index-CUitXazZ.js → index-D93pV0_o.js} +55 -55
- package/dist/{index-CUitXazZ.js.map → index-D93pV0_o.js.map} +1 -1
- package/dist/{index-D0bAYNQy.js → index-DuCQRbcQ.js} +279 -265
- package/dist/index-DuCQRbcQ.js.map +1 -0
- package/dist/{index-KSX4Qjip.cjs → index-y_PNN5vK.cjs} +10 -10
- package/dist/index-y_PNN5vK.cjs.map +1 -0
- package/dist/lib/cassette.d.ts +4 -4
- package/dist/lib/errorTracking.d.ts +1 -1
- package/dist/lib/messageConverter.d.ts +1 -1
- package/dist/lib/models.d.ts +1 -1
- package/dist/plugins/chart/ui/bar-chart.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/accordion-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/accordion.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/action-button.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/alert-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/alert.d.ts +4 -4
- package/dist/plugins/generative-ui/ui/avatar.d.ts +5 -5
- package/dist/plugins/generative-ui/ui/badge.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/button-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/button.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/card-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/card.d.ts +8 -8
- package/dist/plugins/generative-ui/ui/checkbox.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/data-table.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/dialog.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/dropdown-menu.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/grid.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/input-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/input.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/label.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/metric.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/pagination.d.ts +6 -6
- package/dist/plugins/generative-ui/ui/popover.d.ts +4 -4
- package/dist/plugins/generative-ui/ui/progress.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/radio-group.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/select.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/separator.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/skeleton.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/stack.d.ts +6 -6
- package/dist/plugins/generative-ui/ui/switch.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/table.d.ts +9 -9
- package/dist/plugins/generative-ui/ui/tabs-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/tabs.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/text.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/textarea.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/tooltip.d.ts +1 -1
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.js +1 -1
- package/dist/{profiler-BFkhZRxj.js → profiler-FpBY9eRv.js} +2 -2
- package/dist/{profiler-BFkhZRxj.js.map → profiler-FpBY9eRv.js.map} +1 -1
- package/dist/{profiler-CyzxBxVz.cjs → profiler-_mthyjvo.cjs} +2 -2
- package/dist/{profiler-CyzxBxVz.cjs.map → profiler-_mthyjvo.cjs.map} +1 -1
- package/dist/react-shim.js +1 -1
- package/dist/server/express.cjs.map +1 -1
- package/dist/server/express.js.map +1 -1
- package/dist/{startRecording-Dq92sEHf.cjs → startRecording-NJcpiHw-.cjs} +2 -2
- package/dist/{startRecording-Dq92sEHf.cjs.map → startRecording-NJcpiHw-.cjs.map} +1 -1
- package/dist/{startRecording-C-PPAs_Z.js → startRecording-r5MXQ2Dm.js} +2 -2
- package/dist/{startRecording-C-PPAs_Z.js.map → startRecording-r5MXQ2Dm.js.map} +1 -1
- package/dist/types/index.d.ts +2 -2
- package/package.json +1 -5
- package/src/compat-plugin.ts +14 -14
- package/src/compat-shims.ts +33 -31
- package/src/compat.test.ts +48 -48
- package/src/compat.ts +6 -6
- package/src/components/Chat/index.tsx +17 -17
- package/src/components/Chat/stories/Charts.stories.tsx +98 -98
- package/src/components/Chat/stories/Composer.stories.tsx +15 -15
- package/src/components/Chat/stories/ConnectionConfiguration.stories.tsx +44 -44
- package/src/components/Chat/stories/CustomComponents.stories.tsx +17 -17
- package/src/components/Chat/stories/Density.stories.tsx +20 -20
- package/src/components/Chat/stories/ErrorBoundary.stories.tsx +47 -47
- package/src/components/Chat/stories/FrontendTools.stories.tsx +39 -39
- package/src/components/Chat/stories/GenerativeUI.stories.tsx +48 -48
- package/src/components/Chat/stories/MessageFeedback.stories.tsx +52 -52
- package/src/components/Chat/stories/Modal.stories.tsx +28 -28
- package/src/components/Chat/stories/Model.stories.tsx +11 -11
- package/src/components/Chat/stories/Radius.stories.tsx +20 -20
- package/src/components/Chat/stories/Sidecar.stories.tsx +13 -13
- package/src/components/Chat/stories/StyleIsolation.stories.tsx +11 -11
- package/src/components/Chat/stories/Theme.stories.tsx +25 -25
- package/src/components/Chat/stories/Thread.stories.tsx +25 -25
- package/src/components/Chat/stories/ToolApproval.stories.tsx +55 -55
- package/src/components/Chat/stories/ToolMentions.stories.tsx +17 -17
- package/src/components/Chat/stories/Tools.stories.tsx +88 -88
- package/src/components/Chat/stories/Variants.stories.tsx +32 -32
- package/src/components/Chat/stories/Welcome.stories.tsx +14 -14
- package/src/components/ChatHistory.tsx +7 -7
- package/src/components/FrontendTools/index.tsx +5 -5
- package/src/components/Replay.stories.tsx +157 -157
- package/src/components/Replay.tsx +76 -73
- package/src/components/ShadowRoot.tsx +40 -40
- package/src/components/ShareButton/index.tsx +32 -32
- package/src/components/assistant-ui/assistant-modal.tsx +92 -87
- package/src/components/assistant-ui/assistant-sidecar.tsx +35 -35
- package/src/components/assistant-ui/attachment.tsx +80 -80
- package/src/components/assistant-ui/connection-status-indicator.tsx +33 -33
- package/src/components/assistant-ui/error-boundary.tsx +34 -34
- package/src/components/assistant-ui/follow-on-suggestions.tsx +26 -26
- package/src/components/assistant-ui/markdown-text.tsx +69 -69
- package/src/components/assistant-ui/mentioned-tools-badges.tsx +38 -38
- package/src/components/assistant-ui/message-feedback.tsx +57 -50
- package/src/components/assistant-ui/reasoning.tsx +83 -83
- package/src/components/assistant-ui/thread-list.tsx +45 -45
- package/src/components/assistant-ui/thread.tsx +278 -278
- package/src/components/assistant-ui/tool-fallback.tsx +37 -37
- package/src/components/assistant-ui/tool-group.tsx +26 -26
- package/src/components/assistant-ui/tool-mention-autocomplete.tsx +122 -122
- package/src/components/assistant-ui/tooltip-icon-button.tsx +18 -18
- package/src/components/ui/avatar.tsx +12 -12
- package/src/components/ui/button.tsx +12 -12
- package/src/components/ui/buttonVariants.ts +17 -17
- package/src/components/ui/calendar.tsx +106 -106
- package/src/components/ui/charts.stories.tsx +56 -56
- package/src/components/ui/collapsible.tsx +5 -5
- package/src/components/ui/dialog.tsx +30 -30
- package/src/components/ui/generative-ui.stories.tsx +200 -200
- package/src/components/ui/generative-ui.tsx +26 -26
- package/src/components/ui/popover.tsx +14 -14
- package/src/components/ui/skeleton.tsx +5 -5
- package/src/components/ui/time-range-picker.stories.tsx +80 -80
- package/src/components/ui/time-range-picker.tsx +245 -244
- package/src/components/ui/tool-ui.stories.tsx +37 -37
- package/src/components/ui/tool-ui.tsx +221 -215
- package/src/components/ui/tooltip.tsx +15 -15
- package/src/constants/tailwind.ts +1 -1
- package/src/contexts/ChatIdContext.tsx +7 -7
- package/src/contexts/ConnectionStatusContext.tsx +64 -64
- package/src/contexts/ElementsProvider.tsx +214 -213
- package/src/contexts/ReplayContext.ts +3 -3
- package/src/contexts/ToolApprovalContext.tsx +54 -54
- package/src/contexts/ToolExecutionContext.tsx +34 -34
- package/src/contexts/contexts.ts +7 -7
- package/src/contexts/portal-container-context.ts +2 -2
- package/src/contexts/portal-container.tsx +7 -7
- package/src/embedded.ts +1 -1
- package/src/global.css +25 -25
- package/src/hooks/useAuth.ts +72 -72
- package/src/hooks/useDensity.ts +79 -79
- package/src/hooks/useElements.ts +6 -6
- package/src/hooks/useExpanded.ts +12 -12
- package/src/hooks/useFollowOnSuggestions.ts +83 -83
- package/src/hooks/useGramThreadListAdapter.tsx +99 -99
- package/src/hooks/useMCPTools.ts +47 -47
- package/src/hooks/useModel.ts +14 -14
- package/src/hooks/usePluginComponents.ts +11 -11
- package/src/hooks/usePortalContainer.ts +5 -5
- package/src/hooks/useRadius.ts +23 -23
- package/src/hooks/useRecordCassette.ts +34 -34
- package/src/hooks/useSession.ts +11 -11
- package/src/hooks/useThemeProps.ts +13 -13
- package/src/hooks/useThreadId.ts +4 -4
- package/src/hooks/useToolApproval.ts +7 -7
- package/src/hooks/useToolMentions.ts +40 -40
- package/src/index.ts +26 -26
- package/src/lib/api.test.ts +61 -61
- package/src/lib/api.ts +4 -3
- package/src/lib/auth.ts +13 -13
- package/src/lib/cassette.ts +84 -84
- package/src/lib/easing.ts +1 -1
- package/src/lib/errorTracking.config.ts +5 -5
- package/src/lib/errorTracking.ts +29 -29
- package/src/lib/generative-ui.ts +7 -7
- package/src/lib/humanize.ts +3 -3
- package/src/lib/messageConverter.test.ts +130 -127
- package/src/lib/messageConverter.ts +196 -196
- package/src/lib/models.ts +21 -20
- package/src/lib/token.test.ts +56 -56
- package/src/lib/token.ts +14 -14
- package/src/lib/tool-mentions.ts +45 -45
- package/src/lib/tools.ts +66 -62
- package/src/lib/utils.ts +5 -5
- package/src/lib.d.ts +1 -1
- package/src/plugins/README.md +5 -5
- package/src/plugins/chart/catalog.ts +18 -18
- package/src/plugins/chart/chart.test.ts +31 -31
- package/src/plugins/chart/component.tsx +34 -34
- package/src/plugins/chart/index.ts +4 -4
- package/src/plugins/chart/ui/area-chart.tsx +42 -42
- package/src/plugins/chart/ui/bar-chart.tsx +46 -46
- package/src/plugins/chart/ui/donut-chart.tsx +48 -48
- package/src/plugins/chart/ui/index.ts +7 -7
- package/src/plugins/chart/ui/line-chart.tsx +43 -43
- package/src/plugins/chart/ui/pie-chart.tsx +44 -44
- package/src/plugins/chart/ui/radar-chart.tsx +33 -33
- package/src/plugins/chart/ui/scatter-chart.tsx +43 -43
- package/src/plugins/components/MacOSWindowFrame.tsx +15 -15
- package/src/plugins/components/PluginLoadingState.tsx +10 -10
- package/src/plugins/components/index.ts +1 -1
- package/src/plugins/generative-ui/catalog.ts +54 -54
- package/src/plugins/generative-ui/component.tsx +85 -85
- package/src/plugins/generative-ui/index.ts +4 -4
- package/src/plugins/generative-ui/ui/accordion-wrapper.tsx +16 -16
- package/src/plugins/generative-ui/ui/accordion.tsx +16 -16
- package/src/plugins/generative-ui/ui/action-button.tsx +28 -28
- package/src/plugins/generative-ui/ui/alert-wrapper.tsx +8 -8
- package/src/plugins/generative-ui/ui/alert.tsx +20 -20
- package/src/plugins/generative-ui/ui/avatar-wrapper.tsx +7 -7
- package/src/plugins/generative-ui/ui/avatar.tsx +30 -30
- package/src/plugins/generative-ui/ui/badge.tsx +22 -22
- package/src/plugins/generative-ui/ui/button-wrapper.tsx +12 -12
- package/src/plugins/generative-ui/ui/button.tsx +28 -28
- package/src/plugins/generative-ui/ui/card-wrapper.tsx +8 -8
- package/src/plugins/generative-ui/ui/card.tsx +27 -27
- package/src/plugins/generative-ui/ui/checkbox-wrapper.tsx +9 -9
- package/src/plugins/generative-ui/ui/checkbox.tsx +9 -9
- package/src/plugins/generative-ui/ui/data-table.tsx +8 -8
- package/src/plugins/generative-ui/ui/dialog.tsx +31 -31
- package/src/plugins/generative-ui/ui/dropdown-menu.tsx +44 -44
- package/src/plugins/generative-ui/ui/grid.tsx +12 -12
- package/src/plugins/generative-ui/ui/index.ts +40 -40
- package/src/plugins/generative-ui/ui/input-wrapper.tsx +11 -11
- package/src/plugins/generative-ui/ui/input.tsx +9 -9
- package/src/plugins/generative-ui/ui/label.tsx +8 -8
- package/src/plugins/generative-ui/ui/list.tsx +11 -11
- package/src/plugins/generative-ui/ui/metric.tsx +23 -23
- package/src/plugins/generative-ui/ui/pagination.tsx +28 -28
- package/src/plugins/generative-ui/ui/popover.tsx +21 -21
- package/src/plugins/generative-ui/ui/progress.tsx +13 -13
- package/src/plugins/generative-ui/ui/radio-group.tsx +12 -12
- package/src/plugins/generative-ui/ui/select-wrapper.tsx +7 -7
- package/src/plugins/generative-ui/ui/select.tsx +37 -37
- package/src/plugins/generative-ui/ui/separator.tsx +9 -9
- package/src/plugins/generative-ui/ui/skeleton-wrapper.tsx +10 -10
- package/src/plugins/generative-ui/ui/skeleton.tsx +5 -5
- package/src/plugins/generative-ui/ui/stack.tsx +28 -28
- package/src/plugins/generative-ui/ui/switch.tsx +11 -11
- package/src/plugins/generative-ui/ui/table.tsx +32 -32
- package/src/plugins/generative-ui/ui/tabs-wrapper.tsx +11 -11
- package/src/plugins/generative-ui/ui/tabs.tsx +26 -26
- package/src/plugins/generative-ui/ui/text.tsx +12 -12
- package/src/plugins/generative-ui/ui/textarea.tsx +7 -7
- package/src/plugins/generative-ui/ui/tooltip.tsx +12 -12
- package/src/plugins/index.ts +7 -7
- package/src/react-shim.ts +6 -6
- package/src/server/bun.ts +12 -12
- package/src/server/core.ts +25 -25
- package/src/server/express.ts +17 -15
- package/src/server/fastify.ts +14 -14
- package/src/server/hono.ts +9 -9
- package/src/server/nextjs.ts +12 -12
- package/src/server/tanstack-start.ts +12 -12
- package/src/server.ts +27 -27
- package/src/storybook.d.ts +4 -4
- package/src/types/index.ts +122 -122
- package/src/types/plugins.ts +7 -7
- package/src/vite-env.d.ts +12 -12
- package/dist/compat-shims-BPJ7Q68c.js.map +0 -1
- package/dist/index-D0bAYNQy.js.map +0 -1
- package/dist/index-KSX4Qjip.cjs.map +0 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { FrontendTools } from
|
|
2
|
-
import { ROOT_SELECTOR } from
|
|
1
|
+
import { FrontendTools } from "@/components/FrontendTools";
|
|
2
|
+
import { ROOT_SELECTOR } from "@/constants/tailwind";
|
|
3
3
|
import {
|
|
4
4
|
isLocalThreadId,
|
|
5
5
|
useGramThreadListAdapter,
|
|
6
|
-
} from
|
|
7
|
-
import { useMCPTools } from
|
|
8
|
-
import { useToolApproval } from
|
|
9
|
-
import { getApiUrl } from
|
|
10
|
-
import { initErrorTracking, trackError } from
|
|
11
|
-
import { MODELS } from
|
|
6
|
+
} from "@/hooks/useGramThreadListAdapter";
|
|
7
|
+
import { useMCPTools } from "@/hooks/useMCPTools";
|
|
8
|
+
import { useToolApproval } from "@/hooks/useToolApproval";
|
|
9
|
+
import { getApiUrl } from "@/lib/api";
|
|
10
|
+
import { initErrorTracking, trackError } from "@/lib/errorTracking";
|
|
11
|
+
import { MODELS } from "@/lib/models";
|
|
12
12
|
import {
|
|
13
13
|
clearFrontendToolApprovalConfig,
|
|
14
14
|
getEnabledTools,
|
|
@@ -17,33 +17,34 @@ import {
|
|
|
17
17
|
wrapToolsWithApproval,
|
|
18
18
|
type ApprovalHelpers,
|
|
19
19
|
type FrontendTool,
|
|
20
|
-
} from
|
|
21
|
-
import { cn } from
|
|
22
|
-
import { recommended } from
|
|
23
|
-
import { ElementsConfig, Model } from
|
|
24
|
-
import { Plugin } from
|
|
20
|
+
} from "@/lib/tools";
|
|
21
|
+
import { cn } from "@/lib/utils";
|
|
22
|
+
import { recommended } from "@/plugins";
|
|
23
|
+
import { ElementsConfig, Model } from "@/types";
|
|
24
|
+
import { Plugin } from "@/types/plugins";
|
|
25
25
|
import {
|
|
26
26
|
AssistantRuntimeProvider,
|
|
27
27
|
AssistantTool,
|
|
28
28
|
useAssistantState,
|
|
29
29
|
unstable_useRemoteThreadListRuntime as useRemoteThreadListRuntime,
|
|
30
|
-
} from
|
|
30
|
+
} from "@assistant-ui/react";
|
|
31
31
|
import {
|
|
32
32
|
frontendTools as convertFrontendToolsToAISDKTools,
|
|
33
33
|
useChatRuntime,
|
|
34
|
-
} from
|
|
35
|
-
import { createOpenRouter } from
|
|
36
|
-
import { QueryClient, QueryClientProvider } from
|
|
34
|
+
} from "@assistant-ui/react-ai-sdk";
|
|
35
|
+
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
36
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
37
37
|
import {
|
|
38
38
|
convertToModelMessages,
|
|
39
39
|
createUIMessageStream,
|
|
40
|
+
LanguageModel,
|
|
40
41
|
smoothStream,
|
|
41
42
|
stepCountIs,
|
|
42
43
|
streamText,
|
|
43
44
|
ToolSet,
|
|
44
45
|
type ChatTransport,
|
|
45
46
|
type UIMessage,
|
|
46
|
-
} from
|
|
47
|
+
} from "ai";
|
|
47
48
|
import {
|
|
48
49
|
ReactNode,
|
|
49
50
|
useCallback,
|
|
@@ -51,16 +52,16 @@ import {
|
|
|
51
52
|
useMemo,
|
|
52
53
|
useRef,
|
|
53
54
|
useState,
|
|
54
|
-
} from
|
|
55
|
-
import { useAuth } from
|
|
56
|
-
import { ChatIdContext } from
|
|
55
|
+
} from "react";
|
|
56
|
+
import { useAuth } from "../hooks/useAuth";
|
|
57
|
+
import { ChatIdContext } from "./ChatIdContext";
|
|
57
58
|
import {
|
|
58
59
|
ConnectionStatusProvider,
|
|
59
60
|
useConnectionStatusOptional,
|
|
60
|
-
} from
|
|
61
|
-
import { ElementsContext } from
|
|
62
|
-
import { ToolApprovalProvider } from
|
|
63
|
-
import { ToolExecutionProvider } from
|
|
61
|
+
} from "./ConnectionStatusContext";
|
|
62
|
+
import { ElementsContext } from "./contexts";
|
|
63
|
+
import { ToolApprovalProvider } from "./ToolApprovalContext";
|
|
64
|
+
import { ToolExecutionProvider } from "./ToolExecutionContext";
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
67
|
* Extracts executable tools from frontend tool definitions.
|
|
@@ -69,30 +70,30 @@ import { ToolExecutionProvider } from './ToolExecutionContext'
|
|
|
69
70
|
*/
|
|
70
71
|
function extractExecutableTools(
|
|
71
72
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
-
frontendTools: Record<string, FrontendTool<any, any>> | undefined
|
|
73
|
+
frontendTools: Record<string, FrontendTool<any, any>> | undefined,
|
|
73
74
|
): Record<
|
|
74
75
|
string,
|
|
75
76
|
{ execute?: (args: unknown, options?: unknown) => Promise<unknown> }
|
|
76
77
|
> {
|
|
77
|
-
if (!frontendTools) return {}
|
|
78
|
+
if (!frontendTools) return {};
|
|
78
79
|
|
|
79
80
|
return Object.fromEntries(
|
|
80
81
|
Object.entries(frontendTools).map(([name, tool]) => {
|
|
81
82
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
82
|
-
const toolDef = (tool as any).unstable_tool
|
|
83
|
+
const toolDef = (tool as any).unstable_tool;
|
|
83
84
|
return [
|
|
84
85
|
name,
|
|
85
86
|
{
|
|
86
87
|
execute: toolDef?.execute,
|
|
87
88
|
},
|
|
88
|
-
]
|
|
89
|
-
})
|
|
90
|
-
)
|
|
89
|
+
];
|
|
90
|
+
}),
|
|
91
|
+
);
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
export interface ElementsProviderProps {
|
|
94
|
-
children: ReactNode
|
|
95
|
-
config: ElementsConfig
|
|
95
|
+
children: ReactNode;
|
|
96
|
+
config: ElementsConfig;
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
const BASE_SYSTEM_PROMPT = `You are a helpful assistant that can answer questions and help with tasks.
|
|
@@ -103,26 +104,26 @@ Some tools have custom visual components that automatically render their results
|
|
|
103
104
|
For tools WITHOUT custom components, you should present the data clearly - either as plain text for simple results, or using the UI code block format for structured data like lists of items, categories, or dashboards.
|
|
104
105
|
|
|
105
106
|
UI Widget Guidelines:
|
|
106
|
-
IMPORTANT: Only render ONE generative UI widget (chart, dashboard, visualization) per response. Never render multiple widgets in a single message - this causes layout shifts during streaming and overwhelms the user. If you have multiple visualizations to show, render the most important one and explicitly offer to show others as follow-ups (e.g., "Would you like to see a breakdown by status as well?")
|
|
107
|
+
IMPORTANT: Only render ONE generative UI widget (chart, dashboard, visualization) per response. Never render multiple widgets in a single message - this causes layout shifts during streaming and overwhelms the user. If you have multiple visualizations to show, render the most important one and explicitly offer to show others as follow-ups (e.g., "Would you like to see a breakdown by status as well?").`;
|
|
107
108
|
|
|
108
109
|
function mergeInternalSystemPromptWith(
|
|
109
110
|
userSystemPrompt: string | undefined,
|
|
110
111
|
plugins: Plugin[],
|
|
111
|
-
toolsWithCustomComponents: string[]
|
|
112
|
+
toolsWithCustomComponents: string[],
|
|
112
113
|
) {
|
|
113
114
|
const customToolsSection =
|
|
114
115
|
toolsWithCustomComponents.length > 0
|
|
115
|
-
? `\n\nTools with custom visual components (DO NOT render UI widgets for these - they already display rich visuals):\n${toolsWithCustomComponents.map((t) => `- ${t}`).join(
|
|
116
|
-
:
|
|
116
|
+
? `\n\nTools with custom visual components (DO NOT render UI widgets for these - they already display rich visuals):\n${toolsWithCustomComponents.map((t) => `- ${t}`).join("\n")}`
|
|
117
|
+
: "";
|
|
117
118
|
|
|
118
119
|
return `
|
|
119
120
|
${BASE_SYSTEM_PROMPT}${customToolsSection}
|
|
120
121
|
|
|
121
122
|
User-provided System Prompt:
|
|
122
|
-
${userSystemPrompt ??
|
|
123
|
+
${userSystemPrompt ?? "None provided"}
|
|
123
124
|
|
|
124
125
|
Utilities:
|
|
125
|
-
${plugins.map((plugin) => `- ${plugin.language}: ${plugin.prompt}`).join(
|
|
126
|
+
${plugins.map((plugin) => `- ${plugin.language}: ${plugin.prompt}`).join("\n")}`;
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
/**
|
|
@@ -131,9 +132,9 @@ function mergeInternalSystemPromptWith(
|
|
|
131
132
|
*/
|
|
132
133
|
function cleanMessagesForModel(messages: UIMessage[]): UIMessage[] {
|
|
133
134
|
return messages.map((message) => {
|
|
134
|
-
const partsArray = message.parts
|
|
135
|
+
const partsArray = message.parts;
|
|
135
136
|
if (!Array.isArray(partsArray)) {
|
|
136
|
-
return message
|
|
137
|
+
return message;
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
// Process each part: strip providerOptions/providerMetadata and filter reasoning
|
|
@@ -141,15 +142,15 @@ function cleanMessagesForModel(messages: UIMessage[]): UIMessage[] {
|
|
|
141
142
|
const cleanedParts = partsArray.map((part: any) => {
|
|
142
143
|
// Strip providerOptions and providerMetadata from all remaining parts
|
|
143
144
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
144
|
-
const { callProviderMetadata: _, ...cleanPart } = part
|
|
145
|
-
return cleanPart
|
|
146
|
-
})
|
|
145
|
+
const { callProviderMetadata: _, ...cleanPart } = part;
|
|
146
|
+
return cleanPart;
|
|
147
|
+
});
|
|
147
148
|
|
|
148
149
|
return {
|
|
149
150
|
...message,
|
|
150
151
|
parts: cleanedParts,
|
|
151
|
-
}
|
|
152
|
-
})
|
|
152
|
+
};
|
|
153
|
+
});
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
/**
|
|
@@ -157,35 +158,35 @@ function cleanMessagesForModel(messages: UIMessage[]): UIMessage[] {
|
|
|
157
158
|
* Delegates to either WithHistory or WithoutHistory based on config.
|
|
158
159
|
*/
|
|
159
160
|
const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
160
|
-
const apiUrl = getApiUrl(config)
|
|
161
|
+
const apiUrl = getApiUrl(config);
|
|
161
162
|
const auth = useAuth({
|
|
162
163
|
auth: config.api,
|
|
163
164
|
projectSlug: config.projectSlug,
|
|
164
|
-
})
|
|
165
|
+
});
|
|
165
166
|
|
|
166
167
|
// Ref to access ensureValidHeaders in async transport without stale closures
|
|
167
|
-
const ensureValidHeadersRef = useRef(auth.ensureValidHeaders)
|
|
168
|
-
ensureValidHeadersRef.current = auth.ensureValidHeaders
|
|
169
|
-
const toolApproval = useToolApproval()
|
|
168
|
+
const ensureValidHeadersRef = useRef(auth.ensureValidHeaders);
|
|
169
|
+
ensureValidHeadersRef.current = auth.ensureValidHeaders;
|
|
170
|
+
const toolApproval = useToolApproval();
|
|
170
171
|
|
|
171
172
|
const [model, setModel] = useState<Model>(
|
|
172
|
-
config.model?.defaultModel ?? MODELS[0]
|
|
173
|
-
)
|
|
173
|
+
config.model?.defaultModel ?? MODELS[0],
|
|
174
|
+
);
|
|
174
175
|
const [isExpanded, setIsExpanded] = useState(
|
|
175
|
-
config.modal?.defaultExpanded ?? false
|
|
176
|
-
)
|
|
177
|
-
const [isOpen, setIsOpen] = useState(config.modal?.defaultOpen)
|
|
176
|
+
config.modal?.defaultExpanded ?? false,
|
|
177
|
+
);
|
|
178
|
+
const [isOpen, setIsOpen] = useState(config.modal?.defaultOpen);
|
|
178
179
|
|
|
179
|
-
const plugins = config.plugins ?? recommended
|
|
180
|
+
const plugins = config.plugins ?? recommended;
|
|
180
181
|
|
|
181
182
|
// Get list of tools that have custom components registered
|
|
182
|
-
const toolsWithCustomComponents = Object.keys(config.tools?.components ?? {})
|
|
183
|
+
const toolsWithCustomComponents = Object.keys(config.tools?.components ?? {});
|
|
183
184
|
|
|
184
185
|
const systemPrompt = mergeInternalSystemPromptWith(
|
|
185
186
|
config.systemPrompt,
|
|
186
187
|
plugins,
|
|
187
|
-
toolsWithCustomComponents
|
|
188
|
-
)
|
|
188
|
+
toolsWithCustomComponents,
|
|
189
|
+
);
|
|
189
190
|
|
|
190
191
|
// Initialize error tracking on mount
|
|
191
192
|
useEffect(() => {
|
|
@@ -193,15 +194,15 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
193
194
|
enabled: config.errorTracking?.enabled,
|
|
194
195
|
projectSlug: config.projectSlug,
|
|
195
196
|
variant: config.variant,
|
|
196
|
-
})
|
|
197
|
-
}, [])
|
|
197
|
+
});
|
|
198
|
+
}, []);
|
|
198
199
|
|
|
199
200
|
// Generate a stable chat ID for server-side persistence (when history is disabled)
|
|
200
201
|
// When history is enabled, the thread adapter manages chat IDs instead
|
|
201
|
-
const chatIdRef = useRef<string | null>(null)
|
|
202
|
+
const chatIdRef = useRef<string | null>(null);
|
|
202
203
|
|
|
203
204
|
// State to expose the current chat ID via context
|
|
204
|
-
const [currentChatId, setCurrentChatId] = useState<string | null>(null)
|
|
205
|
+
const [currentChatId, setCurrentChatId] = useState<string | null>(null);
|
|
205
206
|
|
|
206
207
|
const { data: mcpTools, mcpHeaders } = useMCPTools({
|
|
207
208
|
auth,
|
|
@@ -209,23 +210,23 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
209
210
|
environment: config.environment ?? {},
|
|
210
211
|
toolsToInclude: config.tools?.toolsToInclude,
|
|
211
212
|
gramEnvironment: config.gramEnvironment,
|
|
212
|
-
})
|
|
213
|
+
});
|
|
213
214
|
|
|
214
215
|
// Store approval helpers in ref so they can be used in async contexts
|
|
215
216
|
const approvalHelpersRef = useRef<ApprovalHelpers>({
|
|
216
217
|
requestApproval: toolApproval.requestApproval,
|
|
217
218
|
isToolApproved: toolApproval.isToolApproved,
|
|
218
219
|
whitelistTool: toolApproval.whitelistTool,
|
|
219
|
-
})
|
|
220
|
+
});
|
|
220
221
|
|
|
221
222
|
// Connection status for tracking network failures
|
|
222
|
-
const connectionStatus = useConnectionStatusOptional()
|
|
223
|
+
const connectionStatus = useConnectionStatusOptional();
|
|
223
224
|
|
|
224
225
|
approvalHelpersRef.current = {
|
|
225
226
|
requestApproval: toolApproval.requestApproval,
|
|
226
227
|
isToolApproved: toolApproval.isToolApproved,
|
|
227
228
|
whitelistTool: toolApproval.whitelistTool,
|
|
228
|
-
}
|
|
229
|
+
};
|
|
229
230
|
|
|
230
231
|
const getApprovalHelpers = useCallback((): ApprovalHelpers => {
|
|
231
232
|
return {
|
|
@@ -235,51 +236,51 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
235
236
|
approvalHelpersRef.current.isToolApproved(...args),
|
|
236
237
|
whitelistTool: (...args) =>
|
|
237
238
|
approvalHelpersRef.current.whitelistTool(...args),
|
|
238
|
-
}
|
|
239
|
-
}, [])
|
|
239
|
+
};
|
|
240
|
+
}, []);
|
|
240
241
|
|
|
241
242
|
// Set up frontend tool approval config for runtime checking
|
|
242
243
|
useEffect(() => {
|
|
243
244
|
if (config.tools?.toolsRequiringApproval) {
|
|
244
245
|
setFrontendToolApprovalConfig(
|
|
245
246
|
getApprovalHelpers(),
|
|
246
|
-
config.tools.toolsRequiringApproval
|
|
247
|
-
)
|
|
247
|
+
config.tools.toolsRequiringApproval,
|
|
248
|
+
);
|
|
248
249
|
}
|
|
249
250
|
return () => {
|
|
250
|
-
clearFrontendToolApprovalConfig()
|
|
251
|
-
}
|
|
252
|
-
}, [config.tools?.toolsRequiringApproval, getApprovalHelpers])
|
|
251
|
+
clearFrontendToolApprovalConfig();
|
|
252
|
+
};
|
|
253
|
+
}, [config.tools?.toolsRequiringApproval, getApprovalHelpers]);
|
|
253
254
|
|
|
254
255
|
// Ref to access runtime from within transport's sendMessages.
|
|
255
256
|
// This solves a circular dependency: transport needs runtime.thread.getModelContext(),
|
|
256
257
|
// but runtime is created using transport. The ref gets populated after runtime creation.
|
|
257
|
-
const runtimeRef = useRef<ReturnType<typeof useChatRuntime> | null>(null)
|
|
258
|
+
const runtimeRef = useRef<ReturnType<typeof useChatRuntime> | null>(null);
|
|
258
259
|
|
|
259
260
|
// Map to share local thread IDs to UUIDs between adapter and transport (for history mode)
|
|
260
|
-
const localIdToUuidMapRef = useRef(new Map<string, string>())
|
|
261
|
+
const localIdToUuidMapRef = useRef(new Map<string, string>());
|
|
261
262
|
|
|
262
263
|
// Ref to store the current thread's remoteId, synced from assistant-ui state.
|
|
263
264
|
// This is needed because the runtime object doesn't expose threadListItem.remoteId
|
|
264
265
|
// in a way that's accessible from the transport's sendMessages function.
|
|
265
|
-
const currentRemoteIdRef = useRef<string | null>(null)
|
|
266
|
+
const currentRemoteIdRef = useRef<string | null>(null);
|
|
266
267
|
|
|
267
268
|
// Create chat transport configuration
|
|
268
269
|
const transport = useMemo<ChatTransport<UIMessage>>(
|
|
269
270
|
() => ({
|
|
270
271
|
sendMessages: async ({ messages, abortSignal }) => {
|
|
271
|
-
const usingCustomModel = !!config.languageModel
|
|
272
|
+
const usingCustomModel = !!config.languageModel;
|
|
272
273
|
|
|
273
274
|
if (auth.isLoading) {
|
|
274
|
-
throw new Error(
|
|
275
|
+
throw new Error("Session is loading");
|
|
275
276
|
}
|
|
276
277
|
|
|
277
278
|
// Ensure the session token is still valid; refresh if expired
|
|
278
|
-
const validHeaders = await ensureValidHeadersRef.current()
|
|
279
|
+
const validHeaders = await ensureValidHeadersRef.current();
|
|
279
280
|
|
|
280
281
|
// Get chat ID - use the synced remoteId ref first (history mode),
|
|
281
282
|
// fall back to generated ID (non-history mode)
|
|
282
|
-
let chatId = currentRemoteIdRef.current
|
|
283
|
+
let chatId = currentRemoteIdRef.current;
|
|
283
284
|
|
|
284
285
|
// If we have a valid remoteId (not a local ID), use it directly
|
|
285
286
|
if (chatId && !isLocalThreadId(chatId)) {
|
|
@@ -287,21 +288,21 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
287
288
|
} else if (isLocalThreadId(chatId) || !chatId) {
|
|
288
289
|
// For local thread IDs or no ID, check/generate UUID mapping
|
|
289
290
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
290
|
-
const runtimeAny = runtimeRef.current as any
|
|
291
|
-
const threadsState = runtimeAny?.threads?.getState?.()
|
|
291
|
+
const runtimeAny = runtimeRef.current as any;
|
|
292
|
+
const threadsState = runtimeAny?.threads?.getState?.();
|
|
292
293
|
const localThreadId = (threadsState?.mainThreadId ??
|
|
293
|
-
threadsState?.threadIds?.[0]) as string | undefined
|
|
294
|
+
threadsState?.threadIds?.[0]) as string | undefined;
|
|
294
295
|
|
|
295
|
-
const lookupKey = chatId ?? localThreadId
|
|
296
|
+
const lookupKey = chatId ?? localThreadId;
|
|
296
297
|
if (lookupKey) {
|
|
297
|
-
const existingUuid = localIdToUuidMapRef.current.get(lookupKey)
|
|
298
|
+
const existingUuid = localIdToUuidMapRef.current.get(lookupKey);
|
|
298
299
|
if (existingUuid) {
|
|
299
|
-
chatId = existingUuid
|
|
300
|
+
chatId = existingUuid;
|
|
300
301
|
} else {
|
|
301
302
|
// Generate a new UUID and store the mapping
|
|
302
|
-
const newUuid = crypto.randomUUID()
|
|
303
|
-
localIdToUuidMapRef.current.set(lookupKey, newUuid)
|
|
304
|
-
chatId = newUuid
|
|
303
|
+
const newUuid = crypto.randomUUID();
|
|
304
|
+
localIdToUuidMapRef.current.set(lookupKey, newUuid);
|
|
305
|
+
chatId = newUuid;
|
|
305
306
|
}
|
|
306
307
|
}
|
|
307
308
|
}
|
|
@@ -309,40 +310,40 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
309
310
|
if (!chatId) {
|
|
310
311
|
// Non-history mode fallback - use stable chatIdRef
|
|
311
312
|
if (!chatIdRef.current) {
|
|
312
|
-
chatIdRef.current = crypto.randomUUID()
|
|
313
|
+
chatIdRef.current = crypto.randomUUID();
|
|
313
314
|
}
|
|
314
|
-
chatId = chatIdRef.current
|
|
315
|
+
chatId = chatIdRef.current;
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
// Mutate the shared headers object so the MCP transport picks up the
|
|
318
319
|
// chat ID on subsequent tool call requests.
|
|
319
320
|
if (chatId) {
|
|
320
|
-
mcpHeaders[
|
|
321
|
+
mcpHeaders["Gram-Chat-ID"] = chatId;
|
|
321
322
|
// Update the context state so consumers can access the current chat ID
|
|
322
|
-
setCurrentChatId(chatId)
|
|
323
|
+
setCurrentChatId(chatId);
|
|
323
324
|
}
|
|
324
325
|
|
|
325
|
-
const context = runtimeRef.current?.thread.getModelContext()
|
|
326
|
+
const context = runtimeRef.current?.thread.getModelContext();
|
|
326
327
|
const frontendTools = toAISDKTools(
|
|
327
|
-
getEnabledTools(context?.tools ?? {})
|
|
328
|
-
)
|
|
328
|
+
getEnabledTools(context?.tools ?? {}),
|
|
329
|
+
);
|
|
329
330
|
|
|
330
331
|
// Include Gram-Chat-ID header for chat persistence and Gram-Environment for environment selection
|
|
331
332
|
const headersWithChatId = {
|
|
332
333
|
...validHeaders,
|
|
333
|
-
|
|
334
|
-
|
|
334
|
+
"Gram-Chat-ID": chatId,
|
|
335
|
+
"X-Gram-Source": "elements",
|
|
335
336
|
...config.api?.headers, // We do this after X-Gram-Source so the playground can override it
|
|
336
337
|
...(config.gramEnvironment && {
|
|
337
|
-
|
|
338
|
+
"Gram-Environment": config.gramEnvironment,
|
|
338
339
|
}),
|
|
339
|
-
}
|
|
340
|
+
};
|
|
340
341
|
|
|
341
342
|
// Update MCP headers with the (possibly refreshed) session token
|
|
342
343
|
// so mid-stream MCP tool calls use the fresh token
|
|
343
|
-
const freshSession = validHeaders[
|
|
344
|
+
const freshSession = validHeaders["Gram-Chat-Session"];
|
|
344
345
|
if (freshSession) {
|
|
345
|
-
mcpHeaders[
|
|
346
|
+
mcpHeaders["Gram-Chat-Session"] = freshSession;
|
|
346
347
|
}
|
|
347
348
|
|
|
348
349
|
// Create OpenRouter model (only needed when not using custom model)
|
|
@@ -350,43 +351,43 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
350
351
|
? null
|
|
351
352
|
: createOpenRouter({
|
|
352
353
|
baseURL: apiUrl,
|
|
353
|
-
apiKey:
|
|
354
|
+
apiKey: "unused, but must be set",
|
|
354
355
|
headers: headersWithChatId,
|
|
355
|
-
})
|
|
356
|
+
});
|
|
356
357
|
|
|
357
358
|
if (config.languageModel) {
|
|
358
|
-
console.log(
|
|
359
|
+
console.log("Using custom language model", config.languageModel);
|
|
359
360
|
}
|
|
360
361
|
|
|
361
362
|
// Combine tools - MCP tools only available when not using custom model
|
|
362
363
|
const combinedTools: ToolSet = {
|
|
363
364
|
...mcpTools,
|
|
364
365
|
...convertFrontendToolsToAISDKTools(frontendTools),
|
|
365
|
-
} as ToolSet
|
|
366
|
+
} as ToolSet;
|
|
366
367
|
|
|
367
368
|
// Wrap tools that require approval
|
|
368
369
|
const tools = wrapToolsWithApproval(
|
|
369
370
|
combinedTools,
|
|
370
371
|
config.tools?.toolsRequiringApproval,
|
|
371
|
-
getApprovalHelpers()
|
|
372
|
-
)
|
|
372
|
+
getApprovalHelpers(),
|
|
373
|
+
);
|
|
373
374
|
|
|
374
375
|
// Stream the response
|
|
375
376
|
const modelToUse = config.languageModel
|
|
376
377
|
? config.languageModel
|
|
377
|
-
: openRouterModel!.chat(model)
|
|
378
|
+
: (openRouterModel!.chat(model) as LanguageModel);
|
|
378
379
|
|
|
379
380
|
try {
|
|
380
381
|
// This works around AI SDK bug where these fields cause validation failures
|
|
381
|
-
const cleanedMessages = cleanMessagesForModel(messages)
|
|
382
|
+
const cleanedMessages = cleanMessagesForModel(messages);
|
|
382
383
|
// Filter out system messages from the UI state — the system prompt
|
|
383
384
|
// is already provided via the `system:` parameter to streamText().
|
|
384
385
|
// Without this, loaded chat history includes the system message which
|
|
385
386
|
// gets sent alongside the `system:` param, causing duplication.
|
|
386
387
|
const nonSystemMessages = cleanedMessages.filter(
|
|
387
|
-
(m) => m.role !==
|
|
388
|
-
)
|
|
389
|
-
const modelMessages = convertToModelMessages(nonSystemMessages)
|
|
388
|
+
(m) => m.role !== "system",
|
|
389
|
+
);
|
|
390
|
+
const modelMessages = convertToModelMessages(nonSystemMessages);
|
|
390
391
|
|
|
391
392
|
const result = streamText({
|
|
392
393
|
system: systemPrompt,
|
|
@@ -397,59 +398,59 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
397
398
|
experimental_transform: smoothStream({ delayInMs: 15 }),
|
|
398
399
|
abortSignal,
|
|
399
400
|
onError: ({ error }) => {
|
|
400
|
-
console.error(
|
|
401
|
-
trackError(error, { source:
|
|
401
|
+
console.error("Stream error in onError callback:", error);
|
|
402
|
+
trackError(error, { source: "streaming" });
|
|
402
403
|
|
|
403
404
|
// Check if this is a network/connection error
|
|
404
405
|
const isNetworkError =
|
|
405
406
|
error instanceof TypeError ||
|
|
406
407
|
(error instanceof Error &&
|
|
407
|
-
(error.message.includes(
|
|
408
|
-
error.message.includes(
|
|
409
|
-
error.message.includes(
|
|
410
|
-
error.message.includes(
|
|
411
|
-
error.message.includes(
|
|
412
|
-
error.message.includes(
|
|
408
|
+
(error.message.includes("fetch") ||
|
|
409
|
+
error.message.includes("network") ||
|
|
410
|
+
error.message.includes("Failed to fetch") ||
|
|
411
|
+
error.message.includes("NetworkError") ||
|
|
412
|
+
error.message.includes("ECONNREFUSED") ||
|
|
413
|
+
error.message.includes("ETIMEDOUT")));
|
|
413
414
|
|
|
414
415
|
if (isNetworkError) {
|
|
415
|
-
connectionStatus?.markDisconnected()
|
|
416
|
+
connectionStatus?.markDisconnected();
|
|
416
417
|
}
|
|
417
418
|
},
|
|
418
|
-
})
|
|
419
|
+
});
|
|
419
420
|
|
|
420
421
|
// Mark as connected when stream starts successfully
|
|
421
|
-
connectionStatus?.markConnected()
|
|
422
|
+
connectionStatus?.markConnected();
|
|
422
423
|
|
|
423
424
|
// This weird construction is necessary to get errors to propagate properly to assistant-ui
|
|
424
425
|
return createUIMessageStream({
|
|
425
426
|
execute: ({ writer }) => {
|
|
426
|
-
writer.merge(result.toUIMessageStream())
|
|
427
|
+
writer.merge(result.toUIMessageStream());
|
|
427
428
|
},
|
|
428
|
-
})
|
|
429
|
+
});
|
|
429
430
|
} catch (error) {
|
|
430
|
-
console.error(
|
|
431
|
-
trackError(error, { source:
|
|
431
|
+
console.error("Error creating stream:", error);
|
|
432
|
+
trackError(error, { source: "stream-creation" });
|
|
432
433
|
|
|
433
434
|
// Check if this is a network/connection error
|
|
434
435
|
const isNetworkError =
|
|
435
436
|
error instanceof TypeError ||
|
|
436
437
|
(error instanceof Error &&
|
|
437
|
-
(error.message.includes(
|
|
438
|
-
error.message.includes(
|
|
439
|
-
error.message.includes(
|
|
440
|
-
error.message.includes(
|
|
441
|
-
error.message.includes(
|
|
442
|
-
error.message.includes(
|
|
438
|
+
(error.message.includes("fetch") ||
|
|
439
|
+
error.message.includes("network") ||
|
|
440
|
+
error.message.includes("Failed to fetch") ||
|
|
441
|
+
error.message.includes("NetworkError") ||
|
|
442
|
+
error.message.includes("ECONNREFUSED") ||
|
|
443
|
+
error.message.includes("ETIMEDOUT")));
|
|
443
444
|
|
|
444
445
|
if (isNetworkError) {
|
|
445
|
-
connectionStatus?.markDisconnected()
|
|
446
|
+
connectionStatus?.markDisconnected();
|
|
446
447
|
}
|
|
447
448
|
|
|
448
|
-
throw error
|
|
449
|
+
throw error;
|
|
449
450
|
}
|
|
450
451
|
},
|
|
451
452
|
reconnectToStream: async () => {
|
|
452
|
-
throw new Error(
|
|
453
|
+
throw new Error("Stream reconnection not supported");
|
|
453
454
|
},
|
|
454
455
|
}),
|
|
455
456
|
[
|
|
@@ -462,10 +463,10 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
462
463
|
apiUrl,
|
|
463
464
|
auth.isLoading,
|
|
464
465
|
connectionStatus,
|
|
465
|
-
]
|
|
466
|
-
)
|
|
466
|
+
],
|
|
467
|
+
);
|
|
467
468
|
|
|
468
|
-
const historyEnabled = config.history?.enabled ?? false
|
|
469
|
+
const historyEnabled = config.history?.enabled ?? false;
|
|
469
470
|
|
|
470
471
|
// Shared context value for ElementsContext
|
|
471
472
|
const contextValue = useMemo(
|
|
@@ -480,10 +481,10 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
480
481
|
plugins,
|
|
481
482
|
mcpTools,
|
|
482
483
|
}),
|
|
483
|
-
[config, model, isExpanded, isOpen, plugins, mcpTools]
|
|
484
|
-
)
|
|
484
|
+
[config, model, isExpanded, isOpen, plugins, mcpTools],
|
|
485
|
+
);
|
|
485
486
|
|
|
486
|
-
const frontendTools = config.tools?.frontendTools ?? {}
|
|
487
|
+
const frontendTools = config.tools?.frontendTools ?? {};
|
|
487
488
|
|
|
488
489
|
// Create combined executable tools for direct tool execution (ActionButton)
|
|
489
490
|
// Uses a simplified type that focuses on the execute function
|
|
@@ -491,17 +492,17 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
491
492
|
string,
|
|
492
493
|
| { execute?: (args: unknown, options?: unknown) => Promise<unknown> }
|
|
493
494
|
| undefined
|
|
494
|
-
|
|
495
|
+
>;
|
|
495
496
|
const executableTools = useMemo<ExecutableToolSet>(() => {
|
|
496
497
|
const extractedFrontendTools = extractExecutableTools(
|
|
497
|
-
config.tools?.frontendTools
|
|
498
|
-
)
|
|
498
|
+
config.tools?.frontendTools,
|
|
499
|
+
);
|
|
499
500
|
// MCP tools and extracted frontend tools both have execute functions
|
|
500
501
|
return {
|
|
501
502
|
...mcpTools,
|
|
502
503
|
...extractedFrontendTools,
|
|
503
|
-
} as ExecutableToolSet
|
|
504
|
-
}, [mcpTools, config.tools?.frontendTools])
|
|
504
|
+
} as ExecutableToolSet;
|
|
505
|
+
}, [mcpTools, config.tools?.frontendTools]);
|
|
505
506
|
|
|
506
507
|
// Render the appropriate runtime provider based on history config.
|
|
507
508
|
// We use separate components to avoid conditional hook calls.
|
|
@@ -522,7 +523,7 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
522
523
|
>
|
|
523
524
|
{children}
|
|
524
525
|
</ElementsProviderWithHistory>
|
|
525
|
-
)
|
|
526
|
+
);
|
|
526
527
|
}
|
|
527
528
|
|
|
528
529
|
return (
|
|
@@ -536,31 +537,31 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
536
537
|
>
|
|
537
538
|
{children}
|
|
538
539
|
</ElementsProviderWithoutHistory>
|
|
539
|
-
)
|
|
540
|
-
}
|
|
540
|
+
);
|
|
541
|
+
};
|
|
541
542
|
|
|
542
543
|
// Shared type for executable tools
|
|
543
544
|
type ExecutableToolSet = Record<
|
|
544
545
|
string,
|
|
545
546
|
| { execute?: (args: unknown, options?: unknown) => Promise<unknown> }
|
|
546
547
|
| undefined
|
|
547
|
-
|
|
548
|
+
>;
|
|
548
549
|
|
|
549
550
|
// Separate component for history-enabled mode to avoid conditional hook calls
|
|
550
551
|
interface ElementsProviderWithHistoryProps {
|
|
551
|
-
children: ReactNode
|
|
552
|
-
transport: ChatTransport<UIMessage
|
|
553
|
-
apiUrl: string
|
|
554
|
-
headers: Record<string, string
|
|
555
|
-
contextValue: React.ContextType<typeof ElementsContext
|
|
556
|
-
runtimeRef: React.RefObject<ReturnType<typeof useChatRuntime> | null
|
|
552
|
+
children: ReactNode;
|
|
553
|
+
transport: ChatTransport<UIMessage>;
|
|
554
|
+
apiUrl: string;
|
|
555
|
+
headers: Record<string, string>;
|
|
556
|
+
contextValue: React.ContextType<typeof ElementsContext>;
|
|
557
|
+
runtimeRef: React.RefObject<ReturnType<typeof useChatRuntime> | null>;
|
|
557
558
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
558
|
-
frontendTools: Record<string, AssistantTool | FrontendTool<any, any
|
|
559
|
-
localIdToUuidMap: Map<string, string
|
|
560
|
-
currentRemoteIdRef: React.RefObject<string | null
|
|
561
|
-
executableTools: ExecutableToolSet
|
|
562
|
-
currentChatId: string | null
|
|
563
|
-
setCurrentChatId: (chatId: string | null) => void
|
|
559
|
+
frontendTools: Record<string, AssistantTool | FrontendTool<any, any>>;
|
|
560
|
+
localIdToUuidMap: Map<string, string>;
|
|
561
|
+
currentRemoteIdRef: React.RefObject<string | null>;
|
|
562
|
+
executableTools: ExecutableToolSet;
|
|
563
|
+
currentChatId: string | null;
|
|
564
|
+
setCurrentChatId: (chatId: string | null) => void;
|
|
564
565
|
}
|
|
565
566
|
|
|
566
567
|
/**
|
|
@@ -571,18 +572,18 @@ const ThreadIdSync = ({
|
|
|
571
572
|
remoteIdRef,
|
|
572
573
|
onChatIdChange,
|
|
573
574
|
}: {
|
|
574
|
-
remoteIdRef: React.RefObject<string | null
|
|
575
|
-
onChatIdChange: (chatId: string | null) => void
|
|
575
|
+
remoteIdRef: React.RefObject<string | null>;
|
|
576
|
+
onChatIdChange: (chatId: string | null) => void;
|
|
576
577
|
}) => {
|
|
577
578
|
const remoteId = useAssistantState(
|
|
578
|
-
({ threadListItem }) => threadListItem.remoteId ?? null
|
|
579
|
-
)
|
|
579
|
+
({ threadListItem }) => threadListItem.remoteId ?? null,
|
|
580
|
+
);
|
|
580
581
|
useEffect(() => {
|
|
581
|
-
remoteIdRef.current = remoteId
|
|
582
|
-
onChatIdChange(remoteId)
|
|
583
|
-
}, [remoteId, remoteIdRef, onChatIdChange])
|
|
584
|
-
return null
|
|
585
|
-
}
|
|
582
|
+
remoteIdRef.current = remoteId;
|
|
583
|
+
onChatIdChange(remoteId);
|
|
584
|
+
}, [remoteId, remoteIdRef, onChatIdChange]);
|
|
585
|
+
return null;
|
|
586
|
+
};
|
|
586
587
|
|
|
587
588
|
const ElementsProviderWithHistory = ({
|
|
588
589
|
children,
|
|
@@ -602,43 +603,43 @@ const ElementsProviderWithHistory = ({
|
|
|
602
603
|
apiUrl,
|
|
603
604
|
headers,
|
|
604
605
|
localIdToUuidMap,
|
|
605
|
-
})
|
|
606
|
-
const initialThreadId = contextValue?.config.history?.initialThreadId
|
|
606
|
+
});
|
|
607
|
+
const initialThreadId = contextValue?.config.history?.initialThreadId;
|
|
607
608
|
|
|
608
609
|
// Hook factory for creating the base chat runtime
|
|
609
610
|
const useChatRuntimeHook = useCallback(() => {
|
|
610
|
-
return useChatRuntime({ transport })
|
|
611
|
-
}, [transport])
|
|
611
|
+
return useChatRuntime({ transport });
|
|
612
|
+
}, [transport]);
|
|
612
613
|
|
|
613
614
|
const runtime = useRemoteThreadListRuntime({
|
|
614
615
|
adapter: threadListAdapter,
|
|
615
616
|
runtimeHook: useChatRuntimeHook,
|
|
616
|
-
})
|
|
617
|
+
});
|
|
617
618
|
|
|
618
619
|
// Populate runtimeRef so transport can access thread context
|
|
619
620
|
useEffect(() => {
|
|
620
|
-
runtimeRef.current = runtime as ReturnType<typeof useChatRuntime
|
|
621
|
-
}, [runtime, runtimeRef])
|
|
621
|
+
runtimeRef.current = runtime as ReturnType<typeof useChatRuntime>;
|
|
622
|
+
}, [runtime, runtimeRef]);
|
|
622
623
|
|
|
623
624
|
// Switch to initial thread if provided (for shared chat URLs)
|
|
624
|
-
const initialThreadSwitched = useRef(false)
|
|
625
|
+
const initialThreadSwitched = useRef(false);
|
|
625
626
|
useEffect(() => {
|
|
626
627
|
if (initialThreadId && !initialThreadSwitched.current) {
|
|
627
|
-
initialThreadSwitched.current = true
|
|
628
|
+
initialThreadSwitched.current = true;
|
|
628
629
|
// Use setTimeout to ensure runtime is fully initialized
|
|
629
630
|
const timeoutId = setTimeout(() => {
|
|
630
631
|
runtime.threads.switchToThread(initialThreadId).catch((error) => {
|
|
631
|
-
console.error(
|
|
632
|
-
})
|
|
633
|
-
}, 100)
|
|
634
|
-
return () => clearTimeout(timeoutId)
|
|
632
|
+
console.error("Failed to switch to initial thread:", error);
|
|
633
|
+
});
|
|
634
|
+
}, 100);
|
|
635
|
+
return () => clearTimeout(timeoutId);
|
|
635
636
|
}
|
|
636
|
-
}, [initialThreadId, runtime])
|
|
637
|
+
}, [initialThreadId, runtime]);
|
|
637
638
|
|
|
638
639
|
// Get the Provider from our adapter to wrap the content
|
|
639
640
|
const HistoryProvider =
|
|
640
641
|
threadListAdapter.unstable_Provider ??
|
|
641
|
-
(({ children }: { children: React.ReactNode }) => <>{children}</>)
|
|
642
|
+
(({ children }: { children: React.ReactNode }) => <>{children}</>);
|
|
642
643
|
|
|
643
644
|
return (
|
|
644
645
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
@@ -653,9 +654,9 @@ const ElementsProviderWithHistory = ({
|
|
|
653
654
|
<div
|
|
654
655
|
className={cn(
|
|
655
656
|
ROOT_SELECTOR,
|
|
656
|
-
(contextValue?.config.variant ===
|
|
657
|
-
contextValue?.config.variant ===
|
|
658
|
-
|
|
657
|
+
(contextValue?.config.variant === "standalone" ||
|
|
658
|
+
contextValue?.config.variant === "sidecar") &&
|
|
659
|
+
"h-full",
|
|
659
660
|
)}
|
|
660
661
|
>
|
|
661
662
|
{children}
|
|
@@ -666,19 +667,19 @@ const ElementsProviderWithHistory = ({
|
|
|
666
667
|
</ChatIdContext.Provider>
|
|
667
668
|
</HistoryProvider>
|
|
668
669
|
</AssistantRuntimeProvider>
|
|
669
|
-
)
|
|
670
|
-
}
|
|
670
|
+
);
|
|
671
|
+
};
|
|
671
672
|
|
|
672
673
|
// Separate component for non-history mode to avoid conditional hook calls
|
|
673
674
|
interface ElementsProviderWithoutHistoryProps {
|
|
674
|
-
children: ReactNode
|
|
675
|
-
transport: ChatTransport<UIMessage
|
|
676
|
-
contextValue: React.ContextType<typeof ElementsContext
|
|
677
|
-
runtimeRef: React.RefObject<ReturnType<typeof useChatRuntime> | null
|
|
675
|
+
children: ReactNode;
|
|
676
|
+
transport: ChatTransport<UIMessage>;
|
|
677
|
+
contextValue: React.ContextType<typeof ElementsContext>;
|
|
678
|
+
runtimeRef: React.RefObject<ReturnType<typeof useChatRuntime> | null>;
|
|
678
679
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
679
|
-
frontendTools: Record<string, AssistantTool | FrontendTool<any, any
|
|
680
|
-
executableTools: ExecutableToolSet
|
|
681
|
-
currentChatId: string | null
|
|
680
|
+
frontendTools: Record<string, AssistantTool | FrontendTool<any, any>>;
|
|
681
|
+
executableTools: ExecutableToolSet;
|
|
682
|
+
currentChatId: string | null;
|
|
682
683
|
}
|
|
683
684
|
|
|
684
685
|
const ElementsProviderWithoutHistory = ({
|
|
@@ -690,12 +691,12 @@ const ElementsProviderWithoutHistory = ({
|
|
|
690
691
|
executableTools,
|
|
691
692
|
currentChatId,
|
|
692
693
|
}: ElementsProviderWithoutHistoryProps) => {
|
|
693
|
-
const runtime = useChatRuntime({ transport })
|
|
694
|
+
const runtime = useChatRuntime({ transport });
|
|
694
695
|
|
|
695
696
|
// Populate runtimeRef so transport can access thread context
|
|
696
697
|
useEffect(() => {
|
|
697
|
-
runtimeRef.current = runtime
|
|
698
|
-
}, [runtime, runtimeRef])
|
|
698
|
+
runtimeRef.current = runtime;
|
|
699
|
+
}, [runtime, runtimeRef]);
|
|
699
700
|
|
|
700
701
|
return (
|
|
701
702
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
@@ -705,9 +706,9 @@ const ElementsProviderWithoutHistory = ({
|
|
|
705
706
|
<div
|
|
706
707
|
className={cn(
|
|
707
708
|
ROOT_SELECTOR,
|
|
708
|
-
(contextValue?.config.variant ===
|
|
709
|
-
contextValue?.config.variant ===
|
|
710
|
-
|
|
709
|
+
(contextValue?.config.variant === "standalone" ||
|
|
710
|
+
contextValue?.config.variant === "sidecar") &&
|
|
711
|
+
"h-full",
|
|
711
712
|
)}
|
|
712
713
|
>
|
|
713
714
|
{children}
|
|
@@ -717,10 +718,10 @@ const ElementsProviderWithoutHistory = ({
|
|
|
717
718
|
</ElementsContext.Provider>
|
|
718
719
|
</ChatIdContext.Provider>
|
|
719
720
|
</AssistantRuntimeProvider>
|
|
720
|
-
)
|
|
721
|
-
}
|
|
721
|
+
);
|
|
722
|
+
};
|
|
722
723
|
|
|
723
|
-
const queryClient = new QueryClient()
|
|
724
|
+
const queryClient = new QueryClient();
|
|
724
725
|
|
|
725
726
|
export const ElementsProvider = (props: ElementsProviderProps) => {
|
|
726
727
|
return (
|
|
@@ -731,5 +732,5 @@ export const ElementsProvider = (props: ElementsProviderProps) => {
|
|
|
731
732
|
</ToolApprovalProvider>
|
|
732
733
|
</ConnectionStatusProvider>
|
|
733
734
|
</QueryClientProvider>
|
|
734
|
-
)
|
|
735
|
-
}
|
|
735
|
+
);
|
|
736
|
+
};
|