mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-20 10:19:02 +00:00
Add editedFileEvents (#246996)
* Start plumbing working set events * more * More fixes * Update tests
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
import { ChatContext, ChatRequest, ChatResult, Disposable, Event, EventEmitter, chat, commands, lm } from 'vscode';
|
import { ChatContext, ChatRequest, ChatRequestTurn, ChatRequestTurn2, ChatResult, Disposable, Event, EventEmitter, chat, commands, lm } from 'vscode';
|
||||||
import { DeferredPromise, asPromise, assertNoRpc, closeAllEditors, delay, disposeAll } from '../utils';
|
import { DeferredPromise, asPromise, assertNoRpc, closeAllEditors, delay, disposeAll } from '../utils';
|
||||||
|
|
||||||
suite('chat', () => {
|
suite('chat', () => {
|
||||||
@@ -71,6 +71,7 @@ suite('chat', () => {
|
|||||||
assert.strictEqual(request.context.history.length, 2);
|
assert.strictEqual(request.context.history.length, 2);
|
||||||
assert.strictEqual(request.context.history[0].participant, 'api-test.participant');
|
assert.strictEqual(request.context.history[0].participant, 'api-test.participant');
|
||||||
assert.strictEqual(request.context.history[0].command, 'hello');
|
assert.strictEqual(request.context.history[0].command, 'hello');
|
||||||
|
assert.ok(request.context.history[0] instanceof ChatRequestTurn && request.context.history[0] instanceof ChatRequestTurn2);
|
||||||
deferred.complete();
|
deferred.complete();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1800,11 +1800,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||||||
ChatResponseExtensionsPart: extHostTypes.ChatResponseExtensionsPart,
|
ChatResponseExtensionsPart: extHostTypes.ChatResponseExtensionsPart,
|
||||||
ChatResponseReferencePartStatusKind: extHostTypes.ChatResponseReferencePartStatusKind,
|
ChatResponseReferencePartStatusKind: extHostTypes.ChatResponseReferencePartStatusKind,
|
||||||
ChatRequestTurn: extHostTypes.ChatRequestTurn,
|
ChatRequestTurn: extHostTypes.ChatRequestTurn,
|
||||||
|
ChatRequestTurn2: extHostTypes.ChatRequestTurn,
|
||||||
ChatResponseTurn: extHostTypes.ChatResponseTurn,
|
ChatResponseTurn: extHostTypes.ChatResponseTurn,
|
||||||
ChatLocation: extHostTypes.ChatLocation,
|
ChatLocation: extHostTypes.ChatLocation,
|
||||||
ChatRequestEditorData: extHostTypes.ChatRequestEditorData,
|
ChatRequestEditorData: extHostTypes.ChatRequestEditorData,
|
||||||
ChatRequestNotebookData: extHostTypes.ChatRequestNotebookData,
|
ChatRequestNotebookData: extHostTypes.ChatRequestNotebookData,
|
||||||
ChatReferenceBinaryData: extHostTypes.ChatReferenceBinaryData,
|
ChatReferenceBinaryData: extHostTypes.ChatReferenceBinaryData,
|
||||||
|
ChatRequestEditedFileEventKind: extHostTypes.ChatRequestEditedFileEventKind,
|
||||||
LanguageModelChatMessageRole: extHostTypes.LanguageModelChatMessageRole,
|
LanguageModelChatMessageRole: extHostTypes.LanguageModelChatMessageRole,
|
||||||
LanguageModelChatMessage: extHostTypes.LanguageModelChatMessage,
|
LanguageModelChatMessage: extHostTypes.LanguageModelChatMessage,
|
||||||
LanguageModelChatMessage2: extHostTypes.LanguageModelChatMessage2,
|
LanguageModelChatMessage2: extHostTypes.LanguageModelChatMessage2,
|
||||||
|
|||||||
@@ -410,8 +410,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS
|
|||||||
const { request, location, history } = await this._createRequest(requestDto, context, detector.extension);
|
const { request, location, history } = await this._createRequest(requestDto, context, detector.extension);
|
||||||
|
|
||||||
const model = await this.getModelForRequest(request, detector.extension);
|
const model = await this.getModelForRequest(request, detector.extension);
|
||||||
const includeInteractionId = isProposedApiEnabled(detector.extension, 'chatParticipantPrivate');
|
const extRequest = typeConvert.ChatAgentRequest.to(request, location, model, this.getDiagnosticsWhenEnabled(detector.extension), this.getToolsForRequest(detector.extension, request), detector.extension);
|
||||||
const extRequest = typeConvert.ChatAgentRequest.to(includeInteractionId ? request : { ...request, requestId: '' }, location, model, this.getDiagnosticsWhenEnabled(detector.extension), this.getToolsForRequest(detector.extension, request));
|
|
||||||
|
|
||||||
return detector.provider.provideParticipantDetection(
|
return detector.provider.provideParticipantDetection(
|
||||||
extRequest,
|
extRequest,
|
||||||
@@ -495,13 +494,13 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS
|
|||||||
stream = new ChatAgentResponseStream(agent.extension, request, this._proxy, this._commands.converter, sessionDisposables);
|
stream = new ChatAgentResponseStream(agent.extension, request, this._proxy, this._commands.converter, sessionDisposables);
|
||||||
|
|
||||||
const model = await this.getModelForRequest(request, agent.extension);
|
const model = await this.getModelForRequest(request, agent.extension);
|
||||||
const includeInteractionId = isProposedApiEnabled(agent.extension, 'chatParticipantPrivate');
|
|
||||||
const extRequest = typeConvert.ChatAgentRequest.to(
|
const extRequest = typeConvert.ChatAgentRequest.to(
|
||||||
includeInteractionId ? request : { ...request, requestId: '' },
|
request,
|
||||||
location,
|
location,
|
||||||
model,
|
model,
|
||||||
this.getDiagnosticsWhenEnabled(agent.extension),
|
this.getDiagnosticsWhenEnabled(agent.extension),
|
||||||
this.getToolsForRequest(agent.extension, request)
|
this.getToolsForRequest(agent.extension, request),
|
||||||
|
agent.extension
|
||||||
);
|
);
|
||||||
inFlightRequest = { requestId: requestDto.requestId, extRequest };
|
inFlightRequest = { requestId: requestDto.requestId, extRequest };
|
||||||
this._inFlightRequests.add(inFlightRequest);
|
this._inFlightRequests.add(inFlightRequest);
|
||||||
@@ -585,7 +584,8 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS
|
|||||||
const toolReferences = h.request.variables.variables
|
const toolReferences = h.request.variables.variables
|
||||||
.filter(v => v.kind === 'tool')
|
.filter(v => v.kind === 'tool')
|
||||||
.map(typeConvert.ChatLanguageModelToolReference.to);
|
.map(typeConvert.ChatLanguageModelToolReference.to);
|
||||||
const turn = new extHostTypes.ChatRequestTurn(h.request.message, h.request.command, varsWithoutTools, h.request.agentId, toolReferences);
|
const editedFileEvents = isProposedApiEnabled(extension, 'chatParticipantPrivate') ? h.request.editedFileEvents : undefined;
|
||||||
|
const turn = new extHostTypes.ChatRequestTurn(h.request.message, h.request.command, varsWithoutTools, h.request.agentId, toolReferences, editedFileEvents);
|
||||||
res.push(turn);
|
res.push(turn);
|
||||||
|
|
||||||
// RESPONSE turn
|
// RESPONSE turn
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import * as languageSelector from '../../../editor/common/languageSelector.js';
|
|||||||
import * as languages from '../../../editor/common/languages.js';
|
import * as languages from '../../../editor/common/languages.js';
|
||||||
import { EndOfLineSequence, TrackedRangeStickiness } from '../../../editor/common/model.js';
|
import { EndOfLineSequence, TrackedRangeStickiness } from '../../../editor/common/model.js';
|
||||||
import { ITextEditorOptions } from '../../../platform/editor/common/editor.js';
|
import { ITextEditorOptions } from '../../../platform/editor/common/editor.js';
|
||||||
import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
|
import { IExtensionDescription, IRelaxedExtensionDescription } from '../../../platform/extensions/common/extensions.js';
|
||||||
import { IMarkerData, IRelatedInformation, MarkerSeverity, MarkerTag } from '../../../platform/markers/common/markers.js';
|
import { IMarkerData, IRelatedInformation, MarkerSeverity, MarkerTag } from '../../../platform/markers/common/markers.js';
|
||||||
import { ProgressLocation as MainProgressLocation } from '../../../platform/progress/common/progress.js';
|
import { ProgressLocation as MainProgressLocation } from '../../../platform/progress/common/progress.js';
|
||||||
import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from '../../common/editor.js';
|
import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from '../../common/editor.js';
|
||||||
@@ -55,7 +55,7 @@ import { TestId } from '../../contrib/testing/common/testId.js';
|
|||||||
import { CoverageDetails, DetailType, ICoverageCount, IFileCoverage, ISerializedTestResults, ITestErrorMessage, ITestItem, ITestRunProfileReference, ITestTag, TestMessageType, TestResultItem, TestRunProfileBitset, denamespaceTestTag, namespaceTestTag } from '../../contrib/testing/common/testTypes.js';
|
import { CoverageDetails, DetailType, ICoverageCount, IFileCoverage, ISerializedTestResults, ITestErrorMessage, ITestItem, ITestRunProfileReference, ITestTag, TestMessageType, TestResultItem, TestRunProfileBitset, denamespaceTestTag, namespaceTestTag } from '../../contrib/testing/common/testTypes.js';
|
||||||
import { EditorGroupColumn } from '../../services/editor/common/editorGroupColumn.js';
|
import { EditorGroupColumn } from '../../services/editor/common/editorGroupColumn.js';
|
||||||
import { ACTIVE_GROUP, SIDE_GROUP } from '../../services/editor/common/editorService.js';
|
import { ACTIVE_GROUP, SIDE_GROUP } from '../../services/editor/common/editorService.js';
|
||||||
import { checkProposedApiEnabled } from '../../services/extensions/common/extensions.js';
|
import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js';
|
||||||
import { Dto } from '../../services/extensions/common/proxyIdentifier.js';
|
import { Dto } from '../../services/extensions/common/proxyIdentifier.js';
|
||||||
import * as extHostProtocol from './extHost.protocol.js';
|
import * as extHostProtocol from './extHost.protocol.js';
|
||||||
import { CommandsConverter } from './extHostCommands.js';
|
import { CommandsConverter } from './extHostCommands.js';
|
||||||
@@ -2897,10 +2897,11 @@ export namespace ChatResponsePart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export namespace ChatAgentRequest {
|
export namespace ChatAgentRequest {
|
||||||
export function to(request: IChatAgentRequest, location2: vscode.ChatRequestEditorData | vscode.ChatRequestNotebookData | undefined, model: vscode.LanguageModelChat, diagnostics: readonly [vscode.Uri, readonly vscode.Diagnostic[]][], tools: vscode.LanguageModelToolInformation[] | undefined): vscode.ChatRequest {
|
export function to(request: IChatAgentRequest, location2: vscode.ChatRequestEditorData | vscode.ChatRequestNotebookData | undefined, model: vscode.LanguageModelChat, diagnostics: readonly [vscode.Uri, readonly vscode.Diagnostic[]][], tools: vscode.LanguageModelToolInformation[] | undefined, extension: IRelaxedExtensionDescription): vscode.ChatRequest {
|
||||||
const toolReferences = request.variables.variables.filter(v => v.kind === 'tool');
|
const toolReferences = request.variables.variables.filter(v => v.kind === 'tool');
|
||||||
const variableReferences = request.variables.variables.filter(v => v.kind !== 'tool');
|
const variableReferences = request.variables.variables.filter(v => v.kind !== 'tool');
|
||||||
const requestWithoutId = {
|
const requestWithAllProps: vscode.ChatRequest = {
|
||||||
|
id: request.requestId,
|
||||||
prompt: request.message,
|
prompt: request.message,
|
||||||
command: request.command,
|
command: request.command,
|
||||||
attempt: request.attempt ?? 0,
|
attempt: request.attempt ?? 0,
|
||||||
@@ -2914,16 +2915,28 @@ export namespace ChatAgentRequest {
|
|||||||
location2,
|
location2,
|
||||||
toolInvocationToken: Object.freeze({ sessionId: request.sessionId }) as never,
|
toolInvocationToken: Object.freeze({ sessionId: request.sessionId }) as never,
|
||||||
tools,
|
tools,
|
||||||
model
|
model,
|
||||||
|
editedFileEvents: request.editedFileEvents,
|
||||||
};
|
};
|
||||||
if (request.requestId) {
|
|
||||||
return {
|
if (!isProposedApiEnabled(extension, 'chatParticipantPrivate')) {
|
||||||
...requestWithoutId,
|
delete (requestWithAllProps as any).id;
|
||||||
id: request.requestId
|
delete (requestWithAllProps as any).attempt;
|
||||||
};
|
delete (requestWithAllProps as any).enableCommandDetection;
|
||||||
|
delete (requestWithAllProps as any).isParticipantDetected;
|
||||||
|
delete (requestWithAllProps as any).location;
|
||||||
|
delete (requestWithAllProps as any).location2;
|
||||||
|
delete (requestWithAllProps as any).editedFileEvents;
|
||||||
}
|
}
|
||||||
// This cast is done to allow sending the stabl version of ChatRequest which does not have an id property
|
|
||||||
return requestWithoutId as unknown as vscode.ChatRequest;
|
if (!isProposedApiEnabled(extension, 'chatParticipantAdditions')) {
|
||||||
|
delete requestWithAllProps.acceptedConfirmationData;
|
||||||
|
delete requestWithAllProps.rejectedConfirmationData;
|
||||||
|
delete (requestWithAllProps as any).tools;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return requestWithAllProps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4525,6 +4525,12 @@ export enum ChatEditingSessionActionOutcome {
|
|||||||
Saved = 3
|
Saved = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ChatRequestEditedFileEventKind {
|
||||||
|
Keep = 1,
|
||||||
|
Undo = 2,
|
||||||
|
UserModification = 3,
|
||||||
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Interactive Editor
|
//#region Interactive Editor
|
||||||
@@ -4718,13 +4724,14 @@ export class ChatResponseNotebookEditPart implements vscode.ChatResponseNotebook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChatRequestTurn implements vscode.ChatRequestTurn {
|
export class ChatRequestTurn implements vscode.ChatRequestTurn2 {
|
||||||
constructor(
|
constructor(
|
||||||
readonly prompt: string,
|
readonly prompt: string,
|
||||||
readonly command: string | undefined,
|
readonly command: string | undefined,
|
||||||
readonly references: vscode.ChatPromptReference[],
|
readonly references: vscode.ChatPromptReference[],
|
||||||
readonly participant: string,
|
readonly participant: string,
|
||||||
readonly toolReferences: vscode.ChatLanguageModelToolReference[]
|
readonly toolReferences: vscode.ChatLanguageModelToolReference[],
|
||||||
|
readonly editedFileEvents?: vscode.ChatRequestEditedFileEvent[]
|
||||||
) { }
|
) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -236,7 +236,6 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie
|
|||||||
const e_sum = this._edit;
|
const e_sum = this._edit;
|
||||||
const e_ai = edit;
|
const e_ai = edit;
|
||||||
this._edit = e_sum.compose(e_ai);
|
this._edit = e_sum.compose(e_ai);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// e_ai
|
// e_ai
|
||||||
@@ -269,6 +268,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._allEditsAreFromUs = false;
|
this._allEditsAreFromUs = false;
|
||||||
|
this._userEditScheduler.schedule();
|
||||||
this._updateDiffInfoSeq();
|
this._updateDiffInfoSeq();
|
||||||
|
|
||||||
const didResetToOriginalContent = this.modifiedModel.getValue() === this.initialContent;
|
const didResetToOriginalContent = this.modifiedModel.getValue() === this.initialContent;
|
||||||
@@ -348,6 +348,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie
|
|||||||
await this._updateDiffInfoSeq();
|
await this._updateDiffInfoSeq();
|
||||||
if (this._diffInfo.get().identical) {
|
if (this._diffInfo.get().identical) {
|
||||||
this._stateObs.set(ModifiedFileEntryState.Accepted, undefined);
|
this._stateObs.set(ModifiedFileEntryState.Accepted, undefined);
|
||||||
|
this._notifyAction('accepted');
|
||||||
}
|
}
|
||||||
this._accessibilitySignalService.playSignal(AccessibilitySignal.editsKept, { allowManyInParallel: true });
|
this._accessibilitySignalService.playSignal(AccessibilitySignal.editsKept, { allowManyInParallel: true });
|
||||||
return true;
|
return true;
|
||||||
@@ -366,6 +367,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie
|
|||||||
await this._updateDiffInfoSeq();
|
await this._updateDiffInfoSeq();
|
||||||
if (this._diffInfo.get().identical) {
|
if (this._diffInfo.get().identical) {
|
||||||
this._stateObs.set(ModifiedFileEntryState.Rejected, undefined);
|
this._stateObs.set(ModifiedFileEntryState.Rejected, undefined);
|
||||||
|
this._notifyAction('rejected');
|
||||||
}
|
}
|
||||||
this._accessibilitySignalService.playSignal(AccessibilitySignal.editsUndone, { allowManyInParallel: true });
|
this._accessibilitySignalService.playSignal(AccessibilitySignal.editsUndone, { allowManyInParallel: true });
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { RunOnceScheduler } from '../../../../../base/common/async.js';
|
||||||
import { Emitter } from '../../../../../base/common/event.js';
|
import { Emitter } from '../../../../../base/common/event.js';
|
||||||
import { Disposable, DisposableMap, MutableDisposable } from '../../../../../base/common/lifecycle.js';
|
import { Disposable, DisposableMap, MutableDisposable } from '../../../../../base/common/lifecycle.js';
|
||||||
import { Schemas } from '../../../../../base/common/network.js';
|
import { Schemas } from '../../../../../base/common/network.js';
|
||||||
@@ -81,6 +82,8 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im
|
|||||||
|
|
||||||
readonly abstract originalURI: URI;
|
readonly abstract originalURI: URI;
|
||||||
|
|
||||||
|
protected readonly _userEditScheduler = this._register(new RunOnceScheduler(() => this._notifyAction('userModified'), 1000));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly modifiedURI: URI,
|
readonly modifiedURI: URI,
|
||||||
protected _telemetryInfo: IModifiedEntryTelemetryInfo,
|
protected _telemetryInfo: IModifiedEntryTelemetryInfo,
|
||||||
@@ -189,7 +192,7 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im
|
|||||||
|
|
||||||
protected abstract _doReject(tx: ITransaction | undefined): Promise<void>;
|
protected abstract _doReject(tx: ITransaction | undefined): Promise<void>;
|
||||||
|
|
||||||
private _notifyAction(outcome: 'accepted' | 'rejected') {
|
protected _notifyAction(outcome: 'accepted' | 'rejected' | 'userModified') {
|
||||||
this._chatService.notifyUserAction({
|
this._chatService.notifyUserAction({
|
||||||
action: { kind: 'chatEditingSessionAction', uri: this.modifiedURI, hasRemainingEdits: false, outcome },
|
action: { kind: 'chatEditingSessionAction', uri: this.modifiedURI, hasRemainingEdits: false, outcome },
|
||||||
agentId: this._telemetryInfo.agentId,
|
agentId: this._telemetryInfo.agentId,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { IProductService } from '../../../../platform/product/common/productServ
|
|||||||
import { asJson, IRequestService } from '../../../../platform/request/common/request.js';
|
import { asJson, IRequestService } from '../../../../platform/request/common/request.js';
|
||||||
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
|
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
|
||||||
import { ChatContextKeys } from './chatContextKeys.js';
|
import { ChatContextKeys } from './chatContextKeys.js';
|
||||||
import { IChatProgressHistoryResponseContent, IChatRequestVariableData, ISerializableChatAgentData } from './chatModel.js';
|
import { IChatAgentEditedFileEvent, IChatProgressHistoryResponseContent, IChatRequestVariableData, ISerializableChatAgentData } from './chatModel.js';
|
||||||
import { IRawChatCommandContribution } from './chatParticipantContribTypes.js';
|
import { IRawChatCommandContribution } from './chatParticipantContribTypes.js';
|
||||||
import { IChatFollowup, IChatLocationData, IChatProgress, IChatResponseErrorDetails, IChatTaskDto } from './chatService.js';
|
import { IChatFollowup, IChatLocationData, IChatProgress, IChatResponseErrorDetails, IChatTaskDto } from './chatService.js';
|
||||||
import { ChatAgentLocation, ChatMode } from './constants.js';
|
import { ChatAgentLocation, ChatMode } from './constants.js';
|
||||||
@@ -138,6 +138,7 @@ export interface IChatAgentRequest {
|
|||||||
rejectedConfirmationData?: any[];
|
rejectedConfirmationData?: any[];
|
||||||
userSelectedModelId?: string;
|
userSelectedModelId?: string;
|
||||||
userSelectedTools?: string[];
|
userSelectedTools?: string[];
|
||||||
|
editedFileEvents?: IChatAgentEditedFileEvent[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IChatQuestion {
|
export interface IChatQuestion {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Codicon } from '../../../../base/common/codicons.js';
|
|||||||
import { Emitter, Event } from '../../../../base/common/event.js';
|
import { Emitter, Event } from '../../../../base/common/event.js';
|
||||||
import { IMarkdownString, MarkdownString, isMarkdownString } from '../../../../base/common/htmlContent.js';
|
import { IMarkdownString, MarkdownString, isMarkdownString } from '../../../../base/common/htmlContent.js';
|
||||||
import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js';
|
import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js';
|
||||||
|
import { ResourceMap } from '../../../../base/common/map.js';
|
||||||
import { revive } from '../../../../base/common/marshalling.js';
|
import { revive } from '../../../../base/common/marshalling.js';
|
||||||
import { Schemas } from '../../../../base/common/network.js';
|
import { Schemas } from '../../../../base/common/network.js';
|
||||||
import { equals } from '../../../../base/common/objects.js';
|
import { equals } from '../../../../base/common/objects.js';
|
||||||
@@ -27,7 +28,7 @@ import { CellUri, ICellEditOperation } from '../../notebook/common/notebookCommo
|
|||||||
import { IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, reviveSerializedAgent } from './chatAgents.js';
|
import { IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, reviveSerializedAgent } from './chatAgents.js';
|
||||||
import { IChatEditingService, IChatEditingSession } from './chatEditingService.js';
|
import { IChatEditingService, IChatEditingSession } from './chatEditingService.js';
|
||||||
import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from './chatParserTypes.js';
|
import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from './chatParserTypes.js';
|
||||||
import { ChatAgentVoteDirection, ChatAgentVoteDownReason, IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatExtensionsContent, IChatFollowup, IChatLocationData, IChatMarkdownContent, IChatNotebookEdit, IChatProgress, IChatProgressMessage, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatTask, IChatTextEdit, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsedContext, IChatWarningMessage, isIUsedContext } from './chatService.js';
|
import { ChatAgentVoteDirection, ChatAgentVoteDownReason, IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatEditingSessionAction, IChatExtensionsContent, IChatFollowup, IChatLocationData, IChatMarkdownContent, IChatNotebookEdit, IChatProgress, IChatProgressMessage, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatTask, IChatTextEdit, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsedContext, IChatWarningMessage, isIUsedContext } from './chatService.js';
|
||||||
import { IChatRequestVariableValue } from './chatVariables.js';
|
import { IChatRequestVariableValue } from './chatVariables.js';
|
||||||
import { ChatAgentLocation, ChatMode } from './constants.js';
|
import { ChatAgentLocation, ChatMode } from './constants.js';
|
||||||
|
|
||||||
@@ -248,6 +249,7 @@ export interface IChatRequestModel {
|
|||||||
readonly attachedContext?: IChatRequestVariableEntry[];
|
readonly attachedContext?: IChatRequestVariableEntry[];
|
||||||
readonly isCompleteAddedRequest: boolean;
|
readonly isCompleteAddedRequest: boolean;
|
||||||
readonly response?: IChatResponseModel;
|
readonly response?: IChatResponseModel;
|
||||||
|
readonly editedFileEvents?: IChatAgentEditedFileEvent[];
|
||||||
shouldBeRemovedOnSend: IChatRequestDisablement | undefined;
|
shouldBeRemovedOnSend: IChatRequestDisablement | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,6 +391,7 @@ export interface IChatRequestModelParameters {
|
|||||||
isCompleteAddedRequest?: boolean;
|
isCompleteAddedRequest?: boolean;
|
||||||
modelId?: string;
|
modelId?: string;
|
||||||
restoredId?: string;
|
restoredId?: string;
|
||||||
|
editedFileEvents?: IChatAgentEditedFileEvent[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChatRequestModel implements IChatRequestModel {
|
export class ChatRequestModel implements IChatRequestModel {
|
||||||
@@ -406,6 +409,7 @@ export class ChatRequestModel implements IChatRequestModel {
|
|||||||
private readonly _confirmation?: string;
|
private readonly _confirmation?: string;
|
||||||
private readonly _locationData?: IChatLocationData;
|
private readonly _locationData?: IChatLocationData;
|
||||||
private readonly _attachedContext?: IChatRequestVariableEntry[];
|
private readonly _attachedContext?: IChatRequestVariableEntry[];
|
||||||
|
private readonly _editedFileEvents?: IChatAgentEditedFileEvent[];
|
||||||
|
|
||||||
public get session(): ChatModel {
|
public get session(): ChatModel {
|
||||||
return this._session;
|
return this._session;
|
||||||
@@ -443,6 +447,10 @@ export class ChatRequestModel implements IChatRequestModel {
|
|||||||
return this._attachedContext;
|
return this._attachedContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get editedFileEvents(): IChatAgentEditedFileEvent[] | undefined {
|
||||||
|
return this._editedFileEvents;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(params: IChatRequestModelParameters) {
|
constructor(params: IChatRequestModelParameters) {
|
||||||
this._session = params.session;
|
this._session = params.session;
|
||||||
this.message = params.message;
|
this.message = params.message;
|
||||||
@@ -455,6 +463,7 @@ export class ChatRequestModel implements IChatRequestModel {
|
|||||||
this.isCompleteAddedRequest = params.isCompleteAddedRequest ?? false;
|
this.isCompleteAddedRequest = params.isCompleteAddedRequest ?? false;
|
||||||
this.modelId = params.modelId;
|
this.modelId = params.modelId;
|
||||||
this.id = params.restoredId ?? 'request_' + generateUuid();
|
this.id = params.restoredId ?? 'request_' + generateUuid();
|
||||||
|
this._editedFileEvents = params.editedFileEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
adoptTo(session: ChatModel) {
|
adoptTo(session: ChatModel) {
|
||||||
@@ -1082,6 +1091,7 @@ export interface ISerializableChatRequestData {
|
|||||||
codeCitations?: ReadonlyArray<IChatCodeCitation>;
|
codeCitations?: ReadonlyArray<IChatCodeCitation>;
|
||||||
timestamp?: number;
|
timestamp?: number;
|
||||||
confirmation?: string;
|
confirmation?: string;
|
||||||
|
editedFileEvents?: IChatAgentEditedFileEvent[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExportableChatData {
|
export interface IExportableChatData {
|
||||||
@@ -1437,7 +1447,37 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||||||
this.chatEditingService.startOrContinueGlobalEditingSession(this) :
|
this.chatEditingService.startOrContinueGlobalEditingSession(this) :
|
||||||
this.chatEditingService.createEditingSession(this);
|
this.chatEditingService.createEditingSession(this);
|
||||||
this._editingSession = new ObservablePromise(editingSessionPromise);
|
this._editingSession = new ObservablePromise(editingSessionPromise);
|
||||||
this._editingSession.promise.then(editingSession => this._store.isDisposed ? editingSession.dispose() : this._register(editingSession));
|
this._editingSession.promise.then(editingSession => {
|
||||||
|
this._store.isDisposed ? editingSession.dispose() : this._register(editingSession);
|
||||||
|
|
||||||
|
// const currentStates = new ResourceMap<ModifiedFileEntryState>();
|
||||||
|
// this._register(autorun(r => {
|
||||||
|
// editingSession.entries.read(r).forEach(entry => {
|
||||||
|
// const state = entry.state.read(r);
|
||||||
|
// if (state !== currentStates.get(entry.modifiedURI)) {
|
||||||
|
// currentStates.set(entry.modifiedURI, state);
|
||||||
|
// if (state === ModifiedFileEntryState.Rejected) {
|
||||||
|
// this.currentWorkingSetEntries.push({
|
||||||
|
// uri: entry.modifiedURI,
|
||||||
|
// state: ChatAgentWorkingSetEntryState.Rejected
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private currentEditedFileEvents = new ResourceMap<IChatAgentEditedFileEvent>();
|
||||||
|
notifyEditingAction(action: IChatEditingSessionAction): void {
|
||||||
|
const state = action.outcome === 'accepted' ? ChatRequestEditedFileEventKind.Keep :
|
||||||
|
action.outcome === 'rejected' ? ChatRequestEditedFileEventKind.Undo :
|
||||||
|
action.outcome === 'userModified' ? ChatRequestEditedFileEventKind.UserModification : null;
|
||||||
|
if (state === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentEditedFileEvents.set(action.uri, { eventKind: state, uri: action.uri });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deserialize(obj: IExportableChatData): ChatRequestModel[] {
|
private _deserialize(obj: IExportableChatData): ChatRequestModel[] {
|
||||||
@@ -1463,6 +1503,7 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||||||
timestamp: raw.timestamp ?? -1,
|
timestamp: raw.timestamp ?? -1,
|
||||||
restoredId: raw.requestId,
|
restoredId: raw.requestId,
|
||||||
confirmation: raw.confirmation,
|
confirmation: raw.confirmation,
|
||||||
|
editedFileEvents: raw.editedFileEvents,
|
||||||
});
|
});
|
||||||
request.shouldBeRemovedOnSend = raw.isHidden ? { requestId: raw.requestId } : raw.shouldBeRemovedOnSend;
|
request.shouldBeRemovedOnSend = raw.isHidden ? { requestId: raw.requestId } : raw.shouldBeRemovedOnSend;
|
||||||
if (raw.response || raw.result || (raw as any).responseErrorDetails) {
|
if (raw.response || raw.result || (raw as any).responseErrorDetails) {
|
||||||
@@ -1610,6 +1651,8 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addRequest(message: IParsedChatRequest, variableData: IChatRequestVariableData, attempt: number, chatAgent?: IChatAgentData, slashCommand?: IChatAgentCommand, confirmation?: string, locationData?: IChatLocationData, attachments?: IChatRequestVariableEntry[], isCompleteAddedRequest?: boolean, modelId?: string): ChatRequestModel {
|
addRequest(message: IParsedChatRequest, variableData: IChatRequestVariableData, attempt: number, chatAgent?: IChatAgentData, slashCommand?: IChatAgentCommand, confirmation?: string, locationData?: IChatLocationData, attachments?: IChatRequestVariableEntry[], isCompleteAddedRequest?: boolean, modelId?: string): ChatRequestModel {
|
||||||
|
const editedFileEvents = [...this.currentEditedFileEvents.values()];
|
||||||
|
this.currentEditedFileEvents.clear();
|
||||||
const request = new ChatRequestModel({
|
const request = new ChatRequestModel({
|
||||||
session: this,
|
session: this,
|
||||||
message,
|
message,
|
||||||
@@ -1620,7 +1663,8 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||||||
locationData,
|
locationData,
|
||||||
attachedContext: attachments,
|
attachedContext: attachments,
|
||||||
isCompleteAddedRequest,
|
isCompleteAddedRequest,
|
||||||
modelId
|
modelId,
|
||||||
|
editedFileEvents: editedFileEvents.length ? editedFileEvents : undefined,
|
||||||
});
|
});
|
||||||
request.response = new ChatResponseModel({
|
request.response = new ChatResponseModel({
|
||||||
responseContent: [],
|
responseContent: [],
|
||||||
@@ -1804,6 +1848,7 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||||||
codeCitations: r.response?.codeCitations,
|
codeCitations: r.response?.codeCitations,
|
||||||
timestamp: r.timestamp,
|
timestamp: r.timestamp,
|
||||||
confirmation: r.confirmation,
|
confirmation: r.confirmation,
|
||||||
|
editedFileEvents: r.editedFileEvents,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@@ -1882,3 +1927,14 @@ export function getCodeCitationsMessage(citations: ReadonlyArray<IChatCodeCitati
|
|||||||
localize('codeCitations', "Similar code found with {0} license types", licenseTypes.size);
|
localize('codeCitations', "Similar code found with {0} license types", licenseTypes.size);
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ChatRequestEditedFileEventKind {
|
||||||
|
Keep = 1,
|
||||||
|
Undo = 2,
|
||||||
|
UserModification = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IChatAgentEditedFileEvent {
|
||||||
|
readonly uri: URI;
|
||||||
|
readonly eventKind: ChatRequestEditedFileEventKind;
|
||||||
|
}
|
||||||
|
|||||||
@@ -381,7 +381,7 @@ export interface IChatEditingSessionAction {
|
|||||||
kind: 'chatEditingSessionAction';
|
kind: 'chatEditingSessionAction';
|
||||||
uri: URI;
|
uri: URI;
|
||||||
hasRemainingEdits: boolean;
|
hasRemainingEdits: boolean;
|
||||||
outcome: 'accepted' | 'rejected' | 'saved';
|
outcome: 'accepted' | 'rejected' | 'userModified';
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChatUserAction = IChatVoteAction | IChatCopyAction | IChatInsertAction | IChatApplyAction | IChatTerminalAction | IChatCommandAction | IChatFollowupAction | IChatBugReportAction | IChatInlineChatCodeAction | IChatEditingSessionAction;
|
export type ChatUserAction = IChatVoteAction | IChatCopyAction | IChatInsertAction | IChatApplyAction | IChatTerminalAction | IChatCommandAction | IChatFollowupAction | IChatBugReportAction | IChatInlineChatCodeAction | IChatEditingSessionAction;
|
||||||
|
|||||||
@@ -288,6 +288,12 @@ export class ChatService extends Disposable implements IChatService {
|
|||||||
notifyUserAction(action: IChatUserActionEvent): void {
|
notifyUserAction(action: IChatUserActionEvent): void {
|
||||||
this._chatServiceTelemetry.notifyUserAction(action);
|
this._chatServiceTelemetry.notifyUserAction(action);
|
||||||
this._onDidPerformUserAction.fire(action);
|
this._onDidPerformUserAction.fire(action);
|
||||||
|
if (action.action.kind === 'chatEditingSessionAction') {
|
||||||
|
const model = this._sessionModels.get(action.sessionId);
|
||||||
|
if (model) {
|
||||||
|
model.notifyEditingAction(action.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setChatSessionTitle(sessionId: string, title: string): Promise<void> {
|
async setChatSessionTitle(sessionId: string, title: string): Promise<void> {
|
||||||
@@ -761,7 +767,8 @@ export class ChatService extends Disposable implements IChatService {
|
|||||||
acceptedConfirmationData: options?.acceptedConfirmationData,
|
acceptedConfirmationData: options?.acceptedConfirmationData,
|
||||||
rejectedConfirmationData: options?.rejectedConfirmationData,
|
rejectedConfirmationData: options?.rejectedConfirmationData,
|
||||||
userSelectedModelId: options?.userSelectedModelId,
|
userSelectedModelId: options?.userSelectedModelId,
|
||||||
userSelectedTools: options?.userSelectedTools
|
userSelectedTools: options?.userSelectedTools,
|
||||||
|
editedFileEvents: request.editedFileEvents
|
||||||
} satisfies IChatAgentRequest;
|
} satisfies IChatAgentRequest;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -998,7 +1005,8 @@ export class ChatService extends Disposable implements IChatService {
|
|||||||
message: promptTextResult.message,
|
message: promptTextResult.message,
|
||||||
command: request.response.slashCommand?.name,
|
command: request.response.slashCommand?.name,
|
||||||
variables: updateRanges(request.variableData, promptTextResult.diff), // TODO bit of a hack
|
variables: updateRanges(request.variableData, promptTextResult.diff), // TODO bit of a hack
|
||||||
location: ChatAgentLocation.Panel
|
location: ChatAgentLocation.Panel,
|
||||||
|
editedFileEvents: request.editedFileEvents,
|
||||||
};
|
};
|
||||||
history.push({ request: historyRequest, response: toChatHistoryContent(request.response.response.value), result: request.response.result ?? {} });
|
history.push({ request: historyRequest, response: toChatHistoryContent(request.response.response.value), result: request.response.result ?? {} });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,8 @@
|
|||||||
contentReferences: [ ],
|
contentReferences: [ ],
|
||||||
codeCitations: [ ],
|
codeCitations: [ ],
|
||||||
timestamp: undefined,
|
timestamp: undefined,
|
||||||
confirmation: undefined
|
confirmation: undefined,
|
||||||
|
editedFileEvents: undefined
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,8 @@
|
|||||||
contentReferences: [ ],
|
contentReferences: [ ],
|
||||||
codeCitations: [ ],
|
codeCitations: [ ],
|
||||||
timestamp: undefined,
|
timestamp: undefined,
|
||||||
confirmation: undefined
|
confirmation: undefined,
|
||||||
|
editedFileEvents: undefined
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,8 @@
|
|||||||
contentReferences: [ ],
|
contentReferences: [ ],
|
||||||
codeCitations: [ ],
|
codeCitations: [ ],
|
||||||
timestamp: undefined,
|
timestamp: undefined,
|
||||||
confirmation: undefined
|
confirmation: undefined,
|
||||||
|
editedFileEvents: undefined
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requestId: undefined,
|
requestId: undefined,
|
||||||
@@ -162,7 +163,8 @@
|
|||||||
contentReferences: [ ],
|
contentReferences: [ ],
|
||||||
codeCitations: [ ],
|
codeCitations: [ ],
|
||||||
timestamp: undefined,
|
timestamp: undefined,
|
||||||
confirmation: undefined
|
confirmation: undefined,
|
||||||
|
editedFileEvents: undefined
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,8 @@
|
|||||||
contentReferences: [ ],
|
contentReferences: [ ],
|
||||||
codeCitations: [ ],
|
codeCitations: [ ],
|
||||||
timestamp: undefined,
|
timestamp: undefined,
|
||||||
confirmation: undefined
|
confirmation: undefined,
|
||||||
|
editedFileEvents: undefined
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -77,6 +77,67 @@ declare module 'vscode' {
|
|||||||
* or terminal. Will be `undefined` for the chat panel.
|
* or terminal. Will be `undefined` for the chat panel.
|
||||||
*/
|
*/
|
||||||
readonly location2: ChatRequestEditorData | ChatRequestNotebookData | undefined;
|
readonly location2: ChatRequestEditorData | ChatRequestNotebookData | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Events for edited files in this session collected since the last request.
|
||||||
|
*/
|
||||||
|
readonly editedFileEvents?: ChatRequestEditedFileEvent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ChatRequestEditedFileEventKind {
|
||||||
|
Keep = 1,
|
||||||
|
Undo = 2,
|
||||||
|
UserModification = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatRequestEditedFileEvent {
|
||||||
|
readonly uri: Uri;
|
||||||
|
readonly eventKind: ChatRequestEditedFileEventKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChatRequestTurn + private additions. Note- at runtime this is the SAME as ChatRequestTurn and instanceof is safe.
|
||||||
|
*/
|
||||||
|
export class ChatRequestTurn2 {
|
||||||
|
/**
|
||||||
|
* The prompt as entered by the user.
|
||||||
|
*
|
||||||
|
* Information about references used in this request is stored in {@link ChatRequestTurn.references}.
|
||||||
|
*
|
||||||
|
* *Note* that the {@link ChatParticipant.name name} of the participant and the {@link ChatCommand.name command}
|
||||||
|
* are not part of the prompt.
|
||||||
|
*/
|
||||||
|
readonly prompt: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id of the chat participant to which this request was directed.
|
||||||
|
*/
|
||||||
|
readonly participant: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the {@link ChatCommand command} that was selected for this request.
|
||||||
|
*/
|
||||||
|
readonly command?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The references that were used in this message.
|
||||||
|
*/
|
||||||
|
readonly references: ChatPromptReference[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of tools were attached to this request.
|
||||||
|
*/
|
||||||
|
readonly toolReferences: readonly ChatLanguageModelToolReference[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Events for edited files in this session collected between the previous request and this one.
|
||||||
|
*/
|
||||||
|
readonly editedFileEvents?: ChatRequestEditedFileEvent[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
private constructor(prompt: string, command: string | undefined, references: ChatPromptReference[], participant: string, toolReferences: ChatLanguageModelToolReference[], editedFileEvents: ChatRequestEditedFileEvent[] | undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatParticipant {
|
export interface ChatParticipant {
|
||||||
|
|||||||
Reference in New Issue
Block a user