@datalayer/agent-runtimes 1.0.5 → 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 +157 -10
- package/lib/AgentNode.d.ts +3 -0
- package/lib/AgentNode.js +676 -0
- 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/chat/Chat.js +16 -10
- package/lib/chat/ChatFloating.js +1 -1
- package/lib/chat/ChatSidebar.js +81 -49
- package/lib/chat/base/ChatBase.js +388 -74
- 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 +29 -9
- package/lib/chat/header/ChatHeaderBase.js +26 -3
- package/lib/chat/indicators/SandboxStatusIndicator.js +82 -47
- package/lib/chat/messages/ChatMessageList.js +46 -1
- package/lib/chat/messages/ChatMessages.js +6 -2
- package/lib/chat/prompt/InputFooter.d.ts +3 -1
- package/lib/chat/prompt/InputFooter.js +8 -5
- package/lib/chat/prompt/InputPrompt.d.ts +3 -1
- package/lib/chat/prompt/InputPrompt.js +2 -2
- package/lib/chat/prompt/InputPromptFooter.d.ts +3 -1
- package/lib/chat/prompt/InputPromptFooter.js +3 -3
- package/lib/client/AgentsMixin.js +14 -0
- package/lib/config/AgentConfiguration.d.ts +22 -0
- package/lib/config/AgentConfiguration.js +319 -64
- package/lib/examples/AgUiSharedStateExample.js +2 -1
- package/lib/examples/AgentCheckpointsExample.js +3 -3
- package/lib/examples/AgentCodemodeExample.d.ts +3 -3
- package/lib/examples/AgentCodemodeExample.js +24 -12
- package/lib/examples/AgentEvalsExample.js +330 -40
- package/lib/examples/AgentGuardrailsExample.js +16 -5
- package/lib/examples/AgentHooksExample.js +27 -9
- package/lib/examples/AgentInferenceProviderExample.d.ts +3 -0
- package/lib/examples/AgentInferenceProviderExample.js +329 -0
- package/lib/examples/AgentMCPExample.js +6 -5
- package/lib/examples/AgentMemoryExample.d.ts +1 -2
- package/lib/examples/AgentMemoryExample.js +71 -22
- package/lib/examples/AgentMonitoringExample.js +5 -5
- package/lib/examples/AgentNotificationsExample.d.ts +1 -2
- package/lib/examples/AgentNotificationsExample.js +71 -22
- package/lib/examples/AgentOtelExample.js +31 -40
- package/lib/examples/AgentOutputsExample.d.ts +1 -1
- package/lib/examples/AgentOutputsExample.js +67 -16
- package/lib/examples/AgentParametersExample.js +10 -8
- package/lib/examples/AgentSandboxExample.d.ts +1 -1
- package/lib/examples/AgentSandboxExample.js +7 -6
- package/lib/examples/AgentSkillsExample.js +6 -6
- package/lib/examples/AgentSubagentsExample.d.ts +1 -1
- package/lib/examples/AgentSubagentsExample.js +6 -6
- package/lib/examples/AgentToolApprovalsExample.js +27 -11
- package/lib/examples/AgentTriggersExample.js +5 -5
- package/lib/examples/{AgentSpecsExample.d.ts → AgentspecsExample.d.ts} +2 -2
- package/lib/examples/AgentspecsExample.js +1096 -0
- package/lib/examples/ChatCustomExample.js +6 -5
- package/lib/examples/ChatExample.js +6 -5
- package/lib/examples/Lexical2Example.js +1 -1
- package/lib/examples/LexicalAgentExample.js +1 -1
- package/lib/examples/NotebookAgentExample.js +3 -3
- package/lib/examples/components/ExampleWrapper.d.ts +6 -7
- package/lib/examples/components/ExampleWrapper.js +27 -10
- package/lib/examples/example-selector.js +2 -1
- package/lib/examples/index.d.ts +2 -1
- package/lib/examples/index.js +2 -1
- package/lib/examples/lexical/initial-content.json +6 -6
- package/lib/examples/main.js +56 -16
- package/lib/examples/utils/agentId.d.ts +1 -1
- package/lib/examples/utils/agentId.js +1 -1
- package/lib/examples/utils/useExampleAgentRuntimesUrl.d.ts +5 -0
- package/lib/examples/utils/useExampleAgentRuntimesUrl.js +19 -0
- package/lib/hooks/useAIAgentsWebSocket.js +35 -0
- package/lib/hooks/useAgentRuntimes.d.ts +32 -3
- package/lib/hooks/useAgentRuntimes.js +114 -19
- package/lib/index.d.ts +1 -1
- package/lib/specs/agents/agents.d.ts +20 -13
- package/lib/specs/agents/agents.js +1267 -581
- package/lib/specs/benchmarks.d.ts +20 -0
- package/lib/specs/benchmarks.js +205 -0
- package/lib/specs/envvars.d.ts +0 -1
- package/lib/specs/envvars.js +0 -11
- package/lib/specs/evals.d.ts +10 -9
- package/lib/specs/evals.js +128 -88
- package/lib/specs/index.d.ts +0 -1
- package/lib/specs/index.js +0 -1
- package/lib/specs/models.d.ts +0 -2
- package/lib/specs/models.js +0 -15
- package/lib/specs/skills.d.ts +0 -1
- package/lib/specs/skills.js +0 -18
- package/lib/stores/agentRuntimeStore.d.ts +5 -1
- package/lib/stores/agentRuntimeStore.js +22 -8
- package/lib/stores/conversationStore.js +2 -2
- package/lib/types/agents-lifecycle.d.ts +18 -0
- package/lib/types/agents.d.ts +6 -0
- package/lib/types/agentspecs.d.ts +4 -0
- package/lib/types/benchmarks.d.ts +43 -0
- package/lib/types/benchmarks.js +5 -0
- package/lib/types/chat.d.ts +16 -0
- package/lib/types/evals.d.ts +26 -17
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/package.json +9 -5
- 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/generate_agents.py +89 -43
- package/scripts/codegen/generate_benchmarks.py +441 -0
- package/scripts/codegen/generate_evals.py +94 -16
- package/scripts/codegen/generate_events.py +0 -1
- package/lib/examples/AgentSpecsExample.js +0 -694
|
@@ -34,8 +34,8 @@ import { useCoreStore } from '@datalayer/core/lib/state';
|
|
|
34
34
|
const queryClient = new QueryClient();
|
|
35
35
|
import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel';
|
|
36
36
|
import { Chat } from '../chat';
|
|
37
|
-
const AGENT_NAME = 'monitoring-
|
|
38
|
-
const AGENT_SPEC_ID = '
|
|
37
|
+
const AGENT_NAME = 'monitoring-example-agent';
|
|
38
|
+
const AGENT_SPEC_ID = 'example-monitoring';
|
|
39
39
|
const DEFAULT_LOCAL_BASE_URL = import.meta.env.VITE_BASE_URL || 'http://localhost:8765';
|
|
40
40
|
const OTEL_BASE_URL_ENV = import.meta.env.VITE_OTEL_BASE_URL;
|
|
41
41
|
const DATALAYER_RUN_URL_ENV = import.meta.env.DATALAYER_RUN_URL;
|
|
@@ -87,7 +87,7 @@ const AgentMonitoringInner = ({ onLogout, }) => {
|
|
|
87
87
|
method: 'POST',
|
|
88
88
|
body: JSON.stringify({
|
|
89
89
|
name: agentName,
|
|
90
|
-
description: 'MCP monitoring
|
|
90
|
+
description: 'MCP monitoring example – web crawling via Tavily with live cost/token metrics',
|
|
91
91
|
agent_library: 'pydantic-ai',
|
|
92
92
|
transport: 'vercel-ai',
|
|
93
93
|
agent_spec_id: AGENT_SPEC_ID,
|
|
@@ -215,7 +215,7 @@ const AgentMonitoringInner = ({ onLogout, }) => {
|
|
|
215
215
|
justifyContent: 'center',
|
|
216
216
|
height: '100vh',
|
|
217
217
|
gap: 3,
|
|
218
|
-
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Launching local monitoring
|
|
218
|
+
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Launching local monitoring example agent..." })] }));
|
|
219
219
|
}
|
|
220
220
|
if (runtimeStatus === 'error' || hookError) {
|
|
221
221
|
return _jsx(ErrorView, { error: hookError, onLogout: onLogout });
|
|
@@ -267,7 +267,7 @@ const AgentMonitoringInner = ({ onLogout, }) => {
|
|
|
267
267
|
fontSize: 0,
|
|
268
268
|
mt: 1,
|
|
269
269
|
display: 'block',
|
|
270
|
-
}, children: ["Last close: ", monitorSocket.lastClose.detail] }))] }))] })] }), _jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, authToken: chatAuthToken, title: "Monitoring Agent", placeholder: "Ask for cost, token usage, and turn-level monitoring insights...", description: `${alerts.length} active alert${alerts.length !== 1 ? 's' : ''}`, showHeader: true, showTokenUsage: true, showToolsMenu: true, showSkillsMenu: true, autoFocus: true, height: "100%", runtimeId: agentId, historyEndpoint: `${agentBaseUrl}/api/v1/history`, suggestions: [
|
|
270
|
+
}, children: ["Last close: ", monitorSocket.lastClose.detail] }))] }))] })] }), _jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, authToken: chatAuthToken, title: "Monitoring Agent", brandIcon: _jsx(GraphIcon, { size: 16 }), placeholder: "Ask for cost, token usage, and turn-level monitoring insights...", description: `${alerts.length} active alert${alerts.length !== 1 ? 's' : ''}`, showHeader: true, showTokenUsage: true, showToolsMenu: true, showSkillsMenu: true, autoFocus: true, height: "100%", runtimeId: agentId, historyEndpoint: `${agentBaseUrl}/api/v1/history`, suggestions: [
|
|
271
271
|
{
|
|
272
272
|
title: '▶ No-tool turn',
|
|
273
273
|
message: 'Briefly introduce yourself without calling any tool or skill — produces a linear Start → Model → Decision → End graph.',
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* Demonstrates notification channels for agents: in-app toasts, email digests,
|
|
5
5
|
* Slack webhook integrations, and notification preference management.
|
|
6
6
|
*
|
|
7
|
-
* - Creates a
|
|
8
|
-
* Runtimes API and deploys an agent on its sidecar
|
|
7
|
+
* - Creates a local agent-runtimes agent using the `example-notifications` spec
|
|
9
8
|
* - Shows a notification center alongside the chat where users can configure
|
|
10
9
|
* channels and review recent notifications
|
|
11
10
|
*/
|
|
@@ -9,8 +9,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
* Demonstrates notification channels for agents: in-app toasts, email digests,
|
|
10
10
|
* Slack webhook integrations, and notification preference management.
|
|
11
11
|
*
|
|
12
|
-
* - Creates a
|
|
13
|
-
* Runtimes API and deploys an agent on its sidecar
|
|
12
|
+
* - Creates a local agent-runtimes agent using the `example-notifications` spec
|
|
14
13
|
* - Shows a notification center alongside the chat where users can configure
|
|
15
14
|
* channels and review recent notifications
|
|
16
15
|
*/
|
|
@@ -23,13 +22,13 @@ import { Box } from '@datalayer/primer-addons';
|
|
|
23
22
|
import { AuthRequiredView, ErrorView } from './components';
|
|
24
23
|
import { ThemedProvider } from './utils/themedProvider';
|
|
25
24
|
import { uniqueAgentId } from './utils/agentId';
|
|
25
|
+
import { useExampleAgentRuntimesUrl } from './utils/useExampleAgentRuntimesUrl';
|
|
26
26
|
import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel';
|
|
27
27
|
import { Chat } from '../chat';
|
|
28
|
-
import { useAgentRuntimes } from '../hooks/useAgentRuntimes';
|
|
29
28
|
const queryClient = new QueryClient();
|
|
30
29
|
// ─── Constants ─────────────────────────────────────────────────────────────
|
|
31
|
-
const AGENT_NAME = 'notification-
|
|
32
|
-
const AGENT_SPEC_ID = '
|
|
30
|
+
const AGENT_NAME = 'notification-example-agent';
|
|
31
|
+
const AGENT_SPEC_ID = 'example-notifications';
|
|
33
32
|
const alertVariant = (severity) => {
|
|
34
33
|
if (severity === 'critical')
|
|
35
34
|
return 'danger';
|
|
@@ -41,16 +40,11 @@ const alertVariant = (severity) => {
|
|
|
41
40
|
const AgentNotificationsInner = ({ onLogout, }) => {
|
|
42
41
|
const { token } = useSimpleAuthStore();
|
|
43
42
|
const agentName = useRef(uniqueAgentId(AGENT_NAME)).current;
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
model: 'bedrock:us.anthropic.claude-3-5-haiku-20241022-v1:0',
|
|
50
|
-
protocol: 'vercel-ai',
|
|
51
|
-
description: 'Agent with multi-channel notification support',
|
|
52
|
-
},
|
|
53
|
-
});
|
|
43
|
+
const agentBaseUrl = useExampleAgentRuntimesUrl();
|
|
44
|
+
const [runtimeStatus, setRuntimeStatus] = useState('launching');
|
|
45
|
+
const [isReady, setIsReady] = useState(false);
|
|
46
|
+
const [hookError, setHookError] = useState(null);
|
|
47
|
+
const [agentId, setAgentId] = useState(agentName);
|
|
54
48
|
const [notifications, setNotifications] = useState([]);
|
|
55
49
|
const [channels, setChannels] = useState([
|
|
56
50
|
{ channel: 'in-app', enabled: true },
|
|
@@ -60,9 +54,7 @@ const AgentNotificationsInner = ({ onLogout, }) => {
|
|
|
60
54
|
const [editTargets, setEditTargets] = useState({});
|
|
61
55
|
const [isSaving, setIsSaving] = useState(false);
|
|
62
56
|
const [flash, setFlash] = useState(null);
|
|
63
|
-
const
|
|
64
|
-
const agentId = runtime?.agentId || AGENT_NAME;
|
|
65
|
-
const podName = runtime?.podName || '(launching…)';
|
|
57
|
+
const podName = isReady ? `local:${agentId}` : '(launching…)';
|
|
66
58
|
// Authenticated fetch helper
|
|
67
59
|
const authFetch = useCallback((url, opts = {}) => fetch(url, {
|
|
68
60
|
...opts,
|
|
@@ -72,6 +64,65 @@ const AgentNotificationsInner = ({ onLogout, }) => {
|
|
|
72
64
|
...(opts.headers ?? {}),
|
|
73
65
|
},
|
|
74
66
|
}), [token]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
let isCancelled = false;
|
|
69
|
+
const createLocalAgent = async () => {
|
|
70
|
+
setRuntimeStatus('launching');
|
|
71
|
+
setIsReady(false);
|
|
72
|
+
setHookError(null);
|
|
73
|
+
try {
|
|
74
|
+
const response = await authFetch(`${agentBaseUrl}/api/v1/agents`, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
body: JSON.stringify({
|
|
77
|
+
name: agentName,
|
|
78
|
+
description: 'Agent with multi-channel notification support',
|
|
79
|
+
agent_library: 'pydantic-ai',
|
|
80
|
+
transport: 'vercel-ai',
|
|
81
|
+
agent_spec_id: AGENT_SPEC_ID,
|
|
82
|
+
enable_skills: true,
|
|
83
|
+
tools: [],
|
|
84
|
+
}),
|
|
85
|
+
});
|
|
86
|
+
let resolvedAgentId = agentName;
|
|
87
|
+
if (response.ok) {
|
|
88
|
+
const data = await response.json();
|
|
89
|
+
resolvedAgentId = data?.id || agentName;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
const contentType = response.headers.get('content-type') || '';
|
|
93
|
+
let detail = '';
|
|
94
|
+
if (contentType.includes('application/json')) {
|
|
95
|
+
const data = await response.json().catch(() => null);
|
|
96
|
+
detail =
|
|
97
|
+
(typeof data?.detail === 'string' && data.detail) ||
|
|
98
|
+
(typeof data?.message === 'string' && data.message) ||
|
|
99
|
+
'';
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
detail = await response.text();
|
|
103
|
+
}
|
|
104
|
+
if (!(response.status === 409 || /already exists/i.test(detail))) {
|
|
105
|
+
throw new Error(detail || `Failed to create local agent: ${response.status}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (!isCancelled) {
|
|
109
|
+
setAgentId(resolvedAgentId);
|
|
110
|
+
setIsReady(true);
|
|
111
|
+
setRuntimeStatus('ready');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (!isCancelled) {
|
|
116
|
+
setHookError(error instanceof Error ? error.message : 'Agent failed to start');
|
|
117
|
+
setRuntimeStatus('error');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
void createLocalAgent();
|
|
122
|
+
return () => {
|
|
123
|
+
isCancelled = true;
|
|
124
|
+
};
|
|
125
|
+
}, [agentBaseUrl, agentName, authFetch]);
|
|
75
126
|
// ── Poll notifications ────────────────────────────────────────────────
|
|
76
127
|
useEffect(() => {
|
|
77
128
|
if (!isReady || !agentBaseUrl)
|
|
@@ -164,9 +215,7 @@ const AgentNotificationsInner = ({ onLogout, }) => {
|
|
|
164
215
|
justifyContent: 'center',
|
|
165
216
|
height: '100vh',
|
|
166
217
|
gap: 3,
|
|
167
|
-
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children:
|
|
168
|
-
? 'Launching runtime for notification agent…'
|
|
169
|
-
: 'Creating notification demo agent…' })] }));
|
|
218
|
+
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Launching local notification agent\u2026" })] }));
|
|
170
219
|
}
|
|
171
220
|
if (runtimeStatus === 'error' || hookError) {
|
|
172
221
|
return _jsx(ErrorView, { error: hookError, onLogout: onLogout });
|
|
@@ -201,7 +250,7 @@ const AgentNotificationsInner = ({ onLogout, }) => {
|
|
|
201
250
|
borderBottom: '1px solid',
|
|
202
251
|
borderColor: 'border.default',
|
|
203
252
|
flexShrink: 0,
|
|
204
|
-
}, children: [_jsx(BellIcon, { size: 16 }), _jsxs(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: ["Notifications \u2014 ", podName] }), unreadCount > 0 && (_jsxs(Label, { variant: "accent", size: "small", children: [unreadCount, " unread"] }))] }), _jsxs(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, title: "Notification Agent", placeholder: "Ask the agent to send you notifications\u2026", description: `${unreadCount} unread notification${unreadCount !== 1 ? 's' : ''}`, showHeader: true, autoFocus: true, height: "100%", runtimeId: podName, historyEndpoint: `${agentBaseUrl}/api/v1/history`, suggestions: [
|
|
253
|
+
}, children: [_jsx(BellIcon, { size: 16 }), _jsxs(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: ["Notifications \u2014 ", podName] }), unreadCount > 0 && (_jsxs(Label, { variant: "accent", size: "small", children: [unreadCount, " unread"] }))] }), _jsxs(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, title: "Notification Agent", brandIcon: _jsx(BellIcon, { size: 16 }), placeholder: "Ask the agent to send you notifications\u2026", description: `${unreadCount} unread notification${unreadCount !== 1 ? 's' : ''}`, showHeader: true, autoFocus: true, height: "100%", runtimeId: podName, historyEndpoint: `${agentBaseUrl}/api/v1/history`, suggestions: [
|
|
205
254
|
{
|
|
206
255
|
title: 'Alert me',
|
|
207
256
|
message: 'Notify me when KPIs drop below threshold',
|
|
@@ -24,7 +24,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
24
24
|
*/
|
|
25
25
|
/// <reference types="vite/client" />
|
|
26
26
|
import { useState, useEffect, useCallback, useRef, useMemo, } from 'react';
|
|
27
|
-
import { Text, Button,
|
|
27
|
+
import { Text, Button, Spinner } from '@primer/react';
|
|
28
28
|
import { TelescopeIcon, PlugIcon, XIcon } from '@primer/octicons-react';
|
|
29
29
|
import { Box } from '@datalayer/primer-addons';
|
|
30
30
|
import { OtelHeader, DashboardView, SqlView, SystemView, useSimpleAuthStore, } from '@datalayer/core/lib/views/otel';
|
|
@@ -32,7 +32,6 @@ import { useCoreStore } from '@datalayer/core';
|
|
|
32
32
|
import { ThemedProvider } from './utils/themedProvider';
|
|
33
33
|
import { AuthRequiredView } from './components';
|
|
34
34
|
import { ChatSidebar } from '../chat';
|
|
35
|
-
import { DEFAULT_MODEL } from '../specs';
|
|
36
35
|
// ─── Environment / defaults ────────────────────────────────────────────────
|
|
37
36
|
const OTEL_BASE_URL_ENV = import.meta.env.VITE_OTEL_BASE_URL ?? '';
|
|
38
37
|
const DATALAYER_RUN_URL_ENV = import.meta.env.VITE_DATALAYER_RUN_URL ?? '';
|
|
@@ -43,69 +42,62 @@ const DATALAYER_RUN_URL_ENV = import.meta.env.VITE_DATALAYER_RUN_URL ?? '';
|
|
|
43
42
|
const AGENT_BASE_URL_ENV = import.meta.env.VITE_BASE_URL || '';
|
|
44
43
|
const DEFAULT_AGENT_PROTOCOL = 'vercel-ai';
|
|
45
44
|
const DEFAULT_AGENT_LIBRARY = 'pydantic-ai';
|
|
45
|
+
/** Spec id this example always launches. */
|
|
46
|
+
const AGENT_SPEC_ID = 'example-otel';
|
|
46
47
|
/**
|
|
47
48
|
* Small form for picking an agent spec and launching it.
|
|
48
49
|
* Renders as the `children` of the ChatSidebar so it appears above the chat.
|
|
49
50
|
*/
|
|
50
|
-
const
|
|
51
|
-
const [specs, setSpecs] = useState([]);
|
|
52
|
-
const [selectedSpecId, setSelectedSpecId] = useState('');
|
|
51
|
+
const AgentLaunchPanel = ({ baseUrl, onConnected, onDisconnected, isConnected, connectedAgentName, }) => {
|
|
53
52
|
const [loading, setLoading] = useState(false);
|
|
54
53
|
const [error, setError] = useState(null);
|
|
55
|
-
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
const load = async () => {
|
|
58
|
-
try {
|
|
59
|
-
const res = await fetch(`${baseUrl}/api/v1/agents/library`);
|
|
60
|
-
if (!res.ok)
|
|
61
|
-
throw new Error(`HTTP ${res.status}`);
|
|
62
|
-
const data = await res.json();
|
|
63
|
-
setSpecs(data);
|
|
64
|
-
if (data.length > 0)
|
|
65
|
-
setSelectedSpecId(data[0].id);
|
|
66
|
-
}
|
|
67
|
-
catch (e) {
|
|
68
|
-
setError('Could not load agent specs from the backend.');
|
|
69
|
-
console.warn('[AgentOtelExample] Failed to load library specs:', e);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
void load();
|
|
73
|
-
}, [baseUrl]);
|
|
54
|
+
const launchedRef = useRef(false);
|
|
74
55
|
const handleLaunch = useCallback(async () => {
|
|
75
|
-
if (!selectedSpecId)
|
|
76
|
-
return;
|
|
77
56
|
setLoading(true);
|
|
78
57
|
setError(null);
|
|
79
58
|
try {
|
|
80
|
-
const
|
|
81
|
-
const transport = spec?.transport ?? DEFAULT_AGENT_PROTOCOL;
|
|
59
|
+
const transport = DEFAULT_AGENT_PROTOCOL;
|
|
82
60
|
const res = await fetch(`${baseUrl}/api/v1/agents`, {
|
|
83
61
|
method: 'POST',
|
|
84
62
|
headers: { 'Content-Type': 'application/json' },
|
|
85
63
|
body: JSON.stringify({
|
|
86
|
-
name:
|
|
64
|
+
name: AGENT_SPEC_ID,
|
|
87
65
|
description: `Launched from AgentOtelExample`,
|
|
88
66
|
agent_library: DEFAULT_AGENT_LIBRARY,
|
|
89
67
|
transport,
|
|
90
|
-
|
|
91
|
-
system_prompt: 'You are a helpful AI assistant observing OTEL telemetry data.',
|
|
92
|
-
agent_spec_id: selectedSpecId,
|
|
68
|
+
agent_spec_id: AGENT_SPEC_ID,
|
|
93
69
|
}),
|
|
94
70
|
});
|
|
95
71
|
if (!res.ok) {
|
|
96
72
|
const err = await res.json().catch(() => ({ detail: 'Unknown error' }));
|
|
97
|
-
|
|
73
|
+
const detail = typeof err?.detail === 'string' ? err.detail : 'Unknown error';
|
|
74
|
+
// Reuse existing agent when backend reports duplicate creation.
|
|
75
|
+
if (res.status === 409 || /already exists/i.test(detail)) {
|
|
76
|
+
const idMatch = detail.match(/Agent with ID '([^']+)' already exists/i);
|
|
77
|
+
const existingId = idMatch?.[1] || AGENT_SPEC_ID;
|
|
78
|
+
onConnected(existingId, transport);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
throw new Error(detail || `Failed to create agent: ${res.status}`);
|
|
98
82
|
}
|
|
99
83
|
const data = await res.json();
|
|
100
84
|
onConnected(data.id, transport);
|
|
101
85
|
}
|
|
102
86
|
catch (e) {
|
|
103
87
|
setError(e instanceof Error ? e.message : 'Failed to launch agent');
|
|
88
|
+
console.warn('[AgentOtelExample] Failed to launch agent:', e);
|
|
104
89
|
}
|
|
105
90
|
finally {
|
|
106
91
|
setLoading(false);
|
|
107
92
|
}
|
|
108
|
-
}, [
|
|
93
|
+
}, [baseUrl, onConnected]);
|
|
94
|
+
// Auto-launch the example-otel agent on mount.
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (launchedRef.current || isConnected)
|
|
97
|
+
return;
|
|
98
|
+
launchedRef.current = true;
|
|
99
|
+
void handleLaunch();
|
|
100
|
+
}, [handleLaunch, isConnected]);
|
|
109
101
|
if (isConnected) {
|
|
110
102
|
return (_jsxs(Box, { sx: {
|
|
111
103
|
px: 3,
|
|
@@ -129,7 +121,7 @@ const AgentSelectorPanel = ({ baseUrl, onConnected, onDisconnected, isConnected,
|
|
|
129
121
|
gap: 2,
|
|
130
122
|
flexShrink: 0,
|
|
131
123
|
bg: 'canvas.subtle',
|
|
132
|
-
}, children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.muted' }, children: "
|
|
124
|
+
}, children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.muted' }, children: "AGENT" }), loading ? (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Spinner, { size: "small" }), _jsxs(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: ["Launching ", AGENT_SPEC_ID, "\u2026"] })] })) : error ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 1, color: 'danger.fg' }, children: error }), _jsx(Button, { variant: "primary", size: "small", onClick: handleLaunch, leadingVisual: PlugIcon, sx: { width: '100%' }, children: "Retry" })] })) : (_jsxs(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: ["Launching ", AGENT_SPEC_ID, "\u2026"] }))] }));
|
|
133
125
|
};
|
|
134
126
|
const TAB_SX = (active) => ({
|
|
135
127
|
px: 3,
|
|
@@ -142,7 +134,7 @@ const TAB_SX = (active) => ({
|
|
|
142
134
|
borderColor: active ? 'accent.fg' : 'transparent',
|
|
143
135
|
'&:hover': { color: 'fg.default' },
|
|
144
136
|
});
|
|
145
|
-
const AgentOtelExampleInner = ({ token
|
|
137
|
+
const AgentOtelExampleInner = ({ token }) => {
|
|
146
138
|
const { configuration } = useCoreStore();
|
|
147
139
|
const resolvedRunUrl = configuration?.otelRunUrl ||
|
|
148
140
|
configuration?.runUrl ||
|
|
@@ -220,7 +212,7 @@ const AgentOtelExampleInner = ({ token, onSignOut }) => {
|
|
|
220
212
|
overflow: 'hidden',
|
|
221
213
|
bg: 'canvas.default',
|
|
222
214
|
color: 'fg.default',
|
|
223
|
-
}, children: [_jsx(OtelHeader, { baseUrl: otelBaseUrl, token: token, onNavigate: handleNavigate,
|
|
215
|
+
}, children: [_jsx(OtelHeader, { baseUrl: otelBaseUrl, token: token, onNavigate: handleNavigate, showGenerateButtons: true, showAccountControls: false, trailing: _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(TelescopeIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: otelBaseUrl })] }) }), _jsxs(Box, { sx: {
|
|
224
216
|
display: 'flex',
|
|
225
217
|
flex: 1,
|
|
226
218
|
minHeight: 0,
|
|
@@ -267,14 +259,13 @@ const AgentOtelExampleInner = ({ token, onSignOut }) => {
|
|
|
267
259
|
},
|
|
268
260
|
],
|
|
269
261
|
}
|
|
270
|
-
: { useStore: true }, children: _jsx(
|
|
262
|
+
: { useStore: true }, children: _jsx(AgentLaunchPanel, { baseUrl: agentBaseUrl, onConnected: handleAgentConnected, onDisconnected: handleAgentDisconnected, isConnected: !!connectedAgentId, connectedAgentName: connectedAgentId ?? undefined }) })] })] }));
|
|
271
263
|
};
|
|
272
264
|
/**
|
|
273
265
|
* AgentOtelExample – themed root with auth gate.
|
|
274
266
|
*/
|
|
275
267
|
const AgentOtelExample = () => {
|
|
276
268
|
const token = useSimpleAuthStore(s => s.token);
|
|
277
|
-
|
|
278
|
-
return (_jsx(ThemedProvider, { children: !token ? (_jsx(AuthRequiredView, {})) : (_jsx(AgentOtelExampleInner, { token: token, onSignOut: clearAuth })) }));
|
|
269
|
+
return (_jsx(ThemedProvider, { children: !token ? _jsx(AuthRequiredView, {}) : _jsx(AgentOtelExampleInner, { token: token }) }));
|
|
279
270
|
};
|
|
280
271
|
export default AgentOtelExample;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* AgentOutputsExample
|
|
3
3
|
*
|
|
4
4
|
* Demonstrates rich output rendering for agent responses. The agent (spec
|
|
5
|
-
* `
|
|
5
|
+
* `example-outputs`) is prompted to return exactly one of four output types per
|
|
6
6
|
* response:
|
|
7
7
|
* - TABLE → GitHub-flavored Markdown table
|
|
8
8
|
* - JSON → ```json fenced block
|
|
@@ -7,7 +7,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
7
7
|
* AgentOutputsExample
|
|
8
8
|
*
|
|
9
9
|
* Demonstrates rich output rendering for agent responses. The agent (spec
|
|
10
|
-
* `
|
|
10
|
+
* `example-outputs`) is prompted to return exactly one of four output types per
|
|
11
11
|
* response:
|
|
12
12
|
* - TABLE → GitHub-flavored Markdown table
|
|
13
13
|
* - JSON → ```json fenced block
|
|
@@ -33,12 +33,27 @@ import { Chat } from '../chat';
|
|
|
33
33
|
import { useChatStore } from '../stores/chatStore';
|
|
34
34
|
const queryClient = new QueryClient();
|
|
35
35
|
// ─── Constants ─────────────────────────────────────────────────────────────
|
|
36
|
-
const AGENT_NAME = 'outputs-
|
|
36
|
+
const AGENT_NAME = 'outputs-example-agent';
|
|
37
37
|
const AGENT_SPEC_ID = 'demo-outputs';
|
|
38
38
|
const DEFAULT_LOCAL_BASE_URL = import.meta.env.VITE_BASE_URL || 'http://localhost:8765';
|
|
39
39
|
// ─── Output detection ──────────────────────────────────────────────────────
|
|
40
40
|
const FENCE_RE = /```([a-zA-Z0-9_+-]*)\n([\s\S]*?)```/g;
|
|
41
41
|
const MD_TABLE_RE = /(^|\n)\s*\|[^\n]+\|\s*\n\s*\|[\s:|-]+\|\s*\n(\s*\|[^\n]+\|\s*\n?)+/;
|
|
42
|
+
/**
|
|
43
|
+
* Some agents emit a markdown table compressed onto a single logical line by
|
|
44
|
+
* joining row separators with `||`. Expand that form back to one row per line
|
|
45
|
+
* so the markdown table renderer can parse it. Idempotent: any pre-existing
|
|
46
|
+
* `|\n|` boundaries are unaffected.
|
|
47
|
+
*/
|
|
48
|
+
const expandCompactMarkdownTable = (text) => {
|
|
49
|
+
if (!text)
|
|
50
|
+
return text;
|
|
51
|
+
if (!/\|\s*[-:| ]{3,}\|/.test(text))
|
|
52
|
+
return text;
|
|
53
|
+
if (!text.includes('||'))
|
|
54
|
+
return text;
|
|
55
|
+
return text.replace(/\|\|/g, '|\n|');
|
|
56
|
+
};
|
|
42
57
|
const EXT_LIKE_INFOS = new Set([
|
|
43
58
|
'csv',
|
|
44
59
|
'tsv',
|
|
@@ -70,8 +85,28 @@ const detectOutput = (m) => {
|
|
|
70
85
|
const match = FENCE_RE.exec(text);
|
|
71
86
|
if (match) {
|
|
72
87
|
const info = (match[1] || '').toLowerCase();
|
|
73
|
-
const
|
|
88
|
+
const rawBody = match[2] ?? '';
|
|
89
|
+
const body = expandCompactMarkdownTable(rawBody);
|
|
74
90
|
const firstLine = body.split('\n', 1)[0]?.trim() ?? '';
|
|
91
|
+
// Markdown fenced table should render in the table panel, not as a file.
|
|
92
|
+
if ((info === 'markdown' || info === 'md') && MD_TABLE_RE.test(body)) {
|
|
93
|
+
const tableMatch = body.match(MD_TABLE_RE);
|
|
94
|
+
return {
|
|
95
|
+
tab: 'table',
|
|
96
|
+
payload: tableMatch ? tableMatch[0].trim() : body.trim(),
|
|
97
|
+
messageId: m.id,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Bare/unlabeled fence around a markdown table — route to Table tab too.
|
|
101
|
+
if ((!info || info === 'text' || info === 'plain') &&
|
|
102
|
+
MD_TABLE_RE.test(body)) {
|
|
103
|
+
const tableMatch = body.match(MD_TABLE_RE);
|
|
104
|
+
return {
|
|
105
|
+
tab: 'table',
|
|
106
|
+
payload: tableMatch ? tableMatch[0].trim() : body.trim(),
|
|
107
|
+
messageId: m.id,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
75
110
|
// Chart: ```json with `// chart` marker on first line.
|
|
76
111
|
if (info === 'json' && /^\/\/\s*chart\b/i.test(firstLine)) {
|
|
77
112
|
return {
|
|
@@ -137,11 +172,12 @@ const detectOutput = (m) => {
|
|
|
137
172
|
}
|
|
138
173
|
}
|
|
139
174
|
// 2) Markdown table (no fences).
|
|
140
|
-
|
|
141
|
-
|
|
175
|
+
const expandedText = expandCompactMarkdownTable(text.trim());
|
|
176
|
+
if (MD_TABLE_RE.test(expandedText)) {
|
|
177
|
+
const tableMatch = expandedText.match(MD_TABLE_RE);
|
|
142
178
|
return {
|
|
143
179
|
tab: 'table',
|
|
144
|
-
payload: tableMatch ? tableMatch[0].trim() :
|
|
180
|
+
payload: tableMatch ? tableMatch[0].trim() : expandedText,
|
|
145
181
|
messageId: m.id,
|
|
146
182
|
};
|
|
147
183
|
}
|
|
@@ -150,7 +186,10 @@ const detectOutput = (m) => {
|
|
|
150
186
|
// ─── Table renderer (parses the Markdown pipe syntax) ──────────────────────
|
|
151
187
|
const MarkdownTable = ({ source }) => {
|
|
152
188
|
const { headers, rows } = useMemo(() => {
|
|
153
|
-
|
|
189
|
+
// Some model responses compress table rows into one line using `||`.
|
|
190
|
+
// Normalize that form back to one table row per line.
|
|
191
|
+
const normalized = expandCompactMarkdownTable(source).trim();
|
|
192
|
+
const lines = normalized
|
|
154
193
|
.split('\n')
|
|
155
194
|
.map(l => l.trim())
|
|
156
195
|
.filter(l => l.startsWith('|'));
|
|
@@ -175,6 +214,7 @@ const MarkdownTable = ({ source }) => {
|
|
|
175
214
|
}
|
|
176
215
|
return (_jsx(Box, { sx: { overflowX: 'auto' }, children: _jsxs(Box, { as: "table", sx: {
|
|
177
216
|
width: '100%',
|
|
217
|
+
tableLayout: 'auto',
|
|
178
218
|
borderCollapse: 'collapse',
|
|
179
219
|
fontSize: 0,
|
|
180
220
|
'th, td': {
|
|
@@ -184,6 +224,8 @@ const MarkdownTable = ({ source }) => {
|
|
|
184
224
|
py: 1,
|
|
185
225
|
textAlign: 'left',
|
|
186
226
|
verticalAlign: 'top',
|
|
227
|
+
whiteSpace: 'normal',
|
|
228
|
+
wordBreak: 'break-word',
|
|
187
229
|
},
|
|
188
230
|
th: { bg: 'canvas.subtle', fontWeight: 'bold' },
|
|
189
231
|
}, children: [_jsx("thead", { children: _jsx("tr", { children: headers.map((h, i) => (_jsx("th", { children: h }, i))) }) }), _jsx("tbody", { children: rows.map((r, i) => (_jsx("tr", { children: r.map((c, j) => (_jsx("td", { children: c }, j))) }, i))) })] }) }));
|
|
@@ -316,24 +358,33 @@ const AgentOutputsInner = ({ onLogout, }) => {
|
|
|
316
358
|
}, [agentBaseUrl, agentName, authFetch]);
|
|
317
359
|
const [activeTab, setActiveTab] = useState('table');
|
|
318
360
|
const [detected, setDetected] = useState([]);
|
|
319
|
-
const
|
|
361
|
+
const lastTextByIdRef = useRef(new Map());
|
|
320
362
|
// Subscribe to chat store messages and detect outputs in assistant replies.
|
|
363
|
+
// We re-detect on every change because messages stream incrementally — the
|
|
364
|
+
// first chunk may only contain a header + one row, and later chunks add the
|
|
365
|
+
// remaining rows; we always want to keep the latest, most complete output.
|
|
321
366
|
useEffect(() => {
|
|
322
367
|
const process = (messages) => {
|
|
323
368
|
const assistants = messages.filter(m => m.role === 'assistant');
|
|
324
369
|
if (assistants.length === 0)
|
|
325
370
|
return;
|
|
326
371
|
const last = assistants[assistants.length - 1];
|
|
327
|
-
|
|
372
|
+
const currentText = messageText(last);
|
|
373
|
+
if (lastTextByIdRef.current.get(last.id) === currentText)
|
|
328
374
|
return;
|
|
375
|
+
lastTextByIdRef.current.set(last.id, currentText);
|
|
329
376
|
const out = detectOutput(last);
|
|
330
377
|
if (!out)
|
|
331
378
|
return;
|
|
332
|
-
lastProcessedIdRef.current = last.id;
|
|
333
379
|
setDetected(prev => {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
380
|
+
const idx = prev.findIndex(d => d.messageId === out.messageId);
|
|
381
|
+
if (idx === -1)
|
|
382
|
+
return [out, ...prev].slice(0, 20);
|
|
383
|
+
// Replace the existing detection in place so the panel keeps up with
|
|
384
|
+
// streaming content.
|
|
385
|
+
const next = prev.slice();
|
|
386
|
+
next[idx] = out;
|
|
387
|
+
return next;
|
|
337
388
|
});
|
|
338
389
|
setActiveTab(out.tab);
|
|
339
390
|
};
|
|
@@ -351,8 +402,8 @@ const AgentOutputsInner = ({ onLogout, }) => {
|
|
|
351
402
|
height: '100vh',
|
|
352
403
|
gap: 3,
|
|
353
404
|
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: runtimeStatus === 'launching'
|
|
354
|
-
? 'Launching outputs
|
|
355
|
-
: 'Creating outputs
|
|
405
|
+
? 'Launching outputs example agent...'
|
|
406
|
+
: 'Creating outputs example agent...' })] }));
|
|
356
407
|
}
|
|
357
408
|
if (runtimeStatus === 'error' || hookError) {
|
|
358
409
|
return _jsx(ErrorView, { error: hookError, onLogout: onLogout });
|
|
@@ -388,7 +439,7 @@ const AgentOutputsInner = ({ onLogout, }) => {
|
|
|
388
439
|
borderBottom: '1px solid',
|
|
389
440
|
borderColor: 'border.default',
|
|
390
441
|
flexShrink: 0,
|
|
391
|
-
}, children: [_jsx(TableIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: "Agent Outputs \u2014 Local Runtime" })] }), _jsxs(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, authToken: chatAuthToken, title: "Outputs Demo Agent", placeholder: "Ask for a Table, JSON, Chart, or File\u2026", description: `${detected.length} detected output${detected.length !== 1 ? 's' : ''}`, showHeader: true, showToolsMenu: true, showSkillsMenu: true, autoFocus: true, height: "100%", runtimeId: agentId, historyEndpoint: `${agentBaseUrl}/api/v1/history`, suggestions: [
|
|
442
|
+
}, children: [_jsx(TableIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: "Agent Outputs \u2014 Local Runtime" })] }), _jsxs(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, authToken: chatAuthToken, title: "Outputs Demo Agent", brandIcon: _jsx(FileIcon, { size: 16 }), placeholder: "Ask for a Table, JSON, Chart, or File\u2026", description: `${detected.length} detected output${detected.length !== 1 ? 's' : ''}`, showHeader: true, showToolsMenu: true, showSkillsMenu: true, autoFocus: true, height: "100%", runtimeId: agentId, historyEndpoint: `${agentBaseUrl}/api/v1/history`, suggestions: [
|
|
392
443
|
{
|
|
393
444
|
title: 'Table',
|
|
394
445
|
message: 'Generate a Markdown table of the top 5 US cities by population, with columns City, State, Population.',
|
|
@@ -6,15 +6,16 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
6
6
|
import { useEffect, useMemo, useState } from 'react';
|
|
7
7
|
import { Box, setupPrimerPortals } from '@datalayer/primer-addons';
|
|
8
8
|
import { Button, Heading, Label, Spinner, Text } from '@primer/react';
|
|
9
|
+
import { FileIcon } from '@primer/octicons-react';
|
|
9
10
|
import validator from '@rjsf/validator-ajv8';
|
|
10
11
|
import { Form, yamlSchemaToJsonSchema } from '@datalayer/primer-rjsf';
|
|
11
12
|
import { ThemedProvider } from './utils/themedProvider';
|
|
12
13
|
import { uniqueAgentId } from './utils/agentId';
|
|
14
|
+
import { useExampleAgentRuntimesUrl } from './utils/useExampleAgentRuntimesUrl';
|
|
13
15
|
import { ErrorView } from './components';
|
|
14
16
|
import { Chat } from '../chat';
|
|
15
17
|
setupPrimerPortals();
|
|
16
|
-
const
|
|
17
|
-
const AGENT_SPEC_ID = 'demo-parameters';
|
|
18
|
+
const AGENT_SPEC_ID = 'example-parameters';
|
|
18
19
|
const AGENT_NAME = 'parameters-demo';
|
|
19
20
|
function isRecord(value) {
|
|
20
21
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -101,6 +102,7 @@ function hasRequiredValues(schema, formData) {
|
|
|
101
102
|
});
|
|
102
103
|
}
|
|
103
104
|
const AgentParametersExample = () => {
|
|
105
|
+
const baseUrl = useExampleAgentRuntimesUrl();
|
|
104
106
|
const [showSchemaForm, setShowSchemaForm] = useState(false);
|
|
105
107
|
const [isSchemaLoading, setIsSchemaLoading] = useState(false);
|
|
106
108
|
const [schema, setSchema] = useState(null);
|
|
@@ -121,7 +123,7 @@ const AgentParametersExample = () => {
|
|
|
121
123
|
setIsSchemaLoading(true);
|
|
122
124
|
setError(null);
|
|
123
125
|
try {
|
|
124
|
-
const response = await fetch(`${
|
|
126
|
+
const response = await fetch(`${baseUrl}/api/v1/agents/library/${AGENT_SPEC_ID}`);
|
|
125
127
|
if (!response.ok) {
|
|
126
128
|
throw new Error(`Failed to load schema: ${response.status}`);
|
|
127
129
|
}
|
|
@@ -147,7 +149,7 @@ const AgentParametersExample = () => {
|
|
|
147
149
|
setError(null);
|
|
148
150
|
try {
|
|
149
151
|
const name = uniqueAgentId(AGENT_NAME);
|
|
150
|
-
const response = await fetch(`${
|
|
152
|
+
const response = await fetch(`${baseUrl}/api/v1/agents`, {
|
|
151
153
|
method: 'POST',
|
|
152
154
|
headers: { 'Content-Type': 'application/json' },
|
|
153
155
|
body: JSON.stringify({
|
|
@@ -178,13 +180,13 @@ const AgentParametersExample = () => {
|
|
|
178
180
|
if (!agentId) {
|
|
179
181
|
return;
|
|
180
182
|
}
|
|
181
|
-
void fetch(`${
|
|
183
|
+
void fetch(`${baseUrl}/api/v1/agents/${encodeURIComponent(agentId)}`, {
|
|
182
184
|
method: 'DELETE',
|
|
183
185
|
}).catch(() => {
|
|
184
186
|
// Ignore teardown failures in example mode.
|
|
185
187
|
});
|
|
186
188
|
};
|
|
187
|
-
}, [agentId]);
|
|
189
|
+
}, [agentId, baseUrl]);
|
|
188
190
|
if (!agentId) {
|
|
189
191
|
return (_jsx(ThemedProvider, { children: _jsxs(Box, { sx: {
|
|
190
192
|
maxWidth: 760,
|
|
@@ -199,7 +201,7 @@ const AgentParametersExample = () => {
|
|
|
199
201
|
flexDirection: 'column',
|
|
200
202
|
gap: 2,
|
|
201
203
|
bg: 'canvas.subtle',
|
|
202
|
-
}, children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.muted' }, children: "CONFIGURE AGENT" }), _jsx(Heading, { as: "h2", sx: { fontSize: 2 }, children: "Launch Parameterized Agent" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1, maxWidth: 620 }, children: "Load the runtime schema directly from
|
|
204
|
+
}, children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.muted' }, children: "CONFIGURE AGENT" }), _jsx(Heading, { as: "h2", sx: { fontSize: 2 }, children: "Launch Parameterized Agent" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1, maxWidth: 620 }, children: "Load the runtime schema directly from example-parameters, fill the generated form, then launch with validated parameters." }), _jsxs(Box, { sx: {
|
|
203
205
|
display: 'flex',
|
|
204
206
|
alignItems: 'center',
|
|
205
207
|
gap: 2,
|
|
@@ -232,7 +234,7 @@ const AgentParametersExample = () => {
|
|
|
232
234
|
// Prevent implicit form submission; launching is click-only.
|
|
233
235
|
}, noHtml5Validate: true }) })), _jsx(Button, { variant: "primary", size: "small", type: "button", onClick: launchAgent, disabled: !canLaunch || isCreating, sx: { width: '100%' }, children: isCreating ? (_jsxs(_Fragment, { children: [_jsx(Spinner, { size: "small" }), " Launching..."] })) : ('Launch Agent') }), schema && formTouched && (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Parameters are sent as agent_parameters in the create-agent request." })), error && _jsx(ErrorView, { error: "Launch failed", detail: error })] }) }));
|
|
234
236
|
}
|
|
235
|
-
return (_jsx(Chat, { protocol: "vercel-ai", baseUrl:
|
|
237
|
+
return (_jsx(Chat, { protocol: "vercel-ai", baseUrl: baseUrl, agentId: agentId, title: `Parameterized Agent: ${String(formData.project ?? 'Project')}`, brandIcon: _jsx(FileIcon, { size: 16 }), placeholder: "Ask something about your configured project...", description: `Role: ${String(formData.role ?? 'n/a')} · Tone: ${String(formData.tone ?? 'n/a')}`, showHeader: true, showModelSelector: true, showToolsMenu: true, showSkillsMenu: true, showTokenUsage: true, showInformation: true, autoFocus: true, height: "100vh", runtimeId: agentId, historyEndpoint: `${baseUrl}/api/v1/history`, suggestions: [
|
|
236
238
|
{
|
|
237
239
|
title: 'Print demo_params',
|
|
238
240
|
message: 'Use execute_code to print(demo_params) from the sandbox, then explain what it is.',
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* sidebar that streams WebSocket messages to and from the
|
|
6
6
|
* `/configure/sandbox/ws` endpoint.
|
|
7
7
|
*
|
|
8
|
-
* - Creates a local agent (spec:
|
|
8
|
+
* - Creates a local agent (spec: example-full) with codemode enabled
|
|
9
9
|
* - SegmentedControl toggles between "eval" and "jupyter" variants
|
|
10
10
|
* - Sidebar shows live sandbox status, WebSocket event log, and an
|
|
11
11
|
* interrupt button
|