fix(workbench): preserve terminal final text and timing (#2042)
This commit is contained in:
@@ -117,7 +117,7 @@ export async function writeWorkbenchProjectionEvent({ runtimeStore = null, event
|
||||
// switching to a different clock origin.
|
||||
const startedAt = previousTiming?.startedAt ?? explicitStartedAt ?? (sourceSeq <= 1 ? occurredAt : null);
|
||||
const lastEventAt = latestTimestamp(previousTiming?.lastEventAt, occurredAt);
|
||||
const finishedAt = terminal ? latestTimestamp(previousTiming?.lastEventAt, optionalTimestampValue(event.finishedAt), occurredAt) : null;
|
||||
const finishedAt = terminal ? latestTimestamp(previousTiming?.lastEventAt, optionalTimestampValue(event.finishedAt), occurredAt, projectedAt) : null;
|
||||
const timing = eventTimingProjection({ startedAt, lastEventAt, finishedAt, terminal });
|
||||
const eventTiming = suppressedAfterSeal ? previousTiming ?? timing : timing;
|
||||
const sourceEventId = workbenchSourceEventId(event, { traceId, sourceSeq, occurredAt });
|
||||
@@ -141,7 +141,9 @@ export async function writeWorkbenchProjectionEvent({ runtimeStore = null, event
|
||||
const projectedSeq = nonNegativeInteger(allocation?.projectedSeq);
|
||||
if (projectedSeq <= 0) throw new Error("Workbench projection allocator returned an invalid projectedSeq.");
|
||||
const eventId = textValue(event.id) || stableFactId("wte", { traceId, sourceEventId });
|
||||
const messageFinalText = finalResponseTextValue(event.finalResponse, event.assistantText, event.reply);
|
||||
const previousFinalText = finalResponseTextValue(previousCheckpoint?.finalResponse, previousCheckpoint?.assistantText, previousCheckpoint?.finalText);
|
||||
const messageFinalText = finalResponseTextValue(event.finalResponse, event.assistantText, event.reply, previousFinalText);
|
||||
const checkpointFinalResponse = messageFinalText ? { text: messageFinalText, status, traceId, valuesPrinted: false } : null;
|
||||
const messageFact = sessionId && !suppressedAfterSeal ? normalizeMessageFact({
|
||||
messageId: eventProjectionAssistantMessageId(traceId, event),
|
||||
role: "agent",
|
||||
@@ -249,7 +251,9 @@ function eventProjectionAssistantMessageId(traceId, event = {}) {
|
||||
|
||||
async function writeWorkbenchProjectionFacts({ runtimeStore = null, traceStore = defaultCodeAgentTraceStore, traceId = null, ownerUserId = null, ownerRole = null, sessionId = null, projectId = null, conversationId = null, threadId = null, status = "active", session = {}, payload = null, params = {} } = {}) {
|
||||
if (typeof runtimeStore?.writeWorkbenchFacts !== "function") return null;
|
||||
const facts = buildWorkbenchProjectionFacts({ traceId, ownerUserId, ownerRole, sessionId, projectId, conversationId, threadId, status, session, payload, params });
|
||||
const safeId = safeTraceId(traceId ?? session?.traceId ?? payload?.traceId ?? params?.traceId);
|
||||
const previousCheckpoint = safeId ? await latestWorkbenchCheckpoint(runtimeStore, safeId) : null;
|
||||
const facts = buildWorkbenchProjectionFacts({ traceId, ownerUserId, ownerRole, sessionId, projectId, conversationId, threadId, status, session, payload, params, previousCheckpoint });
|
||||
if (isEmptyFacts(facts)) return null;
|
||||
try {
|
||||
return await runtimeStore.writeWorkbenchFacts({ facts }, {
|
||||
@@ -273,7 +277,7 @@ async function writeWorkbenchProjectionFacts({ runtimeStore = null, traceStore =
|
||||
}
|
||||
}
|
||||
|
||||
function buildWorkbenchProjectionFacts({ traceId = null, ownerUserId = null, ownerRole = null, sessionId = null, projectId = null, conversationId = null, threadId = null, status = "active", session = {}, payload = null, params = {} } = {}) {
|
||||
function buildWorkbenchProjectionFacts({ traceId = null, ownerUserId = null, ownerRole = null, sessionId = null, projectId = null, conversationId = null, threadId = null, status = "active", session = {}, payload = null, params = {}, previousCheckpoint = null } = {}) {
|
||||
const safeId = safeTraceId(traceId ?? session?.traceId ?? payload?.traceId ?? params?.traceId);
|
||||
const resolvedSessionId = textValue(sessionId ?? session?.sessionId ?? payload?.sessionId ?? params?.sessionId) || null;
|
||||
const resolvedConversationId = textValue(conversationId ?? session?.conversationId ?? payload?.conversationId ?? params?.conversationId) || null;
|
||||
@@ -285,9 +289,12 @@ function buildWorkbenchProjectionFacts({ traceId = null, ownerUserId = null, own
|
||||
const projectedStatus = normalizeWorkbenchStatus(projection.status ?? normalizedStatus);
|
||||
const terminal = projection.terminal === true;
|
||||
const terminalStatus = terminal ? (TERMINAL_STATUSES.has(projectedStatus) ? projectedStatus : normalizedStatus) : projectedStatus;
|
||||
const timing = projectionTimingForStatus(projection.timing, terminal);
|
||||
const projectedAt = new Date().toISOString();
|
||||
const timing = terminalTimingAtLeastProjectedAt(projectionTimingForStatus(projection.timing, terminal), terminal, projectedAt);
|
||||
const diagnostic = projectionDiagnostics({ traceId: safeId, result: payload, trace: payload?.runnerTrace ?? null, projection });
|
||||
const finalText = finalResponseTextValue(projection.finalResponse, payload?.finalResponse, session?.finalResponse, payload?.assistantText);
|
||||
const previousFinalText = finalResponseTextValue(previousCheckpoint?.finalResponse, previousCheckpoint?.assistantText, previousCheckpoint?.finalText);
|
||||
const finalText = finalResponseTextValue(projection.finalResponse, payload?.finalResponse, session?.finalResponse, payload?.assistantText, previousFinalText);
|
||||
const projectedFinalResponse = finalText ? { text: finalText, status: terminalStatus, traceId: safeId, valuesPrinted: false } : projection.finalResponse ?? null;
|
||||
const messages = normalizeMessages(workbenchProjectionInputMessages({ session, payload, params, traceId: safeId, timestamp, terminal, terminalStatus, finalText }), {
|
||||
traceId: safeId,
|
||||
sessionId: resolvedSessionId,
|
||||
@@ -352,8 +359,10 @@ function buildWorkbenchProjectionFacts({ traceId = null, ownerUserId = null, own
|
||||
sourceEventId: safeId,
|
||||
terminal: projection.terminal,
|
||||
sealed: projection.terminal,
|
||||
finalResponse: projection.finalResponse,
|
||||
finalResponse: projectedFinalResponse,
|
||||
diagnostic,
|
||||
finalResponse: checkpointFinalResponse,
|
||||
assistantText: messageFinalText,
|
||||
timing,
|
||||
startedAt: timing.startedAt,
|
||||
lastEventAt: timing.lastEventAt,
|
||||
@@ -375,6 +384,8 @@ function buildWorkbenchProjectionFacts({ traceId = null, ownerUserId = null, own
|
||||
terminal: projection.terminal,
|
||||
sealed: projection.terminal,
|
||||
diagnostic,
|
||||
finalResponse: projectedFinalResponse,
|
||||
assistantText: finalText,
|
||||
timing,
|
||||
startedAt: timing.startedAt,
|
||||
lastEventAt: timing.lastEventAt,
|
||||
@@ -503,6 +514,18 @@ function projectionTimingForStatus(value, terminal) {
|
||||
return { ...timing, finishedAt: null, durationMs: null, valuesRedacted: timing.valuesRedacted !== false };
|
||||
}
|
||||
|
||||
function terminalTimingAtLeastProjectedAt(value, terminal, projectedAt) {
|
||||
const timing = normalizeTimingProjection(value) ?? emptyTimingProjection();
|
||||
if (!terminal) return timing;
|
||||
const finishedAt = latestTimestamp(timing.finishedAt, projectedAt);
|
||||
return {
|
||||
...timing,
|
||||
finishedAt,
|
||||
durationMs: elapsedBetween(timing.startedAt, finishedAt) ?? timing.durationMs,
|
||||
valuesRedacted: timing.valuesRedacted !== false
|
||||
};
|
||||
}
|
||||
|
||||
function checkpointIsTerminal(value) {
|
||||
const source = value && typeof value === "object" ? value : null;
|
||||
if (!source) return false;
|
||||
|
||||
Reference in New Issue
Block a user