mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 13:00:58 +00:00
Chat code cleanup (#198022)
* Clean up some obsolete chat API Start deleting the interactive session provider, use agents only * Delete old chat agents API * Remove providerRequestId * Remove unused stuff from interactive.d.ts * Get rid of chat session state saving * Fix test * I guess this type was in use
This commit is contained in:
parent
bdc113ffe1
commit
7cbff1919e
|
@ -5,7 +5,7 @@
|
|||
|
||||
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 { CancellationToken, chat, ChatAgentRequest, ChatVariableLevel, Disposable, interactive, InteractiveSession, ProviderResult } from 'vscode';
|
||||
import { assertNoRpc, closeAllEditors, DeferredPromise, disposeAll } from '../utils';
|
||||
|
||||
suite('chat', () => {
|
||||
|
@ -22,24 +22,12 @@ suite('chat', () => {
|
|||
|
||||
function getDeferredForRequest(): DeferredPromise<ChatAgentRequest> {
|
||||
disposables.push(interactive.registerInteractiveSessionProvider('provider', {
|
||||
prepareSession: (_initialState: InteractiveSessionState | undefined, _token: CancellationToken): ProviderResult<InteractiveSession> => {
|
||||
prepareSession: (_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>();
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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');
|
||||
});
|
||||
});
|
|
@ -781,6 +781,10 @@ export class DisposableMap<K, V extends IDisposable = IDisposable> implements ID
|
|||
return this._store.keys();
|
||||
}
|
||||
|
||||
values(): IterableIterator<V> {
|
||||
return this._store.values();
|
||||
}
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[K, V]> {
|
||||
return this._store[Symbol.iterator]();
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import { StatusBarItemsExtensionPoint } from 'vs/workbench/api/browser/statusBar
|
|||
import './mainThreadLocalization';
|
||||
import './mainThreadBulkEdits';
|
||||
import './mainThreadChatProvider';
|
||||
import './mainThreadChatAgents';
|
||||
import './mainThreadChatAgents2';
|
||||
import './mainThreadChatVariables';
|
||||
import './mainThreadCodeInsets';
|
||||
|
|
|
@ -3,31 +3,23 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DeferredPromise } from 'vs/base/common/async';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Disposable, DisposableMap } from 'vs/base/common/lifecycle';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ExtHostChatShape, ExtHostContext, IChatRequestDto, IChatResponseProgressDto, ILocationDto, MainContext, MainThreadChatShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostChatShape, ExtHostContext, MainContext, MainThreadChatShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
|
||||
import { isCompleteInteractiveProgressTreeData } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IChat, IChatDynamicRequest, IChatProgress, IChatResponse, IChatResponseProgressFileTreeData, IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatDynamicRequest, IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadChat)
|
||||
export class MainThreadChat extends Disposable implements MainThreadChatShape {
|
||||
|
||||
private readonly _providerRegistrations = this._register(new DisposableMap<number>());
|
||||
private readonly _activeRequestProgressCallbacks = new Map<string, (progress: IChatProgress) => (DeferredPromise<string | IMarkdownString> | void)>();
|
||||
private readonly _stateEmitters = new Map<number, Emitter<any>>();
|
||||
|
||||
private readonly _proxy: ExtHostChatShape;
|
||||
|
||||
private _responsePartHandlePool = 0;
|
||||
private readonly _activeResponsePartPromises = new Map<string, DeferredPromise<string | IMarkdownString | { treeData: IChatResponseProgressFileTreeData }>>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IChatService private readonly _chatService: IChatService,
|
||||
|
@ -64,8 +56,8 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape {
|
|||
const unreg = this._chatService.registerProvider({
|
||||
id,
|
||||
displayName: registration.label,
|
||||
prepareSession: async (initialState, token) => {
|
||||
const session = await this._proxy.$prepareChat(handle, initialState, token);
|
||||
prepareSession: async (token) => {
|
||||
const session = await this._proxy.$prepareChat(handle, token);
|
||||
if (!session) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -75,14 +67,13 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape {
|
|||
|
||||
const emitter = new Emitter<any>();
|
||||
this._stateEmitters.set(session.id, emitter);
|
||||
return <IChat>{
|
||||
return {
|
||||
id: session.id,
|
||||
requesterUsername: session.requesterUsername,
|
||||
requesterAvatarIconUri: URI.revive(session.requesterAvatarIconUri),
|
||||
responderUsername: session.responderUsername,
|
||||
responderAvatarIconUri,
|
||||
inputPlaceholder: session.inputPlaceholder,
|
||||
onDidChangeState: emitter.event,
|
||||
dispose: () => {
|
||||
emitter.dispose();
|
||||
this._stateEmitters.delete(session.id);
|
||||
|
@ -90,87 +81,17 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape {
|
|||
}
|
||||
};
|
||||
},
|
||||
provideReply: async (request, progress, token) => {
|
||||
const id = `${handle}_${request.session.id}`;
|
||||
this._activeRequestProgressCallbacks.set(id, progress);
|
||||
try {
|
||||
const requestDto: IChatRequestDto = {
|
||||
message: request.message,
|
||||
variables: request.variables
|
||||
};
|
||||
const dto = await this._proxy.$provideReply(handle, request.session.id, requestDto, token);
|
||||
return <IChatResponse>{
|
||||
session: request.session,
|
||||
...dto
|
||||
};
|
||||
} finally {
|
||||
this._activeRequestProgressCallbacks.delete(id);
|
||||
}
|
||||
},
|
||||
provideWelcomeMessage: (token) => {
|
||||
return this._proxy.$provideWelcomeMessage(handle, token);
|
||||
},
|
||||
provideSampleQuestions: (token) => {
|
||||
return this._proxy.$provideSampleQuestions(handle, token);
|
||||
},
|
||||
provideSlashCommands: (session, token) => {
|
||||
return this._proxy.$provideSlashCommands(handle, session.id, token);
|
||||
},
|
||||
provideFollowups: (session, token) => {
|
||||
return this._proxy.$provideFollowups(handle, session.id, token);
|
||||
},
|
||||
removeRequest: (session, requestId) => {
|
||||
return this._proxy.$removeRequest(handle, session.id, requestId);
|
||||
}
|
||||
});
|
||||
|
||||
this._providerRegistrations.set(handle, unreg);
|
||||
}
|
||||
|
||||
async $acceptResponseProgress(handle: number, sessionId: number, progress: IChatResponseProgressDto, responsePartHandle?: number): Promise<number | void> {
|
||||
const id = `${handle}_${sessionId}`;
|
||||
|
||||
if ('placeholder' in progress) {
|
||||
const responsePartId = `${id}_${++this._responsePartHandlePool}`;
|
||||
const deferredContentPromise = new DeferredPromise<string | IMarkdownString | { treeData: IChatResponseProgressFileTreeData }>();
|
||||
this._activeResponsePartPromises.set(responsePartId, deferredContentPromise);
|
||||
this._activeRequestProgressCallbacks.get(id)?.({ ...progress, resolvedContent: deferredContentPromise.p });
|
||||
return this._responsePartHandlePool;
|
||||
} else if (responsePartHandle) {
|
||||
// Complete an existing deferred promise with resolved content
|
||||
const responsePartId = `${id}_${responsePartHandle}`;
|
||||
const deferredContentPromise = this._activeResponsePartPromises.get(responsePartId);
|
||||
if (deferredContentPromise && isCompleteInteractiveProgressTreeData(progress)) {
|
||||
const withRevivedUris = revive<{ treeData: IChatResponseProgressFileTreeData }>(progress);
|
||||
deferredContentPromise.complete(withRevivedUris);
|
||||
this._activeResponsePartPromises.delete(responsePartId);
|
||||
} else if (deferredContentPromise && 'content' in progress) {
|
||||
deferredContentPromise.complete(progress.content);
|
||||
this._activeResponsePartPromises.delete(responsePartId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// No need to support standalone tree data that's not attached to a placeholder in API
|
||||
if (isCompleteInteractiveProgressTreeData(progress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TS won't let us change the type of `progress`
|
||||
let revivedProgress: IChatProgress;
|
||||
if ('documents' in progress) {
|
||||
revivedProgress = { documents: revive(progress.documents) };
|
||||
} else if ('reference' in progress) {
|
||||
revivedProgress = revive<{ reference: UriComponents | ILocationDto }>(progress);
|
||||
} else if ('inlineReference' in progress) {
|
||||
revivedProgress = revive<{ inlineReference: UriComponents | ILocationDto; name?: string }>(progress);
|
||||
} else {
|
||||
revivedProgress = progress;
|
||||
}
|
||||
|
||||
this._activeRequestProgressCallbacks.get(id)?.(revivedProgress);
|
||||
}
|
||||
|
||||
async $acceptChatState(sessionId: number, state: any): Promise<void> {
|
||||
this._stateEmitters.get(sessionId)?.fire(state);
|
||||
}
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DisposableMap } from 'vs/base/common/lifecycle';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { ExtHostChatAgentsShape, ExtHostContext, MainContext, MainThreadChatAgentsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IChatAgentCommand, IChatAgentMetadata, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers';
|
||||
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadChatAgents)
|
||||
export class MainThreadChatAgents implements MainThreadChatAgentsShape {
|
||||
|
||||
private readonly _agents = new DisposableMap<number>;
|
||||
private readonly _pendingProgress = new Map<number, IProgress<IChatProgress>>();
|
||||
private readonly _proxy: ExtHostChatAgentsShape;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IChatAgentService private readonly _chatAgentService: IChatAgentService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatAgents);
|
||||
}
|
||||
|
||||
$unregisterAgent(handle: number): void {
|
||||
this._agents.deleteAndDispose(handle);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._agents.clearAndDisposeAll();
|
||||
}
|
||||
|
||||
$registerAgent(handle: number, name: string, metadata: IChatAgentMetadata & { subCommands: IChatAgentCommand[] }): void {
|
||||
const d = this._chatAgentService.registerAgent({
|
||||
id: name,
|
||||
metadata: revive(metadata),
|
||||
invoke: async (request, progress, history, token) => {
|
||||
const requestId = Math.random();
|
||||
this._pendingProgress.set(requestId, { report: progress });
|
||||
try {
|
||||
const message = request.command ? `/${request.command} ${request.message}` : request.message;
|
||||
const result = await this._proxy.$invokeAgent(handle, requestId, message, { history }, token);
|
||||
return {
|
||||
followUp: result?.followUp ?? [],
|
||||
};
|
||||
} finally {
|
||||
this._pendingProgress.delete(requestId);
|
||||
}
|
||||
},
|
||||
async provideSlashCommands() {
|
||||
return metadata.subCommands;
|
||||
},
|
||||
});
|
||||
this._agents.set(handle, d);
|
||||
}
|
||||
|
||||
async $handleProgressChunk(requestId: number, chunk: IChatSlashFragment): Promise<void> {
|
||||
// An extra step because TS really struggles with type inference in the Revived generic parameter?
|
||||
const revived = revive<IChatSlashFragment>(chunk);
|
||||
if (typeof revived.content === 'string') {
|
||||
this._pendingProgress.get(requestId)?.report({ content: revived.content });
|
||||
} else {
|
||||
this._pendingProgress.get(requestId)?.report(revived.content);
|
||||
}
|
||||
}
|
||||
|
||||
$unregisterCommand(handle: number): void {
|
||||
this._agents.deleteAndDispose(handle);
|
||||
}
|
||||
}
|
|
@ -6,110 +6,109 @@
|
|||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
|
||||
import { OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import * as languageConfiguration from 'vs/editor/common/languages/languageConfiguration';
|
||||
import { score } from 'vs/editor/common/languageSelector';
|
||||
import * as languageConfiguration from 'vs/editor/common/languages/languageConfiguration';
|
||||
import { OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import { ExtensionIdentifierSet, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import * as files from 'vs/platform/files/common/files';
|
||||
import { ExtHostContext, MainContext, CandidatePortSource, ExtHostLogLevelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService, ILoggerService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { matchesScheme } from 'vs/platform/opener/common/opener';
|
||||
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { TelemetryTrustedValue } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { EditSessionIdentityMatch } from 'vs/platform/workspace/common/editSessions';
|
||||
import { CandidatePortSource, ExtHostContext, ExtHostLogLevelServiceShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostRelatedInformation } from 'vs/workbench/api/common/extHostAiRelatedInformation';
|
||||
import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
|
||||
import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits';
|
||||
import { ExtHostChat } from 'vs/workbench/api/common/extHostChat';
|
||||
import { ExtHostChatAgents2 } from 'vs/workbench/api/common/extHostChatAgents2';
|
||||
import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider';
|
||||
import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariables';
|
||||
import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard';
|
||||
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
|
||||
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { createExtHostComments } from 'vs/workbench/api/common/extHostComments';
|
||||
import { ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors';
|
||||
import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { IExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations';
|
||||
import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics';
|
||||
import { ExtHostDialogs } from 'vs/workbench/api/common/extHostDialogs';
|
||||
import { ExtHostDocumentContentProvider } from 'vs/workbench/api/common/extHostDocumentContentProviders';
|
||||
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import { ExtHostAiEmbeddingVector } from 'vs/workbench/api/common/extHostEmbeddingVector';
|
||||
import { Extension, IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem';
|
||||
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
|
||||
import { ExtHostFileSystemEventService, FileSystemWatcherCreateOptions } from 'vs/workbench/api/common/extHostFileSystemEventService';
|
||||
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtHostInteractiveEditor } from 'vs/workbench/api/common/extHostInlineChat';
|
||||
import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive';
|
||||
import { ExtHostIssueReporter } from 'vs/workbench/api/common/extHostIssueReporter';
|
||||
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
|
||||
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
|
||||
import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages';
|
||||
import { IExtHostLocalizationService } from 'vs/workbench/api/common/extHostLocalizationService';
|
||||
import { IExtHostManagedSockets } from 'vs/workbench/api/common/extHostManagedSockets';
|
||||
import { ExtHostMessageService } from 'vs/workbench/api/common/extHostMessageService';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { ExtHostNotebookDocumentSaveParticipant } from 'vs/workbench/api/common/extHostNotebookDocumentSaveParticipant';
|
||||
import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments';
|
||||
import { ExtHostNotebookEditors } from 'vs/workbench/api/common/extHostNotebookEditors';
|
||||
import { ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels';
|
||||
import { ExtHostNotebookRenderers } from 'vs/workbench/api/common/extHostNotebookRenderers';
|
||||
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
|
||||
import { ExtHostProfileContentHandlers } from 'vs/workbench/api/common/extHostProfileContentHandler';
|
||||
import { ExtHostProgress } from 'vs/workbench/api/common/extHostProgress';
|
||||
import { ExtHostQuickDiff } from 'vs/workbench/api/common/extHostQuickDiff';
|
||||
import { createExtHostQuickOpen } from 'vs/workbench/api/common/extHostQuickOpen';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { ExtHostSCM } from 'vs/workbench/api/common/extHostSCM';
|
||||
import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch';
|
||||
import { IExtHostSecretState } from 'vs/workbench/api/common/extHostSecretState';
|
||||
import { ExtHostShare } from 'vs/workbench/api/common/extHostShare';
|
||||
import { ExtHostSpeech } from 'vs/workbench/api/common/extHostSpeech';
|
||||
import { ExtHostStatusBar } from 'vs/workbench/api/common/extHostStatusBar';
|
||||
import { IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { IExtHostTask } from 'vs/workbench/api/common/extHostTask';
|
||||
import { ExtHostTelemetryLogger, IExtHostTelemetry, isNewAppInstall } from 'vs/workbench/api/common/extHostTelemetry';
|
||||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
|
||||
import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors';
|
||||
import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
|
||||
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
|
||||
import { ExtHostTreeViews } from 'vs/workbench/api/common/extHostTreeViews';
|
||||
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { TelemetryTrustedValue } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
|
||||
import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
|
||||
import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls';
|
||||
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
|
||||
import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView';
|
||||
import { IExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ExtensionIdentifierSet, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
|
||||
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
|
||||
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations';
|
||||
import { IExtHostTask } from 'vs/workbench/api/common/extHostTask';
|
||||
import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch';
|
||||
import { ILoggerService, ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
|
||||
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
|
||||
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
|
||||
import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView';
|
||||
import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors';
|
||||
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
|
||||
import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits';
|
||||
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
|
||||
import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
|
||||
import { IExtHostSecretState } from 'vs/workbench/api/common/extHostSecretState';
|
||||
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import { ExtHostTelemetryLogger, IExtHostTelemetry, isNewAppInstall } from 'vs/workbench/api/common/extHostTelemetry';
|
||||
import { ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels';
|
||||
import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes';
|
||||
import { ExtHostNotebookRenderers } from 'vs/workbench/api/common/extHostNotebookRenderers';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { matchesScheme } from 'vs/platform/opener/common/opener';
|
||||
import { ExtHostNotebookEditors } from 'vs/workbench/api/common/extHostNotebookEditors';
|
||||
import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments';
|
||||
import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive';
|
||||
import { combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IExtHostLocalizationService } from 'vs/workbench/api/common/extHostLocalizationService';
|
||||
import { EditSessionIdentityMatch } from 'vs/platform/workspace/common/editSessions';
|
||||
import { ExtHostProfileContentHandlers } from 'vs/workbench/api/common/extHostProfileContentHandler';
|
||||
import { ExtHostQuickDiff } from 'vs/workbench/api/common/extHostQuickDiff';
|
||||
import { ExtHostChat } from 'vs/workbench/api/common/extHostChat';
|
||||
import { ExtHostInteractiveEditor } from 'vs/workbench/api/common/extHostInlineChat';
|
||||
import { ExtHostNotebookDocumentSaveParticipant } from 'vs/workbench/api/common/extHostNotebookDocumentSaveParticipant';
|
||||
import { ExtHostIssueReporter } from 'vs/workbench/api/common/extHostIssueReporter';
|
||||
import { IExtHostManagedSockets } from 'vs/workbench/api/common/extHostManagedSockets';
|
||||
import { ExtHostShare } from 'vs/workbench/api/common/extHostShare';
|
||||
import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider';
|
||||
import { ExtHostSpeech } from 'vs/workbench/api/common/extHostSpeech';
|
||||
import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariables';
|
||||
import { ExtHostRelatedInformation } from 'vs/workbench/api/common/extHostAiRelatedInformation';
|
||||
import { ExtHostAiEmbeddingVector } from 'vs/workbench/api/common/extHostEmbeddingVector';
|
||||
import { ExtHostChatAgents } from 'vs/workbench/api/common/extHostChatAgents';
|
||||
import { ExtHostChatAgents2 } from 'vs/workbench/api/common/extHostChatAgents2';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import { UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
|
||||
import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
export interface IExtensionRegistries {
|
||||
mine: ExtensionDescriptionRegistry;
|
||||
|
@ -210,10 +209,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService));
|
||||
const extHostInteractiveEditor = rpcProtocol.set(ExtHostContext.ExtHostInlineChat, new ExtHostInteractiveEditor(rpcProtocol, extHostCommands, extHostDocuments, extHostLogService));
|
||||
const extHostChatProvider = rpcProtocol.set(ExtHostContext.ExtHostChatProvider, new ExtHostChatProvider(rpcProtocol, extHostLogService));
|
||||
const extHostChatAgents = rpcProtocol.set(ExtHostContext.ExtHostChatAgents, new ExtHostChatAgents(rpcProtocol, extHostChatProvider, extHostLogService));
|
||||
const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostChatProvider, extHostLogService));
|
||||
const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol));
|
||||
const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol, extHostLogService));
|
||||
const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol));
|
||||
const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol));
|
||||
const extHostAiEmbeddingVector = rpcProtocol.set(ExtHostContext.ExtHostAiEmbeddingVector, new ExtHostAiEmbeddingVector(rpcProtocol));
|
||||
const extHostIssueReporter = rpcProtocol.set(ExtHostContext.ExtHostIssueReporter, new ExtHostIssueReporter(rpcProtocol));
|
||||
|
@ -1382,10 +1380,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
checkProposedApiEnabled(extension, 'chatAgents2');
|
||||
return extHostChatAgents2.createChatAgent(extension, name, handler);
|
||||
},
|
||||
registerAgent(name: string, agent: vscode.ChatAgent, metadata: vscode.ChatAgentMetadata) {
|
||||
checkProposedApiEnabled(extension, 'chatAgents');
|
||||
return extHostChatAgents.registerAgent(extension.identifier, name, agent, metadata);
|
||||
}
|
||||
};
|
||||
|
||||
// namespace: speech
|
||||
|
|
|
@ -52,8 +52,7 @@ import { IRevealOptions, ITreeItem, IViewBadge } from 'vs/workbench/common/views
|
|||
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { IChatAgentCommand, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider';
|
||||
import { IChatAgentDetection, IChatDynamicRequest, IChatFollowup, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, ISlashCommand, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChatAgentDetection, IChatDynamicRequest, IChatFollowup, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatMessageResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
|
@ -63,6 +62,7 @@ import { ICellExecutionComplete, ICellExecutionStateUpdate } from 'vs/workbench/
|
|||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { IWorkspaceSymbol, NotebookPriorityInfo } from 'vs/workbench/contrib/search/common/search';
|
||||
import { IRawClosedNotebookFileMatch } from 'vs/workbench/contrib/search/common/searchNotebookHelpers';
|
||||
import { ISpeechProviderMetadata, ISpeechToTextEvent } from 'vs/workbench/contrib/speech/common/speechService';
|
||||
import { CoverageDetails, ExtensionRunTestsRequest, ICallProfileRunHandler, IFileCoverage, ISerializedTestResults, IStartControllerTests, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, TestResultState, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
|
@ -80,7 +80,6 @@ import { CandidatePort } from 'vs/workbench/services/remote/common/tunnelModel';
|
|||
import { ITextQueryBuilderOptions } from 'vs/workbench/services/search/common/queryBuilder';
|
||||
import * as search from 'vs/workbench/services/search/common/search';
|
||||
import { ISaveProfileResult } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
|
||||
import { IRawClosedNotebookFileMatch } from 'vs/workbench/contrib/search/common/searchNotebookHelpers';
|
||||
|
||||
export interface IWorkspaceData extends IStaticWorkspaceData {
|
||||
folders: { uri: UriComponents; name: string; index: number }[];
|
||||
|
@ -1166,12 +1165,6 @@ export interface ExtHostChatProviderShape {
|
|||
$handleResponseFragment(requestId: number, chunk: IChatResponseFragment): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadChatAgentsShape extends IDisposable {
|
||||
$registerAgent(handle: number, name: string, metadata: IChatAgentMetadata & { subCommands: IChatAgentCommand[] }): void;
|
||||
$unregisterAgent(handle: number): void;
|
||||
$handleProgressChunk(requestId: number, chunk: IChatSlashFragment): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IExtensionChatAgentMetadata extends Dto<IChatAgentMetadata> {
|
||||
hasSlashCommands?: boolean;
|
||||
hasFollowup?: boolean;
|
||||
|
@ -1184,10 +1177,6 @@ export interface MainThreadChatAgentsShape2 extends IDisposable {
|
|||
$handleProgressChunk(requestId: string, chunk: IChatResponseProgressDto, responsePartHandle?: number): Promise<number | void>;
|
||||
}
|
||||
|
||||
export interface ExtHostChatAgentsShape {
|
||||
$invokeAgent(handle: number, requestId: number, prompt: string, context: { history: IChatMessage[] }, token: CancellationToken): Promise<any>;
|
||||
}
|
||||
|
||||
export interface ExtHostChatAgentsShape2 {
|
||||
$invokeAgent(handle: number, sessionId: string, requestId: string, request: IChatAgentRequest, context: { history: IChatMessage[] }, token: CancellationToken): Promise<IChatAgentResult | undefined>;
|
||||
$provideSlashCommands(handle: number, token: CancellationToken): Promise<IChatAgentCommand[]>;
|
||||
|
@ -1263,7 +1252,6 @@ export type IDocumentContextDto = {
|
|||
|
||||
export type IChatResponseProgressDto =
|
||||
| { content: string | IMarkdownString }
|
||||
| { requestId: string }
|
||||
| { placeholder: string }
|
||||
| { treeData: IChatResponseProgressFileTreeData }
|
||||
| { documents: IDocumentContextDto[] }
|
||||
|
@ -1276,18 +1264,13 @@ export interface MainThreadChatShape extends IDisposable {
|
|||
$acceptChatState(sessionId: number, state: any): Promise<void>;
|
||||
$sendRequestToProvider(providerId: string, message: IChatDynamicRequest): void;
|
||||
$unregisterChatProvider(handle: number): Promise<void>;
|
||||
$acceptResponseProgress(handle: number, sessionId: number, progress: IChatResponseProgressDto, responsePartHandle?: number): Promise<number | void>;
|
||||
$transferChatSession(sessionId: number, toWorkspace: UriComponents): void;
|
||||
}
|
||||
|
||||
export interface ExtHostChatShape {
|
||||
$prepareChat(handle: number, initialState: any, token: CancellationToken): Promise<IChatDto | undefined>;
|
||||
$prepareChat(handle: number, token: CancellationToken): Promise<IChatDto | undefined>;
|
||||
$provideWelcomeMessage(handle: number, token: CancellationToken): Promise<(string | IMarkdownString | IChatReplyFollowup[])[] | undefined>;
|
||||
$provideSampleQuestions(handle: number, token: CancellationToken): Promise<IChatReplyFollowup[] | undefined>;
|
||||
$provideFollowups(handle: number, sessionId: number, token: CancellationToken): Promise<IChatFollowup[] | undefined>;
|
||||
$provideReply(handle: number, sessionId: number, request: IChatRequestDto, token: CancellationToken): Promise<IChatResponseDto | undefined>;
|
||||
$removeRequest(handle: number, sessionId: number, requestId: string): void;
|
||||
$provideSlashCommands(handle: number, sessionId: number, token: CancellationToken): Promise<ISlashCommand[] | undefined>;
|
||||
$releaseSession(sessionId: number): void;
|
||||
$onDidPerformUserAction(event: IChatUserActionEvent): Promise<void>;
|
||||
}
|
||||
|
@ -2721,7 +2704,6 @@ export const MainContext = {
|
|||
MainThreadAuthentication: createProxyIdentifier<MainThreadAuthenticationShape>('MainThreadAuthentication'),
|
||||
MainThreadBulkEdits: createProxyIdentifier<MainThreadBulkEditsShape>('MainThreadBulkEdits'),
|
||||
MainThreadChatProvider: createProxyIdentifier<MainThreadChatProviderShape>('MainThreadChatProvider'),
|
||||
MainThreadChatAgents: createProxyIdentifier<MainThreadChatAgentsShape>('MainThreadChatAgents'),
|
||||
MainThreadChatAgents2: createProxyIdentifier<MainThreadChatAgentsShape2>('MainThreadChatAgents2'),
|
||||
MainThreadChatVariables: createProxyIdentifier<MainThreadChatVariablesShape>('MainThreadChatVariables'),
|
||||
MainThreadClipboard: createProxyIdentifier<MainThreadClipboardShape>('MainThreadClipboard'),
|
||||
|
@ -2844,7 +2826,6 @@ export const ExtHostContext = {
|
|||
ExtHostInteractive: createProxyIdentifier<ExtHostInteractiveShape>('ExtHostInteractive'),
|
||||
ExtHostInlineChat: createProxyIdentifier<ExtHostInlineChatShape>('ExtHostInlineChatShape'),
|
||||
ExtHostChat: createProxyIdentifier<ExtHostChatShape>('ExtHostChat'),
|
||||
ExtHostChatAgents: createProxyIdentifier<ExtHostChatAgentsShape>('ExtHostChatAgents'),
|
||||
ExtHostChatAgents2: createProxyIdentifier<ExtHostChatAgentsShape2>('ExtHostChatAgents'),
|
||||
ExtHostChatVariables: createProxyIdentifier<ExtHostChatVariablesShape>('ExtHostChatVariables'),
|
||||
ExtHostChatProvider: createProxyIdentifier<ExtHostChatProviderShape>('ExtHostChatProvider'),
|
||||
|
|
|
@ -3,19 +3,15 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { raceCancellation } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ExtHostChatShape, IChatRequestDto, IChatResponseDto, IChatDto, IMainContext, MainContext, MainThreadChatShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostChatShape, IChatDto, IMainContext, MainContext, MainThreadChatShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { IChatFollowup, IChatReplyFollowup, IChatUserActionEvent, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatReplyFollowup, IChatUserActionEvent } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
class ChatProviderWrapper<T> {
|
||||
|
@ -36,7 +32,6 @@ export class ExtHostChat implements ExtHostChatShape {
|
|||
private readonly _chatProvider = new Map<number, ChatProviderWrapper<vscode.InteractiveSessionProvider>>();
|
||||
|
||||
private readonly _chatSessions = new Map<number, vscode.InteractiveSession>();
|
||||
// private readonly _providerResponsesByRequestId = new Map<number, { response: vscode.ProviderResult<vscode.InteractiveResponse | vscode.InteractiveResponseForProgress>; sessionId: number }>();
|
||||
|
||||
private readonly _onDidPerformUserAction = new Emitter<vscode.InteractiveSessionUserActionEvent>();
|
||||
public readonly onDidPerformUserAction = this._onDidPerformUserAction.event;
|
||||
|
@ -45,7 +40,6 @@ export class ExtHostChat implements ExtHostChatShape {
|
|||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
private readonly logService: ILogService
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadChat);
|
||||
}
|
||||
|
@ -75,13 +69,13 @@ export class ExtHostChat implements ExtHostChatShape {
|
|||
this._proxy.$sendRequestToProvider(providerId, message);
|
||||
}
|
||||
|
||||
async $prepareChat(handle: number, initialState: any, token: CancellationToken): Promise<IChatDto | undefined> {
|
||||
async $prepareChat(handle: number, token: CancellationToken): Promise<IChatDto | undefined> {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const session = await entry.provider.prepareSession(initialState, token);
|
||||
const session = await entry.provider.prepareSession(token);
|
||||
if (!session) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -124,25 +118,6 @@ export class ExtHostChat implements ExtHostChatShape {
|
|||
});
|
||||
}
|
||||
|
||||
async $provideFollowups(handle: number, sessionId: number, token: CancellationToken): Promise<IChatFollowup[] | undefined> {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const realSession = this._chatSessions.get(sessionId);
|
||||
if (!realSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry.provider.provideFollowups) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const rawFollowups = await entry.provider.provideFollowups(realSession, token);
|
||||
return rawFollowups?.map(f => typeConvert.ChatFollowup.from(f));
|
||||
}
|
||||
|
||||
async $provideSampleQuestions(handle: number, token: CancellationToken): Promise<IChatReplyFollowup[] | undefined> {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
|
@ -161,121 +136,6 @@ export class ExtHostChat implements ExtHostChatShape {
|
|||
return rawFollowups?.map(f => typeConvert.ChatReplyFollowup.from(f));
|
||||
}
|
||||
|
||||
$removeRequest(handle: number, sessionId: number, requestId: string): void {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const realSession = this._chatSessions.get(sessionId);
|
||||
if (!realSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry.provider.removeRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry.provider.removeRequest(realSession, requestId);
|
||||
}
|
||||
|
||||
async $provideReply(handle: number, sessionId: number, request: IChatRequestDto, token: CancellationToken): Promise<IChatResponseDto | undefined> {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const realSession = this._chatSessions.get(sessionId);
|
||||
if (!realSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
const requestObj: vscode.InteractiveRequest = {
|
||||
session: realSession,
|
||||
message: request.message,
|
||||
variables: {}
|
||||
};
|
||||
|
||||
if (request.variables) {
|
||||
for (const key of Object.keys(request.variables)) {
|
||||
requestObj.variables[key] = request.variables[key].map(typeConvert.ChatVariable.to);
|
||||
}
|
||||
}
|
||||
|
||||
const stopWatch = StopWatch.create(false);
|
||||
let firstProgress: number | undefined;
|
||||
const progressObj: vscode.Progress<vscode.InteractiveProgress> = {
|
||||
report: (progress: vscode.InteractiveProgress) => {
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof firstProgress === 'undefined') {
|
||||
firstProgress = stopWatch.elapsed();
|
||||
}
|
||||
|
||||
const convertedProgress = typeConvert.ChatResponseProgress.from(entry.extension, progress);
|
||||
if ('placeholder' in progress && 'resolvedContent' in progress) {
|
||||
const resolvedContent = Promise.all([this._proxy.$acceptResponseProgress(handle, sessionId, convertedProgress), progress.resolvedContent]);
|
||||
raceCancellation(resolvedContent, token).then((res) => {
|
||||
if (!res) {
|
||||
return; /* Cancelled */
|
||||
}
|
||||
const [progressHandle, progressContent] = res;
|
||||
this._proxy.$acceptResponseProgress(handle, sessionId, progressContent, progressHandle ?? undefined);
|
||||
});
|
||||
} else {
|
||||
this._proxy.$acceptResponseProgress(handle, sessionId, convertedProgress);
|
||||
}
|
||||
}
|
||||
};
|
||||
let result: vscode.InteractiveResponseForProgress | undefined | null;
|
||||
try {
|
||||
result = await entry.provider.provideResponseWithProgress(requestObj, progressObj, token);
|
||||
if (!result) {
|
||||
result = { errorDetails: { message: localize('emptyResponse', "Provider returned null response") } };
|
||||
}
|
||||
} catch (err) {
|
||||
result = { errorDetails: { message: localize('errorResponse', "Error from provider: {0}", err.message), responseIsIncomplete: true } };
|
||||
this.logService.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
// Check that the session has not been released since the request started
|
||||
if (realSession.saveState && this._chatSessions.has(sessionId)) {
|
||||
const newState = realSession.saveState();
|
||||
this._proxy.$acceptChatState(sessionId, newState);
|
||||
}
|
||||
} catch (err) {
|
||||
this.logService.warn(err);
|
||||
}
|
||||
|
||||
const timings = { firstProgress: firstProgress ?? 0, totalElapsed: stopWatch.elapsed() };
|
||||
return { errorDetails: result.errorDetails, timings };
|
||||
}
|
||||
|
||||
async $provideSlashCommands(handle: number, sessionId: number, token: CancellationToken): Promise<ISlashCommand[] | undefined> {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const realSession = this._chatSessions.get(sessionId);
|
||||
if (!realSession) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!entry.provider.provideSlashCommands) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const slashCommands = await entry.provider.provideSlashCommands(realSession, token);
|
||||
return slashCommands?.map(c => (<ISlashCommand>{
|
||||
...c,
|
||||
kind: typeConvert.CompletionItemKind.from(c.kind)
|
||||
}));
|
||||
}
|
||||
|
||||
$releaseSession(sessionId: number) {
|
||||
this._chatSessions.delete(sessionId);
|
||||
}
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DeferredPromise, raceCancellation } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Progress } from 'vs/platform/progress/common/progress';
|
||||
import { ExtHostChatAgentsShape, IMainContext, MainContext, MainThreadChatAgentsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider';
|
||||
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { ChatMessageRole } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
export class ExtHostChatAgents implements ExtHostChatAgentsShape {
|
||||
|
||||
private static _idPool = 0;
|
||||
|
||||
private readonly _agents = new Map<number, { extension: ExtensionIdentifier; agent: vscode.ChatAgent }>();
|
||||
private readonly _proxy: MainThreadChatAgentsShape;
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
private readonly _extHostChatProvider: ExtHostChatProvider,
|
||||
private readonly _logService: ILogService,
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents);
|
||||
}
|
||||
|
||||
registerAgent(extension: ExtensionIdentifier, name: string, agent: vscode.ChatAgent, metadata: vscode.ChatAgentMetadata): IDisposable {
|
||||
const handle = ExtHostChatAgents._idPool++;
|
||||
this._agents.set(handle, { extension, agent });
|
||||
this._proxy.$registerAgent(handle, name, metadata);
|
||||
|
||||
return toDisposable(() => {
|
||||
this._proxy.$unregisterAgent(handle);
|
||||
this._agents.delete(handle);
|
||||
});
|
||||
}
|
||||
|
||||
async $invokeAgent(handle: number, requestId: number, prompt: string, context: { history: IChatMessage[] }, token: CancellationToken): Promise<any> {
|
||||
const data = this._agents.get(handle);
|
||||
if (!data) {
|
||||
this._logService.warn(`[CHAT](${handle}) CANNOT invoke agent because the agent is not registered`);
|
||||
return;
|
||||
}
|
||||
|
||||
let done = false;
|
||||
function throwIfDone() {
|
||||
if (done) {
|
||||
throw new Error('Only valid while executing the command');
|
||||
}
|
||||
}
|
||||
|
||||
const commandExecution = new DeferredPromise<void>();
|
||||
token.onCancellationRequested(() => commandExecution.complete());
|
||||
setTimeout(() => commandExecution.complete(), 10 * 1000);
|
||||
this._extHostChatProvider.allowListExtensionWhile(data.extension, commandExecution.p);
|
||||
|
||||
const task = data.agent(
|
||||
{ role: ChatMessageRole.User, content: prompt },
|
||||
{ history: context.history.map(typeConvert.ChatMessage.to) },
|
||||
new Progress<vscode.ChatAgentResponse>(p => {
|
||||
throwIfDone();
|
||||
this._proxy.$handleProgressChunk(requestId, { content: isInteractiveProgressFileTree(p.message) ? p.message : p.message.value });
|
||||
}),
|
||||
token
|
||||
);
|
||||
|
||||
try {
|
||||
return await raceCancellation(Promise.resolve(task).then((v) => {
|
||||
if (v && 'followUp' in v) {
|
||||
const convertedFollowup = v?.followUp?.map(f => typeConvert.ChatFollowup.from(f));
|
||||
return { followUp: convertedFollowup };
|
||||
}
|
||||
return undefined;
|
||||
}), token);
|
||||
} finally {
|
||||
done = true;
|
||||
commandExecution.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isInteractiveProgressFileTree(thing: unknown): thing is vscode.InteractiveProgressFileTree {
|
||||
return !!thing && typeof thing === 'object' && 'treeData' in thing;
|
||||
}
|
|
@ -2157,20 +2157,10 @@ export namespace DataTransfer {
|
|||
}
|
||||
|
||||
export namespace ChatReplyFollowup {
|
||||
export function to(followup: IChatReplyFollowup): vscode.InteractiveSessionReplyFollowup {
|
||||
return {
|
||||
message: followup.message,
|
||||
metadata: followup.metadata,
|
||||
title: followup.title,
|
||||
tooltip: followup.tooltip,
|
||||
};
|
||||
}
|
||||
|
||||
export function from(followup: vscode.InteractiveSessionReplyFollowup): IChatReplyFollowup {
|
||||
return {
|
||||
kind: 'reply',
|
||||
message: followup.message,
|
||||
metadata: followup.metadata,
|
||||
title: followup.title,
|
||||
tooltip: followup.tooltip,
|
||||
};
|
||||
|
@ -2178,7 +2168,7 @@ export namespace ChatReplyFollowup {
|
|||
}
|
||||
|
||||
export namespace ChatFollowup {
|
||||
export function from(followup: string | vscode.InteractiveSessionFollowup): IChatFollowup {
|
||||
export function from(followup: string | vscode.ChatAgentFollowup): IChatFollowup {
|
||||
if (typeof followup === 'string') {
|
||||
return <IChatReplyFollowup>{ title: followup, message: followup, kind: 'reply' };
|
||||
} else if ('commandId' in followup) {
|
||||
|
@ -2303,11 +2293,9 @@ export namespace InteractiveEditorResponseFeedbackKind {
|
|||
}
|
||||
|
||||
export namespace ChatResponseProgress {
|
||||
export function from(extension: IExtensionDescription, progress: vscode.InteractiveProgress | vscode.ChatAgentExtendedProgress): extHostProtocol.IChatResponseProgressDto {
|
||||
export function from(extension: IExtensionDescription, progress: vscode.ChatAgentExtendedProgress): extHostProtocol.IChatResponseProgressDto {
|
||||
if ('placeholder' in progress && 'resolvedContent' in progress) {
|
||||
return { placeholder: progress.placeholder };
|
||||
} else if ('responseId' in progress) {
|
||||
return { requestId: progress.responseId };
|
||||
} else if ('markdownContent' in progress) {
|
||||
checkProposedApiEnabled(extension, 'chatAgents2Additions');
|
||||
return { content: MarkdownString.from(progress.markdownContent) };
|
||||
|
|
|
@ -28,7 +28,7 @@ import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatAct
|
|||
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ICodeBlockActionContext } from 'vs/workbench/contrib/chat/browser/codeBlockPart';
|
||||
import { CONTEXT_IN_CHAT_SESSION, CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { IChatCopyAction, IChatService, IDocumentContext, InteractiveSessionCopyKind } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatService, IDocumentContext, InteractiveSessionCopyKind } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatResponseViewModel, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { CTX_INLINE_CHAT_VISIBLE } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations';
|
||||
|
@ -111,9 +111,8 @@ export function registerChatCodeBlockActions() {
|
|||
agentId: context.element.agent?.id,
|
||||
sessionId: context.element.sessionId,
|
||||
requestId: context.element.requestId,
|
||||
action: <IChatCopyAction>{
|
||||
action: {
|
||||
kind: 'copy',
|
||||
responseId: context.element.providerResponseId,
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
copyType: InteractiveSessionCopyKind.Toolbar,
|
||||
copiedCharacters: context.code.length,
|
||||
|
@ -149,7 +148,6 @@ export function registerChatCodeBlockActions() {
|
|||
const totalCharacters = editorModel.getValueLength();
|
||||
|
||||
// Report copy to extensions
|
||||
if (context.element.providerResponseId) {
|
||||
const chatService = accessor.get(IChatService);
|
||||
chatService.notifyUserAction({
|
||||
providerId: context.element.providerId,
|
||||
|
@ -159,14 +157,12 @@ export function registerChatCodeBlockActions() {
|
|||
action: {
|
||||
kind: 'copy',
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
responseId: context.element.providerResponseId,
|
||||
copyType: InteractiveSessionCopyKind.Action,
|
||||
copiedText,
|
||||
copiedCharacters: copiedText.length,
|
||||
totalCharacters,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Copy full cell if no selection, otherwise fall back on normal editor implementation
|
||||
if (noSelection) {
|
||||
|
@ -337,7 +333,6 @@ export function registerChatCodeBlockActions() {
|
|||
requestId: context.element.requestId,
|
||||
action: {
|
||||
kind: 'insert',
|
||||
responseId: context.element.providerResponseId!,
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
totalCharacters: context.code.length,
|
||||
}
|
||||
|
@ -386,7 +381,6 @@ export function registerChatCodeBlockActions() {
|
|||
requestId: context.element.requestId,
|
||||
action: {
|
||||
kind: 'insert',
|
||||
responseId: context.element.providerResponseId!,
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
totalCharacters: context.code.length,
|
||||
newFile: true
|
||||
|
@ -482,7 +476,6 @@ export function registerChatCodeBlockActions() {
|
|||
requestId: context.element.requestId,
|
||||
action: {
|
||||
kind: 'runInTerminal',
|
||||
responseId: context.element.providerResponseId!,
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
languageId: context.languageId,
|
||||
}
|
||||
|
|
|
@ -60,7 +60,6 @@ export function registerChatTitleActions() {
|
|||
action: {
|
||||
kind: 'vote',
|
||||
direction: InteractiveSessionVoteDirection.Up,
|
||||
responseId: item.providerResponseId!,
|
||||
}
|
||||
});
|
||||
item.setVote(InteractiveSessionVoteDirection.Up);
|
||||
|
@ -103,7 +102,6 @@ export function registerChatTitleActions() {
|
|||
action: {
|
||||
kind: 'vote',
|
||||
direction: InteractiveSessionVoteDirection.Down,
|
||||
responseId: item.providerResponseId!,
|
||||
}
|
||||
});
|
||||
item.setVote(InteractiveSessionVoteDirection.Down);
|
||||
|
@ -222,12 +220,12 @@ export function registerChatTitleActions() {
|
|||
item = widget?.getFocus();
|
||||
}
|
||||
|
||||
const providerRequestId = isRequestVM(item) ? item.providerRequestId :
|
||||
isResponseVM(item) ? item.providerResponseId : undefined;
|
||||
const requestId = isRequestVM(item) ? item.id :
|
||||
isResponseVM(item) ? item.requestId : undefined;
|
||||
|
||||
if (providerRequestId) {
|
||||
if (requestId) {
|
||||
const chatService = accessor.get(IChatService);
|
||||
chatService.removeRequest(item.sessionId, providerRequestId);
|
||||
chatService.removeRequest(item.sessionId, requestId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatRequestViewModel, IChatResponseViewModel, IChatViewModel, IChatWelcomeMessageViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IChatWidgetContrib } from 'vs/workbench/contrib/chat/browser/chatWidget';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { IChatRequestViewModel, IChatResponseViewModel, IChatViewModel, IChatWelcomeMessageViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
|
||||
export const IChatWidgetService = createDecorator<IChatWidgetService>('chatWidgetService');
|
||||
export const IQuickChatService = createDecorator<IQuickChatService>('quickChatService');
|
||||
|
@ -121,7 +120,6 @@ export interface IChatWidget {
|
|||
focusLastMessage(): void;
|
||||
focusInput(): void;
|
||||
hasInputFocus(): boolean;
|
||||
getSlashCommands(): Promise<ISlashCommand[] | undefined>;
|
||||
getCodeBlockInfoForEditor(uri: URI): IChatCodeBlockInfo | undefined;
|
||||
getCodeBlockInfosForResponse(response: IChatResponseViewModel): IChatCodeBlockInfo[];
|
||||
getFileTreeInfosForResponse(response: IChatResponseViewModel): IChatFileTreeInfo[];
|
||||
|
|
|
@ -54,10 +54,10 @@ import { convertParsedRequestToMarkdown, reduceInlineContentReferences, walkTree
|
|||
import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions';
|
||||
import { CodeBlockPart, ICodeBlockData, ICodeBlockPart } from 'vs/workbench/contrib/chat/browser/codeBlockPart';
|
||||
import { IChatAgentMetadata } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_HAS_PROVIDER_ID, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { IPlaceholderMarkdownString } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IChatContentReference, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, ISlashCommand, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatContentReference, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatResponseMarkdownRenderData, IChatResponseRenderData, IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { IWordCountResult, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
|
||||
import { createFileIconThemableTreeContainerScope } from 'vs/workbench/contrib/files/browser/views/explorerView';
|
||||
|
@ -89,7 +89,6 @@ const forceVerboseLayoutTracing = false;
|
|||
|
||||
export interface IChatRendererDelegate {
|
||||
getListLength(): number;
|
||||
getSlashCommands(): ISlashCommand[];
|
||||
}
|
||||
|
||||
export interface IChatListItemRendererOptions {
|
||||
|
@ -264,7 +263,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
|
||||
CONTEXT_RESPONSE.bindTo(templateData.contextKeyService).set(isResponseVM(element));
|
||||
CONTEXT_REQUEST.bindTo(templateData.contextKeyService).set(isRequestVM(element));
|
||||
CONTEXT_RESPONSE_HAS_PROVIDER_ID.bindTo(templateData.contextKeyService).set(isResponseVM(element) && !!element.providerResponseId);
|
||||
if (isResponseVM(element)) {
|
||||
CONTEXT_RESPONSE_VOTE.bindTo(templateData.contextKeyService).set(element.vote === InteractiveSessionVoteDirection.Up ? 'up' : element.vote === InteractiveSessionVoteDirection.Down ? 'down' : '');
|
||||
} else {
|
||||
|
@ -465,7 +463,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
private renderWelcomeMessage(element: IChatWelcomeMessageViewModel, templateData: IChatListItemTemplate, height?: number) {
|
||||
dom.clearNode(templateData.value);
|
||||
dom.clearNode(templateData.referencesListContainer);
|
||||
const slashCommands = this.delegate.getSlashCommands();
|
||||
|
||||
for (const item of element.content) {
|
||||
if (Array.isArray(item)) {
|
||||
|
@ -477,11 +474,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
templateData.contextKeyService));
|
||||
} else {
|
||||
const result = this.renderMarkdown(item as IMarkdownString, element, templateData);
|
||||
for (const codeElement of result.element.querySelectorAll('code')) {
|
||||
if (codeElement.textContent && slashCommands.find(command => codeElement.textContent === `/${command.command}`)) {
|
||||
codeElement.classList.add('interactive-slash-command');
|
||||
}
|
||||
}
|
||||
templateData.value.appendChild(result.element);
|
||||
templateData.elementDisposables.add(result);
|
||||
}
|
||||
|
@ -779,11 +771,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
const disposables = new DisposableStore();
|
||||
let codeBlockIndex = 0;
|
||||
|
||||
// TODO if the slash commands stay completely dynamic, this isn't quite right
|
||||
const slashCommands = this.delegate.getSlashCommands();
|
||||
const usedSlashCommand = slashCommands.find(s => markdown.value.startsWith(`/${s.command} `));
|
||||
const toRender = usedSlashCommand ? markdown.value.slice(usedSlashCommand.command.length + 2) : markdown.value;
|
||||
markdown = new MarkdownString(toRender, {
|
||||
markdown = new MarkdownString(markdown.value, {
|
||||
isTrusted: {
|
||||
// Disable all other config options except isTrusted
|
||||
enabledCommands: typeof markdown.isTrusted === 'object' ? markdown.isTrusted?.enabledCommands : [] ?? []
|
||||
|
@ -831,15 +819,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
|
||||
walkTreeAndAnnotateReferenceLinks(result.element);
|
||||
|
||||
if (usedSlashCommand) {
|
||||
const slashCommandElement = $('span.interactive-slash-command', { title: usedSlashCommand.detail }, `/${usedSlashCommand.command} `);
|
||||
if (result.element.firstChild?.nodeName.toLowerCase() === 'p') {
|
||||
result.element.firstChild.insertBefore(slashCommandElement, result.element.firstChild.firstChild);
|
||||
} else {
|
||||
result.element.insertBefore($('p', undefined, slashCommandElement), result.element.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
orderedDisposablesList.reverse().forEach(d => disposables.add(d));
|
||||
return {
|
||||
element: result.element,
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
import * as dom from 'vs/base/browser/dom';
|
||||
import { ITreeContextMenuEvent, ITreeElement } from 'vs/base/browser/ui/tree/tree';
|
||||
import { disposableTimeout } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, IDisposable, MutableDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
@ -23,7 +22,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
|
|||
import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
import { ChatTreeItem, IChatWidgetViewOptions, IChatAccessibilityService, IChatCodeBlockInfo, IChatFileTreeInfo, IChatWidget, IChatWidgetService, IChatWidgetViewContext } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ChatTreeItem, IChatAccessibilityService, IChatCodeBlockInfo, IChatFileTreeInfo, IChatWidget, IChatWidgetService, IChatWidgetViewContext, IChatWidgetViewOptions } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart';
|
||||
import { ChatAccessibilityProvider, ChatListDelegate, ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer';
|
||||
import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions';
|
||||
|
@ -31,7 +30,7 @@ import { ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane';
|
|||
import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_IN_CHAT_SESSION } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
|
||||
import { ChatModelInitState, IChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IChatReplyFollowup, IChatService, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatReplyFollowup, IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { ChatViewModel, IChatResponseViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
|
||||
const $ = dom.$;
|
||||
|
@ -109,15 +108,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
this.viewModelDisposables.add(viewModel);
|
||||
}
|
||||
|
||||
this.slashCommandsPromise = undefined;
|
||||
this.lastSlashCommands = undefined;
|
||||
|
||||
this.getSlashCommands().then(() => {
|
||||
if (!this._isDisposed) {
|
||||
this.onDidChangeItems();
|
||||
}
|
||||
});
|
||||
|
||||
this._onDidChangeViewModel.fire();
|
||||
}
|
||||
|
||||
|
@ -125,9 +115,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
return this._viewModel;
|
||||
}
|
||||
|
||||
private lastSlashCommands: ISlashCommand[] | undefined;
|
||||
private slashCommandsPromise: Promise<ISlashCommand[] | undefined> | undefined;
|
||||
|
||||
constructor(
|
||||
readonly viewContext: IChatWidgetViewContext,
|
||||
private readonly viewOptions: IChatWidgetViewOptions,
|
||||
|
@ -164,12 +151,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
return this.inputPart.inputUri;
|
||||
}
|
||||
|
||||
private _isDisposed: boolean = false;
|
||||
public override dispose(): void {
|
||||
this._isDisposed = true;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
render(parent: HTMLElement): void {
|
||||
const viewId = 'viewId' in this.viewContext ? this.viewContext.viewId : undefined;
|
||||
this.editorOptions = this._register(this.instantiationService.createInstance(ChatEditorOptions, viewId, this.styles.listForeground, this.styles.inputEditorBackground, this.styles.resultEditorBackground));
|
||||
|
@ -260,7 +241,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
// TODO? We can give the welcome message a proper VM or get rid of the rest of the VMs
|
||||
((isWelcomeVM(element) && this.viewModel) ? `_${ChatModelInitState[this.viewModel.initState]}` : '') +
|
||||
// Ensure re-rendering an element once slash commands are loaded, so the colorization can be applied.
|
||||
`${(isRequestVM(element) || isWelcomeVM(element)) && !!this.lastSlashCommands ? '_scLoaded' : ''}` +
|
||||
`${(isRequestVM(element) || isWelcomeVM(element)) /* && !!this.lastSlashCommands ? '_scLoaded' : '' */}` +
|
||||
// If a response is in the process of progressive rendering, we need to ensure that it will
|
||||
// be re-rendered so progressive rendering is restarted, even if the model wasn't updated.
|
||||
`${isResponseVM(element) && element.renderData ? `_${this.visibleChangeCount}` : ''}` +
|
||||
|
@ -309,27 +290,11 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
}
|
||||
}
|
||||
|
||||
async getSlashCommands(): Promise<ISlashCommand[] | undefined> {
|
||||
if (!this.viewModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.slashCommandsPromise) {
|
||||
this.slashCommandsPromise = this.chatService.getSlashCommands(this.viewModel.sessionId, CancellationToken.None).then(commands => {
|
||||
this.lastSlashCommands = commands ?? [];
|
||||
return this.lastSlashCommands;
|
||||
});
|
||||
}
|
||||
|
||||
return this.slashCommandsPromise;
|
||||
}
|
||||
|
||||
private createList(listContainer: HTMLElement, options: IChatListItemRendererOptions): void {
|
||||
const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.contextKeyService]));
|
||||
const delegate = scopedInstantiationService.createInstance(ChatListDelegate);
|
||||
const rendererDelegate: IChatRendererDelegate = {
|
||||
getListLength: () => this.tree.getNode(null).visibleChildrenCount,
|
||||
getSlashCommands: () => this.lastSlashCommands ?? [],
|
||||
};
|
||||
this.renderer = this._register(scopedInstantiationService.createInstance(
|
||||
ChatListItemRenderer,
|
||||
|
@ -462,7 +427,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
this.container.setAttribute('data-session-id', model.sessionId);
|
||||
this.viewModel = this.instantiationService.createInstance(ChatViewModel, model);
|
||||
this.viewModelDisposables.add(this.viewModel.onDidChange(e => {
|
||||
this.slashCommandsPromise = undefined;
|
||||
this.requestInProgress.set(this.viewModel!.requestInProgress);
|
||||
this.onDidChangeItems();
|
||||
if (e?.kind === 'addRequest') {
|
||||
|
@ -536,8 +500,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
'query' in opts ? opts.query :
|
||||
`${opts.prefix} ${editorValue}`;
|
||||
const isUserQuery = !opts || 'query' in opts;
|
||||
const usedSlashCommand = this.lookupSlashCommand(input);
|
||||
const result = await this.chatService.sendRequest(this.viewModel.sessionId, input, usedSlashCommand);
|
||||
const result = await this.chatService.sendRequest(this.viewModel.sessionId, input);
|
||||
|
||||
if (result) {
|
||||
this.inputPart.acceptInput(isUserQuery ? input : undefined);
|
||||
|
@ -552,10 +515,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
}
|
||||
}
|
||||
|
||||
private lookupSlashCommand(input: string): ISlashCommand | undefined {
|
||||
return this.lastSlashCommands?.find(sc => input.startsWith(`/${sc.command}`));
|
||||
}
|
||||
|
||||
getCodeBlockInfosForResponse(response: IChatResponseViewModel): IChatCodeBlockInfo[] {
|
||||
return this.renderer.getCodeBlockInfosForResponse(response);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import { chatSlashCommandBackground, chatSlashCommandForeground } from 'vs/workb
|
|||
import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestVariablePart, chatAgentLeader, chatSubcommandLeader, chatVariableLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser';
|
||||
import { IChatService, ISlashCommand } 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 { isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
|
@ -49,7 +50,7 @@ class InputEditorDecorations extends Disposable {
|
|||
|
||||
public readonly id = 'inputEditorDecorations';
|
||||
|
||||
private readonly previouslyUsedSlashCommands = new Set<string>();
|
||||
private readonly previouslyUsedAgents = new Set<string>();
|
||||
|
||||
private readonly viewModelDisposables = this._register(new MutableDisposable());
|
||||
|
||||
|
@ -71,16 +72,12 @@ class InputEditorDecorations extends Disposable {
|
|||
this._register(this.widget.inputEditor.onDidChangeModelContent(() => this.updateInputEditorDecorations()));
|
||||
this._register(this.widget.onDidChangeViewModel(() => {
|
||||
this.registerViewModelListeners();
|
||||
this.previouslyUsedSlashCommands.clear();
|
||||
this.previouslyUsedAgents.clear();
|
||||
this.updateInputEditorDecorations();
|
||||
}));
|
||||
this._register(this.chatService.onDidSubmitSlashCommand((e) => {
|
||||
this._register(this.chatService.onDidSubmitAgent((e) => {
|
||||
if (e.sessionId === this.widget.viewModel?.sessionId) {
|
||||
if ('agent' in e) {
|
||||
this.previouslyUsedSlashCommands.add(agentAndCommandToKey(e.agent.id, e.slashCommand.name));
|
||||
} else {
|
||||
this.previouslyUsedSlashCommands.add(e.slashCommand);
|
||||
}
|
||||
this.previouslyUsedAgents.add(agentAndCommandToKey(e.agent.id, e.slashCommand.name));
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -187,7 +184,7 @@ class InputEditorDecorations extends Disposable {
|
|||
const onlyAgentCommandAndWhitespace = agentPart && agentSubcommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentPart || p instanceof ChatRequestAgentSubcommandPart);
|
||||
if (onlyAgentCommandAndWhitespace) {
|
||||
// Agent reference and subcommand with no other text - show the placeholder
|
||||
const isFollowupSlashCommand = this.previouslyUsedSlashCommands.has(agentAndCommandToKey(agentPart.agent.id, agentSubcommandPart.command.name));
|
||||
const isFollowupSlashCommand = this.previouslyUsedAgents.has(agentAndCommandToKey(agentPart.agent.id, agentSubcommandPart.command.name));
|
||||
const shouldRenderFollowupPlaceholder = isFollowupSlashCommand && agentSubcommandPart.command.followupPlaceholder;
|
||||
if (agentSubcommandPart?.command.description) {
|
||||
placeholderDecoration = [{
|
||||
|
@ -210,7 +207,7 @@ class InputEditorDecorations extends Disposable {
|
|||
const onlySlashCommandAndWhitespace = slashCommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestSlashCommandPart);
|
||||
if (onlySlashCommandAndWhitespace) {
|
||||
// Command reference with no other text - show the placeholder
|
||||
const isFollowupSlashCommand = this.previouslyUsedSlashCommands.has(slashCommandPart.slashCommand.command);
|
||||
const isFollowupSlashCommand = this.previouslyUsedAgents.has(slashCommandPart.slashCommand.command);
|
||||
const shouldRenderFollowupPlaceholder = isFollowupSlashCommand && slashCommandPart.slashCommand.followupPlaceholder;
|
||||
if (shouldRenderFollowupPlaceholder || slashCommandPart.slashCommand.detail) {
|
||||
placeholderDecoration = [{
|
||||
|
@ -264,15 +261,12 @@ class InputEditorSlashCommandMode extends Disposable {
|
|||
@IChatService private readonly chatService: IChatService
|
||||
) {
|
||||
super();
|
||||
this._register(this.chatService.onDidSubmitSlashCommand(e => {
|
||||
this._register(this.chatService.onDidSubmitAgent(e => {
|
||||
if (this.widget.viewModel?.sessionId !== e.sessionId) {
|
||||
return;
|
||||
}
|
||||
if ('agent' in e) {
|
||||
|
||||
this.repopulateAgentCommand(e.agent, e.slashCommand);
|
||||
} else {
|
||||
this.repopulateSlashCommand(e.slashCommand);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -283,20 +277,6 @@ class InputEditorSlashCommandMode extends Disposable {
|
|||
this.widget.inputEditor.setPosition({ lineNumber: 1, column: value.length + 1 });
|
||||
}
|
||||
}
|
||||
|
||||
private async repopulateSlashCommand(slashCommand: string) {
|
||||
const slashCommands = await this.widget.getSlashCommands();
|
||||
|
||||
if (this.widget.inputEditor.getValue().trim().length !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (slashCommands?.find(c => c.command === slashCommand)?.shouldRepopulate) {
|
||||
const value = `/${slashCommand} `;
|
||||
this.widget.inputEditor.setValue(value);
|
||||
this.widget.inputEditor.setPosition({ lineNumber: 1, column: value.length + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChatWidget.CONTRIBS.push(InputEditorDecorations, InputEditorSlashCommandMode);
|
||||
|
@ -306,11 +286,12 @@ class SlashCommandCompletions extends Disposable {
|
|||
@ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService,
|
||||
@IChatWidgetService private readonly chatWidgetService: IChatWidgetService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IChatSlashCommandService private readonly chatSlashCommandService: IChatSlashCommandService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(this.languageFeaturesService.completionProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, hasAccessToAllModels: true }, {
|
||||
_debugDisplayName: 'chatSlashCommand',
|
||||
_debugDisplayName: 'globalSlashCommands',
|
||||
triggerCharacters: ['/'],
|
||||
provideCompletionItems: async (model: ITextModel, _position: Position, _context: CompletionContext, _token: CancellationToken) => {
|
||||
const widget = this.chatWidgetService.getWidgetByInputUri(model.uri);
|
||||
|
@ -329,7 +310,7 @@ class SlashCommandCompletions extends Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
const slashCommands = await widget.getSlashCommands();
|
||||
const slashCommands = this.chatSlashCommandService.getCommands();
|
||||
if (!slashCommands) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -390,10 +390,6 @@
|
|||
color: var(--vscode-notificationsInfoIcon-foreground) !important; /* Have to override default styles which apply to all lists */
|
||||
}
|
||||
|
||||
.interactive-item-container .value .interactive-slash-command {
|
||||
color: var(--vscode-textLink-foreground);
|
||||
}
|
||||
|
||||
.interactive-session .interactive-input-part {
|
||||
padding: 12px 0px;
|
||||
display: flex;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
import { localize } from 'vs/nls';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
export const CONTEXT_RESPONSE_HAS_PROVIDER_ID = new RawContextKey<boolean>('chatSessionResponseHasProviderId', false, { type: 'boolean', description: localize('interactiveSessionResponseHasProviderId', "True when the provider has assigned an id to this response.") });
|
||||
export const CONTEXT_RESPONSE_VOTE = new RawContextKey<string>('chatSessionResponseVote', '', { type: 'string', description: localize('interactiveSessionResponseVote', "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.") });
|
||||
export const CONTEXT_RESPONSE_FILTERED = new RawContextKey<boolean>('chatSessionResponseFiltered', false, { type: 'boolean', description: localize('chatResponseFiltered', "True when the chat response was filtered out by the server.") });
|
||||
export const CONTEXT_CHAT_REQUEST_IN_PROGRESS = new RawContextKey<boolean>('chatSessionRequestInProgress', false, { type: 'boolean', description: localize('interactiveSessionRequestInProgress', "True when the current request is still in progress.") });
|
||||
|
|
|
@ -20,7 +20,6 @@ import { IChat, IChatContentInlineReference, IChatContentReference, IChatFollowu
|
|||
|
||||
export interface IChatRequestModel {
|
||||
readonly id: string;
|
||||
readonly providerRequestId: string | undefined;
|
||||
readonly username: string;
|
||||
readonly avatarIconUri?: URI;
|
||||
readonly session: IChatModel;
|
||||
|
@ -53,7 +52,6 @@ export interface IChatResponseModel {
|
|||
readonly onDidChange: Event<void>;
|
||||
readonly id: string;
|
||||
readonly providerId: string;
|
||||
readonly providerResponseId: string | undefined;
|
||||
readonly requestId: string;
|
||||
readonly username: string;
|
||||
readonly avatarIconUri?: URI;
|
||||
|
@ -79,10 +77,6 @@ export class ChatRequestModel implements IChatRequestModel {
|
|||
return this._id;
|
||||
}
|
||||
|
||||
public get providerRequestId(): string | undefined {
|
||||
return this._providerRequestId;
|
||||
}
|
||||
|
||||
public get username(): string {
|
||||
return this.session.requesterUsername;
|
||||
}
|
||||
|
@ -93,14 +87,9 @@ export class ChatRequestModel implements IChatRequestModel {
|
|||
|
||||
constructor(
|
||||
public readonly session: ChatModel,
|
||||
public readonly message: IParsedChatRequest,
|
||||
private _providerRequestId?: string) {
|
||||
public readonly message: IParsedChatRequest) {
|
||||
this._id = 'request_' + ChatRequestModel.nextId++;
|
||||
}
|
||||
|
||||
setProviderRequestId(providerRequestId: string) {
|
||||
this._providerRequestId = providerRequestId;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPlaceholderMarkdownString extends IMarkdownString {
|
||||
|
@ -256,10 +245,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
|
|||
return this._id;
|
||||
}
|
||||
|
||||
public get providerResponseId(): string | undefined {
|
||||
return this._providerResponseId;
|
||||
}
|
||||
|
||||
public get isComplete(): boolean {
|
||||
return this._isComplete;
|
||||
}
|
||||
|
@ -317,7 +302,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
|
|||
private _isComplete: boolean = false,
|
||||
private _isCanceled = false,
|
||||
private _vote?: InteractiveSessionVoteDirection,
|
||||
private _providerResponseId?: string,
|
||||
private _errorDetails?: IChatResponseErrorDetails,
|
||||
followups?: ReadonlyArray<IChatFollowup>
|
||||
) {
|
||||
|
@ -339,10 +323,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
|
|||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
setProviderResponseId(providerResponseId: string) {
|
||||
this._providerResponseId = providerResponseId;
|
||||
}
|
||||
|
||||
setErrorDetails(errorDetails?: IChatResponseErrorDetails): void {
|
||||
this._errorDetails = errorDetails;
|
||||
this._onDidChange.fire();
|
||||
|
@ -392,7 +372,6 @@ export interface ISerializableChatsData {
|
|||
export type ISerializableChatAgentData = UriDto<IChatAgentData>;
|
||||
|
||||
export interface ISerializableChatRequestData {
|
||||
providerRequestId: string | undefined;
|
||||
message: string | IParsedChatRequest;
|
||||
response: ReadonlyArray<IMarkdownString | IChatResponseProgressFileTreeData | IChatContentInlineReference> | undefined;
|
||||
agent?: ISerializableChatAgentData;
|
||||
|
@ -414,7 +393,6 @@ export interface IExportableChatData {
|
|||
responderUsername: string;
|
||||
requesterAvatarIconUri: UriComponents | undefined;
|
||||
responderAvatarIconUri: UriComponents | undefined;
|
||||
providerState: any;
|
||||
}
|
||||
|
||||
export interface ISerializableChatData extends IExportableChatData {
|
||||
|
@ -490,11 +468,6 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
return this._welcomeMessage;
|
||||
}
|
||||
|
||||
private _providerState: any;
|
||||
get providerState(): any {
|
||||
return this._providerState;
|
||||
}
|
||||
|
||||
// TODO to be clear, this is not the same as the id from the session object, which belongs to the provider.
|
||||
// It's easier to be able to identify this model before its async initialization is complete
|
||||
private _sessionId: string;
|
||||
|
@ -560,7 +533,6 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
this._isImported = (!!initialData && !isSerializableSessionData(initialData)) || (initialData?.isImported ?? false);
|
||||
this._sessionId = (isSerializableSessionData(initialData) && initialData.sessionId) || generateUuid();
|
||||
this._requests = initialData ? this._deserialize(initialData) : [];
|
||||
this._providerState = initialData ? initialData.providerState : undefined;
|
||||
this._creationDate = (isSerializableSessionData(initialData) && initialData.creationDate) || Date.now();
|
||||
|
||||
this._initialRequesterAvatarIconUri = initialData?.requesterAvatarIconUri && URI.revive(initialData.requesterAvatarIconUri);
|
||||
|
@ -585,11 +557,11 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
typeof raw.message === 'string'
|
||||
? this.getParsedRequestFromString(raw.message)
|
||||
: reviveParsedChatRequest(raw.message);
|
||||
const request = new ChatRequestModel(this, parsedRequest, raw.providerRequestId);
|
||||
const request = new ChatRequestModel(this, parsedRequest);
|
||||
if (raw.response || raw.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, request.id, true, raw.isCanceled, raw.vote, raw.providerRequestId, raw.responseErrorDetails, raw.followups);
|
||||
request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, request.id, true, raw.isCanceled, raw.vote, raw.responseErrorDetails, raw.followups);
|
||||
if (raw.usedContext) { // @ulugbekna: if this's a new vscode sessions, doc versions are incorrect anyway?
|
||||
request.response.updateContent(raw.usedContext);
|
||||
}
|
||||
|
@ -642,13 +614,6 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
}
|
||||
|
||||
this._isInitializedDeferred.complete();
|
||||
|
||||
if (session.onDidChangeState) {
|
||||
this._register(session.onDidChangeState(state => {
|
||||
this._providerState = state;
|
||||
this.logService.trace('ChatModel#acceptNewSessionState');
|
||||
}));
|
||||
}
|
||||
this._onDidChange.fire({ kind: 'initialize' });
|
||||
}
|
||||
|
||||
|
@ -707,21 +672,15 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
if (agent) {
|
||||
request.response.setAgent(agent, progress.command);
|
||||
}
|
||||
} else {
|
||||
request.setProviderRequestId(progress.requestId);
|
||||
request.response.setProviderResponseId(progress.requestId);
|
||||
}
|
||||
}
|
||||
|
||||
removeRequest(requestId: string): void {
|
||||
const index = this._requests.findIndex(request => request.providerRequestId === requestId);
|
||||
removeRequest(id: string): void {
|
||||
const index = this._requests.findIndex(request => request.id === id);
|
||||
const request = this._requests[index];
|
||||
if (!request.providerRequestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index !== -1) {
|
||||
this._onDidChange.fire({ kind: 'removeRequest', requestId: request.providerRequestId, responseId: request.response?.providerResponseId });
|
||||
this._onDidChange.fire({ kind: 'removeRequest', requestId: request.id, responseId: request.response?.id });
|
||||
this._requests.splice(index, 1);
|
||||
request.response?.dispose();
|
||||
}
|
||||
|
@ -782,21 +741,19 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
}),
|
||||
requests: this._requests.map((r): ISerializableChatRequestData => {
|
||||
return {
|
||||
providerRequestId: r.providerRequestId,
|
||||
message: r.message,
|
||||
response: r.response ? r.response.response.value : undefined,
|
||||
responseErrorDetails: r.response?.errorDetails,
|
||||
followups: r.response?.followups,
|
||||
isCanceled: r.response?.isCanceled,
|
||||
vote: r.response?.vote,
|
||||
agent: r.response?.agent,
|
||||
agent: r.response?.agent ? { id: r.response.agent.id, metadata: r.response.agent.metadata } : undefined, // May actually be the full IChatAgent instance, just take the data props
|
||||
slashCommand: r.response?.slashCommand,
|
||||
usedContext: r.response?.response.usedContext,
|
||||
contentReferences: r.response?.response.contentReferences
|
||||
};
|
||||
}),
|
||||
providerId: this.providerId,
|
||||
providerState: this._providerState
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position';
|
|||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestDynamicReferencePart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestVariablePart, IParsedChatRequest, IParsedChatRequestPart, chatAgentLeader, chatSubcommandLeader, chatVariableLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChatVariablesService, IDynamicReference } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
|
||||
const agentReg = /^@([\w_\-]+)(?=(\s|$|\b))/i; // An @-agent
|
||||
|
@ -21,7 +21,7 @@ export class ChatRequestParser {
|
|||
constructor(
|
||||
@IChatAgentService private readonly agentService: IChatAgentService,
|
||||
@IChatVariablesService private readonly variableService: IChatVariablesService,
|
||||
@IChatService private readonly chatService: IChatService,
|
||||
@IChatSlashCommandService private readonly slashCommandService: IChatSlashCommandService
|
||||
) { }
|
||||
|
||||
async parseChatRequest(sessionId: string, message: string): Promise<IParsedChatRequest> {
|
||||
|
@ -174,7 +174,7 @@ export class ChatRequestParser {
|
|||
return new ChatRequestAgentSubcommandPart(slashRange, slashEditorRange, subCommand);
|
||||
}
|
||||
} else {
|
||||
const slashCommands = await this.chatService.getSlashCommands(sessionId, CancellationToken.None);
|
||||
const slashCommands = this.slashCommandService.getCommands();
|
||||
const slashCommand = slashCommands.find(c => c.command === command);
|
||||
if (slashCommand) {
|
||||
// Valid standalone slash command
|
||||
|
|
|
@ -23,7 +23,6 @@ export interface IChat {
|
|||
responderUsername: string;
|
||||
responderAvatarIconUri?: URI;
|
||||
inputPlaceholder?: string;
|
||||
onDidChangeState?: Event<any>;
|
||||
dispose?(): void;
|
||||
}
|
||||
|
||||
|
@ -100,7 +99,6 @@ export interface IChatAgentDetection {
|
|||
|
||||
export type IChatProgress =
|
||||
| { content: string | IMarkdownString }
|
||||
| { requestId: string }
|
||||
| { treeData: IChatResponseProgressFileTreeData }
|
||||
| { placeholder: string; resolvedContent: Promise<string | IMarkdownString | { treeData: IChatResponseProgressFileTreeData }> }
|
||||
| IUsedContext
|
||||
|
@ -108,18 +106,13 @@ export type IChatProgress =
|
|||
| IChatContentInlineReference
|
||||
| IChatAgentDetection;
|
||||
|
||||
export interface IPersistedChatState { }
|
||||
export interface IChatProvider {
|
||||
readonly id: string;
|
||||
readonly displayName: string;
|
||||
readonly iconUrl?: string;
|
||||
prepareSession(initialState: IPersistedChatState | undefined, token: CancellationToken): ProviderResult<IChat | undefined>;
|
||||
prepareSession(token: CancellationToken): ProviderResult<IChat | undefined>;
|
||||
provideWelcomeMessage?(token: CancellationToken): ProviderResult<(string | IMarkdownString | IChatReplyFollowup[])[] | undefined>;
|
||||
provideSampleQuestions?(token: CancellationToken): ProviderResult<IChatReplyFollowup[] | undefined>;
|
||||
provideFollowups?(session: IChat, token: CancellationToken): ProviderResult<IChatFollowup[] | undefined>;
|
||||
provideReply(request: IChatRequest, progress: (progress: IChatProgress) => void, token: CancellationToken): ProviderResult<IChatResponse>;
|
||||
provideSlashCommands?(session: IChat, token: CancellationToken): ProviderResult<ISlashCommand[]>;
|
||||
removeRequest?(session: IChat, requestId: string): void;
|
||||
}
|
||||
|
||||
export interface ISlashCommand {
|
||||
|
@ -156,7 +149,6 @@ export interface IChatReplyFollowup {
|
|||
message: string;
|
||||
title?: string;
|
||||
tooltip?: string;
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
export interface IChatResponseCommandFollowup {
|
||||
|
@ -177,7 +169,6 @@ export enum InteractiveSessionVoteDirection {
|
|||
|
||||
export interface IChatVoteAction {
|
||||
kind: 'vote';
|
||||
responseId: string;
|
||||
direction: InteractiveSessionVoteDirection;
|
||||
}
|
||||
|
||||
|
@ -189,7 +180,6 @@ export enum InteractiveSessionCopyKind {
|
|||
|
||||
export interface IChatCopyAction {
|
||||
kind: 'copy';
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
copyType: InteractiveSessionCopyKind;
|
||||
copiedCharacters: number;
|
||||
|
@ -199,7 +189,6 @@ export interface IChatCopyAction {
|
|||
|
||||
export interface IChatInsertAction {
|
||||
kind: 'insert';
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
totalCharacters: number;
|
||||
newFile?: boolean;
|
||||
|
@ -207,7 +196,6 @@ export interface IChatInsertAction {
|
|||
|
||||
export interface IChatTerminalAction {
|
||||
kind: 'runInTerminal';
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
languageId?: string;
|
||||
}
|
||||
|
@ -271,7 +259,7 @@ export interface IChatService {
|
|||
_serviceBrand: undefined;
|
||||
transferredSessionData: IChatTransferredSessionData | undefined;
|
||||
|
||||
onDidSubmitSlashCommand: Event<{ slashCommand: string; sessionId: string } | { agent: IChatAgentData; slashCommand: IChatAgentCommand; sessionId: string }>;
|
||||
onDidSubmitAgent: Event<{ agent: IChatAgentData; slashCommand: IChatAgentCommand; sessionId: string }>;
|
||||
onDidRegisterProvider: Event<{ providerId: string }>;
|
||||
registerProvider(provider: IChatProvider): IDisposable;
|
||||
hasSessions(providerId: string): boolean;
|
||||
|
@ -285,10 +273,9 @@ export interface IChatService {
|
|||
/**
|
||||
* Returns whether the request was accepted.
|
||||
*/
|
||||
sendRequest(sessionId: string, message: string, usedSlashCommand?: ISlashCommand): Promise<{ responseCompletePromise: Promise<void> } | undefined>;
|
||||
sendRequest(sessionId: string, message: string): Promise<{ responseCompletePromise: Promise<void> } | undefined>;
|
||||
removeRequest(sessionid: string, requestId: string): Promise<void>;
|
||||
cancelCurrentRequestForSession(sessionId: string): void;
|
||||
getSlashCommands(sessionId: string, token: CancellationToken): Promise<ISlashCommand[]>;
|
||||
clearSession(sessionId: string): void;
|
||||
addCompleteRequest(sessionId: string, message: IParsedChatRequest | string, response: IChatCompleteResponse): void;
|
||||
sendRequestToProvider(sessionId: string, message: IChatDynamicRequest): void;
|
||||
|
|
|
@ -8,7 +8,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
|||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { MarkdownString, isMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableMap, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
|
@ -27,7 +27,7 @@ import { ChatModel, ChatModelInitState, ChatRequestModel, ChatWelcomeMessageMode
|
|||
import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart } 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 { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatRequest, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ISlashCommand, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, InteractiveSessionCopyKind, 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';
|
||||
|
@ -134,7 +134,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
|
||||
private readonly _providers = new Map<string, IChatProvider>();
|
||||
|
||||
private readonly _sessionModels = new Map<string, ChatModel>();
|
||||
private readonly _sessionModels = this._register(new DisposableMap<string, ChatModel>());
|
||||
private readonly _pendingRequests = new Map<string, CancelablePromise<void>>();
|
||||
private readonly _persistedSessions: ISerializableChatsData;
|
||||
private readonly _hasProvider: IContextKey<boolean>;
|
||||
|
@ -147,8 +147,8 @@ export class ChatService extends Disposable implements IChatService {
|
|||
private readonly _onDidPerformUserAction = this._register(new Emitter<IChatUserActionEvent>());
|
||||
public readonly onDidPerformUserAction: Event<IChatUserActionEvent> = this._onDidPerformUserAction.event;
|
||||
|
||||
private readonly _onDidSubmitSlashCommand = this._register(new Emitter<{ slashCommand: string; sessionId: string } | { agent: IChatAgentData; slashCommand: IChatAgentCommand; sessionId: string }>());
|
||||
public readonly onDidSubmitSlashCommand = this._onDidSubmitSlashCommand.event;
|
||||
private readonly _onDidSubmitAgent = this._register(new Emitter<{ agent: IChatAgentData; slashCommand: IChatAgentCommand; sessionId: string }>());
|
||||
public readonly onDidSubmitAgent = this._onDidSubmitAgent.event;
|
||||
|
||||
private readonly _onDidDisposeSession = this._register(new Emitter<{ sessionId: string; providerId: string; reason: 'initializationFailed' | 'cleared' }>());
|
||||
public readonly onDidDisposeSession = this._onDidDisposeSession.event;
|
||||
|
@ -365,7 +365,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
|
||||
let session: IChat | undefined;
|
||||
try {
|
||||
session = await provider.prepareSession(model.providerState, token) ?? undefined;
|
||||
session = await provider.prepareSession(token) ?? undefined;
|
||||
} catch (err) {
|
||||
this.trace('initializeSession', `Provider initializeSession threw: ${err}`);
|
||||
}
|
||||
|
@ -387,8 +387,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
} catch (err) {
|
||||
this.trace('startSession', `initializeSession failed: ${err}`);
|
||||
model.setInitializationError(err);
|
||||
model.dispose();
|
||||
this._sessionModels.delete(model.sessionId);
|
||||
this._sessionModels.deleteAndDispose(model.sessionId);
|
||||
this._onDidDisposeSession.fire({ sessionId: model.sessionId, providerId: model.providerId, reason: 'initializationFailed' });
|
||||
}
|
||||
}
|
||||
|
@ -424,7 +423,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
return this._startSession(data.providerId, data, CancellationToken.None);
|
||||
}
|
||||
|
||||
async sendRequest(sessionId: string, request: string, usedSlashCommand?: ISlashCommand): Promise<{ responseCompletePromise: Promise<void> } | undefined> {
|
||||
async sendRequest(sessionId: string, request: string): Promise<{ responseCompletePromise: Promise<void> } | undefined> {
|
||||
this.trace('sendRequest', `sessionId: ${sessionId}, message: ${request.substring(0, 20)}${request.length > 20 ? '[...]' : ''}}`);
|
||||
if (!request.trim()) {
|
||||
this.trace('sendRequest', 'Rejected empty message');
|
||||
|
@ -448,10 +447,10 @@ export class ChatService extends Disposable implements IChatService {
|
|||
}
|
||||
|
||||
// This method is only returning whether the request was accepted - don't block on the actual request
|
||||
return { responseCompletePromise: this._sendRequestAsync(model, sessionId, provider, request, usedSlashCommand) };
|
||||
return { responseCompletePromise: this._sendRequestAsync(model, sessionId, provider, request) };
|
||||
}
|
||||
|
||||
private async _sendRequestAsync(model: ChatModel, sessionId: string, provider: IChatProvider, message: string, usedSlashCommand?: ISlashCommand): Promise<void> {
|
||||
private async _sendRequestAsync(model: ChatModel, sessionId: string, provider: IChatProvider, message: string): Promise<void> {
|
||||
const parsedRequest = await this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(sessionId, message);
|
||||
|
||||
let request: ChatRequestModel;
|
||||
|
@ -486,7 +485,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
} else if ('agentName' in progress) {
|
||||
this.trace('sendRequest', `Provider returned an agent detection for session ${model.sessionId}:\n ${JSON.stringify(progress, null, '\t')}`);
|
||||
} else {
|
||||
this.trace('sendRequest', `Provider returned id for session ${model.sessionId}, ${progress.requestId}`);
|
||||
this.trace('sendRequest', `Provider returned unknown progress for session ${model.sessionId}:\n ${JSON.stringify(progress, null, '\t')}`);
|
||||
}
|
||||
|
||||
model.acceptResponseProgress(request, progress);
|
||||
|
@ -503,7 +502,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
result: 'cancelled',
|
||||
requestType,
|
||||
agent: agentPart?.agent.id ?? '',
|
||||
slashCommand: agentSlashCommandPart ? agentSlashCommandPart.command.name : usedSlashCommand?.command,
|
||||
slashCommand: agentSlashCommandPart ? agentSlashCommandPart.command.name : commandPart?.slashCommand.command,
|
||||
chatSessionId: model.sessionId
|
||||
});
|
||||
|
||||
|
@ -511,10 +510,8 @@ export class ChatService extends Disposable implements IChatService {
|
|||
});
|
||||
|
||||
try {
|
||||
if (usedSlashCommand?.command) {
|
||||
this._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId });
|
||||
} else if (agentPart && agentSlashCommandPart?.command) {
|
||||
this._onDidSubmitSlashCommand.fire({ agent: agentPart.agent, slashCommand: agentSlashCommandPart.command, sessionId: model.sessionId });
|
||||
if (agentPart && agentSlashCommandPart?.command) {
|
||||
this._onDidSubmitAgent.fire({ agent: agentPart.agent, slashCommand: agentSlashCommandPart.command, sessionId: model.sessionId });
|
||||
}
|
||||
|
||||
let rawResponse: IChatResponse | null | undefined;
|
||||
|
@ -574,19 +571,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
rawResponse = { session: model.session! };
|
||||
|
||||
} else {
|
||||
request = model.addRequest(parsedRequest);
|
||||
const requestProps: IChatRequest = {
|
||||
session: model.session!,
|
||||
message,
|
||||
variables: {}
|
||||
};
|
||||
|
||||
if ('parts' in parsedRequest) {
|
||||
const varResult = await this.chatVariablesService.resolveVariables(parsedRequest, model, token);
|
||||
requestProps.variables = varResult.variables;
|
||||
requestProps.message = varResult.prompt;
|
||||
}
|
||||
rawResponse = await provider.provideReply(requestProps, progressCallback, token);
|
||||
throw new Error(`Can't handle request`);
|
||||
}
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
|
@ -608,7 +593,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
result,
|
||||
requestType,
|
||||
agent: agentPart?.agent.id ?? '',
|
||||
slashCommand: agentSlashCommandPart ? agentSlashCommandPart.command.name : usedSlashCommand?.command,
|
||||
slashCommand: agentSlashCommandPart ? agentSlashCommandPart.command.name : commandPart?.slashCommand.command,
|
||||
chatSessionId: model.sessionId
|
||||
});
|
||||
model.setResponse(request, rawResponse);
|
||||
|
@ -620,11 +605,6 @@ export class ChatService extends Disposable implements IChatService {
|
|||
model.setFollowups(request, followups);
|
||||
model.completeResponse(request);
|
||||
});
|
||||
} else if (provider.provideFollowups) {
|
||||
Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(providerFollowups => {
|
||||
model.setFollowups(request, providerFollowups ?? undefined);
|
||||
model.completeResponse(request);
|
||||
});
|
||||
} else {
|
||||
model.completeResponse(request);
|
||||
}
|
||||
|
@ -653,43 +633,6 @@ export class ChatService extends Disposable implements IChatService {
|
|||
}
|
||||
|
||||
model.removeRequest(requestId);
|
||||
provider.removeRequest?.(model.session!, requestId);
|
||||
}
|
||||
|
||||
async getSlashCommands(sessionId: string, token: CancellationToken): Promise<ISlashCommand[]> {
|
||||
const model = this._sessionModels.get(sessionId);
|
||||
if (!model) {
|
||||
throw new Error(`Unknown session: ${sessionId}`);
|
||||
}
|
||||
|
||||
await model.waitForInitialization();
|
||||
const provider = this._providers.get(model.providerId);
|
||||
if (!provider) {
|
||||
throw new Error(`Unknown provider: ${model.providerId}`);
|
||||
}
|
||||
|
||||
const serviceResults = this.chatSlashCommandService.getCommands().map(data => {
|
||||
return <ISlashCommand>{
|
||||
command: data.command,
|
||||
detail: data.detail,
|
||||
sortText: data.sortText,
|
||||
executeImmediately: data.executeImmediately
|
||||
};
|
||||
});
|
||||
|
||||
const mainProviderRequest = provider.provideSlashCommands?.(model.session!, token);
|
||||
|
||||
try {
|
||||
const providerResults = await mainProviderRequest;
|
||||
if (providerResults) {
|
||||
return providerResults.concat(serviceResults);
|
||||
}
|
||||
return serviceResults;
|
||||
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
return serviceResults;
|
||||
}
|
||||
}
|
||||
|
||||
async sendRequestToProvider(sessionId: string, message: IChatDynamicRequest): Promise<{ responseCompletePromise: Promise<void> } | undefined> {
|
||||
|
@ -746,8 +689,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
|
||||
this._persistedSessions[sessionId] = model.toJSON();
|
||||
|
||||
model.dispose();
|
||||
this._sessionModels.delete(sessionId);
|
||||
this._sessionModels.deleteAndDispose(sessionId);
|
||||
this._pendingRequests.get(sessionId)?.cancel();
|
||||
this._onDidDisposeSession.fire({ sessionId, providerId: model.providerId, reason: 'cleared' });
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ export interface IChatViewModel {
|
|||
|
||||
export interface IChatRequestViewModel {
|
||||
readonly id: string;
|
||||
readonly providerRequestId: string | undefined;
|
||||
readonly sessionId: string;
|
||||
/** This ID updates every time the underlying data changes */
|
||||
readonly dataId: string;
|
||||
|
@ -88,7 +87,6 @@ export interface IChatResponseViewModel {
|
|||
/** This ID updates every time the underlying data changes */
|
||||
readonly dataId: string;
|
||||
readonly providerId: string;
|
||||
readonly providerResponseId: string | undefined;
|
||||
/** The ID of the associated IChatRequestViewModel */
|
||||
readonly requestId: string;
|
||||
readonly username: string;
|
||||
|
@ -173,12 +171,12 @@ export class ChatViewModel extends Disposable implements IChatViewModel {
|
|||
} else if (e.kind === 'addResponse') {
|
||||
this.onAddResponse(e.response);
|
||||
} else if (e.kind === 'removeRequest') {
|
||||
const requestIdx = this._items.findIndex(item => isRequestVM(item) && item.providerRequestId === e.requestId);
|
||||
const requestIdx = this._items.findIndex(item => isRequestVM(item) && item.id === e.requestId);
|
||||
if (requestIdx >= 0) {
|
||||
this._items.splice(requestIdx, 1);
|
||||
}
|
||||
|
||||
const responseIdx = e.responseId && this._items.findIndex(item => isResponseVM(item) && item.providerResponseId === e.responseId);
|
||||
const responseIdx = e.responseId && this._items.findIndex(item => isResponseVM(item) && item.id === e.responseId);
|
||||
if (typeof responseIdx === 'number' && responseIdx >= 0) {
|
||||
const items = this._items.splice(responseIdx, 1);
|
||||
const item = items[0];
|
||||
|
@ -218,10 +216,6 @@ export class ChatRequestViewModel implements IChatRequestViewModel {
|
|||
return this._model.id;
|
||||
}
|
||||
|
||||
get providerRequestId() {
|
||||
return this._model.providerRequestId;
|
||||
}
|
||||
|
||||
get dataId() {
|
||||
return this.id + `_${ChatModelInitState[this._model.session.initState]}`;
|
||||
}
|
||||
|
@ -269,10 +263,6 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi
|
|||
return this._model.providerId;
|
||||
}
|
||||
|
||||
get providerResponseId() {
|
||||
return this._model.providerResponseId;
|
||||
}
|
||||
|
||||
get sessionId() {
|
||||
return this._model.session.sessionId;
|
||||
}
|
||||
|
|
|
@ -6,32 +6,53 @@
|
|||
welcomeMessage: undefined,
|
||||
requests: [
|
||||
{
|
||||
providerRequestId: undefined,
|
||||
message: {
|
||||
text: "test request",
|
||||
text: "@ChatProviderWithUsedContext test request",
|
||||
parts: [
|
||||
{
|
||||
range: {
|
||||
start: 0,
|
||||
endExclusive: 12
|
||||
endExclusive: 28
|
||||
},
|
||||
editorRange: {
|
||||
startLineNumber: 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: 1,
|
||||
endColumn: 13
|
||||
endColumn: 29
|
||||
},
|
||||
text: "test request",
|
||||
agent: {
|
||||
id: "ChatProviderWithUsedContext",
|
||||
metadata: { },
|
||||
provideSlashCommands: [Function provideSlashCommands],
|
||||
invoke: [Function invoke]
|
||||
},
|
||||
kind: "agent"
|
||||
},
|
||||
{
|
||||
range: {
|
||||
start: 28,
|
||||
endExclusive: 41
|
||||
},
|
||||
editorRange: {
|
||||
startLineNumber: 1,
|
||||
startColumn: 29,
|
||||
endLineNumber: 1,
|
||||
endColumn: 42
|
||||
},
|
||||
text: " test request",
|
||||
kind: "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
response: [ ],
|
||||
responseErrorDetails: undefined,
|
||||
followups: undefined,
|
||||
followups: [ ],
|
||||
isCanceled: false,
|
||||
vote: undefined,
|
||||
agent: undefined,
|
||||
agent: {
|
||||
id: "ChatProviderWithUsedContext",
|
||||
metadata: { }
|
||||
},
|
||||
slashCommand: undefined,
|
||||
usedContext: { documents: [
|
||||
{
|
||||
|
@ -58,6 +79,5 @@
|
|||
contentReferences: [ ]
|
||||
}
|
||||
],
|
||||
providerId: "ChatProviderWithUsedContext",
|
||||
providerState: undefined
|
||||
providerId: "testProvider"
|
||||
}
|
|
@ -5,6 +5,5 @@
|
|||
responderAvatarIconUri: undefined,
|
||||
welcomeMessage: undefined,
|
||||
requests: [ ],
|
||||
providerId: "ChatProviderWithUsedContext",
|
||||
providerState: undefined
|
||||
providerId: "testProvider"
|
||||
}
|
|
@ -6,32 +6,53 @@
|
|||
welcomeMessage: undefined,
|
||||
requests: [
|
||||
{
|
||||
providerRequestId: undefined,
|
||||
message: {
|
||||
parts: [
|
||||
{
|
||||
range: {
|
||||
start: 0,
|
||||
endExclusive: 12
|
||||
endExclusive: 28
|
||||
},
|
||||
editorRange: {
|
||||
startLineNumber: 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: 1,
|
||||
endColumn: 13
|
||||
endColumn: 29
|
||||
},
|
||||
text: "test request",
|
||||
agent: {
|
||||
id: "ChatProviderWithUsedContext",
|
||||
metadata: { },
|
||||
provideSlashCommands: [Function provideSlashCommands],
|
||||
invoke: [Function invoke]
|
||||
},
|
||||
kind: "agent"
|
||||
},
|
||||
{
|
||||
range: {
|
||||
start: 28,
|
||||
endExclusive: 41
|
||||
},
|
||||
editorRange: {
|
||||
startLineNumber: 1,
|
||||
startColumn: 29,
|
||||
endLineNumber: 1,
|
||||
endColumn: 42
|
||||
},
|
||||
text: " test request",
|
||||
kind: "text"
|
||||
}
|
||||
],
|
||||
text: "test request"
|
||||
text: "@ChatProviderWithUsedContext test request"
|
||||
},
|
||||
response: [ ],
|
||||
responseErrorDetails: undefined,
|
||||
followups: undefined,
|
||||
followups: [ ],
|
||||
isCanceled: false,
|
||||
vote: undefined,
|
||||
agent: undefined,
|
||||
agent: {
|
||||
id: "ChatProviderWithUsedContext",
|
||||
metadata: { }
|
||||
},
|
||||
slashCommand: undefined,
|
||||
usedContext: { documents: [
|
||||
{
|
||||
|
@ -58,6 +79,5 @@
|
|||
contentReferences: [ ]
|
||||
}
|
||||
],
|
||||
providerId: "ChatProviderWithUsedContext",
|
||||
providerState: undefined
|
||||
providerId: "testProvider"
|
||||
}
|
|
@ -6,11 +6,14 @@
|
|||
import * as assert from 'assert';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { ChatRequestTextPart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
|
||||
|
@ -98,4 +101,18 @@ suite('ChatModel', () => {
|
|||
|
||||
assert.throws(() => model.initialize({} as any, undefined));
|
||||
});
|
||||
|
||||
test('removeRequest', async () => {
|
||||
const model = testDisposables.add(instantiationService.createInstance(ChatModel, 'provider', undefined));
|
||||
|
||||
model.startInitialize();
|
||||
model.initialize({} as any, undefined);
|
||||
const text = 'hello';
|
||||
model.addRequest({ text, parts: [new ChatRequestTextPart(new OffsetRange(0, text.length), new Range(1, text.length, 1, text.length), text)] });
|
||||
const requests = model.getRequests();
|
||||
assert.strictEqual(requests.length, 1);
|
||||
|
||||
model.removeRequest(requests[0].id);
|
||||
assert.strictEqual(model.getRequests().length, 0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log';
|
|||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ChatAgentService, IChatAgent, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser';
|
||||
import { IChatService } 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';
|
||||
import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
|
@ -48,9 +48,9 @@ suite('ChatRequestParser', () => {
|
|||
});
|
||||
|
||||
test('slash command', async () => {
|
||||
const chatService = mockObject<IChatService>()({});
|
||||
chatService.getSlashCommands.returns(Promise.resolve([{ command: 'fix' }]));
|
||||
instantiationService.stub(IChatService, chatService as any);
|
||||
const slashCommandService = mockObject<IChatSlashCommandService>()({});
|
||||
slashCommandService.getCommands.returns([{ command: 'fix' }]);
|
||||
instantiationService.stub(IChatSlashCommandService, slashCommandService as any);
|
||||
|
||||
parser = instantiationService.createInstance(ChatRequestParser);
|
||||
const text = '/fix this';
|
||||
|
@ -59,9 +59,9 @@ suite('ChatRequestParser', () => {
|
|||
});
|
||||
|
||||
test('invalid slash command', async () => {
|
||||
const chatService = mockObject<IChatService>()({});
|
||||
chatService.getSlashCommands.returns(Promise.resolve([{ command: 'fix' }]));
|
||||
instantiationService.stub(IChatService, chatService as any);
|
||||
const slashCommandService = mockObject<IChatSlashCommandService>()({});
|
||||
slashCommandService.getCommands.returns([{ command: 'fix' }]);
|
||||
instantiationService.stub(IChatSlashCommandService, slashCommandService as any);
|
||||
|
||||
parser = instantiationService.createInstance(ChatRequestParser);
|
||||
const text = '/explain this';
|
||||
|
@ -70,9 +70,9 @@ suite('ChatRequestParser', () => {
|
|||
});
|
||||
|
||||
test('multiple slash commands', async () => {
|
||||
const chatService = mockObject<IChatService>()({});
|
||||
chatService.getSlashCommands.returns(Promise.resolve([{ command: 'fix' }]));
|
||||
instantiationService.stub(IChatService, chatService as any);
|
||||
const slashCommandService = mockObject<IChatSlashCommandService>()({});
|
||||
slashCommandService.getCommands.returns([{ command: 'fix' }]);
|
||||
instantiationService.stub(IChatSlashCommandService, slashCommandService as any);
|
||||
|
||||
parser = instantiationService.createInstance(ChatRequestParser);
|
||||
const text = '/fix /fix';
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { assertSnapshot } from 'vs/base/test/common/snapshot';
|
||||
|
@ -22,10 +21,10 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
|||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
import { ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatAgentService, IChatAgent, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
|
||||
import { ISerializableChatData } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IChat, IChatProgress, IChatProvider, IChatRequest, IChatResponse, IPersistedChatState, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChat, IChatProgress, IChatProvider, IChatRequest } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { ChatService } from 'vs/workbench/contrib/chat/common/chatServiceImpl';
|
||||
import { ChatSlashCommandService, IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
|
@ -36,29 +35,18 @@ import { TestContextService, TestExtensionService, TestStorageService } from 'vs
|
|||
class SimpleTestProvider extends Disposable implements IChatProvider {
|
||||
private static sessionId = 0;
|
||||
|
||||
lastInitialState = undefined;
|
||||
|
||||
readonly displayName = 'Test';
|
||||
|
||||
private _onDidChangeState = this._register(new Emitter());
|
||||
|
||||
constructor(readonly id: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
prepareSession(initialState: any) {
|
||||
this.lastInitialState = initialState;
|
||||
return Promise.resolve(<IChat>{
|
||||
async prepareSession(): Promise<IChat> {
|
||||
return {
|
||||
id: SimpleTestProvider.sessionId++,
|
||||
username: 'test',
|
||||
responderUsername: 'test',
|
||||
requesterUsername: 'test',
|
||||
onDidChangeState: this._onDidChangeState.event
|
||||
});
|
||||
}
|
||||
|
||||
changeState(state: any) {
|
||||
this._onDidChangeState.fire(state);
|
||||
};
|
||||
}
|
||||
|
||||
async provideReply(request: IChatRequest, progress: (progress: IChatProgress) => void): Promise<{ session: IChat; followups: never[] }> {
|
||||
|
@ -66,10 +54,14 @@ class SimpleTestProvider extends Disposable implements IChatProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/** Chat provider for testing that returns used context */
|
||||
class ChatProviderWithUsedContext extends SimpleTestProvider implements IChatProvider {
|
||||
override provideReply(request: IChatRequest, progress: (progress: IChatProgress) => void): Promise<{ session: IChat; followups: never[] }> {
|
||||
|
||||
const chatAgentWithUsedContextId = 'ChatProviderWithUsedContext';
|
||||
const chatAgentWithUsedContext: IChatAgent = {
|
||||
id: chatAgentWithUsedContextId,
|
||||
metadata: {},
|
||||
async provideSlashCommands(token) {
|
||||
return [];
|
||||
},
|
||||
async invoke(request, progress, history, token) {
|
||||
progress({
|
||||
documents: [
|
||||
{
|
||||
|
@ -82,9 +74,9 @@ class ChatProviderWithUsedContext extends SimpleTestProvider implements IChatPro
|
|||
]
|
||||
});
|
||||
|
||||
return super.provideReply(request, progress);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
},
|
||||
};
|
||||
|
||||
suite('Chat', () => {
|
||||
const testDisposables = ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
@ -92,6 +84,8 @@ suite('Chat', () => {
|
|||
let storageService: IStorageService;
|
||||
let instantiationService: TestInstantiationService;
|
||||
|
||||
let chatAgentService: IChatAgentService;
|
||||
|
||||
setup(async () => {
|
||||
instantiationService = testDisposables.add(new TestInstantiationService(new ServiceCollection(
|
||||
[IChatVariablesService, new MockChatVariablesService()],
|
||||
|
@ -105,7 +99,18 @@ suite('Chat', () => {
|
|||
instantiationService.stub(IChatContributionService, new TestExtensionService());
|
||||
instantiationService.stub(IWorkspaceContextService, new TestContextService());
|
||||
instantiationService.stub(IChatSlashCommandService, testDisposables.add(instantiationService.createInstance(ChatSlashCommandService)));
|
||||
instantiationService.stub(IChatAgentService, testDisposables.add(instantiationService.createInstance(ChatAgentService)));
|
||||
|
||||
chatAgentService = testDisposables.add(instantiationService.createInstance(ChatAgentService));
|
||||
instantiationService.stub(IChatAgentService, chatAgentService);
|
||||
|
||||
const agent = {
|
||||
id: 'testAgent',
|
||||
metadata: { isDefault: true },
|
||||
async invoke(request, progress, history, token) {
|
||||
return {};
|
||||
},
|
||||
} as IChatAgent;
|
||||
testDisposables.add(chatAgentService.registerAgent(agent));
|
||||
});
|
||||
|
||||
test('retrieveSession', async () => {
|
||||
|
@ -123,12 +128,7 @@ suite('Chat', () => {
|
|||
await session2.waitForInitialization();
|
||||
session2!.addRequest({ parts: [], text: 'request 2' });
|
||||
|
||||
assert.strictEqual(provider1.lastInitialState, undefined);
|
||||
assert.strictEqual(provider2.lastInitialState, undefined);
|
||||
provider1.changeState({ state: 'provider1_state' });
|
||||
provider2.changeState({ state: 'provider2_state' });
|
||||
storageService.flush();
|
||||
|
||||
const testService2 = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
testDisposables.add(testService2.registerProvider(provider1));
|
||||
testDisposables.add(testService2.registerProvider(provider2));
|
||||
|
@ -136,8 +136,8 @@ suite('Chat', () => {
|
|||
await retrieved1!.waitForInitialization();
|
||||
const retrieved2 = testDisposables.add(testService2.getOrRestoreSession(session2.sessionId)!);
|
||||
await retrieved2!.waitForInitialization();
|
||||
assert.deepStrictEqual(provider1.lastInitialState, { state: 'provider1_state' });
|
||||
assert.deepStrictEqual(provider2.lastInitialState, { state: 'provider2_state' });
|
||||
assert.deepStrictEqual(retrieved1.getRequests()[0]?.message.text, 'request 1');
|
||||
assert.deepStrictEqual(retrieved2.getRequests()[0]?.message.text, 'request 2');
|
||||
});
|
||||
|
||||
test('Handles failed session startup', async () => {
|
||||
|
@ -172,10 +172,7 @@ suite('Chat', () => {
|
|||
testDisposables.add(testService.registerProvider({
|
||||
id,
|
||||
displayName: 'Test',
|
||||
prepareSession: function (initialState: IPersistedChatState | undefined, token: CancellationToken): ProviderResult<IChat | undefined> {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
provideReply: function (request: IChatRequest, progress: (progress: IChatProgress) => void, token: CancellationToken): ProviderResult<IChatResponse> {
|
||||
prepareSession: function (token: CancellationToken): ProviderResult<IChat | undefined> {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
}));
|
||||
|
@ -184,45 +181,13 @@ suite('Chat', () => {
|
|||
testDisposables.add(testService.registerProvider({
|
||||
id,
|
||||
displayName: 'Test',
|
||||
prepareSession: function (initialState: IPersistedChatState | undefined, token: CancellationToken): ProviderResult<IChat | undefined> {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
provideReply: function (request: IChatRequest, progress: (progress: IChatProgress) => void, token: CancellationToken): ProviderResult<IChatResponse> {
|
||||
prepareSession: function (token: CancellationToken): ProviderResult<IChat | undefined> {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
}));
|
||||
}, 'Expected to throw for dupe provider');
|
||||
});
|
||||
|
||||
test('getSlashCommands', async () => {
|
||||
const testService = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
const provider = testDisposables.add(new class extends SimpleTestProvider {
|
||||
constructor() {
|
||||
super('testProvider');
|
||||
}
|
||||
|
||||
provideSlashCommands(): ProviderResult<ISlashCommand[]> {
|
||||
return [
|
||||
{
|
||||
command: 'command',
|
||||
detail: 'detail',
|
||||
sortText: 'sortText',
|
||||
}
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
testDisposables.add(testService.registerProvider(provider));
|
||||
|
||||
const model = testDisposables.add(testService.startSession('testProvider', CancellationToken.None));
|
||||
const commands = await testService.getSlashCommands(model.sessionId, CancellationToken.None);
|
||||
|
||||
assert.strictEqual(commands?.length, 1);
|
||||
assert.strictEqual(commands?.[0].command, 'command');
|
||||
assert.strictEqual(commands?.[0].detail, 'detail');
|
||||
assert.strictEqual(commands?.[0].sortText, 'sortText');
|
||||
});
|
||||
|
||||
test('sendRequestToProvider', async () => {
|
||||
const testService = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
testDisposables.add(testService.registerProvider(testDisposables.add(new SimpleTestProvider('testProvider'))));
|
||||
|
@ -249,18 +214,16 @@ suite('Chat', () => {
|
|||
});
|
||||
|
||||
test('can serialize', async () => {
|
||||
|
||||
testDisposables.add(chatAgentService.registerAgent(chatAgentWithUsedContext));
|
||||
const testService = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
const providerId = 'ChatProviderWithUsedContext';
|
||||
testDisposables.add(testService.registerProvider(testDisposables.add(new SimpleTestProvider('testProvider'))));
|
||||
|
||||
testDisposables.add(testService.registerProvider(testDisposables.add(new ChatProviderWithUsedContext(providerId))));
|
||||
|
||||
const model = testDisposables.add(testService.startSession(providerId, CancellationToken.None));
|
||||
const model = testDisposables.add(testService.startSession('testProvider', CancellationToken.None));
|
||||
assert.strictEqual(model.getRequests().length, 0);
|
||||
|
||||
await assertSnapshot(model.toExport());
|
||||
|
||||
const response = await testService.sendRequest(model.sessionId, 'test request');
|
||||
const response = await testService.sendRequest(model.sessionId, `@${chatAgentWithUsedContextId} test request`);
|
||||
assert(response);
|
||||
|
||||
await response.responseCompletePromise;
|
||||
|
@ -271,21 +234,18 @@ suite('Chat', () => {
|
|||
});
|
||||
|
||||
test('can deserialize', async () => {
|
||||
|
||||
let serializedChatData: ISerializableChatData;
|
||||
|
||||
const providerId = 'ChatProviderWithUsedContext';
|
||||
testDisposables.add(chatAgentService.registerAgent(chatAgentWithUsedContext));
|
||||
|
||||
// create the first service, send request, get response, and serialize the state
|
||||
{ // serapate block to not leak variables in outer scope
|
||||
const testService = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
testDisposables.add(testService.registerProvider(testDisposables.add(new SimpleTestProvider('testProvider'))));
|
||||
|
||||
testDisposables.add(testService.registerProvider(testDisposables.add(new ChatProviderWithUsedContext(providerId))));
|
||||
|
||||
const chatModel1 = testDisposables.add(testService.startSession(providerId, CancellationToken.None));
|
||||
const chatModel1 = testDisposables.add(testService.startSession('testProvider', CancellationToken.None));
|
||||
assert.strictEqual(chatModel1.getRequests().length, 0);
|
||||
|
||||
const response = await testService.sendRequest(chatModel1.sessionId, 'test request');
|
||||
const response = await testService.sendRequest(chatModel1.sessionId, `@${chatAgentWithUsedContextId} test request`);
|
||||
assert(response);
|
||||
|
||||
await response.responseCompletePromise;
|
||||
|
@ -296,17 +256,11 @@ suite('Chat', () => {
|
|||
// try deserializing the state into a new service
|
||||
|
||||
const testService2 = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
|
||||
testDisposables.add(testService2.registerProvider(testDisposables.add(new ChatProviderWithUsedContext(providerId))));
|
||||
testDisposables.add(testService2.registerProvider(testDisposables.add(new SimpleTestProvider('testProvider'))));
|
||||
|
||||
const chatModel2 = testService2.loadSessionFromContent(serializedChatData);
|
||||
assert(chatModel2);
|
||||
|
||||
// should `loadSessionFromContent` return `ChatModel` that's disposable instead of `IChatModel`?
|
||||
testDisposables.add({
|
||||
dispose: () => testService2.clearSession(serializedChatData.sessionId)
|
||||
});
|
||||
|
||||
await assertSnapshot(chatModel2.toExport());
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,7 +11,6 @@ export const allApiProposals = Object.freeze({
|
|||
authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts',
|
||||
canonicalUriProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts',
|
||||
chat: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chat.d.ts',
|
||||
chatAgents: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatAgents.d.ts',
|
||||
chatAgents2: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatAgents2.d.ts',
|
||||
chatAgents2Additions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts',
|
||||
chatProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts',
|
||||
|
|
39
src/vscode-dts/vscode.proposed.chatAgents.d.ts
vendored
39
src/vscode-dts/vscode.proposed.chatAgents.d.ts
vendored
|
@ -1,39 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 ChatAgentContext {
|
||||
history: ChatMessage[];
|
||||
}
|
||||
|
||||
export interface ChatAgentResponse {
|
||||
message: MarkdownString | InteractiveProgressFileTree;
|
||||
}
|
||||
|
||||
export interface ChatAgentResult {
|
||||
followUp?: InteractiveSessionFollowup[];
|
||||
}
|
||||
|
||||
export interface ChatAgentCommand {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ChatAgentMetadata {
|
||||
description: string;
|
||||
fullName?: string;
|
||||
icon?: Uri;
|
||||
subCommands: ChatAgentCommand[];
|
||||
}
|
||||
|
||||
export interface ChatAgent {
|
||||
(prompt: ChatMessage, context: ChatAgentContext, progress: Progress<ChatAgentResponse>, token: CancellationToken): Thenable<ChatAgentResult | void>;
|
||||
}
|
||||
|
||||
export namespace chat {
|
||||
export function registerAgent(id: string, agent: ChatAgent, metadata: ChatAgentMetadata): Disposable;
|
||||
}
|
||||
}
|
106
src/vscode-dts/vscode.proposed.interactive.d.ts
vendored
106
src/vscode-dts/vscode.proposed.interactive.d.ts
vendored
|
@ -85,9 +85,6 @@ declare module 'vscode' {
|
|||
handleInteractiveEditorResponseFeedback?(session: S, response: R, kind: InteractiveEditorResponseFeedbackKind): void;
|
||||
}
|
||||
|
||||
|
||||
export interface InteractiveSessionState { }
|
||||
|
||||
export interface InteractiveSessionParticipantInformation {
|
||||
name: string;
|
||||
|
||||
|
@ -101,81 +98,8 @@ declare module 'vscode' {
|
|||
requester: InteractiveSessionParticipantInformation;
|
||||
responder: InteractiveSessionParticipantInformation;
|
||||
inputPlaceholder?: string;
|
||||
|
||||
saveState?(): InteractiveSessionState;
|
||||
}
|
||||
|
||||
export interface InteractiveSessionRequestArgs {
|
||||
command: string;
|
||||
args: any;
|
||||
}
|
||||
|
||||
export interface InteractiveRequest {
|
||||
session: InteractiveSession;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface InteractiveResponseErrorDetails {
|
||||
message: string;
|
||||
responseIsIncomplete?: boolean;
|
||||
responseIsFiltered?: boolean;
|
||||
}
|
||||
|
||||
export interface InteractiveResponseForProgress {
|
||||
errorDetails?: InteractiveResponseErrorDetails;
|
||||
}
|
||||
|
||||
export interface InteractiveContentReference {
|
||||
reference: Uri | Location;
|
||||
}
|
||||
|
||||
export interface InteractiveInlineContentReference {
|
||||
inlineReference: Uri | Location;
|
||||
title?: string; // eg symbol name
|
||||
}
|
||||
|
||||
export interface InteractiveProgressContent {
|
||||
content: string | MarkdownString;
|
||||
}
|
||||
|
||||
export interface InteractiveProgressId {
|
||||
responseId: string;
|
||||
}
|
||||
|
||||
export interface InteractiveProgressTask {
|
||||
placeholder: string;
|
||||
resolvedContent: Thenable<InteractiveProgressContent | InteractiveProgressFileTree>;
|
||||
}
|
||||
|
||||
export interface FileTreeData {
|
||||
label: string;
|
||||
uri: Uri;
|
||||
children?: FileTreeData[];
|
||||
}
|
||||
|
||||
export interface InteractiveProgressFileTree {
|
||||
treeData: FileTreeData;
|
||||
}
|
||||
|
||||
export interface DocumentContext {
|
||||
uri: Uri;
|
||||
version: number;
|
||||
ranges: Range[];
|
||||
}
|
||||
|
||||
export interface InteractiveProgressUsedContext {
|
||||
documents: DocumentContext[];
|
||||
}
|
||||
|
||||
export type InteractiveProgress =
|
||||
| InteractiveProgressContent
|
||||
| InteractiveProgressId
|
||||
| InteractiveProgressTask
|
||||
| InteractiveProgressFileTree
|
||||
| InteractiveProgressUsedContext
|
||||
| InteractiveContentReference
|
||||
| InteractiveInlineContentReference;
|
||||
|
||||
export interface InteractiveResponseCommand {
|
||||
commandId: string;
|
||||
args?: any[];
|
||||
|
@ -183,40 +107,18 @@ declare module 'vscode' {
|
|||
when?: string;
|
||||
}
|
||||
|
||||
export interface InteractiveSessionSlashCommand {
|
||||
command: string;
|
||||
kind: CompletionItemKind;
|
||||
detail?: string;
|
||||
shouldRepopulate?: boolean;
|
||||
followupPlaceholder?: string;
|
||||
executeImmediately?: boolean;
|
||||
yieldTo?: ReadonlyArray<{ readonly command: string }>;
|
||||
}
|
||||
|
||||
export interface InteractiveSessionReplyFollowup {
|
||||
message: string;
|
||||
tooltip?: string;
|
||||
title?: string;
|
||||
|
||||
// Extensions can put any serializable data here, such as an ID/version
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
export type InteractiveSessionFollowup = InteractiveSessionReplyFollowup | InteractiveResponseCommand;
|
||||
|
||||
export type InteractiveWelcomeMessageContent = string | MarkdownString | InteractiveSessionReplyFollowup[];
|
||||
|
||||
export interface InteractiveSessionProvider<S extends InteractiveSession = InteractiveSession> {
|
||||
provideWelcomeMessage?(token: CancellationToken): ProviderResult<InteractiveWelcomeMessageContent[]>;
|
||||
provideSampleQuestions?(token: CancellationToken): ProviderResult<InteractiveSessionReplyFollowup[]>;
|
||||
provideFollowups?(session: S, token: CancellationToken): ProviderResult<(string | InteractiveSessionFollowup)[]>;
|
||||
provideSlashCommands?(session: S, token: CancellationToken): ProviderResult<InteractiveSessionSlashCommand[]>;
|
||||
|
||||
prepareSession(initialState: InteractiveSessionState | undefined, token: CancellationToken): ProviderResult<S>;
|
||||
provideResponseWithProgress(request: InteractiveRequest, progress: Progress<InteractiveProgress>, token: CancellationToken): ProviderResult<InteractiveResponseForProgress>;
|
||||
|
||||
// eslint-disable-next-line local/vscode-dts-provider-naming
|
||||
removeRequest(session: S, requestId: string): void;
|
||||
prepareSession(token: CancellationToken): ProviderResult<S>;
|
||||
}
|
||||
|
||||
export interface InteractiveSessionDynamicRequest {
|
||||
|
@ -224,12 +126,6 @@ declare module 'vscode' {
|
|||
* The message that will be displayed in the UI
|
||||
*/
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* Any extra metadata/context that will go to the provider.
|
||||
* NOTE not actually used yet.
|
||||
*/
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
export namespace interactive {
|
||||
|
|
|
@ -13,8 +13,6 @@ declare module 'vscode' {
|
|||
export interface InteractiveSessionVoteAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'vote';
|
||||
// sessionId: string;
|
||||
responseId: string;
|
||||
direction: InteractiveSessionVoteDirection;
|
||||
}
|
||||
|
||||
|
@ -27,8 +25,6 @@ declare module 'vscode' {
|
|||
export interface InteractiveSessionCopyAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'copy';
|
||||
// sessionId: string;
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
copyType: InteractiveSessionCopyKind;
|
||||
copiedCharacters: number;
|
||||
|
@ -39,8 +35,6 @@ declare module 'vscode' {
|
|||
export interface InteractiveSessionInsertAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'insert';
|
||||
// sessionId: string;
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
totalCharacters: number;
|
||||
newFile?: boolean;
|
||||
|
@ -49,8 +43,6 @@ declare module 'vscode' {
|
|||
export interface InteractiveSessionTerminalAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'runInTerminal';
|
||||
// sessionId: string;
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
languageId?: string;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue