@jupyterlite/ai 0.9.1 → 0.11.0

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 (71) hide show
  1. package/README.md +5 -214
  2. package/lib/agent.d.ts +58 -66
  3. package/lib/agent.js +291 -310
  4. package/lib/approval-buttons.d.ts +19 -82
  5. package/lib/approval-buttons.js +36 -289
  6. package/lib/chat-model-registry.d.ts +6 -0
  7. package/lib/chat-model-registry.js +4 -1
  8. package/lib/chat-model.d.ts +26 -54
  9. package/lib/chat-model.js +277 -303
  10. package/lib/components/clear-button.d.ts +6 -1
  11. package/lib/components/clear-button.js +10 -6
  12. package/lib/components/completion-status.d.ts +5 -0
  13. package/lib/components/completion-status.js +5 -4
  14. package/lib/components/model-select.d.ts +6 -1
  15. package/lib/components/model-select.js +13 -16
  16. package/lib/components/stop-button.d.ts +6 -1
  17. package/lib/components/stop-button.js +12 -8
  18. package/lib/components/token-usage-display.d.ts +5 -0
  19. package/lib/components/token-usage-display.js +2 -2
  20. package/lib/components/tool-select.d.ts +6 -1
  21. package/lib/components/tool-select.js +10 -9
  22. package/lib/index.d.ts +1 -0
  23. package/lib/index.js +61 -81
  24. package/lib/models/settings-model.d.ts +1 -1
  25. package/lib/models/settings-model.js +40 -26
  26. package/lib/providers/built-in-providers.js +38 -19
  27. package/lib/providers/models.d.ts +3 -3
  28. package/lib/providers/provider-registry.d.ts +3 -4
  29. package/lib/providers/provider-registry.js +1 -4
  30. package/lib/tokens.d.ts +5 -6
  31. package/lib/tools/commands.d.ts +2 -1
  32. package/lib/tools/commands.js +36 -49
  33. package/lib/widgets/ai-settings.d.ts +6 -0
  34. package/lib/widgets/ai-settings.js +72 -71
  35. package/lib/widgets/main-area-chat.d.ts +2 -0
  36. package/lib/widgets/main-area-chat.js +5 -2
  37. package/lib/widgets/provider-config-dialog.d.ts +2 -0
  38. package/lib/widgets/provider-config-dialog.js +34 -34
  39. package/package.json +13 -13
  40. package/schema/settings-model.json +3 -2
  41. package/src/agent.ts +360 -372
  42. package/src/approval-buttons.ts +43 -389
  43. package/src/chat-model-registry.ts +9 -1
  44. package/src/chat-model.ts +399 -370
  45. package/src/completion/completion-provider.ts +2 -3
  46. package/src/components/clear-button.tsx +18 -6
  47. package/src/components/completion-status.tsx +18 -4
  48. package/src/components/model-select.tsx +25 -16
  49. package/src/components/stop-button.tsx +22 -9
  50. package/src/components/token-usage-display.tsx +14 -2
  51. package/src/components/tool-select.tsx +27 -9
  52. package/src/index.ts +78 -134
  53. package/src/models/settings-model.ts +41 -27
  54. package/src/providers/built-in-providers.ts +38 -19
  55. package/src/providers/models.ts +3 -3
  56. package/src/providers/provider-registry.ts +4 -8
  57. package/src/tokens.ts +5 -6
  58. package/src/tools/commands.ts +40 -53
  59. package/src/widgets/ai-settings.tsx +153 -84
  60. package/src/widgets/main-area-chat.ts +8 -2
  61. package/src/widgets/provider-config-dialog.tsx +54 -41
  62. package/style/base.css +24 -73
  63. package/lib/mcp/browser.d.ts +0 -68
  64. package/lib/mcp/browser.js +0 -138
  65. package/lib/tools/file.d.ts +0 -36
  66. package/lib/tools/file.js +0 -351
  67. package/lib/tools/notebook.d.ts +0 -40
  68. package/lib/tools/notebook.js +0 -779
  69. package/src/mcp/browser.ts +0 -220
  70. package/src/tools/file.ts +0 -438
  71. package/src/tools/notebook.ts +0 -986
package/lib/tokens.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { Token } from '@lumino/coreutils';
2
2
  import { ISignal } from '@lumino/signaling';
3
- import { FunctionTool, Model } from '@openai/agents';
4
- import { LanguageModelV2 } from '@ai-sdk/provider';
3
+ import type { Tool, LanguageModel } from 'ai';
5
4
  import { AgentManager } from './agent';
6
5
  import type { AISettingsModel } from './models/settings-model';
7
6
  import type { IModelOptions } from './providers/models';
@@ -19,7 +18,7 @@ export declare namespace CommandIds {
19
18
  /**
20
19
  * Type definition for a tool
21
20
  */
22
- export type ITool = FunctionTool<any, any, any>;
21
+ export type ITool = Tool;
23
22
  /**
24
23
  * Interface for token usage statistics from AI model interactions
25
24
  */
@@ -89,7 +88,7 @@ export declare const IProviderRegistry: Token<IProviderRegistry>;
89
88
  * Interface for a provider factory function that creates language models
90
89
  */
91
90
  export interface IProviderFactory {
92
- (options: IModelOptions): LanguageModelV2;
91
+ (options: IModelOptions): LanguageModel;
93
92
  }
94
93
  /**
95
94
  * Provider information
@@ -165,11 +164,11 @@ export interface IProviderRegistry {
165
164
  /**
166
165
  * Create a chat model instance for the given provider.
167
166
  */
168
- createChatModel(id: string, options: IModelOptions): Model | null;
167
+ createChatModel(id: string, options: IModelOptions): LanguageModel | null;
169
168
  /**
170
169
  * Create a completion model instance for the given provider.
171
170
  */
172
- createCompletionModel(id: string, options: IModelOptions): LanguageModelV2 | null;
171
+ createCompletionModel(id: string, options: IModelOptions): LanguageModel | null;
173
172
  /**
174
173
  * Get all available provider IDs.
175
174
  */
@@ -6,6 +6,7 @@ import { AISettingsModel } from '../models/settings-model';
6
6
  */
7
7
  export declare function createDiscoverCommandsTool(commands: CommandRegistry): ITool;
8
8
  /**
9
- * Create a tool to execute a specific JupyterLab command
9
+ * Create a tool to execute a specific JupyterLab command.
10
+ * Commands in the settings' commandsRequiringApproval list will need approval.
10
11
  */
11
12
  export declare function createExecuteCommandTool(commands: CommandRegistry, settingsModel: AISettingsModel): ITool;
@@ -1,19 +1,18 @@
1
- import { tool } from '@openai/agents';
1
+ import { tool } from 'ai';
2
2
  import { z } from 'zod';
3
3
  /**
4
4
  * Create a tool to discover all available commands and their metadata
5
5
  */
6
6
  export function createDiscoverCommandsTool(commands) {
7
7
  return tool({
8
- name: 'discover_commands',
8
+ title: 'Discover Commands',
9
9
  description: 'Discover all available JupyterLab commands with their metadata, arguments, and descriptions',
10
- parameters: z.object({
11
- // currently unused, but could be used to filter commands by a search term
10
+ inputSchema: z.object({
12
11
  query: z
13
12
  .string()
14
13
  .optional()
15
14
  .nullable()
16
- .describe('Optional search query to filter commands')
15
+ .describe("Optional search query to filter commands. It doesn't need to be provided to list all commands")
17
16
  }),
18
17
  execute: async (input) => {
19
18
  const { query } = input;
@@ -57,23 +56,23 @@ export function createDiscoverCommandsTool(commands) {
57
56
  });
58
57
  }
59
58
  /**
60
- * Create a tool to execute a specific JupyterLab command
59
+ * Create a tool to execute a specific JupyterLab command.
60
+ * Commands in the settings' commandsRequiringApproval list will need approval.
61
61
  */
62
62
  export function createExecuteCommandTool(commands, settingsModel) {
63
63
  return tool({
64
- name: 'execute_command',
64
+ title: 'Execute Command',
65
65
  description: 'Execute a specific JupyterLab command with optional arguments',
66
- parameters: z.object({
66
+ inputSchema: z.object({
67
67
  commandId: z.string().describe('The ID of the command to execute'),
68
68
  args: z
69
- .any()
69
+ .record(z.string(), z.unknown())
70
70
  .optional()
71
- .describe('Optional arguments to pass to the command')
71
+ .describe('Optional arguments object to pass to the command (must be an object, not a string)')
72
72
  }),
73
- needsApproval: async (context, { commandId }) => {
74
- // Use configurable list of commands requiring approval
75
- const commandsRequiringApproval = settingsModel.config.commandsRequiringApproval;
76
- return commandsRequiringApproval.some(cmd => commandId.includes(cmd) || cmd.includes(commandId));
73
+ needsApproval: (input) => {
74
+ const commandsRequiringApproval = settingsModel.config.commandsRequiringApproval || [];
75
+ return commandsRequiringApproval.includes(input.commandId);
77
76
  },
78
77
  execute: async (input) => {
79
78
  const { commandId, args } = input;
@@ -84,44 +83,32 @@ export function createExecuteCommandTool(commands, settingsModel) {
84
83
  error: `Command '${commandId}' does not exist. Use 'discover_commands' to see available commands.`
85
84
  };
86
85
  }
87
- try {
88
- // Execute the command
89
- const result = await commands.execute(commandId, args);
90
- // Handle Widget objects specially (including subclasses like DocumentWidget)
91
- let serializedResult;
92
- if (result &&
93
- typeof result === 'object' &&
94
- (result.constructor?.name?.includes('Widget') || result.id)) {
95
- serializedResult = {
96
- type: result.constructor?.name || 'Widget',
97
- id: result.id,
98
- title: result.title?.label || result.title,
99
- className: result.className
100
- };
101
- }
102
- else {
103
- // For other objects, try JSON serialization with fallback
104
- try {
105
- serializedResult = JSON.parse(JSON.stringify(result));
106
- }
107
- catch {
108
- serializedResult = result
109
- ? '[Complex object - cannot serialize]'
110
- : 'Command executed successfully';
111
- }
112
- }
113
- return {
114
- success: true,
115
- commandId,
116
- result: serializedResult
86
+ // Execute the command
87
+ const result = await commands.execute(commandId, args);
88
+ // Handle Widget objects specially by extracting id and title
89
+ let serializedResult;
90
+ if (result && typeof result === 'object' && result.id) {
91
+ serializedResult = {
92
+ id: result.id,
93
+ title: result.title?.label || result.title
117
94
  };
118
95
  }
119
- catch (error) {
120
- return {
121
- success: false,
122
- error: `Failed to execute command '${commandId}': ${error instanceof Error ? error.message : String(error)}`
123
- };
96
+ else {
97
+ // For other objects, try JSON serialization with fallback
98
+ try {
99
+ serializedResult = JSON.parse(JSON.stringify(result));
100
+ }
101
+ catch {
102
+ serializedResult = result
103
+ ? '[Complex object - cannot serialize]'
104
+ : 'Command executed successfully';
105
+ }
124
106
  }
107
+ return {
108
+ success: true,
109
+ commandId,
110
+ result: serializedResult
111
+ };
125
112
  }
126
113
  });
127
114
  }
@@ -1,5 +1,6 @@
1
1
  import { IThemeManager } from '@jupyterlab/apputils';
2
2
  import { ReactWidget } from '@jupyterlab/ui-components';
3
+ import type { TranslationBundle } from '@jupyterlab/translation';
3
4
  import { ISecretsManager } from 'jupyter-secrets-manager';
4
5
  import React from 'react';
5
6
  import { AgentManagerFactory } from '../agent';
@@ -24,6 +25,7 @@ export declare class AISettingsWidget extends ReactWidget {
24
25
  private _themeManager?;
25
26
  private _providerRegistry;
26
27
  private _secretsManager?;
28
+ private _trans;
27
29
  }
28
30
  /**
29
31
  * Namespace for AISettingsWidget types and interfaces
@@ -45,5 +47,9 @@ export declare namespace AISettingsWidget {
45
47
  * The token used to request the secrets manager.
46
48
  */
47
49
  token: symbol;
50
+ /**
51
+ * The application language translation bundle.
52
+ */
53
+ trans: TranslationBundle;
48
54
  }
49
55
  }
@@ -46,9 +46,10 @@ export class AISettingsWidget extends ReactWidget {
46
46
  this._themeManager = options.themeManager;
47
47
  this._providerRegistry = options.providerRegistry;
48
48
  this._secretsManager = options.secretsManager;
49
+ this._trans = options.trans;
49
50
  this.id = 'jupyterlite-ai-settings';
50
- this.title.label = 'AI Settings';
51
- this.title.caption = 'Configure AI providers and behavior';
51
+ this.title.label = this._trans.__('AI Settings');
52
+ this.title.caption = this._trans.__('Configure AI providers and behavior');
52
53
  this.title.closable = true;
53
54
  }
54
55
  /**
@@ -56,22 +57,23 @@ export class AISettingsWidget extends ReactWidget {
56
57
  * @returns A React element containing the AI settings interface
57
58
  */
58
59
  render() {
59
- return (React.createElement(AISettingsComponent, { model: this._settingsModel, agentManagerFactory: this._agentManagerFactory, themeManager: this._themeManager, providerRegistry: this._providerRegistry, secretsManager: this._secretsManager }));
60
+ return (React.createElement(AISettingsComponent, { model: this._settingsModel, agentManagerFactory: this._agentManagerFactory, themeManager: this._themeManager, providerRegistry: this._providerRegistry, secretsManager: this._secretsManager, trans: this._trans }));
60
61
  }
61
62
  _settingsModel;
62
63
  _agentManagerFactory;
63
64
  _themeManager;
64
65
  _providerRegistry;
65
66
  _secretsManager;
67
+ _trans;
66
68
  }
67
69
  /**
68
70
  * The main AI settings component that provides configuration UI
69
71
  * @param props - Component props containing models and theme manager
70
72
  * @returns A React component for AI settings configuration
71
73
  */
72
- const AISettingsComponent = ({ model, agentManagerFactory, themeManager, providerRegistry, secretsManager }) => {
74
+ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, providerRegistry, secretsManager, trans }) => {
73
75
  if (!model) {
74
- return React.createElement("div", null, "Settings model not available");
76
+ return React.createElement("div", null, trans.__('Settings model not available'));
75
77
  }
76
78
  const [config, setConfig] = useState(model.config || {});
77
79
  const [theme, setTheme] = useState(() => createJupyterLabTheme(themeManager));
@@ -358,28 +360,28 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
358
360
  } },
359
361
  React.createElement(Box, { sx: { mb: 2, display: 'flex', alignItems: 'center', gap: 2 } },
360
362
  React.createElement(Settings, { color: "primary", sx: { fontSize: 24 } }),
361
- React.createElement(Typography, { variant: "h5", component: "h1", sx: { fontWeight: 600 } }, "AI Settings")),
363
+ React.createElement(Typography, { variant: "h5", component: "h1", sx: { fontWeight: 600 } }, trans.__('AI Settings'))),
362
364
  React.createElement(Box, { sx: { borderBottom: 1, borderColor: 'divider', mb: 2 } },
363
365
  React.createElement(Tabs, { value: activeTab, onChange: (_, newValue) => setActiveTab(newValue) },
364
- React.createElement(Tab, { label: "Providers" }),
365
- React.createElement(Tab, { label: "Behavior" }),
366
- React.createElement(Tab, { label: "MCP Servers" }))),
366
+ React.createElement(Tab, { label: trans.__('Providers') }),
367
+ React.createElement(Tab, { label: trans.__('Behavior') }),
368
+ React.createElement(Tab, { label: trans.__('MCP Servers') }))),
367
369
  activeTab === 0 && (React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
368
370
  config.providers.length > 0 && (React.createElement(Card, { elevation: 2 },
369
371
  React.createElement(CardContent, null,
370
- React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, "Default Providers"),
372
+ React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, trans.__('Default Providers')),
371
373
  React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
372
374
  React.createElement(FormControl, { fullWidth: true },
373
- React.createElement(InputLabel, null, "Chat Provider"),
374
- React.createElement(Select, { value: config.defaultProvider, 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))))),
375
+ React.createElement(InputLabel, null, trans.__('Chat Provider')),
376
+ React.createElement(Select, { value: config.defaultProvider, label: trans.__('Chat Provider'), onChange: e => model.setActiveProvider(e.target.value) }, config.providers.map(provider => (React.createElement(MenuItem, { key: provider.id, value: provider.id }, provider.name))))),
375
377
  React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.useSameProviderForChatAndCompleter, onChange: e => handleConfigUpdate({
376
378
  useSameProviderForChatAndCompleter: e.target.checked
377
- }), color: "primary" }), label: "Use same provider for chat and completions" }),
379
+ }), color: "primary" }), label: trans.__('Use same provider for chat and completions') }),
378
380
  !config.useSameProviderForChatAndCompleter && (React.createElement(FormControl, { fullWidth: true },
379
- React.createElement(InputLabel, null, "Completion Provider"),
380
- React.createElement(Select, { value: config.activeCompleterProvider || '', label: "Completion Provider", className: "jp-ai-completion-provider-select", onChange: e => model.setActiveCompleterProvider(e.target.value || undefined) },
381
+ React.createElement(InputLabel, null, trans.__('Completion Provider')),
382
+ React.createElement(Select, { value: config.activeCompleterProvider || '', label: trans.__('Completion Provider'), className: "jp-ai-completion-provider-select", onChange: e => model.setActiveCompleterProvider(e.target.value || undefined) },
381
383
  React.createElement(MenuItem, { value: "" },
382
- React.createElement("em", null, "No completion")),
384
+ React.createElement("em", null, trans.__('No completion'))),
383
385
  config.providers.map(provider => (React.createElement(MenuItem, { key: provider.id, value: provider.id }, provider.name)))))))))),
384
386
  React.createElement(Card, { elevation: 2 },
385
387
  React.createElement(CardContent, null,
@@ -390,9 +392,9 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
390
392
  mb: 2
391
393
  } },
392
394
  React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
393
- React.createElement(Typography, { variant: "h6", component: "h2" }, "Configured Providers")),
394
- React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddDialog, size: "small" }, "Add Provider")),
395
- 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 => {
395
+ React.createElement(Typography, { variant: "h6", component: "h2" }, trans.__('Configured Providers'))),
396
+ React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddDialog, size: "small" }, trans.__('Add Provider'))),
397
+ config.providers.length === 0 ? (React.createElement(Alert, { severity: "info" }, trans.__('No providers configured yet. Click "Add Provider" to get started.'))) : (React.createElement(List, null, config.providers.map(provider => {
396
398
  const isActive = config.defaultProvider === provider.id;
397
399
  const isActiveCompleter = config.useSameProviderForChatAndCompleter
398
400
  ? isActive
@@ -418,8 +420,8 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
418
420
  mb: 0.5
419
421
  } },
420
422
  React.createElement(Typography, { variant: "subtitle1", fontWeight: "medium" }, provider.name),
421
- isActive && (React.createElement(Chip, { label: "Chat", size: "small", color: "primary", icon: React.createElement(CheckCircle, null) })),
422
- isActiveCompleter && (React.createElement(Chip, { label: "Completion", size: "small", color: "secondary", icon: React.createElement(CheckCircle, null) }))),
423
+ isActive && (React.createElement(Chip, { label: trans.__('Chat'), size: "small", color: "primary", icon: React.createElement(CheckCircle, null) })),
424
+ isActiveCompleter && (React.createElement(Chip, { label: trans.__('Completion'), size: "small", color: "secondary", icon: React.createElement(CheckCircle, null) }))),
423
425
  React.createElement(Typography, { variant: "body2", color: "text.secondary", gutterBottom: true },
424
426
  provider.provider,
425
427
  " \u2022 ",
@@ -428,67 +430,67 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
428
430
  ` • ${provider.description}`),
429
431
  params &&
430
432
  (params.temperature !== undefined ||
431
- params.maxTokens !== undefined ||
433
+ params.maxOutputTokens !== undefined ||
432
434
  params.maxTurns !== undefined) && (React.createElement(Box, { sx: {
433
435
  display: 'flex',
434
436
  flexWrap: 'wrap',
435
437
  gap: 1,
436
438
  mt: 1
437
439
  } },
438
- params.temperature !== undefined && (React.createElement(Chip, { label: `Temp: ${params.temperature}`, size: "small", variant: "outlined" })),
439
- params.maxTokens !== undefined && (React.createElement(Chip, { label: `Tokens: ${params.maxTokens}`, size: "small", variant: "outlined" })),
440
- params.maxTurns !== undefined && (React.createElement(Chip, { label: `Turns: ${params.maxTurns}`, size: "small", variant: "outlined" }))))),
440
+ params.temperature !== undefined && (React.createElement(Chip, { label: trans.__('Temp: %1', params.temperature), size: "small", variant: "outlined" })),
441
+ params.maxOutputTokens !== undefined && (React.createElement(Chip, { label: trans.__('Tokens: %1', params.maxOutputTokens), size: "small", variant: "outlined" })),
442
+ params.maxTurns !== undefined && (React.createElement(Chip, { label: trans.__('Turns: %1', params.maxTurns), size: "small", variant: "outlined" }))))),
441
443
  React.createElement(IconButton, { onClick: e => handleMenuClick(e, provider.id), size: "small" },
442
444
  React.createElement(MoreVert, null)))));
443
445
  }))))),
444
446
  secretsManager !== undefined && (React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.useSecretsManager, onChange: e => handleConfigUpdate({
445
447
  useSecretsManager: e.target.checked
446
448
  }), color: "primary", sx: { alignSelf: 'flex-start' } }), label: React.createElement("div", null,
447
- React.createElement("span", null, "Use the secrets manager to manage API keys"),
448
- !config.useSecretsManager && (React.createElement(Alert, { severity: "warning", icon: React.createElement(Error, null), sx: { mb: 2 } }, "The secrets are stored in plain text in settings"))) })))),
449
+ React.createElement("span", null, trans.__('Use the secrets manager to manage API keys')),
450
+ !config.useSecretsManager && (React.createElement(Alert, { severity: "warning", icon: React.createElement(Error, null), sx: { mb: 2 } }, trans.__('The secrets are stored in plain text in settings')))) })))),
449
451
  activeTab === 1 && (React.createElement(Card, { elevation: 2 },
450
452
  React.createElement(CardContent, null,
451
- React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, "Behavior Settings"),
453
+ React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, trans.__('Behavior Settings')),
452
454
  React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
453
455
  React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.toolsEnabled, onChange: e => handleConfigUpdate({
454
456
  toolsEnabled: e.target.checked
455
457
  }), color: "primary" }), label: React.createElement(Box, null,
456
- React.createElement(Typography, { variant: "body1" }, "Enable Tools"),
457
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Allow the AI to use tools like notebook operations, code execution, and file management")) }),
458
+ React.createElement(Typography, { variant: "body1" }, trans.__('Enable Tools')),
459
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Allow the AI to use tools like notebook operations, code execution, and file management'))) }),
458
460
  React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.sendWithShiftEnter, onChange: e => handleConfigUpdate({
459
461
  sendWithShiftEnter: e.target.checked
460
462
  }), color: "primary" }), label: React.createElement(Box, null,
461
- React.createElement(Typography, { variant: "body1" }, "Send with Shift+Enter"),
462
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Use Shift+Enter to send messages (Enter creates new line)")) }),
463
+ React.createElement(Typography, { variant: "body1" }, trans.__('Send with Shift+Enter')),
464
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Use Shift+Enter to send messages (Enter creates new line)'))) }),
463
465
  React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showTokenUsage, onChange: e => handleConfigUpdate({
464
466
  showTokenUsage: e.target.checked
465
467
  }), color: "primary" }), label: React.createElement(Box, null,
466
- React.createElement(Typography, { variant: "body1" }, "Show Token Usage"),
467
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Display token usage information in the chat toolbar")) }),
468
+ React.createElement(Typography, { variant: "body1" }, trans.__('Show Token Usage')),
469
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Display token usage information in the chat toolbar'))) }),
468
470
  React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showCellDiff, onChange: e => handleConfigUpdate({
469
471
  showCellDiff: e.target.checked
470
472
  }), color: "primary" }), label: React.createElement(Box, null,
471
- React.createElement(Typography, { variant: "body1" }, "Show Cell Diff"),
472
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Show diff view when AI modifies cell content")) }),
473
+ React.createElement(Typography, { variant: "body1" }, trans.__('Show Cell Diff')),
474
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Show diff view when AI modifies cell content'))) }),
473
475
  config.showCellDiff && (React.createElement(FormControl, { sx: { ml: 4 } },
474
- React.createElement(InputLabel, null, "Diff Display Mode"),
475
- React.createElement(Select, { value: config.diffDisplayMode, label: "Diff Display Mode", onChange: e => handleConfigUpdate({
476
+ React.createElement(InputLabel, null, trans.__('Diff Display Mode')),
477
+ React.createElement(Select, { value: config.diffDisplayMode, label: trans.__('Diff Display Mode'), onChange: e => handleConfigUpdate({
476
478
  diffDisplayMode: e.target.value
477
479
  }) },
478
- React.createElement(MenuItem, { value: "split" }, "Split View"),
479
- React.createElement(MenuItem, { value: "unified" }, "Unified View")))),
480
+ React.createElement(MenuItem, { value: "split" }, trans.__('Split View')),
481
+ React.createElement(MenuItem, { value: "unified" }, trans.__('Unified View'))))),
480
482
  React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showFileDiff, onChange: e => handleConfigUpdate({
481
483
  showFileDiff: e.target.checked
482
484
  }), color: "primary" }), label: React.createElement(Box, null,
483
- React.createElement(Typography, { variant: "body1" }, "Show File Diff"),
484
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Show diff view when AI modifies file content")) }),
485
+ React.createElement(Typography, { variant: "body1" }, trans.__('Show File Diff')),
486
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Show diff view when AI modifies file content'))) }),
485
487
  React.createElement(Divider, { sx: { my: 1 } }),
486
- React.createElement(TextField, { fullWidth: true, multiline: true, rows: 3, label: "System Prompt", value: systemPromptValue, onChange: e => handleSystemPromptChange(e.target.value), placeholder: "Define the AI's behavior and personality...", helperText: "Instructions that define how the AI should behave and respond" }),
487
- React.createElement(TextField, { fullWidth: true, multiline: true, rows: 3, label: "Completion System Prompt", value: completionPromptValue, onChange: e => handleCompletionPromptChange(e.target.value), placeholder: "Define how the AI should generate code completions...", helperText: "Instructions that define how the AI should generate code completions" }),
488
+ React.createElement(TextField, { fullWidth: true, multiline: true, rows: 3, label: trans.__('System Prompt'), value: systemPromptValue, onChange: e => handleSystemPromptChange(e.target.value), placeholder: trans.__("Define the AI's behavior and personality..."), helperText: trans.__('Instructions that define how the AI should behave and respond') }),
489
+ React.createElement(TextField, { fullWidth: true, multiline: true, rows: 3, label: trans.__('Completion System Prompt'), value: completionPromptValue, onChange: e => handleCompletionPromptChange(e.target.value), placeholder: trans.__('Define how the AI should generate code completions...'), helperText: trans.__('Instructions that define how the AI should generate code completions') }),
488
490
  React.createElement(Divider, { sx: { my: 2 } }),
489
491
  React.createElement(Box, null,
490
- React.createElement(Typography, { variant: "body1", gutterBottom: true }, "Commands Requiring Approval"),
491
- React.createElement(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' } }, "Commands that require user approval before AI can execute them"),
492
+ React.createElement(Typography, { variant: "body1", gutterBottom: true }, trans.__('Commands Requiring Approval')),
493
+ React.createElement(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' } }, trans.__('Commands that require user approval before AI can execute them')),
492
494
  React.createElement(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' } }, config.commandsRequiringApproval.map((command, index) => (React.createElement(ListItem, { key: index, divider: true },
493
495
  React.createElement(ListItemText, { primary: command }),
494
496
  React.createElement(ListItemSecondaryAction, null,
@@ -502,7 +504,7 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
502
504
  });
503
505
  }, size: "small" },
504
506
  React.createElement(Delete, null))))))),
505
- React.createElement(TextField, { fullWidth: true, label: "Add New Command", placeholder: "e.g., notebook:run-cell", onKeyDown: e => {
507
+ React.createElement(TextField, { fullWidth: true, label: trans.__('Add New Command'), placeholder: trans.__('e.g., notebook:run-cell'), onKeyDown: e => {
506
508
  if (e.key === 'Enter') {
507
509
  const value = e.target.value.trim();
508
510
  if (value &&
@@ -517,7 +519,7 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
517
519
  e.target.value = '';
518
520
  }
519
521
  }
520
- }, helperText: "Press Enter to add a command. Common commands: notebook:run-cell, console:execute, fileeditor:run-code" })))))),
522
+ }, helperText: trans.__('Press Enter to add a command. Common commands: notebook:run-cell, console:execute, fileeditor:run-code') })))))),
521
523
  activeTab === 2 && (React.createElement(Card, { elevation: 2 },
522
524
  React.createElement(CardContent, null,
523
525
  React.createElement(Box, { sx: {
@@ -528,10 +530,10 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
528
530
  } },
529
531
  React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
530
532
  React.createElement(Cable, { color: "primary" }),
531
- React.createElement(Typography, { variant: "h6", component: "h2" }, "Remote MCP Servers")),
532
- React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddMCPDialog, size: "small" }, "Add Server")),
533
- 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."),
534
- 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 },
533
+ React.createElement(Typography, { variant: "h6", component: "h2" }, trans.__('Remote MCP Servers'))),
534
+ React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddMCPDialog, size: "small" }, trans.__('Add Server'))),
535
+ React.createElement(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 } }, trans.__("Configure remote Model Context Protocol (MCP) servers to extend the AI's capabilities with external tools and data sources.")),
536
+ config.mcpServers.length === 0 ? (React.createElement(Alert, { severity: "info" }, trans.__('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 },
535
537
  React.createElement(ListItemText, { primary: React.createElement(Box, { sx: {
536
538
  display: 'flex',
537
539
  alignItems: 'center',
@@ -546,16 +548,13 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
546
548
  enabled: e.target.checked
547
549
  }), size: "small", color: "primary" })), secondary: React.createElement(Box, null,
548
550
  React.createElement(Typography, { variant: "body2", color: "text.secondary" }, server.url),
549
- server.enabled && agentManagerFactory && (React.createElement(Typography, { variant: "caption", color: "text.secondary" },
550
- "Status:",
551
- ' ',
552
- agentManagerFactory.isMCPServerConnected(server.name)
553
- ? 'Connected'
554
- : 'Connection failed'))) }),
551
+ server.enabled && agentManagerFactory && (React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Status: %1', agentManagerFactory.isMCPServerConnected(server.name)
552
+ ? trans.__('Connected')
553
+ : trans.__('Connection failed'))))) }),
555
554
  React.createElement(ListItemSecondaryAction, null,
556
555
  React.createElement(IconButton, { onClick: e => handleMCPMenuClick(e, server.id), size: "small" },
557
556
  React.createElement(MoreVert, null))))))))))),
558
- React.createElement(ProviderConfigDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSave: editingProvider ? handleEditProvider : handleAddProvider, initialConfig: editingProvider, mode: editingProvider ? 'edit' : 'add', providerRegistry: providerRegistry, handleSecretField: handleSecretField }),
557
+ React.createElement(ProviderConfigDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSave: editingProvider ? handleEditProvider : handleAddProvider, initialConfig: editingProvider, mode: editingProvider ? 'edit' : 'add', providerRegistry: providerRegistry, handleSecretField: handleSecretField, trans: trans }),
559
558
  React.createElement(Menu, { anchorEl: menuAnchor, open: Boolean(menuAnchor), onClose: handleMenuClose },
560
559
  React.createElement(MenuItem, { onClick: () => {
561
560
  const provider = config.providers.find(p => p.id === menuProviderId);
@@ -564,11 +563,11 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
564
563
  }
565
564
  } },
566
565
  React.createElement(Edit, { sx: { mr: 1 } }),
567
- "Edit"),
566
+ trans.__('Edit')),
568
567
  React.createElement(MenuItem, { onClick: () => handleDeleteProvider(menuProviderId), sx: { color: 'error.main' } },
569
568
  React.createElement(Delete, { sx: { mr: 1 } }),
570
- "Delete")),
571
- React.createElement(MCPServerDialog, { open: mcpDialogOpen, onClose: () => setMcpDialogOpen(false), onSave: editingMCPServer ? handleEditMCPServer : handleAddMCPServer, initialConfig: editingMCPServer, mode: editingMCPServer ? 'edit' : 'add' }),
569
+ trans.__('Delete'))),
570
+ React.createElement(MCPServerDialog, { open: mcpDialogOpen, onClose: () => setMcpDialogOpen(false), onSave: editingMCPServer ? handleEditMCPServer : handleAddMCPServer, initialConfig: editingMCPServer, mode: editingMCPServer ? 'edit' : 'add', trans: trans }),
572
571
  React.createElement(Menu, { anchorEl: mcpMenuAnchor, open: Boolean(mcpMenuAnchor), onClose: handleMCPMenuClose },
573
572
  React.createElement(MenuItem, { onClick: () => {
574
573
  const server = config.mcpServers.find(s => s.id === mcpMenuServerId);
@@ -577,17 +576,17 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
577
576
  }
578
577
  } },
579
578
  React.createElement(Edit, { sx: { mr: 1 } }),
580
- "Edit"),
579
+ trans.__('Edit')),
581
580
  React.createElement(MenuItem, { onClick: () => handleDeleteMCPServer(mcpMenuServerId), sx: { color: 'error.main' } },
582
581
  React.createElement(Delete, { sx: { mr: 1 } }),
583
- "Delete")))));
582
+ trans.__('Delete'))))));
584
583
  };
585
584
  /**
586
585
  * Dialog component for adding/editing MCP server configurations
587
586
  * @param props - Component props for the MCP server dialog
588
587
  * @returns A React component for MCP server configuration
589
588
  */
590
- const MCPServerDialog = ({ open, onClose, onSave, initialConfig, mode }) => {
589
+ const MCPServerDialog = ({ open, onClose, onSave, initialConfig, mode, trans }) => {
591
590
  const [name, setName] = useState(initialConfig?.name || '');
592
591
  const [url, setUrl] = useState(initialConfig?.url || '');
593
592
  const [enabled, setEnabled] = useState(initialConfig?.enabled ?? true);
@@ -631,15 +630,17 @@ const MCPServerDialog = ({ open, onClose, onSave, initialConfig, mode }) => {
631
630
  };
632
631
  const canSave = name.trim() && url.trim() && _isValidUrl(url.trim());
633
632
  return (React.createElement(Dialog, { open: open, onClose: onClose, maxWidth: "sm", fullWidth: true },
634
- React.createElement(DialogTitle, null, mode === 'add' ? 'Add MCP Server' : 'Edit MCP Server'),
633
+ React.createElement(DialogTitle, null, mode === 'add'
634
+ ? trans.__('Add MCP Server')
635
+ : trans.__('Edit MCP Server')),
635
636
  React.createElement(DialogContent, null,
636
637
  React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2, pt: 1 } },
637
- 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" }),
638
- 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())) }),
639
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: enabled, onChange: e => setEnabled(e.target.checked), color: "primary" }), label: "Enable this server" }))),
638
+ React.createElement(TextField, { autoFocus: true, fullWidth: true, label: trans.__('Server Name'), value: name, onChange: e => setName(e.target.value), placeholder: trans.__('My MCP Server'), helperText: trans.__('A friendly name to identify this MCP server') }),
639
+ React.createElement(TextField, { fullWidth: true, label: trans.__('Server URL'), value: url, onChange: e => setUrl(e.target.value), placeholder: trans.__('https://example.com/mcp'), helperText: trans.__('The HTTP/HTTPS URL of the MCP server'), error: Boolean(url.trim() && !_isValidUrl(url.trim())) }),
640
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: enabled, onChange: e => setEnabled(e.target.checked), color: "primary" }), label: trans.__('Enable this server') }))),
640
641
  React.createElement(DialogActions, null,
641
- React.createElement(Button, { onClick: onClose }, "Cancel"),
642
- React.createElement(Button, { onClick: handleSave, variant: "contained", disabled: !canSave }, mode === 'add' ? 'Add' : 'Save'))));
642
+ React.createElement(Button, { onClick: onClose }, trans.__('Cancel')),
643
+ React.createElement(Button, { onClick: handleSave, variant: "contained", disabled: !canSave }, mode === 'add' ? trans.__('Add') : trans.__('Save')))));
643
644
  };
644
645
  var Private;
645
646
  (function (Private) {
@@ -1,5 +1,6 @@
1
1
  import { ChatWidget } from '@jupyter/chat';
2
2
  import { MainAreaWidget } from '@jupyterlab/apputils';
3
+ import type { TranslationBundle } from '@jupyterlab/translation';
3
4
  import { CommandRegistry } from '@lumino/commands';
4
5
  import { AIChatModel } from '../chat-model';
5
6
  import { AISettingsModel } from '../models/settings-model';
@@ -7,6 +8,7 @@ export declare namespace MainAreaChat {
7
8
  interface IOptions extends MainAreaWidget.IOptions<ChatWidget> {
8
9
  commands: CommandRegistry;
9
10
  settingsModel: AISettingsModel;
11
+ trans: TranslationBundle;
10
12
  }
11
13
  }
12
14
  /**
@@ -10,6 +10,7 @@ export class MainAreaChat extends MainAreaWidget {
10
10
  constructor(options) {
11
11
  super(options);
12
12
  this.title.label = this.content.model.name;
13
+ const { trans } = options;
13
14
  // add the move to side button.
14
15
  this.toolbar.addItem('moveToSide', new CommandToolbarButton({
15
16
  commands: options.commands,
@@ -24,12 +25,14 @@ export class MainAreaChat extends MainAreaWidget {
24
25
  const tokenUsageWidget = new TokenUsageWidget({
25
26
  tokenUsageChanged: this.model.tokenUsageChanged,
26
27
  settingsModel: options.settingsModel,
27
- initialTokenUsage: this.model.agentManager.tokenUsage
28
+ initialTokenUsage: this.model.agentManager.tokenUsage,
29
+ translator: trans
28
30
  });
29
31
  this.toolbar.addItem('token-usage', tokenUsageWidget);
30
32
  // Add the approval button, tied to the chat widget.
31
33
  this._approvalButtons = new ApprovalButtons({
32
- chatPanel: this.content
34
+ chatPanel: this.content,
35
+ agentManager: this.model.agentManager
33
36
  });
34
37
  }
35
38
  dispose() {
@@ -1,3 +1,4 @@
1
+ import type { TranslationBundle } from '@jupyterlab/translation';
1
2
  import React from 'react';
2
3
  import { IProviderConfig } from '../models/settings-model';
3
4
  import type { IProviderRegistry } from '../tokens';
@@ -9,6 +10,7 @@ interface IProviderConfigDialogProps {
9
10
  mode: 'add' | 'edit';
10
11
  providerRegistry: IProviderRegistry;
11
12
  handleSecretField: (input: HTMLInputElement, provider: string, fieldName: string) => Promise<void>;
13
+ trans: TranslationBundle;
12
14
  }
13
15
  export declare const ProviderConfigDialog: React.FC<IProviderConfigDialogProps>;
14
16
  export {};