@jupyterlite/ai 0.13.0 → 0.14.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 +12 -0
- package/lib/agent.js +88 -0
- package/lib/index.js +125 -55
- package/lib/providers/built-in-providers.js +26 -32
- package/lib/tokens.d.ts +21 -0
- package/lib/tools/commands.js +58 -20
- package/lib/widgets/ai-settings.d.ts +4 -9
- package/lib/widgets/ai-settings.js +15 -48
- package/lib/widgets/provider-config-dialog.js +5 -8
- package/package.json +8 -8
- package/src/agent.ts +107 -0
- package/src/index.ts +210 -106
- package/src/providers/built-in-providers.ts +26 -32
- package/src/tokens.ts +29 -0
- package/src/tools/commands.ts +95 -29
- package/src/widgets/ai-settings.tsx +18 -68
- package/src/widgets/provider-config-dialog.tsx +9 -9
package/lib/agent.d.ts
CHANGED
|
@@ -317,6 +317,18 @@ export declare class AgentManager {
|
|
|
317
317
|
* Build an instruction line describing MIME types supported by this session.
|
|
318
318
|
*/
|
|
319
319
|
private _getSupportedMimeTypesInstruction;
|
|
320
|
+
/**
|
|
321
|
+
* Sanitizes history to ensure it's in a valid state in case of abort or error.
|
|
322
|
+
*/
|
|
323
|
+
private _sanitizeHistory;
|
|
324
|
+
/**
|
|
325
|
+
* Extracts tool call IDs from a message
|
|
326
|
+
*/
|
|
327
|
+
private _getToolCallIds;
|
|
328
|
+
/**
|
|
329
|
+
* Checks if a tool message contains results for all specified tool call IDs
|
|
330
|
+
*/
|
|
331
|
+
private _matchesAllToolCalls;
|
|
320
332
|
private _settingsModel;
|
|
321
333
|
private _toolRegistry?;
|
|
322
334
|
private _providerRegistry?;
|
package/lib/agent.js
CHANGED
|
@@ -33,6 +33,16 @@ export class AgentManagerFactory {
|
|
|
33
33
|
secretsManager: this._secretsManager
|
|
34
34
|
});
|
|
35
35
|
this._agentManagers.push(agentManager);
|
|
36
|
+
// New chats can be created before MCP setup finishes.
|
|
37
|
+
// Reinitialize them with connected MCP tools once it does.
|
|
38
|
+
this._initQueue
|
|
39
|
+
.then(() => this.getMCPTools())
|
|
40
|
+
.then(mcpTools => {
|
|
41
|
+
if (Object.keys(mcpTools).length > 0) {
|
|
42
|
+
agentManager.initializeAgent(mcpTools);
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
.catch(error => console.warn('Failed to pass MCP tools to new agent:', error));
|
|
36
46
|
return agentManager;
|
|
37
47
|
}
|
|
38
48
|
/**
|
|
@@ -406,6 +416,9 @@ export class AgentManager {
|
|
|
406
416
|
data: { error: error }
|
|
407
417
|
});
|
|
408
418
|
}
|
|
419
|
+
// After an error (including AbortError), sanitize the history
|
|
420
|
+
// to remove any trailing assistant messages without tool results
|
|
421
|
+
this._sanitizeHistory();
|
|
409
422
|
}
|
|
410
423
|
finally {
|
|
411
424
|
this._controller = null;
|
|
@@ -838,6 +851,81 @@ WEB RETRIEVAL POLICY:
|
|
|
838
851
|
}
|
|
839
852
|
return `Supported MIME types in this session: ${safeMimeTypes.join(', ')}`;
|
|
840
853
|
}
|
|
854
|
+
/**
|
|
855
|
+
* Sanitizes history to ensure it's in a valid state in case of abort or error.
|
|
856
|
+
*/
|
|
857
|
+
_sanitizeHistory() {
|
|
858
|
+
if (this._history.length === 0) {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
const newHistory = [];
|
|
862
|
+
for (let i = 0; i < this._history.length; i++) {
|
|
863
|
+
const msg = this._history[i];
|
|
864
|
+
if (msg.role === 'assistant') {
|
|
865
|
+
const toolCallIds = this._getToolCallIds(msg);
|
|
866
|
+
if (toolCallIds.length > 0) {
|
|
867
|
+
// Find if there's a following tool message with results for these calls
|
|
868
|
+
const nextMsg = this._history[i + 1];
|
|
869
|
+
if (nextMsg &&
|
|
870
|
+
nextMsg.role === 'tool' &&
|
|
871
|
+
this._matchesAllToolCalls(nextMsg, toolCallIds)) {
|
|
872
|
+
newHistory.push(msg);
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
// Message has unmatched tool calls drop it and everything after it
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
else {
|
|
880
|
+
newHistory.push(msg);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
else if (msg.role === 'tool') {
|
|
884
|
+
// Tool messages are valid if they were preceded by a valid assistant message
|
|
885
|
+
newHistory.push(msg);
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
newHistory.push(msg);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
this._history = newHistory;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Extracts tool call IDs from a message
|
|
895
|
+
*/
|
|
896
|
+
_getToolCallIds(message) {
|
|
897
|
+
const ids = [];
|
|
898
|
+
// Check content array for tool-call parts
|
|
899
|
+
if (Array.isArray(message.content)) {
|
|
900
|
+
for (const part of message.content) {
|
|
901
|
+
if (typeof part === 'object' &&
|
|
902
|
+
part !== null &&
|
|
903
|
+
'type' in part &&
|
|
904
|
+
part.type === 'tool-call') {
|
|
905
|
+
ids.push(part.toolCallId);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
return ids;
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Checks if a tool message contains results for all specified tool call IDs
|
|
913
|
+
*/
|
|
914
|
+
_matchesAllToolCalls(message, callIds) {
|
|
915
|
+
if (message.role !== 'tool' || !Array.isArray(message.content)) {
|
|
916
|
+
return false;
|
|
917
|
+
}
|
|
918
|
+
const resultIds = new Set();
|
|
919
|
+
for (const part of message.content) {
|
|
920
|
+
if (typeof part === 'object' &&
|
|
921
|
+
part !== null &&
|
|
922
|
+
'type' in part &&
|
|
923
|
+
part.type === 'tool-result') {
|
|
924
|
+
resultIds.add(part.toolCallId);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return callIds.every(id => resultIds.has(id));
|
|
928
|
+
}
|
|
841
929
|
// Private attributes
|
|
842
930
|
_settingsModel;
|
|
843
931
|
_toolRegistry;
|
package/lib/index.js
CHANGED
|
@@ -33,6 +33,45 @@ import { createDiscoverSkillsTool, createLoadSkillTool } from './tools/skills';
|
|
|
33
33
|
import { createBrowserFetchTool } from './tools/web';
|
|
34
34
|
import { AISettingsWidget } from './widgets/ai-settings';
|
|
35
35
|
import { MainAreaChat } from './widgets/main-area-chat';
|
|
36
|
+
var Private;
|
|
37
|
+
(function (Private) {
|
|
38
|
+
let aiSecretsToken = null;
|
|
39
|
+
function setAISecretsToken(token) {
|
|
40
|
+
aiSecretsToken = token;
|
|
41
|
+
}
|
|
42
|
+
Private.setAISecretsToken = setAISecretsToken;
|
|
43
|
+
function createAISecretsAccess(secretsManager) {
|
|
44
|
+
return {
|
|
45
|
+
get isAvailable() {
|
|
46
|
+
return !!(aiSecretsToken && secretsManager);
|
|
47
|
+
},
|
|
48
|
+
async get(id) {
|
|
49
|
+
if (!aiSecretsToken || !secretsManager) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const secret = await secretsManager.get(aiSecretsToken, SECRETS_NAMESPACE, id);
|
|
53
|
+
return secret?.value;
|
|
54
|
+
},
|
|
55
|
+
async set(id, value) {
|
|
56
|
+
if (!aiSecretsToken || !secretsManager) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await secretsManager.set(aiSecretsToken, SECRETS_NAMESPACE, id, {
|
|
60
|
+
namespace: SECRETS_NAMESPACE,
|
|
61
|
+
id,
|
|
62
|
+
value
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
async attach(id, input, callback) {
|
|
66
|
+
if (!aiSecretsToken || !secretsManager) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
await secretsManager.attach(aiSecretsToken, SECRETS_NAMESPACE, id, input, callback);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
Private.createAISecretsAccess = createAISecretsAccess;
|
|
74
|
+
})(Private || (Private = {}));
|
|
36
75
|
/**
|
|
37
76
|
* Provider registry plugin
|
|
38
77
|
*/
|
|
@@ -204,6 +243,11 @@ const plugin = {
|
|
|
204
243
|
attachmentOpenerRegistry.set('notebook', attachment => {
|
|
205
244
|
app.commands.execute('docmanager:open', { path: attachment.value });
|
|
206
245
|
});
|
|
246
|
+
const openSettings = () => {
|
|
247
|
+
if (app.commands.hasCommand(CommandIds.openSettings)) {
|
|
248
|
+
void app.commands.execute(CommandIds.openSettings);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
207
251
|
// Create ActiveCellManager if notebook tracker is available, and add it to the
|
|
208
252
|
// model registry.
|
|
209
253
|
let activeCellManager;
|
|
@@ -229,7 +273,7 @@ const plugin = {
|
|
|
229
273
|
provider = settingsModel.getDefaultProvider()?.id;
|
|
230
274
|
if (!provider) {
|
|
231
275
|
showErrorMessage('Error creating chat', 'Please set up a provider');
|
|
232
|
-
|
|
276
|
+
openSettings();
|
|
233
277
|
return {};
|
|
234
278
|
}
|
|
235
279
|
}
|
|
@@ -262,13 +306,30 @@ const plugin = {
|
|
|
262
306
|
chatPanel.title.icon = chatIcon;
|
|
263
307
|
chatPanel.title.caption = trans.__('Chat with AI assistant');
|
|
264
308
|
chatPanel.toolbar.addItem('spacer', Toolbar.createSpacerItem());
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
309
|
+
const addSettingsButton = () => {
|
|
310
|
+
chatPanel.toolbar.addItem('settings', new ToolbarButton({
|
|
311
|
+
icon: settingsIcon,
|
|
312
|
+
onClick: openSettings,
|
|
313
|
+
tooltip: trans.__('Open AI Settings')
|
|
314
|
+
}));
|
|
315
|
+
};
|
|
316
|
+
if (app.commands.hasCommand(CommandIds.openSettings)) {
|
|
317
|
+
addSettingsButton();
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
const disconnectSettingsButtonListener = () => {
|
|
321
|
+
app.commands.commandChanged.disconnect(onCommandChanged);
|
|
322
|
+
chatPanel.disposed.disconnect(disconnectSettingsButtonListener);
|
|
323
|
+
};
|
|
324
|
+
const onCommandChanged = (_, args) => {
|
|
325
|
+
if (args.id === CommandIds.openSettings && args.type === 'added') {
|
|
326
|
+
disconnectSettingsButtonListener();
|
|
327
|
+
addSettingsButton();
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
app.commands.commandChanged.connect(onCommandChanged);
|
|
331
|
+
chatPanel.disposed.connect(disconnectSettingsButtonListener);
|
|
332
|
+
}
|
|
272
333
|
let tokenUsageWidget = null;
|
|
273
334
|
chatPanel.chatOpened.connect((_, widget) => {
|
|
274
335
|
const model = widget.model;
|
|
@@ -562,59 +623,78 @@ function registerCommands(app, rmRegistry, chatPanel, attachmentOpenerRegistry,
|
|
|
562
623
|
}
|
|
563
624
|
}
|
|
564
625
|
/**
|
|
565
|
-
* A plugin to provide the agent manager factory
|
|
566
|
-
* the
|
|
567
|
-
* All these objects require the secrets manager token with the same namespace.
|
|
626
|
+
* A plugin to provide the agent manager factory and completion provider.
|
|
627
|
+
* These objects require the secrets manager token with the same namespace.
|
|
568
628
|
*/
|
|
569
|
-
const agentManagerFactory = SecretsManager.sign(SECRETS_NAMESPACE, token =>
|
|
570
|
-
|
|
571
|
-
|
|
629
|
+
const agentManagerFactory = SecretsManager.sign(SECRETS_NAMESPACE, token => {
|
|
630
|
+
Private.setAISecretsToken(token);
|
|
631
|
+
return {
|
|
632
|
+
id: SECRETS_NAMESPACE,
|
|
633
|
+
description: 'Provide the AI agent manager',
|
|
634
|
+
autoStart: true,
|
|
635
|
+
provides: IAgentManagerFactory,
|
|
636
|
+
requires: [IAISettingsModel, IProviderRegistry],
|
|
637
|
+
optional: [ISkillRegistry, ICompletionProviderManager, ISecretsManager],
|
|
638
|
+
activate: (app, settingsModel, providerRegistry, skillRegistry, completionManager, secretsManager) => {
|
|
639
|
+
const agentManagerFactory = new AgentManagerFactory({
|
|
640
|
+
settingsModel,
|
|
641
|
+
skillRegistry,
|
|
642
|
+
secretsManager,
|
|
643
|
+
token
|
|
644
|
+
});
|
|
645
|
+
// Build the completion provider
|
|
646
|
+
if (completionManager) {
|
|
647
|
+
const completionProvider = new AICompletionProvider({
|
|
648
|
+
settingsModel,
|
|
649
|
+
providerRegistry,
|
|
650
|
+
secretsManager,
|
|
651
|
+
token
|
|
652
|
+
});
|
|
653
|
+
completionManager.registerInlineProvider(completionProvider);
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
console.info('Completion provider manager not available, skipping AI completion setup');
|
|
657
|
+
}
|
|
658
|
+
return agentManagerFactory;
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
});
|
|
662
|
+
/**
|
|
663
|
+
* AI settings panel plugin.
|
|
664
|
+
*/
|
|
665
|
+
const settingsPanelPlugin = {
|
|
666
|
+
id: '@jupyterlite/ai:settings-panel',
|
|
667
|
+
description: 'Provide the AI settings panel',
|
|
572
668
|
autoStart: true,
|
|
573
|
-
|
|
574
|
-
requires: [IAISettingsModel, IProviderRegistry],
|
|
669
|
+
requires: [IAISettingsModel, IAgentManagerFactory, IProviderRegistry],
|
|
575
670
|
optional: [
|
|
576
|
-
ISkillRegistry,
|
|
577
671
|
ICommandPalette,
|
|
578
|
-
ICompletionProviderManager,
|
|
579
672
|
ILayoutRestorer,
|
|
580
673
|
ISecretsManager,
|
|
581
674
|
IThemeManager,
|
|
582
675
|
ITranslator
|
|
583
676
|
],
|
|
584
|
-
activate: (app, settingsModel,
|
|
677
|
+
activate: (app, settingsModel, agentManagerFactory, providerRegistry, palette, restorer, secretsManager, themeManager, translator) => {
|
|
585
678
|
const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
|
|
586
|
-
const
|
|
587
|
-
settingsModel,
|
|
588
|
-
skillRegistry,
|
|
589
|
-
secretsManager,
|
|
590
|
-
token
|
|
591
|
-
});
|
|
592
|
-
// Build the settings panel
|
|
679
|
+
const secretsAccess = Private.createAISecretsAccess(secretsManager);
|
|
593
680
|
const settingsWidget = new AISettingsWidget({
|
|
594
681
|
settingsModel,
|
|
595
682
|
agentManagerFactory,
|
|
596
683
|
themeManager,
|
|
597
684
|
providerRegistry,
|
|
598
|
-
|
|
599
|
-
token,
|
|
685
|
+
secretsAccess,
|
|
600
686
|
trans
|
|
601
687
|
});
|
|
602
|
-
settingsWidget.id = 'jupyterlite-ai-settings';
|
|
603
688
|
settingsWidget.title.icon = settingsIcon;
|
|
604
689
|
settingsWidget.title.iconClass = 'jp-ai-settings-icon';
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
completionManager.registerInlineProvider(completionProvider);
|
|
614
|
-
}
|
|
615
|
-
else {
|
|
616
|
-
console.info('Completion provider manager not available, skipping AI completion setup');
|
|
617
|
-
}
|
|
690
|
+
const open = () => {
|
|
691
|
+
let widget = Array.from(app.shell.widgets('main')).find(w => w.id === 'jupyterlite-ai-settings');
|
|
692
|
+
if (!widget) {
|
|
693
|
+
widget = settingsWidget;
|
|
694
|
+
app.shell.add(widget, 'main');
|
|
695
|
+
}
|
|
696
|
+
app.shell.activateById(widget.id);
|
|
697
|
+
};
|
|
618
698
|
if (restorer) {
|
|
619
699
|
restorer.add(settingsWidget, settingsWidget.id);
|
|
620
700
|
}
|
|
@@ -624,31 +704,20 @@ const agentManagerFactory = SecretsManager.sign(SECRETS_NAMESPACE, token => ({
|
|
|
624
704
|
icon: settingsIcon,
|
|
625
705
|
iconClass: 'jp-ai-settings-icon',
|
|
626
706
|
execute: () => {
|
|
627
|
-
|
|
628
|
-
let widget = Array.from(app.shell.widgets('main')).find(w => w.id === 'jupyterlite-ai-settings');
|
|
629
|
-
if (!widget && settingsWidget) {
|
|
630
|
-
// Use the pre-created widget
|
|
631
|
-
widget = settingsWidget;
|
|
632
|
-
app.shell.add(widget, 'main');
|
|
633
|
-
}
|
|
634
|
-
if (widget) {
|
|
635
|
-
app.shell.activateById(widget.id);
|
|
636
|
-
}
|
|
707
|
+
open();
|
|
637
708
|
},
|
|
638
709
|
describedBy: {
|
|
639
710
|
args: {}
|
|
640
711
|
}
|
|
641
712
|
});
|
|
642
|
-
// Add to command palette if available
|
|
643
713
|
if (palette) {
|
|
644
714
|
palette.addItem({
|
|
645
715
|
command: CommandIds.openSettings,
|
|
646
716
|
category: trans.__('AI Assistant')
|
|
647
717
|
});
|
|
648
718
|
}
|
|
649
|
-
return agentManagerFactory;
|
|
650
719
|
}
|
|
651
|
-
}
|
|
720
|
+
};
|
|
652
721
|
/**
|
|
653
722
|
* Built-in completion providers plugin
|
|
654
723
|
*/
|
|
@@ -894,6 +963,7 @@ export default [
|
|
|
894
963
|
plugin,
|
|
895
964
|
toolRegistry,
|
|
896
965
|
agentManagerFactory,
|
|
966
|
+
settingsPanelPlugin,
|
|
897
967
|
inputToolbarFactory,
|
|
898
968
|
completionStatus,
|
|
899
969
|
skillsPlugin
|
|
@@ -12,6 +12,7 @@ export const anthropicProvider = {
|
|
|
12
12
|
apiKeyRequirement: 'required',
|
|
13
13
|
defaultModels: [
|
|
14
14
|
'claude-opus-4-6',
|
|
15
|
+
'claude-sonnet-4-6',
|
|
15
16
|
'claude-opus-4-5',
|
|
16
17
|
'claude-opus-4-5-20251101',
|
|
17
18
|
'claude-sonnet-4-5',
|
|
@@ -24,10 +25,6 @@ export const anthropicProvider = {
|
|
|
24
25
|
'claude-opus-4-20250514',
|
|
25
26
|
'claude-sonnet-4-0',
|
|
26
27
|
'claude-sonnet-4-20250514',
|
|
27
|
-
'claude-3-7-sonnet-latest',
|
|
28
|
-
'claude-3-7-sonnet-20250219',
|
|
29
|
-
'claude-3-5-haiku-latest',
|
|
30
|
-
'claude-3-5-haiku-20241022',
|
|
31
28
|
'claude-3-haiku-20240307'
|
|
32
29
|
],
|
|
33
30
|
supportsBaseURL: true,
|
|
@@ -60,6 +57,9 @@ export const googleProvider = {
|
|
|
60
57
|
name: 'Google Generative AI',
|
|
61
58
|
apiKeyRequirement: 'required',
|
|
62
59
|
defaultModels: [
|
|
60
|
+
'gemini-3.1-pro-preview',
|
|
61
|
+
'gemini-3.1-pro-preview-customtools',
|
|
62
|
+
'gemini-3.1-flash-image-preview',
|
|
63
63
|
'gemini-3-pro-preview',
|
|
64
64
|
'gemini-3-pro-image-preview',
|
|
65
65
|
'gemini-3-flash-preview',
|
|
@@ -68,33 +68,21 @@ export const googleProvider = {
|
|
|
68
68
|
'gemini-2.5-flash-image',
|
|
69
69
|
'gemini-2.5-flash-lite',
|
|
70
70
|
'gemini-2.5-flash-lite-preview-09-2025',
|
|
71
|
-
'gemini-2.5-
|
|
72
|
-
'gemini-2.5-flash-preview-09-2025',
|
|
73
|
-
'gemini-2.5-pro-exp-03-25',
|
|
71
|
+
'gemini-2.5-computer-use-preview-10-2025',
|
|
74
72
|
'gemini-2.0-flash',
|
|
75
73
|
'gemini-2.0-flash-001',
|
|
76
|
-
'gemini-2.0-flash-live-001',
|
|
77
74
|
'gemini-2.0-flash-lite',
|
|
78
|
-
'gemini-2.0-
|
|
79
|
-
'gemini-2.0-flash-thinking-exp-01-21',
|
|
80
|
-
'gemini-2.0-flash-exp',
|
|
81
|
-
'gemini-1.5-flash',
|
|
82
|
-
'gemini-1.5-flash-latest',
|
|
83
|
-
'gemini-1.5-flash-001',
|
|
84
|
-
'gemini-1.5-flash-002',
|
|
85
|
-
'gemini-1.5-flash-8b',
|
|
86
|
-
'gemini-1.5-flash-8b-latest',
|
|
87
|
-
'gemini-1.5-flash-8b-001',
|
|
88
|
-
'gemini-1.5-pro',
|
|
89
|
-
'gemini-1.5-pro-latest',
|
|
90
|
-
'gemini-1.5-pro-001',
|
|
91
|
-
'gemini-1.5-pro-002',
|
|
75
|
+
'gemini-2.0-flash-lite-001',
|
|
92
76
|
'gemini-pro-latest',
|
|
93
77
|
'gemini-flash-latest',
|
|
94
78
|
'gemini-flash-lite-latest',
|
|
95
|
-
'
|
|
79
|
+
'deep-research-pro-preview-12-2025',
|
|
80
|
+
'gemma-3-27b-it',
|
|
96
81
|
'gemma-3-12b-it',
|
|
97
|
-
'gemma-3-
|
|
82
|
+
'gemma-3-4b-it',
|
|
83
|
+
'gemma-3-1b-it',
|
|
84
|
+
'gemma-3n-e4b-it',
|
|
85
|
+
'gemma-3n-e2b-it'
|
|
98
86
|
],
|
|
99
87
|
supportsBaseURL: true,
|
|
100
88
|
factory: (options) => {
|
|
@@ -157,10 +145,13 @@ export const openaiProvider = {
|
|
|
157
145
|
apiKeyRequirement: 'required',
|
|
158
146
|
defaultModels: [
|
|
159
147
|
'gpt-5.2',
|
|
148
|
+
'gpt-5.2-2025-12-11',
|
|
160
149
|
'gpt-5.2-chat-latest',
|
|
161
150
|
'gpt-5.2-pro',
|
|
151
|
+
'gpt-5.2-pro-2025-12-11',
|
|
162
152
|
'gpt-5.2-codex',
|
|
163
153
|
'gpt-5.1',
|
|
154
|
+
'gpt-5.1-2025-11-13',
|
|
164
155
|
'gpt-5.1-chat-latest',
|
|
165
156
|
'gpt-5',
|
|
166
157
|
'gpt-5-2025-08-07',
|
|
@@ -177,8 +168,6 @@ export const openaiProvider = {
|
|
|
177
168
|
'o3-mini-2025-01-31',
|
|
178
169
|
'o1',
|
|
179
170
|
'o1-2024-12-17',
|
|
180
|
-
'gpt-4.5-preview',
|
|
181
|
-
'gpt-4.5-preview-2025-02-27',
|
|
182
171
|
'gpt-4.1',
|
|
183
172
|
'gpt-4.1-2025-04-14',
|
|
184
173
|
'gpt-4.1-mini',
|
|
@@ -189,16 +178,21 @@ export const openaiProvider = {
|
|
|
189
178
|
'gpt-4o-2024-05-13',
|
|
190
179
|
'gpt-4o-2024-08-06',
|
|
191
180
|
'gpt-4o-2024-11-20',
|
|
181
|
+
'gpt-4o-audio-preview',
|
|
182
|
+
'gpt-4o-audio-preview-2024-12-17',
|
|
183
|
+
'gpt-4o-audio-preview-2025-06-03',
|
|
192
184
|
'gpt-4o-mini',
|
|
193
185
|
'gpt-4o-mini-2024-07-18',
|
|
194
|
-
'
|
|
195
|
-
'gpt-
|
|
196
|
-
'gpt-
|
|
197
|
-
'gpt-
|
|
198
|
-
'gpt-
|
|
186
|
+
'gpt-4o-mini-audio-preview',
|
|
187
|
+
'gpt-4o-mini-audio-preview-2024-12-17',
|
|
188
|
+
'gpt-4o-search-preview',
|
|
189
|
+
'gpt-4o-search-preview-2025-03-11',
|
|
190
|
+
'gpt-4o-mini-search-preview',
|
|
191
|
+
'gpt-4o-mini-search-preview-2025-03-11',
|
|
199
192
|
'gpt-3.5-turbo',
|
|
200
193
|
'gpt-3.5-turbo-0125',
|
|
201
|
-
'gpt-3.5-turbo-1106'
|
|
194
|
+
'gpt-3.5-turbo-1106',
|
|
195
|
+
'gpt-3.5-turbo-16k'
|
|
202
196
|
],
|
|
203
197
|
supportsBaseURL: true,
|
|
204
198
|
supportsHeaders: true,
|
package/lib/tokens.d.ts
CHANGED
|
@@ -253,6 +253,27 @@ export interface IProviderRegistry {
|
|
|
253
253
|
* Token for the AI settings model.
|
|
254
254
|
*/
|
|
255
255
|
export declare const IAISettingsModel: Token<AISettingsModel>;
|
|
256
|
+
/**
|
|
257
|
+
* Internal interface for AI provider secret access within the shared namespace.
|
|
258
|
+
*/
|
|
259
|
+
export interface IAISecretsAccess {
|
|
260
|
+
/**
|
|
261
|
+
* Whether secrets access is currently available.
|
|
262
|
+
*/
|
|
263
|
+
readonly isAvailable: boolean;
|
|
264
|
+
/**
|
|
265
|
+
* Get a secret value by ID.
|
|
266
|
+
*/
|
|
267
|
+
get(id: string): Promise<string | undefined>;
|
|
268
|
+
/**
|
|
269
|
+
* Set a secret value by ID.
|
|
270
|
+
*/
|
|
271
|
+
set(id: string, value: string): Promise<void>;
|
|
272
|
+
/**
|
|
273
|
+
* Attach an input field to a secret ID.
|
|
274
|
+
*/
|
|
275
|
+
attach(id: string, input: HTMLInputElement, callback?: (value: string) => void): Promise<void>;
|
|
276
|
+
}
|
|
256
277
|
/**
|
|
257
278
|
* Token for the agent manager.
|
|
258
279
|
*/
|
package/lib/tools/commands.js
CHANGED
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
import { Widget } from '@lumino/widgets';
|
|
2
2
|
import { tool } from 'ai';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
+
/**
|
|
5
|
+
* Search commands using case-insensitive term matching across command metadata.
|
|
6
|
+
*
|
|
7
|
+
* Multi-word queries are split into individual terms, and every term must be
|
|
8
|
+
* contained in at least one searchable field. Results are ranked by stronger
|
|
9
|
+
* field matches while keeping a stable fallback order.
|
|
10
|
+
*/
|
|
11
|
+
function searchCommands(commands, query) {
|
|
12
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
13
|
+
if (!normalizedQuery) {
|
|
14
|
+
return commands;
|
|
15
|
+
}
|
|
16
|
+
const terms = normalizedQuery.split(/\s+/).filter(Boolean);
|
|
17
|
+
return commands
|
|
18
|
+
.map((command, index) => {
|
|
19
|
+
const fields = [
|
|
20
|
+
{ value: command.label, weight: 4 },
|
|
21
|
+
{ value: command.caption, weight: 3 },
|
|
22
|
+
{ value: command.id, weight: 2 },
|
|
23
|
+
{ value: command.description, weight: 1 }
|
|
24
|
+
];
|
|
25
|
+
const normalizedFields = fields.map(field => ({
|
|
26
|
+
normalizedValue: field.value?.toLowerCase() ?? '',
|
|
27
|
+
weight: field.weight
|
|
28
|
+
}));
|
|
29
|
+
const matchesAllTerms = terms.every(term => normalizedFields.some(field => field.normalizedValue.includes(term)));
|
|
30
|
+
if (!matchesAllTerms) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const score = normalizedFields.reduce((total, field) => {
|
|
34
|
+
if (!field.normalizedValue) {
|
|
35
|
+
return total;
|
|
36
|
+
}
|
|
37
|
+
let fieldScore = 0;
|
|
38
|
+
if (field.normalizedValue.includes(normalizedQuery)) {
|
|
39
|
+
fieldScore += field.weight * 4;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
fieldScore +=
|
|
43
|
+
terms.filter(term => field.normalizedValue.includes(term)).length *
|
|
44
|
+
field.weight;
|
|
45
|
+
}
|
|
46
|
+
return total + fieldScore;
|
|
47
|
+
}, 0);
|
|
48
|
+
return { command, index, score };
|
|
49
|
+
})
|
|
50
|
+
.filter((result) => result !== null)
|
|
51
|
+
.sort((a, b) => b.score - a.score || a.index - b.index)
|
|
52
|
+
.map(result => result.command);
|
|
53
|
+
}
|
|
4
54
|
/**
|
|
5
55
|
* Create a tool to discover all available commands and their metadata
|
|
6
56
|
*/
|
|
@@ -13,41 +63,29 @@ export function createDiscoverCommandsTool(commands) {
|
|
|
13
63
|
.string()
|
|
14
64
|
.optional()
|
|
15
65
|
.nullable()
|
|
16
|
-
.describe(
|
|
66
|
+
.describe('Optional search query to filter commands. Supports multi-word queries (whitespace-separated) by requiring each word to be contained in the command id, label, caption, or description. Leave empty to list all commands.')
|
|
17
67
|
}),
|
|
18
68
|
execute: async (input) => {
|
|
19
69
|
const { query } = input;
|
|
20
|
-
|
|
21
|
-
// Get all command IDs
|
|
70
|
+
// Build the full command list first.
|
|
22
71
|
const commandIds = commands.listCommands();
|
|
72
|
+
const allCommands = [];
|
|
23
73
|
for (const id of commandIds) {
|
|
24
|
-
// Get command metadata using various CommandRegistry methods
|
|
25
74
|
const description = await commands.describedBy(id);
|
|
26
75
|
const label = commands.label(id);
|
|
27
76
|
const caption = commands.caption(id);
|
|
28
77
|
const usage = commands.usage(id);
|
|
29
|
-
|
|
78
|
+
allCommands.push({
|
|
30
79
|
id,
|
|
31
80
|
label: label || undefined,
|
|
32
81
|
caption: caption || undefined,
|
|
33
82
|
description: usage || undefined,
|
|
34
83
|
args: description?.args || undefined
|
|
35
|
-
};
|
|
36
|
-
// Filter by query if provided
|
|
37
|
-
if (query) {
|
|
38
|
-
const searchTerm = query.toLowerCase();
|
|
39
|
-
const matchesQuery = id.toLowerCase().includes(searchTerm) ||
|
|
40
|
-
label?.toLowerCase().includes(searchTerm) ||
|
|
41
|
-
caption?.toLowerCase().includes(searchTerm) ||
|
|
42
|
-
usage?.toLowerCase().includes(searchTerm);
|
|
43
|
-
if (matchesQuery) {
|
|
44
|
-
commandList.push(command);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
commandList.push(command);
|
|
49
|
-
}
|
|
84
|
+
});
|
|
50
85
|
}
|
|
86
|
+
const commandList = query
|
|
87
|
+
? searchCommands(allCommands, query)
|
|
88
|
+
: allCommands;
|
|
51
89
|
return {
|
|
52
90
|
success: true,
|
|
53
91
|
commandCount: commandList.length,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { IThemeManager } from '@jupyterlab/apputils';
|
|
2
2
|
import { ReactWidget } from '@jupyterlab/ui-components';
|
|
3
3
|
import type { TranslationBundle } from '@jupyterlab/translation';
|
|
4
|
-
import { ISecretsManager } from 'jupyter-secrets-manager';
|
|
5
4
|
import React from 'react';
|
|
6
5
|
import { AgentManagerFactory } from '../agent';
|
|
7
6
|
import { AISettingsModel } from '../models/settings-model';
|
|
8
|
-
import { type IProviderRegistry } from '../tokens';
|
|
7
|
+
import { type IAISecretsAccess, type IProviderRegistry } from '../tokens';
|
|
9
8
|
/**
|
|
10
9
|
* A JupyterLab widget for AI settings configuration
|
|
11
10
|
*/
|
|
@@ -24,7 +23,7 @@ export declare class AISettingsWidget extends ReactWidget {
|
|
|
24
23
|
private _agentManagerFactory?;
|
|
25
24
|
private _themeManager?;
|
|
26
25
|
private _providerRegistry;
|
|
27
|
-
private
|
|
26
|
+
private _secretsAccess?;
|
|
28
27
|
private _trans;
|
|
29
28
|
}
|
|
30
29
|
/**
|
|
@@ -40,13 +39,9 @@ export declare namespace AISettingsWidget {
|
|
|
40
39
|
themeManager?: IThemeManager;
|
|
41
40
|
providerRegistry: IProviderRegistry;
|
|
42
41
|
/**
|
|
43
|
-
*
|
|
42
|
+
* Access to provider secrets in the shared namespace.
|
|
44
43
|
*/
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* The token used to request the secrets manager.
|
|
48
|
-
*/
|
|
49
|
-
token: symbol | null;
|
|
44
|
+
secretsAccess: IAISecretsAccess;
|
|
50
45
|
/**
|
|
51
46
|
* The application language translation bundle.
|
|
52
47
|
*/
|