mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-14 15:22:15 +01:00
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
153 lines
5.8 KiB
TypeScript
153 lines
5.8 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
|
import { DisposableMap } from 'vs/base/common/lifecycle';
|
|
import { joinPath } from 'vs/base/common/resources';
|
|
import { ThemeIcon } from 'vs/base/common/themables';
|
|
import { localize } from 'vs/nls';
|
|
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
|
import { ILogService } from 'vs/platform/log/common/log';
|
|
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
|
import { ILanguageModelToolsService, IToolData } from 'vs/workbench/contrib/chat/common/languageModelToolsService';
|
|
import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
|
|
|
interface IRawToolContribution {
|
|
id: string;
|
|
name?: string;
|
|
icon?: string | { light: string; dark: string };
|
|
displayName?: string;
|
|
description: string;
|
|
parametersSchema?: IJSONSchema;
|
|
canBeInvokedManually?: boolean;
|
|
}
|
|
|
|
const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IRawToolContribution[]>({
|
|
extensionPoint: 'languageModelTools',
|
|
activationEventsGenerator: (contributions: IRawToolContribution[], result) => {
|
|
for (const contrib of contributions) {
|
|
result.push(`onLanguageModelTool:${contrib.name}`);
|
|
}
|
|
},
|
|
jsonSchema: {
|
|
description: localize('vscode.extension.contributes.tools', 'Contributes a tool that can be invoked by a language model.'),
|
|
type: 'array',
|
|
items: {
|
|
additionalProperties: false,
|
|
type: 'object',
|
|
defaultSnippets: [{ body: { name: '', description: '' } }],
|
|
required: ['id', 'description'],
|
|
properties: {
|
|
id: {
|
|
description: localize('toolId', "A unique id for this tool."),
|
|
type: 'string',
|
|
// Borrow OpenAI's requirement for tool names
|
|
pattern: '^[\\w-]+$'
|
|
},
|
|
name: {
|
|
description: localize('toolName', "If {0} is enabled for this tool, the user may use '#' with this name to invoke the tool in a query. Otherwise, the name is not required. Name must not contain whitespace.", '`canBeInvokedManually`'),
|
|
type: 'string',
|
|
pattern: '^[\\w-]+$'
|
|
},
|
|
displayName: {
|
|
description: localize('toolDisplayName', "A human-readable name for this tool that may be used to describe it in the UI."),
|
|
type: 'string'
|
|
},
|
|
description: {
|
|
description: localize('toolDescription', "A description of this tool that may be passed to a language model."),
|
|
type: 'string'
|
|
},
|
|
parametersSchema: {
|
|
description: localize('parametersSchema', "A JSON schema for the parameters this tool accepts."),
|
|
type: 'object',
|
|
$ref: 'http://json-schema.org/draft-07/schema#'
|
|
},
|
|
canBeInvokedManually: {
|
|
description: localize('canBeInvokedManually', "Whether this tool can be invoked manually by the user through the chat UX."),
|
|
type: 'boolean'
|
|
},
|
|
icon: {
|
|
description: localize('icon', "An icon that represents this tool. Either a file path, an object with file paths for dark and light themes, or a theme icon reference, like `\\$(zap)`"),
|
|
anyOf: [{
|
|
type: 'string'
|
|
},
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
light: {
|
|
description: localize('icon.light', 'Icon path when a light theme is used'),
|
|
type: 'string'
|
|
},
|
|
dark: {
|
|
description: localize('icon.dark', 'Icon path when a dark theme is used'),
|
|
type: 'string'
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
function toToolKey(extensionIdentifier: ExtensionIdentifier, toolName: string) {
|
|
return `${extensionIdentifier.value}/${toolName}`;
|
|
}
|
|
|
|
export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContribution {
|
|
static readonly ID = 'workbench.contrib.toolsExtensionPointHandler';
|
|
|
|
private _registrationDisposables = new DisposableMap<string>();
|
|
|
|
constructor(
|
|
@ILanguageModelToolsService languageModelToolsService: ILanguageModelToolsService,
|
|
@ILogService logService: ILogService,
|
|
) {
|
|
languageModelToolsExtensionPoint.setHandler((extensions, delta) => {
|
|
for (const extension of delta.added) {
|
|
for (const rawTool of extension.value) {
|
|
if (!rawTool.name || !rawTool.description) {
|
|
logService.error(`Invalid tool contribution from ${extension.description.identifier.value}: ${JSON.stringify(rawTool)}`);
|
|
continue;
|
|
}
|
|
|
|
if (!rawTool.id.match(/^[\w-]+$/)) {
|
|
logService.error(`Extension '${extension.description.identifier.value}' CANNOT register participant with invalid id: ${rawTool.id}. The id must match /^[\\w-]+$/.`);
|
|
continue;
|
|
}
|
|
|
|
const rawIcon = rawTool.icon;
|
|
let icon: IToolData['icon'] | undefined;
|
|
if (typeof rawIcon === 'string') {
|
|
icon = ThemeIcon.fromString(rawIcon) ?? {
|
|
dark: joinPath(extension.description.extensionLocation, rawIcon),
|
|
light: joinPath(extension.description.extensionLocation, rawIcon)
|
|
};
|
|
} else if (rawIcon) {
|
|
icon = {
|
|
dark: joinPath(extension.description.extensionLocation, rawIcon.dark),
|
|
light: joinPath(extension.description.extensionLocation, rawIcon.light)
|
|
};
|
|
}
|
|
|
|
const tool: IToolData = {
|
|
...rawTool,
|
|
icon
|
|
};
|
|
const disposable = languageModelToolsService.registerToolData(tool);
|
|
this._registrationDisposables.set(toToolKey(extension.description.identifier, rawTool.id), disposable);
|
|
}
|
|
}
|
|
|
|
for (const extension of delta.removed) {
|
|
for (const tool of extension.value) {
|
|
this._registrationDisposables.deleteAndDispose(toToolKey(extension.description.identifier, tool.id));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|