@jupyterlite/ai 0.9.1 → 0.10.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 (66) hide show
  1. package/README.md +5 -214
  2. package/lib/agent.d.ts +58 -66
  3. package/lib/agent.js +274 -300
  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 +19 -54
  9. package/lib/chat-model.js +243 -303
  10. package/lib/components/clear-button.d.ts +6 -1
  11. package/lib/components/clear-button.js +8 -3
  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 +9 -8
  16. package/lib/components/stop-button.d.ts +6 -1
  17. package/lib/components/stop-button.js +8 -3
  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 +6 -5
  22. package/lib/index.js +58 -38
  23. package/lib/models/settings-model.d.ts +1 -1
  24. package/lib/providers/built-in-providers.js +38 -19
  25. package/lib/providers/models.d.ts +3 -3
  26. package/lib/providers/provider-registry.d.ts +3 -4
  27. package/lib/providers/provider-registry.js +1 -4
  28. package/lib/tokens.d.ts +5 -6
  29. package/lib/tools/commands.d.ts +2 -1
  30. package/lib/tools/commands.js +37 -46
  31. package/lib/tools/file.js +49 -73
  32. package/lib/tools/notebook.js +370 -445
  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 +12 -12
  40. package/src/agent.ts +342 -361
  41. package/src/approval-buttons.ts +43 -389
  42. package/src/chat-model-registry.ts +9 -1
  43. package/src/chat-model.ts +355 -370
  44. package/src/completion/completion-provider.ts +2 -3
  45. package/src/components/clear-button.tsx +16 -3
  46. package/src/components/completion-status.tsx +18 -4
  47. package/src/components/model-select.tsx +21 -8
  48. package/src/components/stop-button.tsx +16 -3
  49. package/src/components/token-usage-display.tsx +14 -2
  50. package/src/components/tool-select.tsx +23 -5
  51. package/src/index.ts +75 -36
  52. package/src/models/settings-model.ts +1 -1
  53. package/src/providers/built-in-providers.ts +38 -19
  54. package/src/providers/models.ts +3 -3
  55. package/src/providers/provider-registry.ts +4 -8
  56. package/src/tokens.ts +5 -6
  57. package/src/tools/commands.ts +39 -50
  58. package/src/tools/file.ts +49 -75
  59. package/src/tools/notebook.ts +451 -510
  60. package/src/widgets/ai-settings.tsx +153 -84
  61. package/src/widgets/main-area-chat.ts +8 -2
  62. package/src/widgets/provider-config-dialog.tsx +54 -41
  63. package/style/base.css +13 -73
  64. package/lib/mcp/browser.d.ts +0 -68
  65. package/lib/mcp/browser.js +0 -138
  66. package/src/mcp/browser.ts +0 -220
@@ -1,4 +1,5 @@
1
1
  import { InputToolbarRegistry } from '@jupyter/chat';
2
+ import type { TranslationBundle } from '@jupyterlab/translation';
2
3
  /**
3
4
  * Properties of the clear button.
4
5
  */
@@ -7,6 +8,10 @@ export interface IClearButtonProps extends InputToolbarRegistry.IToolbarItemProp
7
8
  * The function to clear messages.
8
9
  */
9
10
  clearMessages: () => void;
11
+ /**
12
+ * The application language translator.
13
+ */
14
+ translator: TranslationBundle;
10
15
  }
11
16
  /**
12
17
  * The clear button component.
@@ -15,4 +20,4 @@ export declare function ClearButton(props: IClearButtonProps): JSX.Element;
15
20
  /**
16
21
  * Factory returning the clear button toolbar item.
17
22
  */
18
- export declare function clearItem(): InputToolbarRegistry.IToolbarItem;
23
+ export declare function clearItem(translator: TranslationBundle): InputToolbarRegistry.IToolbarItem;
@@ -5,7 +5,8 @@ import React from 'react';
5
5
  * The clear button component.
6
6
  */
7
7
  export function ClearButton(props) {
8
- const tooltip = 'Clear chat';
8
+ const { translator: trans } = props;
9
+ const tooltip = trans.__('Clear chat');
9
10
  return (React.createElement(TooltippedButton, { onClick: props.clearMessages, tooltip: tooltip, buttonProps: {
10
11
  size: 'small',
11
12
  variant: 'outlined',
@@ -17,12 +18,16 @@ export function ClearButton(props) {
17
18
  /**
18
19
  * Factory returning the clear button toolbar item.
19
20
  */
20
- export function clearItem() {
21
+ export function clearItem(translator) {
21
22
  return {
22
23
  element: (props) => {
23
24
  const { model } = props;
24
25
  const clearMessages = () => model.chatContext.clearMessages();
25
- const clearProps = { ...props, clearMessages };
26
+ const clearProps = {
27
+ ...props,
28
+ clearMessages,
29
+ translator
30
+ };
26
31
  return ClearButton(clearProps);
27
32
  },
28
33
  position: 0,
@@ -1,5 +1,6 @@
1
1
  import { AISettingsModel } from '../models/settings-model';
2
2
  import { ReactWidget } from '@jupyterlab/ui-components';
3
+ import type { TranslationBundle } from '@jupyterlab/translation';
3
4
  /**
4
5
  * The completion status props.
5
6
  */
@@ -8,6 +9,10 @@ interface ICompletionStatusProps {
8
9
  * The settings model.
9
10
  */
10
11
  settingsModel: AISettingsModel;
12
+ /**
13
+ * The application language translator.
14
+ */
15
+ translator: TranslationBundle;
11
16
  }
12
17
  /**
13
18
  * The completion status widget that will be added to the status bar.
@@ -7,6 +7,7 @@ const COMPLETION_DISABLED_CLASS = 'jp-ai-completion-disabled';
7
7
  * The completion status component.
8
8
  */
9
9
  function CompletionStatus(props) {
10
+ const { translator: trans } = props;
10
11
  const [disabled, setDisabled] = useState(true);
11
12
  const [title, setTitle] = useState('');
12
13
  /**
@@ -16,15 +17,15 @@ function CompletionStatus(props) {
16
17
  const stateChanged = (model) => {
17
18
  if (model.config.useSameProviderForChatAndCompleter) {
18
19
  setDisabled(false);
19
- setTitle(`Completion using ${model.getDefaultProvider()?.model}`);
20
+ setTitle(trans.__('Completion using %1', model.getDefaultProvider()?.model ?? ''));
20
21
  }
21
22
  else if (model.config.activeCompleterProvider) {
22
23
  setDisabled(false);
23
- setTitle(`Completion using ${model.getProvider(model.config.activeCompleterProvider)?.model}`);
24
+ setTitle(trans.__('Completion using %1', model.getProvider(model.config.activeCompleterProvider)?.model ?? ''));
24
25
  }
25
26
  else {
26
27
  setDisabled(true);
27
- setTitle('No completion');
28
+ setTitle(trans.__('No completion'));
28
29
  }
29
30
  };
30
31
  props.settingsModel.stateChanged.connect(stateChanged);
@@ -32,7 +33,7 @@ function CompletionStatus(props) {
32
33
  return () => {
33
34
  props.settingsModel.stateChanged.disconnect(stateChanged);
34
35
  };
35
- }, [props.settingsModel]);
36
+ }, [props.settingsModel, trans]);
36
37
  return (React.createElement(jupyternautIcon.react, { className: disabled ? COMPLETION_DISABLED_CLASS : '', top: '2px', width: '16px', stylesheet: 'statusBar', title: title }));
37
38
  }
38
39
  /**
@@ -1,4 +1,5 @@
1
1
  import { InputToolbarRegistry } from '@jupyter/chat';
2
+ import type { TranslationBundle } from '@jupyterlab/translation';
2
3
  import { AISettingsModel } from '../models/settings-model';
3
4
  /**
4
5
  * Properties for the model select component.
@@ -8,6 +9,10 @@ export interface IModelSelectProps extends InputToolbarRegistry.IToolbarItemProp
8
9
  * The settings model to get available models and current selection from.
9
10
  */
10
11
  settingsModel: AISettingsModel;
12
+ /**
13
+ * The application language translator.
14
+ */
15
+ translator: TranslationBundle;
11
16
  }
12
17
  /**
13
18
  * The model select component for choosing AI models.
@@ -16,4 +21,4 @@ export declare function ModelSelect(props: IModelSelectProps): JSX.Element;
16
21
  /**
17
22
  * Factory function returning the toolbar item for model selection.
18
23
  */
19
- export declare function createModelSelectItem(settingsModel: AISettingsModel): InputToolbarRegistry.IToolbarItem;
24
+ export declare function createModelSelectItem(settingsModel: AISettingsModel, translator: TranslationBundle): InputToolbarRegistry.IToolbarItem;
@@ -6,7 +6,7 @@ import React, { useCallback, useEffect, useState } from 'react';
6
6
  * The model select component for choosing AI models.
7
7
  */
8
8
  export function ModelSelect(props) {
9
- const { settingsModel, model } = props;
9
+ const { settingsModel, model, translator: trans } = props;
10
10
  const agentManager = model.chatContext
11
11
  .agentManager;
12
12
  const [currentProvider, setCurrentProvider] = useState(agentManager.activeProvider ?? '');
@@ -60,28 +60,28 @@ export function ModelSelect(props) {
60
60
  }));
61
61
  // Show a message if no providers are configured
62
62
  if (availableModels.length === 0) {
63
- return (React.createElement(TooltippedButton, { onClick: () => { }, tooltip: "No providers configured. Please go to AI Settings to add a provider.", buttonProps: {
63
+ return (React.createElement(TooltippedButton, { onClick: () => { }, tooltip: trans.__('No providers configured. Please go to AI Settings to add a provider.'), buttonProps: {
64
64
  size: 'small',
65
65
  variant: 'outlined',
66
66
  color: 'warning',
67
67
  disabled: true,
68
- title: 'No Providers Available'
68
+ title: trans.__('No Providers Available')
69
69
  }, sx: {
70
70
  minWidth: 'auto',
71
71
  display: 'flex',
72
72
  alignItems: 'center',
73
73
  height: '29px'
74
74
  } },
75
- React.createElement(Typography, { variant: "caption", sx: { fontSize: '0.7rem', fontWeight: 500 } }, "No Providers")));
75
+ React.createElement(Typography, { variant: "caption", sx: { fontSize: '0.7rem', fontWeight: 500 } }, trans.__('No Providers'))));
76
76
  }
77
77
  return (React.createElement(React.Fragment, null,
78
78
  React.createElement(TooltippedButton, { onClick: e => {
79
79
  openMenu(e.currentTarget);
80
- }, tooltip: `Current Model: ${currentProviderLabel} - ${currentModel}`, buttonProps: {
80
+ }, tooltip: trans.__('Current Model: %1 - %2', currentProviderLabel, currentModel), buttonProps: {
81
81
  size: 'small',
82
82
  variant: 'contained',
83
83
  color: 'primary',
84
- title: 'Select AI Model',
84
+ title: trans.__('Select AI Model'),
85
85
  onKeyDown: e => {
86
86
  if (e.key !== 'Enter' && e.key !== ' ') {
87
87
  return;
@@ -144,7 +144,7 @@ export function ModelSelect(props) {
144
144
  /**
145
145
  * Factory function returning the toolbar item for model selection.
146
146
  */
147
- export function createModelSelectItem(settingsModel) {
147
+ export function createModelSelectItem(settingsModel, translator) {
148
148
  return {
149
149
  element: (props) => {
150
150
  const chatContext = props.model.chatContext;
@@ -153,7 +153,8 @@ export function createModelSelectItem(settingsModel) {
153
153
  }
154
154
  const modelSelectProps = {
155
155
  ...props,
156
- settingsModel
156
+ settingsModel,
157
+ translator
157
158
  };
158
159
  return React.createElement(ModelSelect, { ...modelSelectProps });
159
160
  },
@@ -1,4 +1,5 @@
1
1
  import { InputToolbarRegistry } from '@jupyter/chat';
2
+ import type { TranslationBundle } from '@jupyterlab/translation';
2
3
  /**
3
4
  * Properties of the stop button.
4
5
  */
@@ -7,6 +8,10 @@ export interface IStopButtonProps extends InputToolbarRegistry.IToolbarItemProps
7
8
  * The function to stop streaming.
8
9
  */
9
10
  stopStreaming: () => void;
11
+ /**
12
+ * The application language translator.
13
+ */
14
+ translator: TranslationBundle;
10
15
  }
11
16
  /**
12
17
  * The stop button component.
@@ -15,4 +20,4 @@ export declare function StopButton(props: IStopButtonProps): JSX.Element;
15
20
  /**
16
21
  * Factory returning the stop button toolbar item.
17
22
  */
18
- export declare function stopItem(): InputToolbarRegistry.IToolbarItem;
23
+ export declare function stopItem(translator: TranslationBundle): InputToolbarRegistry.IToolbarItem;
@@ -5,7 +5,8 @@ import React from 'react';
5
5
  * The stop button component.
6
6
  */
7
7
  export function StopButton(props) {
8
- const tooltip = 'Stop streaming';
8
+ const { translator: trans } = props;
9
+ const tooltip = trans.__('Stop streaming');
9
10
  return (React.createElement(TooltippedButton, { onClick: props.stopStreaming, tooltip: tooltip, buttonProps: {
10
11
  size: 'small',
11
12
  variant: 'contained',
@@ -17,12 +18,16 @@ export function StopButton(props) {
17
18
  /**
18
19
  * Factory returning the stop button toolbar item.
19
20
  */
20
- export function stopItem() {
21
+ export function stopItem(translator) {
21
22
  return {
22
23
  element: (props) => {
23
24
  const { model } = props;
24
25
  const stopStreaming = () => model.chatContext.stopStreaming();
25
- const stopProps = { ...props, stopStreaming };
26
+ const stopProps = {
27
+ ...props,
28
+ stopStreaming,
29
+ translator
30
+ };
26
31
  return StopButton(stopProps);
27
32
  },
28
33
  position: 50,
@@ -1,4 +1,5 @@
1
1
  import { ReactWidget } from '@jupyterlab/ui-components';
2
+ import type { TranslationBundle } from '@jupyterlab/translation';
2
3
  import React from 'react';
3
4
  import { ISignal } from '@lumino/signaling';
4
5
  import { AISettingsModel } from '../models/settings-model';
@@ -19,6 +20,10 @@ export interface ITokenUsageDisplayProps {
19
20
  * Initial token usage.
20
21
  */
21
22
  initialTokenUsage?: ITokenUsage;
23
+ /**
24
+ * The application language translator.
25
+ */
26
+ translator: TranslationBundle;
22
27
  }
23
28
  /**
24
29
  * React component that displays token usage information.
@@ -5,7 +5,7 @@ import React from 'react';
5
5
  * Shows input/output token counts with up/down arrows.
6
6
  * Only renders when token usage display is enabled in settings.
7
7
  */
8
- export const TokenUsageDisplay = ({ tokenUsageChanged, settingsModel, initialTokenUsage }) => {
8
+ export const TokenUsageDisplay = ({ tokenUsageChanged, settingsModel, initialTokenUsage, translator: trans }) => {
9
9
  return (React.createElement(UseSignal, { signal: settingsModel.stateChanged, initialArgs: undefined }, () => {
10
10
  const config = settingsModel.config;
11
11
  if (!config.showTokenUsage) {
@@ -30,7 +30,7 @@ export const TokenUsageDisplay = ({ tokenUsageChanged, settingsModel, initialTok
30
30
  border: '1px solid var(--jp-border-color1)',
31
31
  borderRadius: '4px',
32
32
  whiteSpace: 'nowrap'
33
- }, title: `Token Usage - Sent: ${tokenUsage.inputTokens.toLocaleString()}, Received: ${tokenUsage.outputTokens.toLocaleString()}, Total: ${total.toLocaleString()}` },
33
+ }, title: trans.__('Token Usage - Sent: %1, Received: %2, Total: %3', tokenUsage.inputTokens.toLocaleString(), tokenUsage.outputTokens.toLocaleString(), total.toLocaleString()) },
34
34
  React.createElement("span", { style: {
35
35
  display: 'flex',
36
36
  alignItems: 'center',
@@ -1,4 +1,5 @@
1
1
  import { InputToolbarRegistry } from '@jupyter/chat';
2
+ import type { TranslationBundle } from '@jupyterlab/translation';
2
3
  import { IToolRegistry } from '../tokens';
3
4
  /**
4
5
  * Properties for the tool select component.
@@ -16,6 +17,10 @@ export interface IToolSelectProps extends InputToolbarRegistry.IToolbarItemProps
16
17
  * Function to handle tool selection changes.
17
18
  */
18
19
  onToolSelectionChange: (selectedToolNames: string[]) => void;
20
+ /**
21
+ * The application language translator.
22
+ */
23
+ translator: TranslationBundle;
19
24
  }
20
25
  /**
21
26
  * The tool select component for choosing AI tools.
@@ -24,4 +29,4 @@ export declare function ToolSelect(props: IToolSelectProps): JSX.Element;
24
29
  /**
25
30
  * Factory function returning the toolbar item for tool selection.
26
31
  */
27
- export declare function createToolSelectItem(toolRegistry: IToolRegistry, toolsEnabled?: boolean): InputToolbarRegistry.IToolbarItem;
32
+ export declare function createToolSelectItem(toolRegistry: IToolRegistry, toolsEnabled: boolean | undefined, translator: TranslationBundle): InputToolbarRegistry.IToolbarItem;
@@ -8,7 +8,7 @@ const SELECT_ITEM_CLASS = 'jp-AIToolSelect-item';
8
8
  * The tool select component for choosing AI tools.
9
9
  */
10
10
  export function ToolSelect(props) {
11
- const { toolRegistry, onToolSelectionChange, toolsEnabled } = props;
11
+ const { toolRegistry, onToolSelectionChange, toolsEnabled, translator: trans } = props;
12
12
  const [selectedToolNames, setSelectedToolNames] = useState([]);
13
13
  const [tools, setTools] = useState(toolRegistry?.namedTools || []);
14
14
  const [menuAnchorEl, setMenuAnchorEl] = useState(null);
@@ -63,11 +63,11 @@ export function ToolSelect(props) {
63
63
  return (React.createElement(React.Fragment, null,
64
64
  React.createElement(TooltippedButton, { onClick: e => {
65
65
  openMenu(e.currentTarget);
66
- }, tooltip: `Tools (${selectedToolNames.length}/${tools.length} selected)`, buttonProps: {
66
+ }, tooltip: trans.__('Tools (%1/%2 selected)', selectedToolNames.length.toString(), tools.length.toString()), buttonProps: {
67
67
  size: 'small',
68
68
  variant: selectedToolNames.length > 0 ? 'contained' : 'outlined',
69
69
  color: 'primary',
70
- title: 'Select AI Tools',
70
+ title: trans.__('Select AI Tools'),
71
71
  onKeyDown: e => {
72
72
  if (e.key !== 'Enter' && e.key !== ' ') {
73
73
  return;
@@ -106,7 +106,7 @@ export function ToolSelect(props) {
106
106
  /**
107
107
  * Factory function returning the toolbar item for tool selection.
108
108
  */
109
- export function createToolSelectItem(toolRegistry, toolsEnabled = true) {
109
+ export function createToolSelectItem(toolRegistry, toolsEnabled = true, translator) {
110
110
  return {
111
111
  element: (props) => {
112
112
  const onToolSelectionChange = (tools) => {
@@ -121,7 +121,8 @@ export function createToolSelectItem(toolRegistry, toolsEnabled = true) {
121
121
  ...props,
122
122
  toolRegistry,
123
123
  onToolSelectionChange,
124
- toolsEnabled
124
+ toolsEnabled,
125
+ translator
125
126
  };
126
127
  return React.createElement(ToolSelect, { ...toolSelectProps });
127
128
  },
package/lib/index.js CHANGED
@@ -9,6 +9,7 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
9
9
  import { IKernelSpecManager } from '@jupyterlab/services';
10
10
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
11
11
  import { IStatusBar } from '@jupyterlab/statusbar';
12
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
12
13
  import { settingsIcon, Toolbar, ToolbarButton } from '@jupyterlab/ui-components';
13
14
  import { ISecretsManager, SecretsManager } from 'jupyter-secrets-manager';
14
15
  import { PromiseDelegate, UUID } from '@lumino/coreutils';
@@ -108,9 +109,10 @@ const chatModelRegistry = {
108
109
  description: 'Registry for the current chat model',
109
110
  autoStart: true,
110
111
  requires: [IAISettingsModel, IAgentManagerFactory, IDocumentManager],
111
- optional: [IProviderRegistry, INotebookTracker, IToolRegistry],
112
+ optional: [IProviderRegistry, INotebookTracker, IToolRegistry, ITranslator],
112
113
  provides: IChatModelRegistry,
113
- activate: (app, settingsModel, agentManagerFactory, docManager, providerRegistry, notebookTracker, toolRegistry) => {
114
+ activate: (app, settingsModel, agentManagerFactory, docManager, providerRegistry, notebookTracker, toolRegistry, translator) => {
115
+ const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
114
116
  // Create ActiveCellManager if notebook tracker is available
115
117
  let activeCellManager;
116
118
  if (notebookTracker) {
@@ -125,7 +127,8 @@ const chatModelRegistry = {
125
127
  agentManagerFactory,
126
128
  docManager,
127
129
  providerRegistry,
128
- toolRegistry
130
+ toolRegistry,
131
+ trans
129
132
  });
130
133
  }
131
134
  };
@@ -142,8 +145,9 @@ const plugin = {
142
145
  IChatModelRegistry,
143
146
  IAISettingsModel
144
147
  ],
145
- optional: [IThemeManager, ILayoutRestorer, ILabShell],
146
- activate: (app, rmRegistry, inputToolbarFactory, modelRegistry, settingsModel, themeManager, restorer, labShell) => {
148
+ optional: [IThemeManager, ILayoutRestorer, ILabShell, ITranslator],
149
+ activate: (app, rmRegistry, inputToolbarFactory, modelRegistry, settingsModel, themeManager, restorer, labShell, translator) => {
150
+ const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
147
151
  // Create attachment opener registry to handle file attachments
148
152
  const attachmentOpenerRegistry = new AttachmentOpenerRegistry();
149
153
  attachmentOpenerRegistry.set('file', attachment => {
@@ -178,14 +182,14 @@ const plugin = {
178
182
  });
179
183
  chatPanel.id = '@jupyterlite/ai:chat-panel';
180
184
  chatPanel.title.icon = chatIcon;
181
- chatPanel.title.caption = 'Chat with AI assistant'; // TODO: i18n/
185
+ chatPanel.title.caption = trans.__('Chat with AI assistant');
182
186
  chatPanel.toolbar.addItem('spacer', Toolbar.createSpacerItem());
183
187
  chatPanel.toolbar.addItem('settings', new ToolbarButton({
184
188
  icon: settingsIcon,
185
189
  onClick: () => {
186
190
  app.commands.execute('@jupyterlite/ai:open-settings');
187
191
  },
188
- tooltip: 'Open AI Settings'
192
+ tooltip: trans.__('Open AI Settings')
189
193
  }));
190
194
  chatPanel.sectionAdded.connect((_, section) => {
191
195
  const { widget } = section;
@@ -199,7 +203,8 @@ const plugin = {
199
203
  const tokenUsageWidget = new TokenUsageWidget({
200
204
  tokenUsageChanged: model.tokenUsageChanged,
201
205
  settingsModel,
202
- initialTokenUsage: model.agentManager.tokenUsage
206
+ initialTokenUsage: model.agentManager.tokenUsage,
207
+ translator: trans
203
208
  });
204
209
  section.toolbar.insertBefore('markRead', 'token-usage', tokenUsageWidget);
205
210
  model.writersChanged?.connect((_, writers) => {
@@ -216,7 +221,8 @@ const plugin = {
216
221
  });
217
222
  // Associate an approval buttons object to the chat.
218
223
  const approvalButton = new ApprovalButtons({
219
- chatPanel: widget
224
+ chatPanel: widget,
225
+ agentManager: model.agentManager
220
226
  });
221
227
  widget.disposed.connect(() => {
222
228
  // Dispose of the approval buttons widget when the chat is disposed.
@@ -251,14 +257,14 @@ const plugin = {
251
257
  app.commands.execute(CommandIds.openChat);
252
258
  }
253
259
  });
254
- registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry, inputToolbarFactory, settingsModel, tracker, modelRegistry, themeManager, labShell);
260
+ registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry, inputToolbarFactory, settingsModel, tracker, modelRegistry, trans, themeManager, labShell);
255
261
  }
256
262
  };
257
- function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry, inputToolbarFactory, settingsModel, tracker, modelRegistry, themeManager, labShell) {
263
+ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry, inputToolbarFactory, settingsModel, tracker, modelRegistry, trans, themeManager, labShell) {
258
264
  const { commands } = app;
259
265
  if (labShell) {
260
266
  commands.addCommand(CommandIds.reposition, {
261
- label: 'Reposition Widget',
267
+ label: trans.__('Reposition Widget'),
262
268
  execute: (args) => {
263
269
  const { widgetId, area, mode } = args;
264
270
  const widget = widgetId
@@ -284,16 +290,16 @@ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry,
284
290
  properties: {
285
291
  widgetId: {
286
292
  type: 'string',
287
- description: 'The widget ID to reposition in the application shell'
293
+ description: trans.__('The widget ID to reposition in the application shell')
288
294
  },
289
295
  area: {
290
296
  type: 'string',
291
- description: 'The name of the area to reposition the widget to'
297
+ description: trans.__('The name of the area to reposition the widget to')
292
298
  },
293
299
  mode: {
294
300
  type: 'string',
295
301
  enum: ['split-left', 'split-right', 'split-top', 'split-bottom'],
296
- description: 'The mode to use when repositioning the widget'
302
+ description: trans.__('The mode to use when repositioning the widget')
297
303
  }
298
304
  }
299
305
  }
@@ -307,7 +313,12 @@ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry,
307
313
  inputToolbarRegistry: inputToolbarFactory.create(),
308
314
  attachmentOpenerRegistry
309
315
  });
310
- const widget = new MainAreaChat({ content, commands, settingsModel });
316
+ const widget = new MainAreaChat({
317
+ content,
318
+ commands,
319
+ settingsModel,
320
+ trans
321
+ });
311
322
  app.shell.add(widget, 'main');
312
323
  // Add the widget to the tracker.
313
324
  tracker.add(widget);
@@ -321,7 +332,7 @@ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry,
321
332
  });
322
333
  };
323
334
  commands.addCommand(CommandIds.openChat, {
324
- label: 'Open a chat',
335
+ label: trans.__('Open a chat'),
325
336
  execute: async (args) => {
326
337
  const area = args.area === 'main' ? 'main' : 'side';
327
338
  const provider = args.provider ?? undefined;
@@ -348,22 +359,22 @@ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry,
348
359
  area: {
349
360
  type: 'string',
350
361
  enum: ['main', 'side'],
351
- description: 'The name of the area to open the chat to'
362
+ description: trans.__('The name of the area to open the chat to')
352
363
  },
353
364
  name: {
354
365
  type: 'string',
355
- description: 'The name of the chat'
366
+ description: trans.__('The name of the chat')
356
367
  },
357
368
  provider: {
358
369
  type: 'string',
359
- description: 'The provider/model to use with this chat'
370
+ description: trans.__('The provider/model to use with this chat')
360
371
  }
361
372
  }
362
373
  }
363
374
  }
364
375
  });
365
376
  commands.addCommand(CommandIds.moveChat, {
366
- caption: 'Move chat between area',
377
+ caption: trans.__('Move chat between area'),
367
378
  execute: async (args) => {
368
379
  const area = args.area;
369
380
  if (!['side', 'main'].includes(area)) {
@@ -399,7 +410,7 @@ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry,
399
410
  const status = await Promise.any([
400
411
  trackerUpdated.promise,
401
412
  new Promise(r => setTimeout(() => {
402
- return false;
413
+ r(false);
403
414
  }, 2000))
404
415
  ]);
405
416
  tracker.widgetUpdated.disconnect(widgetUpdated);
@@ -427,11 +438,11 @@ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry,
427
438
  area: {
428
439
  type: 'string',
429
440
  enum: ['main', 'side'],
430
- description: 'The name of the area to move the chat to'
441
+ description: trans.__('The name of the area to move the chat to')
431
442
  },
432
443
  name: {
433
444
  type: 'string',
434
- description: 'The name of the chat to move'
445
+ description: trans.__('The name of the chat to move')
435
446
  }
436
447
  },
437
448
  requires: ['area', 'name']
@@ -454,9 +465,11 @@ const agentManagerFactory = SecretsManager.sign(SECRETS_NAMESPACE, token => ({
454
465
  ICompletionProviderManager,
455
466
  ILayoutRestorer,
456
467
  ISecretsManager,
457
- IThemeManager
468
+ IThemeManager,
469
+ ITranslator
458
470
  ],
459
- activate: (app, settingsModel, providerRegistry, palette, completionManager, restorer, secretsManager, themeManager) => {
471
+ activate: (app, settingsModel, providerRegistry, palette, completionManager, restorer, secretsManager, themeManager, translator) => {
472
+ const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
460
473
  const agentManagerFactory = new AgentManagerFactory({
461
474
  settingsModel,
462
475
  secretsManager,
@@ -469,7 +482,8 @@ const agentManagerFactory = SecretsManager.sign(SECRETS_NAMESPACE, token => ({
469
482
  themeManager,
470
483
  providerRegistry,
471
484
  secretsManager,
472
- token
485
+ token,
486
+ trans
473
487
  });
474
488
  settingsWidget.id = 'jupyterlite-ai-settings';
475
489
  settingsWidget.title.icon = settingsIcon;
@@ -491,8 +505,8 @@ const agentManagerFactory = SecretsManager.sign(SECRETS_NAMESPACE, token => ({
491
505
  restorer.add(settingsWidget, settingsWidget.id);
492
506
  }
493
507
  app.commands.addCommand(CommandIds.openSettings, {
494
- label: 'AI Settings',
495
- caption: 'Configure AI providers and behavior',
508
+ label: trans.__('AI Settings'),
509
+ caption: trans.__('Configure AI providers and behavior'),
496
510
  icon: settingsIcon,
497
511
  iconClass: 'jp-ai-settings-icon',
498
512
  execute: () => {
@@ -515,7 +529,7 @@ const agentManagerFactory = SecretsManager.sign(SECRETS_NAMESPACE, token => ({
515
529
  if (palette) {
516
530
  palette.addItem({
517
531
  command: CommandIds.openSettings,
518
- category: 'AI Assistant'
532
+ category: trans.__('AI Assistant')
519
533
  });
520
534
  }
521
535
  return agentManagerFactory;
@@ -612,11 +626,13 @@ const inputToolbarFactory = {
612
626
  autoStart: true,
613
627
  provides: IInputToolbarRegistryFactory,
614
628
  requires: [IAISettingsModel, IToolRegistry],
615
- activate: (app, settingsModel, toolRegistry) => {
616
- const stopButton = stopItem();
617
- const clearButton = clearItem();
618
- const toolSelectButton = createToolSelectItem(toolRegistry, settingsModel.config.toolsEnabled);
619
- const modelSelectButton = createModelSelectItem(settingsModel);
629
+ optional: [ITranslator],
630
+ activate: (app, settingsModel, toolRegistry, translator) => {
631
+ const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
632
+ const stopButton = stopItem(trans);
633
+ const clearButton = clearItem(trans);
634
+ const toolSelectButton = createToolSelectItem(toolRegistry, settingsModel.config.toolsEnabled, trans);
635
+ const modelSelectButton = createModelSelectItem(settingsModel, trans);
620
636
  return {
621
637
  create() {
622
638
  const inputToolbarRegistry = InputToolbarRegistry.defaultToolbarRegistry();
@@ -644,12 +660,16 @@ const completionStatus = {
644
660
  description: 'The completion status displayed in the status bar',
645
661
  autoStart: true,
646
662
  requires: [IAISettingsModel],
647
- optional: [IStatusBar],
648
- activate: (app, settingsModel, statusBar) => {
663
+ optional: [IStatusBar, ITranslator],
664
+ activate: (app, settingsModel, statusBar, translator) => {
649
665
  if (!statusBar) {
650
666
  return;
651
667
  }
652
- const item = new CompletionStatusWidget({ settingsModel });
668
+ const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
669
+ const item = new CompletionStatusWidget({
670
+ settingsModel,
671
+ translator: trans
672
+ });
653
673
  statusBar?.registerStatusItem('completionState', {
654
674
  item,
655
675
  align: 'right',
@@ -2,7 +2,7 @@ import { VDomModel } from '@jupyterlab/ui-components';
2
2
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
3
3
  export interface IProviderParameters {
4
4
  temperature?: number;
5
- maxTokens?: number;
5
+ maxOutputTokens?: number;
6
6
  maxTurns?: number;
7
7
  supportsFillInMiddle?: boolean;
8
8
  useFilterText?: boolean;