mirror of
https://github.com/Microsoft/vscode
synced 2024-10-02 17:32:41 +00:00
Add ChatAgentResult2#metadata (#204851)
* Support serializable metadata on 'result' object from chat agent * Fix 'errorDetails' on the VM * Fix acceptAction, get rid of generic parameter on ChatAgent * Use result metadata for followups * Use serialized result for history * Don't share metadata between agents * Add a test for result metadata * Fix build
This commit is contained in:
parent
56c54e7fef
commit
b49c1c1170
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import 'mocha';
|
||||
import { CancellationToken, chat, ChatAgentRequest, ChatVariableLevel, Disposable, interactive, InteractiveSession, ProviderResult } from 'vscode';
|
||||
import { CancellationToken, chat, ChatAgentRequest, ChatAgentResult2, ChatVariableLevel, Disposable, interactive, InteractiveSession, ProviderResult } from 'vscode';
|
||||
import { assertNoRpc, closeAllEditors, DeferredPromise, disposeAll } from '../utils';
|
||||
|
||||
suite('chat', () => {
|
||||
|
@ -67,4 +67,37 @@ suite('chat', () => {
|
|||
assert.strictEqual(request.prompt, 'hi [#myVar](values:myVar)');
|
||||
assert.strictEqual(request.variables['myVar'][0].value, 'myValue');
|
||||
});
|
||||
|
||||
test('result metadata is returned to the followup provider', async () => {
|
||||
disposables.push(interactive.registerInteractiveSessionProvider('provider', {
|
||||
prepareSession: (_token: CancellationToken): ProviderResult<InteractiveSession> => {
|
||||
return {
|
||||
requester: { name: 'test' },
|
||||
responder: { name: 'test' },
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
const deferred = new DeferredPromise<ChatAgentResult2>();
|
||||
const agent = chat.createChatAgent('agent', (_request, _context, _progress, _token) => {
|
||||
return { metadata: { key: 'value' } };
|
||||
});
|
||||
agent.isDefault = true;
|
||||
agent.subCommandProvider = {
|
||||
provideSubCommands: (_token) => {
|
||||
return [{ name: 'hello', description: 'Hello' }];
|
||||
}
|
||||
};
|
||||
agent.followupProvider = {
|
||||
provideFollowups(result, _token) {
|
||||
deferred.complete(result);
|
||||
return [];
|
||||
},
|
||||
};
|
||||
disposables.push(agent);
|
||||
|
||||
interactive.sendInteractiveRequestToProvider('provider', { message: '@agent /hello friend' });
|
||||
const result = await deferred.p;
|
||||
assert.deepStrictEqual(result.metadata, { key: 'value' });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -59,9 +59,9 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
|
|||
for (const [handle, agent] of this._agents) {
|
||||
if (agent.name === e.agentId) {
|
||||
if (e.action.kind === 'vote') {
|
||||
this._proxy.$acceptFeedback(handle, e.sessionId, e.requestId, e.action.direction);
|
||||
this._proxy.$acceptFeedback(handle, e.result ?? {}, e.action.direction);
|
||||
} else {
|
||||
this._proxy.$acceptAction(handle, e.sessionId, e.requestId, e);
|
||||
this._proxy.$acceptAction(handle, e.result || {}, e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -87,12 +87,12 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
|
|||
this._pendingProgress.delete(request.requestId);
|
||||
}
|
||||
},
|
||||
provideFollowups: async (sessionId, token): Promise<IChatFollowup[]> => {
|
||||
provideFollowups: async (result, token): Promise<IChatFollowup[]> => {
|
||||
if (!this._agents.get(handle)?.hasFollowups) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this._proxy.$provideFollowups(handle, sessionId, token);
|
||||
return this._proxy.$provideFollowups(handle, result, token);
|
||||
},
|
||||
get lastSlashCommands() {
|
||||
return lastSlashCommands;
|
||||
|
|
|
@ -1221,9 +1221,9 @@ export type IChatAgentHistoryEntryDto = {
|
|||
export interface ExtHostChatAgentsShape2 {
|
||||
$invokeAgent(handle: number, request: IChatAgentRequest, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise<IChatAgentResult | undefined>;
|
||||
$provideSlashCommands(handle: number, token: CancellationToken): Promise<IChatAgentCommand[]>;
|
||||
$provideFollowups(handle: number, sessionId: string, token: CancellationToken): Promise<IChatFollowup[]>;
|
||||
$acceptFeedback(handle: number, sessionId: string, requestId: string, vote: InteractiveSessionVoteDirection, reportIssue?: boolean): void;
|
||||
$acceptAction(handle: number, sessionId: string, requestId: string, action: IChatUserActionEvent): void;
|
||||
$provideFollowups(handle: number, result: IChatAgentResult, token: CancellationToken): Promise<IChatFollowup[]>;
|
||||
$acceptFeedback(handle: number, result: IChatAgentResult, vote: InteractiveSessionVoteDirection, reportIssue?: boolean): void;
|
||||
$acceptAction(handle: number, result: IChatAgentResult, action: IChatUserActionEvent): void;
|
||||
$invokeCompletionProvider(handle: number, query: string, token: CancellationToken): Promise<IChatAgentCompletionItem[]>;
|
||||
$provideWelcomeMessage(handle: number, token: CancellationToken): Promise<(string | IMarkdownString)[] | undefined>;
|
||||
$provideSampleQuestions(handle: number, token: CancellationToken): Promise<IChatFollowup[] | undefined>;
|
||||
|
|
|
@ -157,11 +157,9 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
|
|||
|
||||
private static _idPool = 0;
|
||||
|
||||
private readonly _agents = new Map<number, ExtHostChatAgent<any>>();
|
||||
private readonly _agents = new Map<number, ExtHostChatAgent>();
|
||||
private readonly _proxy: MainThreadChatAgentsShape2;
|
||||
|
||||
private readonly _previousResultMap: Map<string, vscode.ChatAgentResult2> = new Map();
|
||||
private readonly _resultsBySessionAndRequestId: Map<string, Map<string, vscode.ChatAgentResult2>> = new Map();
|
||||
private readonly _sessionDisposables: DisposableMap<string, DisposableStore> = new DisposableMap();
|
||||
|
||||
constructor(
|
||||
|
@ -173,9 +171,9 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
|
|||
this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents2);
|
||||
}
|
||||
|
||||
createChatAgent<TResult extends vscode.ChatAgentResult2>(extension: IExtensionDescription, name: string, handler: vscode.ChatAgentExtendedHandler): vscode.ChatAgent2<TResult> {
|
||||
createChatAgent(extension: IExtensionDescription, name: string, handler: vscode.ChatAgentExtendedHandler): vscode.ChatAgent2 {
|
||||
const handle = ExtHostChatAgents2._idPool++;
|
||||
const agent = new ExtHostChatAgent<TResult>(extension, name, this._proxy, handle, handler);
|
||||
const agent = new ExtHostChatAgent(extension, name, this._proxy, handle, handler);
|
||||
this._agents.set(handle, agent);
|
||||
|
||||
this._proxy.$registerAgent(handle, name, {});
|
||||
|
@ -183,10 +181,6 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
|
|||
}
|
||||
|
||||
async $invokeAgent(handle: number, request: IChatAgentRequest, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise<IChatAgentResult | undefined> {
|
||||
// Clear the previous result so that $acceptFeedback or $acceptAction during a request will be ignored.
|
||||
// We may want to support sending those during a request.
|
||||
this._previousResultMap.delete(request.sessionId);
|
||||
|
||||
const agent = this._agents.get(handle);
|
||||
if (!agent) {
|
||||
throw new Error(`[CHAT](${handle}) CANNOT invoke agent because the agent is not registered`);
|
||||
|
@ -203,7 +197,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
|
|||
|
||||
const stream = new ChatAgentResponseStream(agent.extension, request, this._proxy, this._logService, this.commands.converter, sessionDisposables);
|
||||
try {
|
||||
const convertedHistory = await this.prepareHistory(agent, request, context);
|
||||
const convertedHistory = await this.prepareHistory(request, context);
|
||||
const task = agent.invoke(
|
||||
typeConvert.ChatAgentRequest.to(request),
|
||||
{ history: convertedHistory },
|
||||
|
@ -212,21 +206,16 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
|
|||
);
|
||||
|
||||
return await raceCancellation(Promise.resolve(task).then((result) => {
|
||||
if (result) {
|
||||
this._previousResultMap.set(request.sessionId, result);
|
||||
let sessionResults = this._resultsBySessionAndRequestId.get(request.sessionId);
|
||||
if (!sessionResults) {
|
||||
sessionResults = new Map();
|
||||
this._resultsBySessionAndRequestId.set(request.sessionId, sessionResults);
|
||||
if (result?.metadata) {
|
||||
try {
|
||||
JSON.stringify(result.metadata);
|
||||
} catch (err) {
|
||||
const msg = `result.metadata MUST be JSON.stringify-able. Got error: ${err.message}`;
|
||||
this._logService.error(`[${agent.extension.identifier.value}] [@${agent.id}] ${msg}`, agent.extension);
|
||||
return { errorDetails: { message: msg }, timings: stream.timings };
|
||||
}
|
||||
sessionResults.set(request.requestId, result);
|
||||
|
||||
return { errorDetails: result.errorDetails, timings: stream.timings };
|
||||
} else {
|
||||
this._previousResultMap.delete(request.sessionId);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return { errorDetails: result?.errorDetails, timings: stream.timings, metadata: result?.metadata };
|
||||
}), token);
|
||||
|
||||
} catch (e) {
|
||||
|
@ -239,22 +228,22 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
|
|||
}
|
||||
}
|
||||
|
||||
private async prepareHistory<T extends vscode.ChatAgentResult2>(agent: ExtHostChatAgent<T>, request: IChatAgentRequest, context: { history: IChatAgentHistoryEntryDto[] }): Promise<vscode.ChatAgentHistoryEntry[]> {
|
||||
private async prepareHistory(request: IChatAgentRequest, context: { history: IChatAgentHistoryEntryDto[] }): Promise<vscode.ChatAgentHistoryEntry[]> {
|
||||
return coalesce(await Promise.all(context.history
|
||||
.map(async h => {
|
||||
const result = request.agentId === h.request.agentId && this._resultsBySessionAndRequestId.get(request.sessionId)?.get(h.request.requestId)
|
||||
|| h.result;
|
||||
const ehResult = typeConvert.ChatAgentResult.to(h.result);
|
||||
const result: vscode.ChatAgentResult2 = request.agentId === h.request.agentId ?
|
||||
ehResult :
|
||||
{ ...ehResult, metadata: undefined };
|
||||
return {
|
||||
request: typeConvert.ChatAgentRequest.to(h.request),
|
||||
response: coalesce(h.response.map(r => typeConvert.ChatResponsePart.from(r, this.commands.converter))),
|
||||
result
|
||||
result,
|
||||
} satisfies vscode.ChatAgentHistoryEntry;
|
||||
})));
|
||||
}
|
||||
|
||||
$releaseSession(sessionId: string): void {
|
||||
this._previousResultMap.delete(sessionId);
|
||||
this._resultsBySessionAndRequestId.delete(sessionId);
|
||||
this._sessionDisposables.deleteAndDispose(sessionId);
|
||||
}
|
||||
|
||||
|
@ -267,30 +256,23 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
|
|||
return agent.provideSlashCommands(token);
|
||||
}
|
||||
|
||||
$provideFollowups(handle: number, sessionId: string, token: CancellationToken): Promise<IChatFollowup[]> {
|
||||
$provideFollowups(handle: number, result: IChatAgentResult, token: CancellationToken): Promise<IChatFollowup[]> {
|
||||
const agent = this._agents.get(handle);
|
||||
if (!agent) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const result = this._previousResultMap.get(sessionId);
|
||||
if (!result) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
return agent.provideFollowups(result, token);
|
||||
const ehResult = typeConvert.ChatAgentResult.to(result);
|
||||
return agent.provideFollowups(ehResult, token);
|
||||
}
|
||||
|
||||
$acceptFeedback(handle: number, sessionId: string, requestId: string, vote: InteractiveSessionVoteDirection, reportIssue?: boolean): void {
|
||||
$acceptFeedback(handle: number, result: IChatAgentResult, vote: InteractiveSessionVoteDirection, reportIssue?: boolean): void {
|
||||
const agent = this._agents.get(handle);
|
||||
if (!agent) {
|
||||
return;
|
||||
}
|
||||
const result = this._resultsBySessionAndRequestId.get(sessionId)?.get(requestId);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ehResult = typeConvert.ChatAgentResult.to(result);
|
||||
let kind: extHostTypes.ChatAgentResultFeedbackKind;
|
||||
switch (vote) {
|
||||
case InteractiveSessionVoteDirection.Down:
|
||||
|
@ -300,29 +282,28 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
|
|||
kind = extHostTypes.ChatAgentResultFeedbackKind.Helpful;
|
||||
break;
|
||||
}
|
||||
agent.acceptFeedback(reportIssue ? Object.freeze({ result, kind, reportIssue }) : Object.freeze({ result, kind }));
|
||||
agent.acceptFeedback(reportIssue ?
|
||||
Object.freeze({ result: ehResult, kind, reportIssue }) :
|
||||
Object.freeze({ result: ehResult, kind }));
|
||||
}
|
||||
|
||||
$acceptAction(handle: number, sessionId: string, requestId: string, action: IChatUserActionEvent): void {
|
||||
$acceptAction(handle: number, result: IChatAgentResult, action: IChatUserActionEvent): void {
|
||||
const agent = this._agents.get(handle);
|
||||
if (!agent) {
|
||||
return;
|
||||
}
|
||||
const result = this._resultsBySessionAndRequestId.get(sessionId)?.get(requestId);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
if (action.action.kind === 'vote') {
|
||||
// handled by $acceptFeedback
|
||||
return;
|
||||
}
|
||||
|
||||
const ehResult = typeConvert.ChatAgentResult.to(result);
|
||||
if (action.action.kind === 'command') {
|
||||
const commandAction: vscode.ChatAgentCommandAction = { kind: 'command', commandButton: typeConvert.ChatResponseProgress.toProgressContent(action.action.commandButton, this.commands.converter) as vscode.ChatAgentCommandButton };
|
||||
agent.acceptAction(Object.freeze({ action: commandAction, result }));
|
||||
agent.acceptAction(Object.freeze({ action: commandAction, result: ehResult }));
|
||||
return;
|
||||
} else {
|
||||
agent.acceptAction(Object.freeze({ action: action.action, result }));
|
||||
agent.acceptAction(Object.freeze({ action: action.action, result: ehResult }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -355,10 +336,10 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
|
|||
}
|
||||
}
|
||||
|
||||
class ExtHostChatAgent<TResult extends vscode.ChatAgentResult2> {
|
||||
class ExtHostChatAgent {
|
||||
|
||||
private _subCommandProvider: vscode.ChatAgentSubCommandProvider | undefined;
|
||||
private _followupProvider: vscode.ChatAgentFollowupProvider<TResult> | undefined;
|
||||
private _followupProvider: vscode.ChatAgentFollowupProvider | undefined;
|
||||
private _description: string | undefined;
|
||||
private _fullName: string | undefined;
|
||||
private _iconPath: vscode.Uri | { light: vscode.Uri; dark: vscode.Uri } | vscode.ThemeIcon | undefined;
|
||||
|
@ -367,7 +348,7 @@ class ExtHostChatAgent<TResult extends vscode.ChatAgentResult2> {
|
|||
private _helpTextPostfix: string | vscode.MarkdownString | undefined;
|
||||
private _sampleRequest?: string;
|
||||
private _isSecondary: boolean | undefined;
|
||||
private _onDidReceiveFeedback = new Emitter<vscode.ChatAgentResult2Feedback<TResult>>();
|
||||
private _onDidReceiveFeedback = new Emitter<vscode.ChatAgentResult2Feedback>();
|
||||
private _onDidPerformAction = new Emitter<vscode.ChatAgentUserActionEvent>();
|
||||
private _supportIssueReporting: boolean | undefined;
|
||||
private _agentVariableProvider?: { provider: vscode.ChatAgentCompletionItemProvider; triggerCharacters: string[] };
|
||||
|
@ -381,7 +362,7 @@ class ExtHostChatAgent<TResult extends vscode.ChatAgentResult2> {
|
|||
private readonly _callback: vscode.ChatAgentExtendedHandler,
|
||||
) { }
|
||||
|
||||
acceptFeedback(feedback: vscode.ChatAgentResult2Feedback<TResult>) {
|
||||
acceptFeedback(feedback: vscode.ChatAgentResult2Feedback) {
|
||||
this._onDidReceiveFeedback.fire(feedback);
|
||||
}
|
||||
|
||||
|
@ -415,7 +396,7 @@ class ExtHostChatAgent<TResult extends vscode.ChatAgentResult2> {
|
|||
}));
|
||||
}
|
||||
|
||||
async provideFollowups(result: TResult, token: CancellationToken): Promise<IChatFollowup[]> {
|
||||
async provideFollowups(result: vscode.ChatAgentResult2, token: CancellationToken): Promise<IChatFollowup[]> {
|
||||
if (!this._followupProvider) {
|
||||
return [];
|
||||
}
|
||||
|
@ -458,7 +439,7 @@ class ExtHostChatAgent<TResult extends vscode.ChatAgentResult2> {
|
|||
return content?.map(f => typeConvert.ChatFollowup.from(f));
|
||||
}
|
||||
|
||||
get apiAgent(): vscode.ChatAgent2<TResult> {
|
||||
get apiAgent(): vscode.ChatAgent2 {
|
||||
let disposed = false;
|
||||
let updateScheduled = false;
|
||||
const updateMetadataSoon = () => {
|
||||
|
@ -630,7 +611,7 @@ class ExtHostChatAgent<TResult extends vscode.ChatAgentResult2> {
|
|||
that._onDidReceiveFeedback.dispose();
|
||||
that._proxy.$unregisterAgent(that._handle);
|
||||
},
|
||||
} satisfies vscode.ChatAgent2<TResult>;
|
||||
} satisfies vscode.ChatAgent2;
|
||||
}
|
||||
|
||||
invoke(request: vscode.ChatAgentRequest, context: vscode.ChatAgentContext, response: vscode.ChatAgentExtendedResponseStream, token: CancellationToken): vscode.ProviderResult<vscode.ChatAgentResult2> {
|
||||
|
|
|
@ -34,7 +34,7 @@ import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
|||
import { getPrivateApiFor } from 'vs/workbench/api/common/extHostTestingPrivateApi';
|
||||
import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from 'vs/workbench/common/editor';
|
||||
import { IViewBadge } from 'vs/workbench/common/views';
|
||||
import { IChatAgentRequest } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import * as chatProvider from 'vs/workbench/contrib/chat/common/chatProvider';
|
||||
import { IChatCommandButton, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgressMessage, IChatTreeData } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
|
@ -2628,6 +2628,15 @@ export namespace ChatAgentCompletionItem {
|
|||
}
|
||||
}
|
||||
|
||||
export namespace ChatAgentResult {
|
||||
export function to(result: IChatAgentResult): vscode.ChatAgentResult2 {
|
||||
return {
|
||||
errorDetails: result.errorDetails,
|
||||
metadata: result.metadata,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace TerminalQuickFix {
|
||||
export function from(quickFix: vscode.TerminalQuickFixTerminalCommand | vscode.TerminalQuickFixOpener | vscode.Command, converter: Command.ICommandsConverter, disposables: DisposableStore): extHostProtocol.ITerminalQuickFixTerminalCommandDto | extHostProtocol.ITerminalQuickFixOpenerDto | extHostProtocol.ICommandDto | undefined {
|
||||
|
|
|
@ -108,6 +108,7 @@ export function registerChatCodeBlockActions() {
|
|||
agentId: context.element.agent?.id,
|
||||
sessionId: context.element.sessionId,
|
||||
requestId: context.element.requestId,
|
||||
result: context.element.result,
|
||||
action: {
|
||||
kind: 'copy',
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
|
@ -151,6 +152,7 @@ export function registerChatCodeBlockActions() {
|
|||
agentId: context.element.agent?.id,
|
||||
sessionId: context.element.sessionId,
|
||||
requestId: context.element.requestId,
|
||||
result: context.element.result,
|
||||
action: {
|
||||
kind: 'copy',
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
|
@ -325,6 +327,7 @@ export function registerChatCodeBlockActions() {
|
|||
agentId: context.element.agent?.id,
|
||||
sessionId: context.element.sessionId,
|
||||
requestId: context.element.requestId,
|
||||
result: context.element.result,
|
||||
action: {
|
||||
kind: 'insert',
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
|
@ -370,6 +373,7 @@ export function registerChatCodeBlockActions() {
|
|||
agentId: context.element.agent?.id,
|
||||
sessionId: context.element.sessionId,
|
||||
requestId: context.element.requestId,
|
||||
result: context.element.result,
|
||||
action: {
|
||||
kind: 'insert',
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
|
@ -464,6 +468,7 @@ export function registerChatCodeBlockActions() {
|
|||
agentId: context.element.agent?.id,
|
||||
sessionId: context.element.sessionId,
|
||||
requestId: context.element.requestId,
|
||||
result: context.element.result,
|
||||
action: {
|
||||
kind: 'runInTerminal',
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
|
|
|
@ -54,6 +54,7 @@ export function registerChatTitleActions() {
|
|||
agentId: item.agent?.id,
|
||||
sessionId: item.sessionId,
|
||||
requestId: item.requestId,
|
||||
result: item.result,
|
||||
action: {
|
||||
kind: 'vote',
|
||||
direction: InteractiveSessionVoteDirection.Up,
|
||||
|
@ -93,6 +94,7 @@ export function registerChatTitleActions() {
|
|||
agentId: item.agent?.id,
|
||||
sessionId: item.sessionId,
|
||||
requestId: item.requestId,
|
||||
result: item.result,
|
||||
action: {
|
||||
kind: 'vote',
|
||||
direction: InteractiveSessionVoteDirection.Down,
|
||||
|
@ -131,6 +133,7 @@ export function registerChatTitleActions() {
|
|||
agentId: item.agent?.id,
|
||||
sessionId: item.sessionId,
|
||||
requestId: item.requestId,
|
||||
result: item.result,
|
||||
action: {
|
||||
kind: 'bug'
|
||||
}
|
||||
|
|
|
@ -289,13 +289,13 @@ class QuickChat extends Disposable {
|
|||
}
|
||||
|
||||
for (const request of this.model.getRequests()) {
|
||||
if (request.response?.response.value || request.response?.errorDetails) {
|
||||
if (request.response?.response.value || request.response?.result) {
|
||||
this.chatService.addCompleteRequest(widget.viewModel.sessionId,
|
||||
request.message as IParsedChatRequest,
|
||||
request.variableData,
|
||||
{
|
||||
message: request.response.response.value,
|
||||
errorDetails: request.response.errorDetails,
|
||||
result: request.response.result,
|
||||
followups: request.response.followups
|
||||
});
|
||||
} else if (request.message) {
|
||||
|
|
|
@ -455,7 +455,8 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
providerId: this.viewModel.providerId,
|
||||
sessionId: this.viewModel.sessionId,
|
||||
requestId: e.response.requestId,
|
||||
agentId: e.response?.agent?.id,
|
||||
agentId: e.response.agent?.id,
|
||||
result: e.response.result,
|
||||
action: {
|
||||
kind: 'followUp',
|
||||
followup: e.followup
|
||||
|
|
|
@ -31,7 +31,7 @@ export interface IChatAgentData {
|
|||
|
||||
export interface IChatAgent extends IChatAgentData {
|
||||
invoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult>;
|
||||
provideFollowups?(sessionId: string, token: CancellationToken): Promise<IChatFollowup[]>;
|
||||
provideFollowups?(result: IChatAgentResult, token: CancellationToken): Promise<IChatFollowup[]>;
|
||||
lastSlashCommands?: IChatAgentCommand[];
|
||||
provideSlashCommands(token: CancellationToken): Promise<IChatAgentCommand[]>;
|
||||
provideWelcomeMessage?(token: CancellationToken): ProviderResult<(string | IMarkdownString)[] | undefined>;
|
||||
|
@ -89,13 +89,13 @@ export interface IChatAgentRequest {
|
|||
}
|
||||
|
||||
export interface IChatAgentResult {
|
||||
// delete, keep while people are still using the previous API
|
||||
followUp?: IChatFollowup[];
|
||||
errorDetails?: IChatResponseErrorDetails;
|
||||
timings?: {
|
||||
firstProgress?: number;
|
||||
totalElapsed: number;
|
||||
};
|
||||
/** Extra properties that the agent can use to identify a result */
|
||||
readonly metadata?: { readonly [key: string]: any };
|
||||
}
|
||||
|
||||
export const IChatAgentService = createDecorator<IChatAgentService>('chatAgentService');
|
||||
|
@ -105,7 +105,7 @@ export interface IChatAgentService {
|
|||
readonly onDidChangeAgents: Event<void>;
|
||||
registerAgent(agent: IChatAgent): IDisposable;
|
||||
invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult>;
|
||||
getFollowups(id: string, sessionId: string, token: CancellationToken): Promise<IChatFollowup[]>;
|
||||
getFollowups(id: string, result: IChatAgentResult, token: CancellationToken): Promise<IChatFollowup[]>;
|
||||
getAgents(): Array<IChatAgent>;
|
||||
getAgent(id: string): IChatAgent | undefined;
|
||||
getDefaultAgent(): IChatAgent | undefined;
|
||||
|
@ -184,7 +184,7 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
|
|||
return await data.agent.invoke(request, progress, history, token);
|
||||
}
|
||||
|
||||
async getFollowups(id: string, sessionId: string, token: CancellationToken): Promise<IChatFollowup[]> {
|
||||
async getFollowups(id: string, result: IChatAgentResult, token: CancellationToken): Promise<IChatFollowup[]> {
|
||||
const data = this._agents.get(id);
|
||||
if (!data) {
|
||||
throw new Error(`No agent with id ${id}`);
|
||||
|
@ -194,6 +194,6 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
|
|||
return [];
|
||||
}
|
||||
|
||||
return data.agent.provideFollowups(sessionId, token);
|
||||
return data.agent.provideFollowups(result, token);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@ import { URI, UriComponents, UriDto } from 'vs/base/common/uri';
|
|||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IChat, IChatAgentMarkdownContentWithVulnerability, IChatContent, IChatContentInlineReference, IChatContentReference, IChatMarkdownContent, IChatProgress, IChatProgressMessage, IChatFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatTreeData, IChatUsedContext, InteractiveSessionVoteDirection, isIUsedContext, IChatCommandButton } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChat, IChatAgentMarkdownContentWithVulnerability, IChatCommandButton, IChatContent, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgress, IChatProgressMessage, IChatResponseProgressFileTreeData, IChatTreeData, IChatUsedContext, InteractiveSessionVoteDirection, isIUsedContext } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
|
||||
export interface IChatRequestVariableData {
|
||||
|
@ -73,7 +73,7 @@ export interface IChatResponseModel {
|
|||
readonly isStale: boolean;
|
||||
readonly vote: InteractiveSessionVoteDirection | undefined;
|
||||
readonly followups?: IChatFollowup[] | undefined;
|
||||
readonly errorDetails?: IChatResponseErrorDetails;
|
||||
readonly result?: IChatAgentResult;
|
||||
setVote(vote: InteractiveSessionVoteDirection): void;
|
||||
}
|
||||
|
||||
|
@ -227,8 +227,8 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
|
|||
return this._response;
|
||||
}
|
||||
|
||||
public get errorDetails(): IChatResponseErrorDetails | undefined {
|
||||
return this._errorDetails;
|
||||
public get result(): IChatAgentResult | undefined {
|
||||
return this._result;
|
||||
}
|
||||
|
||||
public get providerId(): string {
|
||||
|
@ -282,7 +282,7 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
|
|||
private _isComplete: boolean = false,
|
||||
private _isCanceled = false,
|
||||
private _vote?: InteractiveSessionVoteDirection,
|
||||
private _errorDetails?: IChatResponseErrorDetails,
|
||||
private _result?: IChatAgentResult,
|
||||
followups?: ReadonlyArray<IChatFollowup>
|
||||
) {
|
||||
super();
|
||||
|
@ -321,13 +321,13 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
|
|||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
setErrorDetails(errorDetails?: IChatResponseErrorDetails): void {
|
||||
this._errorDetails = errorDetails;
|
||||
setResult(result: IChatAgentResult): void {
|
||||
this._result = result;
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
complete(errorDetails?: IChatResponseErrorDetails): void {
|
||||
if (errorDetails?.responseIsRedacted) {
|
||||
complete(): void {
|
||||
if (this._result?.errorDetails?.responseIsRedacted) {
|
||||
this._response.clear();
|
||||
}
|
||||
|
||||
|
@ -380,7 +380,8 @@ export interface ISerializableChatRequestData {
|
|||
response: ReadonlyArray<IMarkdownString | IChatResponseProgressFileTreeData | IChatContentInlineReference | IChatAgentMarkdownContentWithVulnerability> | undefined;
|
||||
agent?: ISerializableChatAgentData;
|
||||
slashCommand?: IChatAgentCommand;
|
||||
responseErrorDetails: IChatResponseErrorDetails | undefined;
|
||||
// responseErrorDetails: IChatResponseErrorDetails | undefined;
|
||||
result?: IChatAgentResult; // Optional for backcompat
|
||||
followups: ReadonlyArray<IChatFollowup> | undefined;
|
||||
isCanceled: boolean | undefined;
|
||||
vote: InteractiveSessionVoteDirection | undefined;
|
||||
|
@ -561,13 +562,18 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
typeof raw.message === 'string'
|
||||
? this.getParsedRequestFromString(raw.message)
|
||||
: reviveParsedChatRequest(raw.message);
|
||||
|
||||
// Only old messages don't have variableData
|
||||
const variableData: IChatRequestVariableData = raw.variableData ?? { message: parsedRequest.text, variables: {} };
|
||||
const request = new ChatRequestModel(this, parsedRequest, variableData);
|
||||
if (raw.response || raw.responseErrorDetails) {
|
||||
if (raw.response || raw.result || (raw as any).responseErrorDetails) {
|
||||
const agent = (raw.agent && 'metadata' in raw.agent) ? // Check for the new format, ignore entries in the old format
|
||||
revive<ISerializableChatAgentData>(raw.agent) : undefined;
|
||||
request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, raw.slashCommand, request.id, true, raw.isCanceled, raw.vote, raw.responseErrorDetails, raw.followups);
|
||||
|
||||
// Port entries from old format
|
||||
const result = 'responseErrorDetails' in raw ?
|
||||
{ errorDetails: raw.responseErrorDetails } as IChatAgentResult : raw.result;
|
||||
request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, raw.slashCommand, request.id, true, raw.isCanceled, raw.vote, result, raw.followups);
|
||||
if (raw.usedContext) { // @ulugbekna: if this's a new vscode sessions, doc versions are incorrect anyway?
|
||||
request.response.applyReference(revive(raw.usedContext));
|
||||
}
|
||||
|
@ -700,7 +706,7 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
}
|
||||
}
|
||||
|
||||
setResponse(request: ChatRequestModel, rawResponse: IChatResponse): void {
|
||||
setResponse(request: ChatRequestModel, result: IChatAgentResult): void {
|
||||
if (!this._session) {
|
||||
throw new Error('completeResponse: No session');
|
||||
}
|
||||
|
@ -709,15 +715,15 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
request.response = new ChatResponseModel([], this, undefined, undefined, request.id);
|
||||
}
|
||||
|
||||
request.response.setErrorDetails(rawResponse.errorDetails);
|
||||
request.response.setResult(result);
|
||||
}
|
||||
|
||||
completeResponse(request: ChatRequestModel, errorDetails: IChatResponseErrorDetails | undefined): void {
|
||||
completeResponse(request: ChatRequestModel): void {
|
||||
if (!request.response) {
|
||||
throw new Error('Call setResponse before completeResponse');
|
||||
}
|
||||
|
||||
request.response.complete(errorDetails);
|
||||
request.response.complete();
|
||||
}
|
||||
|
||||
setFollowups(request: ChatRequestModel, followups: IChatFollowup[] | undefined): void {
|
||||
|
@ -748,8 +754,12 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
}
|
||||
}),
|
||||
requests: this._requests.map((r): ISerializableChatRequestData => {
|
||||
const message = {
|
||||
...r.message,
|
||||
parts: r.message.parts.map(p => p && 'toJSON' in p ? (p.toJSON as Function)() : p)
|
||||
};
|
||||
return {
|
||||
message: r.message,
|
||||
message,
|
||||
variableData: r.variableData,
|
||||
response: r.response ?
|
||||
r.response.response.value.map(item => {
|
||||
|
@ -763,7 +773,7 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
}
|
||||
})
|
||||
: undefined,
|
||||
responseErrorDetails: r.response?.errorDetails,
|
||||
result: r.response?.result,
|
||||
followups: r.response?.followups,
|
||||
isCanceled: r.response?.isCanceled,
|
||||
vote: r.response?.vote,
|
||||
|
|
|
@ -78,6 +78,21 @@ export class ChatRequestAgentPart implements IParsedChatRequestPart {
|
|||
get promptText(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't stringify all the agent methods, just data.
|
||||
*/
|
||||
toJSON(): any {
|
||||
return {
|
||||
kind: this.kind,
|
||||
range: this.range,
|
||||
editorRange: this.editorRange,
|
||||
agent: {
|
||||
id: this.agent.id,
|
||||
metadata: this.agent.metadata
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@ import { IRange, Range } from 'vs/editor/common/core/range';
|
|||
import { Command, Location, ProviderResult } from 'vs/editor/common/languages';
|
||||
import { FileType } from 'vs/platform/files/common/files';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IChatAgentCommand, IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatAgentCommand, IChatAgentData, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatModel, IChatModel, IChatRequestVariableData, ISerializableChatData } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
|
@ -40,15 +40,6 @@ export interface IChatResponseErrorDetails {
|
|||
responseIsRedacted?: boolean;
|
||||
}
|
||||
|
||||
export interface IChatResponse {
|
||||
session: IChat;
|
||||
errorDetails?: IChatResponseErrorDetails;
|
||||
timings?: {
|
||||
firstProgress?: number;
|
||||
totalElapsed: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IChatResponseProgressFileTreeData {
|
||||
label: string;
|
||||
uri: URI;
|
||||
|
@ -234,6 +225,7 @@ export interface IChatUserActionEvent {
|
|||
agentId: string | undefined;
|
||||
sessionId: string;
|
||||
requestId: string;
|
||||
result: IChatAgentResult | undefined;
|
||||
}
|
||||
|
||||
export interface IChatDynamicRequest {
|
||||
|
@ -250,7 +242,7 @@ export interface IChatDynamicRequest {
|
|||
|
||||
export interface IChatCompleteResponse {
|
||||
message: string | ReadonlyArray<IChatProgress>;
|
||||
errorDetails?: IChatResponseErrorDetails;
|
||||
result?: IChatAgentResult;
|
||||
followups?: IChatFollowup[];
|
||||
}
|
||||
|
||||
|
|
|
@ -20,13 +20,13 @@ import { Progress } from 'vs/platform/progress/common/progress';
|
|||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IChatAgentCommand, IChatAgentData, IChatAgentHistoryEntry, IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatAgentCommand, IChatAgentData, IChatAgentHistoryEntry, IChatAgentRequest, IChatAgentResult, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { ChatModel, ChatModelInitState, ChatRequestModel, ChatWelcomeMessageModel, IChatModel, IChatRequestVariableData, ISerializableChatData, ISerializableChatsData } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, IParsedChatRequest, getPromptText } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { ChatMessageRole, IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider';
|
||||
import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser';
|
||||
import { ChatAgentCopyKind, IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { ChatAgentCopyKind, IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatService, IChatTransferredSessionData, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
@ -516,7 +516,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
this._onDidSubmitAgent.fire({ agent: agentPart.agent, slashCommand: agentSlashCommandPart.command, sessionId: model.sessionId });
|
||||
}
|
||||
|
||||
let rawResponse: IChatResponse | null | undefined;
|
||||
let rawResult: IChatAgentResult | null | undefined;
|
||||
let agentOrCommandFollowups: Promise<IChatFollowup[] | undefined> | undefined = undefined;
|
||||
|
||||
const defaultAgent = this.chatAgentService.getDefaultAgent();
|
||||
|
@ -536,7 +536,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
variables: request.variableData.variables,
|
||||
command: request.response.slashCommand?.name
|
||||
};
|
||||
history.push({ request: historyRequest, response: request.response.response.value, result: { errorDetails: request.response.errorDetails } });
|
||||
history.push({ request: historyRequest, response: request.response.response.value, result: request.response.result ?? {} });
|
||||
}
|
||||
|
||||
const initVariableData: IChatRequestVariableData = { message: getPromptText(parsedRequest.parts), variables: {} };
|
||||
|
@ -554,13 +554,8 @@ export class ChatService extends Disposable implements IChatService {
|
|||
};
|
||||
|
||||
const agentResult = await this.chatAgentService.invokeAgent(agent.id, requestProps, progressCallback, history, token);
|
||||
rawResponse = {
|
||||
session: model.session!,
|
||||
errorDetails: agentResult.errorDetails,
|
||||
timings: agentResult.timings
|
||||
};
|
||||
agentOrCommandFollowups = agentResult?.followUp ? Promise.resolve(agentResult.followUp) :
|
||||
this.chatAgentService.getFollowups(agent.id, sessionId, CancellationToken.None);
|
||||
rawResult = agentResult;
|
||||
agentOrCommandFollowups = this.chatAgentService.getFollowups(agent.id, agentResult, CancellationToken.None);
|
||||
} else if (commandPart && this.chatSlashCommandService.hasCommand(commandPart.slashCommand.command)) {
|
||||
request = model.addRequest(parsedRequest, { message, variables: {} });
|
||||
// contributed slash commands
|
||||
|
@ -577,7 +572,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
progressCallback(p);
|
||||
}), history, token);
|
||||
agentOrCommandFollowups = Promise.resolve(commandResult?.followUp);
|
||||
rawResponse = { session: model.session! };
|
||||
rawResult = {};
|
||||
|
||||
} else {
|
||||
throw new Error(`Cannot handle request`);
|
||||
|
@ -586,36 +581,36 @@ export class ChatService extends Disposable implements IChatService {
|
|||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
} else {
|
||||
if (!rawResponse) {
|
||||
if (!rawResult) {
|
||||
this.trace('sendRequest', `Provider returned no response for session ${model.sessionId}`);
|
||||
rawResponse = { session: model.session!, errorDetails: { message: localize('emptyResponse', "Provider returned null response") } };
|
||||
rawResult = { errorDetails: { message: localize('emptyResponse', "Provider returned null response") } };
|
||||
}
|
||||
|
||||
const result = rawResponse.errorDetails?.responseIsFiltered ? 'filtered' :
|
||||
rawResponse.errorDetails && gotProgress ? 'errorWithOutput' :
|
||||
rawResponse.errorDetails ? 'error' :
|
||||
const result = rawResult.errorDetails?.responseIsFiltered ? 'filtered' :
|
||||
rawResult.errorDetails && gotProgress ? 'errorWithOutput' :
|
||||
rawResult.errorDetails ? 'error' :
|
||||
'success';
|
||||
this.telemetryService.publicLog2<ChatProviderInvokedEvent, ChatProviderInvokedClassification>('interactiveSessionProviderInvoked', {
|
||||
providerId: provider.id,
|
||||
timeToFirstProgress: rawResponse.timings?.firstProgress,
|
||||
totalTime: rawResponse.timings?.totalElapsed,
|
||||
timeToFirstProgress: rawResult.timings?.firstProgress,
|
||||
totalTime: rawResult.timings?.totalElapsed,
|
||||
result,
|
||||
requestType,
|
||||
agent: agentPart?.agent.id ?? '',
|
||||
slashCommand: agentSlashCommandPart ? agentSlashCommandPart.command.name : commandPart?.slashCommand.command,
|
||||
chatSessionId: model.sessionId
|
||||
});
|
||||
model.setResponse(request, rawResponse);
|
||||
model.setResponse(request, rawResult);
|
||||
this.trace('sendRequest', `Provider returned response for session ${model.sessionId}`);
|
||||
|
||||
// TODO refactor this or rethink the API https://github.com/microsoft/vscode-copilot/issues/593
|
||||
if (agentOrCommandFollowups) {
|
||||
agentOrCommandFollowups.then(followups => {
|
||||
model.setFollowups(request, followups);
|
||||
model.completeResponse(request, rawResponse?.errorDetails);
|
||||
model.completeResponse(request);
|
||||
});
|
||||
} else {
|
||||
model.completeResponse(request, rawResponse?.errorDetails);
|
||||
model.completeResponse(request);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
@ -674,14 +669,11 @@ export class ChatService extends Disposable implements IChatService {
|
|||
model.acceptResponseProgress(request, part, true);
|
||||
}
|
||||
}
|
||||
model.setResponse(request, {
|
||||
session: model.session!,
|
||||
errorDetails: response.errorDetails,
|
||||
});
|
||||
model.setResponse(request, response.result || {});
|
||||
if (response.followups !== undefined) {
|
||||
model.setFollowups(request, response.followups);
|
||||
}
|
||||
model.completeResponse(request, response.errorDetails);
|
||||
model.completeResponse(request);
|
||||
}
|
||||
|
||||
cancelCurrentRequestForSession(sessionId: string): void {
|
||||
|
|
|
@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IChatAgentCommand, IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatAgentCommand, IChatAgentData, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatModelInitState, IChatModel, IChatRequestModel, IChatResponseModel, IChatWelcomeMessageContent, IResponse } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IChatContentReference, IChatProgressMessage, IChatFollowup, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatUsedContext, InteractiveSessionVoteDirection, IChatCommandButton } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
|
@ -122,6 +122,7 @@ export interface IChatResponseViewModel {
|
|||
readonly vote: InteractiveSessionVoteDirection | undefined;
|
||||
readonly replyFollowups?: IChatFollowup[];
|
||||
readonly errorDetails?: IChatResponseErrorDetails;
|
||||
readonly result?: IChatAgentResult;
|
||||
readonly contentUpdateTimings?: IChatLiveUpdateData;
|
||||
renderData?: IChatResponseRenderData;
|
||||
agentAvatarHasBeenRendered?: boolean;
|
||||
|
@ -203,7 +204,7 @@ export class ChatViewModel extends Disposable implements IChatViewModel {
|
|||
if (typeof responseIdx === 'number' && responseIdx >= 0) {
|
||||
const items = this._items.splice(responseIdx, 1);
|
||||
const item = items[0];
|
||||
if (isResponseVM(item)) {
|
||||
if (item instanceof ChatResponseViewModel) {
|
||||
item.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -334,8 +335,12 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi
|
|||
return this._model.followups?.filter((f): f is IChatFollowup => f.kind === 'reply');
|
||||
}
|
||||
|
||||
get errorDetails() {
|
||||
return this._model.errorDetails;
|
||||
get result() {
|
||||
return this._model.result;
|
||||
}
|
||||
|
||||
get errorDetails(): IChatResponseErrorDetails | undefined {
|
||||
return this.result?.errorDetails;
|
||||
}
|
||||
|
||||
get vote() {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
text: "@ChatProviderWithUsedContext test request",
|
||||
parts: [
|
||||
{
|
||||
kind: "agent",
|
||||
range: {
|
||||
start: 0,
|
||||
endExclusive: 28
|
||||
|
@ -22,11 +23,8 @@
|
|||
},
|
||||
agent: {
|
||||
id: "ChatProviderWithUsedContext",
|
||||
metadata: { },
|
||||
provideSlashCommands: [Function provideSlashCommands],
|
||||
invoke: [Function invoke]
|
||||
},
|
||||
kind: "agent"
|
||||
metadata: { }
|
||||
}
|
||||
},
|
||||
{
|
||||
range: {
|
||||
|
@ -49,8 +47,8 @@
|
|||
variables: { }
|
||||
},
|
||||
response: [ ],
|
||||
responseErrorDetails: undefined,
|
||||
followups: [ ],
|
||||
result: { metadata: { metadataKey: "value" } },
|
||||
followups: undefined,
|
||||
isCanceled: false,
|
||||
vote: undefined,
|
||||
agent: {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
message: {
|
||||
parts: [
|
||||
{
|
||||
kind: "agent",
|
||||
range: {
|
||||
start: 0,
|
||||
endExclusive: 28
|
||||
|
@ -21,11 +22,8 @@
|
|||
},
|
||||
agent: {
|
||||
id: "ChatProviderWithUsedContext",
|
||||
metadata: { },
|
||||
provideSlashCommands: [Function provideSlashCommands],
|
||||
invoke: [Function invoke]
|
||||
},
|
||||
kind: "agent"
|
||||
metadata: { }
|
||||
}
|
||||
},
|
||||
{
|
||||
range: {
|
||||
|
@ -49,7 +47,7 @@
|
|||
variables: { }
|
||||
},
|
||||
response: [ ],
|
||||
responseErrorDetails: undefined,
|
||||
result: { metadata: { metadataKey: "value" } },
|
||||
followups: undefined,
|
||||
isCanceled: false,
|
||||
vote: undefined,
|
||||
|
|
|
@ -75,7 +75,10 @@ const chatAgentWithUsedContext: IChatAgent = {
|
|||
kind: 'usedContext'
|
||||
});
|
||||
|
||||
return {};
|
||||
return { metadata: { metadataKey: 'value' } };
|
||||
},
|
||||
async provideFollowups(sessionId, token) {
|
||||
return [{ kind: 'reply', message: 'Something else', tooltip: 'a tooltip' }];
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -110,6 +113,9 @@ suite('Chat', () => {
|
|||
async invoke(request, progress, history, token) {
|
||||
return {};
|
||||
},
|
||||
async provideSlashCommands(token) {
|
||||
return [];
|
||||
},
|
||||
} as IChatAgent;
|
||||
testDisposables.add(chatAgentService.registerAgent(agent));
|
||||
});
|
||||
|
@ -224,6 +230,7 @@ suite('Chat', () => {
|
|||
|
||||
const response = await testService.sendRequest(model.sessionId, `@${chatAgentWithUsedContextId} test request`);
|
||||
assert(response);
|
||||
await response.responseCompletePromise;
|
||||
|
||||
assert.strictEqual(model.getRequests().length, 1);
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ suite('VoiceChat', () => {
|
|||
readonly onDidChangeAgents = Event.None;
|
||||
registerAgent(agent: IChatAgent): IDisposable { throw new Error(); }
|
||||
invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult> { throw new Error(); }
|
||||
getFollowups(id: string, sessionId: string, token: CancellationToken): Promise<IChatFollowup[]> { throw new Error(); }
|
||||
getFollowups(id: string, result: IChatAgentResult, token: CancellationToken): Promise<IChatFollowup[]> { throw new Error(); }
|
||||
getAgents(): Array<IChatAgent> { return agents; }
|
||||
getAgent(id: string): IChatAgent | undefined { throw new Error(); }
|
||||
getDefaultAgent(): IChatAgent | undefined { throw new Error(); }
|
||||
|
|
23
src/vscode-dts/vscode.proposed.chatAgents2.d.ts
vendored
23
src/vscode-dts/vscode.proposed.chatAgents2.d.ts
vendored
|
@ -86,9 +86,10 @@ declare module 'vscode' {
|
|||
*/
|
||||
errorDetails?: ChatAgentErrorDetails;
|
||||
|
||||
// TODO@API
|
||||
// add CATCH-all signature [name:string]: string|boolean|number instead of `T extends...`
|
||||
// readonly metadata: { readonly [key: string]: any };
|
||||
/**
|
||||
* Arbitrary metadata for this result. Can be anything but must be JSON-stringifyable.
|
||||
*/
|
||||
readonly metadata?: { readonly [key: string]: any };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,12 +110,12 @@ declare module 'vscode' {
|
|||
/**
|
||||
* Represents user feedback for a result.
|
||||
*/
|
||||
export interface ChatAgentResult2Feedback<TResult extends ChatAgentResult2> {
|
||||
export interface ChatAgentResult2Feedback {
|
||||
/**
|
||||
* This instance of ChatAgentResult2 is the same instance that was returned from the chat agent,
|
||||
* and it can be extended with arbitrary properties if needed.
|
||||
*/
|
||||
readonly result: TResult;
|
||||
readonly result: ChatAgentResult2;
|
||||
|
||||
/**
|
||||
* The kind of feedback that was received.
|
||||
|
@ -196,16 +197,16 @@ declare module 'vscode' {
|
|||
/**
|
||||
* Will be invoked once after each request to get suggested followup questions to show the user. The user can click the followup to send it to the chat.
|
||||
*/
|
||||
export interface ChatAgentFollowupProvider<TResult extends ChatAgentResult2> {
|
||||
export interface ChatAgentFollowupProvider {
|
||||
/**
|
||||
*
|
||||
* @param result The same instance of the result object that was returned by the chat agent, and it can be extended with arbitrary properties if needed.
|
||||
* @param token A cancellation token.
|
||||
*/
|
||||
provideFollowups(result: TResult, token: CancellationToken): ProviderResult<ChatAgentFollowup[]>;
|
||||
provideFollowups(result: ChatAgentResult2, token: CancellationToken): ProviderResult<ChatAgentFollowup[]>;
|
||||
}
|
||||
|
||||
export interface ChatAgent2<TResult extends ChatAgentResult2> {
|
||||
export interface ChatAgent2 {
|
||||
|
||||
/**
|
||||
* The short name by which this agent is referred to in the UI, e.g `workspace`.
|
||||
|
@ -244,7 +245,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* This provider will be called once after each request to retrieve suggested followup questions.
|
||||
*/
|
||||
followupProvider?: ChatAgentFollowupProvider<TResult>;
|
||||
followupProvider?: ChatAgentFollowupProvider;
|
||||
|
||||
|
||||
// TODO@API
|
||||
|
@ -268,7 +269,7 @@ declare module 'vscode' {
|
|||
* The passed {@link ChatAgentResult2Feedback.result result} is guaranteed to be the same instance that was
|
||||
* previously returned from this chat agent.
|
||||
*/
|
||||
onDidReceiveFeedback: Event<ChatAgentResult2Feedback<TResult>>;
|
||||
onDidReceiveFeedback: Event<ChatAgentResult2Feedback>;
|
||||
|
||||
/**
|
||||
* Dispose this agent and free resources
|
||||
|
@ -452,7 +453,7 @@ declare module 'vscode' {
|
|||
* @param handler The reply-handler of the agent.
|
||||
* @returns A new chat agent
|
||||
*/
|
||||
export function createChatAgent<TResult extends ChatAgentResult2>(name: string, handler: ChatAgentHandler): ChatAgent2<TResult>;
|
||||
export function createChatAgent(name: string, handler: ChatAgentHandler): ChatAgent2;
|
||||
|
||||
/**
|
||||
* Register a variable which can be used in a chat request to any agent.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
declare module 'vscode' {
|
||||
|
||||
export interface ChatAgent2<TResult extends ChatAgentResult2> {
|
||||
export interface ChatAgent2 {
|
||||
onDidPerformAction: Event<ChatAgentUserActionEvent>;
|
||||
supportIssueReporting?: boolean;
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ declare module 'vscode' {
|
|||
report(value: ChatAgentExtendedProgress): void;
|
||||
};
|
||||
|
||||
export interface ChatAgent2<TResult extends ChatAgentResult2> {
|
||||
export interface ChatAgent2 {
|
||||
/**
|
||||
* Provide a set of variables that can only be used with this agent.
|
||||
*/
|
||||
|
@ -179,7 +179,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* Create a chat agent with the extended progress type
|
||||
*/
|
||||
export function createChatAgent<TResult extends ChatAgentResult2>(name: string, handler: ChatAgentExtendedHandler): ChatAgent2<TResult>;
|
||||
export function createChatAgent(name: string, handler: ChatAgentExtendedHandler): ChatAgent2;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -12,7 +12,7 @@ declare module 'vscode' {
|
|||
provideSampleQuestions?(token: CancellationToken): ProviderResult<ChatAgentFollowup[]>;
|
||||
}
|
||||
|
||||
export interface ChatAgent2<TResult extends ChatAgentResult2> {
|
||||
export interface ChatAgent2 {
|
||||
/**
|
||||
* When true, this agent is invoked by default when no other agent is being invoked
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue