Path: blob/main/extensions/copilot/src/extension/prompts/node/panel/vscode.tsx
13405 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 { BasePromptElementProps, PromptElement, PromptPiece, PromptSizing, SystemMessage, TextChunk, UserMessage } from '@vscode/prompt-tsx';7import type * as vscode from 'vscode';8import { ChatFetchResponseType, ChatLocation } from '../../../../platform/chat/common/commonTypes';9import { EmbeddingType, IEmbeddingsComputer } from '../../../../platform/embeddings/common/embeddingsComputer';10import { CommandListItem, ICombinedEmbeddingIndex, SettingListItem, settingItemToContext } from '../../../../platform/embeddings/common/vscodeIndex';11import { IEndpointProvider } from '../../../../platform/endpoint/common/endpointProvider';12import { IEnvService } from '../../../../platform/env/common/envService';13import { ILogService } from '../../../../platform/log/common/logService';14import { IChatEndpoint } from '../../../../platform/networking/common/networking';15import { IReleaseNotesService } from '../../../../platform/releaseNotes/common/releaseNotesService';16import { reportProgressOnSlowPromise } from '../../../../util/common/progress';17import { sanitizeVSCodeVersion } from '../../../../util/common/vscodeVersion';18import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';19import { ChatResponseProgressPart } from '../../../../vscodeTypes';20import { Turn } from '../../../prompt/common/conversation';21import { IBuildPromptContext } from '../../../prompt/common/intents';22import { ToolName } from '../../../tools/common/toolNames';23import { CopilotIdentityRules } from '../base/copilotIdentity';24import { InstructionMessage } from '../base/instructionMessage';25import { PromptRenderer } from '../base/promptRenderer';26import { ResponseTranslationRules } from '../base/responseTranslationRules';27import { SafetyRules } from '../base/safetyRules';28import { Tag } from '../base/tag';29import { ChatToolReferences, ChatVariablesAndQuery } from './chatVariables';30import { ConversationHistoryWithTools, HistoryWithInstructions } from './conversationHistory';31import { ChatToolCalls } from './toolCalling';32import { UnsafeCodeBlock } from './unsafeElements';3334export interface VscodePromptProps extends BasePromptElementProps {35promptContext: IBuildPromptContext;36endpoint: IChatEndpoint;37}3839export interface VscodePromptState {40settings: SettingListItem[];41commands: CommandListItem[];42query: string;43releaseNotes?: { version: string; notes: string }[];44currentVersion?: string;45}4647export class VscodePrompt extends PromptElement<VscodePromptProps, VscodePromptState> {4849constructor(props: VscodePromptProps,50@ILogService private readonly logService: ILogService,51@IEmbeddingsComputer private readonly embeddingsComputer: IEmbeddingsComputer,52@IEndpointProvider private readonly endPointProvider: IEndpointProvider,53@ICombinedEmbeddingIndex private readonly combinedEmbeddingIndex: ICombinedEmbeddingIndex,54@IEnvService private readonly envService: IEnvService,55@IInstantiationService private readonly instantiationService: IInstantiationService,56@IReleaseNotesService private readonly releaseNotesService: IReleaseNotesService,57) {58super(props);59}6061override async prepare(sizing: PromptSizing, progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken): Promise<VscodePromptState> {62if (!this.props.promptContext.query) {63return { settings: [], commands: [], query: '' };64}6566progress?.report(new ChatResponseProgressPart(l10n.t('Refining question to improve search accuracy.')));67let userQuery: string = this.props.promptContext.query;6869const endpoint = await this.endPointProvider.getChatEndpoint('copilot-fast');70const renderer = PromptRenderer.create(this.instantiationService, endpoint, VscodeMetaPrompt, this.props.promptContext);71const { messages } = await renderer.render();72if (token.isCancellationRequested) {73return { settings: [], commands: [], query: userQuery };74}7576this.logService.debug('[VSCode Prompt] Asking the model to update the user question.');7778const fetchResult = await endpoint.makeChatRequest(79'vscodePrompt',80messages,81async (_) => void 0,82token,83ChatLocation.Panel,84undefined,85{86temperature: 0,87},88);8990if (token.isCancellationRequested) {91return { settings: [], commands: [], query: userQuery };92}9394let fetchReleaseNotes = false;95let shouldIncludeDocsSearch = false;96let extensionSearch = false;97let vscodeApiSearch = false;98if (fetchResult.type === ChatFetchResponseType.Success) {99userQuery = parseMetaPromptResponse(this.props.promptContext.query, fetchResult.value);100shouldIncludeDocsSearch = fetchResult.value.includes('Other Question');101fetchReleaseNotes = fetchResult.value.includes('release_notes');102extensionSearch = fetchResult.value.includes('vscode_extensions');103vscodeApiSearch = fetchResult.value.includes('vscode_api');104} else {105this.logService.error(`[VSCode Prompt] Failed to refine the question: ${fetchResult.requestId}`);106}107108const currentSanitized = sanitizeVSCodeVersion(this.envService.getEditorInfo().version); // major.minor109if (fetchReleaseNotes) {110// Determine which versions to fetch based on meta response111const rnMatch = fetchResult.type === ChatFetchResponseType.Success ? fetchResult.value.match(/release_notes(?:@(?<spec>[A-Za-z0-9._-]+))?/i) : undefined;112const spec = rnMatch?.groups?.['spec']?.toLowerCase();113114let versionsToFetch: string[];115if (spec === 'last3') {116versionsToFetch = getLastNMinorVersions(currentSanitized, 3);117} else {118versionsToFetch = [currentSanitized];119}120121const notes = await Promise.all(versionsToFetch.map(async (ver) => {122const text = await this.releaseNotesService.fetchReleaseNotesForVersion(ver);123return text ? { version: ver, notes: text } : undefined;124}));125126const filtered = notes.filter((n): n is { version: string; notes: string } => !!n);127return { settings: [], commands: [], releaseNotes: filtered, query: this.props.promptContext.query, currentVersion: currentSanitized };128}129130if (extensionSearch || vscodeApiSearch) {131return { settings: [], commands: [], query: this.props.promptContext.query };132}133134if (token.isCancellationRequested) {135return { settings: [], commands: [], query: userQuery };136}137138const embeddingResult = await this.embeddingsComputer.computeEmbeddings(EmbeddingType.text3small_512, [userQuery], {}, undefined);139if (token.isCancellationRequested) {140return { settings: [], commands: [], query: userQuery };141}142143const nClosestValuesPromise = progress144? reportProgressOnSlowPromise(progress, new ChatResponseProgressPart(l10n.t("Searching command and setting index....")), this.combinedEmbeddingIndex.nClosestValues(embeddingResult.values[0], shouldIncludeDocsSearch ? 5 : 25), 500)145: this.combinedEmbeddingIndex.nClosestValues(embeddingResult.values[0], shouldIncludeDocsSearch ? 5 : 25);146147const results = await Promise.allSettled([148nClosestValuesPromise,149]);150151const embeddingResults = results[0].status === 'fulfilled' ? results[0].value : { commands: [], settings: [] };152153return { settings: embeddingResults.settings, commands: embeddingResults.commands, query: userQuery, currentVersion: currentSanitized };154}155156override render(state: VscodePromptState) {157const operatingSystem = this.envService.OS;158return (159<>160<SystemMessage priority={1000}>161You are a Visual Studio Code assistant. Your job is to assist users in using Visual Studio Code by providing knowledge to accomplish their task. This knowledge should focus on settings, commands, keybindings but also includes documentation. <br />162{state.query.length < 1 && <>163If the user does not include a question, respond with: I am your Visual Studio Code assistant. I can help you with settings, commands, keybindings, extensions, and documentation. Ask me anything about using or configuring Visual Studio Code.<br />164</>}165<CopilotIdentityRules />166<SafetyRules />167<InstructionMessage>168Additional Rules<br />169If a command or setting references another command or setting, you must respond with both the original and the referenced commands or settings.<br />170Prefer a setting over a command if the user's request can be achieved by a setting change.<br />171If answering with a keybinding, please still include the command bound to the keybinding.<br />172If a keybinding contains a backtick you must escape it. For example the keybinding Ctrl + backtick would be written as ``ctrl + ` ``<br />173If you believe the context given to you is incorrect or not relevant you may ignore it.<br />174Always respond with a numbered list of steps to be taken to achieve the desired outcome if multiple steps are necessary.<br />175If an extension might help the user, you may suggest a search query for the extension marketplace. You must also include the command **Search marketplace** (`workbench.extensions.search`) with args set to the suggested query in the commands section at the end of your response. The query can also contain the tags "@popular", "@recommended", or "@featured" to filter the results.<br />176The user is working on a {operatingSystem} machine. Please respond with system specific commands if applicable.<br />177If a command or setting is not a valid answer, but it still relates to Visual Studio Code, please still respond.<br />178If the question is about release notes, you must also include the command **Show release notes** (`update.showCurrentReleaseNotes`) in the commands section at the end of your response.<br />179If the response includes a command, only reference the command description in the description. Do not include the actual command in the description.<br />180All responses for settings and commands code blocks must strictly adhere to the template shown below:<br />181<Tag name='responseTemplate'>182<UnsafeCodeBlock code={`183{184"type": "array",185"items": {186"type": "object",187"properties": {188"type": {189"type": "string",190"enum": ["command", "setting"]191},192"details": {193"type": "object",194"properties": {195"key": { "type": "string" },196"value": { "type": "string" }197},198"required": ["key"]199}200},201"required": ["type", "details"],202"additionalProperties": false203}204}205`} languageId='json'></UnsafeCodeBlock>206<br />207where the `type` is either `setting`, `command`.<br />208- `setting` is used for responding with a setting to set.<br />209- `command` is used for responding with a command to execute<br />210where the `details` is an optional object that contains the setting/command objects.<br />211- `key` is the setting | command value to use .<br />212- `value` is the setting value in case of a setting.<br />213- `value` is the optional arguments to the command in case of a command.<br />214</Tag>215<Tag name='examples'>216Below you will find a set of examples of what you should respond with. Please follow these examples as closely as possible.<br />217<Tag name='singleSettingExample'>218Question: How do I disable telemetry?<br />219Response:<br />220Use the **telemetry.telemetryLevel** setting to disable telemetry.<br />221<UnsafeCodeBlock code={`222[223{224"type": "setting",225"details": {226"key": "telemetry.telemetryLevel",227"value": "off"228}229}230]231`} languageId='json'></UnsafeCodeBlock>232</Tag>233<Tag name='singleCommandExample'>234Question: How do I open a specific walkthrough?<br />235Use the **Welcome: Open Walkthrough...** command to open walkthroughs.<br />236Response:<br />237<UnsafeCodeBlock code={`238[239{240"type": "command",241"details": {242"key": "workbench.action.openWalkthrough",243}244}245]246`} languageId='json'></UnsafeCodeBlock>247</Tag>248<Tag name='multipleSettingsExample'>249If you are referencing multiple settings, first describe each setting and then include all settings in a single JSON markdown code block, as shown in the template below:<br />250Question: How can I change the font size in all areas of Visual Studio Code, including the editor, terminal?<br />251Response:<br />252The **editor.fontsize** setting adjusts the font size within the editor.<br />253The **terminal.integrated.fontSize** setting changes the font size in the integrated terminal.<br />254This **window.zoomLevel** setting controls the zoom level of the entire Visual Studio Code interface.<br />255<UnsafeCodeBlock code={`256[257{258"type": "setting",259"details": {260"key": "editor.fontSize",261"value": "18"262}263},264{265"type": "setting",266"details": {267"key": "terminal.integrated.fontSize",268"value": "14"269}270},271{272"type": "setting",273"details": {274"key": "window.zoomLevel",275"value": "1"276}277}278]279`} languageId='json'></UnsafeCodeBlock>280</Tag>281<Tag name='multipleCommandsExample'>282If you are referencing multiple commands, do not combine all the commands into the same JSON markdown code block.<br />283Instead, describe each command and include the JSON markdown code block in a numbered list, as shown in the template below:<br />284Question: How can I setup a python virtual environment in Visual Studio Code?<br />285Response:<br />286Use the **Python: Create Environment** command to create a new python environment.<br />287<UnsafeCodeBlock code={`288[289{290"type": "command",291"details": {292"key": "python.createEnvironment"293}294}295]296`} languageId='json'></UnsafeCodeBlock>297Select the environment type (Venv or Conda) from the list.<br />298If creating a Venv environment, select the interpreter to use as a base for the new virtual environment.<br />299Wait for the environment creation process to complete. A notification will show the progress.<br />300Ensure your new environment is selected by using the **Python: Select Interpreter** command.<br />301<UnsafeCodeBlock code={`302[303{304"type": "command",305"details": {306"key": "python.setInterpreter"307}308}309]310`} languageId='json'></UnsafeCodeBlock>311</Tag>312<Tag name='noSuchCommandExample'>313Question: How do I move the terminal to a new window?<br />314Response:<br />315There is no such command.<br />316</Tag>317<Tag name='invalidQuestionExample'>318Question: How do I bake a potato?<br />319Response:<br />320Sorry this question isn't related to Visual Studio Code.<br />321</Tag>322<Tag name='marketplaceSearchExample'>323Question: How do I add PHP support?<br />324Response:<br />325You can use the **Search marketplace** command to search for extensions that add PHP support.<br />326<UnsafeCodeBlock code={`327[328{329"type": "command",330"details": {331"key": "workbench.extensions.search",332"value": "php"333}334}335]336`} languageId='json'></UnsafeCodeBlock>337<br />338</Tag>339</Tag>340<Tag name='extensionSearchResponseRules'>341If you referene any extensions, you must respond with with the identifiers as a comma seperated string inside ```vscode-extensions code block. <br />342Do not describe the extension. Simply return the response in the format shown above.<br />343<Tag name='extensionResponseExample'>344Question: What are some popular python extensions?<br />345Response:<br />346Here are some popular python extensions.<br />347<UnsafeCodeBlock code={`348ms-python.python,ms-python.vscode-pylance349`} languageId='vscode-extensions'></UnsafeCodeBlock>350</Tag>351</Tag>352<ResponseTranslationRules />353</InstructionMessage>354</SystemMessage>355<ConversationHistoryWithTools flexGrow={1} priority={700} promptContext={this.props.promptContext} />356<UserMessage flexGrow={1} priority={800}>357Use the examples above to help you formulate your response and follow the examples as closely as possible.358Below is a list of information we found which might be relevant to the question. For view related commands "Toggle" often means Show or Hide. A setting may reference another setting, that will appear as \`#setting.id#\`, you must return the referenced setting as well. You may use this context to help you formulate your response, but are not required to.<br />359{state.commands.length > 0 && <><Tag name='command'>360Here are some possible commands:<br />361{state.commands.map(c => <TextChunk>- {c.label} ("{c.key}") (Keybinding: "{c.keybinding}")</TextChunk>)}362</Tag>363</>}364{state.settings.length > 0 && <><Tag name='settings'>365Here are some possible settings:<br />366{state.settings.map(c => <TextChunk>{settingItemToContext(c)}</TextChunk>)}367</Tag>368</>}369{state.currentVersion && <><Tag name='currentVSCodeVersion'>370Current VS Code version (major.minor): {state.currentVersion}371</Tag><br /></>}372{state.releaseNotes && state.releaseNotes.length > 0 && <><Tag name='releaseNotes'>373Below are release notes which might be relevant to the question. <br />374{state.releaseNotes.map(rn => <><TextChunk>Version {rn.version}:</TextChunk><br /><TextChunk>{rn.notes}</TextChunk></>)}375</Tag>376</>}377<Tag name='vscodeAPIToolUseInstructions'>378Always call the tool {ToolName.VSCodeAPI} to get documented references and examples when before responding to questions about VS Code Extension Development.<br />379</Tag>380<Tag name='searchExtensionToolUseInstructions'>381Always call the tool 'vscode_searchExtensions_internal' to first search for extensions in the VS Code Marketplace before responding about extensions.<br />382</Tag>383<Tag name='vscodeCmdToolUseInstructions'>384Call the tool {ToolName.RunVscodeCmd} to run commands in Visual Studio Code, only use as part of a new workspace creation process. <br />385You must use the command name as the `name` field and the command ID as the `commandId` field in the tool call input with any arguments for the command in a `map` array.<br />386For example, to run the command `workbench.action.openWith`, you would use the following input:<br />387<UnsafeCodeBlock code={`{388"name": "workbench.action.openWith",389"commandId": "workbench.action.openWith",390"args": ["file:///path/to/file.txt", "default"]391}392`}></UnsafeCodeBlock>393</Tag>394</UserMessage>395<ChatToolReferences priority={850} flexGrow={2} promptContext={{ ...this.props.promptContext, query: state.query }} embeddedInsideUserMessage={false} />396<ChatToolCalls priority={899} flexGrow={2} promptContext={this.props.promptContext} toolCallRounds={this.props.promptContext.toolCallRounds} toolCallResults={this.props.promptContext.toolCallResults} />397<ChatVariablesAndQuery flexGrow={2} priority={900} chatVariables={this.props.promptContext.chatVariables} query={this.props.promptContext.query} embeddedInsideUserMessage={false} />398</>);399}400}401402interface VscodeMetaPromptProps extends BasePromptElementProps {403history?: readonly Turn[];404query: string;405}406407class VscodeMetaPrompt extends PromptElement<VscodeMetaPromptProps> {408409override render(state: void, sizing: PromptSizing): PromptPiece<any, any> | undefined {410return <>411<SystemMessage priority={1000}>412You are a Visual Studio Code assistant who helps the user create well-formed and unambiguous queries about their Visual Studio Code development environment.<br />413Specifically, you help users rewrite questions about how to use Visual Studio Code's Commands and Settings.414</SystemMessage>415<HistoryWithInstructions historyPriority={500} passPriority history={this.props.history || []}>416<InstructionMessage priority={1000}>417Evaluate the question to determine the user's intent. <br />418Determine if the user's question is about the editor, terminal, activity bar, side bar, status bar, panel or other parts of Visual Studio Code's workbench and include those keyword in the rewrite.<br />419Determine if the user is asking about Visual Studio Code's Commands and/or Settings and explicitly include those keywords during the rewrite. <br />420If the question does not clearly indicate whether it pertains to a command or setting, categorize it as an ‘Other Question’ <br />421If the user is asking about Visual Studio Code Release Notes, respond using this exact protocol and do not rephrase the question: <br />422- Respond with only one of the following: `release_notes@latest` or `release_notes@last3`.<br />423- If the user does not specify a timeframe, respond with: `release_notes@latest`.<br />424- If the request is vague about a timeframe (e.g., "recent changes"), respond with: `release_notes@last3` to consider the last three versions (major.minor).<br />425- If the user asks to find or locate a specific change/feature in the release notes, respond with: `release_notes@last3` to search across the last three versions (major.minor).<br />426If the user is asking about Extensions available in Visual Studio Code, simply respond with "vscode_extensions"<br />427If the user is asking about Visual Studio Code API or Visual Studio Code Extension Development, simply respond with "vscode_api"<br />428Remove any references to "What" or "How" and instead rewrite the question as a description of the command or setting that the user is trying to find. <br />429Respond in Markdown. Under a `# Question` header, output a rephrased version of the user's question that resolves all pronouns and ambiguous words like 'this' to the specific nouns they stand for.<br />430If it is not clear what the user is asking for or if the question appears to be unrelated to Visual Studio Code, do not try to rephrase the question and simply return the original question. <br />431DO NOT ask the user for additional information or clarification.<br />432DO NOT answer the user's question directly.<br />433<br />434# Additional Rules<br />435<br />4362. If the question contains pronouns such as 'it' or 'that', try to understand what the pronoun refers to by looking at the rest of the question and the conversation history.<br />4373. If the question contains an ambiguous word such as 'this', try to understand what 'this' refers to by looking at the rest of the question and the conversation history.<br />4384. After a `# Question` header, output a precise version of the question that resolves all pronouns and ambiguous words like 'this' to the specific nouns they stand for. Be sure to preserve the exact meaning of the question.<br />439<br />440Examples<br />441<br />442User: opne cmmand palete<br />443<br />444Assistant:<br />445# Question<br />446Command to open command palette<br />447<br />448<br />449User: How do I change change font size in the editor?<br />450<br />451Assistant:<br />452# Question<br />453Command or setting to change the font size in the editor<br />454<br />455User: What is the setting to move editor and pin it<br />456Assistant: <br />457# Question<br />458Settings to move and pin editor<br />459<br />460User: latest released features<br />461<br />462Assistant:<br />463release_notes@latest<br />464<br />465User: What are the recent changes?<br />466<br />467Assistant:<br />468release_notes@last3<br />469<br />470User: set up python<br />471<br />472Assistant:<br />473# Other Question<br />474Set up python development in Visual Studio Code<br />475<br />476User: Show me popular extensions<br />477<br />478Assistant:<br />479vscode_extensions<br />480<br />481User: How do I contribute a command to my extension?<br />482<br />483Assistant:<br />484vscode_api<br />485<br />486<ResponseTranslationRules />487</InstructionMessage>488</HistoryWithInstructions>489<UserMessage priority={700}>{this.props.query}</UserMessage>490</>;491}492}493494function parseMetaPromptResponse(originalQuestion: string, response: string): string {495const match = response.match(/#+\s*(Question|Other Question)\n(?<question>.+)/si);496if (!match?.groups) {497return originalQuestion.trim();498}499return match.groups['question'].trim();500}501502function getLastNMinorVersions(current: string, n: number): string[] {503const m = /^(\d+)\.(\d+)$/.exec(current);504if (!m) {505return [current];506}507const major = parseInt(m[1], 10);508let minor = parseInt(m[2], 10);509const out: string[] = [];510for (let i = 0; i < n && minor >= 0; i++, minor--) {511out.push(`${major}.${minor}`);512}513return out;514}515516517