Path: blob/main/extensions/copilot/src/extension/intents/node/vscodeIntent.ts
13399 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as l10n from '@vscode/l10n';6import type * as vscode from 'vscode';7import { IResponsePart } from '../../../platform/chat/common/chatMLFetcher';8import { ChatLocation } from '../../../platform/chat/common/commonTypes';9import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';10import { IChatEndpoint } from '../../../platform/networking/common/networking';11import { IWorkbenchService } from '../../../platform/workbench/common/workbenchService';12import { CancellationToken } from '../../../util/vs/base/common/cancellation';13import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';14import { Intent } from '../../common/constants';15import { parseSettingsAndCommands } from '../../context/node/resolvers/vscodeContext';16import { IBuildPromptContext } from '../../prompt/common/intents';17import { IIntent, IIntentInvocation, IIntentInvocationContext, IIntentSlashCommandInfo, IntentLinkificationOptions, IResponseProcessorContext } from '../../prompt/node/intents';18import { PromptRenderer, RendererIntentInvocation } from '../../prompts/node/base/promptRenderer';19import { VscodePrompt } from '../../prompts/node/panel/vscode';20import { ToolName } from '../../tools/common/toolNames';21import { IToolsService } from '../../tools/common/toolsService';222324class VSCodeIntentInvocation extends RendererIntentInvocation implements IIntentInvocation {2526readonly linkification: IntentLinkificationOptions = { disable: true };2728constructor(29intent: IIntent,30location: ChatLocation,31endpoint: IChatEndpoint,32private readonly request: vscode.ChatRequest,33@IInstantiationService private readonly instantiationService: IInstantiationService,34@IWorkbenchService private readonly workbenchService: IWorkbenchService,35@IToolsService private readonly toolsService: IToolsService,36) {37super(intent, location, endpoint);38}3940async createRenderer(promptContext: IBuildPromptContext, endpoint: IChatEndpoint, progress: vscode.Progress<vscode.ChatResponseProgressPart | vscode.ChatResponseReferencePart>, token: vscode.CancellationToken) {41return PromptRenderer.create(this.instantiationService, endpoint, VscodePrompt, {42endpoint,43promptContext44});45}4647processResponse(context: IResponseProcessorContext, inputStream: AsyncIterable<IResponsePart>, outputStream: vscode.ChatResponseStream, token: CancellationToken): Promise<void> {48const responseProcessor = new VSCodeResponseProcessor(this.workbenchService);49return responseProcessor.processResponse(context, inputStream, outputStream, token);50}5152getAvailableTools(): vscode.LanguageModelToolInformation[] | Promise<vscode.LanguageModelToolInformation[]> | undefined {53return this.toolsService.getEnabledTools(this.request, this.endpoint, tool =>54tool.name === 'vscode_searchExtensions_internal' ||55tool.name === ToolName.VSCodeAPI56);57}58}5960class VSCodeResponseProcessor {61private stagedTextToApply = '';62constructor(private readonly workbenchService: IWorkbenchService) {63}6465async processResponse(context: IResponseProcessorContext, inputStream: AsyncIterable<IResponsePart>, outputStream: vscode.ChatResponseStream, token: vscode.CancellationToken): Promise<void> {66for await (const { delta } of inputStream) {67if (token.isCancellationRequested) {68return;69}70await this.applyDelta(delta.text, outputStream);71}72}7374/**75* Parses a raw Markdown string containing a code block and either extracts settings and commands to show as buttons for the user, or shows the code block.76* @param codeBlock Markdown string containing a single code block surrounded by "```"77*/78// textDelta is a string with a single Markdown code block (wrapped by ```). It might or might not be of json type.79private async processNonReporting(codeBlock: string, progress: vscode.ChatResponseStream) {80const parsedCommands = await parseSettingsAndCommands(this.workbenchService, codeBlock);8182if (parsedCommands.length === 0) {83// Show code block84progress.markdown('\n' + codeBlock + '\n');85} else {86// Show buttons for commands to run (which can include commands to change settings)87for (const parsedCommand of parsedCommands) {88if (parsedCommand.commandToRun) {89progress.button(parsedCommand.commandToRun);90}91}92}93}9495private _incodeblock = false;96private async applyDelta(textDelta: string, progress: vscode.ChatResponseStream) {9798textDelta = this.stagedTextToApply + textDelta;99this.stagedTextToApply = '';100const codeblockStart = textDelta.indexOf('```');101102if (this._incodeblock) {103const codeblockEnd = textDelta.indexOf('```');104if (codeblockEnd === -1) {105this.stagedTextToApply = textDelta;106} else {107this._incodeblock = false;108const codeBlock = '```' + textDelta.substring(0, codeblockEnd) + '```';109await this.processNonReporting(codeBlock, progress);110// Output any text that comes after the code block111progress.markdown(textDelta.substring(codeblockEnd + 3));112}113}114else if (codeblockStart !== -1) {115this._incodeblock = true;116const codeblockEnd = textDelta.indexOf('```', codeblockStart + 3);117if (codeblockEnd !== -1) {118this._incodeblock = false;119// Output any text that comes before the code block120progress.markdown(textDelta.substring(0, codeblockStart));121// Process the codeblock122const codeBlock = '```' + textDelta.substring(codeblockStart + 3, codeblockEnd) + '```';123await this.processNonReporting(codeBlock, progress);124// Output any text that comes after the code block125progress.markdown(textDelta.substring(codeblockEnd + 3));126} else {127this.stagedTextToApply = textDelta.substring(codeblockStart + 3);128// Output any text that comes before the code block129const textToReport = textDelta.substring(0, codeblockStart);130if (textToReport) {131progress.markdown(textToReport);132}133}134} else {135// We have no stop word or partial, so apply the text to the progress and turn136progress.markdown(textDelta);137}138}139}140141export class VscodeIntent implements IIntent {142143static readonly ID = Intent.VSCode;144readonly id: string = VscodeIntent.ID;145readonly locations = [ChatLocation.Panel];146readonly description: string = l10n.t('Ask questions about VS Code');147148readonly commandInfo: IIntentSlashCommandInfo = {149allowsEmptyArgs: true,150};151152constructor(153@IInstantiationService private readonly instantiationService: IInstantiationService,154@IEndpointProvider private readonly endpointProvider: IEndpointProvider,155) { }156157async invoke(invocationContext: IIntentInvocationContext): Promise<IIntentInvocation> {158const location = invocationContext.location;159const endpoint = await this.endpointProvider.getChatEndpoint(invocationContext.request);160return this.instantiationService.createInstance(VSCodeIntentInvocation, this, location, endpoint, invocationContext.request);161}162}163164165