Skip to content

Commit 20859e3

Browse files
authored
Add api command for fetching notebook variables (microsoft#205224)
1 parent b3b6068 commit 20859e3

File tree

3 files changed

+111
-1
lines changed

3 files changed

+111
-1
lines changed

src/vs/workbench/api/common/extHostNotebookKernels.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { ResourceMap } from 'vs/base/common/map';
1212
import { URI, UriComponents } from 'vs/base/common/uri';
1313
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
1414
import { ILogService } from 'vs/platform/log/common/log';
15-
import { ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, IMainContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape, NotebookOutputDto } from 'vs/workbench/api/common/extHost.protocol';
15+
import { ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, IMainContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape, NotebookOutputDto, VariablesResult } from 'vs/workbench/api/common/extHost.protocol';
1616
import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
1717
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
1818
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
@@ -90,7 +90,24 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
9090
})
9191
],
9292
ApiCommandResult.Void);
93+
94+
const requestKernelVariablesApiCommand = new ApiCommand(
95+
'vscode.executeNotebookVariableProvider',
96+
'_executeNotebookVariableProvider',
97+
'Execute notebook variable provider',
98+
[ApiCommandArgument.Uri],
99+
new ApiCommandResult<VariablesResult[], vscode.Variable[]>('A promise that resolves to an array of variables', (value, apiArgs) => {
100+
return value.map(variable => {
101+
return {
102+
name: variable.name,
103+
value: variable.value,
104+
editable: false
105+
};
106+
});
107+
})
108+
);
93109
this._commands.registerApiCommand(selectKernelApiCommand);
110+
this._commands.registerApiCommand(requestKernelVariablesApiCommand);
94111
}
95112

96113
createNotebookController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler?: (cells: vscode.NotebookCell[], notebook: vscode.NotebookDocument, controller: vscode.NotebookController) => void | Thenable<void>, preloads?: vscode.NotebookRendererScript[]): vscode.NotebookController {

src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariableCommands.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { CancellationToken } from 'vs/base/common/cancellation';
7+
import { URI, UriComponents } from 'vs/base/common/uri';
68
import { localize } from 'vs/nls';
79
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
810
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
911
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
1012
import { contextMenuArg } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView';
13+
import { INotebookKernelService, VariablesResult } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
14+
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
1115

1216
export const COPY_NOTEBOOK_VARIABLE_VALUE_ID = 'workbench.debug.viewlet.action.copyWorkspaceVariableValue';
1317
export const COPY_NOTEBOOK_VARIABLE_VALUE_LABEL = localize('copyWorkspaceVariableValue', "Copy Value");
@@ -28,3 +32,39 @@ registerAction2(class extends Action2 {
2832
}
2933
}
3034
});
35+
36+
37+
registerAction2(class extends Action2 {
38+
constructor() {
39+
super({
40+
id: '_executeNotebookVariableProvider',
41+
title: localize('executeNotebookVariableProvider', "Execute Notebook Variable Provider"),
42+
f1: false,
43+
});
44+
}
45+
46+
async run(accessor: ServicesAccessor, resource: UriComponents | undefined): Promise<VariablesResult[]> {
47+
if (!resource) {
48+
return [];
49+
}
50+
51+
const uri = URI.revive(resource);
52+
const notebookKernelService = accessor.get(INotebookKernelService);
53+
const notebookService = accessor.get(INotebookService);
54+
const notebookTextModel = notebookService.getNotebookTextModel(uri);
55+
56+
if (!notebookTextModel) {
57+
return [];
58+
}
59+
60+
const selectedKernel = notebookKernelService.getMatchingKernel(notebookTextModel).selected;
61+
if (selectedKernel && selectedKernel.hasVariableProvider) {
62+
const variables = selectedKernel.provideVariables(notebookTextModel.uri, undefined, 'named', 0, CancellationToken.None);
63+
return await variables
64+
.map(variable => { return variable; })
65+
.toPromise();
66+
}
67+
68+
return [];
69+
}
70+
});

src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ export class NotebookChatController extends Disposable implements INotebookEdito
176176
private _strategy: EditStrategy | undefined;
177177
private _sessionCtor: CancelablePromise<void> | undefined;
178178
private _activeSession?: Session;
179+
private _warmupRequestCts?: CancellationTokenSource;
179180
private readonly _ctxHasActiveRequest: IContextKey<boolean>;
180181
private readonly _ctxCellWidgetFocused: IContextKey<boolean>;
181182
private readonly _ctxUserDidEdit: IContextKey<boolean>;
@@ -275,6 +276,10 @@ export class NotebookChatController extends Disposable implements INotebookEdito
275276
inlineChatWidget.placeholder = localize('default.placeholder', "Ask a question");
276277
inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated code may be incorrect"));
277278
widgetContainer.appendChild(inlineChatWidget.domNode);
279+
this._widgetDisposableStore.add(inlineChatWidget.onDidChangeInput(() => {
280+
this._warmupRequestCts?.dispose(true);
281+
this._warmupRequestCts = undefined;
282+
}));
278283

279284
this._notebookEditor.changeViewZones(accessor => {
280285
const notebookViewZone = {
@@ -307,6 +312,8 @@ export class NotebookChatController extends Disposable implements INotebookEdito
307312

308313
if (fakeParentEditor.hasModel()) {
309314
await this._startSession(fakeParentEditor, token);
315+
this._warmupRequestCts = new CancellationTokenSource();
316+
this._startInitialFolowups(fakeParentEditor, this._warmupRequestCts.token);
310317

311318
if (this._widget) {
312319
this._widget.inlineChatWidget.placeholder = this._activeSession?.session.placeholder ?? localize('default.placeholder', "Ask a question");
@@ -368,6 +375,8 @@ export class NotebookChatController extends Disposable implements INotebookEdito
368375
async acceptInput() {
369376
assertType(this._activeSession);
370377
assertType(this._widget);
378+
this._warmupRequestCts?.dispose(true);
379+
this._warmupRequestCts = undefined;
371380
this._activeSession.addInput(new SessionPrompt(this._widget.inlineChatWidget.value));
372381

373382
assertType(this._activeSession.lastInput);
@@ -559,6 +568,50 @@ export class NotebookChatController extends Disposable implements INotebookEdito
559568
this._strategy = new EditStrategy(session);
560569
}
561570

571+
private async _startInitialFolowups(editor: IActiveCodeEditor, token: CancellationToken) {
572+
if (!this._activeSession || !this._activeSession.provider.provideFollowups) {
573+
return;
574+
}
575+
576+
const request: IInlineChatRequest = {
577+
requestId: generateUuid(),
578+
prompt: '',
579+
attempt: 0,
580+
selection: { selectionStartLineNumber: 1, selectionStartColumn: 1, positionLineNumber: 1, positionColumn: 1 },
581+
wholeRange: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 },
582+
live: true,
583+
previewDocument: editor.getModel().uri,
584+
withIntentDetection: true
585+
};
586+
587+
const progress = new AsyncProgress<IInlineChatProgressItem>(async data => { });
588+
const task = this._activeSession.provider.provideResponse(this._activeSession.session, request, progress, token);
589+
const reply = await raceCancellationError(Promise.resolve(task), token);
590+
if (token.isCancellationRequested) {
591+
return;
592+
}
593+
594+
if (!reply) {
595+
return;
596+
}
597+
598+
const markdownContents = new MarkdownString('', { supportThemeIcons: true, supportHtml: true, isTrusted: false });
599+
const response = this._instantiationService.createInstance(ReplyResponse, reply, markdownContents, this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), [], request.requestId);
600+
const followups = await this._activeSession.provider.provideFollowups(this._activeSession.session, response.raw, token);
601+
if (followups && this._widget) {
602+
const widget = this._widget;
603+
widget.inlineChatWidget.updateFollowUps(followups, async followup => {
604+
if (followup.kind === 'reply') {
605+
widget.inlineChatWidget.value = followup.message;
606+
this.acceptInput();
607+
} else {
608+
await this.acceptSession();
609+
this._commandService.executeCommand(followup.commandId, ...(followup.args ?? []));
610+
}
611+
});
612+
}
613+
}
614+
562615
private async _makeChanges(edits: TextEdit[], opts: ProgressingEditsOptions | undefined) {
563616
assertType(this._activeSession);
564617
assertType(this._strategy);

0 commit comments

Comments
 (0)