@datalayer/agent-runtimes 1.0.4 → 1.0.6
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 +182 -1
- package/lib/AgentNode.d.ts +3 -0
- package/lib/AgentNode.js +676 -0
- package/lib/App.js +1 -1
- package/lib/agent-node/themeStore.d.ts +3 -0
- package/lib/agent-node/themeStore.js +156 -0
- package/lib/agent-node-main.d.ts +1 -0
- package/lib/agent-node-main.js +14 -0
- package/lib/agents/AgentDetails.d.ts +22 -1
- package/lib/agents/AgentDetails.js +34 -47
- package/lib/api/index.d.ts +0 -1
- package/lib/api/index.js +4 -2
- package/lib/chat/Chat.d.ts +5 -106
- package/lib/chat/Chat.js +20 -14
- package/lib/chat/ChatFloating.d.ts +7 -140
- package/lib/chat/ChatFloating.js +3 -3
- package/lib/chat/ChatPopupStandalone.d.ts +8 -47
- package/lib/chat/ChatPopupStandalone.js +3 -3
- package/lib/chat/ChatSidebar.d.ts +4 -69
- package/lib/chat/ChatSidebar.js +83 -51
- package/lib/chat/ChatStandalone.d.ts +4 -54
- package/lib/chat/ChatStandalone.js +3 -3
- package/lib/chat/base/ChatBase.js +1414 -174
- package/lib/chat/display/FloatingBrandButton.js +8 -1
- package/lib/chat/header/ChatHeader.d.ts +3 -1
- package/lib/chat/header/ChatHeader.js +15 -12
- package/lib/chat/header/ChatHeaderBase.d.ts +30 -5
- package/lib/chat/header/ChatHeaderBase.js +41 -16
- package/lib/chat/indicators/McpStatusIndicator.d.ts +7 -4
- package/lib/chat/indicators/McpStatusIndicator.js +7 -32
- package/lib/chat/indicators/SandboxStatusIndicator.d.ts +4 -1
- package/lib/chat/indicators/SandboxStatusIndicator.js +91 -56
- package/lib/chat/indicators/SkillsStatusIndicator.d.ts +7 -0
- package/lib/chat/indicators/SkillsStatusIndicator.js +88 -0
- package/lib/chat/indicators/index.d.ts +1 -0
- package/lib/chat/indicators/index.js +1 -0
- package/lib/chat/messages/ChatMessageList.d.ts +1 -1
- package/lib/chat/messages/ChatMessageList.js +154 -114
- package/lib/chat/messages/ChatMessages.js +6 -2
- package/lib/chat/prompt/InputFooter.d.ts +21 -6
- package/lib/chat/prompt/InputFooter.js +76 -20
- package/lib/chat/prompt/InputPrompt.d.ts +5 -1
- package/lib/chat/prompt/InputPrompt.js +4 -4
- package/lib/chat/prompt/InputPromptFooter.d.ts +3 -1
- package/lib/chat/prompt/InputPromptFooter.js +3 -3
- package/lib/chat/prompt/InputPromptLexical.d.ts +3 -1
- package/lib/chat/prompt/InputPromptLexical.js +12 -5
- package/lib/chat/prompt/InputPromptText.d.ts +3 -1
- package/lib/chat/prompt/InputPromptText.js +2 -2
- package/lib/chat/tools/ToolApprovalBanner.js +1 -1
- package/lib/chat/tools/ToolCallDisplay.d.ts +3 -1
- package/lib/chat/tools/ToolCallDisplay.js +2 -2
- package/lib/chat/usage/TokenUsageBar.js +20 -2
- package/lib/client/AgentRuntimesClientContext.d.ts +53 -0
- package/lib/client/AgentRuntimesClientContext.js +55 -0
- package/lib/client/AgentsMixin.d.ts +0 -18
- package/lib/client/AgentsMixin.js +20 -30
- package/lib/client/IAgentRuntimesClient.d.ts +215 -0
- package/lib/client/IAgentRuntimesClient.js +5 -0
- package/lib/client/SdkAgentRuntimesClient.d.ts +151 -0
- package/lib/client/SdkAgentRuntimesClient.js +134 -0
- package/lib/client/index.d.ts +4 -1
- package/lib/client/index.js +3 -1
- package/lib/components/NotificationEventCard.js +5 -1
- package/lib/config/AgentConfiguration.d.ts +22 -0
- package/lib/config/AgentConfiguration.js +319 -64
- package/lib/context/ContextDistribution.d.ts +3 -1
- package/lib/context/ContextDistribution.js +8 -27
- package/lib/context/ContextInspector.d.ts +3 -1
- package/lib/context/ContextInspector.js +19 -67
- package/lib/context/ContextPanel.d.ts +3 -1
- package/lib/context/ContextPanel.js +104 -64
- package/lib/context/ContextUsage.d.ts +3 -1
- package/lib/context/ContextUsage.js +3 -3
- package/lib/context/CostTracker.d.ts +9 -3
- package/lib/context/CostTracker.js +26 -47
- package/lib/context/CostUsageChart.d.ts +12 -0
- package/lib/context/CostUsageChart.js +378 -0
- package/lib/context/GraphFlowChart.d.ts +16 -0
- package/lib/context/GraphFlowChart.js +182 -0
- package/lib/context/TokenUsageChart.d.ts +8 -1
- package/lib/context/TokenUsageChart.js +349 -211
- package/lib/context/TurnGraphChart.d.ts +39 -0
- package/lib/context/TurnGraphChart.js +538 -0
- package/lib/context/otelWsPool.d.ts +20 -0
- package/lib/context/otelWsPool.js +69 -0
- package/lib/examples/A2UiComponentGalleryExample.d.ts +0 -17
- package/lib/examples/A2UiComponentGalleryExample.js +315 -522
- package/lib/examples/A2UiContactCardExample.d.ts +0 -18
- package/lib/examples/A2UiContactCardExample.js +154 -411
- package/lib/examples/A2UiRestaurantExample.d.ts +0 -30
- package/lib/examples/A2UiRestaurantExample.js +114 -212
- package/lib/examples/A2UiViewerExample.d.ts +0 -18
- package/lib/examples/A2UiViewerExample.js +283 -532
- package/lib/examples/AgUiBackendToolRenderingExample.js +1 -1
- package/lib/examples/AgUiHaikuGenUiExample.d.ts +1 -1
- package/lib/examples/AgUiHaikuGenUiExample.js +1 -1
- package/lib/examples/AgUiSharedStateExample.js +2 -1
- package/lib/examples/AgentCheckpointsExample.js +14 -28
- package/lib/examples/AgentCodemodeExample.d.ts +4 -6
- package/lib/examples/AgentCodemodeExample.js +603 -169
- package/lib/examples/AgentEvalsExample.js +339 -53
- package/lib/examples/AgentGuardrailsExample.js +383 -66
- package/lib/examples/AgentHooksExample.d.ts +3 -0
- package/lib/examples/AgentHooksExample.js +122 -0
- package/lib/examples/AgentInferenceProviderExample.d.ts +3 -0
- package/lib/examples/AgentInferenceProviderExample.js +329 -0
- package/lib/examples/AgentMCPExample.d.ts +3 -0
- package/lib/examples/AgentMCPExample.js +481 -0
- package/lib/examples/AgentMemoryExample.d.ts +1 -2
- package/lib/examples/AgentMemoryExample.js +78 -33
- package/lib/examples/AgentMonitoringExample.js +261 -200
- package/lib/examples/AgentNotificationsExample.d.ts +1 -2
- package/lib/examples/AgentNotificationsExample.js +114 -33
- package/lib/examples/AgentOtelExample.js +32 -42
- package/lib/examples/AgentOutputsExample.d.ts +11 -6
- package/lib/examples/AgentOutputsExample.js +433 -81
- package/lib/examples/AgentParametersExample.d.ts +3 -0
- package/lib/examples/AgentParametersExample.js +248 -0
- package/lib/examples/AgentSandboxExample.d.ts +3 -3
- package/lib/examples/AgentSandboxExample.js +74 -45
- package/lib/examples/AgentSkillsExample.js +95 -103
- package/lib/examples/AgentSubagentsExample.d.ts +14 -0
- package/lib/examples/AgentSubagentsExample.js +228 -0
- package/lib/examples/AgentToolApprovalsExample.js +49 -561
- package/lib/examples/AgentTriggersExample.js +823 -569
- package/lib/examples/{AgentspecExample.d.ts → AgentspecsExample.d.ts} +2 -2
- package/lib/examples/AgentspecsExample.js +1096 -0
- package/lib/examples/ChatCustomExample.js +16 -28
- package/lib/examples/ChatExample.js +13 -29
- package/lib/examples/CopilotKitLexicalExample.js +2 -1
- package/lib/examples/CopilotKitNotebookExample.js +2 -1
- package/lib/examples/HomeExample.d.ts +15 -0
- package/lib/examples/HomeExample.js +77 -0
- package/lib/examples/Lexical2Example.js +4 -2
- package/lib/examples/{LexicalExample.d.ts → LexicalAgentExample.d.ts} +4 -4
- package/lib/examples/{LexicalExample.js → LexicalAgentExample.js} +66 -17
- package/lib/examples/{LexicalSidebarExample.d.ts → LexicalAgentSidebarExample.d.ts} +5 -5
- package/lib/examples/LexicalAgentSidebarExample.js +261 -0
- package/lib/examples/NotebookAgentExample.d.ts +9 -0
- package/lib/examples/NotebookAgentExample.js +192 -0
- package/lib/examples/{NotebookSidebarExample.d.ts → NotebookAgentSidebarExample.d.ts} +2 -2
- package/lib/examples/NotebookAgentSidebarExample.js +221 -0
- package/lib/examples/{DatalayerNotebookExample.d.ts → NotebookCollaborationExample.d.ts} +4 -4
- package/lib/examples/{DatalayerNotebookExample.js → NotebookCollaborationExample.js} +3 -3
- package/lib/examples/NotebookExample.d.ts +4 -7
- package/lib/examples/NotebookExample.js +14 -146
- package/lib/examples/components/AuthRequiredView.d.ts +6 -0
- package/lib/examples/components/AuthRequiredView.js +33 -0
- package/lib/examples/components/ExampleWrapper.d.ts +9 -3
- package/lib/examples/components/ExampleWrapper.js +45 -9
- package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.js +1 -1
- package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.js +1 -1
- package/lib/examples/{ag-ui → components}/haiku/index.d.ts +1 -1
- package/lib/examples/{ag-ui → components}/haiku/index.js +1 -1
- package/lib/examples/components/index.d.ts +3 -0
- package/lib/examples/components/index.js +4 -0
- package/lib/examples/{ag-ui → components}/weather/index.d.ts +1 -1
- package/lib/examples/{ag-ui → components}/weather/index.js +1 -1
- package/lib/examples/example-selector.d.ts +17 -4
- package/lib/examples/example-selector.js +108 -41
- package/lib/examples/index.d.ts +10 -6
- package/lib/examples/index.js +10 -6
- package/lib/examples/lexical/initial-content.json +6 -6
- package/lib/examples/main.js +257 -27
- package/lib/examples/utils/a2ui.d.ts +18 -0
- package/lib/examples/utils/a2ui.js +69 -0
- package/lib/examples/utils/a2uiMarkdownProvider.d.ts +7 -0
- package/lib/examples/utils/a2uiMarkdownProvider.js +9 -0
- package/lib/examples/utils/agentId.d.ts +18 -0
- package/lib/examples/utils/agentId.js +54 -0
- package/lib/examples/utils/agents/earthquake-detector.json +11 -11
- package/lib/examples/utils/agents/sales-forecaster.json +11 -11
- package/lib/examples/utils/agents/social-post-generator.json +11 -11
- package/lib/examples/utils/agents/stock-market.json +11 -11
- package/lib/examples/utils/examplesStore.js +82 -27
- package/lib/examples/utils/useExampleAgentRuntimesUrl.d.ts +5 -0
- package/lib/examples/utils/useExampleAgentRuntimesUrl.js +19 -0
- package/lib/hooks/index.d.ts +8 -8
- package/lib/hooks/index.js +7 -7
- package/lib/hooks/useA2A.d.ts +2 -3
- package/lib/hooks/useAIAgentsWebSocket.d.ts +43 -4
- package/lib/hooks/useAIAgentsWebSocket.js +153 -12
- package/lib/hooks/useAcp.d.ts +1 -2
- package/lib/hooks/useAgUi.d.ts +1 -1
- package/lib/hooks/{useAgents.d.ts → useAgentRuntimes.d.ts} +70 -4
- package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +237 -32
- package/lib/hooks/useAgentsCatalog.js +1 -1
- package/lib/hooks/useAgentsService.d.ts +2 -2
- package/lib/hooks/useAgentsService.js +7 -7
- package/lib/hooks/useCheckpoints.js +1 -1
- package/lib/hooks/useConfig.d.ts +4 -1
- package/lib/hooks/useConfig.js +10 -3
- package/lib/hooks/useContextSnapshot.d.ts +9 -4
- package/lib/hooks/useContextSnapshot.js +9 -37
- package/lib/hooks/useMonitoring.js +3 -0
- package/lib/hooks/useSandbox.d.ts +20 -8
- package/lib/hooks/useSandbox.js +105 -40
- package/lib/hooks/useSkills.d.ts +23 -5
- package/lib/hooks/useSkills.js +94 -39
- package/lib/hooks/useToolApprovals.d.ts +60 -36
- package/lib/hooks/useToolApprovals.js +318 -69
- package/lib/hooks/useVercelAI.d.ts +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +1 -0
- package/lib/inference/index.d.ts +0 -1
- package/lib/middleware/index.d.ts +0 -1
- package/lib/protocols/AGUIAdapter.js +6 -0
- package/lib/protocols/VercelAIAdapter.d.ts +7 -0
- package/lib/protocols/VercelAIAdapter.js +59 -7
- package/lib/specs/agents/agents.d.ts +21 -4
- package/lib/specs/agents/agents.js +2879 -316
- package/lib/specs/agents/index.js +3 -1
- package/lib/specs/benchmarks.d.ts +20 -0
- package/lib/specs/benchmarks.js +205 -0
- package/lib/specs/envvars.js +27 -20
- package/lib/specs/evals.d.ts +10 -9
- package/lib/specs/evals.js +128 -88
- package/lib/specs/events.d.ts +3 -10
- package/lib/specs/events.js +127 -84
- package/lib/specs/frontendTools.js +2 -2
- package/lib/specs/guardrails.d.ts +0 -7
- package/lib/specs/guardrails.js +240 -159
- package/lib/specs/mcpServers.js +35 -6
- package/lib/specs/memory.d.ts +0 -2
- package/lib/specs/memory.js +4 -17
- package/lib/specs/models.d.ts +0 -2
- package/lib/specs/models.js +20 -15
- package/lib/specs/notifications.js +102 -18
- package/lib/specs/outputs.js +15 -9
- package/lib/specs/personas.d.ts +41 -0
- package/lib/specs/personas.js +168 -0
- package/lib/specs/skills.d.ts +1 -1
- package/lib/specs/skills.js +23 -23
- package/lib/specs/teams/index.js +3 -1
- package/lib/specs/teams/teams.js +468 -348
- package/lib/specs/tools.js +4 -4
- package/lib/specs/triggers.js +61 -11
- package/lib/stores/agentRuntimeStore.d.ts +208 -0
- package/lib/stores/agentRuntimeStore.js +650 -0
- package/lib/stores/conversationStore.js +2 -2
- package/lib/stores/index.d.ts +1 -1
- package/lib/stores/index.js +1 -1
- package/lib/tools/adapters/copilotkit/lexicalHooks.d.ts +1 -2
- package/lib/tools/adapters/copilotkit/lexicalHooks.js +1 -3
- package/lib/tools/adapters/copilotkit/notebookHooks.d.ts +1 -2
- package/lib/tools/adapters/copilotkit/notebookHooks.js +1 -3
- package/lib/tools/index.d.ts +0 -2
- package/lib/tools/index.js +0 -1
- package/lib/types/agents-lifecycle.d.ts +18 -0
- package/lib/types/agents.d.ts +6 -0
- package/lib/types/agentspecs.d.ts +54 -1
- package/lib/types/benchmarks.d.ts +43 -0
- package/lib/types/benchmarks.js +5 -0
- package/lib/types/chat.d.ts +325 -8
- package/lib/types/context.d.ts +27 -0
- package/lib/types/cost.d.ts +2 -2
- package/lib/types/evals.d.ts +26 -17
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.js +3 -0
- package/lib/types/mcp.d.ts +8 -0
- package/lib/types/models.d.ts +2 -2
- package/lib/types/personas.d.ts +25 -0
- package/lib/types/personas.js +5 -0
- package/lib/types/skills.d.ts +43 -1
- package/lib/types/stream.d.ts +110 -0
- package/lib/types/stream.js +36 -0
- package/lib/utils/utils.d.ts +9 -5
- package/lib/utils/utils.js +9 -5
- package/package.json +19 -11
- package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_benchmarks.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_events.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
- package/scripts/codegen/generate_agents.py +187 -45
- package/scripts/codegen/generate_benchmarks.py +441 -0
- package/scripts/codegen/generate_evals.py +94 -16
- package/scripts/codegen/generate_events.py +35 -14
- package/scripts/codegen/generate_personas.py +319 -0
- package/scripts/codegen/generate_skills.py +9 -9
- package/scripts/sync-jupyter.sh +26 -7
- package/lib/api/tool-approvals.d.ts +0 -62
- package/lib/api/tool-approvals.js +0 -145
- package/lib/examples/AgentspecExample.js +0 -705
- package/lib/examples/LexicalSidebarExample.js +0 -163
- package/lib/examples/NotebookSidebarExample.js +0 -119
- package/lib/examples/NotebookSimpleExample.d.ts +0 -6
- package/lib/examples/NotebookSimpleExample.js +0 -22
- package/lib/examples/ag-ui/index.d.ts +0 -10
- package/lib/examples/ag-ui/index.js +0 -16
- package/lib/hooks/useAgentsRegistry.d.ts +0 -10
- package/lib/hooks/useAgentsRegistry.js +0 -20
- package/lib/stores/agentsStore.d.ts +0 -123
- package/lib/stores/agentsStore.js +0 -270
- /package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.d.ts +0 -0
- /package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.d.ts +0 -0
- /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.d.ts +0 -0
- /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.js +0 -0
|
@@ -11,17 +11,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
11
11
|
*
|
|
12
12
|
* @module components/context/CostTracker
|
|
13
13
|
*/
|
|
14
|
-
import { Box, Heading, Text, ProgressBar,
|
|
14
|
+
import { Box, Heading, Text, ProgressBar, Flash, Label } from '@primer/react';
|
|
15
15
|
import { CreditCardIcon, AlertIcon } from '@primer/octicons-react';
|
|
16
|
-
import { useQuery } from '@tanstack/react-query';
|
|
17
|
-
function getLocalApiBase() {
|
|
18
|
-
if (typeof window === 'undefined')
|
|
19
|
-
return '';
|
|
20
|
-
const host = window.location.hostname;
|
|
21
|
-
return host === 'localhost' || host === '127.0.0.1'
|
|
22
|
-
? 'http://127.0.0.1:8765'
|
|
23
|
-
: '';
|
|
24
|
-
}
|
|
25
16
|
function formatUsd(amount) {
|
|
26
17
|
if (amount < 0.01)
|
|
27
18
|
return `$${amount.toFixed(4)}`;
|
|
@@ -39,38 +30,23 @@ function formatTokens(tokens) {
|
|
|
39
30
|
/**
|
|
40
31
|
* Displays running cost and budget utilization for an agent.
|
|
41
32
|
*/
|
|
42
|
-
export function CostTracker({ agentId, compact = false }) {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const apiBase = getLocalApiBase();
|
|
47
|
-
const response = await fetch(`${apiBase}/api/v1/configure/agents/${encodeURIComponent(agentId)}/cost-usage`);
|
|
48
|
-
if (!response.ok)
|
|
49
|
-
throw new Error('Failed to fetch cost data');
|
|
50
|
-
return response.json();
|
|
51
|
-
},
|
|
52
|
-
refetchInterval: 5000,
|
|
53
|
-
staleTime: 0,
|
|
54
|
-
});
|
|
55
|
-
if (isLoading) {
|
|
56
|
-
return (_jsxs(Box, { sx: {
|
|
57
|
-
p: 2,
|
|
58
|
-
display: 'flex',
|
|
59
|
-
alignItems: 'center',
|
|
60
|
-
gap: 2,
|
|
61
|
-
}, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Loading cost\u2026" })] }));
|
|
62
|
-
}
|
|
63
|
-
if (error || !costData) {
|
|
64
|
-
return (_jsx(Box, { sx: { p: 2 }, children: _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Cost data unavailable" }) }));
|
|
33
|
+
export function CostTracker({ agentId: _agentId, compact = false, liveData, }) {
|
|
34
|
+
const costData = liveData;
|
|
35
|
+
if (!costData) {
|
|
36
|
+
return (_jsx(Box, { sx: { p: 2 }, children: _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Waiting for websocket snapshot..." }) }));
|
|
65
37
|
}
|
|
66
38
|
const cumulativePercent = costData.cumulativeBudgetUsd != null && costData.cumulativeBudgetUsd > 0
|
|
67
39
|
? (costData.cumulativeCostUsd / costData.cumulativeBudgetUsd) * 100
|
|
68
40
|
: null;
|
|
69
|
-
const
|
|
70
|
-
|
|
41
|
+
const lastTurnCostUsd = costData.lastTurnCostUsd;
|
|
42
|
+
const lastTurnPercent = costData.perRunBudgetUsd != null && costData.perRunBudgetUsd > 0
|
|
43
|
+
? (lastTurnCostUsd / costData.perRunBudgetUsd) * 100
|
|
71
44
|
: null;
|
|
72
45
|
const isOverBudget = cumulativePercent != null && cumulativePercent > 100;
|
|
73
46
|
const isNearBudget = cumulativePercent != null && cumulativePercent > 80;
|
|
47
|
+
const hasUnresolvedPricing = Array.isArray(costData.runs) &&
|
|
48
|
+
costData.runs.some(run => run.pricingResolved === false);
|
|
49
|
+
const displayUsd = (amount) => hasUnresolvedPricing && amount === 0 ? '…' : formatUsd(amount);
|
|
74
50
|
// Compact: single row summary
|
|
75
51
|
if (compact) {
|
|
76
52
|
return (_jsxs(Box, { sx: {
|
|
@@ -79,11 +55,12 @@ export function CostTracker({ agentId, compact = false }) {
|
|
|
79
55
|
gap: 2,
|
|
80
56
|
px: 2,
|
|
81
57
|
py: 1,
|
|
82
|
-
}, children: [_jsx(CreditCardIcon, { size: 14 }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["
|
|
83
|
-
`
|
|
58
|
+
}, children: [_jsx(CreditCardIcon, { size: 14 }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Cumulative: ", displayUsd(costData.cumulativeCostUsd), costData.cumulativeBudgetUsd != null &&
|
|
59
|
+
` (budget ${formatUsd(costData.cumulativeBudgetUsd)})`, ' | ', "Last turn: ", displayUsd(lastTurnCostUsd), costData.perRunBudgetUsd != null &&
|
|
60
|
+
` (budget ${formatUsd(costData.perRunBudgetUsd)})`] }), isOverBudget && (_jsx(Label, { variant: "danger", size: "small", children: "Over budget" })), !isOverBudget && isNearBudget && (_jsx(Label, { variant: "attention", size: "small", children: "Near limit" }))] }));
|
|
84
61
|
}
|
|
85
62
|
// Full display
|
|
86
|
-
return (_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, fontWeight: 'semibold', mb: 2, color: 'fg.muted' }, children: "Cost Tracker" }), isOverBudget && (_jsxs(Flash, { variant: "danger", sx: { mb: 2 }, children: [_jsx(AlertIcon, { size: 16 }), "Cumulative cost (", formatUsd(costData.cumulativeCostUsd), ") exceeds budget (", formatUsd(costData.cumulativeBudgetUsd), ")."] })), _jsxs(Box, { sx: {
|
|
63
|
+
return (_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, fontWeight: 'semibold', mb: 2, color: 'fg.muted' }, children: "Cost Tracker" }), isOverBudget && (_jsxs(Flash, { variant: "danger", sx: { mb: 2 }, children: [_jsx(AlertIcon, { size: 16 }), "Cumulative cost (", formatUsd(costData.cumulativeCostUsd), ") exceeds budget (", formatUsd(costData.cumulativeBudgetUsd), ")."] })), hasUnresolvedPricing && costData.requestCount > 0 && (_jsxs(Flash, { variant: "warning", sx: { mb: 2 }, children: [_jsx(AlertIcon, { size: 16 }), "Some model pricing could not be resolved yet, so cost may remain at \u2026 even when requests/tokens are increasing."] })), _jsxs(Box, { sx: {
|
|
87
64
|
p: 3,
|
|
88
65
|
bg: 'canvas.subtle',
|
|
89
66
|
borderRadius: 2,
|
|
@@ -93,17 +70,17 @@ export function CostTracker({ agentId, compact = false }) {
|
|
|
93
70
|
display: 'flex',
|
|
94
71
|
justifyContent: 'space-between',
|
|
95
72
|
mb: 1,
|
|
96
|
-
}, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: "
|
|
97
|
-
`
|
|
98
|
-
display: 'flex',
|
|
99
|
-
justifyContent: 'space-between',
|
|
100
|
-
mb: 1,
|
|
101
|
-
}, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: "Cumulative" }), _jsxs(Text, { sx: { fontSize: 1 }, children: [formatUsd(costData.cumulativeCostUsd), costData.cumulativeBudgetUsd != null &&
|
|
102
|
-
` / ${formatUsd(costData.cumulativeBudgetUsd)}`] })] }), cumulativePercent != null && (_jsx(ProgressBar, { progress: Math.min(cumulativePercent, 100), sx: { height: 6 }, bg: cumulativePercent > 100
|
|
73
|
+
}, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: "Cumulative" }), _jsxs(Text, { sx: { fontSize: 1 }, children: [displayUsd(costData.cumulativeCostUsd), costData.cumulativeBudgetUsd != null &&
|
|
74
|
+
` (budget ${formatUsd(costData.cumulativeBudgetUsd)})`] })] }), cumulativePercent != null && (_jsx(ProgressBar, { progress: Math.min(cumulativePercent, 100), sx: { height: 6 }, bg: cumulativePercent > 100
|
|
103
75
|
? 'danger.emphasis'
|
|
104
76
|
: cumulativePercent > 80
|
|
105
77
|
? 'attention.emphasis'
|
|
106
|
-
: 'accent.emphasis' }))] }), _jsxs(Box, { sx: {
|
|
78
|
+
: 'accent.emphasis' }))] }), _jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Box, { sx: {
|
|
79
|
+
display: 'flex',
|
|
80
|
+
justifyContent: 'space-between',
|
|
81
|
+
mb: 1,
|
|
82
|
+
}, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: "Last turn" }), _jsxs(Text, { sx: { fontSize: 1 }, children: [displayUsd(lastTurnCostUsd), costData.perRunBudgetUsd != null &&
|
|
83
|
+
` (budget ${formatUsd(costData.perRunBudgetUsd)})`] })] }), lastTurnPercent != null && (_jsx(ProgressBar, { progress: Math.min(lastTurnPercent, 100), sx: { height: 6 }, bg: lastTurnPercent > 80 ? 'danger.emphasis' : 'accent.emphasis' }))] }), _jsxs(Box, { sx: {
|
|
107
84
|
display: 'flex',
|
|
108
85
|
gap: 3,
|
|
109
86
|
borderTop: '1px solid',
|
|
@@ -119,6 +96,8 @@ export function CostTracker({ agentId, compact = false }) {
|
|
|
119
96
|
justifyContent: 'space-between',
|
|
120
97
|
alignItems: 'center',
|
|
121
98
|
py: '2px',
|
|
122
|
-
}, children: [_jsx(Text, { sx: { fontSize: 0 }, children: m.model }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [
|
|
99
|
+
}, children: [_jsx(Text, { sx: { fontSize: 0 }, children: m.model }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [displayUsd(m.costUsd), " (", m.requests, " req)", hasUnresolvedPricing && m.costUsd === 0
|
|
100
|
+
? ' - pricing pending'
|
|
101
|
+
: ''] })] }, m.model)))] }))] })] }));
|
|
123
102
|
}
|
|
124
103
|
export default CostTracker;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface CostUsageChartProps {
|
|
2
|
+
serviceName?: string;
|
|
3
|
+
agentId?: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
runUrl?: string;
|
|
6
|
+
wsRunUrl?: string;
|
|
7
|
+
liveCumulativeUsd?: number;
|
|
8
|
+
liveTimestampMs?: number | null;
|
|
9
|
+
height?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function CostUsageChart({ serviceName, agentId, apiKey, runUrl, wsRunUrl, liveCumulativeUsd, liveTimestampMs, height, }: CostUsageChartProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export default CostUsageChart;
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
7
|
+
import ReactECharts from 'echarts-for-react';
|
|
8
|
+
import { buildOtelWebSocketUrl } from '@datalayer/core/lib/otel';
|
|
9
|
+
import { toMetricValue } from '../hooks/useMonitoring';
|
|
10
|
+
import { subscribeOtelWs } from './otelWsPool';
|
|
11
|
+
import { agentRuntimeStore, getMonitoringCacheKey, useAgentRuntimeStore, } from '../stores/agentRuntimeStore';
|
|
12
|
+
const COST_RUN_METRIC = 'agent_runtimes.capability.cost.run.usd';
|
|
13
|
+
const COST_CUMULATIVE_METRIC = 'agent_runtimes.capability.cost.cumulative.usd';
|
|
14
|
+
function resolveMonitoringEntry(monitoringCache, serviceName, agentId) {
|
|
15
|
+
const direct = monitoringCache[getMonitoringCacheKey(serviceName, agentId)];
|
|
16
|
+
if (direct)
|
|
17
|
+
return direct;
|
|
18
|
+
if (agentId) {
|
|
19
|
+
const byAgent = Object.entries(monitoringCache).find(([key, entry]) => {
|
|
20
|
+
return key.endsWith(`::${agentId}`) && entry.costPoints.length > 0;
|
|
21
|
+
});
|
|
22
|
+
if (byAgent)
|
|
23
|
+
return byAgent[1];
|
|
24
|
+
}
|
|
25
|
+
if (serviceName) {
|
|
26
|
+
const byService = Object.entries(monitoringCache).find(([key, entry]) => {
|
|
27
|
+
return key.startsWith(`${serviceName}::`) && entry.costPoints.length > 0;
|
|
28
|
+
});
|
|
29
|
+
if (byService)
|
|
30
|
+
return byService[1];
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
function localPointToCostPoint(point) {
|
|
35
|
+
return {
|
|
36
|
+
timestampKey: String(point.timestampMs),
|
|
37
|
+
timestampMs: point.timestampMs,
|
|
38
|
+
costUsd: 0,
|
|
39
|
+
cumulativeUsd: point.cumulativeUsd,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Parse attributes that may arrive as a JSON string (WS) or object (HTTP). */
|
|
43
|
+
function parseAttributes(attrs) {
|
|
44
|
+
if (attrs && typeof attrs === 'object' && !Array.isArray(attrs)) {
|
|
45
|
+
return attrs;
|
|
46
|
+
}
|
|
47
|
+
if (typeof attrs === 'string') {
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(attrs);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
/** Convert metric row timestamp to milliseconds. */
|
|
58
|
+
function rowTimestampMs(row) {
|
|
59
|
+
const nanoTs = row.timestamp_unix_nano ?? row.observed_timestamp_unix_nano;
|
|
60
|
+
if (typeof nanoTs === 'number' && nanoTs > 0)
|
|
61
|
+
return nanoTs / 1_000_000;
|
|
62
|
+
if (typeof nanoTs === 'string' && nanoTs.length > 0) {
|
|
63
|
+
const parsed = Number(nanoTs);
|
|
64
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
65
|
+
return parsed / 1_000_000;
|
|
66
|
+
}
|
|
67
|
+
const isoTs = row.timestamp;
|
|
68
|
+
if (typeof isoTs === 'string' && isoTs.length > 0) {
|
|
69
|
+
const ms = new Date(isoTs).getTime();
|
|
70
|
+
if (Number.isFinite(ms) && ms > 0)
|
|
71
|
+
return ms;
|
|
72
|
+
}
|
|
73
|
+
return Date.now();
|
|
74
|
+
}
|
|
75
|
+
function rowTimestampKey(row) {
|
|
76
|
+
const nano = row.timestamp_unix_nano;
|
|
77
|
+
if (typeof nano === 'number' && nano > 0)
|
|
78
|
+
return String(nano);
|
|
79
|
+
if (typeof nano === 'string' && nano.length > 0)
|
|
80
|
+
return nano;
|
|
81
|
+
const iso = row.timestamp;
|
|
82
|
+
if (typeof iso === 'string' && iso.length > 0)
|
|
83
|
+
return iso;
|
|
84
|
+
return '';
|
|
85
|
+
}
|
|
86
|
+
/** Extract cost points from cost metrics rows. */
|
|
87
|
+
function extractCostPoints(rows, agentId) {
|
|
88
|
+
let filtered = rows.filter(row => {
|
|
89
|
+
const metricName = row.metric_name;
|
|
90
|
+
return (metricName === COST_CUMULATIVE_METRIC || metricName === COST_RUN_METRIC);
|
|
91
|
+
});
|
|
92
|
+
if (agentId) {
|
|
93
|
+
filtered = filtered.filter(row => extractAgentId(row) === agentId);
|
|
94
|
+
}
|
|
95
|
+
const byTimestamp = new Map();
|
|
96
|
+
for (const row of filtered) {
|
|
97
|
+
const ts = rowTimestampKey(row);
|
|
98
|
+
if (!ts)
|
|
99
|
+
continue;
|
|
100
|
+
const group = byTimestamp.get(ts) ?? [];
|
|
101
|
+
group.push(row);
|
|
102
|
+
byTimestamp.set(ts, group);
|
|
103
|
+
}
|
|
104
|
+
const points = [];
|
|
105
|
+
const sorted = [...byTimestamp.entries()].sort((a, b) => {
|
|
106
|
+
const na = Number(a[0]);
|
|
107
|
+
const nb = Number(b[0]);
|
|
108
|
+
if (Number.isFinite(na) && Number.isFinite(nb))
|
|
109
|
+
return na - nb;
|
|
110
|
+
return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
|
|
111
|
+
});
|
|
112
|
+
for (const [, groupRows] of sorted) {
|
|
113
|
+
const cumulativeRows = groupRows.filter(r => r.metric_name === COST_CUMULATIVE_METRIC);
|
|
114
|
+
if (cumulativeRows.length === 0)
|
|
115
|
+
continue;
|
|
116
|
+
const runRows = groupRows.filter(r => r.metric_name === COST_RUN_METRIC);
|
|
117
|
+
// Cumulative metric values represent the running total. Repeated snapshot
|
|
118
|
+
// rows for the same timestamp should not be summed; keep the highest value.
|
|
119
|
+
const cumulativeUsd = cumulativeRows.reduce((max, row) => Math.max(max, toMetricValue(row)), 0);
|
|
120
|
+
const costUsd = runRows.reduce((sum, row) => sum + toMetricValue(row), 0);
|
|
121
|
+
if (!Number.isFinite(cumulativeUsd) || !Number.isFinite(costUsd))
|
|
122
|
+
continue;
|
|
123
|
+
const timestampKey = rowTimestampKey(cumulativeRows[0]);
|
|
124
|
+
if (!timestampKey)
|
|
125
|
+
continue;
|
|
126
|
+
points.push({
|
|
127
|
+
timestampKey,
|
|
128
|
+
timestampMs: rowTimestampMs(cumulativeRows[0]),
|
|
129
|
+
costUsd,
|
|
130
|
+
cumulativeUsd,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
points.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
134
|
+
return points;
|
|
135
|
+
}
|
|
136
|
+
function extractServiceName(row) {
|
|
137
|
+
const directCandidates = [row.service_name, row.service, row.serviceName];
|
|
138
|
+
for (const candidate of directCandidates) {
|
|
139
|
+
if (typeof candidate === 'string' && candidate.length > 0) {
|
|
140
|
+
return candidate;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const resourceAttributes = row.resource_attributes;
|
|
144
|
+
if (resourceAttributes && typeof resourceAttributes === 'object') {
|
|
145
|
+
const nested = resourceAttributes['service.name'];
|
|
146
|
+
if (typeof nested === 'string' && nested.length > 0) {
|
|
147
|
+
return nested;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
/** Extract `agent.id` from span/trace attributes. */
|
|
153
|
+
function extractAgentId(row) {
|
|
154
|
+
const attrs = parseAttributes(row.attributes);
|
|
155
|
+
const aid = attrs['agent.id'];
|
|
156
|
+
if (typeof aid === 'string')
|
|
157
|
+
return aid;
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
export function CostUsageChart({ serviceName, agentId, apiKey, runUrl, wsRunUrl, liveCumulativeUsd, liveTimestampMs, height = 160, }) {
|
|
161
|
+
const monitoringCache = useAgentRuntimeStore(s => s.monitoringCache);
|
|
162
|
+
const mergeCostPoints = useAgentRuntimeStore(s => s.mergeCostPoints);
|
|
163
|
+
const upsertLocalCostPoint = useAgentRuntimeStore(s => s.upsertLocalCostPoint);
|
|
164
|
+
const cachedEntry = useMemo(() => resolveMonitoringEntry(monitoringCache, serviceName, agentId), [agentId, monitoringCache, serviceName]);
|
|
165
|
+
const [points, setPoints] = useState([]);
|
|
166
|
+
const initialTimestampMsRef = useRef(Date.now());
|
|
167
|
+
const mergePoints = (existing, incoming) => {
|
|
168
|
+
const byTimestamp = new Map();
|
|
169
|
+
for (const point of existing) {
|
|
170
|
+
byTimestamp.set(point.timestampKey, point);
|
|
171
|
+
}
|
|
172
|
+
for (const point of incoming) {
|
|
173
|
+
const prev = byTimestamp.get(point.timestampKey);
|
|
174
|
+
if (!prev) {
|
|
175
|
+
byTimestamp.set(point.timestampKey, point);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
byTimestamp.set(point.timestampKey, {
|
|
179
|
+
...prev,
|
|
180
|
+
timestampMs: Math.max(prev.timestampMs, point.timestampMs),
|
|
181
|
+
costUsd: Math.max(prev.costUsd, point.costUsd),
|
|
182
|
+
cumulativeUsd: Math.max(prev.cumulativeUsd, point.cumulativeUsd),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const merged = Array.from(byTimestamp.values()).sort((a, b) => a.timestampMs - b.timestampMs);
|
|
186
|
+
// Cumulative cost should never decrease; guard against out-of-order or
|
|
187
|
+
// duplicate snapshots by enforcing a monotonic series.
|
|
188
|
+
let runningMax = 0;
|
|
189
|
+
return merged.map(point => {
|
|
190
|
+
runningMax = Math.max(runningMax, point.cumulativeUsd);
|
|
191
|
+
return {
|
|
192
|
+
...point,
|
|
193
|
+
cumulativeUsd: runningMax,
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
// ── Reset state on source switch ──────────────────────────────
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
if (!serviceName) {
|
|
200
|
+
setPoints([]);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
setPoints((cachedEntry?.costPoints ?? []).map(localPointToCostPoint));
|
|
204
|
+
initialTimestampMsRef.current = Date.now();
|
|
205
|
+
}, [agentId, cachedEntry, serviceName]);
|
|
206
|
+
// Apply immediate post-turn updates from the monitoring websocket snapshot.
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
if (!serviceName)
|
|
209
|
+
return;
|
|
210
|
+
if (typeof liveCumulativeUsd !== 'number' ||
|
|
211
|
+
!Number.isFinite(liveCumulativeUsd)) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const timestampMs = typeof liveTimestampMs === 'number' && Number.isFinite(liveTimestampMs)
|
|
215
|
+
? liveTimestampMs
|
|
216
|
+
: Date.now();
|
|
217
|
+
const livePoint = {
|
|
218
|
+
timestampKey: `live-${timestampMs}`,
|
|
219
|
+
timestampMs,
|
|
220
|
+
costUsd: 0,
|
|
221
|
+
cumulativeUsd: Math.max(0, liveCumulativeUsd),
|
|
222
|
+
};
|
|
223
|
+
upsertLocalCostPoint({
|
|
224
|
+
serviceName,
|
|
225
|
+
agentId,
|
|
226
|
+
timestampMs,
|
|
227
|
+
cumulativeUsd: livePoint.cumulativeUsd,
|
|
228
|
+
});
|
|
229
|
+
const mergedEntry = resolveMonitoringEntry(agentRuntimeStore.getState().monitoringCache, serviceName, agentId);
|
|
230
|
+
if (mergedEntry) {
|
|
231
|
+
setPoints(mergedEntry.costPoints.map(localPointToCostPoint));
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
setPoints(prev => mergePoints(prev, [livePoint]));
|
|
235
|
+
}
|
|
236
|
+
}, [
|
|
237
|
+
agentId,
|
|
238
|
+
liveCumulativeUsd,
|
|
239
|
+
liveTimestampMs,
|
|
240
|
+
serviceName,
|
|
241
|
+
upsertLocalCostPoint,
|
|
242
|
+
]);
|
|
243
|
+
// ── WebSocket subscription (shared connection pool) ─────────
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
if (!serviceName || !apiKey)
|
|
246
|
+
return;
|
|
247
|
+
const rawBaseUrl = wsRunUrl ||
|
|
248
|
+
runUrl ||
|
|
249
|
+
(typeof window !== 'undefined' ? window.location.origin : '');
|
|
250
|
+
if (!rawBaseUrl)
|
|
251
|
+
return;
|
|
252
|
+
const baseWithProtocol = rawBaseUrl.startsWith('http://') ||
|
|
253
|
+
rawBaseUrl.startsWith('https://') ||
|
|
254
|
+
rawBaseUrl.startsWith('ws://') ||
|
|
255
|
+
rawBaseUrl.startsWith('wss://')
|
|
256
|
+
? rawBaseUrl
|
|
257
|
+
: `${typeof window !== 'undefined' &&
|
|
258
|
+
window.location.protocol === 'https:'
|
|
259
|
+
? 'https:'
|
|
260
|
+
: 'http:'}//${typeof window !== 'undefined' ? window.location.host : ''}${rawBaseUrl}`;
|
|
261
|
+
let wsUrl;
|
|
262
|
+
try {
|
|
263
|
+
wsUrl = buildOtelWebSocketUrl({
|
|
264
|
+
baseUrl: baseWithProtocol,
|
|
265
|
+
token: apiKey,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const unsubscribe = subscribeOtelWs(wsUrl, msg => {
|
|
272
|
+
if (msg.signal !== 'metrics')
|
|
273
|
+
return;
|
|
274
|
+
const rows = Array.isArray(msg.data) ? msg.data : [];
|
|
275
|
+
let matchingRows = rows.filter(row => extractServiceName(row) === serviceName);
|
|
276
|
+
// Filter by agent.id when specified.
|
|
277
|
+
if (agentId) {
|
|
278
|
+
matchingRows = matchingRows.filter(row => extractAgentId(row) === agentId);
|
|
279
|
+
}
|
|
280
|
+
if (matchingRows.length === 0)
|
|
281
|
+
return;
|
|
282
|
+
const newPoints = extractCostPoints(matchingRows, agentId);
|
|
283
|
+
if (newPoints.length > 0) {
|
|
284
|
+
mergeCostPoints({
|
|
285
|
+
serviceName,
|
|
286
|
+
agentId,
|
|
287
|
+
points: newPoints.map(point => ({
|
|
288
|
+
timestampMs: point.timestampMs,
|
|
289
|
+
cumulativeUsd: point.cumulativeUsd,
|
|
290
|
+
})),
|
|
291
|
+
});
|
|
292
|
+
const mergedEntry = resolveMonitoringEntry(agentRuntimeStore.getState().monitoringCache, serviceName, agentId);
|
|
293
|
+
if (mergedEntry) {
|
|
294
|
+
setPoints(mergedEntry.costPoints.map(localPointToCostPoint));
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
setPoints(prev => mergePoints(prev, newPoints));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
return unsubscribe;
|
|
302
|
+
}, [agentId, apiKey, mergeCostPoints, runUrl, serviceName, wsRunUrl]);
|
|
303
|
+
// ── Chart options ─────────────────────────────────────────────
|
|
304
|
+
const option = useMemo(() => {
|
|
305
|
+
const chartData = points.length > 0
|
|
306
|
+
? [
|
|
307
|
+
[points[0].timestampMs, 0],
|
|
308
|
+
...points.map(p => [p.timestampMs, p.cumulativeUsd]),
|
|
309
|
+
]
|
|
310
|
+
: [[initialTimestampMsRef.current, 0]];
|
|
311
|
+
return {
|
|
312
|
+
animation: false,
|
|
313
|
+
animationDuration: 0,
|
|
314
|
+
animationDurationUpdate: 0,
|
|
315
|
+
tooltip: {
|
|
316
|
+
trigger: 'axis',
|
|
317
|
+
textStyle: { fontSize: 10 },
|
|
318
|
+
confine: true,
|
|
319
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
320
|
+
formatter: (params) => {
|
|
321
|
+
if (!Array.isArray(params) || params.length === 0)
|
|
322
|
+
return '';
|
|
323
|
+
const lines = params.map((p) => `${p.marker} ${p.seriesName}: $${p.value[1].toFixed(6)}`);
|
|
324
|
+
return lines.join('<br/>');
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
legend: {
|
|
328
|
+
data: ['Cumulative cost'],
|
|
329
|
+
top: 0,
|
|
330
|
+
textStyle: { fontSize: 9 },
|
|
331
|
+
itemWidth: 10,
|
|
332
|
+
itemHeight: 8,
|
|
333
|
+
itemGap: 6,
|
|
334
|
+
},
|
|
335
|
+
grid: {
|
|
336
|
+
left: 50,
|
|
337
|
+
right: 15,
|
|
338
|
+
top: 24,
|
|
339
|
+
bottom: 20,
|
|
340
|
+
},
|
|
341
|
+
xAxis: {
|
|
342
|
+
type: 'time',
|
|
343
|
+
min: 'dataMin',
|
|
344
|
+
max: 'dataMax',
|
|
345
|
+
axisLabel: { fontSize: 9 },
|
|
346
|
+
axisLine: { lineStyle: { color: '#d0d7de' } },
|
|
347
|
+
},
|
|
348
|
+
yAxis: {
|
|
349
|
+
type: 'value',
|
|
350
|
+
min: 0,
|
|
351
|
+
axisLabel: {
|
|
352
|
+
fontSize: 9,
|
|
353
|
+
formatter: (v) => `$${v.toFixed(4)}`,
|
|
354
|
+
},
|
|
355
|
+
splitLine: {
|
|
356
|
+
show: true,
|
|
357
|
+
lineStyle: { color: '#f0f0f0' },
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
series: [
|
|
361
|
+
{
|
|
362
|
+
name: 'Cumulative cost',
|
|
363
|
+
type: 'line',
|
|
364
|
+
smooth: false,
|
|
365
|
+
lineStyle: { width: 2 },
|
|
366
|
+
areaStyle: { opacity: 0.15 },
|
|
367
|
+
symbol: 'circle',
|
|
368
|
+
symbolSize: 4,
|
|
369
|
+
itemStyle: { color: '#cf222e' },
|
|
370
|
+
data: chartData,
|
|
371
|
+
animation: false,
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
};
|
|
375
|
+
}, [points]);
|
|
376
|
+
return (_jsx(ReactECharts, { option: option, style: { height, width: '100%' }, opts: { renderer: 'canvas' }, notMerge: true, lazyUpdate: true }));
|
|
377
|
+
}
|
|
378
|
+
export default CostUsageChart;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphFlowChart — ECharts graph visualisation of pydantic-graph execution.
|
|
3
|
+
*
|
|
4
|
+
* Renders a force-directed graph showing:
|
|
5
|
+
* - Static topology (nodes + edges from the graph definition)
|
|
6
|
+
* - Dynamic execution trace (which nodes were visited, with timing)
|
|
7
|
+
*/
|
|
8
|
+
import { type CSSProperties } from 'react';
|
|
9
|
+
import type { GraphTelemetryData } from '../types/stream';
|
|
10
|
+
export interface GraphFlowChartProps {
|
|
11
|
+
data: GraphTelemetryData;
|
|
12
|
+
height?: number | string;
|
|
13
|
+
style?: CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
export declare const GraphFlowChart: React.FC<GraphFlowChartProps>;
|
|
16
|
+
export default GraphFlowChart;
|