Adopt @vscode/chat-extension-utils (#1130)

* Adopt @vscode/chat-extension-utils

* Update readme
This commit is contained in:
Rob Lourens
2024-11-18 09:46:04 -08:00
committed by GitHub
parent 9378d7c929
commit c9e032ef24
10 changed files with 107 additions and 20108 deletions

View File

@ -5,4 +5,5 @@
"git.branchProtection": [
"main"
],
"files.trimTrailingWhitespace": true
}

View File

@ -8,9 +8,10 @@ When an extension uses the Chat or the Language Model API, we call it a GitHub C
This GitHub Copilot Extension sample shows:
- How to contribute a simple chat participant to the GitHub Copilot Chat view.
- How to use the Language Model API to request access to the Language Model (gpt-4o, gpt-3.5-turbo, gpt-4).
- How to contribute a more sophisticated chat participant that uses the LanguageModelTool API to contribute and invoke tools.
- How to contribute a simple chat participant to the GitHub Copilot Chat view. (`@cat`, [simple.ts](src/simple.ts))
- How to use the Language Model API to request access to the Language Model.
- How to use the `@vscode/chat-extension-utils` library to easily create a chat participant that uses tools. (`@catTools`, [chatUtilsSample.ts](src/chatUtilsSample.ts))
- How to contribute a more sophisticated chat participant that uses the LanguageModelTool API to contribute and invoke tools. (`@tool`, [toolParticipant.ts](src/toolParticipant.ts))
![demo](./demo.png)
@ -32,4 +33,4 @@ This sample shows two different ways to build a chat participant in VS Code:
See [simple.ts](src/simple.ts) for an example of a simple chat participant that makes requests and responds to user queries. It shows how you can create chat participants with or without the [@vscode/prompt-tsx](https://www.npmjs.com/package/@vscode/prompt-tsx) library.
See [toolParticipant.ts](src/toolParticipant.ts) for an example of a chat participant that invokes tools, either dynamically or using the `toolReferences` that are attached to the request. This is a more advanced example that shows how you can use the [@vscode/prompt-tsx](https://www.npmjs.com/package/@vscode/prompt-tsx) library to implement the LLM tool calling flow and tries to implement all the features of the chat API.
See [toolParticipant.ts](src/toolParticipant.ts) for an example of a chat participant that invokes tools, either dynamically or using the `toolReferences` that are attached to the request. This is a more advanced example that shows how you can use the [@vscode/prompt-tsx](https://www.npmjs.com/package/@vscode/prompt-tsx) library to implement the LLM tool calling flow and tries to implement all the features of the chat API.

View File

@ -8,12 +8,14 @@
"name": "chat-sample",
"version": "0.1.0",
"dependencies": {
"@vscode/chat-extension-utils": "^0.0.0-alpha.1",
"@vscode/prompt-tsx": "^0.3.0-alpha.12"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@stylistic/eslint-plugin": "^2.9.0",
"@types/node": "^20",
"@types/vscode": "^1.95.0",
"eslint": "^9.13.0",
"typescript": "^5.6.2",
"typescript-eslint": "^8.11.0"
@ -278,6 +280,13 @@
"undici-types": "~5.26.4"
}
},
"node_modules/@types/vscode": {
"version": "1.95.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.95.0.tgz",
"integrity": "sha512-0LBD8TEiNbet3NvWsmn59zLzOFu/txSlGxnv5yAFHCrhG9WvAnR3IvfHzMOs2aeWqgvNjq9pO99IUw8d3n+unw==",
"dev": true,
"license": "MIT"
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz",
@ -505,10 +514,20 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@vscode/chat-extension-utils": {
"version": "0.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/@vscode/chat-extension-utils/-/chat-extension-utils-0.0.0-alpha.1.tgz",
"integrity": "sha512-49eYur98d1iukPEQqMYQL4lJgaKnM0QFQB4/BFIFvuuKM+Kug2KNE/TSIJJQXrp5CrP0kDOmIIXvTnNRPtO2vg==",
"license": "MIT",
"dependencies": {
"@vscode/prompt-tsx": "^0.3.0-alpha.13"
}
},
"node_modules/@vscode/prompt-tsx": {
"version": "0.3.0-alpha.12",
"resolved": "https://registry.npmjs.org/@vscode/prompt-tsx/-/prompt-tsx-0.3.0-alpha.12.tgz",
"integrity": "sha512-2ANm569UBXIzjPbaDFjzRkucelhsnlnmYIPdDo+USeFq2Do0Q70gKiiRWYrQf5rPqCxrChDvgU14nsdJLUSaOQ=="
"version": "0.3.0-alpha.13",
"resolved": "https://registry.npmjs.org/@vscode/prompt-tsx/-/prompt-tsx-0.3.0-alpha.13.tgz",
"integrity": "sha512-0m9Hy2VqfGcFgXmY7xFV1nYngoq2zm2Wy/3YdesmR6bOwFrJed9xW87y43Ax7UFVHwtjZkpjn4M9HbFvxvzdWA==",
"license": "MIT"
},
"node_modules/acorn": {
"version": "8.13.0",

View File

@ -72,6 +72,19 @@
"description": "Use all registered tools. By default, only this extension's tools are used."
}
]
},
{
"id": "chat-tools-sample.catTools",
"fullName": "Cat (Tools)",
"name": "catTools",
"description": "I use tools, implemented using @vscode/chat-extension-utils, and am also a cat",
"isSticky": true,
"commands": [
{
"name": "all",
"description": "Use all registered tools. By default, only this extension's tools are used."
}
]
}
],
"languageModelTools": [
@ -155,12 +168,14 @@
"watch": "tsc -watch -p ./"
},
"dependencies": {
"@vscode/chat-extension-utils": "^0.0.0-alpha.1",
"@vscode/prompt-tsx": "^0.3.0-alpha.12"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@stylistic/eslint-plugin": "^2.9.0",
"@types/node": "^20",
"@types/vscode": "^1.95.0",
"eslint": "^9.13.0",
"typescript": "^5.6.2",
"typescript-eslint": "^8.11.0"

View File

@ -0,0 +1,35 @@
import * as vscode from 'vscode';
import * as chatUtils from '@vscode/chat-extension-utils';
export function registerChatLibChatParticipant(context: vscode.ExtensionContext) {
const handler: vscode.ChatRequestHandler = async (request: vscode.ChatRequest, chatContext: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken) => {
if (request.command === 'list') {
stream.markdown(`Available tools: ${vscode.lm.tools.map(tool => tool.name).join(', ')}\n\n`);
return;
}
const tools = request.command === 'all' ?
vscode.lm.tools :
vscode.lm.tools.filter(tool => tool.tags.includes('chat-tools-sample'));
const libResult = chatUtils.sendChatParticipantRequest(
request,
chatContext,
{
prompt: 'You are a cat! Answer as a cat.',
responseStreamOptions: {
stream,
references: true,
responseText: true
},
tools
},
token);
return await libResult.result;
};
const chatLibParticipant = vscode.chat.createChatParticipant('chat-tools-sample.catTools', handler);
chatLibParticipant.iconPath = vscode.Uri.joinPath(context.extensionUri, 'cat.jpeg');
context.subscriptions.push(chatLibParticipant);
}

View File

@ -1,18 +1,15 @@
import * as vscode from 'vscode';
import { FindFilesTool, RunInTerminalTool, TabCountTool } from './tools';
import { registerToolUserChatParticipant } from './toolParticipant';
import { registerChatLibChatParticipant } from './chatUtilsSample';
import { registerSimpleParticipant } from './simple';
import { registerToolUserChatParticipant } from './toolParticipant';
import { registerChatTools } from './tools';
export function activate(context: vscode.ExtensionContext) {
registerSimpleParticipant(context);
registerChatTools(context);
registerToolUserChatParticipant(context);
}
registerChatLibChatParticipant(context);
function registerChatTools(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.lm.registerTool('chat-tools-sample_tabCount', new TabCountTool()));
context.subscriptions.push(vscode.lm.registerTool('chat-tools-sample_findFiles', new FindFilesTool()));
context.subscriptions.push(vscode.lm.registerTool('chat-tools-sample_runInTerminal', new RunInTerminalTool()));
registerChatTools(context);
}
export function deactivate() { }

View File

@ -13,7 +13,7 @@ interface ICatChatResult extends vscode.ChatResult {
export function registerSimpleParticipant(context: vscode.ExtensionContext) {
// Define a Cat chat handler.
// Define a Cat chat handler.
const handler: vscode.ChatRequestHandler = async (request: vscode.ChatRequest, context: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken): Promise<ICatChatResult> => {
// To talk to an LLM in your subcommand handler implementation, your
// extension can use VS Code's `requestChatAccess` API to access the Copilot API.

View File

@ -1,5 +1,11 @@
import * as vscode from 'vscode';
export function registerChatTools(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.lm.registerTool('chat-tools-sample_tabCount', new TabCountTool()));
context.subscriptions.push(vscode.lm.registerTool('chat-tools-sample_findFiles', new FindFilesTool()));
context.subscriptions.push(vscode.lm.registerTool('chat-tools-sample_runInTerminal', new RunInTerminalTool()));
}
interface ITabCountParameters {
tabGroup?: number;
}
@ -7,7 +13,7 @@ interface ITabCountParameters {
export class TabCountTool implements vscode.LanguageModelTool<ITabCountParameters> {
async invoke(
options: vscode.LanguageModelToolInvocationOptions<ITabCountParameters>,
token: vscode.CancellationToken
_token: vscode.CancellationToken
) {
const params = options.input;
if (typeof params.tabGroup === 'number') {
@ -16,10 +22,10 @@ export class TabCountTool implements vscode.LanguageModelTool<ITabCountParameter
params.tabGroup === 1
? '1st'
: params.tabGroup === 2
? '2nd'
: params.tabGroup === 3
? '3rd'
: `${params.tabGroup}th`;
? '2nd'
: params.tabGroup === 3
? '3rd'
: `${params.tabGroup}th`;
return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`There are ${group.tabs.length} tabs open in the ${nth} tab group.`)]);
} else {
const group = vscode.window.tabGroups.activeTabGroup;
@ -29,15 +35,15 @@ export class TabCountTool implements vscode.LanguageModelTool<ITabCountParameter
async prepareInvocation(
options: vscode.LanguageModelToolInvocationPrepareOptions<ITabCountParameters>,
token: vscode.CancellationToken
_token: vscode.CancellationToken
) {
const confirmationMessages = {
title: 'Count the number of open tabs',
message: new vscode.MarkdownString(
`Count the number of open tabs?` +
(options.input.tabGroup !== undefined
? ` in tab group ${options.input.tabGroup}`
: '')
(options.input.tabGroup !== undefined
? ` in tab group ${options.input.tabGroup}`
: '')
),
};
@ -71,7 +77,7 @@ export class FindFilesTool implements vscode.LanguageModelTool<IFindFilesParamet
async prepareInvocation(
options: vscode.LanguageModelToolInvocationPrepareOptions<IFindFilesParameters>,
token: vscode.CancellationToken
_token: vscode.CancellationToken
) {
return {
invocationMessage: `Searching workspace for "${options.input.pattern}"`,
@ -89,7 +95,7 @@ async function waitForShellIntegration(
): Promise<void> {
let resolve: () => void;
let reject: (e: Error) => void;
let p = new Promise<void>((_resolve, _reject) => {
const p = new Promise<void>((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
@ -108,11 +114,10 @@ async function waitForShellIntegration(
}
export class RunInTerminalTool
implements vscode.LanguageModelTool<IRunInTerminalParameters>
{
implements vscode.LanguageModelTool<IRunInTerminalParameters> {
async invoke(
options: vscode.LanguageModelToolInvocationOptions<IRunInTerminalParameters>,
token: vscode.CancellationToken
_token: vscode.CancellationToken
) {
const params = options.input as IRunInTerminalParameters;
@ -120,7 +125,7 @@ export class RunInTerminalTool
terminal.show();
try {
await waitForShellIntegration(terminal, 5000);
} catch(e) {
} catch (e) {
return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart((e as Error).message)]);
}
@ -137,13 +142,13 @@ export class RunInTerminalTool
async prepareInvocation(
options: vscode.LanguageModelToolInvocationPrepareOptions<IRunInTerminalParameters>,
token: vscode.CancellationToken
_token: vscode.CancellationToken
) {
const confirmationMessages = {
title: 'Run command in terminal',
message: new vscode.MarkdownString(
`Run this command in a terminal?` +
`\n\n\`\`\`\n${options.input.command}\n\`\`\`\n`
`\n\n\`\`\`\n${options.input.command}\n\`\`\`\n`
),
};

View File

@ -57,7 +57,7 @@ export class ToolUserPrompt extends PromptElement<ToolUserProps, void> {
<ToolCalls
toolCallRounds={this.props.toolCallRounds}
toolInvocationToken={this.props.request.toolInvocationToken}
toolCallResults={this.props.toolCallResults}/>
toolCallResults={this.props.toolCallResults} />
</>
);
}

20074
chat-sample/vscode.d.ts vendored

File diff suppressed because it is too large Load Diff