Skip to content

Commit 6a06de0

Browse files
authored
Hook up feedback/user-action events for inline chat participants (microsoft#210040)
- add a special user-action - inline chat controller doesn't call handleFeedback directly anymore - ReplyResonse knows its chat model response
1 parent 13a88a8 commit 6a06de0

File tree

8 files changed

+86
-43
lines changed

8 files changed

+86
-43
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2697,6 +2697,8 @@ export namespace ChatAgentUserActionEvent {
26972697
} else if (event.action.kind === 'followUp') {
26982698
const followupAction: vscode.ChatFollowupAction = { kind: 'followUp', followup: ChatFollowup.to(event.action.followup) };
26992699
return { action: followupAction, result: ehResult };
2700+
} else if (event.action.kind === 'inlineChat') {
2701+
return { action: { kind: 'editor', accepted: event.action.action === 'accepted' }, result: ehResult };
27002702
} else {
27012703
return { action: event.action, result: ehResult };
27022704
}

src/vs/workbench/contrib/chat/common/chatService.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,12 @@ export interface IChatBugReportAction {
215215
kind: 'bug';
216216
}
217217

218-
export type ChatUserAction = IChatVoteAction | IChatCopyAction | IChatInsertAction | IChatTerminalAction | IChatCommandAction | IChatFollowupAction | IChatBugReportAction;
218+
export interface IChatInlineChatCodeAction {
219+
kind: 'inlineChat';
220+
action: 'accepted' | 'discarded';
221+
}
222+
223+
export type ChatUserAction = IChatVoteAction | IChatCopyAction | IChatInsertAction | IChatTerminalAction | IChatCommandAction | IChatFollowupAction | IChatBugReportAction | IChatInlineChatCodeAction;
219224

220225
export interface IChatUserActionEvent {
221226
action: ChatUserAction;

src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/em
1111
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget';
1212
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
1313
import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
14-
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_WIDGET_DISCARD, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_EDIT_MODE, EditMode, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, InlineChatResponseFeedbackKind, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, MENU_INLINE_CHAT_WIDGET, ACTION_TOGGLE_DIFF } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
14+
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_WIDGET_DISCARD, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_EDIT_MODE, EditMode, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, MENU_INLINE_CHAT_WIDGET, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
1515
import { localize, localize2 } from 'vs/nls';
1616
import { Action2, IAction2Options, MenuRegistry } from 'vs/platform/actions/common/actions';
1717
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
@@ -388,7 +388,7 @@ export class ReportIssueForBugCommand extends AbstractInlineChatAction {
388388
}
389389

390390
override runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController): void {
391-
ctrl.feedbackLast(InlineChatResponseFeedbackKind.Bug);
391+
ctrl.reportBug();
392392
}
393393
}
394394

src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { EmptyResponse, ErrorResponse, ReplyResponse, Session, SessionPrompt } f
3838
import { IInlineChatSessionService } from './inlineChatSessionService';
3939
import { EditModeStrategy, IEditObserver, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies';
4040
import { InlineChatZoneWidget } from './inlineChatZoneWidget';
41-
import { CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_RESPONSE_TYPES, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_VISIBLE, EditMode, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseFeedbackKind, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
41+
import { CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_RESPONSE_TYPES, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_VISIBLE, EditMode, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
4242
import { ICommandService } from 'vs/platform/commands/common/commands';
4343
import { StashedSession } from './inlineChatSession';
4444
import { IModelDeltaDecoration, ITextModel, IValidEditOperation } from 'vs/editor/common/model';
@@ -1108,19 +1108,16 @@ export class InlineChatController implements IEditorContribution {
11081108
this._strategy?.toggleDiff?.();
11091109
}
11101110

1111-
feedbackLast(kind: InlineChatResponseFeedbackKind) {
1112-
if (this._session?.lastExchange?.response instanceof ReplyResponse) {
1113-
this._session.provider.handleInlineChatResponseFeedback?.(this._session.session, this._session.lastExchange.response.raw, kind);
1114-
switch (kind) {
1115-
case InlineChatResponseFeedbackKind.Helpful:
1116-
this._ctxLastFeedbackKind.set('helpful');
1117-
break;
1118-
case InlineChatResponseFeedbackKind.Unhelpful:
1119-
this._ctxLastFeedbackKind.set('unhelpful');
1120-
break;
1121-
default:
1122-
break;
1123-
}
1111+
reportBug() {
1112+
if (this._session?.lastExchange?.response instanceof ReplyResponse && this._session?.lastExchange?.response.chatResponse) {
1113+
const response = this._session.lastExchange.response.chatResponse;
1114+
this._chatService.notifyUserAction({
1115+
sessionId: response.session.sessionId,
1116+
requestId: response.requestId,
1117+
agentId: response.agent?.id,
1118+
result: response.result,
1119+
action: { kind: 'bug' }
1120+
});
11241121
this._zone.value.widget.updateStatus('Thank you for your feedback!', { resetAfter: 1250 });
11251122
}
11261123
}
@@ -1132,8 +1129,18 @@ export class InlineChatController implements IEditorContribution {
11321129
}
11331130

11341131
acceptSession(): void {
1135-
if (this._session?.lastExchange?.response instanceof ReplyResponse) {
1136-
this._session.provider.handleInlineChatResponseFeedback?.(this._session.session, this._session.lastExchange.response.raw, InlineChatResponseFeedbackKind.Accepted);
1132+
if (this._session?.lastExchange?.response instanceof ReplyResponse && this._session?.lastExchange?.response.chatResponse) {
1133+
const response = this._session?.lastExchange?.response.chatResponse;
1134+
this._chatService.notifyUserAction({
1135+
sessionId: response.session.sessionId,
1136+
requestId: response.requestId,
1137+
agentId: response.agent?.id,
1138+
result: response.result,
1139+
action: {
1140+
kind: 'inlineChat',
1141+
action: 'accepted'
1142+
}
1143+
});
11371144
}
11381145
this._messages.fire(Message.ACCEPT_SESSION);
11391146
}
@@ -1154,8 +1161,18 @@ export class InlineChatController implements IEditorContribution {
11541161
const diff = await this._editorWorkerService.computeDiff(this._session.textModel0.uri, this._session.textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced');
11551162
result = this._session.asChangedText(diff?.changes ?? []);
11561163

1157-
if (this._session.lastExchange?.response instanceof ReplyResponse) {
1158-
this._session.provider.handleInlineChatResponseFeedback?.(this._session.session, this._session.lastExchange.response.raw, InlineChatResponseFeedbackKind.Undone);
1164+
if (this._session.lastExchange?.response instanceof ReplyResponse && this._session?.lastExchange?.response.chatResponse) {
1165+
const response = this._session?.lastExchange?.response.chatResponse;
1166+
this._chatService.notifyUserAction({
1167+
sessionId: response.session.sessionId,
1168+
requestId: response.requestId,
1169+
agentId: response.agent?.id,
1170+
result: response.result,
1171+
action: {
1172+
kind: 'inlineChat',
1173+
action: 'discarded'
1174+
}
1175+
});
11591176
}
11601177
}
11611178

src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
3232
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
3333
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
3434
import { ILogService } from 'vs/platform/log/common/log';
35-
import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
35+
import { ChatModel, IChatResponseModel } from 'vs/workbench/contrib/chat/common/chatModel';
3636
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
3737

3838

@@ -323,6 +323,7 @@ export class ReplyResponse {
323323
readonly modelAltVersionId: number,
324324
progressEdits: TextEdit[][],
325325
readonly requestId: string,
326+
readonly chatResponse: IChatResponseModel | undefined,
326327
@ITextFileService private readonly _textFileService: ITextFileService,
327328
@ILanguageService private readonly _languageService: ILanguageService,
328329
) {

src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class BridgeAgent implements IChatAgentImplementation {
5959
return data;
6060
}
6161

62-
async invoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult> {
62+
async invoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void, _history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult> {
6363

6464
if (token.isCancellationRequested) {
6565
return {};
@@ -129,7 +129,9 @@ class BridgeAgent implements IChatAgentImplementation {
129129

130130
const markdownContents = result.message ?? new MarkdownString('', { supportThemeIcons: true, supportHtml: true, isTrusted: false });
131131

132-
response = this._instaService.createInstance(ReplyResponse, result, markdownContents, session.textModelN.uri, modelAltVersionIdNow, progressEdits, request.requestId);
132+
const chatModelRequest = session.chatModel.getRequests().find(candidate => candidate.id === request.requestId);
133+
134+
response = this._instaService.createInstance(ReplyResponse, result, markdownContents, session.textModelN.uri, modelAltVersionIdNow, progressEdits, request.requestId, chatModelRequest?.response);
133135

134136
} else {
135137
response = new EmptyResponse();
@@ -141,9 +143,6 @@ class BridgeAgent implements IChatAgentImplementation {
141143

142144
this._postLastResponse({ id: request.requestId, response });
143145

144-
// TODO@jrieken
145-
// result?.placeholder
146-
// result?.wholeRange
147146

148147
return {
149148
metadata: {
@@ -315,18 +314,19 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService {
315314
}
316315

317316
if (otherEditorAgent) {
318-
brigdeAgent.clear();
319-
_logService.debug(`REMOVED bridge agent "${agentData.id}", found "${otherEditorAgent.id}"`);
317+
bridgeStore.clear();
318+
_logService.info(`REMOVED bridge agent "${agentData.id}", found "${otherEditorAgent.id}"`);
319+
320320
} else if (!myEditorAgent) {
321-
brigdeAgent.value = this._chatAgentService.registerDynamicAgent(agentData, this._instaService.createInstance(BridgeAgent, agentData, this._sessions, data => {
321+
bridgeStore.value = this._chatAgentService.registerDynamicAgent(agentData, this._instaService.createInstance(BridgeAgent, agentData, this._sessions, data => {
322322
this._lastResponsesFromBridgeAgent.set(data.id, data.response);
323323
}));
324-
_logService.debug(`ADDED bridge agent "${agentData.id}"`);
324+
_logService.info(`ADDED bridge agent "${agentData.id}"`);
325325
}
326326
};
327327

328328
this._store.add(this._chatAgentService.onDidChangeAgents(() => addOrRemoveBridgeAgent()));
329-
const brigdeAgent = this._store.add(new MutableDisposable());
329+
const bridgeStore = this._store.add(new MutableDisposable());
330330
addOrRemoveBridgeAgent();
331331

332332

@@ -465,7 +465,8 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService {
465465
session.textModelN.uri,
466466
modelAltVersionIdNow,
467467
[],
468-
e.request.id
468+
e.request.id,
469+
e.request.response
469470
);
470471
}
471472
}
@@ -475,20 +476,32 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService {
475476
}));
476477

477478
store.add(this._chatService.onDidPerformUserAction(e => {
478-
if (e.sessionId !== chatModel.sessionId || e.action.kind !== 'vote') {
479+
if (e.sessionId !== chatModel.sessionId) {
479480
return;
480481
}
481482

482483
// TODO@jrieken VALIDATE candidate is proper, e.g check with `session.exchanges`
483484
const request = chatModel.getRequests().find(request => request.id === e.requestId);
484485
const candidate = request?.response?.result?.metadata?.inlineChatResponse;
485-
if (candidate) {
486-
provider.handleInlineChatResponseFeedback?.(
487-
rawSession,
488-
candidate,
489-
e.action.direction === InteractiveSessionVoteDirection.Down ? InlineChatResponseFeedbackKind.Unhelpful : InlineChatResponseFeedbackKind.Helpful
490-
);
486+
487+
if (!candidate) {
488+
return;
489+
}
490+
491+
let kind: InlineChatResponseFeedbackKind | undefined;
492+
if (e.action.kind === 'vote') {
493+
kind = e.action.direction === InteractiveSessionVoteDirection.Down ? InlineChatResponseFeedbackKind.Unhelpful : InlineChatResponseFeedbackKind.Helpful;
494+
} else if (e.action.kind === 'bug') {
495+
kind = InlineChatResponseFeedbackKind.Bug;
496+
} else if (e.action.kind === 'inlineChat') {
497+
kind = e.action.action === 'accepted' ? InlineChatResponseFeedbackKind.Accepted : InlineChatResponseFeedbackKind.Undone;
491498
}
499+
500+
if (!kind) {
501+
return;
502+
}
503+
504+
provider.handleInlineChatResponseFeedback?.(rawSession, candidate, kind);
492505
}));
493506

494507
store.add(this._inlineChatService.onDidChangeProviders(e => {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ export class NotebookChatController extends Disposable implements INotebookEdito
644644
response = new EmptyResponse();
645645
} else {
646646
const markdownContents = new MarkdownString('', { supportThemeIcons: true, supportHtml: true, isTrusted: false });
647-
const replyResponse = response = this._instantiationService.createInstance(ReplyResponse, reply, markdownContents, this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), progressEdits, request.requestId);
647+
const replyResponse = response = this._instantiationService.createInstance(ReplyResponse, reply, markdownContents, this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), progressEdits, request.requestId, undefined);
648648
for (let i = progressEdits.length; i < replyResponse.allLocalEdits.length; i++) {
649649
await this._makeChanges(replyResponse.allLocalEdits[i], undefined);
650650
}
@@ -747,7 +747,7 @@ export class NotebookChatController extends Disposable implements INotebookEdito
747747
}
748748

749749
const markdownContents = new MarkdownString('', { supportThemeIcons: true, supportHtml: true, isTrusted: false });
750-
const response = this._instantiationService.createInstance(ReplyResponse, reply, markdownContents, this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), [], request.requestId);
750+
const response = this._instantiationService.createInstance(ReplyResponse, reply, markdownContents, this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), [], request.requestId, undefined);
751751
const followups = await this._activeSession.provider.provideFollowups(this._activeSession.session, response.raw, token);
752752
if (followups && this._widget) {
753753
const widget = this._widget;

src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,14 @@ declare module 'vscode' {
281281
kind: 'bug';
282282
}
283283

284+
export interface ChatEditorAction {
285+
kind: 'editor';
286+
accepted: boolean;
287+
}
288+
284289
export interface ChatUserActionEvent {
285290
readonly result: ChatResult;
286-
readonly action: ChatCopyAction | ChatInsertAction | ChatTerminalAction | ChatCommandAction | ChatFollowupAction | ChatBugReportAction;
291+
readonly action: ChatCopyAction | ChatInsertAction | ChatTerminalAction | ChatCommandAction | ChatFollowupAction | ChatBugReportAction | ChatEditorAction;
287292
}
288293

289294
export interface ChatVariableValue {

0 commit comments

Comments
 (0)