Skip to content

Commit 618725e

Browse files
authored
Store timing in metadata (#282408)
1 parent da9c9d6 commit 618725e

File tree

8 files changed

+62
-29
lines changed

8 files changed

+62
-29
lines changed

src/vs/workbench/contrib/chat/browser/actions/chatActions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,7 @@ export function registerChatActions() {
766766
title: session.label,
767767
isActive: false,
768768
lastMessageDate: 0,
769+
timing: { startTime: 0 },
769770
lastResponseState: ResponseModelState.Complete
770771
},
771772
buttons,

src/vs/workbench/contrib/chat/browser/agentSessions/localAgentSessionsProvider.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,12 @@ export class LocalAgentsSessionsProvider extends Disposable implements IChatSess
124124
const model = this.chatService.getSession(chat.sessionResource);
125125

126126
let description: string | undefined;
127-
let startTime: number;
128-
let endTime: number | undefined;
129127
if (model) {
130128
if (!model.hasRequests) {
131129
return undefined; // ignore sessions without requests
132130
}
133131

134-
const lastResponse = model.getRequests().at(-1)?.response;
135132
description = this.chatSessionsService.getInProgressSessionDescription(model);
136-
137-
startTime = model.timestamp;
138-
if (lastResponse) {
139-
endTime = lastResponse.completedAt ?? lastResponse.timestamp;
140-
}
141-
} else {
142-
startTime = chat.lastMessageDate;
143133
}
144134

145135
return {
@@ -151,10 +141,7 @@ export class LocalAgentsSessionsProvider extends Disposable implements IChatSess
151141
this.modelToStatus(model) :
152142
chatResponseStateToSessionStatus(chat.lastResponseState),
153143
iconPath: Codicon.chatSparkle,
154-
timing: {
155-
startTime,
156-
endTime
157-
},
144+
timing: chat.timing,
158145
changes: chat.stats ? {
159146
insertions: chat.stats.added,
160147
deletions: chat.stats.removed,

src/vs/workbench/contrib/chat/common/chatModel.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { migrateLegacyTerminalToolSpecificData } from './chat.js';
3232
import { IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, UserSelectedTools, reviveSerializedAgent } from './chatAgents.js';
3333
import { IChatEditingService, IChatEditingSession, ModifiedFileEntryState, editEntriesToMultiDiffData } from './chatEditingService.js';
3434
import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from './chatParserTypes.js';
35-
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';
35+
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, IChatSessionTiming, IChatTask, IChatTaskSerialized, IChatTextEdit, IChatThinkingPart, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsedContext, IChatWarningMessage, ResponseModelState, isIUsedContext } from './chatService.js';
3636
import { LocalChatSessionUri } from './chatUri.js';
3737
import { ChatRequestToolReferenceEntry, IChatRequestVariableEntry } from './chatVariableEntries.js';
3838
import { ChatAgentLocation, ChatModeKind } from './constants.js';
@@ -1170,6 +1170,7 @@ export interface IChatModel extends IDisposable {
11701170
readonly sessionId: string;
11711171
/** Milliseconds timestamp this chat model was created. */
11721172
readonly timestamp: number;
1173+
readonly timing: IChatSessionTiming;
11731174
readonly sessionResource: URI;
11741175
readonly initialLocation: ChatAgentLocation;
11751176
readonly title: string;
@@ -1638,6 +1639,14 @@ export class ChatModel extends Disposable implements IChatModel {
16381639
return this._timestamp;
16391640
}
16401641

1642+
get timing(): IChatSessionTiming {
1643+
const lastResponse = this._requests.at(-1)?.response;
1644+
return {
1645+
startTime: this._timestamp,
1646+
endTime: lastResponse?.completedAt ?? lastResponse?.timestamp
1647+
};
1648+
}
1649+
16411650
private _lastMessageDate: number;
16421651
get lastMessageDate(): number {
16431652
return this._lastMessageDate;

src/vs/workbench/contrib/chat/common/chatService.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,10 +923,16 @@ export const enum ResponseModelState {
923923
Failed
924924
}
925925

926+
export interface IChatSessionTiming {
927+
startTime: number;
928+
endTime?: number;
929+
}
930+
926931
export interface IChatDetail {
927932
sessionResource: URI;
928933
title: string;
929934
lastMessageDate: number;
935+
timing: IChatSessionTiming;
930936
isActive: boolean;
931937
stats?: IChatSessionStats;
932938
lastResponseState: ResponseModelState;

src/vs/workbench/contrib/chat/common/chatServiceImpl.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ export class ChatService extends Disposable implements IChatService {
399399
sessionResource: session.sessionResource,
400400
title,
401401
lastMessageDate: session.lastMessageDate,
402+
timing: session.timing,
402403
isActive: true,
403404
stats: await awaitStatsForSession(session),
404405
lastResponseState: session.lastRequest?.response?.state ?? ResponseModelState.Pending,
@@ -419,6 +420,8 @@ export class ChatService extends Disposable implements IChatService {
419420
return ({
420421
...entry,
421422
sessionResource,
423+
// TODO@roblourens- missing for old data- normalize inside the store
424+
timing: entry.timing ?? { startTime: entry.lastMessageDate },
422425
isActive: this._sessionModels.has(sessionResource),
423426
// TODO@roblourens- missing for old data- normalize inside the store
424427
lastResponseState: entry.lastResponseState ?? ResponseModelState.Complete,
@@ -433,6 +436,8 @@ export class ChatService extends Disposable implements IChatService {
433436
return {
434437
...metadata,
435438
sessionResource,
439+
// TODO@roblourens- missing for old data- normalize inside the store
440+
timing: metadata.timing ?? { startTime: metadata.lastMessageDate },
436441
isActive: this._sessionModels.has(sessionResource),
437442
// TODO@roblourens- missing for old data- normalize inside the store
438443
lastResponseState: metadata.lastResponseState ?? ResponseModelState.Complete,

src/vs/workbench/contrib/chat/common/chatSessionStore.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.
2323
import { awaitStatsForSession } from './chat.js';
2424
import { ModifiedFileEntryState } from './chatEditingService.js';
2525
import { ChatModel, IChatModelInputState, ISerializableChatData, ISerializableChatDataIn, ISerializableChatsData, normalizeSerializableChatData } from './chatModel.js';
26-
import { IChatSessionStats, ResponseModelState } from './chatService.js';
26+
import { IChatSessionStats, ResponseModelState, IChatSessionTiming } from './chatService.js';
2727
import { LocalChatSessionUri } from './chatUri.js';
2828
import { ChatAgentLocation } from './constants.js';
2929

@@ -430,6 +430,7 @@ export interface IChatSessionEntryMetadata {
430430
sessionId: string;
431431
title: string;
432432
lastMessageDate: number;
433+
timing?: IChatSessionTiming;
433434
initialLocation?: ChatAgentLocation;
434435
hasPendingEdits?: boolean;
435436
stats?: IChatSessionStats;
@@ -498,10 +499,19 @@ async function getSessionMetadata(session: ChatModel | ISerializableChatData): P
498499
stats = await awaitStatsForSession(session);
499500
}
500501

502+
const timing = session instanceof ChatModel ?
503+
session.timing :
504+
// session is only ISerializableChatData in the old pre-fs storage data migration scenario
505+
{
506+
startTime: session.creationDate,
507+
endTime: session.lastMessageDate
508+
};
509+
501510
return {
502511
sessionId: session.sessionId,
503512
title: title || localize('newChat', "New Chat"),
504513
lastMessageDate: session.lastMessageDate,
514+
timing,
505515
initialLocation: session.initialLocation,
506516
hasPendingEdits: session instanceof ChatModel ? (session.editingSession?.entries.get().some(e => e.state.get() === ModifiedFileEntryState.Modified)) : false,
507517
isEmpty: session instanceof ChatModel ? session.getRequests().length === 0 : session.requests.length === 0,

src/vs/workbench/contrib/chat/test/browser/localAgentSessionsProvider.test.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,8 @@ suite('LocalAgentsSessionsProvider', () => {
322322
title: 'Test Session',
323323
lastMessageDate: Date.now(),
324324
isActive: true,
325-
lastResponseState: ResponseModelState.Complete
325+
lastResponseState: ResponseModelState.Complete,
326+
timing: { startTime: 0, endTime: 1 }
326327
}]);
327328

328329
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -344,7 +345,8 @@ suite('LocalAgentsSessionsProvider', () => {
344345
title: 'History Session',
345346
lastMessageDate: Date.now() - 10000,
346347
isActive: false,
347-
lastResponseState: ResponseModelState.Complete
348+
lastResponseState: ResponseModelState.Complete,
349+
timing: { startTime: 0, endTime: 1 }
348350
}]);
349351

350352
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -369,14 +371,16 @@ suite('LocalAgentsSessionsProvider', () => {
369371
title: 'Live Session',
370372
lastMessageDate: Date.now(),
371373
isActive: true,
372-
lastResponseState: ResponseModelState.Complete
374+
lastResponseState: ResponseModelState.Complete,
375+
timing: { startTime: 0, endTime: 1 }
373376
}]);
374377
mockChatService.setHistorySessionItems([{
375378
sessionResource,
376379
title: 'History Session',
377380
lastMessageDate: Date.now() - 10000,
378381
isActive: false,
379-
lastResponseState: ResponseModelState.Complete
382+
lastResponseState: ResponseModelState.Complete,
383+
timing: { startTime: 0, endTime: 1 }
380384
}]);
381385

382386
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -403,7 +407,8 @@ suite('LocalAgentsSessionsProvider', () => {
403407
title: 'In Progress Session',
404408
lastMessageDate: Date.now(),
405409
isActive: true,
406-
lastResponseState: ResponseModelState.Complete
410+
lastResponseState: ResponseModelState.Complete,
411+
timing: { startTime: 0, endTime: 1 }
407412
}]);
408413

409414
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -432,7 +437,8 @@ suite('LocalAgentsSessionsProvider', () => {
432437
title: 'Completed Session',
433438
lastMessageDate: Date.now(),
434439
isActive: true,
435-
lastResponseState: ResponseModelState.Complete
440+
lastResponseState: ResponseModelState.Complete,
441+
timing: { startTime: 0, endTime: 1 },
436442
}]);
437443

438444
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -460,7 +466,8 @@ suite('LocalAgentsSessionsProvider', () => {
460466
title: 'Canceled Session',
461467
lastMessageDate: Date.now(),
462468
isActive: true,
463-
lastResponseState: ResponseModelState.Complete
469+
lastResponseState: ResponseModelState.Complete,
470+
timing: { startTime: 0, endTime: 1 },
464471
}]);
465472

466473
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -488,7 +495,8 @@ suite('LocalAgentsSessionsProvider', () => {
488495
title: 'Error Session',
489496
lastMessageDate: Date.now(),
490497
isActive: true,
491-
lastResponseState: ResponseModelState.Complete
498+
lastResponseState: ResponseModelState.Complete,
499+
timing: { startTime: 0, endTime: 1 },
492500
}]);
493501

494502
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -532,6 +540,7 @@ suite('LocalAgentsSessionsProvider', () => {
532540
lastMessageDate: Date.now(),
533541
isActive: true,
534542
lastResponseState: ResponseModelState.Complete,
543+
timing: { startTime: 0, endTime: 1 },
535544
stats: {
536545
added: 30,
537546
removed: 8,
@@ -575,7 +584,8 @@ suite('LocalAgentsSessionsProvider', () => {
575584
title: 'No Stats Session',
576585
lastMessageDate: Date.now(),
577586
isActive: true,
578-
lastResponseState: ResponseModelState.Complete
587+
lastResponseState: ResponseModelState.Complete,
588+
timing: { startTime: 0, endTime: 1 }
579589
}]);
580590

581591
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -604,7 +614,8 @@ suite('LocalAgentsSessionsProvider', () => {
604614
title: 'Timing Session',
605615
lastMessageDate: Date.now(),
606616
isActive: true,
607-
lastResponseState: ResponseModelState.Complete
617+
lastResponseState: ResponseModelState.Complete,
618+
timing: { startTime: modelTimestamp }
608619
}]);
609620

610621
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -626,7 +637,8 @@ suite('LocalAgentsSessionsProvider', () => {
626637
title: 'History Timing Session',
627638
lastMessageDate,
628639
isActive: false,
629-
lastResponseState: ResponseModelState.Complete
640+
lastResponseState: ResponseModelState.Complete,
641+
timing: { startTime: lastMessageDate }
630642
}]);
631643

632644
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -654,7 +666,8 @@ suite('LocalAgentsSessionsProvider', () => {
654666
title: 'EndTime Session',
655667
lastMessageDate: Date.now(),
656668
isActive: true,
657-
lastResponseState: ResponseModelState.Complete
669+
lastResponseState: ResponseModelState.Complete,
670+
timing: { startTime: 0, endTime: completedAt }
658671
}]);
659672

660673
const sessions = await provider.provideChatSessionItems(CancellationToken.None);
@@ -681,7 +694,8 @@ suite('LocalAgentsSessionsProvider', () => {
681694
title: 'Icon Session',
682695
lastMessageDate: Date.now(),
683696
isActive: true,
684-
lastResponseState: ResponseModelState.Complete
697+
lastResponseState: ResponseModelState.Complete,
698+
timing: { startTime: 0, endTime: 1 }
685699
}]);
686700

687701
const sessions = await provider.provideChatSessionItems(CancellationToken.None);

src/vs/workbench/contrib/chat/test/common/mockChatModel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export class MockChatModel extends Disposable implements IChatModel {
1616
readonly onDidChange = this._register(new Emitter<IChatChangeEvent>()).event;
1717
readonly sessionId = '';
1818
readonly timestamp = 0;
19+
readonly timing = { startTime: 0 };
1920
readonly initialLocation = ChatAgentLocation.Chat;
2021
readonly title = '';
2122
readonly hasCustomTitle = false;

0 commit comments

Comments
 (0)