diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 2757392e5f5..2f1aac77d56 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1421,7 +1421,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'mappedEditsProvider'); return extHostLanguageFeatures.registerMappedEditsProvider(extension, selector, provider); }, - createChatAgent(name: string, handler: vscode.ChatAgentExtendedHandler) { + createChatAgent(name: string, handler: vscode.ChatAgentExtendedRequestHandler) { checkProposedApiEnabled(extension, 'chatAgents2'); return extHostChatAgents2.createChatAgent(extension, name, handler); }, @@ -1460,8 +1460,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I Breakpoint: extHostTypes.Breakpoint, TerminalOutputAnchor: extHostTypes.TerminalOutputAnchor, ChatAgentResultFeedbackKind: extHostTypes.ChatAgentResultFeedbackKind, - ChatMessage: extHostTypes.ChatMessage, - ChatMessageRole: extHostTypes.ChatMessageRole, ChatVariableLevel: extHostTypes.ChatVariableLevel, ChatAgentCompletionItem: extHostTypes.ChatAgentCompletionItem, CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall, diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index efaff6f31c9..61f74812130 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -11,6 +11,7 @@ import { Emitter } from 'vs/base/common/event'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { DisposableMap, DisposableStore } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; +import { assertType } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; @@ -169,7 +170,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents2); } - createChatAgent(extension: IExtensionDescription, name: string, handler: vscode.ChatAgentExtendedHandler): vscode.ChatAgent2 { + createChatAgent(extension: IExtensionDescription, name: string, handler: vscode.ChatAgentExtendedRequestHandler): vscode.ChatAgent2 { const handle = ExtHostChatAgents2._idPool++; const agent = new ExtHostChatAgent(extension, name, this._proxy, handle, handler); this._agents.set(handle, agent); @@ -358,7 +359,7 @@ class ExtHostChatAgent { public readonly id: string, private readonly _proxy: MainThreadChatAgentsShape2, private readonly _handle: number, - private readonly _callback: vscode.ChatAgentExtendedHandler, + private _requestHandler: vscode.ChatAgentExtendedRequestHandler, ) { } acceptFeedback(feedback: vscode.ChatAgentResult2Feedback) { @@ -507,6 +508,13 @@ class ExtHostChatAgent { that._iconPath = v; updateMetadataSoon(); }, + get requestHandler() { + return that._requestHandler; + }, + set requestHandler(v) { + assertType(typeof v === 'function', 'Invalid request handler'); + that._requestHandler = v; + }, get commandProvider() { return that._commandProvider; }, @@ -585,6 +593,7 @@ class ExtHostChatAgent { return that._onDidReceiveFeedback.event; }, set agentVariableProvider(v) { + checkProposedApiEnabled(that.extension, 'chatAgents2Additions'); that._agentVariableProvider = v; if (v) { if (!v.triggerCharacters.length) { @@ -597,13 +606,16 @@ class ExtHostChatAgent { } }, get agentVariableProvider() { + checkProposedApiEnabled(that.extension, 'chatAgents2Additions'); return that._agentVariableProvider; }, set welcomeMessageProvider(v) { + checkProposedApiEnabled(that.extension, 'defaultChatAgent'); that._welcomeMessageProvider = v; updateMetadataSoon(); }, get welcomeMessageProvider() { + checkProposedApiEnabled(that.extension, 'defaultChatAgent'); return that._welcomeMessageProvider; }, onDidPerformAction: !isProposedApiEnabled(this.extension, 'chatAgents2Additions') @@ -621,6 +633,6 @@ class ExtHostChatAgent { } invoke(request: vscode.ChatAgentRequest, context: vscode.ChatAgentContext, response: vscode.ChatAgentExtendedResponseStream, token: CancellationToken): vscode.ProviderResult { - return this._callback(request, context, response, token); + return this._requestHandler(request, context, response, token); } } diff --git a/src/vs/workbench/api/common/extHostChatProvider.ts b/src/vs/workbench/api/common/extHostChatProvider.ts index ee4d7fe863f..5e725927e9b 100644 --- a/src/vs/workbench/api/common/extHostChatProvider.ts +++ b/src/vs/workbench/api/common/extHostChatProvider.ts @@ -149,13 +149,7 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape { this._proxy.$handleProgressChunk(requestId, { index: fragment.index, part: fragment.part }); }); - if (data.provider.provideLanguageModelResponse2) { - return data.provider.provideLanguageModelResponse2(messages.map(typeConvert.LanguageModelMessage.to), options, ExtensionIdentifier.toKey(from), progress, token); - } else { - // TODO@jrieken remove - return data.provider.provideLanguageModelResponse(messages.map(typeConvert.ChatMessage.to), options, ExtensionIdentifier.toKey(from), progress, token); - } - + return data.provider.provideLanguageModelResponse2(messages.map(typeConvert.LanguageModelMessage.to), options, ExtensionIdentifier.toKey(from), progress, token); } //#region --- making request diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 4c403c04398..ae82d84eae3 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2239,12 +2239,6 @@ export namespace ChatInlineFollowup { } } -export namespace ChatMessage { - export function to(message: chatProvider.IChatMessage): vscode.ChatMessage { - return new types.ChatMessage(ChatMessageRole.to(message.role), message.content); - } -} - export namespace LanguageModelMessage { export function to(message: chatProvider.IChatMessage): vscode.LanguageModelMessage { @@ -2268,28 +2262,6 @@ export namespace LanguageModelMessage { } } - -export namespace ChatMessageRole { - - export function to(role: chatProvider.ChatMessageRole): vscode.ChatMessageRole { - switch (role) { - case chatProvider.ChatMessageRole.System: return types.ChatMessageRole.System; - case chatProvider.ChatMessageRole.User: return types.ChatMessageRole.User; - case chatProvider.ChatMessageRole.Assistant: return types.ChatMessageRole.Assistant; - } - } - - export function from(role: vscode.ChatMessageRole): chatProvider.ChatMessageRole { - switch (role) { - case types.ChatMessageRole.System: return chatProvider.ChatMessageRole.System; - case types.ChatMessageRole.Assistant: return chatProvider.ChatMessageRole.Assistant; - case types.ChatMessageRole.User: - default: - return chatProvider.ChatMessageRole.User; - } - } -} - export namespace ChatVariable { export function objectTo(variableObject: Record): Record { const result: Record = {}; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index d4d61dfa28b..41b31a4901d 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4196,24 +4196,6 @@ export enum InteractiveEditorResponseFeedbackKind { Bug = 4 } -export enum ChatMessageRole { - System = 0, - User = 1, - Assistant = 2, -} - -export class ChatMessage implements vscode.ChatMessage { - - role: ChatMessageRole; - content: string; - name?: string; - - constructor(role: ChatMessageRole, content: string) { - this.role = role; - this.content = content; - } -} - export enum ChatAgentResultFeedbackKind { Unhelpful = 0, Helpful = 1, diff --git a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts index 8aa7e9c184f..ed55c5ffbf2 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts @@ -5,26 +5,6 @@ declare module 'vscode' { - /** - * One request/response pair in chat history. - */ - export interface ChatAgentHistoryEntry { - /** - * The request that was sent to the chat agent. - */ - request: ChatAgentRequest; - - /** - * The content that was received from the chat agent. Only the progress parts that represent actual content (not metadata) are represented. - */ - response: ReadonlyArray; - - /** - * The result that was received from the chat agent. - */ - result: ChatAgentResult2; - } - // TODO@API name: Turn? export class ChatAgentRequestTurn { @@ -38,15 +18,10 @@ declare module 'vscode' { */ readonly prompt: string; - // TODO@API NAME agent - // TODO@API TYPE {agent:string, extension:string} - /** @deprecated */ - readonly agentId: string; - /** * The ID of the chat agent to which this request was directed. */ - readonly agent: { extensionId: string; agentId: string }; + readonly agent: { readonly extensionId: string; readonly agentId: string }; /** * The name of the {@link ChatAgentCommand command} that was selected for this request. @@ -74,10 +49,7 @@ declare module 'vscode' { */ readonly result: ChatAgentResult2; - /** @deprecated */ - readonly agentId: string; - - readonly agent: { extensionId: string; agentId: string }; + readonly agent: { readonly extensionId: string; readonly agentId: string }; private constructor(response: ReadonlyArray, result: ChatAgentResult2, agentId: { extensionId: string; agentId: string }); } @@ -189,7 +161,7 @@ declare module 'vscode' { /** * 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 ChatAgentHandler handler} + * can be selected by the user and will then be passed to the {@link ChatAgentRequestHandler handler} * via the {@link ChatAgentRequest.command command} property. * * @@ -197,6 +169,7 @@ declare module 'vscode' { * @returns A list of commands. The lack of a result can be signaled by returning `undefined`, `null`, or * an empty array. */ + // TODO@API Q: should we provide the current history or last results for extra context? provideCommands(token: CancellationToken): ProviderResult; } @@ -228,6 +201,7 @@ declare module 'vscode' { /** * A title to show the user, when it is different than the message. */ + // TODO@API title vs tooltip? title?: string; } @@ -243,6 +217,12 @@ declare module 'vscode' { provideFollowups(result: ChatAgentResult2, token: CancellationToken): ProviderResult; } + /** + * A chat request handler is a callback that will be invoked when a request is made to a chat agent. + */ + export type ChatAgentRequestHandler = (request: ChatAgentRequest, context: ChatAgentContext, response: ChatAgentResponseStream, token: CancellationToken) => ProviderResult; + + export interface ChatAgent2 { /** @@ -274,6 +254,11 @@ declare module 'vscode' { dark: Uri; } | ThemeIcon; + /** + * The handler for requests to this agent. + */ + requestHandler: ChatAgentRequestHandler; + /** * This provider will be called to retrieve the agent's commands. */ @@ -284,16 +269,6 @@ declare module 'vscode' { */ followupProvider?: ChatAgentFollowupProvider; - - // TODO@API - // notify(request: ChatResponsePart, reference: string): boolean; - // BETTER - // requestResponseStream(result: ChatAgentResult, callback: (stream: ChatAgentResponseStream) => void, why?: string): void; - - // TODO@API - // clear NEVER happens - // onDidClearResult(value: TResult): void; - /** * When the user clicks this agent in `/help`, this text will be submitted to this command */ @@ -315,9 +290,9 @@ declare module 'vscode' { } export interface ChatAgentResolvedVariable { - name: string; - range: [start: number, end: number]; - values: ChatVariableValue[]; + readonly name: string; + readonly range: [start: number, end: number]; + readonly values: ChatVariableValue[]; } export interface ChatAgentRequest { @@ -330,17 +305,17 @@ declare module 'vscode' { * *Note* that the {@link ChatAgent2.name name} of the agent and the {@link ChatAgentCommand.name command} * are not part of the prompt. */ - prompt: string; + readonly prompt: string; /** * The name of the {@link ChatAgentCommand command} that was selected for this request. */ - command?: string; + readonly command: string | undefined; /** * The list of variables that are referenced in the prompt. */ - variables: ChatAgentResolvedVariable[]; + readonly variables: readonly ChatAgentResolvedVariable[]; } export interface ChatAgentResponseStream { @@ -429,11 +404,6 @@ declare module 'vscode' { push(part: ChatResponsePart): ChatAgentResponseStream; } - // TODO@API - // support ChatResponseCommandPart - // support ChatResponseTextEditPart - // support ChatResponseCodeReferencePart - // TODO@API should the name suffix differentiate between rendered items (XYZPart) // and metadata like XYZItem export class ChatResponseTextPart { @@ -482,9 +452,6 @@ declare module 'vscode' { | ChatResponseProgressPart | ChatResponseReferencePart | ChatResponseCommandButtonPart; - // TODO@API Remove a different type of `request` so that they can - // evolve at their own pace - export type ChatAgentHandler = (request: ChatAgentRequest, context: ChatAgentContext, response: ChatAgentResponseStream, token: CancellationToken) => ProviderResult; export namespace chat { @@ -495,7 +462,7 @@ declare module 'vscode' { * @param handler The reply-handler of the agent. * @returns A new chat agent */ - export function createChatAgent(name: string, handler: ChatAgentHandler): ChatAgent2; + export function createChatAgent(name: string, handler: ChatAgentRequestHandler): ChatAgent2; /** * Register a variable which can be used in a chat request to any agent. diff --git a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts index b934eeea995..32eb4999065 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts @@ -158,13 +158,13 @@ declare module 'vscode' { constructor(label: string | CompletionItemLabel, values: ChatVariableValue[]); } - export type ChatAgentExtendedHandler = (request: ChatAgentRequest, context: ChatAgentContext, response: ChatAgentExtendedResponseStream, token: CancellationToken) => ProviderResult; + export type ChatAgentExtendedRequestHandler = (request: ChatAgentRequest, context: ChatAgentContext, response: ChatAgentExtendedResponseStream, token: CancellationToken) => ProviderResult; export namespace chat { /** * Create a chat agent with the extended progress type */ - export function createChatAgent(name: string, handler: ChatAgentExtendedHandler): ChatAgent2; + export function createChatAgent(name: string, handler: ChatAgentExtendedRequestHandler): ChatAgent2; } /* diff --git a/src/vscode-dts/vscode.proposed.chatProvider.d.ts b/src/vscode-dts/vscode.proposed.chatProvider.d.ts index 7ac3ddba1b9..7a9e140b5a5 100644 --- a/src/vscode-dts/vscode.proposed.chatProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.chatProvider.d.ts @@ -5,27 +5,6 @@ declare module 'vscode' { - // TODO@API NAME: ChatMessageKind? - export enum ChatMessageRole { - System = 0, - User = 1, - // TODO@API name: align with ChatAgent (or whatever we'll rename that to) - Assistant = 2, - } - - /** - * A chat message that is used to make chat request against a language model. - */ - export class ChatMessage { - - readonly role: ChatMessageRole; - - readonly content: string; - - constructor(role: ChatMessageRole, content: string); - } - - export interface ChatResponseFragment { index: number; part: string; @@ -37,8 +16,7 @@ declare module 'vscode' { * Represents a large language model that accepts ChatML messages and produces a streaming response */ export interface ChatResponseProvider { - provideLanguageModelResponse(messages: ChatMessage[], options: { [name: string]: any }, extensionId: string, progress: Progress, token: CancellationToken): Thenable; - provideLanguageModelResponse2?(messages: LanguageModelMessage[], options: { [name: string]: any }, extensionId: string, progress: Progress, token: CancellationToken): Thenable; + provideLanguageModelResponse2(messages: LanguageModelMessage[], options: { [name: string]: any }, extensionId: string, progress: Progress, token: CancellationToken): Thenable; } export interface ChatResponseProviderMetadata { diff --git a/src/vscode-dts/vscode.proposed.chatRequestAccess.d.ts b/src/vscode-dts/vscode.proposed.chatRequestAccess.d.ts index fdd1cb7f5ef..633c28dc61e 100644 --- a/src/vscode-dts/vscode.proposed.chatRequestAccess.d.ts +++ b/src/vscode-dts/vscode.proposed.chatRequestAccess.d.ts @@ -5,35 +5,91 @@ declare module 'vscode' { + /** + * Represents a language model response. + * + * @see {@link LanguageModelAccess.makeChatRequest} + */ export interface LanguageModelResponse { /** * The overall result of the request which represents failure or success - * but _not_ the actual response or responses + * but. The concrete value is not specified and depends on the selected language model. + * + * *Note* that the actual response represented by the {@link LanguageModelResponse.stream `stream`}-property */ - // TODO@API define this type! result: Thenable; - // TODO@API doc what to expect here + /** + * An async iterable that is a stream of text chunks forming the overall response. + */ stream: AsyncIterable; } - //TODO@API see https://learn.microsoft.com/en-us/dotnet/api/azure.ai.openai.chatrequestmessage?view=azure-dotnet-preview - // this allows to grow message by type, e.g add more content types to User message to support multimodal language models - + /** + * A language model message that represents a system message. + * + * System messages provide instructions to the language model that define the context in + * which user messages are interpreted. + * + * *Note* that a language model may choose to add additional system messages to the ones + * provided by extensions. + */ export class LanguageModelSystemMessage { + + /** + * The content of this message. + */ content: string; + + /** + * Create a new system message. + * + * @param content The content of the message. + */ constructor(content: string); } + /** + * A language model message that represents a user message. + */ export class LanguageModelUserMessage { + + /** + * The content of this message. + */ content: string; + + /** + * The optional name of a user for this message. + */ name: string | undefined; + + /** + * Create a new user message. + * + * @param content The content of the message. + * @param name The optional name of a user for the message. + */ constructor(content: string, name?: string); } + /** + * A language model message that represents an assistant message, usually in response to a user message + * or as a sample response/reply-pair. + */ export class LanguageModelAssistantMessage { + + /** + * The content of this message. + */ content: string; + + /** + * Create a new assistant message. + * + * @param content The content of the message. + */ constructor(content: string); }