Merge pull request #282324 from microsoft/roblou/irrelevant-anaconda

Store lastResponseState in metadata
This commit is contained in:
Peng Lyu 2025-12-09 17:45:10 -08:00 committed by GitHub
commit da9c9d69cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 86 additions and 32 deletions

View File

@ -61,7 +61,7 @@ import { ChatContextKeys } from '../../common/chatContextKeys.js';
import { ModifiedFileEntryState } from '../../common/chatEditingService.js';
import { IChatModel, IChatResponseModel } from '../../common/chatModel.js';
import { ChatMode, IChatMode, IChatModeService } from '../../common/chatModes.js';
import { IChatDetail, IChatService } from '../../common/chatService.js';
import { IChatDetail, IChatService, ResponseModelState } from '../../common/chatService.js';
import { IChatSessionItem, IChatSessionsService, localChatSessionType } from '../../common/chatSessionsService.js';
import { ISCMHistoryItemChangeRangeVariableEntry, ISCMHistoryItemChangeVariableEntry } from '../../common/chatVariableEntries.js';
import { IChatRequestViewModel, IChatResponseViewModel, isRequestVM } from '../../common/chatViewModel.js';
@ -766,6 +766,7 @@ export function registerChatActions() {
title: session.label,
isActive: false,
lastMessageDate: 0,
lastResponseState: ResponseModelState.Complete
},
buttons,
};

View File

@ -12,7 +12,7 @@ import { ResourceSet } from '../../../../../base/common/map.js';
import { Schemas } from '../../../../../base/common/network.js';
import { IWorkbenchContribution } from '../../../../common/contributions.js';
import { IChatModel } from '../../common/chatModel.js';
import { IChatDetail, IChatService } from '../../common/chatService.js';
import { IChatDetail, IChatService, ResponseModelState } from '../../common/chatService.js';
import { ChatSessionStatus, IChatSessionItem, IChatSessionItemProvider, IChatSessionsService, localChatSessionType } from '../../common/chatSessionsService.js';
import { ChatSessionItemWithProvider } from '../chatSessions/common.js';
@ -147,7 +147,9 @@ export class LocalAgentsSessionsProvider extends Disposable implements IChatSess
provider: this,
label: chat.title,
description,
status: model ? this.modelToStatus(model) : undefined,
status: model ?
this.modelToStatus(model) :
chatResponseStateToSessionStatus(chat.lastResponseState),
iconPath: Codicon.chatSparkle,
timing: {
startTime,
@ -161,3 +163,15 @@ export class LocalAgentsSessionsProvider extends Disposable implements IChatSess
};
}
}
function chatResponseStateToSessionStatus(state: ResponseModelState): ChatSessionStatus {
switch (state) {
case ResponseModelState.Cancelled:
case ResponseModelState.Complete:
return ChatSessionStatus.Completed;
case ResponseModelState.Failed:
return ChatSessionStatus.Failed;
case ResponseModelState.Pending:
return ChatSessionStatus.InProgress;
}
}

View File

@ -32,7 +32,7 @@ import { migrateLegacyTerminalToolSpecificData } from './chat.js';
import { IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, UserSelectedTools, reviveSerializedAgent } from './chatAgents.js';
import { IChatEditingService, IChatEditingSession, ModifiedFileEntryState, editEntriesToMultiDiffData } from './chatEditingService.js';
import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from './chatParserTypes.js';
import { ChatAgentVoteDirection, ChatAgentVoteDownReason, ChatResponseClearToPreviousToolInvocationReason, ElicitationState, IChatAgentMarkdownContentWithVulnerability, IChatClearToPreviousToolInvocation, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatEditingSessionAction, IChatElicitationRequest, IChatElicitationRequestSerialized, IChatExtensionsContent, IChatFollowup, IChatLocationData, IChatMarkdownContent, IChatMcpServersStarting, IChatModelReference, IChatMultiDiffData, IChatMultiDiffDataSerialized, IChatNotebookEdit, IChatPrepareToolInvocationPart, IChatProgress, IChatProgressMessage, IChatPullRequestContent, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatService, IChatSessionContext, IChatTask, IChatTaskSerialized, IChatTextEdit, IChatThinkingPart, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsedContext, IChatWarningMessage, isIUsedContext } from './chatService.js';
import { ChatAgentVoteDirection, ChatAgentVoteDownReason, ChatResponseClearToPreviousToolInvocationReason, ElicitationState, IChatAgentMarkdownContentWithVulnerability, IChatClearToPreviousToolInvocation, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatEditingSessionAction, IChatElicitationRequest, IChatElicitationRequestSerialized, IChatExtensionsContent, IChatFollowup, IChatLocationData, IChatMarkdownContent, IChatMcpServersStarting, IChatModelReference, IChatMultiDiffData, IChatMultiDiffDataSerialized, IChatNotebookEdit, IChatPrepareToolInvocationPart, IChatProgress, IChatProgressMessage, IChatPullRequestContent, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatService, IChatSessionContext, IChatTask, IChatTaskSerialized, IChatTextEdit, IChatThinkingPart, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsedContext, IChatWarningMessage, ResponseModelState, isIUsedContext } from './chatService.js';
import { LocalChatSessionUri } from './chatUri.js';
import { ChatRequestToolReferenceEntry, IChatRequestVariableEntry } from './chatVariableEntries.js';
import { ChatAgentLocation, ChatModeKind } from './constants.js';
@ -194,6 +194,8 @@ export interface IChatResponseModel {
readonly timestamp: number;
/** Milliseconds timestamp when this chat response was completed or cancelled. */
readonly completedAt?: number;
/** The state of this response */
readonly state: ResponseModelState;
/**
* Adjusted millisecond timestamp that excludes the duration during which
* the model was pending user confirmation. `Date.now() - confirmationAdjustedTimestamp`
@ -772,15 +774,9 @@ export interface IChatResponseModelParameters {
codeBlockInfos: ICodeBlockInfo[] | undefined;
}
const enum ResponseModelState {
Pending,
Complete,
Cancelled,
}
type ResponseModelStateT =
| { value: ResponseModelState.Pending }
| { value: ResponseModelState.Complete | ResponseModelState.Cancelled; completedAt: number };
| { value: ResponseModelState.Complete | ResponseModelState.Cancelled | ResponseModelState.Failed; completedAt: number };
export class ChatResponseModel extends Disposable implements IChatResponseModel {
private readonly _onDidChange = this._register(new Emitter<ChatResponseModelChangeReason>());
@ -838,12 +834,22 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
public get completedAt(): number | undefined {
const state = this._modelState.get();
if (state.value === ResponseModelState.Complete || state.value === ResponseModelState.Cancelled) {
if (state.value === ResponseModelState.Complete || state.value === ResponseModelState.Cancelled || state.value === ResponseModelState.Failed) {
return state.completedAt;
}
return undefined;
}
public get state(): ResponseModelState {
const state = this._modelState.get().value;
if (state === ResponseModelState.Complete && !!this._result?.errorDetails && this.result?.errorDetails?.code !== 'canceled') {
// This check covers sessions created in previous vscode versions which saved a failed response as 'Complete'
return ResponseModelState.Failed;
}
return state;
}
public get vote(): ChatAgentVoteDirection | undefined {
return this._vote;
}
@ -1073,7 +1079,9 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
this._response.clear();
}
this._modelState.set({ value: ResponseModelState.Complete, completedAt: Date.now() }, undefined);
// Canceled sessions can be considered 'Complete'
const state = !!this._result?.errorDetails && this._result.errorDetails.code !== 'canceled' ? ResponseModelState.Failed : ResponseModelState.Complete;
this._modelState.set({ value: state, completedAt: Date.now() }, undefined);
this._onDidChange.fire({ reason: 'completedRequest' });
}

View File

@ -916,12 +916,20 @@ export interface IChatSessionStats {
removed: number;
}
export const enum ResponseModelState {
Pending,
Complete,
Cancelled,
Failed
}
export interface IChatDetail {
sessionResource: URI;
title: string;
lastMessageDate: number;
isActive: boolean;
stats?: IChatSessionStats;
lastResponseState: ResponseModelState;
}
export interface IChatProviderInfo {

View File

@ -36,7 +36,7 @@ import { ChatModel, ChatRequestModel, ChatRequestRemovalReason, IChatModel, ICha
import { ChatModelStore, IStartSessionProps } from './chatModelStore.js';
import { chatAgentLeader, ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, ChatRequestTextPart, chatSubcommandLeader, getPromptText, IParsedChatRequest } from './chatParserTypes.js';
import { ChatRequestParser } from './chatRequestParser.js';
import { ChatMcpServersStarting, IChatCompleteResponse, IChatDetail, IChatFollowup, IChatModelReference, IChatProgress, IChatSendRequestData, IChatSendRequestOptions, IChatSendRequestResponseState, IChatService, IChatSessionContext, IChatSessionStartOptions, IChatTransferredSessionData, IChatUserActionEvent } from './chatService.js';
import { ChatMcpServersStarting, IChatCompleteResponse, IChatDetail, IChatFollowup, IChatModelReference, IChatProgress, IChatSendRequestData, IChatSendRequestOptions, IChatSendRequestResponseState, IChatService, IChatSessionContext, IChatSessionStartOptions, IChatTransferredSessionData, IChatUserActionEvent, ResponseModelState } from './chatService.js';
import { ChatRequestTelemetry, ChatServiceTelemetry } from './chatServiceTelemetry.js';
import { IChatSessionsService } from './chatSessionsService.js';
import { ChatSessionStore, IChatSessionEntryMetadata, IChatTransfer2 } from './chatSessionStore.js';
@ -401,6 +401,7 @@ export class ChatService extends Disposable implements IChatService {
lastMessageDate: session.lastMessageDate,
isActive: true,
stats: await awaitStatsForSession(session),
lastResponseState: session.lastRequest?.response?.state ?? ResponseModelState.Pending,
};
}));
}
@ -419,6 +420,8 @@ export class ChatService extends Disposable implements IChatService {
...entry,
sessionResource,
isActive: this._sessionModels.has(sessionResource),
// TODO@roblourens- missing for old data- normalize inside the store
lastResponseState: entry.lastResponseState ?? ResponseModelState.Complete,
});
});
}
@ -431,6 +434,8 @@ export class ChatService extends Disposable implements IChatService {
...metadata,
sessionResource,
isActive: this._sessionModels.has(sessionResource),
// TODO@roblourens- missing for old data- normalize inside the store
lastResponseState: metadata.lastResponseState ?? ResponseModelState.Complete,
};
}

View File

@ -23,7 +23,7 @@ import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.
import { awaitStatsForSession } from './chat.js';
import { ModifiedFileEntryState } from './chatEditingService.js';
import { ChatModel, IChatModelInputState, ISerializableChatData, ISerializableChatDataIn, ISerializableChatsData, normalizeSerializableChatData } from './chatModel.js';
import { IChatSessionStats } from './chatService.js';
import { IChatSessionStats, ResponseModelState } from './chatService.js';
import { LocalChatSessionUri } from './chatUri.js';
import { ChatAgentLocation } from './constants.js';
@ -433,6 +433,7 @@ export interface IChatSessionEntryMetadata {
initialLocation?: ChatAgentLocation;
hasPendingEdits?: boolean;
stats?: IChatSessionStats;
lastResponseState?: ResponseModelState;
/**
* This only exists because the migrated data from the storage service had empty sessions persisted, and it's impossible to know which ones are
@ -505,7 +506,10 @@ async function getSessionMetadata(session: ChatModel | ISerializableChatData): P
hasPendingEdits: session instanceof ChatModel ? (session.editingSession?.entries.get().some(e => e.state.get() === ModifiedFileEntryState.Modified)) : false,
isEmpty: session instanceof ChatModel ? session.getRequests().length === 0 : session.requests.length === 0,
stats,
isExternal: session instanceof ChatModel && !LocalChatSessionUri.parseLocalSessionId(session.sessionResource)
isExternal: session instanceof ChatModel && !LocalChatSessionUri.parseLocalSessionId(session.sessionResource),
lastResponseState: session instanceof ChatModel ?
(session.lastRequest?.response?.state ?? ResponseModelState.Complete) :
ResponseModelState.Complete
};
}

View File

@ -17,7 +17,7 @@ import { workbenchInstantiationService } from '../../../../test/browser/workbenc
import { LocalAgentsSessionsProvider } from '../../browser/agentSessions/localAgentSessionsProvider.js';
import { ModifiedFileEntryState } from '../../common/chatEditingService.js';
import { IChatModel, IChatRequestModel, IChatResponseModel } from '../../common/chatModel.js';
import { IChatDetail, IChatService, IChatSessionStartOptions } from '../../common/chatService.js';
import { IChatDetail, IChatService, IChatSessionStartOptions, ResponseModelState } from '../../common/chatService.js';
import { ChatSessionStatus, IChatSessionsService, localChatSessionType } from '../../common/chatSessionsService.js';
import { LocalChatSessionUri } from '../../common/chatUri.js';
import { ChatAgentLocation } from '../../common/constants.js';
@ -321,7 +321,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'Test Session',
lastMessageDate: Date.now(),
isActive: true
isActive: true,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -342,7 +343,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'History Session',
lastMessageDate: Date.now() - 10000,
isActive: false
isActive: false,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -366,13 +368,15 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'Live Session',
lastMessageDate: Date.now(),
isActive: true
isActive: true,
lastResponseState: ResponseModelState.Complete
}]);
mockChatService.setHistorySessionItems([{
sessionResource,
title: 'History Session',
lastMessageDate: Date.now() - 10000,
isActive: false
isActive: false,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -398,7 +402,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'In Progress Session',
lastMessageDate: Date.now(),
isActive: true
isActive: true,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -426,7 +431,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'Completed Session',
lastMessageDate: Date.now(),
isActive: true
isActive: true,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -453,7 +459,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'Canceled Session',
lastMessageDate: Date.now(),
isActive: true
isActive: true,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -480,7 +487,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'Error Session',
lastMessageDate: Date.now(),
isActive: true
isActive: true,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -523,6 +531,7 @@ suite('LocalAgentsSessionsProvider', () => {
title: 'Stats Session',
lastMessageDate: Date.now(),
isActive: true,
lastResponseState: ResponseModelState.Complete,
stats: {
added: 30,
removed: 8,
@ -565,7 +574,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'No Stats Session',
lastMessageDate: Date.now(),
isActive: true
isActive: true,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -593,7 +603,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'Timing Session',
lastMessageDate: Date.now(),
isActive: true
isActive: true,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -614,7 +625,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'History Timing Session',
lastMessageDate,
isActive: false
isActive: false,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -641,7 +653,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'EndTime Session',
lastMessageDate: Date.now(),
isActive: true
isActive: true,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@ -667,7 +680,8 @@ suite('LocalAgentsSessionsProvider', () => {
sessionResource,
title: 'Icon Session',
lastMessageDate: Date.now(),
isActive: true
isActive: true,
lastResponseState: ResponseModelState.Complete
}]);
const sessions = await provider.provideChatSessionItems(CancellationToken.None);

View File

@ -81,7 +81,7 @@
responseMarkdownInfo: undefined,
followups: undefined,
modelState: {
value: 1,
value: 3,
completedAt: undefined
},
vote: undefined,

View File

@ -83,7 +83,7 @@
responseMarkdownInfo: undefined,
followups: undefined,
modelState: {
value: 1,
value: 3,
completedAt: undefined
},
vote: undefined,