@jupyterlite/ai 0.8.0 → 0.9.0-a0

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.
Files changed (162) hide show
  1. package/lib/agent.d.ts +233 -0
  2. package/lib/agent.js +604 -0
  3. package/lib/chat-model.d.ts +195 -0
  4. package/lib/chat-model.js +590 -0
  5. package/lib/completion/completion-provider.d.ts +83 -0
  6. package/lib/completion/completion-provider.js +209 -0
  7. package/lib/completion/index.d.ts +1 -0
  8. package/lib/completion/index.js +1 -0
  9. package/lib/components/clear-button.d.ts +18 -0
  10. package/lib/components/clear-button.js +31 -0
  11. package/lib/components/index.d.ts +3 -0
  12. package/lib/components/index.js +3 -0
  13. package/lib/components/model-select.d.ts +19 -0
  14. package/lib/components/model-select.js +154 -0
  15. package/lib/components/stop-button.d.ts +3 -3
  16. package/lib/components/stop-button.js +8 -9
  17. package/lib/components/token-usage-display.d.ts +45 -0
  18. package/lib/components/token-usage-display.js +74 -0
  19. package/lib/components/tool-select.d.ts +27 -0
  20. package/lib/components/tool-select.js +130 -0
  21. package/lib/icons.d.ts +3 -1
  22. package/lib/icons.js +10 -13
  23. package/lib/index.d.ts +4 -5
  24. package/lib/index.js +322 -167
  25. package/lib/mcp/browser.d.ts +68 -0
  26. package/lib/mcp/browser.js +132 -0
  27. package/lib/models/settings-model.d.ts +69 -0
  28. package/lib/models/settings-model.js +295 -0
  29. package/lib/providers/built-in-providers.d.ts +9 -0
  30. package/lib/providers/built-in-providers.js +192 -0
  31. package/lib/providers/models.d.ts +37 -0
  32. package/lib/providers/models.js +28 -0
  33. package/lib/providers/provider-registry.d.ts +94 -0
  34. package/lib/providers/provider-registry.js +155 -0
  35. package/lib/tokens.d.ts +157 -86
  36. package/lib/tokens.js +16 -12
  37. package/lib/tools/commands.d.ts +11 -0
  38. package/lib/tools/commands.js +126 -0
  39. package/lib/tools/file.d.ts +27 -0
  40. package/lib/tools/file.js +262 -0
  41. package/lib/tools/notebook.d.ts +40 -0
  42. package/lib/tools/notebook.js +762 -0
  43. package/lib/tools/tool-registry.d.ts +35 -0
  44. package/lib/tools/tool-registry.js +55 -0
  45. package/lib/widgets/ai-settings.d.ts +39 -0
  46. package/lib/widgets/ai-settings.js +506 -0
  47. package/lib/widgets/chat-wrapper.d.ts +144 -0
  48. package/lib/widgets/chat-wrapper.js +390 -0
  49. package/lib/widgets/provider-config-dialog.d.ts +13 -0
  50. package/lib/widgets/provider-config-dialog.js +104 -0
  51. package/package.json +150 -41
  52. package/schema/settings-model.json +153 -0
  53. package/src/agent.ts +800 -0
  54. package/src/chat-model.ts +770 -0
  55. package/src/completion/completion-provider.ts +308 -0
  56. package/src/completion/index.ts +1 -0
  57. package/src/components/clear-button.tsx +56 -0
  58. package/src/components/index.ts +3 -0
  59. package/src/components/model-select.tsx +245 -0
  60. package/src/components/stop-button.tsx +11 -11
  61. package/src/components/token-usage-display.tsx +130 -0
  62. package/src/components/tool-select.tsx +218 -0
  63. package/src/icons.ts +12 -14
  64. package/src/index.ts +468 -238
  65. package/src/mcp/browser.ts +213 -0
  66. package/src/models/settings-model.ts +409 -0
  67. package/src/providers/built-in-providers.ts +216 -0
  68. package/src/providers/models.ts +79 -0
  69. package/src/providers/provider-registry.ts +189 -0
  70. package/src/tokens.ts +203 -90
  71. package/src/tools/commands.ts +151 -0
  72. package/src/tools/file.ts +307 -0
  73. package/src/tools/notebook.ts +964 -0
  74. package/src/tools/tool-registry.ts +63 -0
  75. package/src/types.d.ts +4 -0
  76. package/src/widgets/ai-settings.tsx +1100 -0
  77. package/src/widgets/chat-wrapper.tsx +543 -0
  78. package/src/widgets/provider-config-dialog.tsx +256 -0
  79. package/style/base.css +335 -14
  80. package/style/icons/jupyternaut-lite.svg +1 -1
  81. package/lib/base-completer.d.ts +0 -49
  82. package/lib/base-completer.js +0 -14
  83. package/lib/chat-handler.d.ts +0 -56
  84. package/lib/chat-handler.js +0 -201
  85. package/lib/completion-provider.d.ts +0 -34
  86. package/lib/completion-provider.js +0 -32
  87. package/lib/default-prompts.d.ts +0 -2
  88. package/lib/default-prompts.js +0 -31
  89. package/lib/default-providers/Anthropic/completer.d.ts +0 -12
  90. package/lib/default-providers/Anthropic/completer.js +0 -46
  91. package/lib/default-providers/Anthropic/settings-schema.json +0 -70
  92. package/lib/default-providers/ChromeAI/completer.d.ts +0 -12
  93. package/lib/default-providers/ChromeAI/completer.js +0 -56
  94. package/lib/default-providers/ChromeAI/instructions.d.ts +0 -6
  95. package/lib/default-providers/ChromeAI/instructions.js +0 -42
  96. package/lib/default-providers/ChromeAI/settings-schema.json +0 -18
  97. package/lib/default-providers/Gemini/completer.d.ts +0 -12
  98. package/lib/default-providers/Gemini/completer.js +0 -48
  99. package/lib/default-providers/Gemini/instructions.d.ts +0 -2
  100. package/lib/default-providers/Gemini/instructions.js +0 -9
  101. package/lib/default-providers/Gemini/settings-schema.json +0 -64
  102. package/lib/default-providers/MistralAI/completer.d.ts +0 -13
  103. package/lib/default-providers/MistralAI/completer.js +0 -52
  104. package/lib/default-providers/MistralAI/instructions.d.ts +0 -2
  105. package/lib/default-providers/MistralAI/instructions.js +0 -18
  106. package/lib/default-providers/MistralAI/settings-schema.json +0 -75
  107. package/lib/default-providers/Ollama/completer.d.ts +0 -12
  108. package/lib/default-providers/Ollama/completer.js +0 -43
  109. package/lib/default-providers/Ollama/instructions.d.ts +0 -2
  110. package/lib/default-providers/Ollama/instructions.js +0 -70
  111. package/lib/default-providers/Ollama/settings-schema.json +0 -143
  112. package/lib/default-providers/OpenAI/completer.d.ts +0 -12
  113. package/lib/default-providers/OpenAI/completer.js +0 -43
  114. package/lib/default-providers/OpenAI/settings-schema.json +0 -628
  115. package/lib/default-providers/WebLLM/completer.d.ts +0 -21
  116. package/lib/default-providers/WebLLM/completer.js +0 -127
  117. package/lib/default-providers/WebLLM/instructions.d.ts +0 -6
  118. package/lib/default-providers/WebLLM/instructions.js +0 -32
  119. package/lib/default-providers/WebLLM/settings-schema.json +0 -19
  120. package/lib/default-providers/index.d.ts +0 -2
  121. package/lib/default-providers/index.js +0 -179
  122. package/lib/provider.d.ts +0 -144
  123. package/lib/provider.js +0 -412
  124. package/lib/settings/base.json +0 -7
  125. package/lib/settings/index.d.ts +0 -3
  126. package/lib/settings/index.js +0 -3
  127. package/lib/settings/panel.d.ts +0 -226
  128. package/lib/settings/panel.js +0 -510
  129. package/lib/settings/textarea.d.ts +0 -2
  130. package/lib/settings/textarea.js +0 -18
  131. package/lib/settings/utils.d.ts +0 -2
  132. package/lib/settings/utils.js +0 -4
  133. package/lib/types/ai-model.d.ts +0 -24
  134. package/lib/types/ai-model.js +0 -5
  135. package/schema/chat.json +0 -28
  136. package/schema/provider-registry.json +0 -29
  137. package/schema/system-prompts.json +0 -22
  138. package/src/base-completer.ts +0 -75
  139. package/src/chat-handler.ts +0 -262
  140. package/src/completion-provider.ts +0 -64
  141. package/src/default-prompts.ts +0 -33
  142. package/src/default-providers/Anthropic/completer.ts +0 -59
  143. package/src/default-providers/ChromeAI/completer.ts +0 -73
  144. package/src/default-providers/ChromeAI/instructions.ts +0 -45
  145. package/src/default-providers/Gemini/completer.ts +0 -61
  146. package/src/default-providers/Gemini/instructions.ts +0 -9
  147. package/src/default-providers/MistralAI/completer.ts +0 -69
  148. package/src/default-providers/MistralAI/instructions.ts +0 -18
  149. package/src/default-providers/Ollama/completer.ts +0 -54
  150. package/src/default-providers/Ollama/instructions.ts +0 -70
  151. package/src/default-providers/OpenAI/completer.ts +0 -54
  152. package/src/default-providers/WebLLM/completer.ts +0 -151
  153. package/src/default-providers/WebLLM/instructions.ts +0 -33
  154. package/src/default-providers/index.ts +0 -211
  155. package/src/global.d.ts +0 -9
  156. package/src/provider.ts +0 -514
  157. package/src/settings/index.ts +0 -3
  158. package/src/settings/panel.tsx +0 -773
  159. package/src/settings/textarea.tsx +0 -33
  160. package/src/settings/utils.ts +0 -5
  161. package/src/types/ai-model.ts +0 -37
  162. package/src/types/service-worker.d.ts +0 -6
@@ -0,0 +1,35 @@
1
+ import { ISignal } from '@lumino/signaling';
2
+ import { ITool, IToolRegistry, INamedTool } from '../tokens';
3
+ /**
4
+ * Implementation of the tool registry for managing AI tools
5
+ */
6
+ export declare class ToolRegistry implements IToolRegistry {
7
+ /**
8
+ * The registered tools as a record (name -> tool mapping).
9
+ */
10
+ get tools(): Record<string, ITool>;
11
+ /**
12
+ * The registered named tools array.
13
+ */
14
+ get namedTools(): INamedTool[];
15
+ /**
16
+ * A signal triggered when the tools have changed.
17
+ */
18
+ get toolsChanged(): ISignal<IToolRegistry, void>;
19
+ /**
20
+ * Add a new tool to the registry.
21
+ */
22
+ add(name: string, tool: ITool): void;
23
+ /**
24
+ * Get a tool for a given name.
25
+ * Return null if the name is not provided or if there is no registered tool with the
26
+ * given name.
27
+ */
28
+ get(name: string | null): ITool | null;
29
+ /**
30
+ * Remove a tool from the registry by name.
31
+ */
32
+ remove(name: string): boolean;
33
+ private _tools;
34
+ private _toolsChanged;
35
+ }
@@ -0,0 +1,55 @@
1
+ import { Signal } from '@lumino/signaling';
2
+ /**
3
+ * Implementation of the tool registry for managing AI tools
4
+ */
5
+ export class ToolRegistry {
6
+ /**
7
+ * The registered tools as a record (name -> tool mapping).
8
+ */
9
+ get tools() {
10
+ return { ...this._tools }; // Return a copy to prevent external modification
11
+ }
12
+ /**
13
+ * The registered named tools array.
14
+ */
15
+ get namedTools() {
16
+ return Object.entries(this._tools).map(([name, tool]) => ({ name, tool }));
17
+ }
18
+ /**
19
+ * A signal triggered when the tools have changed.
20
+ */
21
+ get toolsChanged() {
22
+ return this._toolsChanged;
23
+ }
24
+ /**
25
+ * Add a new tool to the registry.
26
+ */
27
+ add(name, tool) {
28
+ this._tools[name] = tool;
29
+ this._toolsChanged.emit();
30
+ }
31
+ /**
32
+ * Get a tool for a given name.
33
+ * Return null if the name is not provided or if there is no registered tool with the
34
+ * given name.
35
+ */
36
+ get(name) {
37
+ if (name === null) {
38
+ return null;
39
+ }
40
+ return this._tools[name] || null;
41
+ }
42
+ /**
43
+ * Remove a tool from the registry by name.
44
+ */
45
+ remove(name) {
46
+ if (name in this._tools) {
47
+ delete this._tools[name];
48
+ this._toolsChanged.emit();
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ _tools = {};
54
+ _toolsChanged = new Signal(this);
55
+ }
@@ -0,0 +1,39 @@
1
+ import { IThemeManager } from '@jupyterlab/apputils';
2
+ import { ReactWidget } from '@jupyterlab/ui-components';
3
+ import React from 'react';
4
+ import { AgentManager } from '../agent';
5
+ import { AISettingsModel } from '../models/settings-model';
6
+ import type { IChatProviderRegistry } from '../tokens';
7
+ /**
8
+ * A JupyterLab widget for AI settings configuration
9
+ */
10
+ export declare class AISettingsWidget extends ReactWidget {
11
+ /**
12
+ * Construct a new AI settings widget
13
+ * @param options - The options for initializing the widget
14
+ */
15
+ constructor(options: AISettingsWidget.IOptions);
16
+ /**
17
+ * Render the AI settings component
18
+ * @returns A React element containing the AI settings interface
19
+ */
20
+ protected render(): React.ReactElement;
21
+ private _settingsModel;
22
+ private _agentManager?;
23
+ private _themeManager?;
24
+ private _chatProviderRegistry;
25
+ }
26
+ /**
27
+ * Namespace for AISettingsWidget types and interfaces
28
+ */
29
+ export declare namespace AISettingsWidget {
30
+ /**
31
+ * Options interface for constructing an AISettingsWidget
32
+ */
33
+ interface IOptions {
34
+ settingsModel: AISettingsModel;
35
+ agentManager?: AgentManager;
36
+ themeManager?: IThemeManager;
37
+ chatProviderRegistry: IChatProviderRegistry;
38
+ }
39
+ }
@@ -0,0 +1,506 @@
1
+ import { ReactWidget } from '@jupyterlab/ui-components';
2
+ import Add from '@mui/icons-material/Add';
3
+ import Cable from '@mui/icons-material/Cable';
4
+ import CheckCircle from '@mui/icons-material/CheckCircle';
5
+ import CheckCircleOutline from '@mui/icons-material/CheckCircleOutline';
6
+ import Delete from '@mui/icons-material/Delete';
7
+ import Edit from '@mui/icons-material/Edit';
8
+ import Error from '@mui/icons-material/Error';
9
+ import ErrorOutline from '@mui/icons-material/ErrorOutline';
10
+ import MoreVert from '@mui/icons-material/MoreVert';
11
+ import Psychology from '@mui/icons-material/Psychology';
12
+ import Settings from '@mui/icons-material/Settings';
13
+ import { Alert, Box, Button, Card, CardContent, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControl, FormControlLabel, IconButton, InputLabel, List, ListItem, ListItemSecondaryAction, ListItemText, Menu, MenuItem, Select, Slider, Switch, Tab, Tabs, TextField, ThemeProvider, Typography, createTheme } from '@mui/material';
14
+ import React, { useEffect, useState } from 'react';
15
+ import { ProviderConfigDialog } from './provider-config-dialog';
16
+ /**
17
+ * Create a theme that uses IThemeManager to detect theme
18
+ * @param themeManager - Optional theme manager to detect theme
19
+ * @returns A Material-UI theme configured for the current JupyterLab theme
20
+ */
21
+ const createJupyterLabTheme = (themeManager) => {
22
+ // Use IThemeManager if available, otherwise default to light theme
23
+ const isDark = themeManager?.theme
24
+ ? !themeManager.isLight(themeManager.theme)
25
+ : false;
26
+ return createTheme({
27
+ palette: {
28
+ mode: isDark ? 'dark' : 'light'
29
+ }
30
+ });
31
+ };
32
+ /**
33
+ * A JupyterLab widget for AI settings configuration
34
+ */
35
+ export class AISettingsWidget extends ReactWidget {
36
+ /**
37
+ * Construct a new AI settings widget
38
+ * @param options - The options for initializing the widget
39
+ */
40
+ constructor(options) {
41
+ super();
42
+ this._settingsModel = options.settingsModel;
43
+ this._agentManager = options.agentManager;
44
+ this._themeManager = options.themeManager;
45
+ this._chatProviderRegistry = options.chatProviderRegistry;
46
+ this.id = 'jupyterlite-ai-settings';
47
+ this.title.label = 'AI Settings';
48
+ this.title.caption = 'Configure AI providers and behavior';
49
+ this.title.closable = true;
50
+ }
51
+ /**
52
+ * Render the AI settings component
53
+ * @returns A React element containing the AI settings interface
54
+ */
55
+ render() {
56
+ return (React.createElement(AISettingsComponent, { model: this._settingsModel, agentManager: this._agentManager, themeManager: this._themeManager, chatProviderRegistry: this._chatProviderRegistry }));
57
+ }
58
+ _settingsModel;
59
+ _agentManager;
60
+ _themeManager;
61
+ _chatProviderRegistry;
62
+ }
63
+ /**
64
+ * The main AI settings component that provides configuration UI
65
+ * @param props - Component props containing models and theme manager
66
+ * @returns A React component for AI settings configuration
67
+ */
68
+ const AISettingsComponent = ({ model, agentManager, themeManager, chatProviderRegistry }) => {
69
+ if (!model) {
70
+ return React.createElement("div", null, "Settings model not available");
71
+ }
72
+ const [config, setConfig] = useState(model.config || {});
73
+ const [theme, setTheme] = useState(() => createJupyterLabTheme(themeManager));
74
+ const [activeTab, setActiveTab] = useState(0);
75
+ const [dialogOpen, setDialogOpen] = useState(false);
76
+ const [editingProvider, setEditingProvider] = useState();
77
+ const [menuAnchor, setMenuAnchor] = useState(null);
78
+ const [menuProviderId, setMenuProviderId] = useState('');
79
+ const [mcpDialogOpen, setMcpDialogOpen] = useState(false);
80
+ const [editingMCPServer, setEditingMCPServer] = useState();
81
+ const [mcpMenuAnchor, setMcpMenuAnchor] = useState(null);
82
+ const [mcpMenuServerId, setMcpMenuServerId] = useState('');
83
+ /**
84
+ * Effect to listen for model state changes and update config
85
+ */
86
+ useEffect(() => {
87
+ if (!model || !model.stateChanged) {
88
+ return;
89
+ }
90
+ const onStateChanged = () => {
91
+ setConfig(model.config || {});
92
+ };
93
+ model.stateChanged.connect(onStateChanged);
94
+ return () => {
95
+ model.stateChanged.disconnect(onStateChanged);
96
+ };
97
+ }, [model]);
98
+ /**
99
+ * Effect to listen for theme changes and update the Material-UI theme
100
+ */
101
+ useEffect(() => {
102
+ if (!themeManager) {
103
+ return;
104
+ }
105
+ const updateTheme = () => {
106
+ setTheme(createJupyterLabTheme(themeManager));
107
+ };
108
+ themeManager.themeChanged.connect(updateTheme);
109
+ return () => {
110
+ themeManager.themeChanged.disconnect(updateTheme);
111
+ };
112
+ }, [themeManager]);
113
+ /**
114
+ * Effect to listen for MCP connection changes to re-render connection status
115
+ */
116
+ useEffect(() => {
117
+ if (!agentManager) {
118
+ return;
119
+ }
120
+ const onMCPConnectionChanged = () => {
121
+ // Force a re-render by updating the config state
122
+ setConfig(prevConfig => ({ ...prevConfig }));
123
+ };
124
+ agentManager.mcpConnectionChanged.connect(onMCPConnectionChanged);
125
+ return () => {
126
+ agentManager.mcpConnectionChanged.disconnect(onMCPConnectionChanged);
127
+ };
128
+ }, [agentManager]);
129
+ /**
130
+ * Handle adding a new AI provider
131
+ * @param providerConfig - The provider configuration to add
132
+ */
133
+ const handleAddProvider = async (providerConfig) => {
134
+ await model.addProvider(providerConfig);
135
+ };
136
+ /**
137
+ * Handle editing an existing AI provider
138
+ * @param providerConfig - The updated provider configuration
139
+ */
140
+ const handleEditProvider = async (providerConfig) => {
141
+ if (editingProvider) {
142
+ await model.updateProvider(editingProvider.id, providerConfig);
143
+ setEditingProvider(undefined);
144
+ }
145
+ };
146
+ /**
147
+ * Handle deleting an AI provider
148
+ * @param id - The ID of the provider to delete
149
+ */
150
+ const handleDeleteProvider = async (id) => {
151
+ await model.removeProvider(id);
152
+ setMenuAnchor(null);
153
+ };
154
+ /**
155
+ * Open the provider edit dialog
156
+ * @param provider - The provider to edit
157
+ */
158
+ const openEditDialog = (provider) => {
159
+ setEditingProvider(provider);
160
+ setDialogOpen(true);
161
+ setMenuAnchor(null);
162
+ };
163
+ /**
164
+ * Open the provider add dialog
165
+ */
166
+ const openAddDialog = () => {
167
+ setEditingProvider(undefined);
168
+ setDialogOpen(true);
169
+ };
170
+ /**
171
+ * Handle provider menu click
172
+ * @param event - The click event
173
+ * @param providerId - The ID of the provider
174
+ */
175
+ const handleMenuClick = (event, providerId) => {
176
+ setMenuAnchor(event.currentTarget);
177
+ setMenuProviderId(providerId);
178
+ };
179
+ /**
180
+ * Handle provider menu close
181
+ */
182
+ const handleMenuClose = () => {
183
+ setMenuAnchor(null);
184
+ setMenuProviderId('');
185
+ };
186
+ /**
187
+ * Handle updating AI configuration
188
+ * @param updates - Partial configuration updates to apply
189
+ */
190
+ const handleConfigUpdate = async (updates) => {
191
+ await model.updateConfig(updates);
192
+ };
193
+ /**
194
+ * Handle adding a new MCP server
195
+ * @param serverConfig - The MCP server configuration to add
196
+ */
197
+ const handleAddMCPServer = async (serverConfig) => {
198
+ await model.addMCPServer(serverConfig);
199
+ };
200
+ /**
201
+ * Handle editing an existing MCP server
202
+ * @param serverConfig - The updated MCP server configuration
203
+ */
204
+ const handleEditMCPServer = async (serverConfig) => {
205
+ if (editingMCPServer) {
206
+ await model.updateMCPServer(editingMCPServer.id, serverConfig);
207
+ setEditingMCPServer(undefined);
208
+ }
209
+ };
210
+ /**
211
+ * Handle deleting an MCP server
212
+ * @param id - The ID of the MCP server to delete
213
+ */
214
+ const handleDeleteMCPServer = async (id) => {
215
+ await model.removeMCPServer(id);
216
+ setMcpMenuAnchor(null);
217
+ };
218
+ /**
219
+ * Open the MCP server edit dialog
220
+ * @param server - The MCP server to edit
221
+ */
222
+ const openEditMCPDialog = (server) => {
223
+ setEditingMCPServer(server);
224
+ setMcpDialogOpen(true);
225
+ setMcpMenuAnchor(null);
226
+ };
227
+ /**
228
+ * Open the MCP server add dialog
229
+ */
230
+ const openAddMCPDialog = () => {
231
+ setEditingMCPServer(undefined);
232
+ setMcpDialogOpen(true);
233
+ };
234
+ /**
235
+ * Handle MCP server menu click
236
+ * @param event - The click event
237
+ * @param serverId - The ID of the MCP server
238
+ */
239
+ const handleMCPMenuClick = (event, serverId) => {
240
+ setMcpMenuAnchor(event.currentTarget);
241
+ setMcpMenuServerId(serverId);
242
+ };
243
+ /**
244
+ * Handle MCP server menu close
245
+ */
246
+ const handleMCPMenuClose = () => {
247
+ setMcpMenuAnchor(null);
248
+ setMcpMenuServerId('');
249
+ };
250
+ const isValidConfig = agentManager?.hasValidConfig() ?? false;
251
+ return (React.createElement(ThemeProvider, { theme: theme },
252
+ React.createElement(Box, { sx: {
253
+ height: '100%',
254
+ maxHeight: '100vh',
255
+ overflow: 'auto',
256
+ p: 2,
257
+ pb: 4,
258
+ fontSize: '0.9rem'
259
+ } },
260
+ React.createElement(Box, { sx: { mb: 2, display: 'flex', alignItems: 'center', gap: 2 } },
261
+ React.createElement(Settings, { color: "primary", sx: { fontSize: 24 } }),
262
+ React.createElement(Typography, { variant: "h5", component: "h1", sx: { fontWeight: 600 } }, "AI Settings")),
263
+ React.createElement(Alert, { severity: isValidConfig ? 'success' : 'warning', icon: isValidConfig ? React.createElement(CheckCircle, null) : React.createElement(Error, null), sx: { mb: 2 } }, isValidConfig
264
+ ? 'Configuration is valid and ready to use'
265
+ : 'Please add and configure a provider to get started'),
266
+ React.createElement(Box, { sx: { borderBottom: 1, borderColor: 'divider', mb: 2 } },
267
+ React.createElement(Tabs, { value: activeTab, onChange: (_, newValue) => setActiveTab(newValue) },
268
+ React.createElement(Tab, { label: "Providers" }),
269
+ React.createElement(Tab, { label: "Model Parameters" }),
270
+ React.createElement(Tab, { label: "Behavior" }),
271
+ React.createElement(Tab, { label: "MCP Servers" }))),
272
+ activeTab === 0 && (React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
273
+ config.providers.length > 0 && (React.createElement(Card, { elevation: 2 },
274
+ React.createElement(CardContent, null,
275
+ React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, "Active Providers"),
276
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
277
+ React.createElement(FormControl, { fullWidth: true },
278
+ React.createElement(InputLabel, null, "Chat Provider"),
279
+ React.createElement(Select, { value: config.activeProvider, label: "Chat Provider", onChange: e => model.setActiveProvider(e.target.value) }, config.providers.map(provider => (React.createElement(MenuItem, { key: provider.id, value: provider.id }, provider.name))))),
280
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.useSameProviderForChatAndCompleter, onChange: e => handleConfigUpdate({
281
+ useSameProviderForChatAndCompleter: e.target.checked
282
+ }), color: "primary" }), label: "Use same provider for chat and completions" }),
283
+ !config.useSameProviderForChatAndCompleter && (React.createElement(FormControl, { fullWidth: true },
284
+ React.createElement(InputLabel, null, "Completion Provider"),
285
+ React.createElement(Select, { value: config.activeCompleterProvider || '', label: "Completion Provider", onChange: e => model.setActiveCompleterProvider(e.target.value || undefined) },
286
+ React.createElement(MenuItem, { value: "" },
287
+ React.createElement("em", null, "Use chat provider")),
288
+ config.providers.map(provider => (React.createElement(MenuItem, { key: provider.id, value: provider.id }, provider.name)))))))))),
289
+ React.createElement(Card, { elevation: 2 },
290
+ React.createElement(CardContent, null,
291
+ React.createElement(Box, { sx: {
292
+ display: 'flex',
293
+ alignItems: 'center',
294
+ justifyContent: 'space-between',
295
+ mb: 2
296
+ } },
297
+ React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
298
+ React.createElement(Typography, { variant: "h6", component: "h2" }, "Configured Providers")),
299
+ React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddDialog, size: "small" }, "Add Provider")),
300
+ config.providers.length === 0 ? (React.createElement(Alert, { severity: "info" }, "No providers configured yet. Click \"Add Provider\" to get started.")) : (React.createElement(List, null, config.providers.map(provider => (React.createElement(ListItem, { key: provider.id, divider: true },
301
+ React.createElement(ListItemText, { primary: provider.name, secondary: React.createElement(Typography, { variant: "body2", color: "text.secondary" },
302
+ provider.provider,
303
+ " \u2022 ",
304
+ provider.model) }),
305
+ React.createElement(ListItemSecondaryAction, null,
306
+ React.createElement(IconButton, { onClick: e => handleMenuClick(e, provider.id), size: "small" },
307
+ React.createElement(MoreVert, null)))))))))))),
308
+ activeTab === 1 && (React.createElement(Card, { elevation: 2 },
309
+ React.createElement(CardContent, null,
310
+ React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', mb: 1.5, gap: 1 } },
311
+ React.createElement(Psychology, { color: "primary" }),
312
+ React.createElement(Typography, { variant: "h6", component: "h2" }, "Model Parameters")),
313
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
314
+ React.createElement(Box, null,
315
+ React.createElement(Typography, { variant: "body1", gutterBottom: true },
316
+ "Temperature: ",
317
+ config.temperature),
318
+ React.createElement(Slider, { value: config.temperature, onChange: (_, value) => handleConfigUpdate({ temperature: value }), min: 0, max: 2, step: 0.1, valueLabelDisplay: "auto" }),
319
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Controls response creativity: low value = deterministic and focused, middle value = balanced, high value = creative and varied")),
320
+ React.createElement(TextField, { fullWidth: true, label: "Max Tokens", type: "number", value: config.maxTokens || '', onChange: e => handleConfigUpdate({
321
+ maxTokens: e.target.value
322
+ ? parseInt(e.target.value)
323
+ : undefined
324
+ }), inputProps: { min: 1, max: 8192 }, helperText: "Maximum length of AI responses (leave empty for provider default)" }),
325
+ React.createElement(TextField, { fullWidth: true, label: "Max Turns", type: "number", value: config.maxTurns, onChange: e => handleConfigUpdate({
326
+ maxTurns: parseInt(e.target.value)
327
+ }), inputProps: { min: 1, max: 100 }, helperText: "Maximum number of tool execution turns (when using tools)" }))))),
328
+ activeTab === 2 && (React.createElement(Card, { elevation: 2 },
329
+ React.createElement(CardContent, null,
330
+ React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, "Behavior Settings"),
331
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
332
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.toolsEnabled, onChange: e => handleConfigUpdate({
333
+ toolsEnabled: e.target.checked
334
+ }), color: "primary" }), label: React.createElement(Box, null,
335
+ React.createElement(Typography, { variant: "body1" }, "Enable Tools"),
336
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Allow the AI to use tools like notebook operations, code execution, and file management")) }),
337
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.sendWithShiftEnter, onChange: e => handleConfigUpdate({
338
+ sendWithShiftEnter: e.target.checked
339
+ }), color: "primary" }), label: React.createElement(Box, null,
340
+ React.createElement(Typography, { variant: "body1" }, "Send with Shift+Enter"),
341
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Use Shift+Enter to send messages (Enter creates new line)")) }),
342
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showTokenUsage, onChange: e => handleConfigUpdate({
343
+ showTokenUsage: e.target.checked
344
+ }), color: "primary" }), label: React.createElement(Box, null,
345
+ React.createElement(Typography, { variant: "body1" }, "Show Token Usage"),
346
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Display token usage information in the chat toolbar")) }),
347
+ React.createElement(Divider, { sx: { my: 1 } }),
348
+ React.createElement(TextField, { fullWidth: true, multiline: true, rows: 3, label: "System Prompt", value: config.systemPrompt, onChange: e => handleConfigUpdate({ systemPrompt: e.target.value }), placeholder: "Define the AI's behavior and personality...", helperText: "Instructions that define how the AI should behave and respond" }),
349
+ React.createElement(Divider, { sx: { my: 2 } }),
350
+ React.createElement(Box, null,
351
+ React.createElement(Typography, { variant: "body1", gutterBottom: true }, "Commands Requiring Approval"),
352
+ React.createElement(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' } }, "Commands that require user approval before AI can execute them"),
353
+ React.createElement(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' } }, config.commandsRequiringApproval.map((command, index) => (React.createElement(ListItem, { key: index, divider: true },
354
+ React.createElement(ListItemText, { primary: command }),
355
+ React.createElement(ListItemSecondaryAction, null,
356
+ React.createElement(IconButton, { onClick: () => {
357
+ const newCommands = [
358
+ ...config.commandsRequiringApproval
359
+ ];
360
+ newCommands.splice(index, 1);
361
+ handleConfigUpdate({
362
+ commandsRequiringApproval: newCommands
363
+ });
364
+ }, size: "small" },
365
+ React.createElement(Delete, null))))))),
366
+ React.createElement(TextField, { fullWidth: true, label: "Add New Command", placeholder: "e.g., notebook:run-cell", onKeyDown: e => {
367
+ if (e.key === 'Enter') {
368
+ const value = e.target.value.trim();
369
+ if (value &&
370
+ !config.commandsRequiringApproval.includes(value)) {
371
+ const newCommands = [
372
+ ...config.commandsRequiringApproval,
373
+ value
374
+ ];
375
+ handleConfigUpdate({
376
+ commandsRequiringApproval: newCommands
377
+ });
378
+ e.target.value = '';
379
+ }
380
+ }
381
+ }, helperText: "Press Enter to add a command. Common commands: notebook:run-cell, console:execute, fileeditor:run-code" })))))),
382
+ activeTab === 3 && (React.createElement(Card, { elevation: 2 },
383
+ React.createElement(CardContent, null,
384
+ React.createElement(Box, { sx: {
385
+ display: 'flex',
386
+ alignItems: 'center',
387
+ justifyContent: 'space-between',
388
+ mb: 2
389
+ } },
390
+ React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
391
+ React.createElement(Cable, { color: "primary" }),
392
+ React.createElement(Typography, { variant: "h6", component: "h2" }, "Remote MCP Servers")),
393
+ React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddMCPDialog, size: "small" }, "Add Server")),
394
+ React.createElement(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 } }, "Configure remote Model Context Protocol (MCP) servers to extend the AI's capabilities with external tools and data sources."),
395
+ config.mcpServers.length === 0 ? (React.createElement(Alert, { severity: "info" }, "No MCP servers configured yet. Click \"Add Server\" to connect to remote MCP services.")) : (React.createElement(List, null, config.mcpServers.map(server => (React.createElement(ListItem, { key: server.id, divider: true },
396
+ React.createElement(ListItemText, { primary: React.createElement(Box, { sx: {
397
+ display: 'flex',
398
+ alignItems: 'center',
399
+ gap: 1
400
+ } },
401
+ React.createElement(Typography, { variant: "body1" }, server.name),
402
+ server.enabled &&
403
+ agentManager?.isMCPServerConnected(server.name) && (React.createElement(CheckCircleOutline, { sx: { color: 'success.main', fontSize: 16 } })),
404
+ server.enabled &&
405
+ !agentManager?.isMCPServerConnected(server.name) && (React.createElement(ErrorOutline, { sx: { color: 'error.main', fontSize: 16 } })),
406
+ React.createElement(Switch, { checked: server.enabled, onChange: e => model.updateMCPServer(server.id, {
407
+ enabled: e.target.checked
408
+ }), size: "small", color: "primary" })), secondary: React.createElement(Box, null,
409
+ React.createElement(Typography, { variant: "body2", color: "text.secondary" }, server.url),
410
+ server.enabled &&
411
+ server.connected !== undefined && (React.createElement(Typography, { variant: "caption", color: "text.secondary" },
412
+ "Status:",
413
+ ' ',
414
+ server.connected
415
+ ? 'Connected'
416
+ : 'Connection failed'))) }),
417
+ React.createElement(ListItemSecondaryAction, null,
418
+ React.createElement(IconButton, { onClick: e => handleMCPMenuClick(e, server.id), size: "small" },
419
+ React.createElement(MoreVert, null))))))))))),
420
+ React.createElement(ProviderConfigDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSave: editingProvider ? handleEditProvider : handleAddProvider, initialConfig: editingProvider, mode: editingProvider ? 'edit' : 'add', chatProviderRegistry: chatProviderRegistry }),
421
+ React.createElement(Menu, { anchorEl: menuAnchor, open: Boolean(menuAnchor), onClose: handleMenuClose },
422
+ React.createElement(MenuItem, { onClick: () => {
423
+ const provider = config.providers.find(p => p.id === menuProviderId);
424
+ if (provider) {
425
+ openEditDialog(provider);
426
+ }
427
+ } },
428
+ React.createElement(Edit, { sx: { mr: 1 } }),
429
+ "Edit"),
430
+ React.createElement(MenuItem, { onClick: () => handleDeleteProvider(menuProviderId), sx: { color: 'error.main' } },
431
+ React.createElement(Delete, { sx: { mr: 1 } }),
432
+ "Delete")),
433
+ React.createElement(MCPServerDialog, { open: mcpDialogOpen, onClose: () => setMcpDialogOpen(false), onSave: editingMCPServer ? handleEditMCPServer : handleAddMCPServer, initialConfig: editingMCPServer, mode: editingMCPServer ? 'edit' : 'add' }),
434
+ React.createElement(Menu, { anchorEl: mcpMenuAnchor, open: Boolean(mcpMenuAnchor), onClose: handleMCPMenuClose },
435
+ React.createElement(MenuItem, { onClick: () => {
436
+ const server = config.mcpServers.find(s => s.id === mcpMenuServerId);
437
+ if (server) {
438
+ openEditMCPDialog(server);
439
+ }
440
+ } },
441
+ React.createElement(Edit, { sx: { mr: 1 } }),
442
+ "Edit"),
443
+ React.createElement(MenuItem, { onClick: () => handleDeleteMCPServer(mcpMenuServerId), sx: { color: 'error.main' } },
444
+ React.createElement(Delete, { sx: { mr: 1 } }),
445
+ "Delete")))));
446
+ };
447
+ /**
448
+ * Dialog component for adding/editing MCP server configurations
449
+ * @param props - Component props for the MCP server dialog
450
+ * @returns A React component for MCP server configuration
451
+ */
452
+ const MCPServerDialog = ({ open, onClose, onSave, initialConfig, mode }) => {
453
+ const [name, setName] = useState(initialConfig?.name || '');
454
+ const [url, setUrl] = useState(initialConfig?.url || '');
455
+ const [enabled, setEnabled] = useState(initialConfig?.enabled ?? true);
456
+ /**
457
+ * Effect to reset dialog state when opened with new config
458
+ */
459
+ useEffect(() => {
460
+ if (open) {
461
+ setName(initialConfig?.name || '');
462
+ setUrl(initialConfig?.url || '');
463
+ setEnabled(initialConfig?.enabled ?? true);
464
+ }
465
+ }, [open, initialConfig]);
466
+ /**
467
+ * Handle saving the MCP server configuration
468
+ */
469
+ const handleSave = () => {
470
+ if (!name.trim() || !url.trim()) {
471
+ return;
472
+ }
473
+ onSave({
474
+ name: name.trim(),
475
+ url: url.trim(),
476
+ enabled,
477
+ connected: false
478
+ });
479
+ onClose();
480
+ };
481
+ /**
482
+ * Check if a URL is valid
483
+ * @param url - The URL to validate
484
+ * @returns true if the URL is valid
485
+ */
486
+ const _isValidUrl = (url) => {
487
+ try {
488
+ new URL(url);
489
+ return true;
490
+ }
491
+ catch {
492
+ return false;
493
+ }
494
+ };
495
+ const canSave = name.trim() && url.trim() && _isValidUrl(url.trim());
496
+ return (React.createElement(Dialog, { open: open, onClose: onClose, maxWidth: "sm", fullWidth: true },
497
+ React.createElement(DialogTitle, null, mode === 'add' ? 'Add MCP Server' : 'Edit MCP Server'),
498
+ React.createElement(DialogContent, null,
499
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2, pt: 1 } },
500
+ React.createElement(TextField, { autoFocus: true, fullWidth: true, label: "Server Name", value: name, onChange: e => setName(e.target.value), placeholder: "My MCP Server", helperText: "A friendly name to identify this MCP server" }),
501
+ React.createElement(TextField, { fullWidth: true, label: "Server URL", value: url, onChange: e => setUrl(e.target.value), placeholder: "https://example.com/mcp", helperText: "The HTTP/HTTPS URL of the MCP server", error: Boolean(url.trim() && !_isValidUrl(url.trim())) }),
502
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: enabled, onChange: e => setEnabled(e.target.checked), color: "primary" }), label: "Enable this server" }))),
503
+ React.createElement(DialogActions, null,
504
+ React.createElement(Button, { onClick: onClose }, "Cancel"),
505
+ React.createElement(Button, { onClick: handleSave, variant: "contained", disabled: !canSave }, mode === 'add' ? 'Add' : 'Save'))));
506
+ };