Show dynamically registered participants in suggest widget (#206989)

* Show dynamically registered participants in suggest widget

* Fix
This commit is contained in:
Rob Lourens 2024-03-06 17:07:53 -03:00 committed by GitHub
parent a381c88fd3
commit 586b9561d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 38 additions and 22 deletions

View file

@ -88,7 +88,7 @@ const chatParticipantExtensionPoint = extensionsRegistry.ExtensionsRegistry.regi
additionalProperties: false,
type: 'object',
defaultSnippets: [{ body: { name: '', description: '' } }],
required: ['name', 'description'],
required: ['name'],
properties: {
name: {
description: localize('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. The name should be unique among the commands provided by this participant."),

View file

@ -344,7 +344,7 @@ class AgentCompletions extends Disposable {
return null;
}
const agents = this.chatAgentService.getRegisteredAgents()
const agents = this.chatAgentService.getAgents()
.filter(a => !a.isDefault);
return <CompletionList>{
suggestions: agents.map((c, i) => {
@ -427,7 +427,7 @@ class AgentCompletions extends Disposable {
return null;
}
const agents = this.chatAgentService.getRegisteredAgents();
const agents = this.chatAgentService.getAgents();
const justAgents: CompletionItem[] = agents
.filter(a => !a.isDefault)
.map(agent => {
@ -452,7 +452,7 @@ class AgentCompletions extends Disposable {
filterText: `${chatSubcommandLeader}${agent.id}${c.name}`,
commitCharacters: [' '],
insertText: `${agentLabel} ${withSlash} `,
detail: `(${agentLabel}) ${c.description}`,
detail: `(${agentLabel}) ${c.description ?? ''}`,
range: new Range(1, 1, 1, 1),
kind: CompletionItemKind.Text, // The icons are disabled here anyway
sortText: `${chatSubcommandLeader}${agent.id}${c.name}`,

View file

@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { distinct } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { IMarkdownString } from 'vs/base/common/htmlContent';
@ -129,9 +130,10 @@ export interface IChatAgentService {
registerDynamicAgent(data: IChatAgentData, agentImpl: IChatAgentImplementation): IDisposable;
invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult>;
getFollowups(id: string, request: IChatAgentRequest, result: IChatAgentResult, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatFollowup[]>;
getAgents(): IChatAgentData[];
getRegisteredAgents(): Array<IChatAgentData>;
getActivatedAgents(): Array<IChatAgent>;
getRegisteredAgent(id: string): IChatAgentData | undefined;
getAgent(id: string): IChatAgentData | undefined;
getDefaultAgent(): IChatAgent | undefined;
getSecondaryAgent(): IChatAgentData | undefined;
updateAgent(id: string, updateMetadata: IChatAgentMetadata): void;
@ -166,7 +168,7 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
throw new Error(`Already registered an agent with id ${name}`);
}
const data = this.getRegisteredAgent(name);
const data = this.getAgent(name);
if (!data) {
throw new Error(`Unknown agent: ${name}`);
}
@ -227,14 +229,28 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
} satisfies IChatAgentData));
}
/**
* Returns all agent datas that exist- static registered and dynamic ones.
*/
getAgents(): IChatAgentData[] {
const registeredAgents = this.getRegisteredAgents();
const dynamicAgents = Array.from(this._agents.values()).map(a => a.data);
const all = [
...registeredAgents,
...dynamicAgents
];
return distinct(all, a => a.id);
}
getActivatedAgents(): IChatAgent[] {
return Array.from(this._agents.values())
.filter(a => !!a.impl)
.map(a => new MergedChatAgent(a.data, a.impl!));
}
getRegisteredAgent(id: string): IChatAgentData | undefined {
return this.getRegisteredAgents().find(a => a.id === id);
getAgent(id: string): IChatAgentData | undefined {
return this.getAgents().find(a => a.id === id);
}
async invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult> {

View file

@ -693,7 +693,7 @@ export class ChatModel extends Disposable implements IChatModel {
} else if (progress.kind === 'usedContext' || progress.kind === 'reference') {
request.response.applyReference(progress);
} else if (progress.kind === 'agentDetection') {
const agent = this.chatAgentService.getRegisteredAgent(progress.agentName);
const agent = this.chatAgentService.getAgent(progress.agentName);
if (agent) {
request.response.setAgent(agent, progress.command);
}

View file

@ -99,7 +99,7 @@ export class ChatRequestParser {
const varRange = new OffsetRange(offset, offset + full.length);
const varEditorRange = new Range(position.lineNumber, position.column, position.lineNumber, position.column + full.length);
const agent = this.agentService.getRegisteredAgent(name);
const agent = this.agentService.getAgent(name);
if (!agent) {
return;
}

View file

@ -117,7 +117,7 @@ suite('ChatRequestParser', () => {
test('agent with subcommand after text', async () => {
const agentsService = mockObject<IChatAgentService>()({});
agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
instantiationService.stub(IChatAgentService, agentsService as any);
parser = instantiationService.createInstance(ChatRequestParser);
@ -127,7 +127,7 @@ suite('ChatRequestParser', () => {
test('agents, subCommand', async () => {
const agentsService = mockObject<IChatAgentService>()({});
agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
instantiationService.stub(IChatAgentService, agentsService as any);
parser = instantiationService.createInstance(ChatRequestParser);
@ -137,7 +137,7 @@ suite('ChatRequestParser', () => {
test('agent with question mark', async () => {
const agentsService = mockObject<IChatAgentService>()({});
agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
instantiationService.stub(IChatAgentService, agentsService as any);
parser = instantiationService.createInstance(ChatRequestParser);
@ -147,7 +147,7 @@ suite('ChatRequestParser', () => {
test('agent and subcommand with leading whitespace', async () => {
const agentsService = mockObject<IChatAgentService>()({});
agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
instantiationService.stub(IChatAgentService, agentsService as any);
parser = instantiationService.createInstance(ChatRequestParser);
@ -157,7 +157,7 @@ suite('ChatRequestParser', () => {
test('agent and subcommand after newline', async () => {
const agentsService = mockObject<IChatAgentService>()({});
agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
instantiationService.stub(IChatAgentService, agentsService as any);
parser = instantiationService.createInstance(ChatRequestParser);
@ -167,7 +167,7 @@ suite('ChatRequestParser', () => {
test('agent not first', async () => {
const agentsService = mockObject<IChatAgentService>()({});
agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
instantiationService.stub(IChatAgentService, agentsService as any);
parser = instantiationService.createInstance(ChatRequestParser);
@ -177,7 +177,7 @@ suite('ChatRequestParser', () => {
test('agents and variables and multiline', async () => {
const agentsService = mockObject<IChatAgentService>()({});
agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
instantiationService.stub(IChatAgentService, agentsService as any);
varService.hasVariable.returns(true);
@ -189,7 +189,7 @@ suite('ChatRequestParser', () => {
test('agents and variables and multiline, part2', async () => {
const agentsService = mockObject<IChatAgentService>()({});
agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }]));
instantiationService.stub(IChatAgentService, agentsService as any);
varService.hasVariable.returns(true);

View file

@ -53,7 +53,8 @@ suite('VoiceChat', () => {
getFollowups(id: string, request: IChatAgentRequest, result: IChatAgentResult, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatFollowup[]> { throw new Error(); }
getRegisteredAgents(): Array<IChatAgent> { return agents; }
getActivatedAgents(): IChatAgent[] { return agents; }
getRegisteredAgent(id: string): IChatAgent | undefined { throw new Error(); }
getAgents(): IChatAgent[] { return agents; }
getAgent(id: string): IChatAgent | undefined { throw new Error(); }
getDefaultAgent(): IChatAgent | undefined { throw new Error(); }
getSecondaryAgent(): IChatAgent | undefined { throw new Error(); }
updateAgent(id: string, updateMetadata: IChatAgentMetadata): void { throw new Error(); }

View file

@ -99,9 +99,9 @@ export class TerminalChatController extends Disposable implements ITerminalContr
this._responseSupportsIssueReportingContextKey = TerminalChatContextKeys.responseSupportsIssueReporting.bindTo(this._contextKeyService);
this._sessionResponseVoteContextKey = TerminalChatContextKeys.sessionResponseVote.bindTo(this._contextKeyService);
if (!this._chatAgentService.getRegisteredAgent(this._terminalAgentId)) {
if (!this._chatAgentService.getAgent(this._terminalAgentId)) {
this._register(this._chatAgentService.onDidChangeAgents(() => {
if (this._chatAgentService.getRegisteredAgent(this._terminalAgentId)) {
if (this._chatAgentService.getAgent(this._terminalAgentId)) {
this._terminalAgentRegisteredContextKey.set(true);
}
}));
@ -370,4 +370,3 @@ export class TerminalChatController extends Disposable implements ITerminalContr
this._chatWidget?.rawValue?.dispose();
}
}