Skip to content

Commit efc04b8

Browse files
authored
New proposal for chat variable resolver (microsoft#205572)
* Tweak ChatFollowup * Remove API TODOs * New proposal for chat variable resolver * Bump distro * Enforce same-extension followup * Add participant proposal to integration test folder * Allow no participant for a followup
1 parent 74724fb commit efc04b8

File tree

9 files changed

+78
-58
lines changed

9 files changed

+78
-58
lines changed

extensions/vscode-api-tests/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"chatParticipant",
1111
"languageModels",
1212
"defaultChatParticipant",
13+
"chatVariableResolver",
1314
"contribViewsRemote",
1415
"contribStatusBarItems",
1516
"createFileSystemWatcher",

extensions/vscode-api-tests/src/singlefolder-tests/chat.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ suite('chat', () => {
7474
});
7575

7676
test('participant and variable', async () => {
77-
disposables.push(chat.registerVariable('myVar', 'My variable', {
77+
disposables.push(chat.registerChatVariableResolver('myVar', 'My variable', {
7878
resolve(_name, _context, _token) {
7979
return [{ level: ChatVariableLevel.Full, value: 'myValue' }];
8080
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "code-oss-dev",
33
"version": "1.87.0",
4-
"distro": "af73a537ea203329debad3df7ca7b42b4799473f",
4+
"distro": "b314654a31bdba8cd2b0c7548e931916d03416bf",
55
"author": {
66
"name": "Microsoft Corporation"
77
},

src/vs/workbench/api/common/extHost.api.impl.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,8 +1409,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
14091409
checkProposedApiEnabled(extension, 'chatProvider');
14101410
return extHostChatProvider.registerLanguageModel(extension, id, provider, metadata);
14111411
},
1412-
registerVariable(name: string, description: string, resolver: vscode.ChatVariableResolver) {
1413-
checkProposedApiEnabled(extension, 'chatParticipant');
1412+
registerChatVariableResolver(name: string, description: string, resolver: vscode.ChatVariableResolver) {
1413+
checkProposedApiEnabled(extension, 'chatVariableResolver');
14141414
return extHostChatVariables.registerVariableResolver(extension, name, description, resolver);
14151415
},
14161416
registerMappedEditsProvider(selector: vscode.DocumentSelector, provider: vscode.MappedEditsProvider) {

src/vs/workbench/api/common/extHostChatAgents2.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ import { CancellationToken } from 'vs/base/common/cancellation';
99
import { toErrorMessage } from 'vs/base/common/errorMessage';
1010
import { Emitter } from 'vs/base/common/event';
1111
import { IMarkdownString } from 'vs/base/common/htmlContent';
12+
import { Iterable } from 'vs/base/common/iterator';
1213
import { DisposableMap, DisposableStore } from 'vs/base/common/lifecycle';
1314
import { StopWatch } from 'vs/base/common/stopwatch';
1415
import { assertType } from 'vs/base/common/types';
1516
import { URI } from 'vs/base/common/uri';
1617
import { localize } from 'vs/nls';
17-
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
18+
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
1819
import { ILogService } from 'vs/platform/log/common/log';
1920
import { ExtHostChatAgentsShape2, IChatAgentCompletionItem, IChatAgentHistoryEntryDto, IMainContext, MainContext, MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol';
2021
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
@@ -261,6 +262,16 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
261262

262263
const ehResult = typeConvert.ChatAgentResult.to(result);
263264
return (await agent.provideFollowups(ehResult, token))
265+
.filter(f => {
266+
// The followup must refer to a participant that exists from the same extension
267+
const isValid = !f.participant || Iterable.some(
268+
this._agents.values(),
269+
a => a.id === f.participant && ExtensionIdentifier.equals(a.extension.identifier, agent.extension.identifier));
270+
if (!isValid) {
271+
this._logService.warn(`[@${agent.id}] ChatFollowup refers to an invalid participant: ${f.participant}`);
272+
}
273+
return isValid;
274+
})
264275
.map(f => typeConvert.ChatFollowup.from(f, request));
265276
}
266277

src/vs/workbench/api/common/extHostTypeConverters.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2201,18 +2201,16 @@ export namespace ChatFollowup {
22012201
agentId: followup.participant ?? request?.agentId ?? '',
22022202
subCommand: followup.command ?? request?.command,
22032203
message: followup.prompt,
2204-
title: followup.title,
2205-
tooltip: followup.tooltip,
2204+
title: followup.label
22062205
};
22072206
}
22082207

22092208
export function to(followup: IChatFollowup): vscode.ChatFollowup {
22102209
return {
22112210
prompt: followup.message,
2212-
title: followup.title,
2211+
label: followup.title,
22132212
participant: followup.agentId,
22142213
command: followup.subCommand,
2215-
tooltip: followup.tooltip,
22162214
};
22172215
}
22182216
}

src/vs/workbench/services/extensions/common/extensionsApiProposals.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const allApiProposals = Object.freeze({
1515
chatParticipantAdditions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts',
1616
chatProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts',
1717
chatTab: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatTab.d.ts',
18+
chatVariableResolver: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatVariableResolver.d.ts',
1819
codeActionAI: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codeActionAI.d.ts',
1920
codeActionRanges: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codeActionRanges.d.ts',
2021
codiconDecoration: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codiconDecoration.d.ts',

src/vscode-dts/vscode.proposed.chatParticipant.d.ts

Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
declare module 'vscode' {
77

8-
// TODO@API name: Turn?
98
export class ChatRequestTurn {
109

1110
/**
@@ -36,7 +35,6 @@ declare module 'vscode' {
3635
private constructor(prompt: string, command: string | undefined, variables: ChatResolvedVariable[], participant: { extensionId: string; participant: string });
3736
}
3837

39-
// TODO@API name: Turn?
4038
export class ChatResponseTurn {
4139

4240
/**
@@ -185,27 +183,21 @@ declare module 'vscode' {
185183
*/
186184
prompt: string;
187185

186+
/**
187+
* A title to show the user, when it is different than the message.
188+
*/
189+
label?: string;
190+
188191
/**
189192
* By default, the followup goes to the same participant/command. But this property can be set to invoke a different participant.
190-
* TODO@API do extensions need to specify the extensionID of the participant here as well?
193+
* Followups can only invoke a participant that was contributed by the same extension.
191194
*/
192195
participant?: string;
193196

194197
/**
195198
* By default, the followup goes to the same participant/command. But this property can be set to invoke a different command.
196199
*/
197200
command?: string;
198-
199-
/**
200-
* A tooltip to show when hovering over the followup.
201-
*/
202-
tooltip?: string;
203-
204-
/**
205-
* A title to show the user, when it is different than the message.
206-
*/
207-
// TODO@API title vs tooltip?
208-
title?: string;
209201
}
210202

211203
/**
@@ -400,9 +392,6 @@ declare module 'vscode' {
400392
* @param value
401393
* @returns This stream.
402394
*/
403-
// TODO@API is this always inline or not
404-
// TODO@API is this markdown or string?
405-
// TODO@API this influences the rendering, it inserts new lines which is likely a bug
406395
progress(value: string): ChatResponseStream;
407396

408397
/**
@@ -414,8 +403,6 @@ declare module 'vscode' {
414403
* @param value A uri or location
415404
* @returns This stream.
416405
*/
417-
// TODO@API support non-file uris, like http://example.com
418-
// TODO@API support mapped edits
419406
reference(value: Uri | Location): ChatResponseStream;
420407

421408
/**
@@ -426,8 +413,6 @@ declare module 'vscode' {
426413
push(part: ChatResponsePart): ChatResponseStream;
427414
}
428415

429-
// TODO@API should the name suffix differentiate between rendered items (XYZPart)
430-
// and metadata like XYZItem
431416
export class ChatResponseTextPart {
432417
value: string;
433418
constructor(value: string);
@@ -457,7 +442,6 @@ declare module 'vscode' {
457442

458443
export class ChatResponseProgressPart {
459444
value: string;
460-
// TODO@API inline
461445
constructor(value: string);
462446
}
463447

@@ -489,21 +473,11 @@ declare module 'vscode' {
489473
* @returns A new chat participant
490474
*/
491475
export function createChatParticipant(name: string, handler: ChatRequestHandler): ChatParticipant;
492-
493-
/**
494-
* Register a variable which can be used in a chat request to any participant.
495-
* @param name The name of the variable, to be used in the chat input as `#name`.
496-
* @param description A description of the variable for the chat input suggest widget.
497-
* @param resolver Will be called to provide the chat variable's value when it is used.
498-
*/
499-
// TODO@API NAME: registerChatVariable, registerChatVariableResolver
500-
export function registerVariable(name: string, description: string, resolver: ChatVariableResolver): Disposable;
501476
}
502477

503478
/**
504479
* The detail level of this chat variable value.
505480
*/
506-
// TODO@API maybe for round2
507481
export enum ChatVariableLevel {
508482
Short = 1,
509483
Medium = 2,
@@ -526,21 +500,4 @@ declare module 'vscode' {
526500
*/
527501
description?: string;
528502
}
529-
530-
export interface ChatVariableContext {
531-
/**
532-
* The message entered by the user, which includes this variable.
533-
*/
534-
prompt: string;
535-
}
536-
537-
export interface ChatVariableResolver {
538-
/**
539-
* A callback to resolve the value of a chat variable.
540-
* @param name The name of the variable.
541-
* @param context Contextual information about this chat request.
542-
* @param token A cancellation token.
543-
*/
544-
resolve(name: string, context: ChatVariableContext, token: CancellationToken): ProviderResult<ChatVariableValue[]>;
545-
}
546503
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
declare module 'vscode' {
7+
8+
export namespace chat {
9+
10+
/**
11+
* Register a variable which can be used in a chat request to any participant.
12+
* @param name The name of the variable, to be used in the chat input as `#name`.
13+
* @param description A description of the variable for the chat input suggest widget.
14+
* @param resolver Will be called to provide the chat variable's value when it is used.
15+
*/
16+
export function registerChatVariableResolver(name: string, description: string, resolver: ChatVariableResolver): Disposable;
17+
}
18+
19+
export interface ChatVariableValue {
20+
/**
21+
* 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.
22+
*/
23+
level: ChatVariableLevel;
24+
25+
/**
26+
* 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.
27+
*/
28+
value: string | Uri;
29+
30+
/**
31+
* A description of this value, which could be provided to the LLM as a hint.
32+
*/
33+
description?: string;
34+
}
35+
36+
export interface ChatVariableContext {
37+
/**
38+
* The message entered by the user, which includes this variable.
39+
*/
40+
prompt: string;
41+
}
42+
43+
export interface ChatVariableResolver {
44+
/**
45+
* A callback to resolve the value of a chat variable.
46+
* @param name The name of the variable.
47+
* @param context Contextual information about this chat request.
48+
* @param token A cancellation token.
49+
*/
50+
resolve(name: string, context: ChatVariableContext, token: CancellationToken): ProviderResult<ChatVariableValue[]>;
51+
}
52+
}

0 commit comments

Comments
 (0)