@jupyterlite/ai 0.10.0 → 0.11.1
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.js +17 -10
- package/lib/chat-model-registry.d.ts +6 -1
- package/lib/chat-model-registry.js +12 -0
- package/lib/chat-model.d.ts +7 -0
- package/lib/chat-model.js +37 -3
- package/lib/components/clear-button.js +2 -3
- package/lib/components/model-select.js +4 -8
- package/lib/components/stop-button.js +4 -5
- package/lib/components/tool-select.js +4 -4
- package/lib/index.d.ts +1 -0
- package/lib/index.js +23 -56
- package/lib/models/settings-model.js +40 -26
- package/lib/tokens.d.ts +2 -0
- package/lib/tools/commands.js +6 -10
- package/package.json +10 -10
- package/schema/settings-model.json +3 -2
- package/src/agent.ts +18 -11
- package/src/chat-model-registry.ts +14 -1
- package/src/chat-model.ts +47 -3
- package/src/components/clear-button.tsx +2 -3
- package/src/components/model-select.tsx +4 -8
- package/src/components/stop-button.tsx +6 -6
- package/src/components/tool-select.tsx +4 -4
- package/src/index.ts +23 -110
- package/src/models/settings-model.ts +40 -26
- package/src/tokens.ts +2 -0
- package/src/tools/commands.ts +10 -12
- package/style/base.css +11 -0
- package/lib/tools/file.d.ts +0 -36
- package/lib/tools/file.js +0 -327
- package/lib/tools/notebook.d.ts +0 -40
- package/lib/tools/notebook.js +0 -704
- package/src/tools/file.ts +0 -412
- package/src/tools/notebook.ts +0 -927
package/lib/agent.js
CHANGED
|
@@ -654,17 +654,24 @@ Guidelines:
|
|
|
654
654
|
- End with a brief summary of accomplishments
|
|
655
655
|
- Use natural, conversational tone throughout
|
|
656
656
|
|
|
657
|
-
COMMAND
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
- This ensures you have complete information about command IDs, descriptions, and required arguments before attempting to execute them. Only after discovering the available commands should you use the 'execute_command' tool with the correct command ID and arguments.
|
|
657
|
+
PRIMARY TOOL USAGE - COMMAND-BASED OPERATIONS:
|
|
658
|
+
Most operations in JupyterLab should be performed using the command system:
|
|
659
|
+
1. Use 'discover_commands' to find available commands and their metadata
|
|
660
|
+
2. Use 'execute_command' to perform the actual operation
|
|
662
661
|
|
|
663
|
-
|
|
664
|
-
- For file
|
|
665
|
-
- For
|
|
666
|
-
-
|
|
667
|
-
|
|
662
|
+
COMMAND DISCOVERY WORKFLOW:
|
|
663
|
+
- For file and notebook operations, use query 'jupyterlab-ai-commands' to discover the curated set of AI commands (~17 commands for file/notebook/directory operations)
|
|
664
|
+
- For other JupyterLab operations (terminal, launcher, UI), use specific keywords like 'terminal', 'launcher', etc.
|
|
665
|
+
- IMPORTANT: Always use 'jupyterlab-ai-commands' as the query for file/notebook tasks - this returns a focused set of commands instead of 100+ generic JupyterLab commands
|
|
666
|
+
|
|
667
|
+
KERNEL PREFERENCE FOR NOTEBOOKS AND CONSOLES:
|
|
668
|
+
When creating notebooks or consoles for a specific programming language, use the 'kernelPreference' argument to specify the kernel:
|
|
669
|
+
- To specify by language: { "kernelPreference": { "language": "python" } } or { "kernelPreference": { "language": "julia" } }
|
|
670
|
+
- To specify by kernel name: { "kernelPreference": { "name": "python3" } } or { "kernelPreference": { "name": "julia-1.10" } }
|
|
671
|
+
- Example: execute_command with commandId="notebook:create-new" and args={ "kernelPreference": { "language": "python" } }
|
|
672
|
+
- Example: execute_command with commandId="console:create" and args={ "kernelPreference": { "name": "python3" } }
|
|
673
|
+
- Common kernel names: "python3" (Python), "julia-1.10" (Julia), "ir" (R), "xpython" (xeus-python)
|
|
674
|
+
- If unsure of exact kernel name, prefer using "language" which will match any kernel supporting that language
|
|
668
675
|
`;
|
|
669
676
|
return baseSystemPrompt + progressReportingPrompt;
|
|
670
677
|
}
|
|
@@ -15,6 +15,11 @@ export declare class ChatModelRegistry implements IChatModelRegistry {
|
|
|
15
15
|
get(name: string): AIChatModel | undefined;
|
|
16
16
|
getAll(): AIChatModel[];
|
|
17
17
|
remove(name: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Getter/setter for the active cell manager.
|
|
20
|
+
*/
|
|
21
|
+
get activeCellManager(): ActiveCellManager | undefined;
|
|
22
|
+
set activeCellManager(manager: ActiveCellManager | undefined);
|
|
18
23
|
private _models;
|
|
19
24
|
private _docManager;
|
|
20
25
|
private _agentManagerFactory;
|
|
@@ -49,7 +54,7 @@ export declare namespace ChatModelRegistry {
|
|
|
49
54
|
/**
|
|
50
55
|
* The active cell manager.
|
|
51
56
|
*/
|
|
52
|
-
activeCellManager
|
|
57
|
+
activeCellManager?: ActiveCellManager | undefined;
|
|
53
58
|
/**
|
|
54
59
|
* The application language translation bundle.
|
|
55
60
|
*/
|
|
@@ -52,6 +52,9 @@ export class ChatModelRegistry {
|
|
|
52
52
|
add(model) {
|
|
53
53
|
if (!this._models.find(m => m.name === model.name)) {
|
|
54
54
|
this._models.push(model);
|
|
55
|
+
model.disposed.connect(() => {
|
|
56
|
+
this.remove(model.name);
|
|
57
|
+
});
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
get(name) {
|
|
@@ -66,6 +69,15 @@ export class ChatModelRegistry {
|
|
|
66
69
|
this._models.splice(index, 1);
|
|
67
70
|
}
|
|
68
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Getter/setter for the active cell manager.
|
|
74
|
+
*/
|
|
75
|
+
get activeCellManager() {
|
|
76
|
+
return this._activeCellManager;
|
|
77
|
+
}
|
|
78
|
+
set activeCellManager(manager) {
|
|
79
|
+
this._activeCellManager = manager;
|
|
80
|
+
}
|
|
69
81
|
_models = [];
|
|
70
82
|
_docManager;
|
|
71
83
|
_agentManagerFactory;
|
package/lib/chat-model.d.ts
CHANGED
|
@@ -81,6 +81,13 @@ export declare class AIChatModel extends AbstractChatModel {
|
|
|
81
81
|
* @param event Event containing the message completion data
|
|
82
82
|
*/
|
|
83
83
|
private _handleMessageComplete;
|
|
84
|
+
/**
|
|
85
|
+
* Extracts a human-readable summary from tool input for display in the header.
|
|
86
|
+
* @param toolName The name of the tool being called
|
|
87
|
+
* @param input The formatted JSON input string
|
|
88
|
+
* @returns A short summary string or empty string if none available
|
|
89
|
+
*/
|
|
90
|
+
private _extractToolSummary;
|
|
84
91
|
/**
|
|
85
92
|
* Handles the start of a tool call execution.
|
|
86
93
|
* @param event Event containing the tool call start data
|
package/lib/chat-model.js
CHANGED
|
@@ -242,18 +242,47 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
242
242
|
this._currentStreamingMessage = null;
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Extracts a human-readable summary from tool input for display in the header.
|
|
247
|
+
* @param toolName The name of the tool being called
|
|
248
|
+
* @param input The formatted JSON input string
|
|
249
|
+
* @returns A short summary string or empty string if none available
|
|
250
|
+
*/
|
|
251
|
+
_extractToolSummary(toolName, input) {
|
|
252
|
+
try {
|
|
253
|
+
const parsedInput = JSON.parse(input);
|
|
254
|
+
switch (toolName) {
|
|
255
|
+
case 'execute_command':
|
|
256
|
+
if (parsedInput.commandId) {
|
|
257
|
+
return parsedInput.commandId;
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
case 'discover_commands':
|
|
261
|
+
if (parsedInput.query) {
|
|
262
|
+
return `query: "${parsedInput.query}"`;
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
// If parsing fails, return empty string
|
|
269
|
+
}
|
|
270
|
+
return '';
|
|
271
|
+
}
|
|
245
272
|
/**
|
|
246
273
|
* Handles the start of a tool call execution.
|
|
247
274
|
* @param event Event containing the tool call start data
|
|
248
275
|
*/
|
|
249
276
|
_handleToolCallStartEvent(event) {
|
|
250
277
|
const messageId = UUID.uuid4();
|
|
278
|
+
const summary = this._extractToolSummary(event.data.toolName, event.data.input);
|
|
251
279
|
const context = {
|
|
252
280
|
toolCallId: event.data.callId,
|
|
253
281
|
messageId,
|
|
254
282
|
toolName: event.data.toolName,
|
|
255
283
|
input: event.data.input,
|
|
256
|
-
status: 'pending'
|
|
284
|
+
status: 'pending',
|
|
285
|
+
summary
|
|
257
286
|
};
|
|
258
287
|
this._toolContexts.set(event.data.callId, context);
|
|
259
288
|
const toolCallMessage = {
|
|
@@ -261,6 +290,7 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
261
290
|
toolName: context.toolName,
|
|
262
291
|
input: context.input,
|
|
263
292
|
status: context.status,
|
|
293
|
+
summary: context.summary,
|
|
264
294
|
trans: this._trans
|
|
265
295
|
}),
|
|
266
296
|
sender: this._getAIUser(),
|
|
@@ -337,6 +367,7 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
337
367
|
toolName: context.toolName,
|
|
338
368
|
input: context.input,
|
|
339
369
|
status: context.status,
|
|
370
|
+
summary: context.summary,
|
|
340
371
|
output,
|
|
341
372
|
approvalId: context.approvalId,
|
|
342
373
|
trans: this._trans
|
|
@@ -642,12 +673,15 @@ var Private;
|
|
|
642
673
|
* Builds HTML for a tool call display.
|
|
643
674
|
*/
|
|
644
675
|
function buildToolCallHtml(options) {
|
|
645
|
-
const { toolName, input, status, output, approvalId, trans } = options;
|
|
676
|
+
const { toolName, input, status, summary, output, approvalId, trans } = options;
|
|
646
677
|
const config = STATUS_CONFIG[status];
|
|
647
678
|
const statusText = getStatusText(status, trans);
|
|
648
679
|
const escapedToolName = escapeHtml(toolName);
|
|
649
680
|
const escapedInput = escapeHtml(input);
|
|
650
681
|
const openAttr = config.open ? ' open' : '';
|
|
682
|
+
const summaryHtml = summary
|
|
683
|
+
? `<span class="jp-ai-tool-summary">${escapeHtml(summary)}</span>`
|
|
684
|
+
: '';
|
|
651
685
|
let bodyContent = `
|
|
652
686
|
<div class="jp-ai-tool-section">
|
|
653
687
|
<div class="jp-ai-tool-label">${trans.__('Input')}</div>
|
|
@@ -674,7 +708,7 @@ var Private;
|
|
|
674
708
|
return `<details class="jp-ai-tool-call ${config.cssClass}"${openAttr}>
|
|
675
709
|
<summary class="jp-ai-tool-header">
|
|
676
710
|
<div class="jp-ai-tool-icon">⚡</div>
|
|
677
|
-
<div class="jp-ai-tool-title">${escapedToolName}</div>
|
|
711
|
+
<div class="jp-ai-tool-title">${escapedToolName}${summaryHtml}</div>
|
|
678
712
|
<div class="jp-ai-tool-status ${config.statusClass}">${statusText}</div>
|
|
679
713
|
</summary>
|
|
680
714
|
<div class="jp-ai-tool-body">${bodyContent}
|
|
@@ -8,10 +8,9 @@ export function ClearButton(props) {
|
|
|
8
8
|
const { translator: trans } = props;
|
|
9
9
|
const tooltip = trans.__('Clear chat');
|
|
10
10
|
return (React.createElement(TooltippedButton, { onClick: props.clearMessages, tooltip: tooltip, buttonProps: {
|
|
11
|
-
|
|
11
|
+
title: tooltip,
|
|
12
12
|
variant: 'outlined',
|
|
13
|
-
color: 'secondary'
|
|
14
|
-
title: tooltip
|
|
13
|
+
color: 'secondary'
|
|
15
14
|
} },
|
|
16
15
|
React.createElement(ClearIcon, null)));
|
|
17
16
|
}
|
|
@@ -61,16 +61,15 @@ export function ModelSelect(props) {
|
|
|
61
61
|
// Show a message if no providers are configured
|
|
62
62
|
if (availableModels.length === 0) {
|
|
63
63
|
return (React.createElement(TooltippedButton, { onClick: () => { }, tooltip: trans.__('No providers configured. Please go to AI Settings to add a provider.'), buttonProps: {
|
|
64
|
-
size: 'small',
|
|
65
64
|
variant: 'outlined',
|
|
66
65
|
color: 'warning',
|
|
67
66
|
disabled: true,
|
|
68
67
|
title: trans.__('No Providers Available')
|
|
69
68
|
}, sx: {
|
|
70
69
|
minWidth: 'auto',
|
|
70
|
+
width: 'unset',
|
|
71
71
|
display: 'flex',
|
|
72
|
-
alignItems: 'center'
|
|
73
|
-
height: '29px'
|
|
72
|
+
alignItems: 'center'
|
|
74
73
|
} },
|
|
75
74
|
React.createElement(Typography, { variant: "caption", sx: { fontSize: '0.7rem', fontWeight: 500 } }, trans.__('No Providers'))));
|
|
76
75
|
}
|
|
@@ -78,9 +77,6 @@ export function ModelSelect(props) {
|
|
|
78
77
|
React.createElement(TooltippedButton, { onClick: e => {
|
|
79
78
|
openMenu(e.currentTarget);
|
|
80
79
|
}, tooltip: trans.__('Current Model: %1 - %2', currentProviderLabel, currentModel), buttonProps: {
|
|
81
|
-
size: 'small',
|
|
82
|
-
variant: 'contained',
|
|
83
|
-
color: 'primary',
|
|
84
80
|
title: trans.__('Select AI Model'),
|
|
85
81
|
onKeyDown: e => {
|
|
86
82
|
if (e.key !== 'Enter' && e.key !== ' ') {
|
|
@@ -92,9 +88,9 @@ export function ModelSelect(props) {
|
|
|
92
88
|
}
|
|
93
89
|
}, sx: {
|
|
94
90
|
minWidth: 'auto',
|
|
91
|
+
width: 'unset',
|
|
95
92
|
display: 'flex',
|
|
96
|
-
alignItems: 'center'
|
|
97
|
-
height: '29px'
|
|
93
|
+
alignItems: 'center'
|
|
98
94
|
} },
|
|
99
95
|
React.createElement(Typography, { variant: "caption", sx: { fontSize: '0.7rem', fontWeight: 500, textTransform: 'none' } }, currentProviderLabel)),
|
|
100
96
|
React.createElement(Menu, { open: menuOpen, onClose: closeMenu, anchorEl: menuAnchorEl, anchorOrigin: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TooltippedIconButton } from '@jupyter/chat';
|
|
2
2
|
import StopIcon from '@mui/icons-material/Stop';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
/**
|
|
@@ -7,11 +7,10 @@ import React from 'react';
|
|
|
7
7
|
export function StopButton(props) {
|
|
8
8
|
const { translator: trans } = props;
|
|
9
9
|
const tooltip = trans.__('Stop streaming');
|
|
10
|
-
return (React.createElement(
|
|
11
|
-
size: 'small',
|
|
12
|
-
variant: 'contained',
|
|
13
|
-
color: 'error',
|
|
10
|
+
return (React.createElement(TooltippedIconButton, { onClick: props.stopStreaming, tooltip: tooltip, buttonProps: {
|
|
14
11
|
title: tooltip
|
|
12
|
+
}, sx: {
|
|
13
|
+
backgroundColor: 'var(--mui-palette-error-main, #d32f2f);'
|
|
15
14
|
} },
|
|
16
15
|
React.createElement(StopIcon, null)));
|
|
17
16
|
}
|
|
@@ -64,9 +64,9 @@ export function ToolSelect(props) {
|
|
|
64
64
|
React.createElement(TooltippedButton, { onClick: e => {
|
|
65
65
|
openMenu(e.currentTarget);
|
|
66
66
|
}, tooltip: trans.__('Tools (%1/%2 selected)', selectedToolNames.length.toString(), tools.length.toString()), buttonProps: {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
...(selectedToolNames.length === 0 && {
|
|
68
|
+
variant: 'outlined'
|
|
69
|
+
}),
|
|
70
70
|
title: trans.__('Select AI Tools'),
|
|
71
71
|
onKeyDown: e => {
|
|
72
72
|
if (e.key !== 'Enter' && e.key !== ' ') {
|
|
@@ -79,7 +79,7 @@ export function ToolSelect(props) {
|
|
|
79
79
|
}, sx: selectedToolNames.length === 0
|
|
80
80
|
? { backgroundColor: 'var(--jp-layout-color3)' }
|
|
81
81
|
: {} },
|
|
82
|
-
React.createElement(BuildIcon,
|
|
82
|
+
React.createElement(BuildIcon, { sx: { fontSize: 'small' } })),
|
|
83
83
|
React.createElement(Menu, { open: menuOpen, onClose: closeMenu, anchorEl: menuAnchorEl, anchorOrigin: {
|
|
84
84
|
vertical: 'top',
|
|
85
85
|
horizontal: 'right'
|
package/lib/index.d.ts
CHANGED
|
@@ -6,3 +6,4 @@ import { AISettingsModel } from './models/settings-model';
|
|
|
6
6
|
declare const _default: (JupyterFrontEndPlugin<IProviderRegistry> | JupyterFrontEndPlugin<void> | JupyterFrontEndPlugin<IChatModelRegistry> | JupyterFrontEndPlugin<AgentManagerFactory> | JupyterFrontEndPlugin<AISettingsModel> | JupyterFrontEndPlugin<IDiffManager> | JupyterFrontEndPlugin<IToolRegistry> | JupyterFrontEndPlugin<IInputToolbarRegistryFactory>)[];
|
|
7
7
|
export default _default;
|
|
8
8
|
export * from './tokens';
|
|
9
|
+
export * from './icons';
|
package/lib/index.js
CHANGED
|
@@ -3,10 +3,8 @@ import { ActiveCellManager, AttachmentOpenerRegistry, chatIcon, ChatWidget, IInp
|
|
|
3
3
|
import { ICommandPalette, IThemeManager, WidgetTracker } from '@jupyterlab/apputils';
|
|
4
4
|
import { ICompletionProviderManager } from '@jupyterlab/completer';
|
|
5
5
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
6
|
-
import { IEditorTracker } from '@jupyterlab/fileeditor';
|
|
7
6
|
import { INotebookTracker } from '@jupyterlab/notebook';
|
|
8
7
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
9
|
-
import { IKernelSpecManager } from '@jupyterlab/services';
|
|
10
8
|
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
11
9
|
import { IStatusBar } from '@jupyterlab/statusbar';
|
|
12
10
|
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
@@ -24,8 +22,6 @@ import { clearItem, createModelSelectItem, createToolSelectItem, stopItem, Compl
|
|
|
24
22
|
import { AISettingsModel } from './models/settings-model';
|
|
25
23
|
import { DiffManager } from './diff-manager';
|
|
26
24
|
import { ToolRegistry } from './tools/tool-registry';
|
|
27
|
-
import { createAddCellTool, createDeleteCellTool, createExecuteActiveCellTool, createGetCellInfoTool, createGetNotebookInfoTool, createNotebookCreationTool, createRunCellTool, createSaveNotebookTool, createSetCellContentTool } from './tools/notebook';
|
|
28
|
-
import { createCopyFileTool, createDeleteFileTool, createGetFileInfoTool, createNavigateToDirectoryTool, createNewFileTool, createOpenFileTool, createRenameFileTool, createSetFileContentTool } from './tools/file';
|
|
29
25
|
import { createDiscoverCommandsTool, createExecuteCommandTool } from './tools/commands';
|
|
30
26
|
import { AISettingsWidget } from './widgets/ai-settings';
|
|
31
27
|
import { MainAreaChat } from './widgets/main-area-chat';
|
|
@@ -109,20 +105,11 @@ const chatModelRegistry = {
|
|
|
109
105
|
description: 'Registry for the current chat model',
|
|
110
106
|
autoStart: true,
|
|
111
107
|
requires: [IAISettingsModel, IAgentManagerFactory, IDocumentManager],
|
|
112
|
-
optional: [IProviderRegistry,
|
|
108
|
+
optional: [IProviderRegistry, IToolRegistry, ITranslator],
|
|
113
109
|
provides: IChatModelRegistry,
|
|
114
|
-
activate: (app, settingsModel, agentManagerFactory, docManager, providerRegistry,
|
|
110
|
+
activate: (app, settingsModel, agentManagerFactory, docManager, providerRegistry, toolRegistry, translator) => {
|
|
115
111
|
const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
|
|
116
|
-
// Create ActiveCellManager if notebook tracker is available
|
|
117
|
-
let activeCellManager;
|
|
118
|
-
if (notebookTracker) {
|
|
119
|
-
activeCellManager = new ActiveCellManager({
|
|
120
|
-
tracker: notebookTracker,
|
|
121
|
-
shell: app.shell
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
112
|
return new ChatModelRegistry({
|
|
125
|
-
activeCellManager,
|
|
126
113
|
settingsModel,
|
|
127
114
|
agentManagerFactory,
|
|
128
115
|
docManager,
|
|
@@ -145,8 +132,14 @@ const plugin = {
|
|
|
145
132
|
IChatModelRegistry,
|
|
146
133
|
IAISettingsModel
|
|
147
134
|
],
|
|
148
|
-
optional: [
|
|
149
|
-
|
|
135
|
+
optional: [
|
|
136
|
+
IThemeManager,
|
|
137
|
+
ILayoutRestorer,
|
|
138
|
+
ILabShell,
|
|
139
|
+
INotebookTracker,
|
|
140
|
+
ITranslator
|
|
141
|
+
],
|
|
142
|
+
activate: (app, rmRegistry, inputToolbarFactory, modelRegistry, settingsModel, themeManager, restorer, labShell, notebookTracker, translator) => {
|
|
150
143
|
const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
|
|
151
144
|
// Create attachment opener registry to handle file attachments
|
|
152
145
|
const attachmentOpenerRegistry = new AttachmentOpenerRegistry();
|
|
@@ -156,6 +149,16 @@ const plugin = {
|
|
|
156
149
|
attachmentOpenerRegistry.set('notebook', attachment => {
|
|
157
150
|
app.commands.execute('docmanager:open', { path: attachment.value });
|
|
158
151
|
});
|
|
152
|
+
// Create ActiveCellManager if notebook tracker is available, and add it to the
|
|
153
|
+
// model registry.
|
|
154
|
+
let activeCellManager;
|
|
155
|
+
if (notebookTracker) {
|
|
156
|
+
activeCellManager = new ActiveCellManager({
|
|
157
|
+
tracker: notebookTracker,
|
|
158
|
+
shell: app.shell
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
modelRegistry.activeCellManager = activeCellManager;
|
|
159
162
|
// Create chat panel with drag/drop functionality
|
|
160
163
|
const chatPanel = new MultiChatPanel({
|
|
161
164
|
rmRegistry,
|
|
@@ -568,47 +571,10 @@ const toolRegistry = {
|
|
|
568
571
|
id: '@jupyterlite/ai:tool-registry',
|
|
569
572
|
description: 'Provide the AI tool registry',
|
|
570
573
|
autoStart: true,
|
|
571
|
-
requires: [IAISettingsModel
|
|
572
|
-
optional: [INotebookTracker, IDiffManager, IEditorTracker],
|
|
574
|
+
requires: [IAISettingsModel],
|
|
573
575
|
provides: IToolRegistry,
|
|
574
|
-
activate: (app, settingsModel
|
|
576
|
+
activate: (app, settingsModel) => {
|
|
575
577
|
const toolRegistry = new ToolRegistry();
|
|
576
|
-
const notebookCreationTool = createNotebookCreationTool(docManager, kernelSpecManager);
|
|
577
|
-
toolRegistry.add('create_notebook', notebookCreationTool);
|
|
578
|
-
// Add high-level notebook operation tools
|
|
579
|
-
const addCellTool = createAddCellTool(docManager, notebookTracker);
|
|
580
|
-
const getNotebookInfoTool = createGetNotebookInfoTool(docManager, notebookTracker);
|
|
581
|
-
const getCellInfoTool = createGetCellInfoTool(docManager, notebookTracker);
|
|
582
|
-
const setCellContentTool = createSetCellContentTool(docManager, notebookTracker, diffManager);
|
|
583
|
-
const runCellTool = createRunCellTool(docManager, notebookTracker);
|
|
584
|
-
const deleteCellTool = createDeleteCellTool(docManager, notebookTracker);
|
|
585
|
-
const saveNotebookTool = createSaveNotebookTool(docManager, notebookTracker);
|
|
586
|
-
const executeActiveCellTool = createExecuteActiveCellTool(docManager, notebookTracker);
|
|
587
|
-
toolRegistry.add('add_cell', addCellTool);
|
|
588
|
-
toolRegistry.add('get_notebook_info', getNotebookInfoTool);
|
|
589
|
-
toolRegistry.add('get_cell_info', getCellInfoTool);
|
|
590
|
-
toolRegistry.add('set_cell_content', setCellContentTool);
|
|
591
|
-
toolRegistry.add('run_cell', runCellTool);
|
|
592
|
-
toolRegistry.add('delete_cell', deleteCellTool);
|
|
593
|
-
toolRegistry.add('save_notebook', saveNotebookTool);
|
|
594
|
-
toolRegistry.add('execute_active_cell', executeActiveCellTool);
|
|
595
|
-
// Add file operation tools
|
|
596
|
-
const newFileTool = createNewFileTool(docManager);
|
|
597
|
-
const openFileTool = createOpenFileTool(docManager);
|
|
598
|
-
const deleteFileTool = createDeleteFileTool(docManager);
|
|
599
|
-
const renameFileTool = createRenameFileTool(docManager);
|
|
600
|
-
const copyFileTool = createCopyFileTool(docManager);
|
|
601
|
-
const navigateToDirectoryTool = createNavigateToDirectoryTool(app.commands);
|
|
602
|
-
const getFileInfoTool = createGetFileInfoTool(docManager, editorTracker);
|
|
603
|
-
const setFileContentTool = createSetFileContentTool(docManager, diffManager);
|
|
604
|
-
toolRegistry.add('create_file', newFileTool);
|
|
605
|
-
toolRegistry.add('open_file', openFileTool);
|
|
606
|
-
toolRegistry.add('delete_file', deleteFileTool);
|
|
607
|
-
toolRegistry.add('rename_file', renameFileTool);
|
|
608
|
-
toolRegistry.add('copy_file', copyFileTool);
|
|
609
|
-
toolRegistry.add('navigate_to_directory', navigateToDirectoryTool);
|
|
610
|
-
toolRegistry.add('get_file_info', getFileInfoTool);
|
|
611
|
-
toolRegistry.add('set_file_content', setFileContentTool);
|
|
612
578
|
// Add command operation tools
|
|
613
579
|
const discoverCommandsTool = createDiscoverCommandsTool(app.commands);
|
|
614
580
|
const executeCommandTool = createExecuteCommandTool(app.commands, settingsModel);
|
|
@@ -695,3 +661,4 @@ export default [
|
|
|
695
661
|
];
|
|
696
662
|
// Export extension points for other extensions to use
|
|
697
663
|
export * from './tokens';
|
|
664
|
+
export * from './icons';
|
|
@@ -29,7 +29,8 @@ export class AISettingsModel extends VDomModel {
|
|
|
29
29
|
'fileeditor:run-code',
|
|
30
30
|
'kernelmenu:run',
|
|
31
31
|
'kernelmenu:restart-and-run-all',
|
|
32
|
-
'runmenu:run-all'
|
|
32
|
+
'runmenu:run-all',
|
|
33
|
+
'jupyterlab-ai-commands:run-cell'
|
|
33
34
|
],
|
|
34
35
|
systemPrompt: `You are Jupyternaut, an AI coding assistant built specifically for the JupyterLab environment.
|
|
35
36
|
|
|
@@ -38,7 +39,7 @@ You're designed to be a capable partner for data science, research, and developm
|
|
|
38
39
|
|
|
39
40
|
## Your Capabilities
|
|
40
41
|
**📁 File & Project Management:**
|
|
41
|
-
- Create, read, edit, and organize
|
|
42
|
+
- Create, read, edit, and organize files and notebooks in any language
|
|
42
43
|
- Manage project structure and navigate file systems
|
|
43
44
|
- Help with version control and project organization
|
|
44
45
|
|
|
@@ -48,51 +49,64 @@ You're designed to be a capable partner for data science, research, and developm
|
|
|
48
49
|
- Help with notebook structure and organization
|
|
49
50
|
- Retrieve and analyze cell outputs and execution results
|
|
50
51
|
|
|
52
|
+
**⚡ Kernel Management:**
|
|
53
|
+
- Start new kernels with specified language or kernel name
|
|
54
|
+
- Execute code directly in running kernels without creating cells
|
|
55
|
+
- List running kernels and monitor their status
|
|
56
|
+
- Manage kernel lifecycle (start, monitor, shutdown)
|
|
57
|
+
|
|
51
58
|
**🧠 Coding & Development:**
|
|
52
|
-
- Write, debug, and optimize Python
|
|
59
|
+
- Write, debug, and optimize code in any language supported by Jupyter kernels (Python, R, Julia, JavaScript, C++, and more)
|
|
53
60
|
- Explain complex algorithms and data structures
|
|
54
61
|
- Help with data analysis, visualization, and machine learning
|
|
55
|
-
- Support for
|
|
62
|
+
- Support for libraries and packages across different languages
|
|
56
63
|
- Code reviews and best practices recommendations
|
|
57
64
|
|
|
58
65
|
**💡 Adaptive Assistance:**
|
|
59
|
-
- Understand context from
|
|
60
|
-
- Provide suggestions tailored to
|
|
66
|
+
- Understand context from the user's current work environment
|
|
67
|
+
- Provide suggestions tailored to the user's specific use case
|
|
61
68
|
- Help with both quick fixes and long-term project planning
|
|
62
69
|
|
|
63
|
-
## How
|
|
64
|
-
|
|
65
|
-
- Execute operations directly in
|
|
70
|
+
## How You Work
|
|
71
|
+
You can actively interact with the user's JupyterLab environment using specialized tools. When asked to perform actions, you can:
|
|
72
|
+
- Execute operations directly in notebooks
|
|
66
73
|
- Create and modify files as needed
|
|
67
74
|
- Run code and analyze results
|
|
68
75
|
- Make systematic changes across multiple files
|
|
69
76
|
|
|
70
|
-
##
|
|
71
|
-
|
|
72
|
-
- **
|
|
73
|
-
- **
|
|
74
|
-
|
|
77
|
+
## Code Execution Strategy
|
|
78
|
+
When asked to run code or perform computations, choose the most appropriate approach:
|
|
79
|
+
- **For quick computations or one-off code execution**: Use kernel commands to start a kernel and execute code directly, without creating notebook files. This is ideal for calculations, data lookups, or testing code snippets.
|
|
80
|
+
- **For work that should be saved**: Create or use notebooks when the user needs a persistent record of their work, wants to iterate on code, or is building something they'll return to later.
|
|
81
|
+
|
|
82
|
+
This means if the user asks you to "calculate the factorial of 100" or "check what library version is installed", run that directly in a kernel rather than creating a new notebook file.
|
|
83
|
+
|
|
84
|
+
## Your Approach
|
|
85
|
+
- **Context-aware**: You understand the user is working in a data science/research environment
|
|
86
|
+
- **Practical**: You focus on actionable solutions that work in the user's current setup
|
|
87
|
+
- **Educational**: You explain your reasoning and teach best practices along the way
|
|
88
|
+
- **Collaborative**: You are a pair programming partner, not just a code generator
|
|
75
89
|
|
|
76
90
|
## Communication Style & Agent Behavior
|
|
77
|
-
- **Conversational**:
|
|
78
|
-
- **Progress Updates**:
|
|
79
|
-
- **No Filler**:
|
|
80
|
-
- **Purposeful Communication**:
|
|
81
|
-
- **Active Narration**:
|
|
82
|
-
- **Checkpoint Updates**: After several operations,
|
|
83
|
-
- **Natural Flow**:
|
|
91
|
+
- **Conversational**: You maintain a friendly, natural conversation flow throughout the interaction
|
|
92
|
+
- **Progress Updates**: You write brief progress messages between tool uses that appear directly in the conversation
|
|
93
|
+
- **No Filler**: You avoid empty acknowledgments like "Sounds good!" or "Okay, I will..." - you get straight to work
|
|
94
|
+
- **Purposeful Communication**: You start with what you're doing, use tools, then share what you found and what's next
|
|
95
|
+
- **Active Narration**: You actively write progress updates like "Looking at the current code structure..." or "Found the issue in the notebook..." between tool calls
|
|
96
|
+
- **Checkpoint Updates**: After several operations, you summarize what you've accomplished and what remains
|
|
97
|
+
- **Natural Flow**: Your explanations and progress reports appear as normal conversation text, not just in tool blocks
|
|
84
98
|
|
|
85
99
|
## IMPORTANT: Always write progress messages between tools that explain what you're doing and what you found. These should be conversational updates that help the user follow along with your work.
|
|
86
100
|
|
|
87
101
|
## Technical Communication
|
|
88
102
|
- Code is formatted in proper markdown blocks with syntax highlighting
|
|
89
103
|
- Mathematical notation uses LaTeX formatting: \\(equations\\) and \\[display math\\]
|
|
90
|
-
-
|
|
91
|
-
- When creating or modifying multiple files,
|
|
92
|
-
-
|
|
104
|
+
- You provide context for your actions and explain your reasoning as you work
|
|
105
|
+
- When creating or modifying multiple files, you give brief summaries of changes
|
|
106
|
+
- You keep users informed of progress while staying focused on the task
|
|
93
107
|
|
|
94
108
|
## Multi-Step Task Handling
|
|
95
|
-
When users request complex tasks that require multiple steps (like "create a notebook with example cells"),
|
|
109
|
+
When users request complex tasks that require multiple steps (like "create a notebook with example cells"), you use tools in sequence to accomplish the complete task. For example:
|
|
96
110
|
- First use create_notebook to create the notebook
|
|
97
111
|
- Then use add_code_cell or add_markdown_cell to add cells
|
|
98
112
|
- Use set_cell_content to add content to cells as needed
|
|
@@ -100,7 +114,7 @@ When users request complex tasks that require multiple steps (like "create a not
|
|
|
100
114
|
|
|
101
115
|
Always think through multi-step tasks and use tools to fully complete the user's request rather than stopping after just one action.
|
|
102
116
|
|
|
103
|
-
|
|
117
|
+
You are ready to help users build something great!`,
|
|
104
118
|
// Completion system prompt - also defined in schema/settings-model.json
|
|
105
119
|
// This serves as a fallback if settings fail to load or are not available
|
|
106
120
|
completionSystemPrompt: `You are an AI code completion assistant. Complete the given code fragment with appropriate code.
|
package/lib/tokens.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ActiveCellManager } from '@jupyter/chat';
|
|
1
2
|
import { Token } from '@lumino/coreutils';
|
|
2
3
|
import { ISignal } from '@lumino/signaling';
|
|
3
4
|
import type { Tool, LanguageModel } from 'ai';
|
|
@@ -194,6 +195,7 @@ export interface IChatModelRegistry {
|
|
|
194
195
|
getAll(): AIChatModel[];
|
|
195
196
|
remove(name: string): void;
|
|
196
197
|
createModel(name?: string, activeProvider?: string, tokenUsage?: ITokenUsage): AIChatModel;
|
|
198
|
+
activeCellManager: ActiveCellManager | undefined;
|
|
197
199
|
}
|
|
198
200
|
export declare const IChatModelRegistry: Token<IChatModelRegistry>;
|
|
199
201
|
/**
|
package/lib/tools/commands.js
CHANGED
|
@@ -12,7 +12,7 @@ export function createDiscoverCommandsTool(commands) {
|
|
|
12
12
|
.string()
|
|
13
13
|
.optional()
|
|
14
14
|
.nullable()
|
|
15
|
-
.describe(
|
|
15
|
+
.describe("Optional search query to filter commands. It doesn't need to be provided to list all commands")
|
|
16
16
|
}),
|
|
17
17
|
execute: async (input) => {
|
|
18
18
|
const { query } = input;
|
|
@@ -66,9 +66,9 @@ export function createExecuteCommandTool(commands, settingsModel) {
|
|
|
66
66
|
inputSchema: z.object({
|
|
67
67
|
commandId: z.string().describe('The ID of the command to execute'),
|
|
68
68
|
args: z
|
|
69
|
-
.
|
|
69
|
+
.record(z.string(), z.unknown())
|
|
70
70
|
.optional()
|
|
71
|
-
.describe('Optional arguments to pass to the command')
|
|
71
|
+
.describe('Optional arguments object to pass to the command (must be an object, not a string)')
|
|
72
72
|
}),
|
|
73
73
|
needsApproval: (input) => {
|
|
74
74
|
const commandsRequiringApproval = settingsModel.config.commandsRequiringApproval || [];
|
|
@@ -85,16 +85,12 @@ export function createExecuteCommandTool(commands, settingsModel) {
|
|
|
85
85
|
}
|
|
86
86
|
// Execute the command
|
|
87
87
|
const result = await commands.execute(commandId, args);
|
|
88
|
-
// Handle Widget objects specially
|
|
88
|
+
// Handle Widget objects specially by extracting id and title
|
|
89
89
|
let serializedResult;
|
|
90
|
-
if (result &&
|
|
91
|
-
typeof result === 'object' &&
|
|
92
|
-
(result.constructor?.name?.includes('Widget') || result.id)) {
|
|
90
|
+
if (result && typeof result === 'object' && result.id) {
|
|
93
91
|
serializedResult = {
|
|
94
|
-
type: result.constructor?.name || 'Widget',
|
|
95
92
|
id: result.id,
|
|
96
|
-
title: result.title?.label || result.title
|
|
97
|
-
className: result.className
|
|
93
|
+
title: result.title?.label || result.title
|
|
98
94
|
};
|
|
99
95
|
}
|
|
100
96
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyterlite/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "AI code completions and chat for JupyterLite",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -56,13 +56,13 @@
|
|
|
56
56
|
"docs:build": "sed -e 's/\\[@/[/g' -e 's/@/\\@/g' CHANGELOG.md > docs/_changelog_content.md && jupyter book build --html"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@ai-sdk/anthropic": "^3.0.
|
|
60
|
-
"@ai-sdk/google": "^3.0.
|
|
61
|
-
"@ai-sdk/mcp": "^1.0.
|
|
62
|
-
"@ai-sdk/mistral": "^3.0.
|
|
63
|
-
"@ai-sdk/openai": "^3.0.
|
|
64
|
-
"@ai-sdk/openai-compatible": "^2.0.
|
|
65
|
-
"@jupyter/chat": "^0.
|
|
59
|
+
"@ai-sdk/anthropic": "^3.0.23",
|
|
60
|
+
"@ai-sdk/google": "^3.0.13",
|
|
61
|
+
"@ai-sdk/mcp": "^1.0.13",
|
|
62
|
+
"@ai-sdk/mistral": "^3.0.12",
|
|
63
|
+
"@ai-sdk/openai": "^3.0.18",
|
|
64
|
+
"@ai-sdk/openai-compatible": "^2.0.18",
|
|
65
|
+
"@jupyter/chat": "^0.19.0-alpha.3",
|
|
66
66
|
"@jupyterlab/application": "^4.0.0",
|
|
67
67
|
"@jupyterlab/apputils": "^4.5.6",
|
|
68
68
|
"@jupyterlab/cells": "^4.4.6",
|
|
@@ -86,9 +86,9 @@
|
|
|
86
86
|
"@lumino/widgets": "^2.7.1",
|
|
87
87
|
"@mui/icons-material": "^7",
|
|
88
88
|
"@mui/material": "^7",
|
|
89
|
-
"ai": "^6.0.
|
|
89
|
+
"ai": "^6.0.49",
|
|
90
90
|
"jupyter-secrets-manager": "^0.4.0",
|
|
91
|
-
"zod": "^4.
|
|
91
|
+
"zod": "^4.3.6"
|
|
92
92
|
},
|
|
93
93
|
"devDependencies": {
|
|
94
94
|
"@jupyterlab/builder": "^4.0.0",
|