@jupyterlite/ai 0.14.0 → 0.16.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.
- package/lib/agent.d.ts +33 -115
- package/lib/agent.js +192 -106
- package/lib/chat-model-handler.d.ts +9 -11
- package/lib/chat-model-handler.js +9 -4
- package/lib/chat-model.d.ts +84 -13
- package/lib/chat-model.js +214 -136
- package/lib/completion/completion-provider.d.ts +2 -3
- package/lib/components/completion-status.d.ts +2 -2
- package/lib/components/index.d.ts +1 -1
- package/lib/components/index.js +1 -1
- package/lib/components/model-select.d.ts +3 -3
- package/lib/components/save-button.d.ts +31 -0
- package/lib/components/save-button.js +41 -0
- package/lib/components/tool-select.d.ts +3 -4
- package/lib/components/{token-usage-display.d.ts → usage-display.d.ts} +13 -14
- package/lib/components/usage-display.js +109 -0
- package/lib/diff-manager.d.ts +2 -3
- package/lib/index.d.ts +2 -4
- package/lib/index.js +186 -28
- package/lib/models/settings-model.d.ts +11 -53
- package/lib/models/settings-model.js +38 -22
- package/lib/providers/built-in-providers.js +22 -36
- package/lib/providers/generated-context-windows.d.ts +8 -0
- package/lib/providers/generated-context-windows.js +96 -0
- package/lib/providers/model-info.d.ts +3 -0
- package/lib/providers/model-info.js +58 -0
- package/lib/tokens.d.ts +361 -36
- package/lib/tokens.js +18 -13
- package/lib/tools/commands.d.ts +2 -3
- package/lib/widgets/ai-settings.d.ts +3 -5
- package/lib/widgets/ai-settings.js +12 -0
- package/lib/widgets/main-area-chat.d.ts +2 -3
- package/lib/widgets/main-area-chat.js +12 -12
- package/lib/widgets/provider-config-dialog.d.ts +1 -2
- package/lib/widgets/provider-config-dialog.js +34 -34
- package/package.json +17 -10
- package/schema/settings-model.json +18 -1
- package/src/agent.ts +275 -248
- package/src/chat-model-handler.ts +25 -21
- package/src/chat-model.ts +307 -196
- package/src/completion/completion-provider.ts +7 -4
- package/src/components/completion-status.tsx +3 -3
- package/src/components/index.ts +1 -1
- package/src/components/model-select.tsx +4 -3
- package/src/components/save-button.tsx +84 -0
- package/src/components/tool-select.tsx +10 -4
- package/src/components/usage-display.tsx +208 -0
- package/src/diff-manager.ts +4 -4
- package/src/index.ts +250 -58
- package/src/models/settings-model.ts +46 -88
- package/src/providers/built-in-providers.ts +22 -36
- package/src/providers/generated-context-windows.ts +102 -0
- package/src/providers/model-info.ts +88 -0
- package/src/tokens.ts +438 -58
- package/src/tools/commands.ts +2 -3
- package/src/widgets/ai-settings.tsx +69 -15
- package/src/widgets/main-area-chat.ts +18 -15
- package/src/widgets/provider-config-dialog.tsx +96 -61
- package/style/base.css +17 -195
- package/lib/approval-buttons.d.ts +0 -49
- package/lib/approval-buttons.js +0 -79
- package/lib/components/token-usage-display.js +0 -72
- package/src/approval-buttons.ts +0 -115
- package/src/components/token-usage-display.tsx +0 -138
package/lib/index.js
CHANGED
|
@@ -1,29 +1,31 @@
|
|
|
1
1
|
import { ILabShell, ILayoutRestorer } from '@jupyterlab/application';
|
|
2
2
|
import { ActiveCellManager, AttachmentOpenerRegistry, chatIcon, ChatCommandRegistry, ChatWidget, IChatCommandRegistry, IChatTracker, IInputToolbarRegistryFactory, InputToolbarRegistry, MultiChatPanel } from '@jupyter/chat';
|
|
3
|
-
import { ICommandPalette, IThemeManager, showErrorMessage, WidgetTracker } from '@jupyterlab/apputils';
|
|
3
|
+
import { ICommandPalette, IThemeManager, showDialog, showErrorMessage, WidgetTracker } from '@jupyterlab/apputils';
|
|
4
4
|
import { ICompletionProviderManager } from '@jupyterlab/completer';
|
|
5
5
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
6
|
+
import { FileDialog } from '@jupyterlab/filebrowser';
|
|
6
7
|
import { INotebookTracker } from '@jupyterlab/notebook';
|
|
7
8
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
8
9
|
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
9
10
|
import { IStatusBar } from '@jupyterlab/statusbar';
|
|
10
11
|
import { PathExt } from '@jupyterlab/coreutils';
|
|
11
12
|
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
12
|
-
import { settingsIcon, Toolbar, ToolbarButton } from '@jupyterlab/ui-components';
|
|
13
|
-
import { ISecretsManager, SecretsManager } from 'jupyter-secrets-manager';
|
|
13
|
+
import { fileUploadIcon, saveIcon, settingsIcon, Toolbar, ToolbarButton } from '@jupyterlab/ui-components';
|
|
14
14
|
import { PromiseDelegate, UUID } from '@lumino/coreutils';
|
|
15
15
|
import { DisposableSet } from '@lumino/disposable';
|
|
16
|
+
import { IComponentsRendererFactory } from 'jupyter-chat-components';
|
|
17
|
+
import { ISecretsManager, SecretsManager } from 'jupyter-secrets-manager';
|
|
16
18
|
import { AgentManagerFactory } from './agent';
|
|
17
19
|
import { RenderedMessageOutputAreaCompat } from './rendered-message-outputarea';
|
|
18
20
|
import { ClearCommandProvider } from './chat-commands/clear';
|
|
19
21
|
import { SkillsCommandProvider } from './chat-commands/skills';
|
|
20
22
|
import { ProviderRegistry } from './providers/provider-registry';
|
|
21
|
-
import {
|
|
23
|
+
import { SaveComponentWidget } from './components/save-button';
|
|
22
24
|
import { ChatModelHandler } from './chat-model-handler';
|
|
23
|
-
import { CommandIds, IAgentManagerFactory,
|
|
25
|
+
import { CommandIds, IAgentManagerFactory, IAISettingsModel, IChatModelHandler, IDiffManager, IProviderRegistry, IToolRegistry, ISkillRegistry, SECRETS_NAMESPACE } from './tokens';
|
|
24
26
|
import { anthropicProvider, googleProvider, mistralProvider, openaiProvider, genericProvider } from './providers/built-in-providers';
|
|
25
27
|
import { AICompletionProvider } from './completion';
|
|
26
|
-
import { clearItem, createModelSelectItem, createToolSelectItem, stopItem, CompletionStatusWidget,
|
|
28
|
+
import { clearItem, createModelSelectItem, createToolSelectItem, stopItem, CompletionStatusWidget, UsageWidget } from './components';
|
|
27
29
|
import { AISettingsModel } from './models/settings-model';
|
|
28
30
|
import { loadSkillsFromPaths, SkillRegistry } from './skills';
|
|
29
31
|
import { DiffManager } from './diff-manager';
|
|
@@ -198,8 +200,8 @@ const chatModelHandler = {
|
|
|
198
200
|
],
|
|
199
201
|
optional: [IProviderRegistry, IToolRegistry, ITranslator],
|
|
200
202
|
provides: IChatModelHandler,
|
|
201
|
-
activate: (app, settingsModel, agentManagerFactory, docManager, rmRegistry, providerRegistry, toolRegistry
|
|
202
|
-
|
|
203
|
+
activate: async (app, settingsModel, agentManagerFactory, docManager, rmRegistry, providerRegistry, toolRegistry) => {
|
|
204
|
+
await app.serviceManager.ready;
|
|
203
205
|
return new ChatModelHandler({
|
|
204
206
|
settingsModel,
|
|
205
207
|
agentManagerFactory,
|
|
@@ -207,7 +209,7 @@ const chatModelHandler = {
|
|
|
207
209
|
rmRegistry,
|
|
208
210
|
providerRegistry,
|
|
209
211
|
toolRegistry,
|
|
210
|
-
|
|
212
|
+
contentsManager: app.serviceManager.contents
|
|
211
213
|
});
|
|
212
214
|
}
|
|
213
215
|
};
|
|
@@ -231,9 +233,12 @@ const plugin = {
|
|
|
231
233
|
ILayoutRestorer,
|
|
232
234
|
ILabShell,
|
|
233
235
|
INotebookTracker,
|
|
234
|
-
ITranslator
|
|
236
|
+
ITranslator,
|
|
237
|
+
IComponentsRendererFactory,
|
|
238
|
+
ICommandPalette,
|
|
239
|
+
IDocumentManager
|
|
235
240
|
],
|
|
236
|
-
activate: (app, rmRegistry, inputToolbarFactory, modelHandler, settingsModel, chatCommandRegistry, themeManager, restorer, labShell, notebookTracker, translator) => {
|
|
241
|
+
activate: (app, rmRegistry, inputToolbarFactory, modelHandler, settingsModel, chatCommandRegistry, themeManager, restorer, labShell, notebookTracker, translator, chatComponentsFactory, palette, documentManager) => {
|
|
237
242
|
const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
|
|
238
243
|
// Create attachment opener registry to handle file attachments
|
|
239
244
|
const attachmentOpenerRegistry = new AttachmentOpenerRegistry();
|
|
@@ -286,7 +291,10 @@ const plugin = {
|
|
|
286
291
|
name = `${modelName}-${i}`;
|
|
287
292
|
i += 1;
|
|
288
293
|
}
|
|
289
|
-
const model = modelHandler.createModel(
|
|
294
|
+
const model = modelHandler.createModel({
|
|
295
|
+
name,
|
|
296
|
+
activeProvider: provider
|
|
297
|
+
});
|
|
290
298
|
return { model };
|
|
291
299
|
},
|
|
292
300
|
getChatNames: async () => {
|
|
@@ -330,7 +338,7 @@ const plugin = {
|
|
|
330
338
|
app.commands.commandChanged.connect(onCommandChanged);
|
|
331
339
|
chatPanel.disposed.connect(disconnectSettingsButtonListener);
|
|
332
340
|
}
|
|
333
|
-
let
|
|
341
|
+
let usageWidget = null;
|
|
334
342
|
chatPanel.chatOpened.connect((_, widget) => {
|
|
335
343
|
const model = widget.model;
|
|
336
344
|
// Add the widget to the tracker.
|
|
@@ -343,14 +351,21 @@ const plugin = {
|
|
|
343
351
|
// Update the tracker if the active provider changed.
|
|
344
352
|
model.agentManager.activeProviderChanged.connect(saveTracker);
|
|
345
353
|
// Update the token usage widget.
|
|
346
|
-
|
|
347
|
-
|
|
354
|
+
usageWidget?.dispose();
|
|
355
|
+
usageWidget = new UsageWidget({
|
|
348
356
|
tokenUsageChanged: model.tokenUsageChanged,
|
|
349
357
|
settingsModel,
|
|
350
358
|
initialTokenUsage: model.agentManager.tokenUsage,
|
|
351
359
|
translator: trans
|
|
352
360
|
});
|
|
353
|
-
chatPanel.current?.toolbar.insertBefore('markRead', '
|
|
361
|
+
chatPanel.current?.toolbar.insertBefore('markRead', 'usage', usageWidget);
|
|
362
|
+
if (model.saveAvailable) {
|
|
363
|
+
const saveChatButton = new SaveComponentWidget({
|
|
364
|
+
model,
|
|
365
|
+
translator: trans
|
|
366
|
+
});
|
|
367
|
+
chatPanel.current?.toolbar.insertAfter('markRead', 'saveChat', saveChatButton);
|
|
368
|
+
}
|
|
354
369
|
// Listen for writers change to display the stop button.
|
|
355
370
|
function writersChanged(_, writers) {
|
|
356
371
|
// Check if AI is currently writing (streaming)
|
|
@@ -365,11 +380,6 @@ const plugin = {
|
|
|
365
380
|
}
|
|
366
381
|
}
|
|
367
382
|
model.writersChanged?.connect(writersChanged);
|
|
368
|
-
// Associate an approval buttons object to the chat.
|
|
369
|
-
const approvalButton = new ApprovalButtons({
|
|
370
|
-
chatPanel: widget,
|
|
371
|
-
agentManager: model.agentManager
|
|
372
|
-
});
|
|
373
383
|
// Temporary compat: keep output-area CSS context for MIME renderers
|
|
374
384
|
// until jupyter-chat provides it natively.
|
|
375
385
|
const outputAreaCompat = new RenderedMessageOutputAreaCompat({
|
|
@@ -380,10 +390,15 @@ const plugin = {
|
|
|
380
390
|
model.agentManager.activeProviderChanged.disconnect(saveTracker);
|
|
381
391
|
model.writersChanged?.disconnect(writersChanged);
|
|
382
392
|
// Dispose of the approval buttons widget when the chat is disposed.
|
|
383
|
-
approvalButton.dispose();
|
|
384
393
|
outputAreaCompat.dispose();
|
|
385
394
|
});
|
|
386
395
|
});
|
|
396
|
+
/**
|
|
397
|
+
* Update the available chat list when settings config changed.
|
|
398
|
+
*/
|
|
399
|
+
settingsModel.stateChanged.connect(() => {
|
|
400
|
+
chatPanel.updateChatList();
|
|
401
|
+
});
|
|
387
402
|
app.shell.add(chatPanel, 'left', { rank: 1000 });
|
|
388
403
|
if (restorer) {
|
|
389
404
|
restorer.add(chatPanel, chatPanel.id);
|
|
@@ -406,11 +421,26 @@ const plugin = {
|
|
|
406
421
|
app.commands.execute(CommandIds.openChat);
|
|
407
422
|
}
|
|
408
423
|
});
|
|
409
|
-
registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry, inputToolbarFactory, settingsModel, chatCommandRegistry, tracker, modelHandler, trans, themeManager, labShell);
|
|
424
|
+
registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry, inputToolbarFactory, settingsModel, chatCommandRegistry, tracker, modelHandler, trans, themeManager, labShell, palette, documentManager);
|
|
425
|
+
/**
|
|
426
|
+
* The callback to approve or reject a tool.
|
|
427
|
+
*/
|
|
428
|
+
function toolCallApproval(targetId, approvalId, isApproved) {
|
|
429
|
+
const model = tracker.find(chat => chat.model.name === targetId)?.model;
|
|
430
|
+
if (!model) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
isApproved
|
|
434
|
+
? model.agentManager.approveToolCall(approvalId)
|
|
435
|
+
: model.agentManager.rejectToolCall(approvalId);
|
|
436
|
+
}
|
|
437
|
+
if (chatComponentsFactory) {
|
|
438
|
+
chatComponentsFactory.toolCallApproval = toolCallApproval;
|
|
439
|
+
}
|
|
410
440
|
return tracker;
|
|
411
441
|
}
|
|
412
442
|
};
|
|
413
|
-
function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry, inputToolbarFactory, settingsModel, chatCommandRegistry, tracker, modelRegistry, trans, themeManager, labShell) {
|
|
443
|
+
function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry, inputToolbarFactory, settingsModel, chatCommandRegistry, tracker, modelRegistry, trans, themeManager, labShell, palette, documentManager) {
|
|
414
444
|
const { commands } = app;
|
|
415
445
|
if (labShell) {
|
|
416
446
|
commands.addCommand(CommandIds.reposition, {
|
|
@@ -506,7 +536,10 @@ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry,
|
|
|
506
536
|
if (!name) {
|
|
507
537
|
name = providerConfig.name;
|
|
508
538
|
}
|
|
509
|
-
const model = modelRegistry.createModel(
|
|
539
|
+
const model = modelRegistry.createModel({
|
|
540
|
+
name,
|
|
541
|
+
activeProvider: provider
|
|
542
|
+
});
|
|
510
543
|
if (!model) {
|
|
511
544
|
return false;
|
|
512
545
|
}
|
|
@@ -577,9 +610,12 @@ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry,
|
|
|
577
610
|
// model. The previous is intended to be disposed anyway.
|
|
578
611
|
previousModel.name = UUID.uuid4();
|
|
579
612
|
// Create a new model by duplicating the previous model attributes.
|
|
580
|
-
const model = modelRegistry.createModel(
|
|
581
|
-
|
|
582
|
-
|
|
613
|
+
const model = modelRegistry.createModel({
|
|
614
|
+
name: args.name,
|
|
615
|
+
activeProvider: previousModel.agentManager.activeProvider,
|
|
616
|
+
tokenUsage: previousModel.agentManager.tokenUsage,
|
|
617
|
+
messages: previousModel.messages,
|
|
618
|
+
autosave: previousModel.autosave
|
|
583
619
|
});
|
|
584
620
|
// Wait (with timeout) for the tracker to have updated the previous widget.
|
|
585
621
|
const status = await Promise.any([
|
|
@@ -620,6 +656,128 @@ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry,
|
|
|
620
656
|
}
|
|
621
657
|
}
|
|
622
658
|
});
|
|
659
|
+
commands.addCommand(CommandIds.saveChat, {
|
|
660
|
+
label: args => (args.isPalette ? trans.__('Save chat') : ''),
|
|
661
|
+
caption: trans.__('Save the chat as local file'),
|
|
662
|
+
icon: saveIcon,
|
|
663
|
+
execute: async (args) => {
|
|
664
|
+
let model = null;
|
|
665
|
+
if (args.name) {
|
|
666
|
+
tracker.forEach(widget => {
|
|
667
|
+
if (widget.model.name === args.name) {
|
|
668
|
+
model = widget.model;
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
model = tracker.currentWidget?.model ?? null;
|
|
674
|
+
}
|
|
675
|
+
if (model === null) {
|
|
676
|
+
console.log('No chat to save');
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
model.save();
|
|
680
|
+
return true;
|
|
681
|
+
},
|
|
682
|
+
describedBy: {
|
|
683
|
+
args: {
|
|
684
|
+
type: 'object',
|
|
685
|
+
properties: {
|
|
686
|
+
isPalette: {
|
|
687
|
+
type: 'boolean',
|
|
688
|
+
description: trans.__('Whether the command is in palette')
|
|
689
|
+
},
|
|
690
|
+
name: {
|
|
691
|
+
type: 'string',
|
|
692
|
+
description: trans.__('The name of the chat to save')
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
commands.addCommand(CommandIds.restoreChat, {
|
|
699
|
+
label: args => (args.isPalette ? trans.__('Restore chat') : ''),
|
|
700
|
+
caption: trans.__('Restore the chat from a local file'),
|
|
701
|
+
icon: fileUploadIcon,
|
|
702
|
+
isVisible: () => !!documentManager,
|
|
703
|
+
execute: async (args) => {
|
|
704
|
+
if (!documentManager) {
|
|
705
|
+
console.warn('The restoration is not possible');
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
let model = null;
|
|
709
|
+
if (args.name) {
|
|
710
|
+
tracker.forEach(widget => {
|
|
711
|
+
if (widget.model.name === args.name) {
|
|
712
|
+
model = widget.model;
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
model = tracker.currentWidget?.model ?? null;
|
|
718
|
+
}
|
|
719
|
+
if (model === null) {
|
|
720
|
+
console.warn('There is no chat to restore');
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
let backupDirExists = false;
|
|
724
|
+
await app.serviceManager.contents
|
|
725
|
+
.get(settingsModel.config.chatBackupDirectory, { content: false })
|
|
726
|
+
.then(() => (backupDirExists = true))
|
|
727
|
+
.catch(() => (backupDirExists = false));
|
|
728
|
+
const selection = await FileDialog.getOpenFiles({
|
|
729
|
+
title: trans.__('Select files to attach'),
|
|
730
|
+
manager: documentManager,
|
|
731
|
+
defaultPath: backupDirExists
|
|
732
|
+
? settingsModel.config.chatBackupDirectory
|
|
733
|
+
: ''
|
|
734
|
+
});
|
|
735
|
+
const filepath = selection.value?.[0].path;
|
|
736
|
+
if (!filepath) {
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
739
|
+
if (model.messages.length) {
|
|
740
|
+
const result = await showDialog({
|
|
741
|
+
body: trans.__('All the message will be deleted')
|
|
742
|
+
});
|
|
743
|
+
if (!result.button.accept) {
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return model.restore(filepath);
|
|
748
|
+
},
|
|
749
|
+
describedBy: {
|
|
750
|
+
args: {
|
|
751
|
+
type: 'object',
|
|
752
|
+
properties: {
|
|
753
|
+
isPalette: {
|
|
754
|
+
type: 'boolean',
|
|
755
|
+
description: trans.__('Whether the command is in palette')
|
|
756
|
+
},
|
|
757
|
+
name: {
|
|
758
|
+
type: 'string',
|
|
759
|
+
description: trans.__('The name of the chat to save')
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
if (palette) {
|
|
766
|
+
palette.addItem({
|
|
767
|
+
category: trans.__('AI Assistant'),
|
|
768
|
+
command: CommandIds.saveChat,
|
|
769
|
+
args: {
|
|
770
|
+
isPalette: true
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
palette.addItem({
|
|
774
|
+
category: trans.__('AI Assistant'),
|
|
775
|
+
command: CommandIds.restoreChat,
|
|
776
|
+
args: {
|
|
777
|
+
isPalette: true
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
}
|
|
623
781
|
}
|
|
624
782
|
}
|
|
625
783
|
/**
|
|
@@ -1,61 +1,14 @@
|
|
|
1
1
|
import { VDomModel } from '@jupyterlab/ui-components';
|
|
2
2
|
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
maxOutputTokens?: number;
|
|
6
|
-
maxTurns?: number;
|
|
7
|
-
supportsFillInMiddle?: boolean;
|
|
8
|
-
useFilterText?: boolean;
|
|
9
|
-
}
|
|
10
|
-
export interface IProviderConfig {
|
|
11
|
-
id: string;
|
|
12
|
-
name: string;
|
|
13
|
-
provider: string;
|
|
14
|
-
model: string;
|
|
15
|
-
apiKey?: string;
|
|
16
|
-
baseURL?: string;
|
|
17
|
-
headers?: Record<string, string>;
|
|
18
|
-
parameters?: IProviderParameters;
|
|
19
|
-
customSettings?: Record<string, any>;
|
|
20
|
-
[key: string]: any;
|
|
21
|
-
}
|
|
22
|
-
export interface IMCPServerConfig {
|
|
23
|
-
id: string;
|
|
24
|
-
name: string;
|
|
25
|
-
url: string;
|
|
26
|
-
enabled: boolean;
|
|
27
|
-
[key: string]: any;
|
|
28
|
-
}
|
|
29
|
-
export interface IAIConfig {
|
|
30
|
-
useSecretsManager: boolean;
|
|
31
|
-
providers: IProviderConfig[];
|
|
32
|
-
defaultProvider: string;
|
|
33
|
-
activeCompleterProvider?: string;
|
|
34
|
-
useSameProviderForChatAndCompleter: boolean;
|
|
35
|
-
mcpServers: IMCPServerConfig[];
|
|
36
|
-
contextAwareness: boolean;
|
|
37
|
-
codeExecution: boolean;
|
|
38
|
-
systemPrompt: string;
|
|
39
|
-
completionSystemPrompt: string;
|
|
40
|
-
toolsEnabled: boolean;
|
|
41
|
-
sendWithShiftEnter: boolean;
|
|
42
|
-
showTokenUsage: boolean;
|
|
43
|
-
commandsRequiringApproval: string[];
|
|
44
|
-
commandsAutoRenderMimeBundles: string[];
|
|
45
|
-
trustedMimeTypesForAutoRender: string[];
|
|
46
|
-
showCellDiff: boolean;
|
|
47
|
-
showFileDiff: boolean;
|
|
48
|
-
diffDisplayMode: 'split' | 'unified';
|
|
49
|
-
skillsPaths: string[];
|
|
50
|
-
}
|
|
51
|
-
export declare class AISettingsModel extends VDomModel {
|
|
3
|
+
import { IAIConfig, IAISettingsModel, IMCPServerConfig, IProviderConfig } from '../tokens';
|
|
4
|
+
export declare class AISettingsModel extends VDomModel implements IAISettingsModel {
|
|
52
5
|
private _config;
|
|
53
6
|
private _settingRegistry;
|
|
54
7
|
private _settings;
|
|
55
8
|
constructor(options: AISettingsModel.IOptions);
|
|
56
|
-
private
|
|
57
|
-
private
|
|
58
|
-
private
|
|
9
|
+
private _initializeSettings;
|
|
10
|
+
private _onSettingsChanged;
|
|
11
|
+
private _loadFromSettings;
|
|
59
12
|
get config(): IAIConfig;
|
|
60
13
|
get providers(): IProviderConfig[];
|
|
61
14
|
getProvider(id: string): IProviderConfig | undefined;
|
|
@@ -72,8 +25,13 @@ export declare class AISettingsModel extends VDomModel {
|
|
|
72
25
|
removeMCPServer(id: string): Promise<void>;
|
|
73
26
|
updateMCPServer(id: string, updates: Partial<IMCPServerConfig>): Promise<void>;
|
|
74
27
|
updateConfig(updates: Partial<IAIConfig>): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Get the API key saved in the settings file for a given provider.
|
|
30
|
+
*
|
|
31
|
+
* @param id - the id of the provider.
|
|
32
|
+
*/
|
|
75
33
|
getApiKey(id: string): string;
|
|
76
|
-
private
|
|
34
|
+
private _saveSetting;
|
|
77
35
|
}
|
|
78
36
|
export declare namespace AISettingsModel {
|
|
79
37
|
interface IOptions {
|
|
@@ -13,10 +13,12 @@ export class AISettingsModel extends VDomModel {
|
|
|
13
13
|
toolsEnabled: true,
|
|
14
14
|
sendWithShiftEnter: false,
|
|
15
15
|
showTokenUsage: false,
|
|
16
|
+
showContextUsage: false,
|
|
16
17
|
showCellDiff: true,
|
|
17
18
|
showFileDiff: true,
|
|
18
19
|
diffDisplayMode: 'split',
|
|
19
20
|
skillsPaths: ['.agents/skills', '_agents/skills'],
|
|
21
|
+
chatBackupDirectory: '',
|
|
20
22
|
commandsRequiringApproval: [
|
|
21
23
|
'notebook:restart-run-all',
|
|
22
24
|
'notebook:run-cell',
|
|
@@ -98,6 +100,15 @@ When asked to run code or perform computations, choose the most appropriate appr
|
|
|
98
100
|
|
|
99
101
|
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.
|
|
100
102
|
|
|
103
|
+
## Notebook State and Cell Identity
|
|
104
|
+
When working with an existing notebook, use the notebook's current structure and kernel state as the source of truth.
|
|
105
|
+
- Before changing notebook content or structure, inspect the notebook and any target cells with the relevant notebook commands you have discovered.
|
|
106
|
+
- 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.
|
|
107
|
+
- 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.
|
|
108
|
+
- 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.
|
|
109
|
+
- 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.
|
|
110
|
+
- 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.
|
|
111
|
+
|
|
101
112
|
## Your Approach
|
|
102
113
|
- **Context-aware**: You understand the user is working in a data science/research environment
|
|
103
114
|
- **Practical**: You focus on actionable solutions that work in the user's current setup
|
|
@@ -141,7 +152,7 @@ Guidelines:
|
|
|
141
152
|
|
|
142
153
|
## Multi-Step Task Handling
|
|
143
154
|
When users request complex tasks, you use the command system to accomplish them:
|
|
144
|
-
- For file and notebook operations, use discover_commands with query 'jupyterlab-ai-commands' to find the curated set of AI commands (~
|
|
155
|
+
- For file and notebook operations, use discover_commands with query 'jupyterlab-ai-commands' to find the curated set of AI commands (~22 commands)
|
|
145
156
|
- For other JupyterLab operations (terminal, launcher, UI), use specific keywords like 'terminal', 'launcher', etc.
|
|
146
157
|
- IMPORTANT: Always use 'jupyterlab-ai-commands' as the query for file/notebook tasks - this returns a focused set instead of 100+ generic commands
|
|
147
158
|
- For example, to create a notebook with cells:
|
|
@@ -181,14 +192,14 @@ Rules:
|
|
|
181
192
|
constructor(options) {
|
|
182
193
|
super();
|
|
183
194
|
this._settingRegistry = options.settingRegistry;
|
|
184
|
-
this.
|
|
195
|
+
this._initializeSettings();
|
|
185
196
|
}
|
|
186
|
-
async
|
|
197
|
+
async _initializeSettings() {
|
|
187
198
|
try {
|
|
188
199
|
this._settings = await this._settingRegistry.load(PLUGIN_ID);
|
|
189
|
-
this.
|
|
200
|
+
this._loadFromSettings();
|
|
190
201
|
// Listen for settings changes
|
|
191
|
-
this._settings.changed.connect(this.
|
|
202
|
+
this._settings.changed.connect(this._onSettingsChanged, this);
|
|
192
203
|
this.stateChanged.emit(void 0);
|
|
193
204
|
}
|
|
194
205
|
catch (error) {
|
|
@@ -196,11 +207,11 @@ Rules:
|
|
|
196
207
|
this.stateChanged.emit(void 0);
|
|
197
208
|
}
|
|
198
209
|
}
|
|
199
|
-
|
|
200
|
-
this.
|
|
210
|
+
_onSettingsChanged() {
|
|
211
|
+
this._loadFromSettings();
|
|
201
212
|
this.stateChanged.emit(void 0);
|
|
202
213
|
}
|
|
203
|
-
|
|
214
|
+
_loadFromSettings() {
|
|
204
215
|
if (!this._settings) {
|
|
205
216
|
return;
|
|
206
217
|
}
|
|
@@ -248,13 +259,13 @@ Rules:
|
|
|
248
259
|
// If this is the first provider, make it active
|
|
249
260
|
if (this._config.providers.length === 1) {
|
|
250
261
|
// Save both providers and defaultProvider
|
|
251
|
-
await this.
|
|
262
|
+
await this._saveSetting('providers', this._config.providers);
|
|
252
263
|
this._config.defaultProvider = id;
|
|
253
|
-
await this.
|
|
264
|
+
await this._saveSetting('defaultProvider', this._config.defaultProvider);
|
|
254
265
|
}
|
|
255
266
|
else {
|
|
256
267
|
// Only save providers
|
|
257
|
-
await this.
|
|
268
|
+
await this._saveSetting('providers', this._config.providers);
|
|
258
269
|
}
|
|
259
270
|
return id;
|
|
260
271
|
}
|
|
@@ -264,16 +275,16 @@ Rules:
|
|
|
264
275
|
return;
|
|
265
276
|
}
|
|
266
277
|
this._config.providers.splice(index, 1);
|
|
267
|
-
await this.
|
|
278
|
+
await this._saveSetting('providers', this._config.providers);
|
|
268
279
|
// If this was the active provider, select a new one
|
|
269
280
|
if (this._config.defaultProvider === id) {
|
|
270
281
|
this._config.defaultProvider =
|
|
271
282
|
this._config.providers.length > 0 ? this._config.providers[0].id : '';
|
|
272
|
-
await this.
|
|
283
|
+
await this._saveSetting('defaultProvider', this._config.defaultProvider);
|
|
273
284
|
}
|
|
274
285
|
if (this._config.activeCompleterProvider === id) {
|
|
275
286
|
this._config.activeCompleterProvider = undefined;
|
|
276
|
-
await this.
|
|
287
|
+
await this._saveSetting('activeCompleterProvider', this._config.activeCompleterProvider);
|
|
277
288
|
}
|
|
278
289
|
}
|
|
279
290
|
async updateProvider(id, updates) {
|
|
@@ -287,17 +298,17 @@ Rules:
|
|
|
287
298
|
delete provider[key];
|
|
288
299
|
}
|
|
289
300
|
});
|
|
290
|
-
await this.
|
|
301
|
+
await this._saveSetting('providers', this._config.providers);
|
|
291
302
|
}
|
|
292
303
|
async setActiveProvider(id) {
|
|
293
304
|
if (this.getProvider(id)) {
|
|
294
305
|
this._config.defaultProvider = id;
|
|
295
|
-
await this.
|
|
306
|
+
await this._saveSetting('defaultProvider', this._config.defaultProvider);
|
|
296
307
|
}
|
|
297
308
|
}
|
|
298
309
|
async setActiveCompleterProvider(id) {
|
|
299
310
|
this._config.activeCompleterProvider = id;
|
|
300
|
-
await this.
|
|
311
|
+
await this._saveSetting('activeCompleterProvider', this._config.activeCompleterProvider);
|
|
301
312
|
}
|
|
302
313
|
get mcpServers() {
|
|
303
314
|
return [...this._config.mcpServers];
|
|
@@ -314,7 +325,7 @@ Rules:
|
|
|
314
325
|
enabled: serverConfig.enabled
|
|
315
326
|
};
|
|
316
327
|
this._config.mcpServers.push(newServer);
|
|
317
|
-
await this.
|
|
328
|
+
await this._saveSetting('mcpServers', this._config.mcpServers);
|
|
318
329
|
return id;
|
|
319
330
|
}
|
|
320
331
|
async removeMCPServer(id) {
|
|
@@ -323,7 +334,7 @@ Rules:
|
|
|
323
334
|
return;
|
|
324
335
|
}
|
|
325
336
|
this._config.mcpServers.splice(index, 1);
|
|
326
|
-
await this.
|
|
337
|
+
await this._saveSetting('mcpServers', this._config.mcpServers);
|
|
327
338
|
}
|
|
328
339
|
async updateMCPServer(id, updates) {
|
|
329
340
|
const server = this.getMCPServer(id);
|
|
@@ -331,7 +342,7 @@ Rules:
|
|
|
331
342
|
return;
|
|
332
343
|
}
|
|
333
344
|
Object.assign(server, updates);
|
|
334
|
-
await this.
|
|
345
|
+
await this._saveSetting('mcpServers', this._config.mcpServers);
|
|
335
346
|
}
|
|
336
347
|
async updateConfig(updates) {
|
|
337
348
|
// Update config and save only changed settings
|
|
@@ -340,12 +351,17 @@ Rules:
|
|
|
340
351
|
if (key in this._config &&
|
|
341
352
|
this._config[key] !== value) {
|
|
342
353
|
this._config[key] = value;
|
|
343
|
-
promises.push(this.
|
|
354
|
+
promises.push(this._saveSetting(key, value));
|
|
344
355
|
}
|
|
345
356
|
}
|
|
346
357
|
// Wait for all settings to be saved
|
|
347
358
|
await Promise.all(promises);
|
|
348
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* Get the API key saved in the settings file for a given provider.
|
|
362
|
+
*
|
|
363
|
+
* @param id - the id of the provider.
|
|
364
|
+
*/
|
|
349
365
|
getApiKey(id) {
|
|
350
366
|
// First check the active completer provider
|
|
351
367
|
const activeCompleterProvider = this.getCompleterProvider();
|
|
@@ -359,7 +375,7 @@ Rules:
|
|
|
359
375
|
}
|
|
360
376
|
return '';
|
|
361
377
|
}
|
|
362
|
-
async
|
|
378
|
+
async _saveSetting(key, value) {
|
|
363
379
|
try {
|
|
364
380
|
if (this._settings) {
|
|
365
381
|
// Only save the specific setting that changed
|