From f15e46a466c1c8ca8a32abdb001b9de64e1604ad Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 13 Nov 2023 23:18:55 +0100 Subject: [PATCH 1/2] followup --- .../api/browser/mainThreadInlineChat.ts | 5 ++++- .../workbench/api/common/extHost.protocol.ts | 5 +++-- .../workbench/api/common/extHostInlineChat.ts | 21 +++++++++++++++++-- .../browser/inlineChatController.ts | 14 +++++++++++++ .../contrib/inlineChat/common/inlineChat.ts | 7 +++++++ .../vscode.proposed.interactive.d.ts | 6 ++++++ 6 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadInlineChat.ts b/src/vs/workbench/api/browser/mainThreadInlineChat.ts index 27e8deeb72b6e..e7207c1a8f5d2 100644 --- a/src/vs/workbench/api/browser/mainThreadInlineChat.ts +++ b/src/vs/workbench/api/browser/mainThreadInlineChat.ts @@ -31,7 +31,7 @@ export class MainThreadInlineChat implements MainThreadInlineChatShape { this._registrations.dispose(); } - async $registerInteractiveEditorProvider(handle: number, label: string, debugName: string, supportsFeedback: boolean): Promise { + async $registerInteractiveEditorProvider(handle: number, label: string, debugName: string, supportsFeedback: boolean, supportsFollowups: boolean): Promise { const unreg = this._inlineChatService.addProvider({ debugName, label, @@ -59,6 +59,9 @@ export class MainThreadInlineChat implements MainThreadInlineChatShape { this._progresses.delete(request.requestId); } }, + provideFollowups: !supportsFollowups ? undefined : async (session, response, token) => { + return this._proxy.$provideFollowups(handle, session.id, response.id, token); + }, handleInlineChatResponseFeedback: !supportsFeedback ? undefined : async (session, response, kind) => { this._proxy.$handleFeedback(handle, session.id, response.id, kind); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index a01697db5a6d7..de37daa8f9a80 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -55,7 +55,7 @@ import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } fr import { IChatAgentDetection, IChatDynamicRequest, IChatFollowup, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug'; -import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatMessageResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatMessageResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind, InteractiveEditorReplyFollowup } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import * as notebookCommon from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { ICellExecutionComplete, ICellExecutionStateUpdate } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; @@ -1196,7 +1196,7 @@ export interface ExtHostChatVariablesShape { } export interface MainThreadInlineChatShape extends IDisposable { - $registerInteractiveEditorProvider(handle: number, label: string, debugName: string, supportsFeedback: boolean): Promise; + $registerInteractiveEditorProvider(handle: number, label: string, debugName: string, supportsFeedback: boolean, supportsFollowups: boolean): Promise; $handleProgressChunk(requestId: string, chunk: Dto): Promise; $unregisterInteractiveEditorProvider(handle: number): Promise; } @@ -1206,6 +1206,7 @@ export type IInlineChatResponseDto = Dto; $provideResponse(handle: number, session: IInlineChatSession, request: IInlineChatRequest, token: CancellationToken): Promise; + $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise; $handleFeedback(handle: number, sessionId: number, responseId: number, kind: InlineChatResponseFeedbackKind): void; $releaseSession(handle: number, sessionId: number): void; } diff --git a/src/vs/workbench/api/common/extHostInlineChat.ts b/src/vs/workbench/api/common/extHostInlineChat.ts index 0b2c0ee02ce90..0bcd689e9b9d2 100644 --- a/src/vs/workbench/api/common/extHostInlineChat.ts +++ b/src/vs/workbench/api/common/extHostInlineChat.ts @@ -7,7 +7,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { toDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ISelection } from 'vs/editor/common/core/selection'; -import { IInlineChatSession, IInlineChatRequest, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IInlineChatSession, IInlineChatRequest, InlineChatResponseFeedbackKind, InlineChatResponseType, InteractiveEditorReplyFollowup } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostInlineChatShape, IInlineChatResponseDto, IMainContext, MainContext, MainThreadInlineChatShape } from 'vs/workbench/api/common/extHost.protocol'; @@ -96,7 +96,7 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape { registerProvider(extension: Readonly, provider: vscode.InteractiveEditorSessionProvider, metadata: vscode.InteractiveEditorSessionProviderMetadata): vscode.Disposable { const wrapper = new ProviderWrapper(extension, provider); this._inputProvider.set(wrapper.handle, wrapper); - this._proxy.$registerInteractiveEditorProvider(wrapper.handle, metadata.label, extension.identifier.value, typeof provider.handleInteractiveEditorResponseFeedback === 'function'); + this._proxy.$registerInteractiveEditorProvider(wrapper.handle, metadata.label, extension.identifier.value, typeof provider.handleInteractiveEditorResponseFeedback === 'function', typeof provider.provideFollowups === 'function'); return toDisposable(() => { this._proxy.$unregisterInteractiveEditorProvider(wrapper.handle); this._inputProvider.delete(wrapper.handle); @@ -223,6 +223,23 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape { }; } + async $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise { + const entry = this._inputProvider.get(handle); + const sessionData = this._inputSessions.get(sessionId); + const response = sessionData?.responses[responseId]; + if (entry && response) { + const task = Promise.resolve(entry.provider.provideFollowups?.(sessionData.session, response, token)); + const res = await raceCancellation(task, token); + if (res) { + return res.map(f => ({ + message: typeConvert.MarkdownString.from(f.contents), + })); + } + } + return undefined; + } + + $handleFeedback(handle: number, sessionId: number, responseId: number, kind: InlineChatResponseFeedbackKind): void { const entry = this._inputProvider.get(handle); const sessionData = this._inputSessions.get(sessionId); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 4c42fac250f46..87f2f6a781c53 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -690,6 +690,20 @@ export class InlineChatController implements IEditorContribution { a11yResponse = this._strategy.checkChanges(replyResponse) && a11yVerboseInlineChat ? a11yMessageResponse ? localize('editResponseMessage2', "{0}, also review proposed changes in the diff editor.", a11yMessageResponse) : localize('editResponseMessage', "Review proposed changes in the diff editor.") : a11yMessageResponse; + + + if (this._activeSession.provider.provideFollowups && replyResponse.responseType !== InlineChateResponseTypes.OnlyEdits) { + const followupCts = new CancellationTokenSource(requestCts.token); + const followupTask = this._activeSession.provider.provideFollowups(this._activeSession.session, reply, followupCts.token); + this._log('followup request started', this._activeSession.provider.debugName, this._activeSession.session, reply); + const followupReply = await raceCancellationError(Promise.resolve(followupTask), followupCts.token); + if (followupReply) { + this._log('followup request receiver', this._activeSession.provider.debugName, this._activeSession.session, followupReply); + for (const message of followupReply) { + markdownContents.appendMarkdown(message.message.value); + } + } + } } } catch (e) { diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 2d0894d1bb8cb..79393b59e4144 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -48,6 +48,11 @@ export interface IInlineChatRequest { export type IInlineChatResponse = IInlineChatEditResponse | IInlineChatBulkEditResponse | IInlineChatMessageResponse; +export interface InteractiveEditorReplyFollowup { + message: IMarkdownString; +} + + export const enum InlineChatResponseType { EditorEdit = 'editorEdit', BulkEdit = 'bulkEdit', @@ -110,6 +115,8 @@ export interface IInlineChatSessionProvider { provideResponse(item: IInlineChatSession, request: IInlineChatRequest, progress: IProgress, token: CancellationToken): ProviderResult; + provideFollowups?(session: IInlineChatSession, response: IInlineChatResponse, token: CancellationToken): ProviderResult; + handleInlineChatResponseFeedback?(session: IInlineChatSession, response: IInlineChatResponse, kind: InlineChatResponseFeedbackKind): void; } diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index 8595d59c3ab42..24d93b0bb7381 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -74,6 +74,10 @@ declare module 'vscode' { label: string; } + export interface InteractiveEditorReplyFollowup { + contents: MarkdownString; + } + export interface InteractiveEditorSessionProvider { // Create a session. The lifetime of this session is the duration of the editing session with the input mode widget. @@ -81,6 +85,8 @@ declare module 'vscode' { provideInteractiveEditorResponse(session: S, request: InteractiveEditorRequest, progress: Progress, token: CancellationToken): ProviderResult; + provideFollowups?(session: S, response: R, token: CancellationToken): ProviderResult; + // eslint-disable-next-line local/vscode-dts-provider-naming handleInteractiveEditorResponseFeedback?(session: S, response: R, kind: InteractiveEditorResponseFeedbackKind): void; } From 7a451c447d0fc76eaa4f873b86c64ca8d17f5f77 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 17 Nov 2023 10:21:32 +0100 Subject: [PATCH 2/2] support provideFollowups in inline chat --- src/vs/workbench/api/common/extHost.protocol.ts | 4 ++-- .../workbench/api/common/extHostInlineChat.ts | 17 +++++++---------- .../api/common/extHostTypeConverters.ts | 2 +- .../inlineChat/browser/inlineChatController.ts | 14 -------------- .../contrib/inlineChat/common/inlineChat.ts | 8 ++------ src/vscode-dts/vscode.proposed.interactive.d.ts | 4 +++- 6 files changed, 15 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index de37daa8f9a80..279ea4aebb23c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -55,7 +55,7 @@ import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } fr import { IChatAgentDetection, IChatDynamicRequest, IChatFollowup, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug'; -import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatMessageResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind, InteractiveEditorReplyFollowup } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatMessageResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import * as notebookCommon from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { ICellExecutionComplete, ICellExecutionStateUpdate } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; @@ -1206,7 +1206,7 @@ export type IInlineChatResponseDto = Dto; $provideResponse(handle: number, session: IInlineChatSession, request: IInlineChatRequest, token: CancellationToken): Promise; - $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise; + $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise; $handleFeedback(handle: number, sessionId: number, responseId: number, kind: InlineChatResponseFeedbackKind): void; $releaseSession(handle: number, sessionId: number): void; } diff --git a/src/vs/workbench/api/common/extHostInlineChat.ts b/src/vs/workbench/api/common/extHostInlineChat.ts index 0bcd689e9b9d2..7854b9611d219 100644 --- a/src/vs/workbench/api/common/extHostInlineChat.ts +++ b/src/vs/workbench/api/common/extHostInlineChat.ts @@ -7,7 +7,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { toDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ISelection } from 'vs/editor/common/core/selection'; -import { IInlineChatSession, IInlineChatRequest, InlineChatResponseFeedbackKind, InlineChatResponseType, InteractiveEditorReplyFollowup } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IInlineChatSession, IInlineChatRequest, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostInlineChatShape, IInlineChatResponseDto, IMainContext, MainContext, MainThreadInlineChatShape } from 'vs/workbench/api/common/extHost.protocol'; @@ -19,6 +19,7 @@ import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } fro import { IRange } from 'vs/editor/common/core/range'; import { IPosition } from 'vs/editor/common/core/position'; import { raceCancellation } from 'vs/base/common/async'; +import { IChatReplyFollowup } from 'vs/workbench/contrib/chat/common/chatService'; class ProviderWrapper { @@ -223,18 +224,14 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape { }; } - async $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise { + async $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise { const entry = this._inputProvider.get(handle); const sessionData = this._inputSessions.get(sessionId); const response = sessionData?.responses[responseId]; - if (entry && response) { - const task = Promise.resolve(entry.provider.provideFollowups?.(sessionData.session, response, token)); - const res = await raceCancellation(task, token); - if (res) { - return res.map(f => ({ - message: typeConvert.MarkdownString.from(f.contents), - })); - } + if (entry && response && entry.provider.provideFollowups) { + const task = Promise.resolve(entry.provider.provideFollowups(sessionData.session, response, token)); + const followups = await raceCancellation(task, token); + return followups?.map(typeConvert.ChatReplyFollowup.from); } return undefined; } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 8ae2479fd2fd1..a1d7d522d0ae0 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2157,7 +2157,7 @@ export namespace DataTransfer { } export namespace ChatReplyFollowup { - export function from(followup: vscode.InteractiveSessionReplyFollowup): IChatReplyFollowup { + export function from(followup: vscode.InteractiveSessionReplyFollowup | vscode.InteractiveEditorReplyFollowup): IChatReplyFollowup { return { kind: 'reply', message: followup.message, diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 87f2f6a781c53..4c42fac250f46 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -690,20 +690,6 @@ export class InlineChatController implements IEditorContribution { a11yResponse = this._strategy.checkChanges(replyResponse) && a11yVerboseInlineChat ? a11yMessageResponse ? localize('editResponseMessage2', "{0}, also review proposed changes in the diff editor.", a11yMessageResponse) : localize('editResponseMessage', "Review proposed changes in the diff editor.") : a11yMessageResponse; - - - if (this._activeSession.provider.provideFollowups && replyResponse.responseType !== InlineChateResponseTypes.OnlyEdits) { - const followupCts = new CancellationTokenSource(requestCts.token); - const followupTask = this._activeSession.provider.provideFollowups(this._activeSession.session, reply, followupCts.token); - this._log('followup request started', this._activeSession.provider.debugName, this._activeSession.session, reply); - const followupReply = await raceCancellationError(Promise.resolve(followupTask), followupCts.token); - if (followupReply) { - this._log('followup request receiver', this._activeSession.provider.debugName, this._activeSession.session, followupReply); - for (const message of followupReply) { - markdownContents.appendMarkdown(message.message.value); - } - } - } } } catch (e) { diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 79393b59e4144..f0b224f9137f8 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -20,6 +20,7 @@ import { IProgress } from 'vs/platform/progress/common/progress'; import { Registry } from 'vs/platform/registry/common/platform'; import { diffInserted, diffRemoved, editorHoverHighlight, editorWidgetBackground, editorWidgetBorder, focusBorder, inputBackground, inputPlaceholderForeground, registerColor, transparent, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { Extensions as ExtensionsMigration, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; +import { IChatReplyFollowup } from 'vs/workbench/contrib/chat/common/chatService'; export interface IInlineChatSlashCommand { command: string; @@ -48,11 +49,6 @@ export interface IInlineChatRequest { export type IInlineChatResponse = IInlineChatEditResponse | IInlineChatBulkEditResponse | IInlineChatMessageResponse; -export interface InteractiveEditorReplyFollowup { - message: IMarkdownString; -} - - export const enum InlineChatResponseType { EditorEdit = 'editorEdit', BulkEdit = 'bulkEdit', @@ -115,7 +111,7 @@ export interface IInlineChatSessionProvider { provideResponse(item: IInlineChatSession, request: IInlineChatRequest, progress: IProgress, token: CancellationToken): ProviderResult; - provideFollowups?(session: IInlineChatSession, response: IInlineChatResponse, token: CancellationToken): ProviderResult; + provideFollowups?(session: IInlineChatSession, response: IInlineChatResponse, token: CancellationToken): ProviderResult; handleInlineChatResponseFeedback?(session: IInlineChatSession, response: IInlineChatResponse, kind: InlineChatResponseFeedbackKind): void; } diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index 24d93b0bb7381..20358ce051288 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -75,7 +75,9 @@ declare module 'vscode' { } export interface InteractiveEditorReplyFollowup { - contents: MarkdownString; + message: string; + tooltip?: string; + title?: string; } export interface InteractiveEditorSessionProvider {