Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/askUserQuestionHandler.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { ChatParticipantToolToken, commands, LanguageModelTextPart } from 'vscode';
7
import { ILogService } from '../../../../platform/log/common/logService';
8
import { CancellationToken } from '../../../../util/vs/base/common/cancellation';
9
import { ToolName } from '../../../tools/common/toolNames';
10
import { IToolsService } from '../../../tools/common/toolsService';
11
import { IQuestion, IQuestionAnswer, IUserQuestionHandler, UserInputResponse } from '../../copilotcli/node/userInputHelpers';
12
13
14
export interface IAskQuestionsParams {
15
readonly questions: IQuestion[];
16
}
17
18
export interface IAnswerResult {
19
readonly answers: Record<string, IQuestionAnswer>;
20
}
21
22
const NotifyQuestionCarouselAnswerCommandId = '_chat.notifyQuestionCarouselAnswer';
23
24
function toCarouselAnswerValue(question: IQuestion, response: UserInputResponse): string | { selectedValue?: string; freeformValue?: string } | { selectedValues: string[]; freeformValue?: string } | undefined {
25
if (!response.answer) {
26
return undefined;
27
}
28
29
if (!question.options || question.options.length === 0) {
30
return response.answer;
31
}
32
33
if (question.multiSelect) {
34
const selectedValues = question.options.some(option => option.label === response.answer)
35
? [response.answer]
36
: response.answer.split(',').map(value => value.trim()).filter(Boolean);
37
return response.wasFreeform
38
? { selectedValues, freeformValue: response.answer }
39
: { selectedValues };
40
}
41
42
return response.wasFreeform
43
? { freeformValue: response.answer }
44
: { selectedValue: response.answer };
45
}
46
47
export class UserQuestionHandler implements IUserQuestionHandler {
48
declare _serviceBrand: undefined;
49
constructor(
50
@ILogService protected readonly _logService: ILogService,
51
@IToolsService private readonly _toolsService: IToolsService,
52
) {
53
}
54
async askUserQuestion(question: IQuestion, toolInvocationToken: ChatParticipantToolToken, token: CancellationToken, toolCallId?: string): Promise<IQuestionAnswer | undefined> {
55
const input: IAskQuestionsParams = { questions: [question] };
56
const result = await this._toolsService.invokeTool(ToolName.CoreAskQuestions, {
57
input,
58
toolInvocationToken,
59
chatStreamToolCallId: toolCallId,
60
}, token);
61
62
63
// Parse the result
64
const firstPart = result?.content.at(0);
65
if (!(firstPart instanceof LanguageModelTextPart) || !firstPart.value) {
66
return undefined;
67
}
68
69
const carouselAnswers = JSON.parse(firstPart.value) as IAnswerResult;
70
71
// Log all available keys in carouselAnswers for debugging
72
this._logService.trace(`[AskQuestionsTool] Question & answers ${question.question}, Answers object: ${JSON.stringify(carouselAnswers)}`);
73
74
const answer = carouselAnswers.answers[question.question] ?? carouselAnswers.answers[question.header];
75
if (answer === undefined) {
76
return undefined;
77
} else if (answer.freeText) {
78
return answer;
79
} else if (answer.selected.length) {
80
return answer;
81
}
82
return undefined;
83
}
84
85
async notifyQuestionCarouselAnswer(toolCallId: string, question: IQuestion, response: UserInputResponse): Promise<void> {
86
const answerValue = toCarouselAnswerValue(question, response);
87
await commands.executeCommand(NotifyQuestionCarouselAnswerCommandId, toolCallId, answerValue === undefined ? undefined : {
88
[`${toolCallId}:0`]: answerValue,
89
});
90
}
91
}
92
93