mirror of
https://github.com/microsoft/vscode-extension-samples.git
synced 2026-06-13 07:10:26 +08:00
Add chat-agent sample
This commit is contained in:
95
chat-agent-sample/src/extension.ts
Normal file
95
chat-agent-sample/src/extension.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
const CAT_LEARN_SYSTEM_PROMPT = 'You are a cat! Your job is to explain computer science concepts in a funny manner of a cat. Always start your response by stating what concept you are explaining.';
|
||||
const CAT_PLAY_SYSTEM_PROMPT = 'You are a cat that wants to play! Reply in a helpful way for a coder, but with the hidden meaning that all you want to do is play.';
|
||||
const MEOW_COMMAND_ID = 'cat.meow';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
const teachResult = { /* you can return anything in your result object */ };
|
||||
const playResult = { /* you can return anything in your result object */ };
|
||||
// Define a Cat chat agent handler.
|
||||
const handler: vscode.ChatAgentHandler = async (request: vscode.ChatAgentRequest, context: vscode.ChatAgentContext, progress: vscode.Progress<vscode.ChatAgentProgress>, token: vscode.CancellationToken) => {
|
||||
// To talk to an LLM in your slash command handler implementation, your
|
||||
// extension can use VS Code's `requestChatAccess` API to access the Copilot API.
|
||||
// The pre-release of the GitHub Copilot Chat extension implements this provider.
|
||||
if (request.slashCommand?.name == 'teach') {
|
||||
const access = await vscode.chat.requestChatAccess('copilot');
|
||||
const topics = ["linked list", "recursion", "stack", "queue", "pointers"];
|
||||
const topic = topics[Math.floor(Math.random() * topics.length)];
|
||||
const messages = [
|
||||
{
|
||||
role: vscode.ChatMessageRole.System,
|
||||
content: CAT_LEARN_SYSTEM_PROMPT
|
||||
},
|
||||
{
|
||||
role: vscode.ChatMessageRole.User,
|
||||
content: topic
|
||||
},
|
||||
];
|
||||
const request = access.makeRequest(messages, {}, token);
|
||||
for await (const fragment of request.response) {
|
||||
const incomingText = fragment.replace('[RESPONSE END]', '');
|
||||
progress.report({ content: incomingText });
|
||||
}
|
||||
return teachResult;
|
||||
} else if (request.slashCommand?.name == 'play') {
|
||||
const access = await vscode.chat.requestChatAccess('copilot');
|
||||
const messages = [
|
||||
{
|
||||
role: vscode.ChatMessageRole.System,
|
||||
content: CAT_PLAY_SYSTEM_PROMPT
|
||||
},
|
||||
];
|
||||
const request = access.makeRequest(messages, {}, token);
|
||||
for await (const fragment of request.response) {
|
||||
const incomingText = fragment.replace('[RESPONSE END]', '');
|
||||
progress.report({ content: incomingText });
|
||||
}
|
||||
return playResult;
|
||||
}
|
||||
};
|
||||
|
||||
// Agents appear as top-level options in the chat input
|
||||
// when you type `@`, and can contribute sub-commands in the chat input
|
||||
// that appear when you type `/`.
|
||||
const agent = vscode.chat.createChatAgent('cat', handler);
|
||||
agent.iconPath = vscode.Uri.joinPath(context.extensionUri, 'cat.jpeg');
|
||||
agent.description = vscode.l10n.t('Meow! What can I help you with?');
|
||||
agent.fullName = vscode.l10n.t('Cat');
|
||||
agent.slashCommandProvider = {
|
||||
provideSlashCommands(token) {
|
||||
return [
|
||||
{ name: 'teach', description: 'Pick at random a computer science concept then explain it in purfect way of a cat' },
|
||||
{ name: 'play', description: 'Do whatever you want, you are a cat after all' }
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
agent.followupProvider = {
|
||||
provideFollowups(result, token) {
|
||||
if (result === teachResult) {
|
||||
return [{
|
||||
commandId: MEOW_COMMAND_ID,
|
||||
message: '@cat thank you',
|
||||
title: vscode.l10n.t('Meow!')
|
||||
}];
|
||||
} else if (result === playResult) {
|
||||
return [{
|
||||
message: '@cat let us play',
|
||||
title: vscode.l10n.t('Play with the cat')
|
||||
}];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
context.subscriptions.push(
|
||||
agent,
|
||||
// Register the command handler for the /meow followup
|
||||
vscode.commands.registerCommand(MEOW_COMMAND_ID, async () => {
|
||||
vscode.window.showInformationMessage('Meow!');
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() { }
|
||||
25
chat-agent-sample/src/vscode.proposed.chat.d.ts
vendored
Normal file
25
chat-agent-sample/src/vscode.proposed.chat.d.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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' {
|
||||
|
||||
// ChatML
|
||||
export enum ChatMessageRole {
|
||||
System = 0,
|
||||
User = 1,
|
||||
Assistant = 2,
|
||||
Function = 3,
|
||||
}
|
||||
|
||||
// ChatML
|
||||
export class ChatMessage {
|
||||
role: ChatMessageRole;
|
||||
content: string;
|
||||
name?: string;
|
||||
|
||||
constructor(role: ChatMessageRole, content: string);
|
||||
}
|
||||
|
||||
}
|
||||
368
chat-agent-sample/src/vscode.proposed.chatAgents2.d.ts
vendored
Normal file
368
chat-agent-sample/src/vscode.proposed.chatAgents2.d.ts
vendored
Normal file
@ -0,0 +1,368 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 ChatAgentContext {
|
||||
/**
|
||||
* All of the chat messages so far in the current chat session.
|
||||
*/
|
||||
history: ChatMessage[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an error result from a chat request.
|
||||
*/
|
||||
export interface ChatAgentErrorDetails {
|
||||
/**
|
||||
* An error message that is shown to the user.
|
||||
*/
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* If partial markdown content was sent over the `progress` callback before the response terminated, then this flag
|
||||
* can be set to true and it will be rendered with incomplete markdown features patched up.
|
||||
*
|
||||
* For example, if the response terminated after sending part of a triple-backtick code block, then the editor will
|
||||
* render it as a complete code block.
|
||||
*/
|
||||
responseIsIncomplete?: boolean;
|
||||
|
||||
/**
|
||||
* If set to true, the response will be partly blurred out.
|
||||
*/
|
||||
responseIsFiltered?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of a chat request.
|
||||
*/
|
||||
export interface ChatAgentResult2 {
|
||||
/**
|
||||
* If the request resulted in an error, this property defines the error details.
|
||||
*/
|
||||
errorDetails?: ChatAgentErrorDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the type of user feedback received.
|
||||
*/
|
||||
export enum ChatAgentResultFeedbackKind {
|
||||
/**
|
||||
* The user marked the result as helpful.
|
||||
*/
|
||||
Unhelpful = 0,
|
||||
|
||||
/**
|
||||
* The user marked the result as unhelpful.
|
||||
*/
|
||||
Helpful = 1,
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents user feedback for a result.
|
||||
*/
|
||||
export interface ChatAgentResult2Feedback {
|
||||
/**
|
||||
* This instance of ChatAgentResult2 is the same instance that was returned from the chat agent,
|
||||
* and it can be extended with arbitrary properties if needed.
|
||||
*/
|
||||
readonly result: ChatAgentResult2;
|
||||
|
||||
/**
|
||||
* The kind of feedback that was received.
|
||||
*/
|
||||
readonly kind: ChatAgentResultFeedbackKind;
|
||||
}
|
||||
|
||||
export interface ChatAgentSlashCommand {
|
||||
/**
|
||||
* A short name by which this command is referred to in the UI, e.g. `fix` or
|
||||
* `explain` for commands that fix an issue or explain code.
|
||||
*
|
||||
* **Note**: The name should be unique among the slash commands provided by this agent.
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* Human-readable description explaining what this command does.
|
||||
*/
|
||||
readonly description: string;
|
||||
|
||||
/**
|
||||
* When the user clicks this slash command in `/help`, this text will be submitted to this slash command
|
||||
*/
|
||||
readonly sampleRequest?: string;
|
||||
|
||||
/**
|
||||
* Whether executing the command puts the
|
||||
* chat into a persistent mode, where the
|
||||
* slash command is prepended to the chat input.
|
||||
*/
|
||||
readonly shouldRepopulate?: boolean;
|
||||
/**
|
||||
* Placeholder text to render in the chat input
|
||||
* when the slash command has been repopulated.
|
||||
* Has no effect if `shouldRepopulate` is `false`.
|
||||
*/
|
||||
readonly followupPlaceholder?: string;
|
||||
}
|
||||
|
||||
export interface ChatAgentSlashCommandProvider {
|
||||
|
||||
/**
|
||||
* Returns a list of slash commands that its agent is capable of handling. A slash command
|
||||
* can be selected by the user and will then be passed to the {@link ChatAgentHandler handler}
|
||||
* via the {@link ChatAgentRequest.slashCommand slashCommand} property.
|
||||
*
|
||||
*
|
||||
* @param token A cancellation token.
|
||||
* @returns A list of slash commands. The lack of a result can be signaled by returning `undefined`, `null`, or
|
||||
* an empty array.
|
||||
*/
|
||||
provideSlashCommands(token: CancellationToken): ProviderResult<ChatAgentSlashCommand[]>;
|
||||
}
|
||||
|
||||
// TODO@API This should become a progress type, and use vscode.Command
|
||||
// TODO@API what's the when-property for? how about not returning it in the first place?
|
||||
export interface ChatAgentCommandFollowup {
|
||||
commandId: string;
|
||||
args?: any[];
|
||||
title: string; // supports codicon strings
|
||||
when?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A followup question suggested by the model.
|
||||
*/
|
||||
export interface ChatAgentReplyFollowup {
|
||||
/**
|
||||
* The message to send to the chat.
|
||||
*/
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* A tooltip to show when hovering over the followup.
|
||||
*/
|
||||
tooltip?: string;
|
||||
|
||||
/**
|
||||
* A title to show the user, when it is different than the message.
|
||||
*/
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export type ChatAgentFollowup = ChatAgentCommandFollowup | ChatAgentReplyFollowup;
|
||||
|
||||
/**
|
||||
* Will be invoked once after each request to get suggested followup questions to show the user. The user can click the followup to send it to the chat.
|
||||
*/
|
||||
export interface FollowupProvider {
|
||||
/**
|
||||
*
|
||||
* @param result The same instance of the result object that was returned by the chat agent, and it can be extended with arbitrary properties if needed.
|
||||
* @param token A cancellation token.
|
||||
*/
|
||||
provideFollowups(result: ChatAgentResult2, token: CancellationToken): ProviderResult<ChatAgentFollowup[]>;
|
||||
}
|
||||
|
||||
export interface ChatAgent2 {
|
||||
|
||||
/**
|
||||
* The short name by which this agent is referred to in the UI, e.g `workspace`.
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* The full name of this agent.
|
||||
*/
|
||||
fullName: string;
|
||||
|
||||
/**
|
||||
* A human-readable description explaining what this agent does.
|
||||
*/
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* Icon for the agent shown in UI.
|
||||
*/
|
||||
iconPath?: Uri | {
|
||||
/**
|
||||
* The icon path for the light theme.
|
||||
*/
|
||||
light: Uri;
|
||||
/**
|
||||
* The icon path for the dark theme.
|
||||
*/
|
||||
dark: Uri;
|
||||
} | ThemeIcon;
|
||||
|
||||
/**
|
||||
* This provider will be called to retrieve the agent's slash commands.
|
||||
*/
|
||||
slashCommandProvider?: ChatAgentSlashCommandProvider;
|
||||
|
||||
/**
|
||||
* This provider will be called once after each request to retrieve suggested followup questions.
|
||||
*/
|
||||
followupProvider?: FollowupProvider;
|
||||
|
||||
/**
|
||||
* When the user clicks this agent in `/help`, this text will be submitted to this slash command
|
||||
*/
|
||||
sampleRequest?: string;
|
||||
|
||||
/**
|
||||
* An event that fires whenever feedback for a result is received, e.g. when a user up- or down-votes
|
||||
* a result.
|
||||
*
|
||||
* The passed {@link ChatAgentResult2Feedback.result result} is guaranteed to be the same instance that was
|
||||
* previously returned from this chat agent.
|
||||
*/
|
||||
onDidReceiveFeedback: Event<ChatAgentResult2Feedback>;
|
||||
|
||||
/**
|
||||
* TODO@API explain what happens wrt to history, in-flight requests etc...
|
||||
* Dispose this agent and free resources
|
||||
*/
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export interface ChatAgentRequest {
|
||||
|
||||
/**
|
||||
* The prompt entered by the user. The {@link ChatAgent2.name name} of the agent or the {@link ChatAgentSlashCommand.name slash command}
|
||||
* are not part of the prompt.
|
||||
*
|
||||
* @see {@link ChatAgentRequest.slashCommand}
|
||||
*/
|
||||
prompt: string;
|
||||
|
||||
/**
|
||||
* The {@link ChatAgentSlashCommand slash command} that was selected for this request. It is guaranteed that the passed slash
|
||||
* command is an instance that was previously returned from the {@link ChatAgentSlashCommandProvider.provideSlashCommands slash command provider}.
|
||||
*/
|
||||
slashCommand?: ChatAgentSlashCommand;
|
||||
|
||||
variables: Record<string, ChatVariableValue[]>;
|
||||
}
|
||||
|
||||
// TODO@API should these each be prefixed ChatAgentProgress*?
|
||||
export type ChatAgentProgress =
|
||||
| ChatAgentContent
|
||||
| ChatAgentTask
|
||||
| ChatAgentFileTree
|
||||
| ChatAgentUsedContext
|
||||
| ChatAgentContentReference
|
||||
| ChatAgentInlineContentReference;
|
||||
|
||||
/**
|
||||
* Indicates a piece of content that was used by the chat agent while processing the request. Will be displayed to the user.
|
||||
*/
|
||||
export interface ChatAgentContentReference {
|
||||
/**
|
||||
* The resource that was referenced.
|
||||
*/
|
||||
reference: Uri | Location;
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to a piece of content that will be rendered inline with the markdown content.
|
||||
*/
|
||||
export interface ChatAgentInlineContentReference {
|
||||
/**
|
||||
* The resource being referenced.
|
||||
*/
|
||||
inlineReference: Uri | Location;
|
||||
|
||||
/**
|
||||
* An alternate title for the resource.
|
||||
*/
|
||||
title?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A piece of the chat response's content. Will be merged with other progress pieces as needed, and rendered as markdown.
|
||||
*/
|
||||
export interface ChatAgentContent {
|
||||
/**
|
||||
* The content as a string of markdown source.
|
||||
*/
|
||||
content: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a piece of the chat response's content that is resolved asynchronously. It is rendered immediately with a placeholder,
|
||||
* which is replaced once the full content is available.
|
||||
*/
|
||||
export interface ChatAgentTask {
|
||||
/**
|
||||
* The markdown string to be rendered immediately.
|
||||
*/
|
||||
placeholder: string;
|
||||
|
||||
/**
|
||||
* A Thenable resolving to the real content. The placeholder will be replaced with this content once it's available.
|
||||
*/
|
||||
resolvedContent: Thenable<ChatAgentContent | ChatAgentFileTree>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a tree, such as a file and directory structure, rendered in the chat response.
|
||||
*/
|
||||
export interface ChatAgentFileTree {
|
||||
/**
|
||||
* The root node of the tree.
|
||||
*/
|
||||
treeData: ChatAgentFileTreeData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a node in a chat response tree.
|
||||
*/
|
||||
export interface ChatAgentFileTreeData {
|
||||
/**
|
||||
* A human-readable string describing this node.
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* A Uri for this node, opened when it's clicked.
|
||||
*/
|
||||
uri: Uri;
|
||||
|
||||
/**
|
||||
* The children of this node.
|
||||
*/
|
||||
children?: ChatAgentFileTreeData[];
|
||||
}
|
||||
|
||||
export interface ChatAgentDocumentContext {
|
||||
uri: Uri;
|
||||
version: number;
|
||||
ranges: Range[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Document references that should be used by the MappedEditsProvider.
|
||||
*/
|
||||
export interface ChatAgentUsedContext {
|
||||
documents: ChatAgentDocumentContext[];
|
||||
}
|
||||
|
||||
export type ChatAgentHandler = (request: ChatAgentRequest, context: ChatAgentContext, progress: Progress<ChatAgentProgress>, token: CancellationToken) => ProviderResult<ChatAgentResult2>;
|
||||
|
||||
export namespace chat {
|
||||
|
||||
/**
|
||||
* Create a new {@link ChatAgent2 chat agent} instance.
|
||||
*
|
||||
* @param name Short name by which this agent is referred to in the UI
|
||||
* @param handler The reply-handler of the agent.
|
||||
* @returns A new chat agent
|
||||
*/
|
||||
export function createChatAgent(name: string, handler: ChatAgentHandler): ChatAgent2;
|
||||
}
|
||||
}
|
||||
99
chat-agent-sample/src/vscode.proposed.chatRequestAccess.d.ts
vendored
Normal file
99
chat-agent-sample/src/vscode.proposed.chatRequestAccess.d.ts
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 ChatResponseStream {
|
||||
|
||||
/**
|
||||
* The response stream.
|
||||
*/
|
||||
readonly response: AsyncIterable<string>;
|
||||
|
||||
/**
|
||||
* The variant of multiple responses. This is used to disambiguate between multiple
|
||||
* response streams when having asked for multiple response options
|
||||
*/
|
||||
readonly option: number;
|
||||
}
|
||||
|
||||
export interface ChatRequest {
|
||||
|
||||
/**
|
||||
* The overall result of the request which represents failure or success
|
||||
* but _not_ the actual response or responses
|
||||
*/
|
||||
result: Thenable<any>;
|
||||
|
||||
/**
|
||||
* The _default response_ stream. This is the stream of the first response option
|
||||
* receiving data.
|
||||
*
|
||||
* Usually there is only one response option and this stream is more convienient to use
|
||||
* than the {@link onDidStartResponseStream `onDidStartResponseStream`} event.
|
||||
*/
|
||||
response: AsyncIterable<string>;
|
||||
|
||||
/**
|
||||
* An event that fires whenever a new response option is available. The response
|
||||
* itself is a stream of the actual response.
|
||||
*
|
||||
* *Note* that the first time this event fires, the {@link ChatResponseStream.response response stream}
|
||||
* is the same as the {@link response `default response stream`}.
|
||||
*
|
||||
* *Note* that unless requested there is only one response option, so this event will only fire
|
||||
* once.
|
||||
*/
|
||||
onDidStartResponseStream: Event<ChatResponseStream>;
|
||||
|
||||
/**
|
||||
* Cancel this request.
|
||||
*/
|
||||
// TODO@API remove this? We pass a token to makeRequest call already
|
||||
cancel(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents access to using a chat provider (LLM). Access is granted and temporary, usually only valid
|
||||
* for the duration of an user interaction or specific time frame.
|
||||
*/
|
||||
export interface ChatAccess {
|
||||
|
||||
/**
|
||||
* Whether the access to chat has been revoked. This happens when the condition that allowed for
|
||||
* chat access doesn't hold anymore, e.g a user interaction has ended.
|
||||
*/
|
||||
isRevoked: boolean;
|
||||
|
||||
/**
|
||||
* Make a chat request.
|
||||
*
|
||||
* The actual response will be reported back via the `progress` callback. The promise returned by this function
|
||||
* returns a overall result which represents failure or success of the request.
|
||||
*
|
||||
* Chat can be asked for multiple response options. In that case the `progress` callback will be called multiple
|
||||
* time with different `ChatResponseStream` objects. Each object represents a different response option and the actual
|
||||
* response will be reported back via their `stream` property.
|
||||
*
|
||||
* *Note:* This will throw an error if access has been revoked.
|
||||
*
|
||||
* @param messages
|
||||
* @param options
|
||||
*/
|
||||
makeRequest(messages: ChatMessage[], options: { [name: string]: any }, token: CancellationToken): ChatRequest;
|
||||
}
|
||||
|
||||
export namespace chat {
|
||||
|
||||
/**
|
||||
* Request access to chat.
|
||||
*
|
||||
* *Note* that this function will throw an error unless an user interaction is currently active.
|
||||
*
|
||||
* @param id The id of the chat provider, e.g `copilot`
|
||||
*/
|
||||
export function requestChatAccess(id: string): Thenable<ChatAccess>;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user