@datalayer/agent-runtimes 1.0.4 → 1.0.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 +34 -0
- package/lib/App.js +1 -1
- 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 +4 -4
- package/lib/chat/ChatFloating.d.ts +7 -140
- package/lib/chat/ChatFloating.js +2 -2
- 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 +2 -2
- package/lib/chat/ChatStandalone.d.ts +4 -54
- package/lib/chat/ChatStandalone.js +3 -3
- package/lib/chat/base/ChatBase.js +1083 -157
- package/lib/chat/header/ChatHeaderBase.d.ts +11 -6
- package/lib/chat/header/ChatHeaderBase.js +18 -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 +9 -9
- 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 +108 -113
- package/lib/chat/prompt/InputFooter.d.ts +19 -6
- package/lib/chat/prompt/InputFooter.js +71 -18
- package/lib/chat/prompt/InputPrompt.d.ts +3 -1
- package/lib/chat/prompt/InputPrompt.js +4 -4
- package/lib/chat/prompt/InputPromptFooter.js +1 -1
- 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 +6 -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.js +3 -3
- 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/AgentCheckpointsExample.js +13 -27
- package/lib/examples/AgentCodemodeExample.d.ts +4 -6
- package/lib/examples/AgentCodemodeExample.js +591 -169
- package/lib/examples/AgentEvalsExample.js +12 -16
- package/lib/examples/AgentGuardrailsExample.js +370 -64
- package/lib/examples/AgentHooksExample.d.ts +3 -0
- package/lib/examples/AgentHooksExample.js +104 -0
- package/lib/examples/AgentMCPExample.d.ts +3 -0
- package/lib/examples/AgentMCPExample.js +480 -0
- package/lib/examples/AgentMemoryExample.js +13 -17
- package/lib/examples/AgentMonitoringExample.js +260 -199
- package/lib/examples/AgentNotificationsExample.js +49 -17
- package/lib/examples/AgentOtelExample.js +2 -3
- package/lib/examples/AgentOutputsExample.d.ts +11 -6
- package/lib/examples/AgentOutputsExample.js +382 -81
- package/lib/examples/AgentParametersExample.d.ts +3 -0
- package/lib/examples/AgentParametersExample.js +246 -0
- package/lib/examples/AgentSandboxExample.d.ts +2 -2
- package/lib/examples/AgentSandboxExample.js +68 -40
- package/lib/examples/AgentSkillsExample.js +91 -99
- package/lib/examples/{AgentspecExample.js → AgentSpecsExample.js} +10 -21
- package/lib/examples/AgentSubagentsExample.d.ts +14 -0
- package/lib/examples/AgentSubagentsExample.js +228 -0
- package/lib/examples/AgentToolApprovalsExample.js +29 -557
- package/lib/examples/AgentTriggersExample.js +819 -565
- package/lib/examples/ChatCustomExample.js +11 -24
- package/lib/examples/ChatExample.js +7 -24
- 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} +65 -16
- 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 +7 -0
- package/lib/examples/components/ExampleWrapper.js +25 -6
- 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 +107 -41
- package/lib/examples/index.d.ts +9 -6
- package/lib/examples/index.js +9 -6
- package/lib/examples/main.js +217 -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/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 +118 -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} +39 -2
- package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +125 -15
- 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 +10 -0
- package/lib/specs/agents/agents.js +2139 -262
- package/lib/specs/agents/index.js +3 -1
- package/lib/specs/envvars.d.ts +1 -0
- package/lib/specs/envvars.js +38 -20
- package/lib/specs/evals.js +6 -6
- 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/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- 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.js +25 -5
- 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 +2 -1
- package/lib/specs/skills.js +41 -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 +204 -0
- package/lib/stores/agentRuntimeStore.js +636 -0
- 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/agentspecs.d.ts +50 -1
- package/lib/types/chat.d.ts +309 -8
- package/lib/types/context.d.ts +27 -0
- package/lib/types/cost.d.ts +2 -2
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.js +2 -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 +13 -9
- package/scripts/codegen/__pycache__/generate_agents.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 +102 -6
- package/scripts/codegen/generate_events.py +35 -13
- 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/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/{AgentspecExample.d.ts → AgentSpecsExample.d.ts} +0 -0
- /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
|
@@ -3,9 +3,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
4
|
* Distributed under the terms of the Modified BSD License.
|
|
5
5
|
*/
|
|
6
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
6
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
7
7
|
import ReactECharts from 'echarts-for-react';
|
|
8
|
-
import {
|
|
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';
|
|
9
12
|
const SERIES = [
|
|
10
13
|
{
|
|
11
14
|
label: 'System prompt',
|
|
@@ -20,7 +23,7 @@ const SERIES = [
|
|
|
20
23
|
metric: 'agent_runtimes.prompt.turn.user_message_tokens',
|
|
21
24
|
},
|
|
22
25
|
{
|
|
23
|
-
label: '
|
|
26
|
+
label: 'Agent messages',
|
|
24
27
|
metric: 'agent_runtimes.prompt.turn.ai_message_tokens',
|
|
25
28
|
},
|
|
26
29
|
{
|
|
@@ -28,65 +31,148 @@ const SERIES = [
|
|
|
28
31
|
metric: 'agent_runtimes.prompt.turn.tools_usage_tokens',
|
|
29
32
|
},
|
|
30
33
|
];
|
|
31
|
-
function
|
|
34
|
+
function resolveMonitoringEntry(monitoringCache, serviceName, agentId) {
|
|
35
|
+
const direct = monitoringCache[getMonitoringCacheKey(serviceName, agentId)];
|
|
36
|
+
if (direct)
|
|
37
|
+
return direct;
|
|
38
|
+
if (agentId) {
|
|
39
|
+
const byAgent = Object.entries(monitoringCache).find(([key, entry]) => {
|
|
40
|
+
return key.endsWith(`::${agentId}`) && entry.tokenTurns.length > 0;
|
|
41
|
+
});
|
|
42
|
+
if (byAgent)
|
|
43
|
+
return byAgent[1];
|
|
44
|
+
}
|
|
45
|
+
if (serviceName) {
|
|
46
|
+
const byService = Object.entries(monitoringCache).find(([key, entry]) => {
|
|
47
|
+
return key.startsWith(`${serviceName}::`) && entry.tokenTurns.length > 0;
|
|
48
|
+
});
|
|
49
|
+
if (byService)
|
|
50
|
+
return byService[1];
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
function localTokenTurnToPoint(turn) {
|
|
55
|
+
return {
|
|
56
|
+
turnNumber: turn.turnNumber,
|
|
57
|
+
timestampMs: turn.timestampMs,
|
|
58
|
+
values: {
|
|
59
|
+
'System prompt': turn.systemPromptTokens,
|
|
60
|
+
'Tools description': turn.toolsDescriptionTokens,
|
|
61
|
+
'User messages': turn.userMessageTokens,
|
|
62
|
+
'Agent messages': turn.aiMessageTokens,
|
|
63
|
+
'Tools usage': turn.toolsUsageTokens,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function pointToLocalTokenTurn(point) {
|
|
68
|
+
return {
|
|
69
|
+
turnNumber: point.turnNumber,
|
|
70
|
+
timestampMs: point.timestampMs,
|
|
71
|
+
systemPromptTokens: point.values['System prompt'],
|
|
72
|
+
toolsDescriptionTokens: point.values['Tools description'],
|
|
73
|
+
userMessageTokens: point.values['User messages'],
|
|
74
|
+
aiMessageTokens: point.values['Agent messages'],
|
|
75
|
+
toolsUsageTokens: point.values['Tools usage'],
|
|
76
|
+
totalTokens: point.values['System prompt'] +
|
|
77
|
+
point.values['Tools description'] +
|
|
78
|
+
point.values['User messages'] +
|
|
79
|
+
point.values['Agent messages'] +
|
|
80
|
+
point.values['Tools usage'],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function emptyValues() {
|
|
32
84
|
return SERIES.reduce((acc, s) => {
|
|
33
85
|
acc[s.label] = 0;
|
|
34
86
|
return acc;
|
|
35
87
|
}, {});
|
|
36
88
|
}
|
|
37
|
-
const
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const now = new Date();
|
|
42
|
-
for (let i = days - 1; i >= 0; i--) {
|
|
43
|
-
const d = new Date(now);
|
|
44
|
-
d.setDate(now.getDate() - i);
|
|
45
|
-
result.push(d.toISOString().slice(0, 10));
|
|
46
|
-
}
|
|
47
|
-
return result;
|
|
48
|
-
}
|
|
49
|
-
/** Build a contiguous list of day keys between min and max (inclusive),
|
|
50
|
-
* always padding at least one day on each side so single-day data is visible. */
|
|
51
|
-
function fillDayRange(sortedKeys) {
|
|
52
|
-
if (sortedKeys.length === 0)
|
|
53
|
-
return [];
|
|
54
|
-
const start = new Date(sortedKeys[0] + 'T00:00:00');
|
|
55
|
-
const end = new Date(sortedKeys[sortedKeys.length - 1] + 'T00:00:00');
|
|
56
|
-
// Pad one day before and after so a single-point line/area is visible
|
|
57
|
-
start.setDate(start.getDate() - 1);
|
|
58
|
-
end.setDate(end.getDate() + 1);
|
|
59
|
-
const filled = [];
|
|
60
|
-
for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
|
|
61
|
-
filled.push(d.toISOString().slice(0, 10));
|
|
62
|
-
}
|
|
63
|
-
return filled;
|
|
64
|
-
}
|
|
65
|
-
function toDayKey(row) {
|
|
66
|
-
// Prefer nanosecond integer timestamps (OTEL schema)
|
|
89
|
+
const COMPLETIONS_METRIC = 'agent_runtimes.prompt.turn.completions';
|
|
90
|
+
/** Convert nanosecond OTEL timestamp to milliseconds.
|
|
91
|
+
* Also accepts ISO date strings (from normalised OtelMetric responses). */
|
|
92
|
+
function nanoToMs(row) {
|
|
67
93
|
const nanoTs = row.timestamp_unix_nano ?? row.observed_timestamp_unix_nano;
|
|
68
|
-
if (typeof nanoTs === 'number' && nanoTs > 0)
|
|
69
|
-
return
|
|
70
|
-
}
|
|
94
|
+
if (typeof nanoTs === 'number' && nanoTs > 0)
|
|
95
|
+
return nanoTs / 1_000_000;
|
|
71
96
|
if (typeof nanoTs === 'string' && nanoTs.length > 0) {
|
|
72
97
|
const parsed = Number(nanoTs);
|
|
73
|
-
if (Number.isFinite(parsed) && parsed > 0)
|
|
74
|
-
return
|
|
98
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
99
|
+
return parsed / 1_000_000;
|
|
100
|
+
}
|
|
101
|
+
// Fallback: ISO timestamp string from normalised OtelMetric
|
|
102
|
+
const isoTs = row.timestamp;
|
|
103
|
+
if (typeof isoTs === 'string' && isoTs.length > 0) {
|
|
104
|
+
const ms = new Date(isoTs).getTime();
|
|
105
|
+
if (Number.isFinite(ms) && ms > 0)
|
|
106
|
+
return ms;
|
|
107
|
+
}
|
|
108
|
+
return Date.now();
|
|
109
|
+
}
|
|
110
|
+
/** Return a stable grouping key from a metric row's timestamp. */
|
|
111
|
+
function rowTimestampKey(row) {
|
|
112
|
+
const nano = row.timestamp_unix_nano;
|
|
113
|
+
if (typeof nano === 'number' && nano > 0)
|
|
114
|
+
return String(nano);
|
|
115
|
+
if (typeof nano === 'string' && nano.length > 0)
|
|
116
|
+
return nano;
|
|
117
|
+
// Normalised OtelMetric: use the ISO timestamp
|
|
118
|
+
const iso = row.timestamp;
|
|
119
|
+
if (typeof iso === 'string' && iso.length > 0)
|
|
120
|
+
return iso;
|
|
121
|
+
return '';
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Group metric rows by timestamp, sort chronologically,
|
|
125
|
+
* and extract cumulative values by watching the completions counter.
|
|
126
|
+
*/
|
|
127
|
+
function extractTurnsFromRows(rows, initialState) {
|
|
128
|
+
const byTimestamp = new Map();
|
|
129
|
+
for (const row of rows) {
|
|
130
|
+
const ts = rowTimestampKey(row);
|
|
131
|
+
if (!ts)
|
|
132
|
+
continue;
|
|
133
|
+
let group = byTimestamp.get(ts);
|
|
134
|
+
if (!group) {
|
|
135
|
+
group = [];
|
|
136
|
+
byTimestamp.set(ts, group);
|
|
75
137
|
}
|
|
138
|
+
group.push(row);
|
|
76
139
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
140
|
+
const sortedGroups = [...byTimestamp.entries()].sort((a, b) => {
|
|
141
|
+
const na = Number(a[0]);
|
|
142
|
+
const nb = Number(b[0]);
|
|
143
|
+
// Both numeric (nanosecond timestamps)
|
|
144
|
+
if (Number.isFinite(na) && Number.isFinite(nb))
|
|
145
|
+
return na - nb;
|
|
146
|
+
// ISO strings: lexicographic comparison works for ISO 8601
|
|
147
|
+
return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
|
|
148
|
+
});
|
|
149
|
+
const turns = [];
|
|
150
|
+
let prev = initialState;
|
|
151
|
+
for (const [, groupRows] of sortedGroups) {
|
|
152
|
+
// Sum ALL completions rows in this group — each OTEL attribute combination
|
|
153
|
+
// (e.g. baseline vs vercel-ai) produces a separate counter, and we need
|
|
154
|
+
// the total across all of them to detect new turns.
|
|
155
|
+
const completionsRows = groupRows.filter(r => r.metric_name === COMPLETIONS_METRIC);
|
|
156
|
+
if (completionsRows.length === 0)
|
|
157
|
+
continue;
|
|
158
|
+
const newCompletions = completionsRows.reduce((sum, r) => sum + toMetricValue(r), 0);
|
|
159
|
+
if (newCompletions <= prev.completions)
|
|
160
|
+
continue;
|
|
161
|
+
// Sum values per metric across all attribute sets so that baseline (0)
|
|
162
|
+
// and real-protocol rows (non-zero) are combined correctly.
|
|
163
|
+
const current = emptyValues();
|
|
164
|
+
for (const seriesItem of SERIES) {
|
|
165
|
+
const metricRows = groupRows.filter(r => r.metric_name === seriesItem.metric);
|
|
166
|
+
current[seriesItem.label] = metricRows.reduce((sum, r) => sum + toMetricValue(r), 0);
|
|
87
167
|
}
|
|
168
|
+
turns.push({
|
|
169
|
+
turnNumber: newCompletions,
|
|
170
|
+
timestampMs: nanoToMs(completionsRows[0]),
|
|
171
|
+
values: current,
|
|
172
|
+
});
|
|
173
|
+
prev = { completions: newCompletions, values: current };
|
|
88
174
|
}
|
|
89
|
-
return
|
|
175
|
+
return { turns, finalState: prev };
|
|
90
176
|
}
|
|
91
177
|
function extractServiceName(row) {
|
|
92
178
|
const directCandidates = [row.service_name, row.service, row.serviceName];
|
|
@@ -104,185 +190,237 @@ function extractServiceName(row) {
|
|
|
104
190
|
}
|
|
105
191
|
return undefined;
|
|
106
192
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
193
|
+
/** Extract `agent.id` from the `attributes` field of a metric row. */
|
|
194
|
+
function extractAgentId(row) {
|
|
195
|
+
const attrs = row.attributes;
|
|
196
|
+
if (typeof attrs === 'string') {
|
|
197
|
+
try {
|
|
198
|
+
const parsed = JSON.parse(attrs);
|
|
199
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
200
|
+
const aid = parsed['agent.id'];
|
|
201
|
+
if (typeof aid === 'string')
|
|
202
|
+
return aid;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// ignore
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else if (typeof attrs === 'object' && attrs !== null) {
|
|
210
|
+
const aid = attrs['agent.id'];
|
|
211
|
+
if (typeof aid === 'string')
|
|
212
|
+
return aid;
|
|
213
|
+
}
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
export function TokenUsageChart({ serviceName, agentId, apiKey, runUrl, wsRunUrl, liveSystemPromptTokens, liveToolsDescriptionTokens, liveUserMessageTokens, liveAgentMessageTokens, liveToolsUsageTokens, liveTimestampMs, height = 160, }) {
|
|
217
|
+
const monitoringCache = useAgentRuntimeStore(s => s.monitoringCache);
|
|
218
|
+
const mergeTokenTurns = useAgentRuntimeStore(s => s.mergeTokenTurns);
|
|
219
|
+
const appendLocalTokenTurnFull = useAgentRuntimeStore(s => s.appendLocalTokenTurnFull);
|
|
220
|
+
const cachedEntry = useMemo(() => resolveMonitoringEntry(monitoringCache, serviceName, agentId), [agentId, monitoringCache, serviceName]);
|
|
221
|
+
const [turns, setTurns] = useState([]);
|
|
222
|
+
const initialTimestampMsRef = useRef(Date.now());
|
|
223
|
+
const cumulativeRef = useRef({
|
|
224
|
+
completions: 0,
|
|
225
|
+
values: emptyValues(),
|
|
226
|
+
});
|
|
227
|
+
// ── Reset state on source switch ──────────────────────────────
|
|
120
228
|
useEffect(() => {
|
|
121
229
|
if (!serviceName) {
|
|
122
|
-
|
|
230
|
+
setTurns([]);
|
|
231
|
+
initialTimestampMsRef.current = Date.now();
|
|
232
|
+
cumulativeRef.current = { completions: 0, values: emptyValues() };
|
|
123
233
|
return;
|
|
124
234
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
apiKey,
|
|
135
|
-
limit: 500,
|
|
136
|
-
});
|
|
137
|
-
for (const row of rows) {
|
|
138
|
-
const typedRow = row;
|
|
139
|
-
const dayKey = toDayKey(typedRow);
|
|
140
|
-
if (!dayKey) {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (!result[dayKey]) {
|
|
144
|
-
result[dayKey] = emptyDay();
|
|
145
|
-
}
|
|
146
|
-
const value = toMetricValue(typedRow);
|
|
147
|
-
result[dayKey][item.label] += value;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
}));
|
|
154
|
-
if (!cancelled) {
|
|
155
|
-
setDayData(result);
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
load();
|
|
159
|
-
return () => {
|
|
160
|
-
cancelled = true;
|
|
161
|
-
};
|
|
162
|
-
}, [apiKey, runUrl, serviceName]);
|
|
235
|
+
const hydratedTurns = (cachedEntry?.tokenTurns ?? []).map(localTokenTurnToPoint);
|
|
236
|
+
setTurns(hydratedTurns);
|
|
237
|
+
initialTimestampMsRef.current = Date.now();
|
|
238
|
+
const latestTurn = hydratedTurns[hydratedTurns.length - 1];
|
|
239
|
+
cumulativeRef.current = latestTurn
|
|
240
|
+
? { completions: latestTurn.turnNumber, values: latestTurn.values }
|
|
241
|
+
: { completions: 0, values: emptyValues() };
|
|
242
|
+
}, [agentId, cachedEntry, serviceName]);
|
|
243
|
+
// Apply immediate post-turn token totals from monitoring snapshots.
|
|
163
244
|
useEffect(() => {
|
|
164
|
-
if (!serviceName
|
|
245
|
+
if (!serviceName)
|
|
165
246
|
return;
|
|
247
|
+
const rawValues = [
|
|
248
|
+
liveSystemPromptTokens,
|
|
249
|
+
liveToolsDescriptionTokens,
|
|
250
|
+
liveUserMessageTokens,
|
|
251
|
+
liveAgentMessageTokens,
|
|
252
|
+
liveToolsUsageTokens,
|
|
253
|
+
];
|
|
254
|
+
if (!rawValues.some(v => typeof v === 'number' && Number.isFinite(v))) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const systemPromptTokens = Math.max(0, liveSystemPromptTokens ?? 0);
|
|
258
|
+
const toolsDescriptionTokens = Math.max(0, liveToolsDescriptionTokens ?? 0);
|
|
259
|
+
const userMessageTokens = Math.max(0, liveUserMessageTokens ?? 0);
|
|
260
|
+
const aiMessageTokens = Math.max(0, liveAgentMessageTokens ?? 0);
|
|
261
|
+
const toolsUsageTokens = Math.max(0, liveToolsUsageTokens ?? 0);
|
|
262
|
+
const totalTokens = systemPromptTokens +
|
|
263
|
+
toolsDescriptionTokens +
|
|
264
|
+
userMessageTokens +
|
|
265
|
+
aiMessageTokens +
|
|
266
|
+
toolsUsageTokens;
|
|
267
|
+
const timestampMs = typeof liveTimestampMs === 'number' && Number.isFinite(liveTimestampMs)
|
|
268
|
+
? liveTimestampMs
|
|
269
|
+
: Date.now();
|
|
270
|
+
appendLocalTokenTurnFull({
|
|
271
|
+
serviceName,
|
|
272
|
+
agentId,
|
|
273
|
+
timestampMs,
|
|
274
|
+
systemPromptTokens,
|
|
275
|
+
toolsDescriptionTokens,
|
|
276
|
+
userMessageTokens,
|
|
277
|
+
aiMessageTokens,
|
|
278
|
+
toolsUsageTokens,
|
|
279
|
+
totalTokens,
|
|
280
|
+
});
|
|
281
|
+
const mergedEntry = resolveMonitoringEntry(agentRuntimeStore.getState().monitoringCache, serviceName, agentId);
|
|
282
|
+
if (mergedEntry) {
|
|
283
|
+
setTurns(mergedEntry.tokenTurns.map(localTokenTurnToPoint));
|
|
166
284
|
}
|
|
285
|
+
}, [
|
|
286
|
+
agentId,
|
|
287
|
+
appendLocalTokenTurnFull,
|
|
288
|
+
liveAgentMessageTokens,
|
|
289
|
+
liveSystemPromptTokens,
|
|
290
|
+
liveTimestampMs,
|
|
291
|
+
liveToolsDescriptionTokens,
|
|
292
|
+
liveToolsUsageTokens,
|
|
293
|
+
liveUserMessageTokens,
|
|
294
|
+
serviceName,
|
|
295
|
+
]);
|
|
296
|
+
// ── WebSocket subscription (shared connection pool) ─────────
|
|
297
|
+
useEffect(() => {
|
|
298
|
+
if (!serviceName || !apiKey)
|
|
299
|
+
return;
|
|
167
300
|
const rawBaseUrl = wsRunUrl ||
|
|
168
301
|
runUrl ||
|
|
169
302
|
(typeof window !== 'undefined' ? window.location.origin : '');
|
|
170
|
-
if (!rawBaseUrl)
|
|
303
|
+
if (!rawBaseUrl)
|
|
171
304
|
return;
|
|
172
|
-
|
|
305
|
+
const baseWithProtocol = rawBaseUrl.startsWith('http://') ||
|
|
306
|
+
rawBaseUrl.startsWith('https://') ||
|
|
307
|
+
rawBaseUrl.startsWith('ws://') ||
|
|
308
|
+
rawBaseUrl.startsWith('wss://')
|
|
309
|
+
? rawBaseUrl
|
|
310
|
+
: `${typeof window !== 'undefined' &&
|
|
311
|
+
window.location.protocol === 'https:'
|
|
312
|
+
? 'https:'
|
|
313
|
+
: 'http:'}//${typeof window !== 'undefined' ? window.location.host : ''}${rawBaseUrl}`;
|
|
173
314
|
let wsUrl;
|
|
174
|
-
|
|
175
|
-
wsUrl =
|
|
315
|
+
try {
|
|
316
|
+
wsUrl = buildOtelWebSocketUrl({
|
|
317
|
+
baseUrl: baseWithProtocol,
|
|
318
|
+
token: apiKey,
|
|
319
|
+
});
|
|
176
320
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
else if (rawBaseUrl.startsWith('ws://') ||
|
|
181
|
-
rawBaseUrl.startsWith('wss://')) {
|
|
182
|
-
wsUrl = rawBaseUrl;
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
const proto = typeof window !== 'undefined' && window.location.protocol === 'https:'
|
|
186
|
-
? 'wss:'
|
|
187
|
-
: 'ws:';
|
|
188
|
-
wsUrl = `${proto}//${typeof window !== 'undefined' ? window.location.host : ''}${rawBaseUrl}`;
|
|
321
|
+
catch {
|
|
322
|
+
return;
|
|
189
323
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const rows = Array.isArray(msg.data) ? msg.data : [];
|
|
199
|
-
const matchingRows = rows.filter(row => extractServiceName(row) === serviceName);
|
|
200
|
-
if (matchingRows.length === 0) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
// Update chart directly from WS data (bypasses HTTP fetch which
|
|
204
|
-
// may fail due to CORS when the UI runs on a different origin).
|
|
205
|
-
setDayData(prev => {
|
|
206
|
-
const updated = { ...prev };
|
|
207
|
-
for (const row of matchingRows) {
|
|
208
|
-
const metricName = row.metric_name;
|
|
209
|
-
const seriesItem = SERIES.find(s => s.metric === metricName);
|
|
210
|
-
if (!seriesItem) {
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
const dayKey = toDayKey(row);
|
|
214
|
-
if (!dayKey) {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (!updated[dayKey]) {
|
|
218
|
-
updated[dayKey] = emptyDay();
|
|
219
|
-
}
|
|
220
|
-
else {
|
|
221
|
-
// Clone to avoid mutating prev
|
|
222
|
-
updated[dayKey] = { ...updated[dayKey] };
|
|
223
|
-
}
|
|
224
|
-
const value = toMetricValue(row);
|
|
225
|
-
updated[dayKey][seriesItem.label] += value;
|
|
226
|
-
}
|
|
227
|
-
return updated;
|
|
228
|
-
});
|
|
324
|
+
const unsubscribe = subscribeOtelWs(wsUrl, msg => {
|
|
325
|
+
if (msg.signal !== 'metrics')
|
|
326
|
+
return;
|
|
327
|
+
const rows = Array.isArray(msg.data) ? msg.data : [];
|
|
328
|
+
let matchingRows = rows.filter(row => extractServiceName(row) === serviceName);
|
|
329
|
+
// Filter by agent.id when specified.
|
|
330
|
+
if (agentId) {
|
|
331
|
+
matchingRows = matchingRows.filter(row => extractAgentId(row) === agentId);
|
|
229
332
|
}
|
|
230
|
-
|
|
333
|
+
if (matchingRows.length === 0)
|
|
231
334
|
return;
|
|
335
|
+
const { turns: newTurns, finalState } = extractTurnsFromRows(matchingRows, cumulativeRef.current);
|
|
336
|
+
if (newTurns.length > 0) {
|
|
337
|
+
cumulativeRef.current = finalState;
|
|
338
|
+
mergeTokenTurns({
|
|
339
|
+
serviceName,
|
|
340
|
+
agentId,
|
|
341
|
+
turns: newTurns.map(pointToLocalTokenTurn),
|
|
342
|
+
});
|
|
343
|
+
const mergedEntry = resolveMonitoringEntry(agentRuntimeStore.getState().monitoringCache, serviceName, agentId);
|
|
344
|
+
if (mergedEntry) {
|
|
345
|
+
setTurns(mergedEntry.tokenTurns.map(localTokenTurnToPoint));
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
setTurns(prev => [...prev, ...newTurns]);
|
|
349
|
+
}
|
|
232
350
|
}
|
|
233
|
-
};
|
|
234
|
-
return
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
},
|
|
265
|
-
yAxis: SERIES.map((_, index) => ({
|
|
266
|
-
type: 'value',
|
|
267
|
-
scale: true,
|
|
268
|
-
show: false,
|
|
269
|
-
splitLine: {
|
|
270
|
-
show: index === 0,
|
|
271
|
-
lineStyle: { color: '#f0f0f0' },
|
|
351
|
+
});
|
|
352
|
+
return unsubscribe;
|
|
353
|
+
}, [agentId, apiKey, mergeTokenTurns, runUrl, serviceName, wsRunUrl]);
|
|
354
|
+
// ── Chart options ─────────────────────────────────────────────
|
|
355
|
+
const option = useMemo(() => {
|
|
356
|
+
const legendLabels = Array.from(new Set(SERIES.map(item => item.label)));
|
|
357
|
+
const baselineTimestampMs = turns.length > 0
|
|
358
|
+
? Math.max(0, Math.min(initialTimestampMsRef.current, turns[0].timestampMs - 1))
|
|
359
|
+
: initialTimestampMsRef.current;
|
|
360
|
+
return {
|
|
361
|
+
animation: false,
|
|
362
|
+
animationDuration: 0,
|
|
363
|
+
animationDurationUpdate: 0,
|
|
364
|
+
tooltip: {
|
|
365
|
+
trigger: 'axis',
|
|
366
|
+
textStyle: { fontSize: 10 },
|
|
367
|
+
confine: true,
|
|
368
|
+
},
|
|
369
|
+
legend: {
|
|
370
|
+
data: legendLabels,
|
|
371
|
+
top: 0,
|
|
372
|
+
textStyle: { fontSize: 9 },
|
|
373
|
+
itemWidth: 10,
|
|
374
|
+
itemHeight: 8,
|
|
375
|
+
itemGap: 6,
|
|
376
|
+
},
|
|
377
|
+
grid: {
|
|
378
|
+
left: 45,
|
|
379
|
+
right: 15,
|
|
380
|
+
top: 24,
|
|
381
|
+
bottom: 20,
|
|
272
382
|
},
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
383
|
+
xAxis: {
|
|
384
|
+
type: 'time',
|
|
385
|
+
min: 'dataMin',
|
|
386
|
+
max: 'dataMax',
|
|
387
|
+
axisLabel: { fontSize: 9 },
|
|
388
|
+
axisLine: { lineStyle: { color: '#d0d7de' } },
|
|
389
|
+
},
|
|
390
|
+
yAxis: {
|
|
391
|
+
type: 'value',
|
|
392
|
+
axisLabel: {
|
|
393
|
+
fontSize: 9,
|
|
394
|
+
formatter: (v) => {
|
|
395
|
+
if (v >= 1000)
|
|
396
|
+
return `${(v / 1000).toFixed(1)}K`;
|
|
397
|
+
return String(v);
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
splitLine: {
|
|
401
|
+
show: true,
|
|
402
|
+
lineStyle: { color: '#f0f0f0' },
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
series: SERIES.map(item => ({
|
|
406
|
+
name: item.label,
|
|
407
|
+
type: 'line',
|
|
408
|
+
animation: false,
|
|
409
|
+
smooth: false,
|
|
410
|
+
lineStyle: { width: 2 },
|
|
411
|
+
areaStyle: { opacity: 0.15 },
|
|
412
|
+
symbol: 'circle',
|
|
413
|
+
symbolSize: 4,
|
|
414
|
+
data: turns.length > 0
|
|
415
|
+
? [
|
|
416
|
+
[baselineTimestampMs, 0],
|
|
417
|
+
...turns.map(t => [t.timestampMs, t.values[item.label]]),
|
|
418
|
+
]
|
|
419
|
+
: [[initialTimestampMsRef.current, 0]],
|
|
420
|
+
})),
|
|
421
|
+
color: ['#2da44e', '#0969da', '#8250df', '#bf8700', '#cf222e'],
|
|
422
|
+
};
|
|
423
|
+
}, [turns]);
|
|
286
424
|
return (_jsx(ReactECharts, { option: option, style: { height, width: '100%' }, opts: { renderer: 'canvas' }, notMerge: true, lazyUpdate: true }));
|
|
287
425
|
}
|
|
288
426
|
export default TokenUsageChart;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TurnGraphChart — ECharts force-directed graph built from OTEL traces.
|
|
3
|
+
*
|
|
4
|
+
* Each graph run emitted by ``run_graph_with_telemetry`` /
|
|
5
|
+
* ``run_beta_graph_with_telemetry`` produces one OTEL trace whose spans map
|
|
6
|
+
* directly to pydantic-graph nodes. This component:
|
|
7
|
+
*
|
|
8
|
+
* 1. Fetches recent traces from the Datalayer OTEL service using
|
|
9
|
+
* ``createOtelClient`` from ``@datalayer/core/lib/otel`` (TypeScript-side
|
|
10
|
+
* fetch, Python-side emission).
|
|
11
|
+
* 2. Filters and groups spans by ``agent.id`` attribute and ``trace_id``.
|
|
12
|
+
* 3. Renders the latest run as an ECharts ``graph`` type (force-directed),
|
|
13
|
+
* with:
|
|
14
|
+
* - Node size ∝ span duration
|
|
15
|
+
* - Node colour by type (start / step / end / error)
|
|
16
|
+
* - Edges following the parent→child span hierarchy
|
|
17
|
+
* - Rich hover tooltips (duration, type, error)
|
|
18
|
+
* 4. Shows a small "run history" selector so the user can browse past runs.
|
|
19
|
+
*/
|
|
20
|
+
import { type CSSProperties } from 'react';
|
|
21
|
+
export interface TurnGraphChartProps {
|
|
22
|
+
/** OTEL service name — typically ``"agent-runtimes"``. */
|
|
23
|
+
serviceName: string;
|
|
24
|
+
/** Filter spans to this agent ID (matches ``agent.id`` span attribute). */
|
|
25
|
+
agentId?: string;
|
|
26
|
+
/** Base URL for the Datalayer OTEL service. */
|
|
27
|
+
runUrl?: string;
|
|
28
|
+
/** JWT / API key for OTEL auth. */
|
|
29
|
+
apiKey?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Auto-refresh interval in ms (default 10 000).
|
|
32
|
+
* Set to 0 to disable polling.
|
|
33
|
+
*/
|
|
34
|
+
autoRefreshMs?: number;
|
|
35
|
+
height?: number | string;
|
|
36
|
+
style?: CSSProperties;
|
|
37
|
+
}
|
|
38
|
+
export declare const TurnGraphChart: React.FC<TurnGraphChartProps>;
|
|
39
|
+
export default TurnGraphChart;
|