Path: blob/main/src/vs/workbench/contrib/chat/browser/promptSyntax/promptToolsCodeLensProvider.ts
5255 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 { CancellationToken } from '../../../../../base/common/cancellation.js';6import { Disposable } from '../../../../../base/common/lifecycle.js';7import { generateUuid } from '../../../../../base/common/uuid.js';8import { CodeLens, CodeLensList, CodeLensProvider } from '../../../../../editor/common/languages.js';9import { isITextModel, ITextModel } from '../../../../../editor/common/model.js';10import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js';11import { localize } from '../../../../../nls.js';12import { CommandsRegistry } from '../../../../../platform/commands/common/commands.js';13import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';14import { showToolsPicker } from '../actions/chatToolPicker.js';15import { ILanguageModelToolsService } from '../../common/tools/languageModelToolsService.js';16import { ALL_PROMPTS_LANGUAGE_SELECTOR, getPromptsTypeForLanguageId, PromptsType } from '../../common/promptSyntax/promptTypes.js';17import { IPromptsService, Target } from '../../common/promptSyntax/service/promptsService.js';18import { registerEditorFeature } from '../../../../../editor/common/editorFeatures.js';19import { PromptFileRewriter } from './promptFileRewriter.js';20import { Range } from '../../../../../editor/common/core/range.js';21import { IEditorModel } from '../../../../../editor/common/editorCommon.js';22import { isTarget, parseCommaSeparatedList, PromptHeaderAttributes } from '../../common/promptSyntax/promptFileParser.js';23import { getTarget, isVSCodeOrDefaultTarget } from '../../common/promptSyntax/languageProviders/promptValidator.js';24import { isBoolean } from '../../../../../base/common/types.js';2526class PromptToolsCodeLensProvider extends Disposable implements CodeLensProvider {2728// `_`-prefix marks this as private command29private readonly cmdId = `_configure/${generateUuid()}`;3031constructor(32@IPromptsService private readonly promptsService: IPromptsService,33@ILanguageFeaturesService private readonly languageService: ILanguageFeaturesService,34@ILanguageModelToolsService private readonly languageModelToolsService: ILanguageModelToolsService,35@IInstantiationService private readonly instantiationService: IInstantiationService,36) {37super();383940this._register(this.languageService.codeLensProvider.register(ALL_PROMPTS_LANGUAGE_SELECTOR, this));4142this._register(CommandsRegistry.registerCommand(this.cmdId, (_accessor, ...args) => {43const [modelArg, rangeArg, isStringArg, toolsArg, targetArg] = args;44const model = modelArg as IEditorModel;45if (isITextModel(model) && Range.isIRange(rangeArg) && isBoolean(isStringArg) && Array.isArray(toolsArg) && isTarget(targetArg)) {46this.updateTools(model as ITextModel, Range.lift(rangeArg), isStringArg, toolsArg, targetArg);47}48}));49}5051async provideCodeLenses(model: ITextModel, token: CancellationToken): Promise<undefined | CodeLensList> {52const promptType = getPromptsTypeForLanguageId(model.getLanguageId());53if (!promptType || promptType === PromptsType.instructions) {54// if the model is not a prompt, we don't provide any code actions55return undefined;56}5758const promptAST = this.promptsService.getParsedPromptFile(model);59const header = promptAST.header;60if (!header) {61return undefined;62}6364const target = getTarget(promptType, header);65if (!isVSCodeOrDefaultTarget(target)) {66return undefined;67}6869const toolsAttr = header.getAttribute(PromptHeaderAttributes.tools);70if (!toolsAttr) {71return undefined;72}73let value = toolsAttr.value;74if (value.type === 'string') {75value = parseCommaSeparatedList(value);76}77if (value.type !== 'array') {78return undefined;79}80const items = value.items;81const selectedTools = items.filter(item => item.type === 'string').map(item => item.value);8283const codeLens: CodeLens = {84range: toolsAttr.range.collapseToStart(),85command: {86title: localize('configure-tools.capitalized.ellipsis', "Configure Tools..."),87id: this.cmdId,88arguments: [model, toolsAttr.range, toolsAttr.value.type === 'string', selectedTools, target]89}90};91return { lenses: [codeLens] };92}9394private async updateTools(model: ITextModel, range: Range, isString: boolean, selectedTools: readonly string[], target: Target): Promise<void> {95const selectedToolsNow = () => this.languageModelToolsService.toToolAndToolSetEnablementMap(selectedTools, undefined);96const newSelectedAfter = await this.instantiationService.invokeFunction(showToolsPicker, localize('placeholder', "Select tools"), 'codeLens', undefined, selectedToolsNow);97if (!newSelectedAfter) {98return;99}100this.instantiationService.createInstance(PromptFileRewriter).rewriteTools(model, newSelectedAfter, range, isString);101}102}103104registerEditorFeature(PromptToolsCodeLensProvider);105106107