mirror of
https://github.com/microsoft/vscode-extension-samples.git
synced 2026-04-27 16:55:44 +08:00
Adopt tools change
This commit is contained in:
@ -29,6 +29,13 @@
|
||||
"description": "I use tools",
|
||||
"isSticky": true
|
||||
}
|
||||
],
|
||||
"languageModelTools": [
|
||||
{
|
||||
"name": "chat-sample.activeTabCount",
|
||||
"description": "The number of active tabs in the editor",
|
||||
"displayName": "Text tab count"
|
||||
}
|
||||
]
|
||||
},
|
||||
"main": "./out/extension.js",
|
||||
|
||||
@ -1,22 +1,31 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
const PARTICIPANT_ID = 'chat-sample.tools';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
context.subscriptions.push(registerChatTool());
|
||||
context.subscriptions.push(registerChatParticipant());
|
||||
}
|
||||
|
||||
function registerChatTool() {
|
||||
return vscode.lm.registerTool('chat-sample.activeTabCount', {
|
||||
async invoke(parameters, token) {
|
||||
return {
|
||||
toString() {
|
||||
const activeTabCount = vscode.window.tabGroups.activeTabGroup.tabs.length;
|
||||
return `There are ${activeTabCount} tabs open.`;
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function registerChatParticipant() {
|
||||
const handler: vscode.ChatRequestHandler = async (request: vscode.ChatRequest, chatContext: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken) => {
|
||||
const models = await vscode.lm.selectChatModels({
|
||||
vendor: 'copilot',
|
||||
family: 'gpt-4-turbo'
|
||||
family: 'gpt-4o'
|
||||
});
|
||||
|
||||
const chat = models[0];
|
||||
|
||||
if (!chat) {
|
||||
console.log('NO MODELS')
|
||||
return {};
|
||||
}
|
||||
|
||||
const model = models[0];
|
||||
stream.markdown(`Available tools: ${vscode.lm.tools.map(tool => tool.name).join(', ')}\n\n`);
|
||||
|
||||
const options: vscode.LanguageModelChatRequestOptions = {
|
||||
@ -25,24 +34,23 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
name: tool.name.replace(/\./g, '_'),
|
||||
description: tool.description,
|
||||
parametersSchema: tool.parametersSchema ?? {}
|
||||
}
|
||||
};
|
||||
}),
|
||||
justification: 'Just because!',
|
||||
}
|
||||
};
|
||||
|
||||
const messages = [
|
||||
vscode.LanguageModelChatMessage.User(`There is a selection of tools that may give helpful context to answer the user's query. If you aren't sure which tool is relevant, you can call multiple tools.`),
|
||||
vscode.LanguageModelChatMessage.User(request.prompt)
|
||||
vscode.LanguageModelChatMessage.User(request.prompt),
|
||||
];
|
||||
const runWithFunctions = async () => {
|
||||
|
||||
let didReceiveFunctionUse = false;
|
||||
|
||||
const response = await chat.sendRequest(messages, options, token);
|
||||
const response = await model.sendRequest(messages, options, token);
|
||||
|
||||
for await (const part of response.stream) {
|
||||
if (part instanceof vscode.LanguageModelChatResponseTextPart) {
|
||||
stream.markdown(part.value)
|
||||
stream.markdown(part.value);
|
||||
} else if (part instanceof vscode.LanguageModelChatResponseFunctionUsePart) {
|
||||
const tool = vscode.lm.tools.find(tool => tool.name.replace(/\./g, '_') === part.name);
|
||||
if (!tool) {
|
||||
@ -50,22 +58,27 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let parameters: any;
|
||||
try {
|
||||
parameters = JSON.parse(part.parameters);
|
||||
} catch (err) {
|
||||
throw new Error(`Got invalid tool use parameters: "${part.parameters}". (${(err as Error).message})`);
|
||||
}
|
||||
|
||||
const resultPromise = vscode.lm.invokeTool(tool.name, JSON.parse(part.parameters), token);
|
||||
stream.progress(`FUNCTION_CALL: ${tool.name} with \`${part.parameters}\``, async (progress) => {
|
||||
await resultPromise;
|
||||
});
|
||||
stream.progress(`FUNCTION_CALL: ${tool.name} with ${part.parameters}`);
|
||||
|
||||
const result = await resultPromise;
|
||||
|
||||
// NOTE that the result of calling a function is a special content type of a USER-message
|
||||
let message = vscode.LanguageModelChatMessage.User('');
|
||||
message.content2 = new vscode.LanguageModelChatMessageFunctionResultPart(tool.name, result)
|
||||
messages.push(message)
|
||||
message.content2 = new vscode.LanguageModelChatMessageFunctionResultPart(tool.name.replace(/\./g, '_'), result.toString());
|
||||
messages.push(message);
|
||||
|
||||
// IMPORTANT
|
||||
// IMPORTANT working around CAPI always wanting to end with a `User`-message
|
||||
// IMPORTANT
|
||||
messages.push(vscode.LanguageModelChatMessage.User(`Above is the result of calling the function ${tool.name}. The user cannot see this result, so you should explain it to the user if referencing it in your answer.`))
|
||||
messages.push(vscode.LanguageModelChatMessage.User(`Above is the result of calling the function ${tool.name}. The user cannot see this result, so you should explain it to the user if referencing it in your answer.`));
|
||||
didReceiveFunctionUse = true;
|
||||
}
|
||||
}
|
||||
@ -76,11 +89,13 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
}
|
||||
};
|
||||
|
||||
await runWithFunctions()
|
||||
await runWithFunctions();
|
||||
};
|
||||
|
||||
const toolUser = vscode.chat.createChatParticipant(PARTICIPANT_ID, handler);
|
||||
const toolUser = vscode.chat.createChatParticipant('chat-sample.tools', handler);
|
||||
toolUser.iconPath = new vscode.ThemeIcon('tools');
|
||||
|
||||
return toolUser;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,252 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'vscode' {
|
||||
|
||||
export interface ChatParticipant {
|
||||
onDidPerformAction: Event<ChatUserActionEvent>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Now only used for the "intent detection" API below
|
||||
*/
|
||||
export interface ChatCommand {
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
}
|
||||
|
||||
export class ChatResponseDetectedParticipantPart {
|
||||
participant: string;
|
||||
// TODO@API validate this against statically-declared slash commands?
|
||||
command?: ChatCommand;
|
||||
constructor(participant: string, command?: ChatCommand);
|
||||
}
|
||||
|
||||
export interface ChatVulnerability {
|
||||
title: string;
|
||||
description: string;
|
||||
// id: string; // Later we will need to be able to link these across multiple content chunks.
|
||||
}
|
||||
|
||||
export class ChatResponseMarkdownWithVulnerabilitiesPart {
|
||||
value: MarkdownString;
|
||||
vulnerabilities: ChatVulnerability[];
|
||||
constructor(value: string | MarkdownString, vulnerabilities: ChatVulnerability[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a {@link Command command} as a button in the chat response.
|
||||
*/
|
||||
export interface ChatCommandButton {
|
||||
command: Command;
|
||||
}
|
||||
|
||||
export interface ChatDocumentContext {
|
||||
uri: Uri;
|
||||
version: number;
|
||||
ranges: Range[];
|
||||
}
|
||||
|
||||
export class ChatResponseTextEditPart {
|
||||
uri: Uri;
|
||||
edits: TextEdit[];
|
||||
constructor(uri: Uri, edits: TextEdit | TextEdit[]);
|
||||
}
|
||||
|
||||
export class ChatResponseConfirmationPart {
|
||||
title: string;
|
||||
message: string;
|
||||
data: any;
|
||||
constructor(title: string, message: string, data: any);
|
||||
}
|
||||
|
||||
export type ExtendedChatResponsePart = ChatResponsePart | ChatResponseTextEditPart | ChatResponseDetectedParticipantPart | ChatResponseConfirmationPart;
|
||||
|
||||
export class ChatResponseWarningPart {
|
||||
value: MarkdownString;
|
||||
constructor(value: string | MarkdownString);
|
||||
}
|
||||
|
||||
export class ChatResponseProgressPart2 extends ChatResponseProgressPart {
|
||||
value: string;
|
||||
task?: (progress: Progress<ChatResponseWarningPart | ChatResponseReferencePart>) => Thenable<string | void>;
|
||||
constructor(value: string, task?: (progress: Progress<ChatResponseWarningPart | ChatResponseReferencePart>) => Thenable<string | void>);
|
||||
}
|
||||
|
||||
export interface ChatResponseStream {
|
||||
|
||||
/**
|
||||
* Push a progress part to this stream. Short-hand for
|
||||
* `push(new ChatResponseProgressPart(value))`.
|
||||
*
|
||||
* @param value A progress message
|
||||
* @param task If provided, a task to run while the progress is displayed. When the Thenable resolves, the progress will be marked complete in the UI, and the progress message will be updated to the resolved string if one is specified.
|
||||
* @returns This stream.
|
||||
*/
|
||||
progress(value: string, task?: (progress: Progress<ChatResponseWarningPart | ChatResponseReferencePart>) => Thenable<string | void>): void;
|
||||
|
||||
textEdit(target: Uri, edits: TextEdit | TextEdit[]): void;
|
||||
markdownWithVulnerabilities(value: string | MarkdownString, vulnerabilities: ChatVulnerability[]): void;
|
||||
detectedParticipant(participant: string, command?: ChatCommand): void;
|
||||
push(part: ChatResponsePart | ChatResponseTextEditPart | ChatResponseDetectedParticipantPart | ChatResponseWarningPart | ChatResponseProgressPart2): void;
|
||||
|
||||
/**
|
||||
* Show an inline message in the chat view asking the user to confirm an action.
|
||||
* Multiple confirmations may be shown per response. The UI might show "Accept All" / "Reject All" actions.
|
||||
* @param title The title of the confirmation entry
|
||||
* @param message An extra message to display to the user
|
||||
* @param data An arbitrary JSON-stringifiable object that will be included in the ChatRequest when
|
||||
* the confirmation is accepted or rejected
|
||||
* TODO@API should this be MarkdownString?
|
||||
* TODO@API should actually be a more generic function that takes an array of buttons
|
||||
*/
|
||||
confirmation(title: string, message: string, data: any): void;
|
||||
|
||||
/**
|
||||
* Push a warning to this stream. Short-hand for
|
||||
* `push(new ChatResponseWarningPart(message))`.
|
||||
*
|
||||
* @param message A warning message
|
||||
* @returns This stream.
|
||||
*/
|
||||
warning(message: string | MarkdownString): void;
|
||||
|
||||
reference(value: Uri | Location | { variableName: string; value?: Uri | Location }, iconPath?: Uri | ThemeIcon | { light: Uri; dark: Uri }): void;
|
||||
|
||||
push(part: ExtendedChatResponsePart): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this piggy-back on the existing ChatRequest, or is it a different type of request entirely?
|
||||
* Does it show up in history?
|
||||
*/
|
||||
export interface ChatRequest {
|
||||
/**
|
||||
* The `data` for any confirmations that were accepted
|
||||
*/
|
||||
acceptedConfirmationData?: any[];
|
||||
|
||||
/**
|
||||
* The `data` for any confirmations that were rejected
|
||||
*/
|
||||
rejectedConfirmationData?: any[];
|
||||
}
|
||||
|
||||
// TODO@API fit this into the stream
|
||||
export interface ChatUsedContext {
|
||||
documents: ChatDocumentContext[];
|
||||
}
|
||||
|
||||
export interface ChatParticipant {
|
||||
/**
|
||||
* Provide a set of variables that can only be used with this participant.
|
||||
*/
|
||||
participantVariableProvider?: { provider: ChatParticipantCompletionItemProvider; triggerCharacters: string[] };
|
||||
}
|
||||
|
||||
export interface ChatParticipantCompletionItemProvider {
|
||||
provideCompletionItems(query: string, token: CancellationToken): ProviderResult<ChatCompletionItem[]>;
|
||||
}
|
||||
|
||||
export class ChatCompletionItem {
|
||||
id: string;
|
||||
label: string | CompletionItemLabel;
|
||||
values: ChatVariableValue[];
|
||||
fullName?: string;
|
||||
icon?: ThemeIcon;
|
||||
insertText?: string;
|
||||
detail?: string;
|
||||
documentation?: string | MarkdownString;
|
||||
command?: Command;
|
||||
|
||||
constructor(id: string, label: string | CompletionItemLabel, values: ChatVariableValue[]);
|
||||
}
|
||||
|
||||
export type ChatExtendedRequestHandler = (request: ChatRequest, context: ChatContext, response: ChatResponseStream, token: CancellationToken) => ProviderResult<ChatResult | void>;
|
||||
|
||||
export namespace chat {
|
||||
/**
|
||||
* Create a chat participant with the extended progress type
|
||||
*/
|
||||
export function createChatParticipant(id: string, handler: ChatExtendedRequestHandler): ChatParticipant;
|
||||
|
||||
/**
|
||||
* Current version of the proposal. Changes whenever backwards-incompatible changes are made.
|
||||
* If a new feature is added that doesn't break existing code, the version is not incremented. When the extension uses this new feature, it should set its engines.vscode version appropriately.
|
||||
* But if a change is made to an existing feature that would break existing code, the version should be incremented.
|
||||
* The chat extension should not activate if it doesn't support the current version.
|
||||
*/
|
||||
export const _version: 1 | number;
|
||||
}
|
||||
|
||||
/*
|
||||
* User action events
|
||||
*/
|
||||
|
||||
export enum ChatCopyKind {
|
||||
// Keyboard shortcut or context menu
|
||||
Action = 1,
|
||||
Toolbar = 2
|
||||
}
|
||||
|
||||
export interface ChatCopyAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'copy';
|
||||
codeBlockIndex: number;
|
||||
copyKind: ChatCopyKind;
|
||||
copiedCharacters: number;
|
||||
totalCharacters: number;
|
||||
copiedText: string;
|
||||
}
|
||||
|
||||
export interface ChatInsertAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'insert';
|
||||
codeBlockIndex: number;
|
||||
totalCharacters: number;
|
||||
newFile?: boolean;
|
||||
}
|
||||
|
||||
export interface ChatTerminalAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'runInTerminal';
|
||||
codeBlockIndex: number;
|
||||
languageId?: string;
|
||||
}
|
||||
|
||||
export interface ChatCommandAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'command';
|
||||
commandButton: ChatCommandButton;
|
||||
}
|
||||
|
||||
export interface ChatFollowupAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'followUp';
|
||||
followup: ChatFollowup;
|
||||
}
|
||||
|
||||
export interface ChatBugReportAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'bug';
|
||||
}
|
||||
|
||||
export interface ChatEditorAction {
|
||||
kind: 'editor';
|
||||
accepted: boolean;
|
||||
}
|
||||
|
||||
export interface ChatUserActionEvent {
|
||||
readonly result: ChatResult;
|
||||
readonly action: ChatCopyAction | ChatInsertAction | ChatTerminalAction | ChatCommandAction | ChatFollowupAction | ChatBugReportAction | ChatEditorAction;
|
||||
}
|
||||
|
||||
export interface ChatPromptReference {
|
||||
/**
|
||||
* TODO Needed for now to drive the variableName-type reference, but probably both of these should go away in the future.
|
||||
*/
|
||||
readonly name: string;
|
||||
}
|
||||
}
|
||||
24
chat-sample/vscode.proposed.lmTools.d.ts
vendored
24
chat-sample/vscode.proposed.lmTools.d.ts
vendored
@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// version: 2
|
||||
// version: 3
|
||||
// https://github.com/microsoft/vscode/issues/213274
|
||||
|
||||
declare module 'vscode' {
|
||||
@ -23,6 +23,11 @@ declare module 'vscode' {
|
||||
export interface LanguageModelChatRequestOptions {
|
||||
// TODO@API this will a heterogeneous array of different types of tools
|
||||
tools?: LanguageModelChatFunction[];
|
||||
|
||||
/**
|
||||
* Force a specific tool to be used.
|
||||
*/
|
||||
toolChoice?: string;
|
||||
}
|
||||
|
||||
// LM -> USER: function that should be used
|
||||
@ -59,6 +64,18 @@ declare module 'vscode' {
|
||||
content2: string | LanguageModelChatMessageFunctionResultPart;
|
||||
}
|
||||
|
||||
export interface LanguageModelToolResult {
|
||||
/**
|
||||
* The result can contain arbitrary representations of the content. An example might be 'prompt-tsx' to indicate an element that can be rendered with the @vscode/prompt-tsx library.
|
||||
*/
|
||||
[contentType: string]: any;
|
||||
|
||||
/**
|
||||
* A string representation of the result which can be incorporated back into an LLM prompt without any special handling.
|
||||
*/
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
// Tool registration/invoking between extensions
|
||||
|
||||
export namespace lm {
|
||||
@ -74,8 +91,9 @@ declare module 'vscode' {
|
||||
|
||||
/**
|
||||
* Invoke a tool with the given parameters.
|
||||
* TODO Could request a set of contentTypes to be returned so they don't all need to be computed?
|
||||
*/
|
||||
export function invokeTool(name: string, parameters: Object, token: CancellationToken): Thenable<string>;
|
||||
export function invokeTool(name: string, parameters: Object, token: CancellationToken): Thenable<LanguageModelToolResult>;
|
||||
}
|
||||
|
||||
// Is the same as LanguageModelChatFunction now, but could have more details in the future
|
||||
@ -86,6 +104,6 @@ declare module 'vscode' {
|
||||
}
|
||||
|
||||
export interface LanguageModelTool {
|
||||
invoke(parameters: any, token: CancellationToken): Thenable<string>;
|
||||
invoke(parameters: any, token: CancellationToken): Thenable<LanguageModelToolResult>;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user