@jupyterlite/ai 0.9.0-a3 → 0.9.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/README.md +20 -89
- package/lib/agent.d.ts +10 -4
- package/lib/agent.js +30 -17
- package/lib/chat-model.d.ts +6 -0
- package/lib/chat-model.js +144 -17
- package/lib/completion/completion-provider.js +1 -13
- package/lib/components/completion-status.d.ts +20 -0
- package/lib/components/completion-status.js +51 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/model-select.js +1 -2
- package/lib/diff-manager.d.ts +25 -0
- package/lib/diff-manager.js +60 -0
- package/lib/icons.d.ts +0 -1
- package/lib/icons.js +2 -6
- package/lib/index.d.ts +2 -2
- package/lib/index.js +54 -23
- package/lib/models/settings-model.d.ts +4 -0
- package/lib/models/settings-model.js +24 -2
- package/lib/providers/built-in-providers.d.ts +0 -4
- package/lib/providers/built-in-providers.js +17 -23
- package/lib/tokens.d.ts +74 -0
- package/lib/tokens.js +4 -0
- package/lib/tools/commands.js +36 -35
- package/lib/tools/file.d.ts +10 -1
- package/lib/tools/file.js +235 -146
- package/lib/tools/notebook.d.ts +2 -3
- package/lib/tools/notebook.js +11 -11
- package/lib/widgets/ai-settings.js +78 -13
- package/lib/widgets/provider-config-dialog.js +15 -8
- package/package.json +5 -3
- package/schema/settings-model.json +25 -0
- package/src/agent.ts +35 -20
- package/src/chat-model.ts +182 -19
- package/src/completion/completion-provider.ts +1 -14
- package/src/components/completion-status.tsx +79 -0
- package/src/components/index.ts +1 -0
- package/src/components/model-select.tsx +0 -3
- package/src/diff-manager.ts +81 -0
- package/src/icons.ts +2 -7
- package/src/index.ts +74 -24
- package/src/models/settings-model.ts +28 -2
- package/src/providers/built-in-providers.ts +17 -24
- package/src/tokens.ts +78 -0
- package/src/tools/commands.ts +45 -40
- package/src/tools/file.ts +295 -164
- package/src/tools/notebook.ts +13 -14
- package/src/widgets/ai-settings.tsx +184 -35
- package/src/widgets/provider-config-dialog.tsx +43 -16
- package/style/base.css +14 -0
package/lib/tools/file.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CommandRegistry } from '@lumino/commands';
|
|
2
2
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
3
|
-
import {
|
|
3
|
+
import { IEditorTracker } from '@jupyterlab/fileeditor';
|
|
4
|
+
import { IDiffManager, ITool } from '../tokens';
|
|
4
5
|
/**
|
|
5
6
|
* Create a tool for creating new files of various types
|
|
6
7
|
*/
|
|
@@ -25,3 +26,11 @@ export declare function createCopyFileTool(docManager: IDocumentManager): ITool;
|
|
|
25
26
|
* Create a tool for navigating to directories in the file browser
|
|
26
27
|
*/
|
|
27
28
|
export declare function createNavigateToDirectoryTool(commands: CommandRegistry): ITool;
|
|
29
|
+
/**
|
|
30
|
+
* Create a tool for getting file information and content
|
|
31
|
+
*/
|
|
32
|
+
export declare function createGetFileInfoTool(docManager: IDocumentManager, editorTracker?: IEditorTracker): ITool;
|
|
33
|
+
/**
|
|
34
|
+
* Create a tool for setting the content of a file
|
|
35
|
+
*/
|
|
36
|
+
export declare function createSetFileContentTool(docManager: IDocumentManager, diffManager?: IDiffManager): ITool;
|
package/lib/tools/file.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PathExt } from '@jupyterlab/coreutils';
|
|
1
2
|
import { tool } from '@openai/agents';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
/**
|
|
@@ -10,16 +11,9 @@ export function createNewFileTool(docManager) {
|
|
|
10
11
|
parameters: z.object({
|
|
11
12
|
fileName: z.string().describe('Name of the file to create'),
|
|
12
13
|
fileType: z
|
|
13
|
-
.
|
|
14
|
-
'text',
|
|
15
|
-
'python',
|
|
16
|
-
'markdown',
|
|
17
|
-
'json',
|
|
18
|
-
'javascript',
|
|
19
|
-
'typescript'
|
|
20
|
-
])
|
|
14
|
+
.string()
|
|
21
15
|
.default('text')
|
|
22
|
-
.describe('Type of file to create'),
|
|
16
|
+
.describe('Type of file to create. Common examples: text, python, markdown, json, javascript, typescript, yaml, julia, r, csv'),
|
|
23
17
|
content: z
|
|
24
18
|
.string()
|
|
25
19
|
.optional()
|
|
@@ -31,72 +25,50 @@ export function createNewFileTool(docManager) {
|
|
|
31
25
|
.nullable()
|
|
32
26
|
.describe('Directory where to create the file (optional)')
|
|
33
27
|
}),
|
|
28
|
+
errorFunction: (context, error) => {
|
|
29
|
+
return JSON.stringify({
|
|
30
|
+
success: false,
|
|
31
|
+
error: `Failed to create file: ${error instanceof Error ? error.message : String(error)}`
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
34
|
execute: async (input) => {
|
|
35
35
|
const { fileName, content = '', cwd, fileType = 'text' } = input;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
!fileName.endsWith('.py') &&
|
|
54
|
-
!fileName.includes('.')
|
|
55
|
-
? `${fileName}.py`
|
|
56
|
-
: fullFileName;
|
|
57
|
-
const fullPath = cwd ? `${cwd}/${finalFileName}` : finalFileName;
|
|
58
|
-
// Create file with content using document manager
|
|
59
|
-
const model = await docManager.services.contents.newUntitled({
|
|
60
|
-
path: cwd || '',
|
|
36
|
+
const registeredFileType = docManager.registry.getFileType(fileType);
|
|
37
|
+
const ext = registeredFileType?.extensions[0] || '.txt';
|
|
38
|
+
const existingExt = PathExt.extname(fileName);
|
|
39
|
+
const fullFileName = existingExt ? fileName : `${fileName}${ext}`;
|
|
40
|
+
const fullPath = cwd ? `${cwd}/${fullFileName}` : fullFileName;
|
|
41
|
+
const model = await docManager.services.contents.newUntitled({
|
|
42
|
+
path: cwd || '',
|
|
43
|
+
type: 'file',
|
|
44
|
+
ext
|
|
45
|
+
});
|
|
46
|
+
let finalPath = model.path;
|
|
47
|
+
if (model.name !== fullFileName) {
|
|
48
|
+
const renamed = await docManager.services.contents.rename(model.path, fullPath);
|
|
49
|
+
finalPath = renamed.path;
|
|
50
|
+
}
|
|
51
|
+
if (content) {
|
|
52
|
+
await docManager.services.contents.save(finalPath, {
|
|
61
53
|
type: 'file',
|
|
62
|
-
|
|
54
|
+
format: 'text',
|
|
55
|
+
content
|
|
63
56
|
});
|
|
64
|
-
// Rename to desired name if needed
|
|
65
|
-
let finalPath = model.path;
|
|
66
|
-
if (model.name !== finalFileName) {
|
|
67
|
-
const renamed = await docManager.services.contents.rename(model.path, fullPath);
|
|
68
|
-
finalPath = renamed.path;
|
|
69
|
-
}
|
|
70
|
-
// Set content if provided
|
|
71
|
-
if (content) {
|
|
72
|
-
await docManager.services.contents.save(finalPath, {
|
|
73
|
-
type: 'file',
|
|
74
|
-
format: 'text',
|
|
75
|
-
content
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
// Open the newly created file
|
|
79
|
-
let opened = false;
|
|
80
|
-
if (!docManager.findWidget(finalPath)) {
|
|
81
|
-
docManager.openOrReveal(finalPath);
|
|
82
|
-
opened = true;
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
success: true,
|
|
86
|
-
message: `${fileType} file '${finalFileName}' created and opened successfully`,
|
|
87
|
-
fileName: finalFileName,
|
|
88
|
-
filePath: finalPath,
|
|
89
|
-
fileType,
|
|
90
|
-
hasContent: !!content,
|
|
91
|
-
opened
|
|
92
|
-
};
|
|
93
57
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
};
|
|
58
|
+
let opened = false;
|
|
59
|
+
if (!docManager.findWidget(finalPath)) {
|
|
60
|
+
docManager.openOrReveal(finalPath);
|
|
61
|
+
opened = true;
|
|
99
62
|
}
|
|
63
|
+
return {
|
|
64
|
+
success: true,
|
|
65
|
+
message: `${fileType} file '${fullFileName}' created and opened successfully`,
|
|
66
|
+
fileName: fullFileName,
|
|
67
|
+
filePath: finalPath,
|
|
68
|
+
fileType,
|
|
69
|
+
hasContent: !!content,
|
|
70
|
+
opened
|
|
71
|
+
};
|
|
100
72
|
}
|
|
101
73
|
});
|
|
102
74
|
}
|
|
@@ -110,29 +82,24 @@ export function createOpenFileTool(docManager) {
|
|
|
110
82
|
parameters: z.object({
|
|
111
83
|
filePath: z.string().describe('Path to the file to open')
|
|
112
84
|
}),
|
|
85
|
+
errorFunction: (context, error) => {
|
|
86
|
+
return JSON.stringify({
|
|
87
|
+
success: false,
|
|
88
|
+
error: `Failed to open file: ${error instanceof Error ? error.message : String(error)}`
|
|
89
|
+
});
|
|
90
|
+
},
|
|
113
91
|
execute: async (input) => {
|
|
114
92
|
const { filePath } = input;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
success: false,
|
|
120
|
-
error: `Failed to open file: ${filePath}`
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
success: true,
|
|
125
|
-
message: `File '${filePath}' opened successfully`,
|
|
126
|
-
filePath,
|
|
127
|
-
widgetId: widget.id
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
return {
|
|
132
|
-
success: false,
|
|
133
|
-
error: `Failed to open file: ${error.message}`
|
|
134
|
-
};
|
|
93
|
+
const widget = docManager.openOrReveal(filePath);
|
|
94
|
+
if (!widget) {
|
|
95
|
+
throw new Error(`Could not open file: ${filePath}`);
|
|
135
96
|
}
|
|
97
|
+
return {
|
|
98
|
+
success: true,
|
|
99
|
+
message: `File '${filePath}' opened successfully`,
|
|
100
|
+
filePath,
|
|
101
|
+
widgetId: widget.id
|
|
102
|
+
};
|
|
136
103
|
}
|
|
137
104
|
});
|
|
138
105
|
}
|
|
@@ -146,22 +113,20 @@ export function createDeleteFileTool(docManager) {
|
|
|
146
113
|
parameters: z.object({
|
|
147
114
|
filePath: z.string().describe('Path to the file to delete')
|
|
148
115
|
}),
|
|
116
|
+
errorFunction: (context, error) => {
|
|
117
|
+
return JSON.stringify({
|
|
118
|
+
success: false,
|
|
119
|
+
error: `Failed to delete file: ${error instanceof Error ? error.message : String(error)}`
|
|
120
|
+
});
|
|
121
|
+
},
|
|
149
122
|
execute: async (input) => {
|
|
150
123
|
const { filePath } = input;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
return {
|
|
161
|
-
success: false,
|
|
162
|
-
error: `Failed to delete file: ${error.message}`
|
|
163
|
-
};
|
|
164
|
-
}
|
|
124
|
+
await docManager.services.contents.delete(filePath);
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
message: `File '${filePath}' deleted successfully`,
|
|
128
|
+
filePath
|
|
129
|
+
};
|
|
165
130
|
}
|
|
166
131
|
});
|
|
167
132
|
}
|
|
@@ -176,23 +141,21 @@ export function createRenameFileTool(docManager) {
|
|
|
176
141
|
oldPath: z.string().describe('Current path of the file'),
|
|
177
142
|
newPath: z.string().describe('New path/name for the file')
|
|
178
143
|
}),
|
|
144
|
+
errorFunction: (context, error) => {
|
|
145
|
+
return JSON.stringify({
|
|
146
|
+
success: false,
|
|
147
|
+
error: `Failed to rename file: ${error instanceof Error ? error.message : String(error)}`
|
|
148
|
+
});
|
|
149
|
+
},
|
|
179
150
|
execute: async (input) => {
|
|
180
151
|
const { oldPath, newPath } = input;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
return {
|
|
192
|
-
success: false,
|
|
193
|
-
error: `Failed to rename file: ${error.message}`
|
|
194
|
-
};
|
|
195
|
-
}
|
|
152
|
+
await docManager.services.contents.rename(oldPath, newPath);
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
message: `File renamed from '${oldPath}' to '${newPath}' successfully`,
|
|
156
|
+
oldPath,
|
|
157
|
+
newPath
|
|
158
|
+
};
|
|
196
159
|
}
|
|
197
160
|
});
|
|
198
161
|
}
|
|
@@ -209,23 +172,21 @@ export function createCopyFileTool(docManager) {
|
|
|
209
172
|
.string()
|
|
210
173
|
.describe('Destination path for the copied file')
|
|
211
174
|
}),
|
|
175
|
+
errorFunction: (context, error) => {
|
|
176
|
+
return JSON.stringify({
|
|
177
|
+
success: false,
|
|
178
|
+
error: `Failed to copy file: ${error instanceof Error ? error.message : String(error)}`
|
|
179
|
+
});
|
|
180
|
+
},
|
|
212
181
|
execute: async (input) => {
|
|
213
182
|
const { sourcePath, destinationPath } = input;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
return {
|
|
225
|
-
success: false,
|
|
226
|
-
error: `Failed to copy file: ${error.message}`
|
|
227
|
-
};
|
|
228
|
-
}
|
|
183
|
+
await docManager.services.contents.copy(sourcePath, destinationPath);
|
|
184
|
+
return {
|
|
185
|
+
success: true,
|
|
186
|
+
message: `File copied from '${sourcePath}' to '${destinationPath}' successfully`,
|
|
187
|
+
sourcePath,
|
|
188
|
+
destinationPath
|
|
189
|
+
};
|
|
229
190
|
}
|
|
230
191
|
});
|
|
231
192
|
}
|
|
@@ -239,24 +200,152 @@ export function createNavigateToDirectoryTool(commands) {
|
|
|
239
200
|
parameters: z.object({
|
|
240
201
|
directoryPath: z.string().describe('Path to the directory to navigate to')
|
|
241
202
|
}),
|
|
203
|
+
errorFunction: (context, error) => {
|
|
204
|
+
return JSON.stringify({
|
|
205
|
+
success: false,
|
|
206
|
+
error: `Failed to navigate to directory: ${error instanceof Error ? error.message : String(error)}`
|
|
207
|
+
});
|
|
208
|
+
},
|
|
242
209
|
execute: async (input) => {
|
|
243
210
|
const { directoryPath } = input;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
211
|
+
await commands.execute('filebrowser:go-to-path', {
|
|
212
|
+
path: directoryPath
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
success: true,
|
|
216
|
+
message: `Navigated to directory '${directoryPath}' successfully`,
|
|
217
|
+
directoryPath
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Create a tool for getting file information and content
|
|
224
|
+
*/
|
|
225
|
+
export function createGetFileInfoTool(docManager, editorTracker) {
|
|
226
|
+
return tool({
|
|
227
|
+
name: 'get_file_info',
|
|
228
|
+
description: 'Get information about a file including its path, name, extension, and content. Works with text-based files like Python files, markdown, JSON, etc. For Jupyter notebooks, use dedicated notebook tools instead. If no file path is provided, returns information about the currently active file in the editor.',
|
|
229
|
+
parameters: z.object({
|
|
230
|
+
filePath: z
|
|
231
|
+
.string()
|
|
232
|
+
.optional()
|
|
233
|
+
.nullable()
|
|
234
|
+
.describe('Path to the file to read (e.g., "script.py", "README.md", "config.json"). If not provided, uses the currently active file in the editor.')
|
|
235
|
+
}),
|
|
236
|
+
errorFunction: (context, error) => {
|
|
237
|
+
return JSON.stringify({
|
|
238
|
+
success: false,
|
|
239
|
+
error: `Failed to get file info: ${error instanceof Error ? error.message : String(error)}`
|
|
240
|
+
});
|
|
241
|
+
},
|
|
242
|
+
execute: async (input) => {
|
|
243
|
+
const { filePath } = input;
|
|
244
|
+
let widget = null;
|
|
245
|
+
if (filePath) {
|
|
246
|
+
widget =
|
|
247
|
+
docManager.findWidget(filePath) ??
|
|
248
|
+
docManager.openOrReveal(filePath) ??
|
|
249
|
+
null;
|
|
250
|
+
if (!widget) {
|
|
251
|
+
throw new Error(`Failed to open file at path: ${filePath}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
widget = editorTracker?.currentWidget ?? null;
|
|
256
|
+
if (!widget) {
|
|
257
|
+
throw new Error('No active file in the editor and no file path provided');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (!widget.context) {
|
|
261
|
+
throw new Error('Widget is not a document');
|
|
262
|
+
}
|
|
263
|
+
await widget.context.ready;
|
|
264
|
+
const model = widget.context.model;
|
|
265
|
+
if (!model) {
|
|
266
|
+
throw new Error('File model not available');
|
|
267
|
+
}
|
|
268
|
+
const sharedModel = model.sharedModel;
|
|
269
|
+
const content = sharedModel.getSource();
|
|
270
|
+
const resolvedFilePath = widget.context.path;
|
|
271
|
+
const fileName = widget.title.label;
|
|
272
|
+
const fileExtension = PathExt.extname(resolvedFilePath) || 'unknown';
|
|
273
|
+
return JSON.stringify({
|
|
274
|
+
success: true,
|
|
275
|
+
filePath: resolvedFilePath,
|
|
276
|
+
fileName,
|
|
277
|
+
fileExtension,
|
|
278
|
+
content,
|
|
279
|
+
isDirty: model.dirty,
|
|
280
|
+
readOnly: model.readOnly,
|
|
281
|
+
widgetType: widget.constructor.name
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Create a tool for setting the content of a file
|
|
288
|
+
*/
|
|
289
|
+
export function createSetFileContentTool(docManager, diffManager) {
|
|
290
|
+
return tool({
|
|
291
|
+
name: 'set_file_content',
|
|
292
|
+
description: 'Set or update the content of an existing file. This will replace the entire content of the file. For Jupyter notebooks, use dedicated notebook tools instead.',
|
|
293
|
+
parameters: z.object({
|
|
294
|
+
filePath: z
|
|
295
|
+
.string()
|
|
296
|
+
.describe('Path to the file to update (e.g., "script.py", "README.md", "config.json")'),
|
|
297
|
+
content: z.string().describe('The new content to set for the file'),
|
|
298
|
+
save: z
|
|
299
|
+
.boolean()
|
|
300
|
+
.optional()
|
|
301
|
+
.default(true)
|
|
302
|
+
.describe('Whether to save the file after updating (default: true)')
|
|
303
|
+
}),
|
|
304
|
+
errorFunction: (context, error) => {
|
|
305
|
+
return JSON.stringify({
|
|
306
|
+
success: false,
|
|
307
|
+
error: `Failed to set file content: ${error instanceof Error ? error.message : String(error)}`
|
|
308
|
+
});
|
|
309
|
+
},
|
|
310
|
+
execute: async (input) => {
|
|
311
|
+
const { filePath, content, save = true } = input;
|
|
312
|
+
let widget = docManager.findWidget(filePath);
|
|
313
|
+
if (!widget) {
|
|
314
|
+
widget = docManager.openOrReveal(filePath);
|
|
315
|
+
}
|
|
316
|
+
if (!widget) {
|
|
317
|
+
throw new Error(`Failed to open file at path: ${filePath}`);
|
|
318
|
+
}
|
|
319
|
+
await widget.context.ready;
|
|
320
|
+
const model = widget.context.model;
|
|
321
|
+
if (!model) {
|
|
322
|
+
throw new Error('File model not available');
|
|
323
|
+
}
|
|
324
|
+
if (model.readOnly) {
|
|
325
|
+
throw new Error('File is read-only and cannot be modified');
|
|
326
|
+
}
|
|
327
|
+
const sharedModel = model.sharedModel;
|
|
328
|
+
const originalContent = sharedModel.getSource();
|
|
329
|
+
sharedModel.setSource(content);
|
|
330
|
+
// Show the file diff using the diff manager if available
|
|
331
|
+
if (diffManager) {
|
|
332
|
+
await diffManager.showFileDiff({
|
|
333
|
+
original: String(originalContent),
|
|
334
|
+
modified: content,
|
|
335
|
+
filePath
|
|
247
336
|
});
|
|
248
|
-
return {
|
|
249
|
-
success: true,
|
|
250
|
-
message: `Navigated to directory '${directoryPath}' successfully`,
|
|
251
|
-
directoryPath
|
|
252
|
-
};
|
|
253
337
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
success: false,
|
|
257
|
-
error: `Failed to navigate to directory: ${error.message}`
|
|
258
|
-
};
|
|
338
|
+
if (save) {
|
|
339
|
+
await widget.context.save();
|
|
259
340
|
}
|
|
341
|
+
return JSON.stringify({
|
|
342
|
+
success: true,
|
|
343
|
+
filePath,
|
|
344
|
+
fileName: widget.title.label,
|
|
345
|
+
contentLength: content.length,
|
|
346
|
+
saved: save,
|
|
347
|
+
isDirty: model.dirty
|
|
348
|
+
});
|
|
260
349
|
}
|
|
261
350
|
});
|
|
262
351
|
}
|
package/lib/tools/notebook.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
2
2
|
import { INotebookTracker } from '@jupyterlab/notebook';
|
|
3
3
|
import { KernelSpec } from '@jupyterlab/services';
|
|
4
|
-
import {
|
|
5
|
-
import { ITool } from '../tokens';
|
|
4
|
+
import { IDiffManager, ITool } from '../tokens';
|
|
6
5
|
/**
|
|
7
6
|
* Create a notebook creation tool
|
|
8
7
|
*/
|
|
@@ -22,7 +21,7 @@ export declare function createGetCellInfoTool(docManager: IDocumentManager, note
|
|
|
22
21
|
/**
|
|
23
22
|
* Create a tool for setting cell content and type
|
|
24
23
|
*/
|
|
25
|
-
export declare function createSetCellContentTool(docManager: IDocumentManager,
|
|
24
|
+
export declare function createSetCellContentTool(docManager: IDocumentManager, notebookTracker?: INotebookTracker, diffManager?: IDiffManager): ITool;
|
|
26
25
|
/**
|
|
27
26
|
* Create a tool for running a specific cell
|
|
28
27
|
*/
|
package/lib/tools/notebook.js
CHANGED
|
@@ -145,6 +145,7 @@ export function createAddCellTool(docManager, notebookTracker) {
|
|
|
145
145
|
.describe('Type of cell to add'),
|
|
146
146
|
position: z
|
|
147
147
|
.enum(['above', 'below'])
|
|
148
|
+
.optional()
|
|
148
149
|
.default('below')
|
|
149
150
|
.describe('Position relative to current cell')
|
|
150
151
|
}),
|
|
@@ -350,7 +351,7 @@ export function createGetCellInfoTool(docManager, notebookTracker) {
|
|
|
350
351
|
/**
|
|
351
352
|
* Create a tool for setting cell content and type
|
|
352
353
|
*/
|
|
353
|
-
export function createSetCellContentTool(docManager,
|
|
354
|
+
export function createSetCellContentTool(docManager, notebookTracker, diffManager) {
|
|
354
355
|
return tool({
|
|
355
356
|
name: 'set_cell_content',
|
|
356
357
|
description: 'Set the content of a specific cell and return both the previous and new content',
|
|
@@ -445,16 +446,15 @@ export function createSetCellContentTool(docManager, commands, notebookTracker)
|
|
|
445
446
|
const previousCellType = targetCell.type;
|
|
446
447
|
const retrievedCellId = targetCell.id;
|
|
447
448
|
sharedModel.setSource(content);
|
|
448
|
-
// Show the cell diff using
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
});
|
|
449
|
+
// Show the cell diff using the diff manager if available
|
|
450
|
+
if (diffManager) {
|
|
451
|
+
await diffManager.showCellDiff({
|
|
452
|
+
original: previousContent,
|
|
453
|
+
modified: content,
|
|
454
|
+
cellId: retrievedCellId,
|
|
455
|
+
notebookPath: targetNotebookPath
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
458
|
return JSON.stringify({
|
|
459
459
|
success: true,
|
|
460
460
|
message: cellId !== undefined && cellId !== null
|