@jupyterlite/ai 0.14.0 → 0.15.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 (52) hide show
  1. package/lib/agent.d.ts +28 -114
  2. package/lib/agent.js +140 -100
  3. package/lib/chat-model-handler.d.ts +9 -11
  4. package/lib/chat-model-handler.js +9 -4
  5. package/lib/chat-model.d.ts +84 -13
  6. package/lib/chat-model.js +208 -136
  7. package/lib/completion/completion-provider.d.ts +2 -3
  8. package/lib/components/completion-status.d.ts +2 -2
  9. package/lib/components/model-select.d.ts +3 -3
  10. package/lib/components/save-button.d.ts +31 -0
  11. package/lib/components/save-button.js +41 -0
  12. package/lib/components/token-usage-display.d.ts +2 -3
  13. package/lib/components/tool-select.d.ts +3 -4
  14. package/lib/diff-manager.d.ts +2 -3
  15. package/lib/index.d.ts +2 -4
  16. package/lib/index.js +181 -23
  17. package/lib/models/settings-model.d.ts +11 -53
  18. package/lib/models/settings-model.js +37 -22
  19. package/lib/providers/built-in-providers.js +17 -36
  20. package/lib/tokens.d.ts +340 -36
  21. package/lib/tokens.js +11 -6
  22. package/lib/tools/commands.d.ts +2 -3
  23. package/lib/widgets/ai-settings.d.ts +3 -5
  24. package/lib/widgets/ai-settings.js +3 -0
  25. package/lib/widgets/main-area-chat.d.ts +2 -3
  26. package/lib/widgets/main-area-chat.js +9 -9
  27. package/lib/widgets/provider-config-dialog.d.ts +1 -2
  28. package/lib/widgets/provider-config-dialog.js +16 -29
  29. package/package.json +15 -9
  30. package/schema/settings-model.json +7 -1
  31. package/src/agent.ts +197 -242
  32. package/src/chat-model-handler.ts +25 -21
  33. package/src/chat-model.ts +304 -196
  34. package/src/completion/completion-provider.ts +7 -4
  35. package/src/components/completion-status.tsx +3 -3
  36. package/src/components/model-select.tsx +4 -3
  37. package/src/components/save-button.tsx +84 -0
  38. package/src/components/token-usage-display.tsx +2 -3
  39. package/src/components/tool-select.tsx +10 -4
  40. package/src/diff-manager.ts +4 -4
  41. package/src/index.ts +245 -49
  42. package/src/models/settings-model.ts +45 -88
  43. package/src/providers/built-in-providers.ts +17 -36
  44. package/src/tokens.ts +406 -52
  45. package/src/tools/commands.ts +2 -3
  46. package/src/widgets/ai-settings.tsx +27 -15
  47. package/src/widgets/main-area-chat.ts +15 -12
  48. package/src/widgets/provider-config-dialog.tsx +51 -56
  49. package/style/base.css +17 -195
  50. package/lib/approval-buttons.d.ts +0 -49
  51. package/lib/approval-buttons.js +0 -79
  52. package/src/approval-buttons.ts +0 -115
@@ -1,74 +1,16 @@
1
1
  import { VDomModel } from '@jupyterlab/ui-components';
2
2
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
3
3
 
4
- const PLUGIN_ID = '@jupyterlite/ai:settings-model';
5
-
6
- export interface IProviderParameters {
7
- temperature?: number;
8
- maxOutputTokens?: number;
9
- maxTurns?: number;
10
- supportsFillInMiddle?: boolean;
11
- useFilterText?: boolean;
12
- }
13
-
14
- export interface IProviderConfig {
15
- id: string;
16
- name: string;
17
- provider: string;
18
- model: string;
19
- apiKey?: string;
20
- baseURL?: string;
21
- headers?: Record<string, string>;
22
- parameters?: IProviderParameters;
23
- customSettings?: Record<string, any>;
24
- [key: string]: any; // Index signature for JupyterLab settings compatibility
25
- }
4
+ import {
5
+ IAIConfig,
6
+ IAISettingsModel,
7
+ IMCPServerConfig,
8
+ IProviderConfig
9
+ } from '../tokens';
26
10
 
27
- export interface IMCPServerConfig {
28
- id: string;
29
- name: string;
30
- url: string;
31
- enabled: boolean;
32
- [key: string]: any; // Index signature for JupyterLab settings compatibility
33
- }
34
-
35
- export interface IAIConfig {
36
- // Whether to use the secrets manager
37
- useSecretsManager: boolean;
38
- // List of configured providers
39
- providers: IProviderConfig[];
40
- // Active provider IDs for different use cases
41
- defaultProvider: string; // Default provider for chat
42
- activeCompleterProvider?: string; // Provider for completions (if different)
43
- // When true, use the same provider for chat and completions
44
- useSameProviderForChatAndCompleter: boolean;
45
- // MCP servers configuration
46
- mcpServers: IMCPServerConfig[];
47
- // Global settings
48
- contextAwareness: boolean;
49
- codeExecution: boolean;
50
- systemPrompt: string;
51
- completionSystemPrompt: string;
52
- toolsEnabled: boolean;
53
- // Chat behavior settings
54
- sendWithShiftEnter: boolean;
55
- // Token usage display setting
56
- showTokenUsage: boolean;
57
- // Commands that require approval before execution
58
- commandsRequiringApproval: string[];
59
- // Commands whose execute_command outputs may auto-render MIME bundles in chat
60
- commandsAutoRenderMimeBundles: string[];
61
- // MIME types that are trusted when auto-rendering execute_command outputs
62
- trustedMimeTypesForAutoRender: string[];
63
- // Diff display settings
64
- showCellDiff: boolean;
65
- showFileDiff: boolean;
66
- diffDisplayMode: 'split' | 'unified';
67
- // Paths to directories containing agent skills
68
- skillsPaths: string[];
69
- }
11
+ const PLUGIN_ID = '@jupyterlite/ai:settings-model';
70
12
 
71
- export class AISettingsModel extends VDomModel {
13
+ export class AISettingsModel extends VDomModel implements IAISettingsModel {
72
14
  private _config: IAIConfig = {
73
15
  useSecretsManager: true,
74
16
  providers: [],
@@ -85,6 +27,7 @@ export class AISettingsModel extends VDomModel {
85
27
  showFileDiff: true,
86
28
  diffDisplayMode: 'split',
87
29
  skillsPaths: ['.agents/skills', '_agents/skills'],
30
+ chatBackupDirectory: '',
88
31
  commandsRequiringApproval: [
89
32
  'notebook:restart-run-all',
90
33
  'notebook:run-cell',
@@ -166,6 +109,15 @@ When asked to run code or perform computations, choose the most appropriate appr
166
109
 
167
110
  This means if the user asks you to "calculate the factorial of 100" or "check what library version is installed", run that directly with the jupyterlab-ai-commands kernel execution command rather than creating a new notebook file.
168
111
 
112
+ ## Notebook State and Cell Identity
113
+ When working with an existing notebook, use the notebook's current structure and kernel state as the source of truth.
114
+ - Before changing notebook content or structure, inspect the notebook and any target cells with the relevant notebook commands you have discovered.
115
+ - If the user may have edited the notebook, or if a previous command could have changed it, refresh your view before continuing rather than relying on earlier results.
116
+ - Treat variables from previously executed cells as part of the active kernel state. When the user asks you to work with existing data or variables, use them by name instead of recreating them unless the user asks you to redefine them or the kernel state is unavailable.
117
+ - Be explicit about the kind of cell reference you are using. A visible execution count (for example In [6]), a notebook position, and an internal cell ID or UUID are different identifiers and may not match.
118
+ - When the user identifies a cell by execution count, relative position, or content, verify the target cell from the current notebook contents before editing it or inserting cells relative to it.
119
+ - For relative insertions, anchor the change to the confirmed target cell rather than to empty placeholder or trailing cells unless the user explicitly refers to those cells.
120
+
169
121
  ## Your Approach
170
122
  - **Context-aware**: You understand the user is working in a data science/research environment
171
123
  - **Practical**: You focus on actionable solutions that work in the user's current setup
@@ -209,7 +161,7 @@ Guidelines:
209
161
 
210
162
  ## Multi-Step Task Handling
211
163
  When users request complex tasks, you use the command system to accomplish them:
212
- - For file and notebook operations, use discover_commands with query 'jupyterlab-ai-commands' to find the curated set of AI commands (~17 commands)
164
+ - For file and notebook operations, use discover_commands with query 'jupyterlab-ai-commands' to find the curated set of AI commands (~22 commands)
213
165
  - For other JupyterLab operations (terminal, launcher, UI), use specific keywords like 'terminal', 'launcher', etc.
214
166
  - IMPORTANT: Always use 'jupyterlab-ai-commands' as the query for file/notebook tasks - this returns a focused set instead of 100+ generic commands
215
167
  - For example, to create a notebook with cells:
@@ -251,16 +203,16 @@ Rules:
251
203
  constructor(options: AISettingsModel.IOptions) {
252
204
  super();
253
205
  this._settingRegistry = options.settingRegistry;
254
- this.initializeSettings();
206
+ this._initializeSettings();
255
207
  }
256
208
 
257
- private async initializeSettings(): Promise<void> {
209
+ private async _initializeSettings(): Promise<void> {
258
210
  try {
259
211
  this._settings = await this._settingRegistry.load(PLUGIN_ID);
260
- this.loadFromSettings();
212
+ this._loadFromSettings();
261
213
 
262
214
  // Listen for settings changes
263
- this._settings.changed.connect(this.onSettingsChanged, this);
215
+ this._settings.changed.connect(this._onSettingsChanged, this);
264
216
 
265
217
  this.stateChanged.emit(void 0);
266
218
  } catch (error) {
@@ -269,12 +221,12 @@ Rules:
269
221
  }
270
222
  }
271
223
 
272
- private onSettingsChanged(): void {
273
- this.loadFromSettings();
224
+ private _onSettingsChanged(): void {
225
+ this._loadFromSettings();
274
226
  this.stateChanged.emit(void 0);
275
227
  }
276
228
 
277
- private loadFromSettings(): void {
229
+ private _loadFromSettings(): void {
278
230
  if (!this._settings) {
279
231
  return;
280
232
  }
@@ -334,12 +286,12 @@ Rules:
334
286
  // If this is the first provider, make it active
335
287
  if (this._config.providers.length === 1) {
336
288
  // Save both providers and defaultProvider
337
- await this.saveSetting('providers', this._config.providers);
289
+ await this._saveSetting('providers', this._config.providers);
338
290
  this._config.defaultProvider = id;
339
- await this.saveSetting('defaultProvider', this._config.defaultProvider);
291
+ await this._saveSetting('defaultProvider', this._config.defaultProvider);
340
292
  } else {
341
293
  // Only save providers
342
- await this.saveSetting('providers', this._config.providers);
294
+ await this._saveSetting('providers', this._config.providers);
343
295
  }
344
296
 
345
297
  return id;
@@ -352,18 +304,18 @@ Rules:
352
304
  }
353
305
 
354
306
  this._config.providers.splice(index, 1);
355
- await this.saveSetting('providers', this._config.providers);
307
+ await this._saveSetting('providers', this._config.providers);
356
308
 
357
309
  // If this was the active provider, select a new one
358
310
  if (this._config.defaultProvider === id) {
359
311
  this._config.defaultProvider =
360
312
  this._config.providers.length > 0 ? this._config.providers[0].id : '';
361
- await this.saveSetting('defaultProvider', this._config.defaultProvider);
313
+ await this._saveSetting('defaultProvider', this._config.defaultProvider);
362
314
  }
363
315
 
364
316
  if (this._config.activeCompleterProvider === id) {
365
317
  this._config.activeCompleterProvider = undefined;
366
- await this.saveSetting(
318
+ await this._saveSetting(
367
319
  'activeCompleterProvider',
368
320
  this._config.activeCompleterProvider
369
321
  );
@@ -385,19 +337,19 @@ Rules:
385
337
  delete provider[key];
386
338
  }
387
339
  });
388
- await this.saveSetting('providers', this._config.providers);
340
+ await this._saveSetting('providers', this._config.providers);
389
341
  }
390
342
 
391
343
  async setActiveProvider(id: string): Promise<void> {
392
344
  if (this.getProvider(id)) {
393
345
  this._config.defaultProvider = id;
394
- await this.saveSetting('defaultProvider', this._config.defaultProvider);
346
+ await this._saveSetting('defaultProvider', this._config.defaultProvider);
395
347
  }
396
348
  }
397
349
 
398
350
  async setActiveCompleterProvider(id: string | undefined): Promise<void> {
399
351
  this._config.activeCompleterProvider = id;
400
- await this.saveSetting(
352
+ await this._saveSetting(
401
353
  'activeCompleterProvider',
402
354
  this._config.activeCompleterProvider
403
355
  );
@@ -423,7 +375,7 @@ Rules:
423
375
  };
424
376
 
425
377
  this._config.mcpServers.push(newServer);
426
- await this.saveSetting('mcpServers', this._config.mcpServers);
378
+ await this._saveSetting('mcpServers', this._config.mcpServers);
427
379
  return id;
428
380
  }
429
381
 
@@ -434,7 +386,7 @@ Rules:
434
386
  }
435
387
 
436
388
  this._config.mcpServers.splice(index, 1);
437
- await this.saveSetting('mcpServers', this._config.mcpServers);
389
+ await this._saveSetting('mcpServers', this._config.mcpServers);
438
390
  }
439
391
 
440
392
  async updateMCPServer(
@@ -447,7 +399,7 @@ Rules:
447
399
  }
448
400
 
449
401
  Object.assign(server, updates);
450
- await this.saveSetting('mcpServers', this._config.mcpServers);
402
+ await this._saveSetting('mcpServers', this._config.mcpServers);
451
403
  }
452
404
 
453
405
  async updateConfig(updates: Partial<IAIConfig>): Promise<void> {
@@ -460,7 +412,7 @@ Rules:
460
412
  this._config[key as keyof IAIConfig] !== value
461
413
  ) {
462
414
  (this._config as any)[key] = value;
463
- promises.push(this.saveSetting(key as keyof IAIConfig, value));
415
+ promises.push(this._saveSetting(key as keyof IAIConfig, value));
464
416
  }
465
417
  }
466
418
 
@@ -468,6 +420,11 @@ Rules:
468
420
  await Promise.all(promises);
469
421
  }
470
422
 
423
+ /**
424
+ * Get the API key saved in the settings file for a given provider.
425
+ *
426
+ * @param id - the id of the provider.
427
+ */
471
428
  getApiKey(id: string): string {
472
429
  // First check the active completer provider
473
430
  const activeCompleterProvider = this.getCompleterProvider();
@@ -484,7 +441,7 @@ Rules:
484
441
  return '';
485
442
  }
486
443
 
487
- private async saveSetting(key: keyof IAIConfig, value: any): Promise<void> {
444
+ private async _saveSetting(key: keyof IAIConfig, value: any): Promise<void> {
488
445
  try {
489
446
  if (this._settings) {
490
447
  // Only save the specific setting that changed
@@ -28,8 +28,7 @@ export const anthropicProvider: IProviderInfo = {
28
28
  'claude-opus-4-0',
29
29
  'claude-opus-4-20250514',
30
30
  'claude-sonnet-4-0',
31
- 'claude-sonnet-4-20250514',
32
- 'claude-3-haiku-20240307'
31
+ 'claude-sonnet-4-20250514'
33
32
  ],
34
33
  supportsBaseURL: true,
35
34
  supportsHeaders: true,
@@ -65,29 +64,18 @@ export const googleProvider: IProviderInfo = {
65
64
  'gemini-3.1-pro-preview',
66
65
  'gemini-3.1-pro-preview-customtools',
67
66
  'gemini-3.1-flash-image-preview',
68
- 'gemini-3-pro-preview',
67
+ 'gemini-3.1-flash-lite-preview',
69
68
  'gemini-3-pro-image-preview',
70
69
  'gemini-3-flash-preview',
71
70
  'gemini-2.5-pro',
72
71
  'gemini-2.5-flash',
73
72
  'gemini-2.5-flash-image',
74
73
  'gemini-2.5-flash-lite',
75
- 'gemini-2.5-flash-lite-preview-09-2025',
76
74
  'gemini-2.5-computer-use-preview-10-2025',
77
- 'gemini-2.0-flash',
78
- 'gemini-2.0-flash-001',
79
- 'gemini-2.0-flash-lite',
80
- 'gemini-2.0-flash-lite-001',
75
+ 'deep-research-pro-preview-12-2025',
81
76
  'gemini-pro-latest',
82
77
  'gemini-flash-latest',
83
- 'gemini-flash-lite-latest',
84
- 'deep-research-pro-preview-12-2025',
85
- 'gemma-3-27b-it',
86
- 'gemma-3-12b-it',
87
- 'gemma-3-4b-it',
88
- 'gemma-3-1b-it',
89
- 'gemma-3n-e4b-it',
90
- 'gemma-3n-e2b-it'
78
+ 'gemini-flash-lite-latest'
91
79
  ],
92
80
  supportsBaseURL: true,
93
81
  factory: (options: IModelOptions) => {
@@ -111,23 +99,19 @@ export const mistralProvider: IProviderInfo = {
111
99
  name: 'Mistral AI',
112
100
  apiKeyRequirement: 'required',
113
101
  defaultModels: [
114
- 'ministral-3b-latest',
115
- 'ministral-8b-latest',
116
102
  'mistral-large-latest',
117
103
  'mistral-medium-latest',
118
104
  'mistral-medium-2508',
119
- 'mistral-medium-2505',
120
105
  'mistral-small-latest',
121
- 'codestral-latest',
106
+ 'mistral-small-2506',
107
+ 'ministral-3b-latest',
108
+ 'ministral-8b-latest',
109
+ 'ministral-14b-latest',
110
+ 'magistral-small-latest',
111
+ 'magistral-medium-latest',
122
112
  'pixtral-large-latest',
123
- 'magistral-small-2507',
124
- 'magistral-medium-2507',
125
- 'magistral-small-2506',
126
- 'magistral-medium-2506',
127
- 'pixtral-12b-2409',
128
- 'open-mistral-7b',
129
- 'open-mixtral-8x7b',
130
- 'open-mixtral-8x22b'
113
+ 'codestral-latest',
114
+ 'devstral-latest'
131
115
  ],
132
116
  supportsBaseURL: true,
133
117
  factory: (options: IModelOptions) => {
@@ -151,6 +135,9 @@ export const openaiProvider: IProviderInfo = {
151
135
  name: 'OpenAI',
152
136
  apiKeyRequirement: 'required',
153
137
  defaultModels: [
138
+ 'gpt-5.4',
139
+ 'gpt-5.4-mini',
140
+ 'gpt-5.4-nano',
154
141
  'gpt-5.2',
155
142
  'gpt-5.2-2025-12-11',
156
143
  'gpt-5.2-chat-latest',
@@ -169,6 +156,7 @@ export const openaiProvider: IProviderInfo = {
169
156
  'gpt-5-nano-2025-08-07',
170
157
  'o4-mini',
171
158
  'o4-mini-2025-04-16',
159
+ 'o3-pro',
172
160
  'o3',
173
161
  'o3-2025-04-16',
174
162
  'o3-mini',
@@ -185,21 +173,14 @@ export const openaiProvider: IProviderInfo = {
185
173
  'gpt-4o-2024-05-13',
186
174
  'gpt-4o-2024-08-06',
187
175
  'gpt-4o-2024-11-20',
188
- 'gpt-4o-audio-preview',
189
- 'gpt-4o-audio-preview-2024-12-17',
190
- 'gpt-4o-audio-preview-2025-06-03',
191
176
  'gpt-4o-mini',
192
177
  'gpt-4o-mini-2024-07-18',
193
- 'gpt-4o-mini-audio-preview',
194
- 'gpt-4o-mini-audio-preview-2024-12-17',
195
178
  'gpt-4o-search-preview',
196
179
  'gpt-4o-search-preview-2025-03-11',
197
180
  'gpt-4o-mini-search-preview',
198
181
  'gpt-4o-mini-search-preview-2025-03-11',
199
182
  'gpt-3.5-turbo',
200
- 'gpt-3.5-turbo-0125',
201
- 'gpt-3.5-turbo-1106',
202
- 'gpt-3.5-turbo-16k'
183
+ 'gpt-3.5-turbo-0125'
203
184
  ],
204
185
  supportsBaseURL: true,
205
186
  supportsHeaders: true,