Merge pull request #2259 from pikasTech/fix/2255-terminal-trace-hydration
fix: avoid terminal trace hydration storms
This commit is contained in:
@@ -960,15 +960,10 @@ function nonBlockingProjection(projection: ProjectionDiagnostic | null): Project
|
||||
}
|
||||
|
||||
function hydrateTerminalTraceGaps(source: ChatMessage[], reason: string): void {
|
||||
for (const message of terminalTraceHydrationGaps(source)) {
|
||||
const traceId = firstNonEmptyString(message.traceId, message.runnerTrace?.traceId);
|
||||
if (traceId && activeSessionId.value) recordActivity(`${reason}:${traceId}`);
|
||||
void hydrateTraceEventsForMessage(message, { force: true });
|
||||
}
|
||||
}
|
||||
|
||||
function terminalTraceHydrationGaps(source: ChatMessage[]): ChatMessage[] {
|
||||
return source.filter(messageNeedsTerminalTraceGapHydration).slice(-TRACE_HYDRATION_AUTO_QUEUE_LIMIT).reverse();
|
||||
// Terminal trace rows are loaded on explicit force/detail paths. Auto-filling
|
||||
// historical terminal traces makes multi-turn Workbench sessions main-thread bound.
|
||||
void source;
|
||||
void reason;
|
||||
}
|
||||
|
||||
function scheduleForcedTraceHydrationRetry(message: ChatMessage): void {
|
||||
@@ -1232,13 +1227,13 @@ function nonBlockingProjection(projection: ProjectionDiagnostic | null): Project
|
||||
}
|
||||
const ownerMessages = ownerSessionId ? serverState.value.messagesBySessionId[ownerSessionId] ?? [] : messages.value;
|
||||
const message = latestMessageForTrace(id, ownerMessages);
|
||||
if (message) await hydrateTraceEventsForMessage(message, { force: true });
|
||||
const turn = turnStatusAuthority.value[id];
|
||||
const terminal = turn?.terminal === true || isTerminalMessageStatus(turn?.status) || isTerminalMessageStatus(message?.status);
|
||||
if (terminal) {
|
||||
clearActiveTraceRestGapFill(id);
|
||||
return;
|
||||
}
|
||||
if (message) await hydrateTraceEventsForMessage(message, { force: true });
|
||||
const stillActive = currentRequest.value?.traceId === id || isTraceActiveStatus(turn?.status) || isTraceActiveStatus(message?.status);
|
||||
if (stillActive) scheduleActiveTraceRestGapFill(id, "active-rest-gap:repeat", WORKBENCH_ACTIVE_TRACE_REST_GAP_FILL_REPEAT_MS);
|
||||
}
|
||||
@@ -1416,9 +1411,7 @@ function nonBlockingProjection(projection: ProjectionDiagnostic | null): Project
|
||||
if (ownerSessionId) await refreshMessageProjectionForTrace(ownerSessionId, id, { force: true });
|
||||
const ownerMessages = ownerSessionId ? serverState.value.messagesBySessionId[ownerSessionId] ?? [] : messages.value;
|
||||
const message = latestMessageForTrace(id, ownerMessages);
|
||||
if (message) {
|
||||
await hydrateTraceEventsForMessage(message, { force: true });
|
||||
} else if (ownerSessionId) {
|
||||
if (!message && ownerSessionId) {
|
||||
await refreshRealtimeSessionMessages(ownerSessionId, `${reason}:message-gap`, { force: true });
|
||||
}
|
||||
} finally {
|
||||
@@ -1540,7 +1533,6 @@ function nonBlockingProjection(projection: ProjectionDiagnostic | null): Project
|
||||
const ownerMessages = serverState.value.messagesBySessionId[ownerSessionId] ?? [];
|
||||
const terminalMessage = latestMessageForTrace(traceId, ownerMessages);
|
||||
if (options.forceRead && terminalMessage) void hydrateTraceEventsForMessage(terminalMessage, { force: true });
|
||||
else void hydrateTraceEvents(ownerMessages);
|
||||
if (options.forceRead) void refreshSessions(ownerSessionId, { force: true });
|
||||
if (ownerSessionId === activeSessionId.value) {
|
||||
chatPending.value = false;
|
||||
@@ -2479,6 +2471,7 @@ function messageNeedsTraceHydration(message: ChatMessage): boolean {
|
||||
if (message.role !== "agent") return false;
|
||||
const traceId = firstNonEmptyString(message.traceId, message.runnerTrace?.traceId);
|
||||
if (!traceId) return false;
|
||||
if (!messageHasActiveTraceForHydration(message)) return false;
|
||||
const trace = message.runnerTrace;
|
||||
const events = Array.isArray(trace?.events) ? trace.events : [];
|
||||
const eventCount = firstFiniteNumber(trace?.eventCount) ?? events.length;
|
||||
@@ -2486,17 +2479,11 @@ function messageNeedsTraceHydration(message: ChatMessage): boolean {
|
||||
return events.length === 0 || trace?.eventsCompacted === true || trace?.fullTraceLoaded !== true;
|
||||
}
|
||||
|
||||
function messageNeedsTerminalTraceGapHydration(message: ChatMessage): boolean {
|
||||
if (!messageHasCompletedFinalResponse(message)) return false;
|
||||
const traceId = firstNonEmptyString(message.traceId, message.runnerTrace?.traceId);
|
||||
if (!traceId) return false;
|
||||
function messageHasActiveTraceForHydration(message: ChatMessage): boolean {
|
||||
const trace = message.runnerTrace;
|
||||
if (!trace) return true;
|
||||
const events = Array.isArray(trace.events) ? trace.events : [];
|
||||
const eventCount = firstFiniteNumber(trace.eventCount) ?? events.length;
|
||||
if (trace.eventsCompacted === true || trace.fullTraceLoaded !== true) return true;
|
||||
if (events.length === 0 && eventCount > 0) return true;
|
||||
return isTraceActiveStatus(trace.status) || isTraceActiveStatus(trace.traceStatus);
|
||||
return isTraceActiveStatus(message.status)
|
||||
|| isTraceActiveStatus(trace?.status)
|
||||
|| isTraceActiveStatus(trace?.traceStatus);
|
||||
}
|
||||
|
||||
function traceHasCompletedFinalResponse(traceId: string | null | undefined, source: ChatMessage[]): boolean {
|
||||
|
||||
Reference in New Issue
Block a user