@datalayer/agent-runtimes 0.0.2 → 0.0.4
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/components/chat/components/AgentDetails.js +65 -2
- package/lib/components/chat/components/ChatFloating.d.ts +13 -3
- package/lib/components/chat/components/ChatFloating.js +20 -9
- package/lib/components/chat/components/base/ChatBase.d.ts +25 -0
- package/lib/components/chat/components/base/ChatBase.js +208 -18
- package/lib/components/chat/components/index.d.ts +1 -1
- package/lib/components/chat/components/parts/TextPart.js +5 -1
- package/lib/components/chat/index.d.ts +1 -1
- package/lib/components/chat/protocols/A2AAdapter.d.ts +2 -0
- package/lib/components/chat/protocols/A2AAdapter.js +9 -0
- package/lib/components/chat/protocols/ACPAdapter.d.ts +2 -0
- package/lib/components/chat/protocols/ACPAdapter.js +13 -0
- package/lib/components/chat/protocols/AGUIAdapter.d.ts +2 -0
- package/lib/components/chat/protocols/AGUIAdapter.js +5 -0
- package/lib/components/chat/protocols/BaseProtocolAdapter.d.ts +2 -0
- package/lib/components/chat/protocols/VercelAIAdapter.d.ts +6 -0
- package/lib/components/chat/protocols/VercelAIAdapter.js +10 -0
- package/lib/components/chat/types/protocol.d.ts +4 -0
- package/lib/examples/AgentRuntimeCustomExample.d.ts +2 -1
- package/lib/examples/AgentRuntimeCustomExample.js +120 -5
- package/lib/examples/AgentRuntimeLexicalExample.js +1 -1
- package/lib/examples/components/AgentConfiguration.d.ts +22 -0
- package/lib/examples/components/AgentConfiguration.js +39 -3
- package/lib/examples/components/index.d.ts +1 -1
- package/lib/hooks/useNotebookAIAgent.d.ts +2 -2
- package/lib/hooks/useNotebookAIAgent.js +26 -9
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/state/substates/AIAgentState.d.ts +80 -10
- package/lib/state/substates/AIAgentState.js +89 -23
- package/lib/stubs/keytar.d.ts +30 -0
- package/lib/stubs/keytar.js +28 -0
- package/package.json +9 -7
- package/style/base.css +1 -43
|
@@ -221,9 +221,14 @@ export class ACPAdapter extends BaseProtocolAdapter {
|
|
|
221
221
|
.filter(c => c.type === 'text')
|
|
222
222
|
.map(c => c.text || '')
|
|
223
223
|
.join('');
|
|
224
|
+
if (_options?.model) {
|
|
225
|
+
console.log('[ACPAdapter] Sending with model:', _options.model);
|
|
226
|
+
}
|
|
224
227
|
await this.sendRequest(AGENT_METHODS.session_prompt, {
|
|
225
228
|
sessionId: this.session.sessionId,
|
|
226
229
|
content: [{ type: 'text', text: content }],
|
|
230
|
+
// Include model for per-request model override
|
|
231
|
+
...(_options?.model && { metadata: { model: _options.model } }),
|
|
227
232
|
});
|
|
228
233
|
}
|
|
229
234
|
catch (error) {
|
|
@@ -450,6 +455,14 @@ export class ACPAdapter extends BaseProtocolAdapter {
|
|
|
450
455
|
timestamp: new Date(),
|
|
451
456
|
});
|
|
452
457
|
}
|
|
458
|
+
// Handle error events - emit as error, not as message
|
|
459
|
+
if (params.error) {
|
|
460
|
+
this.emit({
|
|
461
|
+
type: 'error',
|
|
462
|
+
error: new Error(params.error),
|
|
463
|
+
timestamp: new Date(),
|
|
464
|
+
});
|
|
465
|
+
}
|
|
453
466
|
}
|
|
454
467
|
/**
|
|
455
468
|
* Handle permission request from agent
|
|
@@ -52,6 +52,8 @@ export declare class AGUIAdapter extends BaseProtocolAdapter {
|
|
|
52
52
|
metadata?: Record<string, unknown>;
|
|
53
53
|
/** Full conversation history to send with the message */
|
|
54
54
|
messages?: ChatMessage[];
|
|
55
|
+
/** Model to use for this request (overrides agent default) */
|
|
56
|
+
model?: string;
|
|
55
57
|
}): Promise<void>;
|
|
56
58
|
/**
|
|
57
59
|
* Send tool result back through AG-UI and continue the conversation
|
|
@@ -135,7 +135,12 @@ export class AGUIAdapter extends BaseProtocolAdapter {
|
|
|
135
135
|
tools: options?.tools || [],
|
|
136
136
|
context: [],
|
|
137
137
|
forwardedProps: null,
|
|
138
|
+
// Include model for per-request model override
|
|
139
|
+
...(options?.model && { model: options.model }),
|
|
138
140
|
};
|
|
141
|
+
if (options?.model) {
|
|
142
|
+
console.log('[AGUIAdapter] Sending with model:', options.model);
|
|
143
|
+
}
|
|
139
144
|
try {
|
|
140
145
|
const response = await fetch(this.aguiConfig.baseUrl, {
|
|
141
146
|
method: 'POST',
|
|
@@ -35,6 +35,8 @@ export declare abstract class BaseProtocolAdapter implements ProtocolAdapter {
|
|
|
35
35
|
metadata?: Record<string, unknown>;
|
|
36
36
|
/** Full conversation history to send with the message */
|
|
37
37
|
messages?: ChatMessage[];
|
|
38
|
+
/** Model to use for this request (overrides agent default) */
|
|
39
|
+
model?: string;
|
|
38
40
|
}): Promise<void>;
|
|
39
41
|
/**
|
|
40
42
|
* Send tool execution result back
|
|
@@ -57,6 +57,12 @@ export declare class VercelAIAdapter extends BaseProtocolAdapter {
|
|
|
57
57
|
tools?: ToolDefinition[];
|
|
58
58
|
threadId?: string;
|
|
59
59
|
metadata?: Record<string, unknown>;
|
|
60
|
+
/** Model to use for this request (overrides agent default) */
|
|
61
|
+
model?: string;
|
|
62
|
+
/** Full conversation history to send with the message */
|
|
63
|
+
messages?: ChatMessage[];
|
|
64
|
+
/** Builtin tools / MCP tools to enable for this request */
|
|
65
|
+
builtinTools?: string[];
|
|
60
66
|
}): Promise<void>;
|
|
61
67
|
/**
|
|
62
68
|
* Parse SSE stream from Vercel AI
|
|
@@ -105,7 +105,17 @@ export class VercelAIAdapter extends BaseProtocolAdapter {
|
|
|
105
105
|
trigger: 'submit-message',
|
|
106
106
|
// Optional fields based on Pydantic AI's Vercel adapter
|
|
107
107
|
...(options?.tools && { tools: options.tools }),
|
|
108
|
+
// Model override for per-request model selection
|
|
109
|
+
...(options?.model && { model: options.model }),
|
|
110
|
+
// Builtin tools / MCP tools to enable
|
|
111
|
+
...(options?.builtinTools &&
|
|
112
|
+
options.builtinTools.length > 0 && {
|
|
113
|
+
builtinTools: options.builtinTools,
|
|
114
|
+
}),
|
|
108
115
|
};
|
|
116
|
+
if (options?.model) {
|
|
117
|
+
console.log('[VercelAIAdapter] Sending with model:', options.model);
|
|
118
|
+
}
|
|
109
119
|
// Merge custom headers with defaults
|
|
110
120
|
const headers = {
|
|
111
121
|
'Content-Type': 'application/json',
|
|
@@ -115,6 +115,8 @@ export interface ProtocolAdapter {
|
|
|
115
115
|
metadata?: Record<string, unknown>;
|
|
116
116
|
/** Full conversation history to send with the message */
|
|
117
117
|
messages?: ChatMessage[];
|
|
118
|
+
/** Model to use for this request (overrides agent default) */
|
|
119
|
+
model?: string;
|
|
118
120
|
}): Promise<void>;
|
|
119
121
|
/**
|
|
120
122
|
* Send tool execution result back
|
|
@@ -161,6 +163,8 @@ export declare namespace AGUI {
|
|
|
161
163
|
content: string;
|
|
162
164
|
}>;
|
|
163
165
|
forwardedProps: Record<string, unknown> | null;
|
|
166
|
+
/** Optional model override for per-request model selection */
|
|
167
|
+
model?: string;
|
|
164
168
|
}
|
|
165
169
|
interface Event {
|
|
166
170
|
type: string;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent Runtime Custom Example Component
|
|
3
3
|
*
|
|
4
|
-
* Demonstrates the unified Chat component with
|
|
4
|
+
* Demonstrates the unified Chat component with AG-UI transport
|
|
5
5
|
* and all necessary providers:
|
|
6
6
|
* - QueryClientProvider for data fetching
|
|
7
|
+
* - Auto-creates agent on the server if it doesn't exist
|
|
7
8
|
*/
|
|
8
9
|
declare const AgentRuntimeCustomExample: React.FC;
|
|
9
10
|
export default AgentRuntimeCustomExample;
|
|
@@ -3,7 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } 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 {
|
|
6
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
7
|
+
import { Spinner, Text } from '@primer/react';
|
|
7
8
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
8
9
|
import { Box } from '@datalayer/primer-addons';
|
|
9
10
|
import { datalayerTheme, DatalayerThemeProvider } from '@datalayer/core';
|
|
@@ -19,14 +20,107 @@ const queryClient = new QueryClient({
|
|
|
19
20
|
},
|
|
20
21
|
},
|
|
21
22
|
});
|
|
23
|
+
const AGENT_NAME = 'datalayer-assistant';
|
|
24
|
+
const BASE_URL = 'http://localhost:8765';
|
|
25
|
+
/**
|
|
26
|
+
* Hook to ensure the agent exists on the server
|
|
27
|
+
* Creates the agent if it doesn't exist
|
|
28
|
+
*/
|
|
29
|
+
function useEnsureAgent() {
|
|
30
|
+
const [agentId, setAgentId] = useState(null);
|
|
31
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
32
|
+
const [error, setError] = useState(null);
|
|
33
|
+
const createAgent = useCallback(async () => {
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch(`${BASE_URL}/api/v1/agents`, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: {
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
name: AGENT_NAME,
|
|
42
|
+
description: 'Datalayer AI Assistant - Your helpful coding companion',
|
|
43
|
+
agent_library: 'pydantic-ai',
|
|
44
|
+
transport: 'ag-ui',
|
|
45
|
+
model: 'openai:gpt-4o-mini',
|
|
46
|
+
system_prompt: `You are Datalayer Assistant, a helpful AI assistant specialized in data science, Python programming, and Jupyter notebooks.
|
|
47
|
+
|
|
48
|
+
You can help users with:
|
|
49
|
+
- Writing and debugging Python code
|
|
50
|
+
- Data analysis with pandas, numpy, and other libraries
|
|
51
|
+
- Creating visualizations with matplotlib, plotly, etc.
|
|
52
|
+
- Machine learning with scikit-learn, pytorch, etc.
|
|
53
|
+
- General programming questions
|
|
54
|
+
|
|
55
|
+
Be concise, helpful, and provide working code examples when appropriate.`,
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
const errorData = await response
|
|
60
|
+
.json()
|
|
61
|
+
.catch(() => ({ detail: 'Unknown error' }));
|
|
62
|
+
throw new Error(errorData.detail || `Failed to create agent: ${response.status}`);
|
|
63
|
+
}
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
console.log('[AgentRuntimeCustomExample] Agent created:', data);
|
|
66
|
+
return data.id;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
console.error('[AgentRuntimeCustomExample] Error creating agent:', err);
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}, []);
|
|
73
|
+
const checkOrCreateAgent = useCallback(async () => {
|
|
74
|
+
setIsLoading(true);
|
|
75
|
+
setError(null);
|
|
76
|
+
try {
|
|
77
|
+
// First, try to find existing agent by name
|
|
78
|
+
const listResponse = await fetch(`${BASE_URL}/api/v1/agents`);
|
|
79
|
+
if (listResponse.ok) {
|
|
80
|
+
const data = await listResponse.json();
|
|
81
|
+
// API may return { agents: [...] } or just [...]
|
|
82
|
+
const agents = Array.isArray(data) ? data : data.agents || [];
|
|
83
|
+
const existingAgent = agents.find((a) => a.name === AGENT_NAME);
|
|
84
|
+
if (existingAgent) {
|
|
85
|
+
console.log('[AgentRuntimeCustomExample] Found existing agent:', existingAgent.id);
|
|
86
|
+
setAgentId(existingAgent.id);
|
|
87
|
+
setIsLoading(false);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Agent doesn't exist, create it
|
|
92
|
+
console.log('[AgentRuntimeCustomExample] Creating new agent...');
|
|
93
|
+
const newAgentId = await createAgent();
|
|
94
|
+
if (newAgentId) {
|
|
95
|
+
setAgentId(newAgentId);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
setError('Failed to create agent');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to initialize agent';
|
|
103
|
+
setError(errorMessage);
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
setIsLoading(false);
|
|
107
|
+
}
|
|
108
|
+
}, [createAgent]);
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
checkOrCreateAgent();
|
|
111
|
+
}, [checkOrCreateAgent]);
|
|
112
|
+
return { agentId, isLoading, error, retry: checkOrCreateAgent };
|
|
113
|
+
}
|
|
22
114
|
/**
|
|
23
115
|
* Agent Runtime Custom Example Component
|
|
24
116
|
*
|
|
25
|
-
* Demonstrates the unified Chat component with
|
|
117
|
+
* Demonstrates the unified Chat component with AG-UI transport
|
|
26
118
|
* and all necessary providers:
|
|
27
119
|
* - QueryClientProvider for data fetching
|
|
120
|
+
* - Auto-creates agent on the server if it doesn't exist
|
|
28
121
|
*/
|
|
29
122
|
const AgentRuntimeCustomExample = () => {
|
|
123
|
+
const { agentId, isLoading, error, retry } = useEnsureAgent();
|
|
30
124
|
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(DatalayerThemeProvider, { theme: datalayerTheme, children: _jsxs(Box, { sx: {
|
|
31
125
|
display: 'flex',
|
|
32
126
|
flexDirection: 'column',
|
|
@@ -41,12 +135,33 @@ const AgentRuntimeCustomExample = () => {
|
|
|
41
135
|
fontWeight: 'bold',
|
|
42
136
|
display: 'block',
|
|
43
137
|
marginBottom: 1,
|
|
44
|
-
}, children: "
|
|
138
|
+
}, children: "Datalayer Assistant" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Interactive chat interface with AI assistance" })] }), _jsx(Box, { as: "main", sx: {
|
|
45
139
|
flex: 1,
|
|
46
140
|
overflow: 'hidden',
|
|
47
141
|
display: 'flex',
|
|
48
142
|
flexDirection: 'column',
|
|
49
|
-
}, children:
|
|
143
|
+
}, children: isLoading ? (_jsxs(Box, { sx: {
|
|
144
|
+
display: 'flex',
|
|
145
|
+
flexDirection: 'column',
|
|
146
|
+
alignItems: 'center',
|
|
147
|
+
justifyContent: 'center',
|
|
148
|
+
height: '100%',
|
|
149
|
+
gap: 3,
|
|
150
|
+
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Initializing agent..." })] })) : error ? (_jsxs(Box, { sx: {
|
|
151
|
+
display: 'flex',
|
|
152
|
+
flexDirection: 'column',
|
|
153
|
+
alignItems: 'center',
|
|
154
|
+
justifyContent: 'center',
|
|
155
|
+
height: '100%',
|
|
156
|
+
gap: 3,
|
|
157
|
+
p: 4,
|
|
158
|
+
}, children: [_jsx(Text, { sx: { color: 'danger.fg', fontWeight: 'bold' }, children: "Failed to initialize agent" }), _jsx(Text, { sx: { color: 'fg.muted', textAlign: 'center' }, children: error }), _jsx(Text, { as: "button", onClick: retry, sx: {
|
|
159
|
+
color: 'accent.fg',
|
|
160
|
+
cursor: 'pointer',
|
|
161
|
+
textDecoration: 'underline',
|
|
162
|
+
border: 'none',
|
|
163
|
+
background: 'none',
|
|
164
|
+
}, children: "Retry" })] })) : agentId ? (_jsx(Chat, { transport: "ag-ui", agentId: agentId, showModelSelector: true, showToolsMenu: true, height: "100%", suggestions: [
|
|
50
165
|
{
|
|
51
166
|
title: '👋 Say hello',
|
|
52
167
|
message: 'Hello! What can you help me with today?',
|
|
@@ -63,6 +178,6 @@ const AgentRuntimeCustomExample = () => {
|
|
|
63
178
|
title: '📊 Data analysis',
|
|
64
179
|
message: 'How do I analyze data with pandas?',
|
|
65
180
|
},
|
|
66
|
-
] }) })] }) }) }));
|
|
181
|
+
] })) : null })] }) }) }));
|
|
67
182
|
};
|
|
68
183
|
export default AgentRuntimeCustomExample;
|
|
@@ -239,7 +239,7 @@ function LexicalWithChat({ content, serviceManager, }) {
|
|
|
239
239
|
color: 'danger.fg',
|
|
240
240
|
borderRadius: 2,
|
|
241
241
|
maxWidth: 300,
|
|
242
|
-
}, children: [_jsx("strong", { children: "Error:" }), " ", error] })), isReady && (_jsx(ChatFloating, { endpoint: AG_UI_ENDPOINT, title: "Lexical AI Agent Runtime", description: "Hi! I can help you edit documents. Try: 'Insert a heading', 'Add a code block', or 'Create a list'", defaultOpen: true, defaultViewMode: "panel", position: "bottom-right", brandColor: "#7c3aed", tools: tools, useStore: false, suggestions: [
|
|
242
|
+
}, children: [_jsx("strong", { children: "Error:" }), " ", error] })), isReady && (_jsx(ChatFloating, { endpoint: AG_UI_ENDPOINT, title: "Lexical AI Agent Runtime", description: "Hi! I can help you edit documents. Try: 'Insert a heading', 'Add a code block', or 'Create a list'", defaultOpen: true, defaultViewMode: "panel", position: "bottom-right", brandColor: "#7c3aed", tools: tools, useStore: false, showModelSelector: true, showToolsMenu: true, suggestions: [
|
|
243
243
|
{
|
|
244
244
|
title: 'Insert heading',
|
|
245
245
|
message: 'Insert a heading that says "Welcome"',
|
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Agent } from '../stores/examplesStore';
|
|
3
3
|
import type { Transport, Extension } from '../../components/chat';
|
|
4
|
+
/**
|
|
5
|
+
* MCP Server Tool type
|
|
6
|
+
*/
|
|
7
|
+
export interface MCPServerTool {
|
|
8
|
+
name: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* MCP Server configuration from backend
|
|
14
|
+
*/
|
|
15
|
+
export interface MCPServerConfig {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
url?: string;
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
tools: MCPServerTool[];
|
|
21
|
+
command?: string;
|
|
22
|
+
args?: string[];
|
|
23
|
+
isAvailable?: boolean;
|
|
24
|
+
transport?: string;
|
|
25
|
+
}
|
|
4
26
|
type AgentLibrary = 'pydantic-ai' | 'langchain' | 'jupyter-ai';
|
|
5
27
|
export type { AgentLibrary };
|
|
6
28
|
export type { Transport };
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { Text, TextInput, Button, FormControl, Select, Checkbox, Spinner, Flash, Label, ActionList, } from '@primer/react';
|
|
8
|
+
import { CheckIcon, XIcon, ToolsIcon } from '@primer/octicons-react';
|
|
9
|
+
import { useQuery } from '@tanstack/react-query';
|
|
3
10
|
import { Box } from '@datalayer/primer-addons';
|
|
4
11
|
const AGENT_LIBRARIES = [
|
|
5
12
|
{
|
|
@@ -65,6 +72,21 @@ const EXTENSIONS = [
|
|
|
65
72
|
* Form for configuring agent connection settings.
|
|
66
73
|
*/
|
|
67
74
|
export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl, baseUrl, agentName, agents, selectedAgentId, isCreatingAgent = false, createError = null, onAgentLibraryChange, onTransportChange, onExtensionsChange, onWsUrlChange, onBaseUrlChange, onAgentNameChange, onAgentSelect, onConnect, }) => {
|
|
75
|
+
// Fetch MCP servers configuration from the backend
|
|
76
|
+
const configQuery = useQuery({
|
|
77
|
+
queryKey: ['agent-config', baseUrl],
|
|
78
|
+
queryFn: async () => {
|
|
79
|
+
const response = await fetch(`${baseUrl}/api/v1/configure`);
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
throw new Error('Failed to fetch configuration');
|
|
82
|
+
}
|
|
83
|
+
return response.json();
|
|
84
|
+
},
|
|
85
|
+
enabled: !!baseUrl,
|
|
86
|
+
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
87
|
+
retry: 1,
|
|
88
|
+
});
|
|
89
|
+
const mcpServers = configQuery.data?.mcpServers || [];
|
|
68
90
|
// Determine which extensions are enabled based on transport
|
|
69
91
|
const isExtensionEnabled = (ext) => {
|
|
70
92
|
if (selectedAgentId !== 'new-agent')
|
|
@@ -95,7 +117,7 @@ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl,
|
|
|
95
117
|
fontWeight: 'bold',
|
|
96
118
|
display: 'block',
|
|
97
119
|
marginBottom: 3,
|
|
98
|
-
}, children: "
|
|
120
|
+
}, children: "Create a new Agent" }), _jsxs(FormControl, { sx: { marginBottom: 3 }, children: [_jsx(FormControl.Label, { children: "Available Agents" }), _jsxs(Select, { value: selectedAgentId, onChange: e => onAgentSelect(e.target.value), sx: { width: '100%' }, children: [_jsx(Select.Option, { value: "new-agent", children: "+ New Agent..." }), agents.map(agent => (_jsxs(Select.Option, { value: agent.id, children: [agent.status === 'running' && '● ', agent.name] }, agent.id)))] }), _jsx(FormControl.Caption, { children: selectedAgentId === 'new-agent'
|
|
99
121
|
? 'Configure a new custom agent'
|
|
100
122
|
: 'Selected agent - form fields below are disabled' })] }), _jsxs(Box, { sx: { display: 'flex', gap: 3, marginBottom: 3 }, children: [_jsxs(FormControl, { sx: { flex: 1 }, children: [_jsx(FormControl.Label, { children: "Agent Library" }), _jsx(Select, { value: agentLibrary, onChange: e => onAgentLibraryChange(e.target.value), disabled: selectedAgentId !== 'new-agent', sx: { width: '100%' }, children: AGENT_LIBRARIES.map(lib => (_jsxs(Select.Option, { value: lib.value, disabled: lib.disabled, children: [lib.label, lib.disabled && ' (Coming Soon)'] }, lib.value))) })] }), _jsxs(FormControl, { sx: { flex: 1 }, children: [_jsx(FormControl.Label, { children: "Transport" }), _jsx(Select, { value: transport, onChange: e => onTransportChange(e.target.value), disabled: selectedAgentId !== 'new-agent', sx: { width: '100%' }, children: TRANSPORTS.map(t => (_jsx(Select.Option, { value: t.value, children: t.label }, t.value))) })] }), _jsxs(FormControl, { sx: { flex: 1 }, children: [_jsx(FormControl.Label, { children: "Extensions" }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: EXTENSIONS.map(ext => (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Checkbox, { value: ext.value, checked: extensions.includes(ext.value), disabled: !isExtensionEnabled(ext.value), onChange: e => handleExtensionChange(ext.value, e.target.checked) }), _jsx(Text, { children: ext.label })] }, ext.value))) })] })] }), _jsxs(FormControl, { sx: { marginBottom: 3 }, children: [_jsx(FormControl.Label, { children: transport === 'acp' ? 'WebSocket URL' : 'Base URL' }), _jsx(TextInput, { value: transport === 'acp' ? wsUrl : baseUrl, onChange: e => transport === 'acp'
|
|
101
123
|
? onWsUrlChange(e.target.value)
|
|
@@ -103,7 +125,21 @@ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl,
|
|
|
103
125
|
? 'ws://localhost:8000/api/v1/acp/ws'
|
|
104
126
|
: 'http://localhost:8000', sx: { width: '100%' } }), _jsx(FormControl.Caption, { children: transport === 'acp'
|
|
105
127
|
? 'The WebSocket endpoint of your agent-runtimes server'
|
|
106
|
-
: 'The base URL of your agent-runtimes server' })] }), _jsxs(FormControl, { sx: { marginBottom: 3 }, children: [_jsx(FormControl.Label, { children: "Agent Name" }), _jsx(TextInput, { value: agentName, onChange: e => onAgentNameChange(e.target.value), disabled: selectedAgentId !== 'new-agent', placeholder: "demo-agent", sx: { width: '100%' } }), _jsx(FormControl.Caption, { children: "The name of the agent to connect to" })] }), _jsxs(
|
|
128
|
+
: 'The base URL of your agent-runtimes server' })] }), _jsxs(FormControl, { sx: { marginBottom: 3 }, children: [_jsx(FormControl.Label, { children: "Agent Name" }), _jsx(TextInput, { value: agentName, onChange: e => onAgentNameChange(e.target.value), disabled: selectedAgentId !== 'new-agent', placeholder: "demo-agent", sx: { width: '100%' } }), _jsx(FormControl.Caption, { children: "The name of the agent to connect to" })] }), _jsxs(Box, { sx: {
|
|
129
|
+
marginBottom: 3,
|
|
130
|
+
padding: 3,
|
|
131
|
+
border: '1px solid',
|
|
132
|
+
borderColor: 'border.default',
|
|
133
|
+
borderRadius: 2,
|
|
134
|
+
backgroundColor: 'canvas.default',
|
|
135
|
+
}, children: [_jsxs(Box, { sx: {
|
|
136
|
+
display: 'flex',
|
|
137
|
+
alignItems: 'center',
|
|
138
|
+
gap: 2,
|
|
139
|
+
marginBottom: 2,
|
|
140
|
+
}, children: [_jsx(ToolsIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: "MCP Servers" }), configQuery.isLoading && _jsx(Spinner, { size: "small" })] }), configQuery.isError && (_jsx(Flash, { variant: "warning", sx: { marginBottom: 2 }, children: _jsx(Text, { sx: { fontSize: 0 }, children: "Unable to fetch MCP servers. Check that the server is running." }) })), mcpServers.length === 0 &&
|
|
141
|
+
!configQuery.isLoading &&
|
|
142
|
+
!configQuery.isError && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "No MCP servers configured." })), mcpServers.length > 0 && (_jsx(ActionList, { children: mcpServers.map((server, index) => (_jsxs(React.Fragment, { children: [index > 0 && _jsx(ActionList.Divider, {}), _jsxs(ActionList.Item, { disabled: true, children: [_jsx(ActionList.LeadingVisual, { children: server.isAvailable ? (_jsx(CheckIcon, { size: 16 })) : (_jsx(XIcon, { size: 16 })) }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: server.name }), _jsx(Label, { variant: server.isAvailable ? 'success' : 'secondary', size: "small", children: server.isAvailable ? 'Available' : 'Not Available' })] }), server.tools.length > 0 && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Tools: ", server.tools.map(t => t.name).join(', ')] }))] })] })] }, server.id))) }))] }), createError && (_jsx(Flash, { variant: "danger", sx: { marginBottom: 3 }, children: createError })), _jsx(Button, { variant: "primary", onClick: onConnect, disabled: isCreatingAgent ||
|
|
107
143
|
!agentName ||
|
|
108
144
|
(transport === 'acp' ? !wsUrl : !baseUrl), sx: { width: '100%' }, children: isCreatingAgent ? (_jsxs(Box, { sx: {
|
|
109
145
|
display: 'flex',
|
|
@@ -8,4 +8,4 @@ export { TimeTravel } from './TimeTravel';
|
|
|
8
8
|
export { LexicalEditor } from './LexicalEditor';
|
|
9
9
|
export { Rating } from './Rating';
|
|
10
10
|
export { AgentConfiguration, AGENT_LIBRARIES, TRANSPORTS, EXTENSIONS, } from './AgentConfiguration';
|
|
11
|
-
export type { AgentLibrary, Transport, Extension } from './AgentConfiguration';
|
|
11
|
+
export type { AgentLibrary, Transport, Extension, MCPServerConfig, MCPServerTool, } from './AgentConfiguration';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Agent } from '../state';
|
|
2
2
|
/**
|
|
3
3
|
* Get the document AI Agent if any.
|
|
4
4
|
*
|
|
5
5
|
* It handles checking the AI Agent is alive so it should only be use once per document.
|
|
6
6
|
*/
|
|
7
|
-
export declare function useNotebookAIAgent(notebookId: string):
|
|
7
|
+
export declare function useNotebookAIAgent(notebookId: string): Agent | undefined;
|
|
8
8
|
export default useNotebookAIAgent;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
import { useEffect } from 'react';
|
|
6
|
-
import {
|
|
6
|
+
import { useAgentStore } from '../state';
|
|
7
7
|
import { useAIAgents } from './useAgents';
|
|
8
8
|
/**
|
|
9
9
|
* Get the document AI Agent if any.
|
|
@@ -12,7 +12,10 @@ import { useAIAgents } from './useAgents';
|
|
|
12
12
|
*/
|
|
13
13
|
export function useNotebookAIAgent(notebookId) {
|
|
14
14
|
const { getAIAgent } = useAIAgents();
|
|
15
|
-
const
|
|
15
|
+
const agents = useAgentStore(state => state.agents);
|
|
16
|
+
const upsertAgent = useAgentStore(state => state.upsertAgent);
|
|
17
|
+
const deleteAgent = useAgentStore(state => state.deleteAgent);
|
|
18
|
+
const getAgentById = useAgentStore(state => state.getAgentById);
|
|
16
19
|
// Check AI Agent is alive.
|
|
17
20
|
useEffect(() => {
|
|
18
21
|
let abortController;
|
|
@@ -23,25 +26,39 @@ export function useNotebookAIAgent(notebookId) {
|
|
|
23
26
|
signal: abortController.signal,
|
|
24
27
|
});
|
|
25
28
|
if (!response.success) {
|
|
26
|
-
|
|
29
|
+
deleteAgent(notebookId);
|
|
27
30
|
return;
|
|
28
31
|
}
|
|
29
|
-
const currentAgent =
|
|
32
|
+
const currentAgent = getAgentById(notebookId);
|
|
30
33
|
const runtimeId = response.agent.runtime?.id;
|
|
31
34
|
if (currentAgent) {
|
|
35
|
+
// Update existing agent if runtime changed
|
|
32
36
|
if (currentAgent.runtimeId !== runtimeId) {
|
|
33
|
-
|
|
37
|
+
upsertAgent({
|
|
38
|
+
id: notebookId,
|
|
39
|
+
baseUrl: currentAgent.baseUrl,
|
|
40
|
+
transport: currentAgent.transport,
|
|
41
|
+
runtimeId,
|
|
42
|
+
status: 'running',
|
|
43
|
+
});
|
|
34
44
|
}
|
|
35
45
|
}
|
|
36
46
|
else {
|
|
37
|
-
|
|
47
|
+
// Add new agent - assume it's a notebook agent with document tracking
|
|
48
|
+
upsertAgent({
|
|
49
|
+
id: notebookId,
|
|
50
|
+
name: `Notebook ${notebookId}`,
|
|
51
|
+
description: 'AI agent for notebook',
|
|
52
|
+
baseUrl: '', // Will be set by the actual agent implementation
|
|
53
|
+
transport: 'vercel-ai', // Default transport for notebook agents
|
|
38
54
|
documentId: notebookId,
|
|
39
55
|
runtimeId,
|
|
56
|
+
status: 'running',
|
|
40
57
|
});
|
|
41
58
|
}
|
|
42
59
|
}
|
|
43
60
|
catch (r) {
|
|
44
|
-
|
|
61
|
+
deleteAgent(notebookId);
|
|
45
62
|
}
|
|
46
63
|
};
|
|
47
64
|
const refreshInterval = setInterval(refreshAIAgent, 60_000);
|
|
@@ -49,8 +66,8 @@ export function useNotebookAIAgent(notebookId) {
|
|
|
49
66
|
abortController?.abort('Component unmounted');
|
|
50
67
|
clearInterval(refreshInterval);
|
|
51
68
|
};
|
|
52
|
-
}, [
|
|
53
|
-
const aiAgent =
|
|
69
|
+
}, [agents, notebookId, getAIAgent, deleteAgent, getAgentById, upsertAgent]);
|
|
70
|
+
const aiAgent = getAgentById(notebookId);
|
|
54
71
|
return aiAgent;
|
|
55
72
|
}
|
|
56
73
|
export default useNotebookAIAgent;
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,11 +1,81 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import type { Transport } from '../../components/chat/components/Chat';
|
|
2
|
+
export type AgentStatus = 'running' | 'paused' | 'initializing' | 'error';
|
|
3
|
+
export type { Transport };
|
|
4
|
+
/**
|
|
5
|
+
* Unified Agent model combining runtime tracking and UI state
|
|
6
|
+
*/
|
|
7
|
+
export interface Agent {
|
|
8
|
+
/** Unique agent identifier */
|
|
9
|
+
id: string;
|
|
10
|
+
/** Display name */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Agent description */
|
|
13
|
+
description: string;
|
|
14
|
+
/** Base URL for the agent (for Jupyter: baseUrl, for FastAPI: baseUrl) */
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
/** Transport protocol used */
|
|
17
|
+
transport: Transport;
|
|
18
|
+
/** Current status */
|
|
19
|
+
status: AgentStatus;
|
|
20
|
+
/** Last error message if status is 'error' */
|
|
21
|
+
error?: string | null;
|
|
22
|
+
/** Timestamp of last update */
|
|
23
|
+
lastUpdated: number;
|
|
24
|
+
/** Document ID (for document-based agents) */
|
|
25
|
+
documentId?: string;
|
|
26
|
+
/** Runtime ID (for Jupyter kernel-based agents) */
|
|
27
|
+
runtimeId?: string;
|
|
28
|
+
/** Author name (optional, for display) */
|
|
29
|
+
author?: string;
|
|
30
|
+
/** Avatar URL (optional, for display) */
|
|
31
|
+
avatarUrl?: string;
|
|
32
|
+
}
|
|
33
|
+
export type AgentState = {
|
|
34
|
+
/** All registered agents */
|
|
35
|
+
agents: readonly Agent[];
|
|
36
|
+
/** Add or update an agent */
|
|
37
|
+
upsertAgent: (agent: Partial<Agent> & {
|
|
38
|
+
id: string;
|
|
39
|
+
baseUrl: string;
|
|
40
|
+
transport: Transport;
|
|
41
|
+
}) => void;
|
|
42
|
+
/** Get agent by ID */
|
|
43
|
+
getAgentById: (id: string) => Agent | undefined;
|
|
44
|
+
/** Get agent by baseUrl and transport */
|
|
45
|
+
getAgentByUrl: (baseUrl: string, transport: Transport) => Agent | undefined;
|
|
46
|
+
/** Update agent status */
|
|
47
|
+
updateAgentStatus: (id: string, status: AgentStatus, error?: string | null) => void;
|
|
48
|
+
/** Toggle agent status between running/paused */
|
|
49
|
+
toggleAgentStatus: (id: string) => void;
|
|
50
|
+
/** Delete an agent */
|
|
51
|
+
deleteAgent: (id: string) => void;
|
|
52
|
+
/** Clear all agents */
|
|
53
|
+
clearAgents: () => void;
|
|
7
54
|
};
|
|
8
|
-
export declare const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
55
|
+
export declare const agentStore: Omit<import("zustand").StoreApi<AgentState>, "persist"> & {
|
|
56
|
+
persist: {
|
|
57
|
+
setOptions: (options: Partial<import("zustand/middleware").PersistOptions<AgentState, unknown>>) => void;
|
|
58
|
+
clearStorage: () => void;
|
|
59
|
+
rehydrate: () => Promise<void> | void;
|
|
60
|
+
hasHydrated: () => boolean;
|
|
61
|
+
onHydrate: (fn: (state: AgentState) => void) => () => void;
|
|
62
|
+
onFinishHydration: (fn: (state: AgentState) => void) => () => void;
|
|
63
|
+
getOptions: () => Partial<import("zustand/middleware").PersistOptions<AgentState, unknown>>;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
export declare function useAgentStore(): AgentState;
|
|
67
|
+
export declare function useAgentStore<T>(selector: (state: AgentState) => T): T;
|
|
68
|
+
export type AIAgentState = AgentState;
|
|
69
|
+
export declare const aiAgentStore: Omit<import("zustand").StoreApi<AgentState>, "persist"> & {
|
|
70
|
+
persist: {
|
|
71
|
+
setOptions: (options: Partial<import("zustand/middleware").PersistOptions<AgentState, unknown>>) => void;
|
|
72
|
+
clearStorage: () => void;
|
|
73
|
+
rehydrate: () => Promise<void> | void;
|
|
74
|
+
hasHydrated: () => boolean;
|
|
75
|
+
onHydrate: (fn: (state: AgentState) => void) => () => void;
|
|
76
|
+
onFinishHydration: (fn: (state: AgentState) => void) => () => void;
|
|
77
|
+
getOptions: () => Partial<import("zustand/middleware").PersistOptions<AgentState, unknown>>;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
export declare const useAIAgentStore: typeof useAgentStore;
|
|
81
|
+
export default useAgentStore;
|