@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
package/src/index.ts CHANGED
@@ -1,68 +1,198 @@
1
+ import {
2
+ ILabShell,
3
+ ILayoutRestorer,
4
+ JupyterFrontEnd,
5
+ JupyterFrontEndPlugin
6
+ } from '@jupyterlab/application';
7
+
1
8
  import {
2
9
  ActiveCellManager,
3
- buildChatSidebar,
4
- buildErrorWidget,
5
- ChatCommandRegistry,
6
- IActiveCellManager,
7
- IChatCommandRegistry,
10
+ AttachmentOpenerRegistry,
11
+ ChatWidget,
8
12
  InputToolbarRegistry
9
13
  } from '@jupyter/chat';
10
- import {
11
- JupyterFrontEnd,
12
- JupyterFrontEndPlugin,
13
- ILayoutRestorer
14
- } from '@jupyterlab/application';
15
- import { ReactWidget, IThemeManager } from '@jupyterlab/apputils';
14
+
15
+ import { ICommandPalette, IThemeManager } from '@jupyterlab/apputils';
16
+
16
17
  import { ICompletionProviderManager } from '@jupyterlab/completer';
18
+
19
+ import { IDocumentManager } from '@jupyterlab/docmanager';
20
+
17
21
  import { INotebookTracker } from '@jupyterlab/notebook';
22
+
18
23
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
24
+
25
+ import { IKernelSpecManager, KernelSpec } from '@jupyterlab/services';
26
+
19
27
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
20
- import { IFormRendererRegistry } from '@jupyterlab/ui-components';
21
- import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
22
- import { ISecretsManager, SecretsManager } from 'jupyter-secrets-manager';
23
-
24
- import { ChatHandler, welcomeMessage } from './chat-handler';
25
- import { CompletionProvider } from './completion-provider';
26
- import { defaultProviderPlugins } from './default-providers';
27
- import { AIProviderRegistry } from './provider';
28
- import { aiSettingsRenderer, textArea } from './settings';
29
- import { IAIProviderRegistry, PLUGIN_IDS } from './tokens';
28
+
29
+ import { settingsIcon } from '@jupyterlab/ui-components';
30
+
31
+ import { AgentManager } from './agent';
32
+ import { AIChatModel } from './chat-model';
33
+
34
+ import {
35
+ ChatProviderRegistry,
36
+ CompletionProviderRegistry
37
+ } from './providers/provider-registry';
38
+
39
+ import {
40
+ IChatProviderRegistry,
41
+ ICompletionProviderRegistry,
42
+ IAISettingsModel,
43
+ IToolRegistry
44
+ } from './tokens';
45
+
46
+ import {
47
+ registerBuiltInChatProviders,
48
+ registerBuiltInCompletionProviders
49
+ } from './providers/built-in-providers';
50
+
51
+ import { AICompletionProvider } from './completion';
52
+
53
+ import { clearItem } from './components/clear-button';
54
+
55
+ import { createModelSelectItem } from './components/model-select';
56
+
30
57
  import { stopItem } from './components/stop-button';
31
58
 
32
- const chatCommandRegistryPlugin: JupyterFrontEndPlugin<IChatCommandRegistry> = {
33
- id: PLUGIN_IDS.chatCommandRegistry,
34
- description: 'Autocompletion registry',
59
+ import { createToolSelectItem } from './components/tool-select';
60
+
61
+ import { AISettingsModel } from './models/settings-model';
62
+
63
+ import { ToolRegistry } from './tools/tool-registry';
64
+
65
+ import {
66
+ createAddCellTool,
67
+ createDeleteCellTool,
68
+ createExecuteActiveCellTool,
69
+ createGetCellInfoTool,
70
+ createGetNotebookInfoTool,
71
+ createNotebookCreationTool,
72
+ createRunCellTool,
73
+ createSaveNotebookTool,
74
+ createSetCellContentTool
75
+ } from './tools/notebook';
76
+
77
+ import {
78
+ createCopyFileTool,
79
+ createDeleteFileTool,
80
+ createNavigateToDirectoryTool,
81
+ createNewFileTool,
82
+ createOpenFileTool,
83
+ createRenameFileTool
84
+ } from './tools/file';
85
+
86
+ import {
87
+ createDiscoverCommandsTool,
88
+ createExecuteCommandTool
89
+ } from './tools/commands';
90
+
91
+ import { AISettingsWidget } from './widgets/ai-settings';
92
+
93
+ import { ChatWrapperWidget } from './widgets/chat-wrapper';
94
+
95
+ /**
96
+ * Command IDs namespace
97
+ */
98
+ namespace CommandIds {
99
+ export const openSettings = '@jupyterlite/ai:open-settings';
100
+ export const reposition = '@jupyterlite/ai:reposition';
101
+ }
102
+
103
+ /**
104
+ * Chat provider registry plugin
105
+ */
106
+ const chatProviderRegistryPlugin: JupyterFrontEndPlugin<IChatProviderRegistry> =
107
+ {
108
+ id: '@jupyterlite/ai:chat-provider-registry',
109
+ description: 'Chat AI provider registry',
110
+ autoStart: true,
111
+ provides: IChatProviderRegistry,
112
+ activate: () => {
113
+ return new ChatProviderRegistry();
114
+ }
115
+ };
116
+
117
+ /**
118
+ * Completion provider registry plugin
119
+ */
120
+ const completionProviderRegistryPlugin: JupyterFrontEndPlugin<ICompletionProviderRegistry> =
121
+ {
122
+ id: '@jupyterlite/ai:completion-provider-registry',
123
+ description: 'Completion provider registry',
124
+ autoStart: true,
125
+ provides: ICompletionProviderRegistry,
126
+ activate: () => {
127
+ return new CompletionProviderRegistry();
128
+ }
129
+ };
130
+
131
+ /**
132
+ * Built-in chat providers plugin
133
+ */
134
+ const builtInChatProvidersPlugin: JupyterFrontEndPlugin<void> = {
135
+ id: '@jupyterlite/ai:built-in-chat-providers',
136
+ description: 'Register built-in chat AI providers',
137
+ autoStart: true,
138
+ requires: [IChatProviderRegistry],
139
+ activate: (app: JupyterFrontEnd, chatRegistry: IChatProviderRegistry) => {
140
+ registerBuiltInChatProviders(chatRegistry);
141
+ }
142
+ };
143
+
144
+ /**
145
+ * Built-in completion providers plugin
146
+ */
147
+ const builtInCompletionProvidersPlugin: JupyterFrontEndPlugin<void> = {
148
+ id: '@jupyterlite/ai:built-in-completion-providers',
149
+ description: 'Register built-in completion providers',
35
150
  autoStart: true,
36
- provides: IChatCommandRegistry,
37
- activate: () => {
38
- const registry = new ChatCommandRegistry();
39
- registry.addProvider(new ChatHandler.ClearCommandProvider());
40
- return registry;
151
+ requires: [ICompletionProviderRegistry],
152
+ activate: (
153
+ app: JupyterFrontEnd,
154
+ completionRegistry: ICompletionProviderRegistry
155
+ ) => {
156
+ registerBuiltInCompletionProviders(completionRegistry);
41
157
  }
42
158
  };
43
159
 
44
- const chatPlugin: JupyterFrontEndPlugin<void> = {
45
- id: PLUGIN_IDS.chat,
46
- description: 'LLM chat extension',
160
+ /**
161
+ * Initialization data for the extension.
162
+ */
163
+ const plugin: JupyterFrontEndPlugin<void> = {
164
+ id: '@jupyterlite/ai:plugin',
165
+ description: 'AI in JupyterLab',
47
166
  autoStart: true,
48
- requires: [IAIProviderRegistry, IRenderMimeRegistry, IChatCommandRegistry],
167
+ requires: [
168
+ IAISettingsModel,
169
+ IToolRegistry,
170
+ IRenderMimeRegistry,
171
+ IDocumentManager,
172
+ IChatProviderRegistry
173
+ ],
49
174
  optional: [
50
- INotebookTracker,
51
- ISettingRegistry,
52
175
  IThemeManager,
53
- ILayoutRestorer
176
+ ICommandPalette,
177
+ INotebookTracker,
178
+ ILayoutRestorer,
179
+ ILabShell
54
180
  ],
55
- activate: async (
181
+ activate: (
56
182
  app: JupyterFrontEnd,
57
- providerRegistry: IAIProviderRegistry,
183
+ settingsModel: AISettingsModel,
184
+ toolRegistry: IToolRegistry,
58
185
  rmRegistry: IRenderMimeRegistry,
59
- chatCommandRegistry: IChatCommandRegistry,
60
- notebookTracker: INotebookTracker | null,
61
- settingsRegistry: ISettingRegistry | null,
62
- themeManager: IThemeManager | null,
63
- restorer: ILayoutRestorer | null
64
- ) => {
65
- let activeCellManager: IActiveCellManager | null = null;
186
+ docManager: IDocumentManager,
187
+ chatProviderRegistry: IChatProviderRegistry,
188
+ themeManager?: IThemeManager,
189
+ palette?: ICommandPalette,
190
+ notebookTracker?: INotebookTracker,
191
+ restorer?: ILayoutRestorer,
192
+ labShell?: ILabShell
193
+ ): void => {
194
+ // Create ActiveCellManager if notebook tracker is available
195
+ let activeCellManager: ActiveCellManager | undefined;
66
196
  if (notebookTracker) {
67
197
  activeCellManager = new ActiveCellManager({
68
198
  tracker: notebookTracker,
@@ -70,55 +200,45 @@ const chatPlugin: JupyterFrontEndPlugin<void> = {
70
200
  });
71
201
  }
72
202
 
73
- const chatHandler = new ChatHandler({
74
- providerRegistry,
75
- activeCellManager
203
+ // Create Agent Manager first so it can be shared
204
+ const agentManager = new AgentManager({
205
+ settingsModel,
206
+ toolRegistry,
207
+ chatProviderRegistry: chatProviderRegistry
76
208
  });
77
209
 
78
- let sendWithShiftEnter = false;
79
- let enableCodeToolbar = true;
80
- let personaName = 'AI';
81
-
82
- function loadSetting(setting: ISettingRegistry.ISettings): void {
83
- sendWithShiftEnter = setting.get('sendWithShiftEnter')
84
- .composite as boolean;
85
- enableCodeToolbar = setting.get('enableCodeToolbar').composite as boolean;
86
- personaName = setting.get('personaName').composite as string;
87
-
88
- // set the properties
89
- chatHandler.config = { sendWithShiftEnter, enableCodeToolbar };
90
- chatHandler.personaName = personaName;
91
- }
92
-
93
- Promise.all([app.restored, settingsRegistry?.load(chatPlugin.id)])
94
- .then(([, settings]) => {
95
- if (!settings) {
96
- console.warn(
97
- 'The SettingsRegistry is not loaded for the chat extension'
98
- );
99
- return;
100
- }
101
- loadSetting(settings);
102
- settings.changed.connect(loadSetting);
103
- })
104
- .catch(reason => {
105
- console.error(
106
- `Something went wrong when reading the settings.\n${reason}`
107
- );
108
- });
109
-
110
- let chatWidget: ReactWidget | null = null;
210
+ // Create AI chat model
211
+ const chatModel = new AIChatModel({
212
+ user: { username: 'user', display_name: 'User' },
213
+ settingsModel,
214
+ agentManager,
215
+ activeCellManager,
216
+ documentManager: docManager
217
+ });
111
218
 
219
+ // Create input toolbar registry with all buttons
112
220
  const inputToolbarRegistry = InputToolbarRegistry.defaultToolbarRegistry();
113
- const stopButton = stopItem(() => chatHandler.stopStreaming());
221
+ const stopButton = stopItem();
222
+ const clearButton = clearItem();
223
+ const toolSelectButton = createToolSelectItem(
224
+ toolRegistry,
225
+ settingsModel.config.toolsEnabled
226
+ );
227
+ const modelSelectButton = createModelSelectItem(settingsModel);
228
+
114
229
  inputToolbarRegistry.addItem('stop', stopButton);
230
+ inputToolbarRegistry.addItem('clear', clearButton);
231
+ inputToolbarRegistry.addItem('model', modelSelectButton);
232
+ inputToolbarRegistry.addItem('tools', toolSelectButton);
233
+
234
+ // Listen to writers changes to show/hide stop button
235
+ chatModel.writersChanged.connect((_, writers) => {
236
+ // Check if AI is currently writing (streaming)
237
+ const aiWriting = writers.some(
238
+ writer => writer.user.username === 'ai-assistant'
239
+ );
115
240
 
116
- chatHandler.writersChanged.connect((_, writers) => {
117
- if (
118
- writers.filter(
119
- writer => writer.user.username === chatHandler.personaName
120
- ).length
121
- ) {
241
+ if (aiWriting) {
122
242
  inputToolbarRegistry.hide('send');
123
243
  inputToolbarRegistry.show('stop');
124
244
  } else {
@@ -127,182 +247,292 @@ const chatPlugin: JupyterFrontEndPlugin<void> = {
127
247
  }
128
248
  });
129
249
 
130
- try {
131
- chatWidget = buildChatSidebar({
132
- model: chatHandler,
133
- themeManager,
134
- rmRegistry,
135
- chatCommandRegistry,
136
- inputToolbarRegistry,
137
- welcomeMessage: welcomeMessage(providerRegistry.providers)
138
- });
139
- } catch (e) {
140
- chatWidget = buildErrorWidget(themeManager);
141
- }
250
+ // Listen for settings changes to update tool availability
251
+ settingsModel.stateChanged.connect(() => {
252
+ const config = settingsModel.config;
253
+ if (!config.toolsEnabled) {
254
+ inputToolbarRegistry.hide('tools');
255
+ } else {
256
+ inputToolbarRegistry.show('tools');
257
+ }
258
+ });
259
+
260
+ // Create attachment opener registry to handle file attachments
261
+ const attachmentOpenerRegistry = new AttachmentOpenerRegistry();
262
+ attachmentOpenerRegistry.set('file', attachment => {
263
+ app.commands.execute('docmanager:open', { path: attachment.value });
264
+ });
265
+
266
+ attachmentOpenerRegistry.set('notebook', attachment => {
267
+ app.commands.execute('docmanager:open', { path: attachment.value });
268
+ });
269
+
270
+ // Create chat panel with drag/drop functionality
271
+ const chatPanel = new ChatWidget({
272
+ model: chatModel,
273
+ rmRegistry,
274
+ themeManager,
275
+ inputToolbarRegistry,
276
+ attachmentOpenerRegistry
277
+ });
142
278
 
143
- chatWidget.title.caption = 'Jupyterlite AI Chat';
144
- chatWidget.id = '@jupyterlite/ai:chat-widget';
279
+ // Create wrapper widget with a toolbar
280
+ const chatWrapper = new ChatWrapperWidget({
281
+ chatPanel,
282
+ commands: app.commands,
283
+ chatModel,
284
+ settingsModel
285
+ });
145
286
 
146
- app.shell.add(chatWidget as ReactWidget, 'left', { rank: 2000 });
287
+ app.shell.add(chatWrapper, 'left', { rank: 1000 });
288
+
289
+ const settingsWidget = new AISettingsWidget({
290
+ settingsModel,
291
+ agentManager,
292
+ themeManager,
293
+ chatProviderRegistry
294
+ });
295
+ settingsWidget.id = 'jupyterlite-ai-settings';
296
+ settingsWidget.title.icon = settingsIcon;
147
297
 
148
298
  if (restorer) {
149
- restorer.add(chatWidget, chatWidget.id);
299
+ restorer.add(chatWrapper, chatWrapper.id);
300
+ restorer.add(settingsWidget, settingsWidget.id);
150
301
  }
302
+
303
+ registerCommands(app, palette, settingsWidget, labShell);
151
304
  }
152
305
  };
153
306
 
154
- const completerPlugin: JupyterFrontEndPlugin<void> = {
155
- id: PLUGIN_IDS.completer,
156
- autoStart: true,
157
- requires: [IAIProviderRegistry, ICompletionProviderManager],
158
- activate: (
159
- app: JupyterFrontEnd,
160
- providerRegistry: IAIProviderRegistry,
161
- manager: ICompletionProviderManager
162
- ): void => {
163
- const completer = new CompletionProvider({
164
- providerRegistry,
165
- requestCompletion: () => app.commands.execute('inline-completer:invoke')
307
+ function registerCommands(
308
+ app: JupyterFrontEnd,
309
+ palette?: ICommandPalette,
310
+ settingsWidget?: AISettingsWidget,
311
+ labShell?: ILabShell
312
+ ) {
313
+ const { commands } = app;
314
+
315
+ commands.addCommand(CommandIds.openSettings, {
316
+ label: 'AI Settings',
317
+ caption: 'Configure AI providers and behavior',
318
+ icon: settingsIcon,
319
+ execute: () => {
320
+ // Check if the widget already exists in shell
321
+ let widget = Array.from(app.shell.widgets('main')).find(
322
+ w => w.id === 'jupyterlite-ai-settings'
323
+ ) as AISettingsWidget;
324
+
325
+ if (!widget && settingsWidget) {
326
+ // Use the pre-created widget
327
+ widget = settingsWidget;
328
+ app.shell.add(widget, 'main');
329
+ }
330
+
331
+ if (widget) {
332
+ app.shell.activateById(widget.id);
333
+ }
334
+ },
335
+ describedBy: {
336
+ args: {}
337
+ }
338
+ });
339
+
340
+ if (labShell) {
341
+ commands.addCommand(CommandIds.reposition, {
342
+ label: 'Reposition Widget',
343
+ execute: (args: any) => {
344
+ const { widgetId, area, mode } = args;
345
+ const widget = widgetId
346
+ ? Array.from(labShell.widgets('main')).find(w => w.id === widgetId) ||
347
+ labShell.currentWidget
348
+ : labShell.currentWidget;
349
+
350
+ if (!widget) {
351
+ return;
352
+ }
353
+
354
+ if (area && area !== 'main') {
355
+ // Move to different area
356
+ labShell.move(widget, area);
357
+ labShell.activateById(widget.id);
358
+ } else if (mode) {
359
+ // Reposition within main area using split mode
360
+ labShell.add(widget, 'main', { mode, activate: true });
361
+ }
362
+ },
363
+ describedBy: {
364
+ args: {
365
+ type: 'object',
366
+ properties: {
367
+ widgetId: {
368
+ type: 'string',
369
+ description:
370
+ 'The widget ID to reposition in the application shell'
371
+ },
372
+ area: {
373
+ type: 'string',
374
+ description: 'The name of the area to reposition the widget to'
375
+ },
376
+ mode: {
377
+ type: 'string',
378
+ enum: ['split-left', 'split-right', 'split-top', 'split-bottom'],
379
+ description: 'The mode to use when repositioning the widget'
380
+ }
381
+ }
382
+ }
383
+ }
166
384
  });
167
- manager.registerInlineProvider(completer);
168
385
  }
169
- };
170
386
 
171
- const providerRegistryPlugin: JupyterFrontEndPlugin<IAIProviderRegistry> =
172
- SecretsManager.sign(PLUGIN_IDS.providerRegistry, token => ({
173
- id: PLUGIN_IDS.providerRegistry,
174
- autoStart: true,
175
- requires: [IFormRendererRegistry, ISettingRegistry],
176
- optional: [IRenderMimeRegistry, ISecretsManager],
177
- provides: IAIProviderRegistry,
178
- activate: (
179
- app: JupyterFrontEnd,
180
- editorRegistry: IFormRendererRegistry,
181
- settingRegistry: ISettingRegistry,
182
- rmRegistry?: IRenderMimeRegistry,
183
- secretsManager?: ISecretsManager
184
- ): IAIProviderRegistry => {
185
- const providerRegistry = new AIProviderRegistry({
186
- token,
187
- secretsManager
188
- });
387
+ // Add to command palette if available
388
+ if (palette) {
389
+ palette.addItem({
390
+ command: CommandIds.openSettings,
391
+ category: 'AI Assistant'
392
+ });
393
+ }
394
+ }
189
395
 
190
- editorRegistry.addRenderer(
191
- `${PLUGIN_IDS.providerRegistry}.AIproviders`,
192
- aiSettingsRenderer({
193
- providerRegistry,
194
- secretsToken: token,
195
- rmRegistry,
196
- secretsManager
197
- })
396
+ /**
397
+ * A plugin to provide AI-powered code completion.
398
+ */
399
+ const completionPlugin: JupyterFrontEndPlugin<void> = {
400
+ id: '@jupyterlite/ai:completion',
401
+ description: 'AI-powered code completion',
402
+ autoStart: true,
403
+ requires: [IAISettingsModel, ICompletionProviderRegistry],
404
+ optional: [ICompletionProviderManager],
405
+ activate: (
406
+ app: JupyterFrontEnd,
407
+ settingsModel: AISettingsModel,
408
+ completionProviderRegistry: ICompletionProviderRegistry,
409
+ manager?: ICompletionProviderManager
410
+ ) => {
411
+ if (!manager) {
412
+ console.info(
413
+ 'Completion provider manager not available, skipping AI completion setup'
198
414
  );
415
+ return;
416
+ }
199
417
 
200
- settingRegistry
201
- .load(providerRegistryPlugin.id)
202
- .then(settings => {
203
- if (!secretsManager) {
204
- delete settings.schema.properties?.['UseSecretsManager'];
205
- }
206
- const updateProvider = () => {
207
- // Get the Ai provider settings.
208
- const providerSettings = settings.get('AIproviders')
209
- .composite as ReadonlyPartialJSONObject;
210
-
211
- // Update completer provider.
212
- if (Object.keys(providerSettings).includes('completer')) {
213
- providerRegistry.setCompleterProvider(
214
- providerSettings['completer'] as ReadonlyPartialJSONObject
215
- );
216
- } else {
217
- providerRegistry.setCompleterProvider({});
218
- }
418
+ const completionProvider = new AICompletionProvider({
419
+ settingsModel,
420
+ completionProviderRegistry: completionProviderRegistry
421
+ });
219
422
 
220
- // Update chat provider.
221
- if (Object.keys(providerSettings).includes('chat')) {
222
- providerRegistry.setChatProvider(
223
- providerSettings['chat'] as ReadonlyPartialJSONObject
224
- );
225
- } else {
226
- providerRegistry.setChatProvider({});
227
- }
228
- };
229
-
230
- settings.changed.connect(() => updateProvider());
231
- updateProvider();
232
- })
233
- .catch(reason => {
234
- console.error(
235
- `Failed to load settings for ${providerRegistryPlugin.id}`,
236
- reason
237
- );
238
- });
239
-
240
- return providerRegistry;
241
- }
242
- }));
423
+ manager.registerInlineProvider(completionProvider);
424
+ }
425
+ };
243
426
 
244
- const systemPromptsPlugin: JupyterFrontEndPlugin<void> = {
245
- id: PLUGIN_IDS.systemPrompts,
427
+ /**
428
+ * A plugin to provide the settings model.
429
+ */
430
+ const settingsModel: JupyterFrontEndPlugin<AISettingsModel> = {
431
+ id: '@jupyterlite/ai:settings-model',
432
+ description: 'Provide the AI settings model',
246
433
  autoStart: true,
247
- requires: [IAIProviderRegistry, ISettingRegistry],
248
- optional: [IFormRendererRegistry],
434
+ provides: IAISettingsModel,
435
+ requires: [ISettingRegistry],
436
+ activate: (app: JupyterFrontEnd, settingRegistry: ISettingRegistry) => {
437
+ return new AISettingsModel({ settingRegistry });
438
+ }
439
+ };
440
+
441
+ /**
442
+ * A plugin to provide the tool registry.
443
+ */
444
+ const toolRegistry: JupyterFrontEndPlugin<IToolRegistry> = {
445
+ id: '@jupyterlite/ai:tool-registry',
446
+ description: 'Provide the AI tool registry',
447
+ autoStart: true,
448
+ requires: [IAISettingsModel, IDocumentManager, IKernelSpecManager],
449
+ optional: [INotebookTracker],
450
+ provides: IToolRegistry,
249
451
  activate: (
250
452
  app: JupyterFrontEnd,
251
- providerRegistry: IAIProviderRegistry,
252
- settingsRegistry: ISettingRegistry,
253
- editorRegistry: IFormRendererRegistry | null
254
- ): void => {
255
- // Set textarea renderer for the prompt setting.
256
- editorRegistry?.addRenderer(
257
- `${PLUGIN_IDS.systemPrompts}.chatSystemPrompt`,
258
- textArea
453
+ settingsModel: AISettingsModel,
454
+ docManager: IDocumentManager,
455
+ kernelSpecManager: KernelSpec.IManager,
456
+ notebookTracker?: INotebookTracker
457
+ ) => {
458
+ const toolRegistry = new ToolRegistry();
459
+
460
+ const notebookCreationTool = createNotebookCreationTool(
461
+ docManager,
462
+ kernelSpecManager
259
463
  );
260
- editorRegistry?.addRenderer(
261
- `${PLUGIN_IDS.systemPrompts}.completionSystemPrompt`,
262
- textArea
464
+ toolRegistry.add('create_notebook', notebookCreationTool);
465
+
466
+ // Add high-level notebook operation tools
467
+ const addCellTool = createAddCellTool(docManager, notebookTracker);
468
+ const getNotebookInfoTool = createGetNotebookInfoTool(
469
+ docManager,
470
+ notebookTracker
471
+ );
472
+ const getCellInfoTool = createGetCellInfoTool(docManager, notebookTracker);
473
+ const setCellContentTool = createSetCellContentTool(
474
+ docManager,
475
+ notebookTracker
476
+ );
477
+ const runCellTool = createRunCellTool(docManager, notebookTracker);
478
+ const deleteCellTool = createDeleteCellTool(docManager, notebookTracker);
479
+ const saveNotebookTool = createSaveNotebookTool(
480
+ docManager,
481
+ notebookTracker
482
+ );
483
+ const executeActiveCellTool = createExecuteActiveCellTool(
484
+ docManager,
485
+ notebookTracker
263
486
  );
264
487
 
265
- /**
266
- * Update the prompts in the provider registry.
267
- */
268
- function loadSetting(setting: ISettingRegistry.ISettings): void {
269
- providerRegistry.chatSystemPrompt = setting.get('chatSystemPrompt')
270
- .composite as string;
271
- providerRegistry.completerSystemPrompt = setting.get(
272
- 'completionSystemPrompt'
273
- ).composite as string;
274
- }
488
+ toolRegistry.add('add_cell', addCellTool);
489
+ toolRegistry.add('get_notebook_info', getNotebookInfoTool);
490
+ toolRegistry.add('get_cell_info', getCellInfoTool);
491
+ toolRegistry.add('set_cell_content', setCellContentTool);
492
+ toolRegistry.add('run_cell', runCellTool);
493
+ toolRegistry.add('delete_cell', deleteCellTool);
494
+ toolRegistry.add('save_notebook', saveNotebookTool);
495
+ toolRegistry.add('execute_active_cell', executeActiveCellTool);
275
496
 
276
- Promise.all([
277
- app.restored,
278
- settingsRegistry?.load(PLUGIN_IDS.systemPrompts)
279
- ])
280
- .then(([, settings]) => {
281
- if (!settings) {
282
- console.warn(
283
- 'The SettingsRegistry is not loaded for the chat extension'
284
- );
285
- return;
286
- }
287
- loadSetting(settings);
288
- settings.changed.connect(loadSetting);
289
- })
290
- .catch(reason => {
291
- console.error(
292
- `Something went wrong when reading the settings.\n${reason}`
293
- );
294
- });
497
+ // Add file operation tools
498
+ const newFileTool = createNewFileTool(docManager);
499
+ const openFileTool = createOpenFileTool(docManager);
500
+ const deleteFileTool = createDeleteFileTool(docManager);
501
+ const renameFileTool = createRenameFileTool(docManager);
502
+ const copyFileTool = createCopyFileTool(docManager);
503
+ const navigateToDirectoryTool = createNavigateToDirectoryTool(app.commands);
504
+
505
+ toolRegistry.add('create_file', newFileTool);
506
+ toolRegistry.add('open_file', openFileTool);
507
+ toolRegistry.add('delete_file', deleteFileTool);
508
+ toolRegistry.add('rename_file', renameFileTool);
509
+ toolRegistry.add('copy_file', copyFileTool);
510
+ toolRegistry.add('navigate_to_directory', navigateToDirectoryTool);
511
+
512
+ // Add command operation tools
513
+ const discoverCommandsTool = createDiscoverCommandsTool(app.commands);
514
+ const executeCommandTool = createExecuteCommandTool(
515
+ app.commands,
516
+ settingsModel
517
+ );
518
+
519
+ toolRegistry.add('discover_commands', discoverCommandsTool);
520
+ toolRegistry.add('execute_command', executeCommandTool);
521
+
522
+ return toolRegistry;
295
523
  }
296
524
  };
297
525
 
298
526
  export default [
299
- providerRegistryPlugin,
300
- chatCommandRegistryPlugin,
301
- chatPlugin,
302
- completerPlugin,
303
- systemPromptsPlugin,
304
- ...defaultProviderPlugins
527
+ chatProviderRegistryPlugin,
528
+ completionProviderRegistryPlugin,
529
+ builtInChatProvidersPlugin,
530
+ builtInCompletionProvidersPlugin,
531
+ plugin,
532
+ completionPlugin,
533
+ settingsModel,
534
+ toolRegistry
305
535
  ];
306
536
 
307
- export { IAIProviderRegistry } from './tokens';
308
- export * from './base-completer';
537
+ // Export extension points for other extensions to use
538
+ export * from './tokens';