mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-26 11:38:51 +01:00
prepare for not having a "real" inline chat provider (#210069)
* prepare for not having a "real" inline chat provider - add location to provideWelcomeMessage - add dynamic/fake inline chat provider for editor agents - fix inline content widget when not having a message * fix tests
This commit is contained in:
@@ -5,19 +5,18 @@
|
||||
import { coalesceInPlace, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { raceCancellation } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { CancellationError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableMap, DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { TextEdit } from 'vs/editor/common/languages';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { TextEdit, WorkspaceEdit } from 'vs/editor/common/languages';
|
||||
import { ITextModel, IValidEditOperation } from 'vs/editor/common/model';
|
||||
import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker';
|
||||
@@ -25,18 +24,22 @@ import { IModelService } from 'vs/editor/common/services/model';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Progress } from 'vs/platform/progress/common/progress';
|
||||
import { IProgress, Progress } from 'vs/platform/progress/common/progress';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor';
|
||||
import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentHistoryEntry, IChatAgentImplementation, IChatAgentRequest, IChatAgentResult, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatAgentLocation, IChatAgent, IChatAgentCommand, IChatAgentData, IChatAgentHistoryEntry, IChatAgentImplementation, IChatAgentRequest, IChatAgentResult, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatFollowup, IChatProgress, IChatService, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { EditMode, IInlineChatBulkEditResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatResponse, IInlineChatService, IInlineChatSession, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { EditMode, IInlineChatBulkEditResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatResponse, IInlineChatService, IInlineChatSession, IInlineChatSessionProvider, IInlineChatSlashCommand, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
|
||||
import { EmptyResponse, ErrorResponse, HunkData, ReplyResponse, Session, SessionExchange, SessionWholeRange, StashedSession, TelemetryData, TelemetryDataClassification } from './inlineChatSession';
|
||||
import { IInlineChatSessionEndEvent, IInlineChatSessionEvent, IInlineChatSessionService, ISessionKeyComputer, Recording } from './inlineChatSessionService';
|
||||
import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
|
||||
class BridgeAgent implements IChatAgentImplementation {
|
||||
|
||||
@@ -186,7 +189,7 @@ class BridgeAgent implements IChatAgentImplementation {
|
||||
return chatFollowups;
|
||||
}
|
||||
|
||||
provideWelcomeMessage(token: CancellationToken): string[] {
|
||||
provideWelcomeMessage(location: ChatAgentLocation, token: CancellationToken): string[] {
|
||||
// without this provideSampleQuestions is not called
|
||||
return [];
|
||||
}
|
||||
@@ -227,6 +230,17 @@ export class InlineChatError extends Error {
|
||||
const _bridgeAgentId = 'brigde.editor';
|
||||
const _inlineChatContext = '_inlineChatContext';
|
||||
|
||||
class InlineChatContext {
|
||||
|
||||
static readonly variableName = '_inlineChatContext';
|
||||
|
||||
constructor(
|
||||
readonly uri: URI,
|
||||
readonly selection: ISelection,
|
||||
readonly wholeRange: IRange,
|
||||
) { }
|
||||
}
|
||||
|
||||
export class InlineChatSessionServiceImpl implements IInlineChatSessionService {
|
||||
|
||||
declare _serviceBrand: undefined;
|
||||
@@ -265,6 +279,37 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService {
|
||||
@IChatVariablesService chatVariableService: IChatVariablesService,
|
||||
) {
|
||||
|
||||
const fakeProviders = this._store.add(new DisposableMap<string, IDisposable>());
|
||||
|
||||
this._store.add(this._chatAgentService.onDidChangeAgents(() => {
|
||||
|
||||
const providersNow = new Set<string>();
|
||||
|
||||
for (const agent of this._chatAgentService.getActivatedAgents()) {
|
||||
if (agent.id === _bridgeAgentId) {
|
||||
// not interesting
|
||||
continue;
|
||||
}
|
||||
if (!agent.locations.includes(ChatAgentLocation.Editor) || !agent.isDefault) {
|
||||
// not interesting
|
||||
continue;
|
||||
}
|
||||
providersNow.add(agent.id);
|
||||
|
||||
if (!fakeProviders.has(agent.id)) {
|
||||
fakeProviders.set(agent.id, _inlineChatService.addProvider(_instaService.createInstance(AgentInlineChatProvider, agent)));
|
||||
this._logService.info(`ADDED inline chat provider for agent ${agent.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [id] of fakeProviders) {
|
||||
if (!providersNow.has(id)) {
|
||||
fakeProviders.deleteAndDispose(id);
|
||||
this._logService.info(`REMOVED inline chat provider for agent ${id}`);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// MARK: register fake chat agent
|
||||
const addOrRemoveBridgeAgent = () => {
|
||||
const that = this;
|
||||
@@ -339,10 +384,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService {
|
||||
if (data.session.chatModel === model) {
|
||||
return [{
|
||||
level: 'full',
|
||||
value: JSON.stringify({
|
||||
selection: data.editor.getSelection(),
|
||||
wholeRange: data.session.wholeRange.trackedInitialRange
|
||||
})
|
||||
value: JSON.stringify(new InlineChatContext(data.session.textModelN.uri, data.editor.getSelection()!, data.session.wholeRange.trackedInitialRange))
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -360,7 +402,21 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService {
|
||||
|
||||
async createSession(editor: IActiveCodeEditor, options: { editMode: EditMode; wholeRange?: Range }, token: CancellationToken): Promise<Session | undefined> {
|
||||
|
||||
const provider = Iterable.first(this._inlineChatService.getAllProvider());
|
||||
const agent = this._chatAgentService.getDefaultAgent(ChatAgentLocation.Editor);
|
||||
let provider: IInlineChatSessionProvider | undefined;
|
||||
if (agent) {
|
||||
for (const candidate of this._inlineChatService.getAllProvider()) {
|
||||
if (candidate instanceof AgentInlineChatProvider && candidate.agent === agent) {
|
||||
provider = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!provider) {
|
||||
provider = Iterable.first(this._inlineChatService.getAllProvider());
|
||||
}
|
||||
|
||||
if (!provider) {
|
||||
this._logService.trace('[IE] NO provider found');
|
||||
return undefined;
|
||||
@@ -684,3 +740,92 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService {
|
||||
return this._recordings;
|
||||
}
|
||||
}
|
||||
|
||||
export class AgentInlineChatProvider implements IInlineChatSessionProvider {
|
||||
|
||||
readonly extensionId: ExtensionIdentifier;
|
||||
readonly label: string;
|
||||
readonly supportIssueReporting?: boolean | undefined;
|
||||
|
||||
constructor(
|
||||
readonly agent: IChatAgent,
|
||||
@IChatAgentService private readonly _chatAgentService: IChatAgentService,
|
||||
) {
|
||||
this.label = agent.name;
|
||||
this.extensionId = agent.extensionId;
|
||||
this.supportIssueReporting = agent.metadata.supportIssueReporting;
|
||||
}
|
||||
|
||||
async prepareInlineChatSession(model: ITextModel, range: ISelection, token: CancellationToken): Promise<IInlineChatSession> {
|
||||
|
||||
// TODO@jrieken have a good welcome message
|
||||
// const welcomeMessage = await this.agent.provideWelcomeMessage?.(ChatAgentLocation.Editor, token);
|
||||
// const message = welcomeMessage?.filter(candidate => typeof candidate === 'string').join(''),
|
||||
|
||||
return {
|
||||
id: Math.random(),
|
||||
wholeRange: new Range(range.selectionStartLineNumber, range.selectionStartColumn, range.positionLineNumber, range.positionColumn),
|
||||
placeholder: this.agent.description,
|
||||
slashCommands: this.agent.slashCommands.map(agentCommand => {
|
||||
return {
|
||||
command: agentCommand.name,
|
||||
detail: agentCommand.description,
|
||||
refer: agentCommand.name === 'explain' // TODO@jrieken @joyceerhl this should be cleaned up
|
||||
} satisfies IInlineChatSlashCommand;
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
async provideResponse(item: IInlineChatSession, request: IInlineChatRequest, progress: IProgress<IInlineChatProgressItem>, token: CancellationToken): Promise<IInlineChatResponse> {
|
||||
|
||||
const workspaceEdit: WorkspaceEdit = { edits: [] };
|
||||
|
||||
await this._chatAgentService.invokeAgent(this.agent.id, {
|
||||
sessionId: String(item.id),
|
||||
requestId: request.requestId,
|
||||
agentId: this.agent.id,
|
||||
message: request.prompt,
|
||||
location: ChatAgentLocation.Editor,
|
||||
variables: {
|
||||
variables: [{
|
||||
name: InlineChatContext.variableName,
|
||||
values: [{
|
||||
level: 'full',
|
||||
value: JSON.stringify(new InlineChatContext(request.previewDocument, request.selection, request.wholeRange))
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}, part => {
|
||||
|
||||
if (part.kind === 'markdownContent') {
|
||||
progress.report({ markdownFragment: part.content.value });
|
||||
} else if (part.kind === 'agentDetection') {
|
||||
progress.report({ slashCommand: part.command?.name });
|
||||
} else if (part.kind === 'textEdit') {
|
||||
|
||||
if (isEqual(request.previewDocument, part.uri)) {
|
||||
progress.report({ edits: part.edits });
|
||||
} else {
|
||||
for (const textEdit of part.edits) {
|
||||
workspaceEdit.edits.push({ resource: part.uri, textEdit, versionId: undefined });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, [], token);
|
||||
|
||||
return {
|
||||
type: InlineChatResponseType.BulkEdit,
|
||||
id: Math.random(),
|
||||
edits: workspaceEdit
|
||||
};
|
||||
}
|
||||
|
||||
// handleInlineChatResponseFeedback?(session: IInlineChatSession, response: IInlineChatResponse, kind: InlineChatResponseFeedbackKind): void {
|
||||
// throw new Error('Method not implemented.');
|
||||
// }
|
||||
|
||||
// provideFollowups?(session: IInlineChatSession, response: IInlineChatResponse, token: CancellationToken): ProviderResult<IInlineChatFollowup[]> {
|
||||
// throw new Error('Method not implemented.');
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user