@jupyterlite/ai 0.9.1 → 0.10.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 +5 -214
- package/lib/agent.d.ts +58 -66
- package/lib/agent.js +274 -300
- package/lib/approval-buttons.d.ts +19 -82
- package/lib/approval-buttons.js +36 -289
- package/lib/chat-model-registry.d.ts +6 -0
- package/lib/chat-model-registry.js +4 -1
- package/lib/chat-model.d.ts +19 -54
- package/lib/chat-model.js +243 -303
- package/lib/components/clear-button.d.ts +6 -1
- package/lib/components/clear-button.js +8 -3
- package/lib/components/completion-status.d.ts +5 -0
- package/lib/components/completion-status.js +5 -4
- package/lib/components/model-select.d.ts +6 -1
- package/lib/components/model-select.js +9 -8
- package/lib/components/stop-button.d.ts +6 -1
- package/lib/components/stop-button.js +8 -3
- package/lib/components/token-usage-display.d.ts +5 -0
- package/lib/components/token-usage-display.js +2 -2
- package/lib/components/tool-select.d.ts +6 -1
- package/lib/components/tool-select.js +6 -5
- package/lib/index.js +58 -38
- package/lib/models/settings-model.d.ts +1 -1
- package/lib/providers/built-in-providers.js +38 -19
- package/lib/providers/models.d.ts +3 -3
- package/lib/providers/provider-registry.d.ts +3 -4
- package/lib/providers/provider-registry.js +1 -4
- package/lib/tokens.d.ts +5 -6
- package/lib/tools/commands.d.ts +2 -1
- package/lib/tools/commands.js +37 -46
- package/lib/tools/file.js +49 -73
- package/lib/tools/notebook.js +370 -445
- package/lib/widgets/ai-settings.d.ts +6 -0
- package/lib/widgets/ai-settings.js +72 -71
- package/lib/widgets/main-area-chat.d.ts +2 -0
- package/lib/widgets/main-area-chat.js +5 -2
- package/lib/widgets/provider-config-dialog.d.ts +2 -0
- package/lib/widgets/provider-config-dialog.js +34 -34
- package/package.json +12 -12
- package/src/agent.ts +342 -361
- package/src/approval-buttons.ts +43 -389
- package/src/chat-model-registry.ts +9 -1
- package/src/chat-model.ts +355 -370
- package/src/completion/completion-provider.ts +2 -3
- package/src/components/clear-button.tsx +16 -3
- package/src/components/completion-status.tsx +18 -4
- package/src/components/model-select.tsx +21 -8
- package/src/components/stop-button.tsx +16 -3
- package/src/components/token-usage-display.tsx +14 -2
- package/src/components/tool-select.tsx +23 -5
- package/src/index.ts +75 -36
- package/src/models/settings-model.ts +1 -1
- package/src/providers/built-in-providers.ts +38 -19
- package/src/providers/models.ts +3 -3
- package/src/providers/provider-registry.ts +4 -8
- package/src/tokens.ts +5 -6
- package/src/tools/commands.ts +39 -50
- package/src/tools/file.ts +49 -75
- package/src/tools/notebook.ts +451 -510
- package/src/widgets/ai-settings.tsx +153 -84
- package/src/widgets/main-area-chat.ts +8 -2
- package/src/widgets/provider-config-dialog.tsx +54 -41
- package/style/base.css +13 -73
- package/lib/mcp/browser.d.ts +0 -68
- package/lib/mcp/browser.js +0 -138
- package/src/mcp/browser.ts +0 -220
package/lib/tools/notebook.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CodeCell, MarkdownCell } from '@jupyterlab/cells';
|
|
2
2
|
import { DocumentWidget } from '@jupyterlab/docregistry';
|
|
3
3
|
import { NotebookPanel } from '@jupyterlab/notebook';
|
|
4
|
-
import { tool } from '
|
|
4
|
+
import { tool } from 'ai';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
/**
|
|
7
7
|
* Find a kernel name that matches the specified language
|
|
@@ -64,9 +64,9 @@ async function getNotebookWidget(notebookPath, docManager, notebookTracker) {
|
|
|
64
64
|
*/
|
|
65
65
|
export function createNotebookCreationTool(docManager, kernelSpecManager) {
|
|
66
66
|
return tool({
|
|
67
|
-
|
|
67
|
+
title: 'Create Notebook',
|
|
68
68
|
description: 'Create a new Jupyter notebook with a kernel for the specified programming language',
|
|
69
|
-
|
|
69
|
+
inputSchema: z.object({
|
|
70
70
|
language: z
|
|
71
71
|
.string()
|
|
72
72
|
.optional()
|
|
@@ -82,42 +82,40 @@ export function createNotebookCreationTool(docManager, kernelSpecManager) {
|
|
|
82
82
|
const kernel = await findKernelByLanguage(kernelSpecManager, input.language);
|
|
83
83
|
const { name } = input;
|
|
84
84
|
if (!name) {
|
|
85
|
-
throw new Error('A name must be provided to create a notebook');
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
// TODO: handle cwd / path?
|
|
89
|
-
const fileName = name.endsWith('.ipynb') ? name : `${name}.ipynb`;
|
|
90
|
-
// Create untitled notebook first
|
|
91
|
-
const notebookModel = await docManager.newUntitled({
|
|
92
|
-
type: 'notebook'
|
|
93
|
-
});
|
|
94
|
-
// Rename to desired filename
|
|
95
|
-
await docManager.services.contents.rename(notebookModel.path, fileName);
|
|
96
|
-
// Create widget with specific kernel
|
|
97
|
-
const notebook = docManager.createNew(fileName, 'default', {
|
|
98
|
-
name: kernel
|
|
99
|
-
});
|
|
100
|
-
if (!(notebook instanceof DocumentWidget)) {
|
|
101
|
-
throw new Error('Failed to create notebook widget');
|
|
102
|
-
}
|
|
103
|
-
await notebook.context.ready;
|
|
104
|
-
await notebook.context.save();
|
|
105
|
-
docManager.openOrReveal(fileName);
|
|
106
85
|
return {
|
|
107
|
-
success:
|
|
108
|
-
|
|
109
|
-
notebookPath: fileName,
|
|
110
|
-
notebookName: fileName,
|
|
111
|
-
kernel,
|
|
112
|
-
language: input.language
|
|
86
|
+
success: false,
|
|
87
|
+
error: 'A name must be provided to create a notebook'
|
|
113
88
|
};
|
|
114
89
|
}
|
|
115
|
-
|
|
90
|
+
// TODO: handle cwd / path?
|
|
91
|
+
const fileName = name.endsWith('.ipynb') ? name : `${name}.ipynb`;
|
|
92
|
+
// Create untitled notebook first
|
|
93
|
+
const notebookModel = await docManager.newUntitled({
|
|
94
|
+
type: 'notebook'
|
|
95
|
+
});
|
|
96
|
+
// Rename to desired filename
|
|
97
|
+
await docManager.services.contents.rename(notebookModel.path, fileName);
|
|
98
|
+
// Create widget with specific kernel
|
|
99
|
+
const notebook = docManager.createNew(fileName, 'default', {
|
|
100
|
+
name: kernel
|
|
101
|
+
});
|
|
102
|
+
if (!(notebook instanceof DocumentWidget)) {
|
|
116
103
|
return {
|
|
117
104
|
success: false,
|
|
118
|
-
error:
|
|
105
|
+
error: 'Failed to create notebook widget'
|
|
119
106
|
};
|
|
120
107
|
}
|
|
108
|
+
await notebook.context.ready;
|
|
109
|
+
await notebook.context.save();
|
|
110
|
+
docManager.openOrReveal(fileName);
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
message: `Successfully created notebook ${fileName} with ${kernel} kernel${input.language ? ` for ${input.language}` : ''}`,
|
|
114
|
+
notebookPath: fileName,
|
|
115
|
+
notebookName: fileName,
|
|
116
|
+
kernel,
|
|
117
|
+
language: input.language
|
|
118
|
+
};
|
|
121
119
|
}
|
|
122
120
|
});
|
|
123
121
|
}
|
|
@@ -126,9 +124,9 @@ export function createNotebookCreationTool(docManager, kernelSpecManager) {
|
|
|
126
124
|
*/
|
|
127
125
|
export function createAddCellTool(docManager, notebookTracker) {
|
|
128
126
|
return tool({
|
|
129
|
-
|
|
127
|
+
title: 'Add Cell',
|
|
130
128
|
description: 'Add a cell to the current notebook with optional content',
|
|
131
|
-
|
|
129
|
+
inputSchema: z.object({
|
|
132
130
|
notebookPath: z
|
|
133
131
|
.string()
|
|
134
132
|
.optional()
|
|
@@ -149,67 +147,59 @@ export function createAddCellTool(docManager, notebookTracker) {
|
|
|
149
147
|
.default('below')
|
|
150
148
|
.describe('Position relative to current cell')
|
|
151
149
|
}),
|
|
152
|
-
async
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (!currentWidget) {
|
|
156
|
-
return {
|
|
157
|
-
success: false,
|
|
158
|
-
error: notebookPath
|
|
159
|
-
? `Failed to open notebook at path: ${notebookPath}`
|
|
160
|
-
: 'No active notebook and no notebook path provided'
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
const notebook = currentWidget.content;
|
|
164
|
-
const model = notebook.model;
|
|
165
|
-
if (!model) {
|
|
166
|
-
return {
|
|
167
|
-
success: false,
|
|
168
|
-
error: 'No notebook model available'
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
// Check if we should replace the first empty cell instead of adding
|
|
172
|
-
const shouldReplaceFirstCell = model.cells.length === 1 &&
|
|
173
|
-
model.cells.get(0).sharedModel.getSource().trim() === '';
|
|
174
|
-
if (shouldReplaceFirstCell) {
|
|
175
|
-
// Replace the first empty cell by removing it and adding new one
|
|
176
|
-
model.sharedModel.deleteCell(0);
|
|
177
|
-
}
|
|
178
|
-
// Create the new cell using shared model
|
|
179
|
-
const newCellData = {
|
|
180
|
-
cell_type: cellType,
|
|
181
|
-
source: content || '',
|
|
182
|
-
metadata: cellType === 'code' ? { trusted: true } : {}
|
|
183
|
-
};
|
|
184
|
-
model.sharedModel.addCell(newCellData);
|
|
185
|
-
// Execute markdown cells after creation to render them
|
|
186
|
-
if (cellType === 'markdown' && content) {
|
|
187
|
-
const cellIndex = model.cells.length - 1;
|
|
188
|
-
const cellWidget = notebook.widgets[cellIndex];
|
|
189
|
-
if (cellWidget && cellWidget instanceof MarkdownCell) {
|
|
190
|
-
try {
|
|
191
|
-
await cellWidget.ready;
|
|
192
|
-
cellWidget.rendered = true;
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
console.warn('Failed to render markdown cell:', error);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
150
|
+
execute: async ({ notebookPath, content, cellType = 'code', position = 'below' }) => {
|
|
151
|
+
const currentWidget = await getNotebookWidget(notebookPath, docManager, notebookTracker);
|
|
152
|
+
if (!currentWidget) {
|
|
199
153
|
return {
|
|
200
|
-
success:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
position
|
|
154
|
+
success: false,
|
|
155
|
+
error: notebookPath
|
|
156
|
+
? `Failed to open notebook at path: ${notebookPath}`
|
|
157
|
+
: 'No active notebook and no notebook path provided'
|
|
205
158
|
};
|
|
206
159
|
}
|
|
207
|
-
|
|
160
|
+
const notebook = currentWidget.content;
|
|
161
|
+
const model = notebook.model;
|
|
162
|
+
if (!model) {
|
|
208
163
|
return {
|
|
209
164
|
success: false,
|
|
210
|
-
error:
|
|
165
|
+
error: 'No notebook model available'
|
|
211
166
|
};
|
|
212
167
|
}
|
|
168
|
+
// Check if we should replace the first empty cell instead of adding
|
|
169
|
+
const shouldReplaceFirstCell = model.cells.length === 1 &&
|
|
170
|
+
model.cells.get(0).sharedModel.getSource().trim() === '';
|
|
171
|
+
if (shouldReplaceFirstCell) {
|
|
172
|
+
// Replace the first empty cell by removing it and adding new one
|
|
173
|
+
model.sharedModel.deleteCell(0);
|
|
174
|
+
}
|
|
175
|
+
// Create the new cell using shared model
|
|
176
|
+
const newCellData = {
|
|
177
|
+
cell_type: cellType,
|
|
178
|
+
source: content || '',
|
|
179
|
+
metadata: cellType === 'code' ? { trusted: true } : {}
|
|
180
|
+
};
|
|
181
|
+
model.sharedModel.addCell(newCellData);
|
|
182
|
+
// Execute markdown cells after creation to render them
|
|
183
|
+
if (cellType === 'markdown' && content) {
|
|
184
|
+
const cellIndex = model.cells.length - 1;
|
|
185
|
+
const cellWidget = notebook.widgets[cellIndex];
|
|
186
|
+
if (cellWidget && cellWidget instanceof MarkdownCell) {
|
|
187
|
+
try {
|
|
188
|
+
await cellWidget.ready;
|
|
189
|
+
cellWidget.rendered = true;
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
console.warn('Failed to render markdown cell:', error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
success: true,
|
|
198
|
+
message: `${cellType} cell added successfully`,
|
|
199
|
+
content: content || '',
|
|
200
|
+
cellType,
|
|
201
|
+
position
|
|
202
|
+
};
|
|
213
203
|
}
|
|
214
204
|
});
|
|
215
205
|
}
|
|
@@ -218,9 +208,9 @@ export function createAddCellTool(docManager, notebookTracker) {
|
|
|
218
208
|
*/
|
|
219
209
|
export function createGetNotebookInfoTool(docManager, notebookTracker) {
|
|
220
210
|
return tool({
|
|
221
|
-
|
|
211
|
+
title: 'Get Notebook Info',
|
|
222
212
|
description: 'Get information about a notebook including number of cells and active cell index',
|
|
223
|
-
|
|
213
|
+
inputSchema: z.object({
|
|
224
214
|
notebookPath: z
|
|
225
215
|
.string()
|
|
226
216
|
.optional()
|
|
@@ -229,44 +219,36 @@ export function createGetNotebookInfoTool(docManager, notebookTracker) {
|
|
|
229
219
|
}),
|
|
230
220
|
execute: async (input) => {
|
|
231
221
|
const { notebookPath } = input;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (!currentWidget) {
|
|
235
|
-
return JSON.stringify({
|
|
236
|
-
success: false,
|
|
237
|
-
error: notebookPath
|
|
238
|
-
? `Failed to open notebook at path: ${notebookPath}`
|
|
239
|
-
: 'No active notebook and no notebook path provided'
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
const notebook = currentWidget.content;
|
|
243
|
-
const model = notebook.model;
|
|
244
|
-
if (!model) {
|
|
245
|
-
return JSON.stringify({
|
|
246
|
-
success: false,
|
|
247
|
-
error: 'No notebook model available'
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
const cellCount = model.cells.length;
|
|
251
|
-
const activeCellIndex = notebook.activeCellIndex;
|
|
252
|
-
const activeCell = notebook.activeCell;
|
|
253
|
-
const activeCellType = activeCell?.model.type || 'unknown';
|
|
222
|
+
const currentWidget = await getNotebookWidget(notebookPath, docManager, notebookTracker);
|
|
223
|
+
if (!currentWidget) {
|
|
254
224
|
return JSON.stringify({
|
|
255
|
-
success:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
activeCellIndex,
|
|
260
|
-
activeCellType,
|
|
261
|
-
isDirty: model.dirty
|
|
225
|
+
success: false,
|
|
226
|
+
error: notebookPath
|
|
227
|
+
? `Failed to open notebook at path: ${notebookPath}`
|
|
228
|
+
: 'No active notebook and no notebook path provided'
|
|
262
229
|
});
|
|
263
230
|
}
|
|
264
|
-
|
|
231
|
+
const notebook = currentWidget.content;
|
|
232
|
+
const model = notebook.model;
|
|
233
|
+
if (!model) {
|
|
265
234
|
return JSON.stringify({
|
|
266
235
|
success: false,
|
|
267
|
-
error:
|
|
236
|
+
error: 'No notebook model available'
|
|
268
237
|
});
|
|
269
238
|
}
|
|
239
|
+
const cellCount = model.cells.length;
|
|
240
|
+
const activeCellIndex = notebook.activeCellIndex;
|
|
241
|
+
const activeCell = notebook.activeCell;
|
|
242
|
+
const activeCellType = activeCell?.model.type || 'unknown';
|
|
243
|
+
return JSON.stringify({
|
|
244
|
+
success: true,
|
|
245
|
+
notebookName: currentWidget.title.label,
|
|
246
|
+
notebookPath: currentWidget.context.path,
|
|
247
|
+
cellCount,
|
|
248
|
+
activeCellIndex,
|
|
249
|
+
activeCellType,
|
|
250
|
+
isDirty: model.dirty
|
|
251
|
+
});
|
|
270
252
|
}
|
|
271
253
|
});
|
|
272
254
|
}
|
|
@@ -275,9 +257,9 @@ export function createGetNotebookInfoTool(docManager, notebookTracker) {
|
|
|
275
257
|
*/
|
|
276
258
|
export function createGetCellInfoTool(docManager, notebookTracker) {
|
|
277
259
|
return tool({
|
|
278
|
-
|
|
260
|
+
title: 'Get Cell Info',
|
|
279
261
|
description: 'Get information about a specific cell including its type, source content, and outputs',
|
|
280
|
-
|
|
262
|
+
inputSchema: z.object({
|
|
281
263
|
notebookPath: z
|
|
282
264
|
.string()
|
|
283
265
|
.optional()
|
|
@@ -292,59 +274,51 @@ export function createGetCellInfoTool(docManager, notebookTracker) {
|
|
|
292
274
|
execute: async (input) => {
|
|
293
275
|
const { notebookPath } = input;
|
|
294
276
|
let { cellIndex } = input;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (!currentWidget) {
|
|
298
|
-
return JSON.stringify({
|
|
299
|
-
success: false,
|
|
300
|
-
error: notebookPath
|
|
301
|
-
? `Failed to open notebook at path: ${notebookPath}`
|
|
302
|
-
: 'No active notebook and no notebook path provided'
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
const notebook = currentWidget.content;
|
|
306
|
-
const model = notebook.model;
|
|
307
|
-
if (!model) {
|
|
308
|
-
return JSON.stringify({
|
|
309
|
-
success: false,
|
|
310
|
-
error: 'No notebook model available'
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
if (cellIndex === undefined || cellIndex === null) {
|
|
314
|
-
cellIndex = notebook.activeCellIndex;
|
|
315
|
-
}
|
|
316
|
-
if (cellIndex < 0 || cellIndex >= model.cells.length) {
|
|
317
|
-
return JSON.stringify({
|
|
318
|
-
success: false,
|
|
319
|
-
error: `Invalid cell index: ${cellIndex}. Notebook has ${model.cells.length} cells.`
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
const cell = model.cells.get(cellIndex);
|
|
323
|
-
const cellType = cell.type;
|
|
324
|
-
const sharedModel = cell.sharedModel;
|
|
325
|
-
const source = sharedModel.getSource();
|
|
326
|
-
// Get outputs for code cells
|
|
327
|
-
let outputs = [];
|
|
328
|
-
if (cellType === 'code') {
|
|
329
|
-
const rawOutputs = sharedModel.toJSON().outputs;
|
|
330
|
-
outputs = Array.isArray(rawOutputs) ? rawOutputs : [];
|
|
331
|
-
}
|
|
277
|
+
const currentWidget = await getNotebookWidget(notebookPath, docManager, notebookTracker);
|
|
278
|
+
if (!currentWidget) {
|
|
332
279
|
return JSON.stringify({
|
|
333
|
-
success:
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
source,
|
|
338
|
-
outputs,
|
|
339
|
-
executionCount: cellType === 'code' ? cell.executionCount : null
|
|
280
|
+
success: false,
|
|
281
|
+
error: notebookPath
|
|
282
|
+
? `Failed to open notebook at path: ${notebookPath}`
|
|
283
|
+
: 'No active notebook and no notebook path provided'
|
|
340
284
|
});
|
|
341
285
|
}
|
|
342
|
-
|
|
286
|
+
const notebook = currentWidget.content;
|
|
287
|
+
const model = notebook.model;
|
|
288
|
+
if (!model) {
|
|
343
289
|
return JSON.stringify({
|
|
344
290
|
success: false,
|
|
345
|
-
error:
|
|
291
|
+
error: 'No notebook model available'
|
|
346
292
|
});
|
|
347
293
|
}
|
|
294
|
+
if (cellIndex === undefined || cellIndex === null) {
|
|
295
|
+
cellIndex = notebook.activeCellIndex;
|
|
296
|
+
}
|
|
297
|
+
if (cellIndex < 0 || cellIndex >= model.cells.length) {
|
|
298
|
+
return JSON.stringify({
|
|
299
|
+
success: false,
|
|
300
|
+
error: `Invalid cell index: ${cellIndex}. Notebook has ${model.cells.length} cells.`
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
const cell = model.cells.get(cellIndex);
|
|
304
|
+
const cellType = cell.type;
|
|
305
|
+
const sharedModel = cell.sharedModel;
|
|
306
|
+
const source = sharedModel.getSource();
|
|
307
|
+
// Get outputs for code cells
|
|
308
|
+
let outputs = [];
|
|
309
|
+
if (cellType === 'code') {
|
|
310
|
+
const rawOutputs = sharedModel.toJSON().outputs;
|
|
311
|
+
outputs = Array.isArray(rawOutputs) ? rawOutputs : [];
|
|
312
|
+
}
|
|
313
|
+
return JSON.stringify({
|
|
314
|
+
success: true,
|
|
315
|
+
cellId: cell.id,
|
|
316
|
+
cellIndex,
|
|
317
|
+
cellType,
|
|
318
|
+
source,
|
|
319
|
+
outputs,
|
|
320
|
+
executionCount: cellType === 'code' ? cell.executionCount : null
|
|
321
|
+
});
|
|
348
322
|
}
|
|
349
323
|
});
|
|
350
324
|
}
|
|
@@ -353,9 +327,9 @@ export function createGetCellInfoTool(docManager, notebookTracker) {
|
|
|
353
327
|
*/
|
|
354
328
|
export function createSetCellContentTool(docManager, notebookTracker, diffManager) {
|
|
355
329
|
return tool({
|
|
356
|
-
|
|
330
|
+
title: 'Set Cell Content',
|
|
357
331
|
description: 'Set the content of a specific cell and return both the previous and new content',
|
|
358
|
-
|
|
332
|
+
inputSchema: z.object({
|
|
359
333
|
notebookPath: z
|
|
360
334
|
.string()
|
|
361
335
|
.optional()
|
|
@@ -375,108 +349,100 @@ export function createSetCellContentTool(docManager, notebookTracker, diffManage
|
|
|
375
349
|
}),
|
|
376
350
|
execute: async (input) => {
|
|
377
351
|
const { notebookPath, cellId, cellIndex, content } = input;
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
352
|
+
const notebookWidget = await getNotebookWidget(notebookPath, docManager, notebookTracker);
|
|
353
|
+
if (!notebookWidget) {
|
|
354
|
+
return JSON.stringify({
|
|
355
|
+
success: false,
|
|
356
|
+
error: notebookPath
|
|
357
|
+
? `Failed to open notebook at path: ${notebookPath}`
|
|
358
|
+
: 'No active notebook and no notebook path provided'
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
const notebook = notebookWidget.content;
|
|
362
|
+
const targetNotebookPath = notebookWidget.context.path;
|
|
363
|
+
const model = notebook.model;
|
|
364
|
+
if (!model) {
|
|
365
|
+
return JSON.stringify({
|
|
366
|
+
success: false,
|
|
367
|
+
error: 'No notebook model available'
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
// Determine target cell index
|
|
371
|
+
let targetCellIndex;
|
|
372
|
+
if (cellId !== undefined && cellId !== null) {
|
|
373
|
+
// Find cell by ID
|
|
374
|
+
targetCellIndex = -1;
|
|
375
|
+
for (let i = 0; i < model.cells.length; i++) {
|
|
376
|
+
if (model.cells.get(i).id === cellId) {
|
|
377
|
+
targetCellIndex = i;
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (targetCellIndex === -1) {
|
|
381
382
|
return JSON.stringify({
|
|
382
383
|
success: false,
|
|
383
|
-
error:
|
|
384
|
-
? `Failed to open notebook at path: ${notebookPath}`
|
|
385
|
-
: 'No active notebook and no notebook path provided'
|
|
384
|
+
error: `Cell with ID '${cellId}' not found in notebook`
|
|
386
385
|
});
|
|
387
386
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if (
|
|
387
|
+
}
|
|
388
|
+
else if (cellIndex !== undefined && cellIndex !== null) {
|
|
389
|
+
// Use provided cell index
|
|
390
|
+
if (cellIndex < 0 || cellIndex >= model.cells.length) {
|
|
392
391
|
return JSON.stringify({
|
|
393
392
|
success: false,
|
|
394
|
-
error:
|
|
393
|
+
error: `Invalid cell index: ${cellIndex}. Notebook has ${model.cells.length} cells.`
|
|
395
394
|
});
|
|
396
395
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (model.cells.get(i).id === cellId) {
|
|
404
|
-
targetCellIndex = i;
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
if (targetCellIndex === -1) {
|
|
409
|
-
return JSON.stringify({
|
|
410
|
-
success: false,
|
|
411
|
-
error: `Cell with ID '${cellId}' not found in notebook`
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
else if (cellIndex !== undefined && cellIndex !== null) {
|
|
416
|
-
// Use provided cell index
|
|
417
|
-
if (cellIndex < 0 || cellIndex >= model.cells.length) {
|
|
418
|
-
return JSON.stringify({
|
|
419
|
-
success: false,
|
|
420
|
-
error: `Invalid cell index: ${cellIndex}. Notebook has ${model.cells.length} cells.`
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
targetCellIndex = cellIndex;
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
// Use active cell
|
|
427
|
-
targetCellIndex = notebook.activeCellIndex;
|
|
428
|
-
if (targetCellIndex === -1 || targetCellIndex >= model.cells.length) {
|
|
429
|
-
return JSON.stringify({
|
|
430
|
-
success: false,
|
|
431
|
-
error: 'No active cell or invalid active cell index'
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
// Get the target cell
|
|
436
|
-
const targetCell = model.cells.get(targetCellIndex);
|
|
437
|
-
if (!targetCell) {
|
|
396
|
+
targetCellIndex = cellIndex;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
// Use active cell
|
|
400
|
+
targetCellIndex = notebook.activeCellIndex;
|
|
401
|
+
if (targetCellIndex === -1 || targetCellIndex >= model.cells.length) {
|
|
438
402
|
return JSON.stringify({
|
|
439
403
|
success: false,
|
|
440
|
-
error:
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
const sharedModel = targetCell.sharedModel;
|
|
444
|
-
// Get previous content and type
|
|
445
|
-
const previousContent = sharedModel.getSource();
|
|
446
|
-
const previousCellType = targetCell.type;
|
|
447
|
-
const retrievedCellId = targetCell.id;
|
|
448
|
-
sharedModel.setSource(content);
|
|
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
|
|
404
|
+
error: 'No active cell or invalid active cell index'
|
|
456
405
|
});
|
|
457
406
|
}
|
|
458
|
-
return JSON.stringify({
|
|
459
|
-
success: true,
|
|
460
|
-
message: cellId !== undefined && cellId !== null
|
|
461
|
-
? `Cell with ID '${cellId}' content replaced successfully`
|
|
462
|
-
: cellIndex !== undefined && cellIndex !== null
|
|
463
|
-
? `Cell ${targetCellIndex} content replaced successfully`
|
|
464
|
-
: 'Active cell content replaced successfully',
|
|
465
|
-
notebookPath: targetNotebookPath,
|
|
466
|
-
cellId: retrievedCellId,
|
|
467
|
-
cellIndex: targetCellIndex,
|
|
468
|
-
previousContent,
|
|
469
|
-
previousCellType,
|
|
470
|
-
newContent: content,
|
|
471
|
-
wasActiveCell: cellId === undefined && cellIndex === undefined
|
|
472
|
-
});
|
|
473
407
|
}
|
|
474
|
-
|
|
408
|
+
// Get the target cell
|
|
409
|
+
const targetCell = model.cells.get(targetCellIndex);
|
|
410
|
+
if (!targetCell) {
|
|
475
411
|
return JSON.stringify({
|
|
476
412
|
success: false,
|
|
477
|
-
error: `
|
|
413
|
+
error: `Cell at index ${targetCellIndex} not found`
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
const sharedModel = targetCell.sharedModel;
|
|
417
|
+
// Get previous content and type
|
|
418
|
+
const previousContent = sharedModel.getSource();
|
|
419
|
+
const previousCellType = targetCell.type;
|
|
420
|
+
const retrievedCellId = targetCell.id;
|
|
421
|
+
sharedModel.setSource(content);
|
|
422
|
+
// Show the cell diff using the diff manager if available
|
|
423
|
+
if (diffManager) {
|
|
424
|
+
await diffManager.showCellDiff({
|
|
425
|
+
original: previousContent,
|
|
426
|
+
modified: content,
|
|
427
|
+
cellId: retrievedCellId,
|
|
428
|
+
notebookPath: targetNotebookPath
|
|
478
429
|
});
|
|
479
430
|
}
|
|
431
|
+
return JSON.stringify({
|
|
432
|
+
success: true,
|
|
433
|
+
message: cellId !== undefined && cellId !== null
|
|
434
|
+
? `Cell with ID '${cellId}' content replaced successfully`
|
|
435
|
+
: cellIndex !== undefined && cellIndex !== null
|
|
436
|
+
? `Cell ${targetCellIndex} content replaced successfully`
|
|
437
|
+
: 'Active cell content replaced successfully',
|
|
438
|
+
notebookPath: targetNotebookPath,
|
|
439
|
+
cellId: retrievedCellId,
|
|
440
|
+
cellIndex: targetCellIndex,
|
|
441
|
+
previousContent,
|
|
442
|
+
previousCellType,
|
|
443
|
+
newContent: content,
|
|
444
|
+
wasActiveCell: cellId === undefined && cellIndex === undefined
|
|
445
|
+
});
|
|
480
446
|
}
|
|
481
447
|
});
|
|
482
448
|
}
|
|
@@ -485,9 +451,9 @@ export function createSetCellContentTool(docManager, notebookTracker, diffManage
|
|
|
485
451
|
*/
|
|
486
452
|
export function createRunCellTool(docManager, notebookTracker) {
|
|
487
453
|
return tool({
|
|
488
|
-
|
|
454
|
+
title: 'Run Cell',
|
|
489
455
|
description: 'Run a specific cell in the notebook by index',
|
|
490
|
-
|
|
456
|
+
inputSchema: z.object({
|
|
491
457
|
notebookPath: z
|
|
492
458
|
.string()
|
|
493
459
|
.optional()
|
|
@@ -502,78 +468,61 @@ export function createRunCellTool(docManager, notebookTracker) {
|
|
|
502
468
|
needsApproval: true,
|
|
503
469
|
execute: async (input) => {
|
|
504
470
|
const { notebookPath, cellIndex, recordTiming = true } = input;
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
if (cellIndex < 0 || cellIndex >= model.cells.length) {
|
|
524
|
-
return JSON.stringify({
|
|
525
|
-
success: false,
|
|
526
|
-
error: `Invalid cell index: ${cellIndex}. Notebook has ${model.cells.length} cells.`
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
// Get the target cell widget
|
|
530
|
-
const cellWidget = notebook.widgets[cellIndex];
|
|
531
|
-
if (!cellWidget) {
|
|
532
|
-
return JSON.stringify({
|
|
533
|
-
success: false,
|
|
534
|
-
error: `Cell widget at index ${cellIndex} not found`
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
// Execute using shared model approach (non-disruptive)
|
|
538
|
-
try {
|
|
539
|
-
if (cellWidget instanceof CodeCell) {
|
|
540
|
-
// Use direct CodeCell.execute() method
|
|
541
|
-
const sessionCtx = currentWidget.sessionContext;
|
|
542
|
-
await CodeCell.execute(cellWidget, sessionCtx, {
|
|
543
|
-
recordTiming,
|
|
544
|
-
deletedCells: model.deletedCells
|
|
545
|
-
});
|
|
546
|
-
const codeModel = cellWidget.model;
|
|
547
|
-
return JSON.stringify({
|
|
548
|
-
success: true,
|
|
549
|
-
message: `Cell ${cellIndex} executed successfully`,
|
|
550
|
-
cellIndex,
|
|
551
|
-
executionCount: codeModel.executionCount,
|
|
552
|
-
hasOutput: codeModel.outputs.length > 0
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
// For non-code cells, just return success
|
|
557
|
-
return JSON.stringify({
|
|
558
|
-
success: true,
|
|
559
|
-
message: `Cell ${cellIndex} is not a code cell, no execution needed`,
|
|
560
|
-
cellIndex,
|
|
561
|
-
cellType: cellWidget.model.type
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
catch (error) {
|
|
566
|
-
return JSON.stringify({
|
|
567
|
-
success: false,
|
|
568
|
-
error: `Failed to execute cell: ${error.message}`,
|
|
569
|
-
cellIndex
|
|
570
|
-
});
|
|
571
|
-
}
|
|
471
|
+
const currentWidget = await getNotebookWidget(notebookPath, docManager, notebookTracker);
|
|
472
|
+
if (!currentWidget) {
|
|
473
|
+
return JSON.stringify({
|
|
474
|
+
success: false,
|
|
475
|
+
error: notebookPath
|
|
476
|
+
? `Failed to open notebook at path: ${notebookPath}`
|
|
477
|
+
: 'No active notebook and no notebook path provided'
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
const notebook = currentWidget.content;
|
|
481
|
+
const model = notebook.model;
|
|
482
|
+
if (!model) {
|
|
483
|
+
return JSON.stringify({
|
|
484
|
+
success: false,
|
|
485
|
+
error: 'No notebook model available'
|
|
486
|
+
});
|
|
572
487
|
}
|
|
573
|
-
|
|
488
|
+
if (cellIndex < 0 || cellIndex >= model.cells.length) {
|
|
574
489
|
return JSON.stringify({
|
|
575
490
|
success: false,
|
|
576
|
-
error: `
|
|
491
|
+
error: `Invalid cell index: ${cellIndex}. Notebook has ${model.cells.length} cells.`
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
// Get the target cell widget
|
|
495
|
+
const cellWidget = notebook.widgets[cellIndex];
|
|
496
|
+
if (!cellWidget) {
|
|
497
|
+
return JSON.stringify({
|
|
498
|
+
success: false,
|
|
499
|
+
error: `Cell widget at index ${cellIndex} not found`
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
// Execute using shared model approach (non-disruptive)
|
|
503
|
+
if (cellWidget instanceof CodeCell) {
|
|
504
|
+
// Use direct CodeCell.execute() method
|
|
505
|
+
const sessionCtx = currentWidget.sessionContext;
|
|
506
|
+
await CodeCell.execute(cellWidget, sessionCtx, {
|
|
507
|
+
recordTiming,
|
|
508
|
+
deletedCells: model.deletedCells
|
|
509
|
+
});
|
|
510
|
+
const codeModel = cellWidget.model;
|
|
511
|
+
return JSON.stringify({
|
|
512
|
+
success: true,
|
|
513
|
+
message: `Cell ${cellIndex} executed successfully`,
|
|
514
|
+
cellIndex,
|
|
515
|
+
executionCount: codeModel.executionCount,
|
|
516
|
+
hasOutput: codeModel.outputs.length > 0
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
// For non-code cells, just return success
|
|
521
|
+
return JSON.stringify({
|
|
522
|
+
success: true,
|
|
523
|
+
message: `Cell ${cellIndex} is not a code cell, no execution needed`,
|
|
524
|
+
cellIndex,
|
|
525
|
+
cellType: cellWidget.model.type
|
|
577
526
|
});
|
|
578
527
|
}
|
|
579
528
|
}
|
|
@@ -584,9 +533,9 @@ export function createRunCellTool(docManager, notebookTracker) {
|
|
|
584
533
|
*/
|
|
585
534
|
export function createDeleteCellTool(docManager, notebookTracker) {
|
|
586
535
|
return tool({
|
|
587
|
-
|
|
536
|
+
title: 'Delete Cell',
|
|
588
537
|
description: 'Delete a specific cell from the notebook by index',
|
|
589
|
-
|
|
538
|
+
inputSchema: z.object({
|
|
590
539
|
notebookPath: z
|
|
591
540
|
.string()
|
|
592
541
|
.optional()
|
|
@@ -596,53 +545,45 @@ export function createDeleteCellTool(docManager, notebookTracker) {
|
|
|
596
545
|
}),
|
|
597
546
|
execute: async (input) => {
|
|
598
547
|
const { notebookPath, cellIndex } = input;
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
if (!currentWidget) {
|
|
602
|
-
return JSON.stringify({
|
|
603
|
-
success: false,
|
|
604
|
-
error: notebookPath
|
|
605
|
-
? `Failed to open notebook at path: ${notebookPath}`
|
|
606
|
-
: 'No active notebook and no notebook path provided'
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
const notebook = currentWidget.content;
|
|
610
|
-
const model = notebook.model;
|
|
611
|
-
if (!model) {
|
|
612
|
-
return JSON.stringify({
|
|
613
|
-
success: false,
|
|
614
|
-
error: 'No notebook model available'
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
if (cellIndex < 0 || cellIndex >= model.cells.length) {
|
|
618
|
-
return JSON.stringify({
|
|
619
|
-
success: false,
|
|
620
|
-
error: `Invalid cell index: ${cellIndex}. Notebook has ${model.cells.length} cells.`
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
// Validate cell exists
|
|
624
|
-
const targetCell = model.cells.get(cellIndex);
|
|
625
|
-
if (!targetCell) {
|
|
626
|
-
return JSON.stringify({
|
|
627
|
-
success: false,
|
|
628
|
-
error: `Cell at index ${cellIndex} not found`
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
// Delete cell using shared model (non-disruptive)
|
|
632
|
-
model.sharedModel.deleteCell(cellIndex);
|
|
548
|
+
const currentWidget = await getNotebookWidget(notebookPath, docManager, notebookTracker);
|
|
549
|
+
if (!currentWidget) {
|
|
633
550
|
return JSON.stringify({
|
|
634
|
-
success:
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
551
|
+
success: false,
|
|
552
|
+
error: notebookPath
|
|
553
|
+
? `Failed to open notebook at path: ${notebookPath}`
|
|
554
|
+
: 'No active notebook and no notebook path provided'
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
const notebook = currentWidget.content;
|
|
558
|
+
const model = notebook.model;
|
|
559
|
+
if (!model) {
|
|
560
|
+
return JSON.stringify({
|
|
561
|
+
success: false,
|
|
562
|
+
error: 'No notebook model available'
|
|
638
563
|
});
|
|
639
564
|
}
|
|
640
|
-
|
|
565
|
+
if (cellIndex < 0 || cellIndex >= model.cells.length) {
|
|
641
566
|
return JSON.stringify({
|
|
642
567
|
success: false,
|
|
643
|
-
error: `
|
|
568
|
+
error: `Invalid cell index: ${cellIndex}. Notebook has ${model.cells.length} cells.`
|
|
644
569
|
});
|
|
645
570
|
}
|
|
571
|
+
// Validate cell exists
|
|
572
|
+
const targetCell = model.cells.get(cellIndex);
|
|
573
|
+
if (!targetCell) {
|
|
574
|
+
return JSON.stringify({
|
|
575
|
+
success: false,
|
|
576
|
+
error: `Cell at index ${cellIndex} not found`
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
// Delete cell using shared model (non-disruptive)
|
|
580
|
+
model.sharedModel.deleteCell(cellIndex);
|
|
581
|
+
return JSON.stringify({
|
|
582
|
+
success: true,
|
|
583
|
+
message: `Cell ${cellIndex} deleted successfully`,
|
|
584
|
+
cellIndex,
|
|
585
|
+
remainingCells: model.cells.length
|
|
586
|
+
});
|
|
646
587
|
}
|
|
647
588
|
});
|
|
648
589
|
}
|
|
@@ -651,9 +592,9 @@ export function createDeleteCellTool(docManager, notebookTracker) {
|
|
|
651
592
|
*/
|
|
652
593
|
export function createExecuteActiveCellTool(docManager, notebookTracker) {
|
|
653
594
|
return tool({
|
|
654
|
-
|
|
595
|
+
title: 'Execute Active Cell',
|
|
655
596
|
description: 'Execute the currently active cell in the notebook without disrupting user focus',
|
|
656
|
-
|
|
597
|
+
inputSchema: z.object({
|
|
657
598
|
notebookPath: z
|
|
658
599
|
.string()
|
|
659
600
|
.optional()
|
|
@@ -671,66 +612,58 @@ export function createExecuteActiveCellTool(docManager, notebookTracker) {
|
|
|
671
612
|
}),
|
|
672
613
|
execute: async (input) => {
|
|
673
614
|
const { notebookPath, code, recordTiming = true } = input;
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
: 'No active notebook and no notebook path provided'
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
const notebook = currentWidget.content;
|
|
685
|
-
const model = notebook.model;
|
|
686
|
-
const activeCellIndex = notebook.activeCellIndex;
|
|
687
|
-
if (!model || activeCellIndex === -1) {
|
|
688
|
-
return JSON.stringify({
|
|
689
|
-
success: false,
|
|
690
|
-
error: 'No notebook model or active cell available'
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
const activeCell = model.cells.get(activeCellIndex);
|
|
694
|
-
if (!activeCell) {
|
|
695
|
-
return JSON.stringify({
|
|
696
|
-
success: false,
|
|
697
|
-
error: 'Active cell not found'
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
// Set code content if provided
|
|
701
|
-
if (code) {
|
|
702
|
-
activeCell.sharedModel.setSource(code);
|
|
703
|
-
}
|
|
704
|
-
// Get the cell widget for execution
|
|
705
|
-
const cellWidget = notebook.widgets[activeCellIndex];
|
|
706
|
-
if (!cellWidget || !(cellWidget instanceof CodeCell)) {
|
|
707
|
-
return JSON.stringify({
|
|
708
|
-
success: false,
|
|
709
|
-
error: 'Active cell is not a code cell'
|
|
710
|
-
});
|
|
711
|
-
}
|
|
712
|
-
// Execute using shared model approach (non-disruptive)
|
|
713
|
-
const sessionCtx = currentWidget.sessionContext;
|
|
714
|
-
await CodeCell.execute(cellWidget, sessionCtx, {
|
|
715
|
-
recordTiming,
|
|
716
|
-
deletedCells: model.deletedCells
|
|
615
|
+
const currentWidget = await getNotebookWidget(notebookPath, docManager, notebookTracker);
|
|
616
|
+
if (!currentWidget) {
|
|
617
|
+
return JSON.stringify({
|
|
618
|
+
success: false,
|
|
619
|
+
error: notebookPath
|
|
620
|
+
? `Failed to open notebook at path: ${notebookPath}`
|
|
621
|
+
: 'No active notebook and no notebook path provided'
|
|
717
622
|
});
|
|
718
|
-
|
|
623
|
+
}
|
|
624
|
+
const notebook = currentWidget.content;
|
|
625
|
+
const model = notebook.model;
|
|
626
|
+
const activeCellIndex = notebook.activeCellIndex;
|
|
627
|
+
if (!model || activeCellIndex === -1) {
|
|
719
628
|
return JSON.stringify({
|
|
720
|
-
success:
|
|
721
|
-
|
|
722
|
-
cellIndex: activeCellIndex,
|
|
723
|
-
executionCount: codeModel.executionCount,
|
|
724
|
-
hasOutput: codeModel.outputs.length > 0,
|
|
725
|
-
code: code || activeCell.sharedModel.getSource()
|
|
629
|
+
success: false,
|
|
630
|
+
error: 'No notebook model or active cell available'
|
|
726
631
|
});
|
|
727
632
|
}
|
|
728
|
-
|
|
633
|
+
const activeCell = model.cells.get(activeCellIndex);
|
|
634
|
+
if (!activeCell) {
|
|
729
635
|
return JSON.stringify({
|
|
730
636
|
success: false,
|
|
731
|
-
error:
|
|
637
|
+
error: 'Active cell not found'
|
|
732
638
|
});
|
|
733
639
|
}
|
|
640
|
+
// Set code content if provided
|
|
641
|
+
if (code) {
|
|
642
|
+
activeCell.sharedModel.setSource(code);
|
|
643
|
+
}
|
|
644
|
+
// Get the cell widget for execution
|
|
645
|
+
const cellWidget = notebook.widgets[activeCellIndex];
|
|
646
|
+
if (!cellWidget || !(cellWidget instanceof CodeCell)) {
|
|
647
|
+
return JSON.stringify({
|
|
648
|
+
success: false,
|
|
649
|
+
error: 'Active cell is not a code cell'
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
// Execute using shared model approach (non-disruptive)
|
|
653
|
+
const sessionCtx = currentWidget.sessionContext;
|
|
654
|
+
await CodeCell.execute(cellWidget, sessionCtx, {
|
|
655
|
+
recordTiming,
|
|
656
|
+
deletedCells: model.deletedCells
|
|
657
|
+
});
|
|
658
|
+
const codeModel = cellWidget.model;
|
|
659
|
+
return JSON.stringify({
|
|
660
|
+
success: true,
|
|
661
|
+
message: 'Code executed successfully in active cell',
|
|
662
|
+
cellIndex: activeCellIndex,
|
|
663
|
+
executionCount: codeModel.executionCount,
|
|
664
|
+
hasOutput: codeModel.outputs.length > 0,
|
|
665
|
+
code: code || activeCell.sharedModel.getSource()
|
|
666
|
+
});
|
|
734
667
|
}
|
|
735
668
|
});
|
|
736
669
|
}
|
|
@@ -739,9 +672,9 @@ export function createExecuteActiveCellTool(docManager, notebookTracker) {
|
|
|
739
672
|
*/
|
|
740
673
|
export function createSaveNotebookTool(docManager, notebookTracker) {
|
|
741
674
|
return tool({
|
|
742
|
-
|
|
675
|
+
title: 'Save Notebook',
|
|
743
676
|
description: 'Save a specific notebook to disk',
|
|
744
|
-
|
|
677
|
+
inputSchema: z.object({
|
|
745
678
|
notebookPath: z
|
|
746
679
|
.string()
|
|
747
680
|
.optional()
|
|
@@ -750,30 +683,22 @@ export function createSaveNotebookTool(docManager, notebookTracker) {
|
|
|
750
683
|
}),
|
|
751
684
|
execute: async (input) => {
|
|
752
685
|
const { notebookPath } = input;
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
if (!currentWidget) {
|
|
756
|
-
return JSON.stringify({
|
|
757
|
-
success: false,
|
|
758
|
-
error: notebookPath
|
|
759
|
-
? `Failed to open notebook at path: ${notebookPath}`
|
|
760
|
-
: 'No active notebook and no notebook path provided'
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
await currentWidget.context.save();
|
|
764
|
-
return JSON.stringify({
|
|
765
|
-
success: true,
|
|
766
|
-
message: 'Notebook saved successfully',
|
|
767
|
-
notebookName: currentWidget.title.label,
|
|
768
|
-
notebookPath: currentWidget.context.path
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
catch (error) {
|
|
686
|
+
const currentWidget = await getNotebookWidget(notebookPath, docManager, notebookTracker);
|
|
687
|
+
if (!currentWidget) {
|
|
772
688
|
return JSON.stringify({
|
|
773
689
|
success: false,
|
|
774
|
-
error:
|
|
690
|
+
error: notebookPath
|
|
691
|
+
? `Failed to open notebook at path: ${notebookPath}`
|
|
692
|
+
: 'No active notebook and no notebook path provided'
|
|
775
693
|
});
|
|
776
694
|
}
|
|
695
|
+
await currentWidget.context.save();
|
|
696
|
+
return JSON.stringify({
|
|
697
|
+
success: true,
|
|
698
|
+
message: 'Notebook saved successfully',
|
|
699
|
+
notebookName: currentWidget.title.label,
|
|
700
|
+
notebookPath: currentWidget.context.path
|
|
701
|
+
});
|
|
777
702
|
}
|
|
778
703
|
});
|
|
779
704
|
}
|