From 2ac11b37d27ba16d75d6e64286a827f99d1369ce Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 13 Oct 2023 14:47:14 -0700 Subject: [PATCH] Add 'default agent' concept (#195599) So that every request can be routed through an agent --- src/vs/workbench/api/common/extHostChatAgents2.ts | 9 +++++++++ .../chat/browser/contrib/chatInputEditorContrib.ts | 3 ++- src/vs/workbench/contrib/chat/common/chatAgents.ts | 8 +++++++- .../contrib/chat/common/chatServiceImpl.ts | 12 +++++++----- .../extensions/common/extensionsApiProposals.ts | 1 + .../vscode.proposed.defaultChatAgent.d.ts | 14 ++++++++++++++ 6 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.defaultChatAgent.d.ts diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 13a6b7d6b85..f765884967e 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -185,6 +185,7 @@ class ExtHostChatAgent { private _description: string | undefined; private _fullName: string | undefined; private _iconPath: URI | undefined; + private _isDefault: boolean | undefined; private _onDidReceiveFeedback = new Emitter(); private _onDidPerformAction = new Emitter(); @@ -258,6 +259,7 @@ class ExtHostChatAgent { icon: this._iconPath, hasSlashCommands: this._slashCommandProvider !== undefined, hasFollowup: this._followupProvider !== undefined, + isDefault: this._isDefault }); updateScheduled = false; }); @@ -304,6 +306,13 @@ class ExtHostChatAgent { that._followupProvider = v; updateMetadataSoon(); }, + get isDefault() { + return that._isDefault; + }, + set isDefault(v) { + that._isDefault = v; + updateMetadataSoon(); + }, get onDidReceiveFeedback() { return that._onDidReceiveFeedback.event; }, diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 3c015b89c42..bb7a7b77e79 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -329,7 +329,8 @@ class AgentCompletions extends Disposable { return null; } - const agents = this.chatAgentService.getAgents(); + const agents = this.chatAgentService.getAgents() + .filter(a => !a.metadata.isDefault); return { suggestions: agents.map((c, i) => { const withAt = `@${c.id}`; diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index 525137f000e..886c5aa60a7 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -5,6 +5,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; +import { Iterable } from 'vs/base/common/iterator'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -36,7 +37,7 @@ export interface IChatAgentMetadata { description?: string; // subCommands: IChatAgentCommand[]; requireCommand?: boolean; // Do some agents not have a default action? - isImplicit?: boolean; // Only @workspace. slash commands get promoted to the top-level and this agent is invoked when those are used + isDefault?: boolean; // The agent invoked when no agent is specified fullName?: string; icon?: URI; } @@ -69,6 +70,7 @@ export interface IChatAgentService { getFollowups(id: string, sessionId: string, token: CancellationToken): Promise; getAgents(): Array; getAgent(id: string): IChatAgent | undefined; + getDefaultAgent(): IChatAgent | undefined; hasAgent(id: string): boolean; updateAgent(id: string, updateMetadata: IChatAgentMetadata): void; } @@ -112,6 +114,10 @@ export class ChatAgentService extends Disposable implements IChatAgentService { this._onDidChangeAgents.fire(); } + getDefaultAgent(): IChatAgent | undefined { + return Iterable.find(this._agents.values(), a => !!a.agent.metadata.isDefault)?.agent; + } + getAgents(): Array { return Array.from(this._agents.values(), v => v.agent); } diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index eb3b8680044..92ae2ff6307 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -507,7 +507,9 @@ export class ChatService extends Disposable implements IChatService { let rawResponse: IChatResponse | null | undefined; let agentOrCommandFollowups: Promise | undefined = undefined; - if (typeof message === 'string' && agentPart) { + const defaultAgent = this.chatAgentService.getDefaultAgent(); + if (typeof message === 'string' && (agentPart || defaultAgent)) { + const agent = (agentPart?.agent ?? defaultAgent)!; const history: IChatMessage[] = []; for (const request of model.getRequests()) { if (!request.response) { @@ -518,11 +520,11 @@ export class ChatService extends Disposable implements IChatService { history.push({ role: ChatMessageRole.Assistant, content: request.response.response.asString() }); } - request = model.addRequest(parsedRequest, agentPart.agent); + request = model.addRequest(parsedRequest, agent); const requestProps: IChatAgentRequest = { sessionId, requestId: generateUuid(), - message: message, + message, variables: {}, command: agentSlashCommandPart?.command.name ?? '', }; @@ -532,7 +534,7 @@ export class ChatService extends Disposable implements IChatService { requestProps.message = varResult.prompt; } - const agentResult = await this.chatAgentService.invokeAgent(agentPart.agent.id, requestProps, new Progress(p => { + const agentResult = await this.chatAgentService.invokeAgent(agent.id, requestProps, new Progress(p => { progressCallback(p); }), history, token); rawResponse = { @@ -541,7 +543,7 @@ export class ChatService extends Disposable implements IChatService { timings: agentResult.timings }; agentOrCommandFollowups = agentResult?.followUp ? Promise.resolve(agentResult.followUp) : - this.chatAgentService.getFollowups(agentPart.agent.id, sessionId, CancellationToken.None); + this.chatAgentService.getFollowups(agent.id, sessionId, CancellationToken.None); } else if (commandPart && typeof message === 'string' && this.chatSlashCommandService.hasCommand(commandPart.slashCommand.command)) { request = model.addRequest(parsedRequest); // contributed slash commands diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 3ebf8b558b3..c5e30e4de43 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -38,6 +38,7 @@ export const allApiProposals = Object.freeze({ createFileSystemWatcher: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.createFileSystemWatcher.d.ts', customEditorMove: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.customEditorMove.d.ts', debugFocus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.debugFocus.d.ts', + defaultChatAgent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.defaultChatAgent.d.ts', diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', diffContentOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffContentOptions.d.ts', documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts', diff --git a/src/vscode-dts/vscode.proposed.defaultChatAgent.d.ts b/src/vscode-dts/vscode.proposed.defaultChatAgent.d.ts new file mode 100644 index 00000000000..573b0668744 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.defaultChatAgent.d.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * 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 ChatAgent2 { + /** + * When true, this agent is invoked by default when no other agent is being invoked + */ + isDefault?: boolean; + } +}