mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 13:00:58 +00:00
Fix variables in chatAgents2 API requests, add tests (#195529)
* Fix variables in chatAgents2 API requests * Enable file references and the 'used references' section by default in Insiders * Add integration tests for chat * Fix equality * fix test
This commit is contained in:
parent
e13be231e7
commit
6c02f61149
|
@ -6,6 +6,8 @@
|
|||
"license": "MIT",
|
||||
"enabledApiProposals": [
|
||||
"authSession",
|
||||
"chatAgents2",
|
||||
"chatVariables",
|
||||
"contribViewsRemote",
|
||||
"contribStatusBarItems",
|
||||
"createFileSystemWatcher",
|
||||
|
@ -20,6 +22,7 @@
|
|||
"fileSearchProvider",
|
||||
"findTextInFiles",
|
||||
"fsChunks",
|
||||
"interactive",
|
||||
"mappedEditsProvider",
|
||||
"notebookCellExecutionState",
|
||||
"notebookDeprecated",
|
||||
|
@ -165,6 +168,12 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"interactiveSession": [
|
||||
{
|
||||
"id": "provider",
|
||||
"label": "Provider"
|
||||
}
|
||||
],
|
||||
"notebooks": [
|
||||
{
|
||||
"type": "notebookCoreTest",
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import 'mocha';
|
||||
import { CancellationToken, chat, ChatAgentRequest, ChatVariableLevel, CompletionItemKind, Disposable, interactive, InteractiveProgress, InteractiveRequest, InteractiveResponseForProgress, InteractiveSession, InteractiveSessionState, Progress, ProviderResult } from 'vscode';
|
||||
import { assertNoRpc, closeAllEditors, DeferredPromise, disposeAll } from '../utils';
|
||||
|
||||
suite('chat', () => {
|
||||
let disposables: Disposable[] = [];
|
||||
setup(() => {
|
||||
disposables = [];
|
||||
});
|
||||
|
||||
teardown(async function () {
|
||||
assertNoRpc();
|
||||
await closeAllEditors();
|
||||
disposeAll(disposables);
|
||||
});
|
||||
|
||||
function getDeferredForRequest(): DeferredPromise<ChatAgentRequest> {
|
||||
disposables.push(interactive.registerInteractiveSessionProvider('provider', {
|
||||
prepareSession: (_initialState: InteractiveSessionState | undefined, _token: CancellationToken): ProviderResult<InteractiveSession> => {
|
||||
return {
|
||||
requester: { name: 'test' },
|
||||
responder: { name: 'test' },
|
||||
};
|
||||
},
|
||||
|
||||
provideResponseWithProgress: (_request: InteractiveRequest, _progress: Progress<InteractiveProgress>, _token: CancellationToken): ProviderResult<InteractiveResponseForProgress> => {
|
||||
return null;
|
||||
},
|
||||
|
||||
provideSlashCommands: (_session, _token) => {
|
||||
return [{ command: 'hello', title: 'Hello', kind: CompletionItemKind.Text }];
|
||||
},
|
||||
|
||||
removeRequest: (_session: InteractiveSession, _requestId: string): void => {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
}));
|
||||
|
||||
const deferred = new DeferredPromise<ChatAgentRequest>();
|
||||
const agent = chat.createChatAgent('agent', (request, _context, _progress, _token) => {
|
||||
deferred.complete(request);
|
||||
return null;
|
||||
});
|
||||
agent.slashCommandProvider = {
|
||||
provideSlashCommands: (_token) => {
|
||||
return [{ name: 'hello', description: 'Hello' }];
|
||||
}
|
||||
};
|
||||
disposables.push(agent);
|
||||
return deferred;
|
||||
}
|
||||
|
||||
test('agent and slash command', async () => {
|
||||
const deferred = getDeferredForRequest();
|
||||
interactive.sendInteractiveRequestToProvider('provider', { message: '@agent /hello friend' });
|
||||
const lastResult = await deferred.p;
|
||||
assert.deepStrictEqual(lastResult.slashCommand, { name: 'hello', description: 'Hello' });
|
||||
assert.strictEqual(lastResult.prompt, 'friend');
|
||||
});
|
||||
|
||||
test('agent and variable', async () => {
|
||||
disposables.push(chat.registerVariable('myVar', 'My variable', {
|
||||
resolve(_name, _context, _token) {
|
||||
return [{ level: ChatVariableLevel.Full, value: 'myValue' }];
|
||||
}
|
||||
}));
|
||||
|
||||
const deferred = getDeferredForRequest();
|
||||
interactive.sendInteractiveRequestToProvider('provider', { message: '@agent hi #myVar' });
|
||||
const lastResult = await deferred.p;
|
||||
assert.strictEqual(lastResult.prompt, 'hi [#myVar](values:myVar)');
|
||||
assert.strictEqual(lastResult.variables['myVar'][0].value, 'myValue');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import 'mocha';
|
||||
import { CancellationToken, CompletionItemKind, Disposable, interactive, InteractiveProgress, InteractiveRequest, InteractiveResponseForProgress, InteractiveSession, InteractiveSessionState, Progress, ProviderResult } from 'vscode';
|
||||
import { assertNoRpc, closeAllEditors, DeferredPromise, disposeAll } from '../utils';
|
||||
|
||||
suite('InteractiveSessionProvider', () => {
|
||||
let disposables: Disposable[] = [];
|
||||
setup(async () => {
|
||||
disposables = [];
|
||||
});
|
||||
|
||||
teardown(async function () {
|
||||
assertNoRpc();
|
||||
await closeAllEditors();
|
||||
disposeAll(disposables);
|
||||
});
|
||||
|
||||
function getDeferredForRequest(): DeferredPromise<InteractiveRequest> {
|
||||
const deferred = new DeferredPromise<InteractiveRequest>();
|
||||
disposables.push(interactive.registerInteractiveSessionProvider('provider', {
|
||||
prepareSession: (_initialState: InteractiveSessionState | undefined, _token: CancellationToken): ProviderResult<InteractiveSession> => {
|
||||
return {
|
||||
requester: { name: 'test' },
|
||||
responder: { name: 'test' },
|
||||
};
|
||||
},
|
||||
|
||||
provideResponseWithProgress: (request: InteractiveRequest, _progress: Progress<InteractiveProgress>, _token: CancellationToken): ProviderResult<InteractiveResponseForProgress> => {
|
||||
deferred.complete(request);
|
||||
return null;
|
||||
},
|
||||
|
||||
provideSlashCommands: (_session, _token) => {
|
||||
return [{ command: 'hello', title: 'Hello', kind: CompletionItemKind.Text }];
|
||||
},
|
||||
|
||||
removeRequest: (_session: InteractiveSession, _requestId: string): void => {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
}));
|
||||
return deferred;
|
||||
}
|
||||
|
||||
test('plain text query', async () => {
|
||||
const deferred = getDeferredForRequest();
|
||||
interactive.sendInteractiveRequestToProvider('provider', { message: 'hello' });
|
||||
const lastResult = await deferred.p;
|
||||
assert.strictEqual(lastResult.message, 'hello');
|
||||
});
|
||||
|
||||
test('slash command', async () => {
|
||||
const deferred = getDeferredForRequest();
|
||||
interactive.sendInteractiveRequestToProvider('provider', { message: '/hello' });
|
||||
const lastResult = await deferred.p;
|
||||
assert.strictEqual(lastResult.message, '/hello');
|
||||
});
|
||||
});
|
|
@ -67,11 +67,13 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
|
|||
? await agent.validateSlashCommand(request.command)
|
||||
: undefined;
|
||||
|
||||
|
||||
try {
|
||||
|
||||
const task = agent.invoke(
|
||||
{ prompt: request.message, variables: {}, slashCommand },
|
||||
{
|
||||
prompt: request.message,
|
||||
variables: typeConvert.ChatVariable.objectTo(request.variables),
|
||||
slashCommand
|
||||
},
|
||||
{ history: context.history.map(typeConvert.ChatMessage.to) },
|
||||
new Progress<vscode.InteractiveProgress>(p => {
|
||||
throwIfDone();
|
||||
|
|
|
@ -2236,6 +2236,15 @@ export namespace ChatMessageRole {
|
|||
}
|
||||
|
||||
export namespace ChatVariable {
|
||||
export function objectTo(variableObject: Record<string, IChatRequestVariableValue[]>): Record<string, vscode.ChatVariableValue[]> {
|
||||
const result: Record<string, vscode.ChatVariableValue[]> = {};
|
||||
for (const key of Object.keys(variableObject)) {
|
||||
result[key] = variableObject[key].map(ChatVariable.to);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function to(variable: IChatRequestVariableValue): vscode.ChatVariableValue {
|
||||
return {
|
||||
level: ChatVariableLevel.to(variable.level),
|
||||
|
|
|
@ -55,6 +55,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
|
|||
import { WorkbenchCompressibleAsyncDataTree, WorkbenchList } from 'vs/platform/list/browser/listService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels';
|
||||
|
@ -145,6 +146,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IChatService private readonly chatService: IChatService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IProductService productService: IProductService,
|
||||
) {
|
||||
super();
|
||||
this.renderer = this.instantiationService.createInstance(MarkdownRenderer, {});
|
||||
|
@ -152,10 +154,10 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
this._treePool = this._register(this.instantiationService.createInstance(TreePool, this._onDidChangeVisibility.event));
|
||||
this._contentReferencesListPool = this._register(this.instantiationService.createInstance(ContentReferencesListPool, this._onDidChangeVisibility.event));
|
||||
|
||||
this._usedReferencesEnabled = configService.getValue('chat.experimental.usedReferences');
|
||||
this._usedReferencesEnabled = configService.getValue('chat.experimental.usedReferences') ?? productService.quality !== 'stable';
|
||||
this._register(configService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('chat.experimental.usedReferences')) {
|
||||
this._usedReferencesEnabled = configService.getValue('chat.experimental.usedReferences');
|
||||
this._usedReferencesEnabled = configService.getValue('chat.experimental.usedReferences') ?? productService.quality !== 'stable';
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat
|
|||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
|
@ -435,6 +436,7 @@ class BuiltinDynamicCompletions extends Disposable {
|
|||
@ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService,
|
||||
@IChatWidgetService private readonly chatWidgetService: IChatWidgetService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
) {
|
||||
super();
|
||||
|
||||
|
@ -442,7 +444,7 @@ class BuiltinDynamicCompletions extends Disposable {
|
|||
_debugDisplayName: 'chatDynamicCompletions',
|
||||
triggerCharacters: ['$'],
|
||||
provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, _token: CancellationToken) => {
|
||||
const fileVariablesEnabled = this.configurationService.getValue('chat.experimental.fileVariables');
|
||||
const fileVariablesEnabled = this.configurationService.getValue('chat.experimental.fileVariables') ?? this.productService.quality !== 'stable';
|
||||
if (!fileVariablesEnabled) {
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue