Update chat sample for api changes (#961)

This commit is contained in:
Rob Lourens
2024-02-19 22:25:49 +00:00
committed by GitHub
parent 5a0cb10f5e
commit 9ba1538212
4 changed files with 153 additions and 155 deletions

View File

@ -21,7 +21,8 @@
"onStartupFinished"
],
"enabledApiProposals": [
"chatAgents2",
"chatParticipant",
"chatVariableResolver",
"languageModels"
],
"main": "./out/extension.js",

View File

@ -2,7 +2,7 @@ import * as vscode from 'vscode';
const MEOW_COMMAND_ID = 'cat.meow';
interface ICatChatAgentResult extends vscode.ChatAgentResult2 {
interface ICatChatResult extends vscode.ChatResult {
metadata: {
command: string;
}
@ -13,7 +13,7 @@ const LANGUAGE_MODEL_ID = 'copilot-gpt-4';
export function activate(context: vscode.ExtensionContext) {
// Define a Cat chat agent handler.
const handler: vscode.ChatAgentRequestHandler = async (request: vscode.ChatAgentRequest, context: vscode.ChatAgentContext, stream: vscode.ChatAgentResponseStream, token: vscode.CancellationToken): Promise<ICatChatAgentResult> => {
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.
// The GitHub Copilot Chat extension implements this provider.
@ -65,7 +65,7 @@ export function activate(context: vscode.ExtensionContext) {
// 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);
const agent = vscode.chat.createChatParticipant('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');
@ -79,15 +79,15 @@ export function activate(context: vscode.ExtensionContext) {
};
agent.followupProvider = {
provideFollowups(result: ICatChatAgentResult, token: vscode.CancellationToken) {
provideFollowups(result: ICatChatResult, token: vscode.CancellationToken) {
return [{
prompt: 'let us play',
title: vscode.l10n.t('Play with the cat')
} satisfies vscode.ChatAgentFollowup];
label: vscode.l10n.t('Play with the cat')
} satisfies vscode.ChatFollowup];
}
};
vscode.chat.registerVariable('cat_context', 'Describes the state of mind and version of the cat', {
vscode.chat.registerChatVariableResolver('cat_context', 'Describes the state of mind and version of the cat', {
resolve: (name, context, token) => {
if (name == 'cat_context') {
const mood = Math.random() > 0.5 ? 'happy' : 'grumpy';

View File

@ -5,69 +5,69 @@
declare module 'vscode' {
// TODO@API name: Turn?
export class ChatAgentRequestTurn {
export class ChatRequestTurn {
/**
* The prompt as entered by the user.
*
* Information about variables used in this request are is stored in {@link ChatAgentRequest.variables}.
* Information about variables used in this request are is stored in {@link ChatRequest.variables}.
*
* *Note* that the {@link ChatAgent2.name name} of the agent and the {@link ChatAgentCommand.name command}
* *Note* that the {@link ChatParticipant.name name} of the participant and the {@link ChatCommand.name command}
* are not part of the prompt.
*/
readonly prompt: string;
/**
* The name of the chat agent and contributing extension to which this request was directed.
* The name of the chat participant and contributing extension to which this request was directed.
*/
readonly agent: { readonly extensionId: string; readonly agent: string };
readonly participant: { readonly extensionId: string; readonly participant: string };
/**
* The name of the {@link ChatAgentCommand command} that was selected for this request.
* The name of the {@link ChatCommand command} that was selected for this request.
*/
readonly command: string | undefined;
/**
* The variables that were referenced in this message.
*/
readonly variables: ChatAgentResolvedVariable[];
readonly variables: ChatResolvedVariable[];
private constructor(prompt: string, command: string | undefined, variables: ChatAgentResolvedVariable[], agent: { extensionId: string; agent: string });
private constructor(prompt: string, command: string | undefined, variables: ChatResolvedVariable[], participant: { extensionId: string; participant: string });
}
// TODO@API name: Turn?
export class ChatAgentResponseTurn {
export class ChatResponseTurn {
/**
* The content that was received from the chat agent. Only the progress parts that represent actual content (not metadata) are represented.
* The content that was received from the chat participant. Only the progress parts that represent actual content (not metadata) are represented.
*/
readonly response: ReadonlyArray<ChatResponseTextPart | ChatResponseMarkdownPart | ChatResponseFileTreePart | ChatResponseAnchorPart | ChatResponseCommandButtonPart>;
/**
* The result that was received from the chat agent.
* The result that was received from the chat participant.
*/
readonly result: ChatAgentResult2;
readonly result: ChatResult;
/**
* The name of the chat agent and contributing extension to which this request was directed.
* The name of the chat participant and contributing extension to which this request was directed.
*/
readonly agent: { readonly extensionId: string; readonly agent: string };
readonly participant: { readonly extensionId: string; readonly participant: string };
private constructor(response: ReadonlyArray<ChatResponseTextPart | ChatResponseMarkdownPart | ChatResponseFileTreePart | ChatResponseAnchorPart | ChatResponseCommandButtonPart>, result: ChatAgentResult2, agentId: { extensionId: string; agent: string });
readonly command?: string;
private constructor(response: ReadonlyArray<ChatResponseTextPart | ChatResponseMarkdownPart | ChatResponseFileTreePart | ChatResponseAnchorPart | ChatResponseCommandButtonPart>, result: ChatResult, participant: { extensionId: string; participant: string });
}
export interface ChatAgentContext {
export interface ChatContext {
/**
* All of the chat messages so far in the current chat session.
*/
readonly history: ReadonlyArray<ChatAgentRequestTurn | ChatAgentResponseTurn>;
readonly history: ReadonlyArray<ChatRequestTurn | ChatResponseTurn>;
}
/**
* Represents an error result from a chat request.
*/
export interface ChatAgentErrorDetails {
export interface ChatErrorDetails {
/**
* An error message that is shown to the user.
*/
@ -91,11 +91,11 @@ declare module 'vscode' {
/**
* The result of a chat request.
*/
export interface ChatAgentResult2 {
export interface ChatResult {
/**
* If the request resulted in an error, this property defines the error details.
*/
errorDetails?: ChatAgentErrorDetails;
errorDetails?: ChatErrorDetails;
/**
* Arbitrary metadata for this result. Can be anything but must be JSON-stringifyable.
@ -106,7 +106,7 @@ declare module 'vscode' {
/**
* Represents the type of user feedback received.
*/
export enum ChatAgentResultFeedbackKind {
export enum ChatResultFeedbackKind {
/**
* The user marked the result as helpful.
*/
@ -121,25 +121,24 @@ declare module 'vscode' {
/**
* Represents user feedback for a result.
*/
export interface ChatAgentResult2Feedback {
export interface ChatResultFeedback {
/**
* 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.
* This instance of ChatResult has the same properties as the result returned from the participant callback, including `metadata`, but is not the same instance.
*/
readonly result: ChatAgentResult2;
readonly result: ChatResult;
/**
* The kind of feedback that was received.
*/
readonly kind: ChatAgentResultFeedbackKind;
readonly kind: ChatResultFeedbackKind;
}
export interface ChatAgentCommand {
export interface ChatCommand {
/**
* 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 commands provided by this agent.
* **Note**: The name should be unique among the commands provided by this participant.
*/
readonly name: string;
@ -155,17 +154,16 @@ declare module 'vscode' {
/**
* Whether executing the command puts the chat into a persistent mode, where the command is automatically added to the chat input for the next message.
* If this is not set, the chat input will fall back to the agent after submitting this command.
*/
readonly isSticky?: boolean;
}
export interface ChatAgentCommandProvider {
export interface ChatCommandProvider {
/**
* Returns a list of commands that its agent is capable of handling. A command
* can be selected by the user and will then be passed to the {@link ChatAgentRequestHandler handler}
* via the {@link ChatAgentRequest.command command} property.
* Returns a list of commands that its participant is capable of handling. A command
* can be selected by the user and will then be passed to the {@link ChatRequestHandler handler}
* via the {@link ChatRequest.command command} property.
*
*
* @param token A cancellation token.
@ -173,78 +171,72 @@ declare module 'vscode' {
* an empty array.
*/
// TODO@API Q: should we provide the current history or last results for extra context?
provideCommands(token: CancellationToken): ProviderResult<ChatAgentCommand[]>;
provideCommands(token: CancellationToken): ProviderResult<ChatCommand[]>;
}
/**
* A followup question suggested by the model.
*/
export interface ChatAgentFollowup {
export interface ChatFollowup {
/**
* The message to send to the chat.
*/
prompt: string;
/**
* By default, the followup goes to the same agent/command. But this property can be set to invoke a different agent.
* TODO@API do extensions need to specify the extensionID of the agent here as well?
*/
agentId?: string;
/**
* By default, the followup goes to the same agent/command. But this property can be set to invoke a different command.
*/
command?: 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.
*/
// TODO@API title vs tooltip?
title?: string;
label?: string;
/**
* By default, the followup goes to the same participant/command. But this property can be set to invoke a different participant.
* Followups can only invoke a participant that was contributed by the same extension.
*/
participant?: string;
/**
* By default, the followup goes to the same participant/command. But this property can be set to invoke a different command.
*/
command?: string;
}
/**
* 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 ChatAgentFollowupProvider {
export interface ChatFollowupProvider {
/**
*
* @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 result The same instance of the result object that was returned by the chat participant, and it can be extended with arbitrary properties if needed.
* @param token A cancellation token.
*/
provideFollowups(result: ChatAgentResult2, token: CancellationToken): ProviderResult<ChatAgentFollowup[]>;
provideFollowups(result: ChatResult, token: CancellationToken): ProviderResult<ChatFollowup[]>;
}
/**
* A chat request handler is a callback that will be invoked when a request is made to a chat agent.
* A chat request handler is a callback that will be invoked when a request is made to a chat participant.
*/
export type ChatAgentRequestHandler = (request: ChatAgentRequest, context: ChatAgentContext, response: ChatAgentResponseStream, token: CancellationToken) => ProviderResult<ChatAgentResult2>;
export type ChatRequestHandler = (request: ChatRequest, context: ChatContext, response: ChatResponseStream, token: CancellationToken) => ProviderResult<ChatResult>;
export interface ChatAgent2 {
export interface ChatParticipant {
/**
* The short name by which this agent is referred to in the UI, e.g `workspace`.
* The short name by which this participant is referred to in the UI, e.g `workspace`.
*/
readonly name: string;
/**
* The full name of this agent.
* The full name of this participant.
*/
fullName: string;
/**
* A human-readable description explaining what this agent does.
* A human-readable description explaining what this participant does.
*/
description: string;
/**
* Icon for the agent shown in UI.
* Icon for the participant shown in UI.
*/
iconPath?: Uri | {
/**
@ -258,27 +250,27 @@ declare module 'vscode' {
} | ThemeIcon;
/**
* The handler for requests to this agent.
* The handler for requests to this participant.
*/
requestHandler: ChatAgentRequestHandler;
requestHandler: ChatRequestHandler;
/**
* This provider will be called to retrieve the agent's commands.
* This provider will be called to retrieve the participant's commands.
*/
commandProvider?: ChatAgentCommandProvider;
commandProvider?: ChatCommandProvider;
/**
* This provider will be called once after each request to retrieve suggested followup questions.
*/
followupProvider?: ChatAgentFollowupProvider;
followupProvider?: ChatFollowupProvider;
/**
* When the user clicks this agent in `/help`, this text will be submitted to this command
* When the user clicks this participant in `/help`, this text will be submitted to this command
*/
sampleRequest?: string;
/**
* Whether invoking the agent puts the chat into a persistent mode, where the agent is automatically added to the chat input for the next message.
* Whether invoking the participant puts the chat into a persistent mode, where the participant is automatically added to the chat input for the next message.
*/
isSticky?: boolean;
@ -286,13 +278,13 @@ declare module 'vscode' {
* 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.
* The passed {@link ChatResultFeedback.result result} is guaranteed to be the same instance that was
* previously returned from this chat participant.
*/
onDidReceiveFeedback: Event<ChatAgentResult2Feedback>;
onDidReceiveFeedback: Event<ChatResultFeedback>;
/**
* Dispose this agent and free resources
* Dispose this participant and free resources
*/
dispose(): void;
}
@ -300,7 +292,7 @@ declare module 'vscode' {
/**
* A resolved variable value is a name-value pair as well as the range in the prompt where a variable was used.
*/
export interface ChatAgentResolvedVariable {
export interface ChatResolvedVariable {
/**
* The name of the variable.
@ -311,7 +303,7 @@ declare module 'vscode' {
readonly name: string;
/**
* The start and end index of the variable in the {@link ChatAgentRequest.prompt prompt}.
* The start and end index of the variable in the {@link ChatRequest.prompt prompt}.
*
* *Note* that the indices take the leading `#`-character into account which means they can
* used to modify the prompt as-is.
@ -322,59 +314,47 @@ declare module 'vscode' {
readonly values: ChatVariableValue[];
}
export interface ChatAgentRequest {
export interface ChatRequest {
/**
* The prompt as entered by the user.
*
* Information about variables used in this request are is stored in {@link ChatAgentRequest.variables}.
* Information about variables used in this request are is stored in {@link ChatRequest.variables}.
*
* *Note* that the {@link ChatAgent2.name name} of the agent and the {@link ChatAgentCommand.name command}
* *Note* that the {@link ChatParticipant.name name} of the participant and the {@link ChatCommand.name command}
* are not part of the prompt.
*/
readonly prompt: string;
/**
* The name of the {@link ChatAgentCommand command} that was selected for this request.
* The name of the {@link ChatCommand command} that was selected for this request.
*/
readonly command: string | undefined;
/**
* The list of variables and their values that are referenced in the prompt.
*
* *Note* that the prompt contains varibale references as authored and that it is up to the agent
* *Note* that the prompt contains varibale references as authored and that it is up to the participant
* to further modify the prompt, for instance by inlining variable values or creating links to
* headings which contain the resolved values. vvariables are sorted in reverse by their range
* in the prompt. That means the last variable in the prompt is the first in this list. This simplifies
* string-manipulation of the prompt.
*/
// TODO@API Q? are there implicit variables that are not part of the prompt?
readonly variables: readonly ChatAgentResolvedVariable[];
readonly variables: readonly ChatResolvedVariable[];
}
export interface ChatAgentResponseStream {
/**
* Push a text part to this stream. Short-hand for
* `push(new ChatResponseTextPart(value))`.
*
* @see {@link ChatAgentResponseStream.push}
* @param value A plain text value.
* @returns This stream.
*/
// TODO@API remove?
text(value: string): ChatAgentResponseStream;
export interface ChatResponseStream {
/**
* Push a markdown part to this stream. Short-hand for
* `push(new ChatResponseMarkdownPart(value))`.
*
* @see {@link ChatAgentResponseStream.push}
* @see {@link ChatResponseStream.push}
* @param value A markdown string or a string that should be interpreted as markdown.
* @returns This stream.
*/
// TODO@API NAME: content
markdown(value: string | MarkdownString): ChatAgentResponseStream;
markdown(value: string | MarkdownString): ChatResponseStream;
/**
* Push an anchor part to this stream. Short-hand for
@ -384,7 +364,7 @@ declare module 'vscode' {
* @param title An optional title that is rendered with value
* @returns This stream.
*/
anchor(value: Uri | Location, title?: string): ChatAgentResponseStream;
anchor(value: Uri | Location, title?: string): ChatResponseStream;
/**
* Push a command button part to this stream. Short-hand for
@ -393,7 +373,7 @@ declare module 'vscode' {
* @param command A Command that will be executed when the button is clicked.
* @returns This stream.
*/
button(command: Command): ChatAgentResponseStream;
button(command: Command): ChatResponseStream;
/**
* Push a filetree part to this stream. Short-hand for
@ -403,7 +383,7 @@ declare module 'vscode' {
* @param baseUri The base uri to which this file tree is relative to.
* @returns This stream.
*/
filetree(value: ChatResponseFileTree[], baseUri: Uri): ChatAgentResponseStream;
filetree(value: ChatResponseFileTree[], baseUri: Uri): ChatResponseStream;
/**
* Push a progress part to this stream. Short-hand for
@ -412,10 +392,7 @@ declare module 'vscode' {
* @param value
* @returns This stream.
*/
// TODO@API is this always inline or not
// TODO@API is this markdown or string?
// TODO@API this influences the rendering, it inserts new lines which is likely a bug
progress(value: string): ChatAgentResponseStream;
progress(value: string): ChatResponseStream;
/**
* Push a reference to this stream. Short-hand for
@ -426,20 +403,16 @@ declare module 'vscode' {
* @param value A uri or location
* @returns This stream.
*/
// TODO@API support non-file uris, like http://example.com
// TODO@API support mapped edits
reference(value: Uri | Location): ChatAgentResponseStream;
reference(value: Uri | Location): ChatResponseStream;
/**
* Pushes a part to this stream.
*
* @param part A response part, rendered or metadata
*/
push(part: ChatResponsePart): ChatAgentResponseStream;
push(part: ChatResponsePart): ChatResponseStream;
}
// TODO@API should the name suffix differentiate between rendered items (XYZPart)
// and metadata like XYZItem
export class ChatResponseTextPart {
value: string;
constructor(value: string);
@ -469,7 +442,6 @@ declare module 'vscode' {
export class ChatResponseProgressPart {
value: string;
// TODO@API inline
constructor(value: string);
}
@ -493,29 +465,19 @@ declare module 'vscode' {
export namespace chat {
/**
* Create a new {@link ChatAgent2 chat agent} instance.
* Create a new {@link ChatParticipant chat participant} instance.
*
* @param name Short name by which the agent is referred to in the UI. The name must be unique for the extension
* contributing the agent but can collide with names from other extensions.
* @param handler A request handler for the agent.
* @returns A new chat agent
* @param name Short name by which the participant is referred to in the UI. The name must be unique for the extension
* contributing the participant but can collide with names from other extensions.
* @param handler A request handler for the participant.
* @returns A new chat participant
*/
export function createChatAgent(name: string, handler: ChatAgentRequestHandler): ChatAgent2;
/**
* Register a variable which can be used in a chat request to any agent.
* @param name The name of the variable, to be used in the chat input as `#name`.
* @param description A description of the variable for the chat input suggest widget.
* @param resolver Will be called to provide the chat variable's value when it is used.
*/
// TODO@API NAME: registerChatVariable, registerChatVariableResolver
export function registerVariable(name: string, description: string, resolver: ChatVariableResolver): Disposable;
export function createChatParticipant(name: string, handler: ChatRequestHandler): ChatParticipant;
}
/**
* The detail level of this chat variable value.
*/
// TODO@API maybe for round2
export enum ChatVariableLevel {
Short = 1,
Medium = 2,
@ -529,7 +491,7 @@ declare module 'vscode' {
level: ChatVariableLevel;
/**
* The variable's value, which can be included in an LLM prompt as-is, or the chat agent may decide to read the value and do something else with it.
* The variable's value, which can be included in an LLM prompt as-is, or the chat participant may decide to read the value and do something else with it.
*/
value: string | Uri;
@ -538,21 +500,4 @@ declare module 'vscode' {
*/
description?: string;
}
export interface ChatVariableContext {
/**
* The message entered by the user, which includes this variable.
*/
prompt: string;
}
export interface ChatVariableResolver {
/**
* A callback to resolve the value of a chat variable.
* @param name The name of the variable.
* @param context Contextual information about this chat request.
* @param token A cancellation token.
*/
resolve(name: string, context: ChatVariableContext, token: CancellationToken): ProviderResult<ChatVariableValue[]>;
}
}

View File

@ -0,0 +1,52 @@
/*---------------------------------------------------------------------------------------------
* 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 namespace chat {
/**
* Register a variable which can be used in a chat request to any participant.
* @param name The name of the variable, to be used in the chat input as `#name`.
* @param description A description of the variable for the chat input suggest widget.
* @param resolver Will be called to provide the chat variable's value when it is used.
*/
export function registerChatVariableResolver(name: string, description: string, resolver: ChatVariableResolver): Disposable;
}
export interface ChatVariableValue {
/**
* The detail level of this chat variable value. If possible, variable resolvers should try to offer shorter values that will consume fewer tokens in an LLM prompt.
*/
level: ChatVariableLevel;
/**
* The variable's value, which can be included in an LLM prompt as-is, or the chat participant may decide to read the value and do something else with it.
*/
value: string | Uri;
/**
* A description of this value, which could be provided to the LLM as a hint.
*/
description?: string;
}
export interface ChatVariableContext {
/**
* The message entered by the user, which includes this variable.
*/
prompt: string;
}
export interface ChatVariableResolver {
/**
* A callback to resolve the value of a chat variable.
* @param name The name of the variable.
* @param context Contextual information about this chat request.
* @param token A cancellation token.
*/
resolve(name: string, context: ChatVariableContext, token: CancellationToken): ProviderResult<ChatVariableValue[]>;
}
}