@datalayer/agent-runtimes 1.3.84 → 1.3.86
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/lib/chat/ChatFloating.d.ts +5 -4
- package/lib/chat/ChatFloating.js +8 -6
- package/lib/chat/ChatLanguage.d.ts +17 -0
- package/lib/chat/ChatLanguage.js +29 -0
- package/lib/chat/assistant/AssistantStage.d.ts +4 -4
- package/lib/chat/assistant/AssistantStage.js +4 -2
- package/lib/chat/assistant/BalloonParts.d.ts +2 -1
- package/lib/chat/assistant/BalloonParts.js +12 -5
- package/lib/chat/assistant/BalloonVisual.js +3 -1
- package/lib/chat/assistant/ConversationBalloon.d.ts +1 -1
- package/lib/chat/assistant/ConversationBalloon.js +3 -2
- package/lib/chat/assistant/SpeechBalloon.d.ts +1 -1
- package/lib/chat/assistant/SpeechBalloon.js +10 -7
- package/lib/chat/assistant/toolLine.d.ts +8 -4
- package/lib/chat/assistant/toolLine.js +13 -9
- package/lib/chat/base/ChatBase.js +14 -11
- package/lib/chat/display/EmptyState.js +3 -1
- package/lib/chat/index.d.ts +2 -0
- package/lib/chat/index.js +3 -0
- package/lib/chat/messages/ChatMessages.js +3 -1
- package/lib/chat/messages/TurnFooter.js +3 -1
- package/lib/chat/prompt/InputPrompt.js +3 -1
- package/lib/chat/prompt/InputPromptLexical.js +3 -1
- package/lib/chat/prompt/footer/InPromptFooter.js +3 -1
- package/lib/chat/tools/ToolApprovalBanner.js +5 -3
- package/lib/chat/tools/ToolApprovalDialog.js +5 -3
- package/lib/chat/tools/ToolCallDisplay.js +29 -25
- package/lib/chat/words.d.ts +102 -0
- package/lib/chat/words.js +460 -0
- package/lib/components/a2ui/custom/index.d.ts +39 -0
- package/lib/components/a2ui/custom/index.js +95 -0
- package/lib/components/a2ui/custom/sandbox.d.ts +73 -0
- package/lib/components/a2ui/custom/sandbox.js +188 -0
- package/lib/components/a2ui/datalayer/index.d.ts +1 -1
- package/lib/components/a2ui/datalayer/index.js +1 -1
- package/lib/components/a2ui/datalayer/schema.d.ts +10 -0
- package/lib/components/a2ui/datalayer/schema.js +12 -1
- package/lib/components/a2ui/index.d.ts +1 -0
- package/lib/components/a2ui/index.js +1 -0
- package/lib/components/a2ui/visibleWhen.d.ts +12 -1
- package/lib/components/a2ui/visibleWhen.js +24 -3
- package/lib/components/inspector/AgentInspector.d.ts +25 -44
- package/lib/components/inspector/AgentInspector.js +27 -215
- package/lib/components/inspector/AgentInspectorDialog.d.ts +6 -6
- package/lib/components/inspector/AgentInspectorDialog.js +2 -2
- package/lib/components/inspector/a2aSpans.d.ts +51 -0
- package/lib/components/inspector/a2aSpans.js +373 -0
- package/lib/components/inspector/agentSpans.d.ts +120 -0
- package/lib/components/inspector/agentSpans.js +253 -0
- package/lib/components/inspector/chatSpans.d.ts +36 -0
- package/lib/components/inspector/chatSpans.js +50 -0
- package/lib/components/inspector/classify.d.ts +11 -8
- package/lib/components/inspector/classify.js +23 -24
- package/lib/components/inspector/index.d.ts +7 -7
- package/lib/components/inspector/index.js +7 -7
- package/lib/components/teams/A2ATeamGraph.d.ts +117 -22
- package/lib/components/teams/A2ATeamGraph.js +217 -97
- package/lib/components/teams/SceneTranscript.d.ts +29 -0
- package/lib/components/teams/SceneTranscript.js +54 -0
- package/lib/components/teams/SceneView.d.ts +41 -0
- package/lib/components/teams/SceneView.js +28 -0
- package/lib/components/teams/index.d.ts +4 -0
- package/lib/components/teams/index.js +4 -0
- package/lib/components/teams/sceneStage.d.ts +120 -0
- package/lib/components/teams/sceneStage.js +170 -0
- package/lib/components/teams/sceneTranscript.d.ts +74 -0
- package/lib/components/teams/sceneTranscript.js +294 -0
- package/lib/components/teams/teamConnections.d.ts +2 -2
- package/lib/components/teams/teamConnections.js +3 -3
- package/lib/components/teams/useA2ATeam.d.ts +94 -22
- package/lib/components/teams/useA2ATeam.js +356 -140
- package/lib/examples/AgentA2ATeamExample.d.ts +10 -9
- package/lib/examples/AgentA2ATeamExample.js +70 -60
- package/lib/examples/AssistantExample.js +2 -2
- package/lib/examples/ScenesExample.d.ts +26 -0
- package/lib/examples/ScenesExample.js +241 -0
- package/lib/examples/example-selector.js +1 -0
- package/lib/examples/exampleGroups.js +3 -0
- package/lib/examples/index.d.ts +1 -0
- package/lib/examples/index.js +1 -0
- package/lib/examples/main.js +4 -0
- package/lib/loop/apps/AppComposer.d.ts +2 -1
- package/lib/loop/apps/AppComposer.js +6 -3
- package/lib/loop/apps/AppRenderer.d.ts +2 -1
- package/lib/loop/apps/AppRenderer.js +34 -25
- package/lib/loop/apps/appspec.d.ts +11 -1
- package/lib/loop/apps/appspec.js +115 -1
- package/lib/loop/apps/checks.d.ts +8 -0
- package/lib/loop/apps/checks.js +106 -4
- package/lib/loop/apps/customComponents.d.ts +41 -0
- package/lib/loop/apps/customComponents.js +260 -0
- package/lib/loop/apps/hostTools.d.ts +8 -1
- package/lib/loop/apps/hostTools.js +9 -0
- package/lib/loop/apps/index.d.ts +1 -0
- package/lib/loop/apps/index.js +1 -0
- package/lib/loop/core/a2uiComponents.d.ts +10 -0
- package/lib/loop/core/a2uiComponents.js +15 -0
- package/lib/loop/core/index.d.ts +1 -0
- package/lib/loop/core/index.js +3 -0
- package/lib/loop/embed/AppEmbed.d.ts +11 -3
- package/lib/loop/embed/AppEmbed.js +42 -16
- package/lib/loop/embed/element.d.ts +5 -0
- package/lib/loop/embed/element.js +61 -5
- package/lib/loop/embed/embedConfig.d.ts +8 -0
- package/lib/loop/embed/embedConfig.js +18 -0
- package/lib/loop/embed/hostBridge.d.ts +28 -2
- package/lib/loop/embed/hostBridge.js +22 -2
- package/lib/loop/index.d.ts +4 -3
- package/lib/loop/index.js +4 -3
- package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +7 -1
- package/lib/loop/plugins/a2ui-surface/InlineSurface.js +7 -2
- package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +4 -1
- package/lib/loop/plugins/a2ui-surface/ownComponents.d.ts +6 -0
- package/lib/loop/plugins/a2ui-surface/ownComponents.js +30 -0
- package/lib/loop/plugins/app-components/index.d.ts +26 -0
- package/lib/loop/plugins/app-components/index.js +56 -0
- package/lib/loop/plugins/app-elements/AppElements.js +4 -1
- package/lib/loop/plugins/app-page/AppPage.d.ts +9 -0
- package/lib/loop/plugins/app-page/AppPage.js +106 -8
- package/lib/loop/plugins/app-page/appPageModel.d.ts +17 -1
- package/lib/loop/plugins/app-page/appPageModel.js +120 -4
- package/lib/loop/plugins/app-page/index.d.ts +1 -0
- package/lib/loop/plugins/app-page/index.js +1 -0
- package/lib/loop/plugins/app-page/pageRun.d.ts +46 -0
- package/lib/loop/plugins/app-page/pageRun.js +111 -0
- package/lib/loop/plugins/assistant/index.js +8 -6
- package/lib/loop/plugins/chat/ChatView.js +6 -4
- package/lib/loop/plugins/chat/index.d.ts +1 -1
- package/lib/loop/plugins/chat/index.js +2 -1
- package/lib/models/PageTag.d.ts +1 -1
- package/lib/runtimes/browser/a2aPeer.d.ts +2 -1
- package/lib/specs/actions.js +78 -1
- package/lib/specs/apps.d.ts +5 -0
- package/lib/specs/apps.js +1384 -231
- package/lib/specs/appspecSchema.js +191 -1
- package/lib/specs/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- package/lib/specs/mcpServers.js +8 -2
- package/lib/specs/scenes.d.ts +21 -0
- package/lib/specs/scenes.js +1191 -0
- package/lib/specs/teams/teams.d.ts +3 -0
- package/lib/specs/teams/teams.js +249 -0
- package/lib/types/agentspecs.d.ts +70 -0
- package/lib/types/chat.d.ts +6 -5
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/scenes.d.ts +207 -0
- package/lib/types/scenes.js +5 -0
- package/lib/types/teams.d.ts +13 -2
- package/package.json +4 -4
- package/scripts/codegen/generate_agents.py +1 -0
- package/scripts/codegen/generate_apps.py +4 -2
- package/scripts/codegen/generate_scenes.py +301 -0
- package/scripts/codegen/generate_teams.py +15 -2
- package/lib/components/inspector/JsonTree.d.ts +0 -24
- package/lib/components/inspector/JsonTree.js +0 -89
- package/lib/components/inspector/a2aInspect.d.ts +0 -56
- package/lib/components/inspector/a2aInspect.js +0 -478
- package/lib/components/inspector/agentInspector.d.ts +0 -179
- package/lib/components/inspector/agentInspector.js +0 -267
- package/lib/components/inspector/chatInspect.d.ts +0 -38
- package/lib/components/inspector/chatInspect.js +0 -65
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* What agents do in the page, as OpenTelemetry spans: the record the Agent
|
|
7
|
+
* Inspector reads. A turn is an `invoke_agent` span, each tool call an
|
|
8
|
+
* `execute_tool` span under it — the GenAI semantic conventions the runtime's
|
|
9
|
+
* own spans follow (pydantic-ai's): `gen_ai.operation.name`,
|
|
10
|
+
* `gen_ai.agent.name`, `gen_ai.request.model`, `gen_ai.tool.name`,
|
|
11
|
+
* `gen_ai.tool.call.id`, `gen_ai.tool.call.arguments` and `.result` (JSON
|
|
12
|
+
* text), `gen_ai.input.messages` and `gen_ai.output.messages`, the
|
|
13
|
+
* `gen_ai.usage.*` tokens. An agent is a service: `service.name` is its name.
|
|
14
|
+
*
|
|
15
|
+
* Where a tool comes from — an MCP server, the page (a frontend tool), a
|
|
16
|
+
* skill, codemode, the runtime — no convention says: `datalayer.tool.kind`
|
|
17
|
+
* does, beside `gen_ai.tool.type`, with the tool's marks
|
|
18
|
+
* (`datalayer.mark.icon`, `datalayer.mark.emoji`).
|
|
19
|
+
*
|
|
20
|
+
* The tracer is core's (`createOtelLiveTracer`): one a page, shared by its
|
|
21
|
+
* chats and its team (`AgentInspectorProvider`, or the `inspector` prop).
|
|
22
|
+
*
|
|
23
|
+
* @module components/inspector/agentSpans
|
|
24
|
+
*/
|
|
25
|
+
import { createContext, createElement, useContext, useState, } from 'react';
|
|
26
|
+
import { createOtelLiveTracer, } from '@datalayer/core/lib/otel/live';
|
|
27
|
+
/** The attributes the record uses: the GenAI conventions, and a few of Datalayer's. */
|
|
28
|
+
export const AGENT_SPAN_ATTRIBUTES = {
|
|
29
|
+
operation: 'gen_ai.operation.name',
|
|
30
|
+
agentName: 'gen_ai.agent.name',
|
|
31
|
+
conversationId: 'gen_ai.conversation.id',
|
|
32
|
+
requestModel: 'gen_ai.request.model',
|
|
33
|
+
inputMessages: 'gen_ai.input.messages',
|
|
34
|
+
outputMessages: 'gen_ai.output.messages',
|
|
35
|
+
inputTokens: 'gen_ai.usage.input_tokens',
|
|
36
|
+
outputTokens: 'gen_ai.usage.output_tokens',
|
|
37
|
+
toolName: 'gen_ai.tool.name',
|
|
38
|
+
toolCallId: 'gen_ai.tool.call.id',
|
|
39
|
+
toolArguments: 'gen_ai.tool.call.arguments',
|
|
40
|
+
toolResult: 'gen_ai.tool.call.result',
|
|
41
|
+
toolType: 'gen_ai.tool.type',
|
|
42
|
+
/** `mcp`, `frontend`, `skill`, `codemode`, `runtime`. */
|
|
43
|
+
toolKind: 'datalayer.tool.kind',
|
|
44
|
+
markIcon: 'datalayer.mark.icon',
|
|
45
|
+
markEmoji: 'datalayer.mark.emoji',
|
|
46
|
+
/** How the page knows of a span another process did: `a2a.status`. */
|
|
47
|
+
origin: 'datalayer.span.origin',
|
|
48
|
+
errorType: 'error.type',
|
|
49
|
+
};
|
|
50
|
+
const A = AGENT_SPAN_ATTRIBUTES;
|
|
51
|
+
/** Every kind, in the order the filters list them. */
|
|
52
|
+
export const AGENT_TOOL_KINDS = [
|
|
53
|
+
'mcp',
|
|
54
|
+
'frontend',
|
|
55
|
+
'skill',
|
|
56
|
+
'codemode',
|
|
57
|
+
'runtime',
|
|
58
|
+
];
|
|
59
|
+
/** A value as an attribute carries it: JSON text. */
|
|
60
|
+
export function jsonAttribute(value) {
|
|
61
|
+
if (value === undefined) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
return JSON.stringify(value);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return String(value);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** A line short enough for a row. */
|
|
72
|
+
export function shortLine(text, length = 160) {
|
|
73
|
+
const flat = text.replace(/\s+/g, ' ').trim();
|
|
74
|
+
return flat.length > length ? `${flat.slice(0, length - 1)}…` : flat;
|
|
75
|
+
}
|
|
76
|
+
/** A call's arguments, in a few words: `kind=customer, limit=100`. */
|
|
77
|
+
export function argumentsLine(args, length = 100) {
|
|
78
|
+
if (!args || typeof args !== 'object') {
|
|
79
|
+
return args === undefined ? '' : shortLine(String(args), length);
|
|
80
|
+
}
|
|
81
|
+
const parts = Object.entries(args).map(([key, value]) => `${key}=${typeof value === 'string' ? value : JSON.stringify(value)}`);
|
|
82
|
+
return shortLine(parts.join(', '), length);
|
|
83
|
+
}
|
|
84
|
+
/** A message in the GenAI conventions' shape: its role and its text part. */
|
|
85
|
+
function messages(role, text) {
|
|
86
|
+
return JSON.stringify([{ role, parts: [{ type: 'text', content: text }] }]);
|
|
87
|
+
}
|
|
88
|
+
/** A turn of an agent: from the question to the answer. */
|
|
89
|
+
export function startAgentTurn(tracer, turn) {
|
|
90
|
+
return tracer.startSpan(`invoke_agent ${turn.agent}`, {
|
|
91
|
+
serviceName: turn.agent,
|
|
92
|
+
parent: null,
|
|
93
|
+
attributes: {
|
|
94
|
+
[A.operation]: 'invoke_agent',
|
|
95
|
+
[A.agentName]: turn.agent,
|
|
96
|
+
[A.requestModel]: turn.model || undefined,
|
|
97
|
+
[A.conversationId]: turn.conversationId,
|
|
98
|
+
[A.inputMessages]: messages('user', turn.prompt),
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** End a turn: its answer, its tokens, or its error. */
|
|
103
|
+
export function endAgentTurn(span, outcome) {
|
|
104
|
+
const usage = outcome.usage;
|
|
105
|
+
span.setAttributes({
|
|
106
|
+
[A.outputMessages]: outcome.text
|
|
107
|
+
? messages('assistant', outcome.text)
|
|
108
|
+
: undefined,
|
|
109
|
+
[A.inputTokens]: usage?.inputTokens ?? usage?.promptTokens,
|
|
110
|
+
[A.outputTokens]: usage?.outputTokens ?? usage?.completionTokens,
|
|
111
|
+
});
|
|
112
|
+
if (outcome.error) {
|
|
113
|
+
span.setAttribute(A.errorType, 'error');
|
|
114
|
+
span.recordException(new Error(outcome.error));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
span.setStatus('OK');
|
|
118
|
+
}
|
|
119
|
+
span.end();
|
|
120
|
+
}
|
|
121
|
+
/** `gen_ai.tool.type` for a kind: an MCP server's tool is an extension. */
|
|
122
|
+
function toolType(kind) {
|
|
123
|
+
return kind === 'mcp' ? 'extension' : 'function';
|
|
124
|
+
}
|
|
125
|
+
/** A tool call: its name, id, arguments, kind and marks. */
|
|
126
|
+
export function startToolCall(tracer, call) {
|
|
127
|
+
return tracer.startSpan(`execute_tool ${call.name}`, {
|
|
128
|
+
serviceName: call.agent,
|
|
129
|
+
...(call.parent ? { parent: call.parent } : {}),
|
|
130
|
+
attributes: {
|
|
131
|
+
[A.operation]: 'execute_tool',
|
|
132
|
+
[A.agentName]: call.agent,
|
|
133
|
+
[A.toolName]: call.name,
|
|
134
|
+
[A.toolCallId]: call.id,
|
|
135
|
+
[A.toolArguments]: jsonAttribute(call.args ?? {}),
|
|
136
|
+
[A.toolType]: toolType(call.tool.kind),
|
|
137
|
+
[A.toolKind]: call.tool.kind,
|
|
138
|
+
[A.markIcon]: call.tool.icon,
|
|
139
|
+
[A.markEmoji]: call.tool.emoji,
|
|
140
|
+
[A.origin]: call.origin,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/** End a tool call: its result, or its error. */
|
|
145
|
+
export function endToolCall(span, outcome) {
|
|
146
|
+
span.setAttribute(A.toolResult, jsonAttribute(outcome.result));
|
|
147
|
+
if (outcome.error) {
|
|
148
|
+
span.setAttribute(A.errorType, 'tool_error');
|
|
149
|
+
span.recordException(new Error(outcome.error));
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
span.setStatus('OK');
|
|
153
|
+
}
|
|
154
|
+
span.end();
|
|
155
|
+
}
|
|
156
|
+
/** Where a span comes from, for the Inspector's *Source* filter. */
|
|
157
|
+
export function sourceOfSpan(span) {
|
|
158
|
+
const attributes = span.attributes ?? {};
|
|
159
|
+
if (attributes[A.toolKind]) {
|
|
160
|
+
return String(attributes[A.toolKind]);
|
|
161
|
+
}
|
|
162
|
+
if (attributes['rpc.service'] === 'a2a' ||
|
|
163
|
+
span.span_name.startsWith('a2a ')) {
|
|
164
|
+
return 'a2a';
|
|
165
|
+
}
|
|
166
|
+
if (attributes[A.operation] === 'invoke_agent') {
|
|
167
|
+
return 'model';
|
|
168
|
+
}
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
/** A source, in words. */
|
|
172
|
+
export const SPAN_SOURCE_LABELS = {
|
|
173
|
+
a2a: 'A2A',
|
|
174
|
+
mcp: 'MCP',
|
|
175
|
+
frontend: 'Frontend tool',
|
|
176
|
+
skill: 'Skill',
|
|
177
|
+
codemode: 'Codemode',
|
|
178
|
+
runtime: 'Runtime tool',
|
|
179
|
+
model: 'Model',
|
|
180
|
+
};
|
|
181
|
+
/** The text of the first message of a GenAI messages attribute. */
|
|
182
|
+
function messageText(value) {
|
|
183
|
+
if (typeof value !== 'string') {
|
|
184
|
+
return '';
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const parsed = JSON.parse(value);
|
|
188
|
+
return (parsed[0]?.parts ?? [])
|
|
189
|
+
.map(part => (typeof part.content === 'string' ? part.content : ''))
|
|
190
|
+
.join('');
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return '';
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/** One line that says what a span carried: a call's arguments, a turn's question and answer, a message's words. */
|
|
197
|
+
export function describeAgentSpan(span) {
|
|
198
|
+
const attributes = span.attributes ?? {};
|
|
199
|
+
const operation = attributes[A.operation];
|
|
200
|
+
if (operation === 'execute_tool') {
|
|
201
|
+
let args;
|
|
202
|
+
try {
|
|
203
|
+
args = JSON.parse(String(attributes[A.toolArguments] ?? '{}'));
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
args = attributes[A.toolArguments];
|
|
207
|
+
}
|
|
208
|
+
return argumentsLine(args) || undefined;
|
|
209
|
+
}
|
|
210
|
+
if (operation === 'invoke_agent') {
|
|
211
|
+
const asked = messageText(attributes[A.inputMessages]);
|
|
212
|
+
const said = messageText(attributes[A.outputMessages]);
|
|
213
|
+
const input = Number(attributes[A.inputTokens] ?? 0);
|
|
214
|
+
const output = Number(attributes[A.outputTokens] ?? 0);
|
|
215
|
+
const tokens = input + output ? ` · ${input + output} tokens` : '';
|
|
216
|
+
return (shortLine(said ? `${shortLine(asked, 60)} → ${said}` : asked) + tokens);
|
|
217
|
+
}
|
|
218
|
+
const text = attributes['a2a.message.text'];
|
|
219
|
+
const state = attributes['a2a.task.state'];
|
|
220
|
+
const words = [
|
|
221
|
+
typeof state === 'string' ? stateWord(state) : '',
|
|
222
|
+
typeof text === 'string' ? shortLine(text) : '',
|
|
223
|
+
].filter(Boolean);
|
|
224
|
+
if (words.length) {
|
|
225
|
+
return words.join(' · ');
|
|
226
|
+
}
|
|
227
|
+
const skills = attributes['a2a.agent.skills'];
|
|
228
|
+
return Array.isArray(skills) ? skills.join(', ') : undefined;
|
|
229
|
+
}
|
|
230
|
+
/** `TASK_STATE_WORKING` is `working`. */
|
|
231
|
+
export function stateWord(state) {
|
|
232
|
+
return typeof state === 'string'
|
|
233
|
+
? state
|
|
234
|
+
.replace(/^TASK_STATE_/, '')
|
|
235
|
+
.toLowerCase()
|
|
236
|
+
.replace(/_/g, ' ')
|
|
237
|
+
: '';
|
|
238
|
+
}
|
|
239
|
+
/** A tracer for a page, made once (or the one given). */
|
|
240
|
+
export function useAgentInspector(given) {
|
|
241
|
+
const [own] = useState(() => given ?? createOtelLiveTracer({ serviceName: 'Agent' }));
|
|
242
|
+
return { tracer: given ?? own };
|
|
243
|
+
}
|
|
244
|
+
/** The tracer the chats under it record into. */
|
|
245
|
+
export const AgentInspectorContext = createContext(null);
|
|
246
|
+
/** The tracer of the nearest {@link AgentInspectorProvider}, if any. */
|
|
247
|
+
export function useAgentInspectorTracer() {
|
|
248
|
+
return useContext(AgentInspectorContext);
|
|
249
|
+
}
|
|
250
|
+
/** Every chat under it records what its agent does into `tracer`. */
|
|
251
|
+
export function AgentInspectorProvider({ tracer, children, }) {
|
|
252
|
+
return createElement(AgentInspectorContext.Provider, { value: tracer }, children);
|
|
253
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A chat's agent, as OpenTelemetry spans: each turn an `invoke_agent` span
|
|
3
|
+
* (the prompt, the answer, the tokens), each tool call an `execute_tool` span
|
|
4
|
+
* under it from its start to its end — an MCP server's, a skill, a frontend
|
|
5
|
+
* tool the page runs, codemode's, a tool the runtime runs — whatever
|
|
6
|
+
* transport the chat speaks (AG-UI, Vercel AI, A2A, the in-browser harness):
|
|
7
|
+
* `ChatBase` tells it what its adapter told.
|
|
8
|
+
*
|
|
9
|
+
* @module components/inspector/chatSpans
|
|
10
|
+
*/
|
|
11
|
+
import type { OtelLiveTracer } from '@datalayer/core/lib/otel/live';
|
|
12
|
+
import type { MarkedMcpServer } from '../../chat/marks/toolMarks';
|
|
13
|
+
import { type TurnUsage } from './agentSpans';
|
|
14
|
+
export type ChatSpanRecorder = {
|
|
15
|
+
turnStarted: (prompt: string, model?: string) => void;
|
|
16
|
+
turnEnded: (outcome: {
|
|
17
|
+
text?: string;
|
|
18
|
+
usage?: TurnUsage;
|
|
19
|
+
error?: string;
|
|
20
|
+
}) => void;
|
|
21
|
+
toolStarted: (call: {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
args: Record<string, unknown>;
|
|
25
|
+
}) => void;
|
|
26
|
+
toolEnded: (id: string, outcome: {
|
|
27
|
+
result?: unknown;
|
|
28
|
+
error?: string;
|
|
29
|
+
}) => void;
|
|
30
|
+
};
|
|
31
|
+
/** A recorder for one chat's agent (`agent`) into `tracer`. */
|
|
32
|
+
export declare function chatSpanRecorder(tracer: OtelLiveTracer, agent: () => string, options?: {
|
|
33
|
+
/** The page's own tools, by name: frontend tools. */
|
|
34
|
+
frontendTools?: () => readonly string[];
|
|
35
|
+
mcpServers?: () => readonly MarkedMcpServer[];
|
|
36
|
+
}): ChatSpanRecorder;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { endAgentTurn, endToolCall, startAgentTurn, startToolCall, } from './agentSpans';
|
|
6
|
+
import { classifyToolCall } from './classify';
|
|
7
|
+
/** A recorder for one chat's agent (`agent`) into `tracer`. */
|
|
8
|
+
export function chatSpanRecorder(tracer, agent, options = {}) {
|
|
9
|
+
let turn;
|
|
10
|
+
const tools = new Map();
|
|
11
|
+
return {
|
|
12
|
+
turnStarted(prompt, model) {
|
|
13
|
+
turn = startAgentTurn(tracer, { agent: agent(), prompt, model });
|
|
14
|
+
},
|
|
15
|
+
turnEnded(outcome) {
|
|
16
|
+
if (!turn) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
endAgentTurn(turn, outcome);
|
|
20
|
+
turn = undefined;
|
|
21
|
+
},
|
|
22
|
+
toolStarted({ id, name, args }) {
|
|
23
|
+
const known = tools.get(id);
|
|
24
|
+
if (known) {
|
|
25
|
+
// Arguments that stream in: the latest whole.
|
|
26
|
+
known.setAttribute('gen_ai.tool.call.arguments', JSON.stringify(args ?? {}));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
tools.set(id, startToolCall(tracer, {
|
|
30
|
+
agent: agent(),
|
|
31
|
+
name,
|
|
32
|
+
id,
|
|
33
|
+
args,
|
|
34
|
+
tool: classifyToolCall(name, args, {
|
|
35
|
+
frontendTools: options.frontendTools?.(),
|
|
36
|
+
mcpServers: options.mcpServers?.(),
|
|
37
|
+
}),
|
|
38
|
+
...(turn ? { parent: turn.context } : {}),
|
|
39
|
+
}));
|
|
40
|
+
},
|
|
41
|
+
toolEnded(id, outcome) {
|
|
42
|
+
const span = tools.get(id);
|
|
43
|
+
if (!span) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
tools.delete(id);
|
|
47
|
+
endToolCall(span, outcome);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Where a tool call comes from, for
|
|
3
|
-
* catalogue says of its name. Apart from the
|
|
4
|
-
*
|
|
2
|
+
* Where a tool call comes from, for its span (`datalayer.tool.kind`), by
|
|
3
|
+
* what the catalogue says of its name. Apart from the spans themselves,
|
|
4
|
+
* which a page can record without loading the catalogues.
|
|
5
5
|
*
|
|
6
6
|
* @module components/inspector/classify
|
|
7
7
|
*/
|
|
8
8
|
import { type MarkedMcpServer } from '../../chat/marks/toolMarks';
|
|
9
|
-
import type {
|
|
9
|
+
import type { AgentToolKind, ToolClass } from './agentSpans';
|
|
10
|
+
/** The tools codemode exposes in place of the MCP tools it reaches. */
|
|
11
|
+
export declare const CODEMODE_TOOLS: ReadonlySet<string>;
|
|
10
12
|
/**
|
|
11
|
-
* Where a tool call comes from, by who claims its name: a
|
|
12
|
-
* `mcpServers`, a frontend tool
|
|
13
|
-
* a runtime tool; otherwise `fallback` — a tool
|
|
13
|
+
* Where a tool call comes from, by who claims its name: a frontend tool
|
|
14
|
+
* (one of `frontendTools`), a skill, one of `mcpServers`, a frontend tool of
|
|
15
|
+
* the catalogue, a runtime tool, codemode's; otherwise `fallback` — a tool
|
|
16
|
+
* the runtime runs.
|
|
14
17
|
*/
|
|
15
18
|
export declare function classifyToolCall(name: string, args?: Record<string, unknown>, options?: {
|
|
16
19
|
mcpServers?: readonly MarkedMcpServer[];
|
|
17
20
|
frontendTools?: readonly string[];
|
|
18
|
-
fallback?:
|
|
21
|
+
fallback?: AgentToolKind;
|
|
19
22
|
}): ToolClass;
|
|
@@ -3,39 +3,38 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* Where a tool call comes from, for
|
|
7
|
-
* catalogue says of its name. Apart from the
|
|
8
|
-
*
|
|
6
|
+
* Where a tool call comes from, for its span (`datalayer.tool.kind`), by
|
|
7
|
+
* what the catalogue says of its name. Apart from the spans themselves,
|
|
8
|
+
* which a page can record without loading the catalogues.
|
|
9
9
|
*
|
|
10
10
|
* @module components/inspector/classify
|
|
11
11
|
*/
|
|
12
12
|
import { marksOfToolCall, } from '../../chat/marks/toolMarks';
|
|
13
|
+
/** The tools codemode exposes in place of the MCP tools it reaches. */
|
|
14
|
+
export const CODEMODE_TOOLS = new Set([
|
|
15
|
+
'list_servers',
|
|
16
|
+
'list_tool_names',
|
|
17
|
+
'search_tools',
|
|
18
|
+
'get_tool_details',
|
|
19
|
+
'execute_code',
|
|
20
|
+
]);
|
|
13
21
|
/**
|
|
14
|
-
* Where a tool call comes from, by who claims its name: a
|
|
15
|
-
* `mcpServers`, a frontend tool
|
|
16
|
-
* a runtime tool; otherwise `fallback` — a tool
|
|
22
|
+
* Where a tool call comes from, by who claims its name: a frontend tool
|
|
23
|
+
* (one of `frontendTools`), a skill, one of `mcpServers`, a frontend tool of
|
|
24
|
+
* the catalogue, a runtime tool, codemode's; otherwise `fallback` — a tool
|
|
25
|
+
* the runtime runs.
|
|
17
26
|
*/
|
|
18
27
|
export function classifyToolCall(name, args, options = {}) {
|
|
19
|
-
if (options.frontendTools?.includes(name)) {
|
|
20
|
-
const marks = marksOfToolCall(name, args, options.mcpServers);
|
|
21
|
-
return {
|
|
22
|
-
source: 'frontend-tool',
|
|
23
|
-
...(marks?.icon ? { icon: marks.icon } : {}),
|
|
24
|
-
...(marks?.emoji ? { emoji: marks.emoji } : {}),
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
28
|
const marks = marksOfToolCall(name, args, options.mcpServers);
|
|
28
|
-
const
|
|
29
|
-
? '
|
|
30
|
-
: marks?.kind
|
|
31
|
-
?
|
|
32
|
-
:
|
|
33
|
-
? '
|
|
34
|
-
:
|
|
35
|
-
? 'backend-tool'
|
|
36
|
-
: (options.fallback ?? 'backend-tool');
|
|
29
|
+
const kind = options.frontendTools?.includes(name)
|
|
30
|
+
? 'frontend'
|
|
31
|
+
: marks?.kind
|
|
32
|
+
? marks.kind
|
|
33
|
+
: CODEMODE_TOOLS.has(name)
|
|
34
|
+
? 'codemode'
|
|
35
|
+
: (options.fallback ?? 'runtime');
|
|
37
36
|
return {
|
|
38
|
-
|
|
37
|
+
kind,
|
|
39
38
|
...(marks?.icon ? { icon: marks.icon } : {}),
|
|
40
39
|
...(marks?.emoji ? { emoji: marks.emoji } : {}),
|
|
41
40
|
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Agent Inspector: what agents do —
|
|
3
|
-
* frontend
|
|
4
|
-
*
|
|
2
|
+
* The Agent Inspector: what agents do — model turns, tool calls (MCP,
|
|
3
|
+
* frontend, skills, codemode, runtime), A2A requests and what came back —
|
|
4
|
+
* recorded as OpenTelemetry spans in the page's tracer (core's
|
|
5
|
+
* `createOtelLiveTracer`) and drawn by core's OTEL view (`OtelLiveSpans`).
|
|
5
6
|
*
|
|
6
7
|
* @module components/inspector
|
|
7
8
|
*/
|
|
8
|
-
export * from './
|
|
9
|
+
export * from './agentSpans';
|
|
9
10
|
export * from './classify';
|
|
10
|
-
export * from './
|
|
11
|
-
export * from './
|
|
12
|
-
export * from './JsonTree';
|
|
11
|
+
export * from './chatSpans';
|
|
12
|
+
export * from './a2aSpans';
|
|
13
13
|
export * from './AgentInspector';
|
|
14
14
|
export * from './AgentInspectorDialog';
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* The Agent Inspector: what agents do —
|
|
7
|
-
* frontend
|
|
8
|
-
*
|
|
6
|
+
* The Agent Inspector: what agents do — model turns, tool calls (MCP,
|
|
7
|
+
* frontend, skills, codemode, runtime), A2A requests and what came back —
|
|
8
|
+
* recorded as OpenTelemetry spans in the page's tracer (core's
|
|
9
|
+
* `createOtelLiveTracer`) and drawn by core's OTEL view (`OtelLiveSpans`).
|
|
9
10
|
*
|
|
10
11
|
* @module components/inspector
|
|
11
12
|
*/
|
|
12
|
-
export * from './
|
|
13
|
+
export * from './agentSpans';
|
|
13
14
|
export * from './classify';
|
|
14
|
-
export * from './
|
|
15
|
-
export * from './
|
|
16
|
-
export * from './JsonTree';
|
|
15
|
+
export * from './chatSpans';
|
|
16
|
+
export * from './a2aSpans';
|
|
17
17
|
export * from './AgentInspector';
|
|
18
18
|
export * from './AgentInspectorDialog';
|