Merge pull request #1784 from pikasTech/fix/workbench-terminal-live-1770
fix(workbench): active turn 持续收敛到终态
This commit is contained in:
@@ -19,8 +19,8 @@ const TRACE_HYDRATION_PAGE_LIMIT = 100;
|
|||||||
const TRACE_HYDRATION_MAX_PAGES = 60;
|
const TRACE_HYDRATION_MAX_PAGES = 60;
|
||||||
const TRACE_HYDRATION_MAX_ATTEMPTS = 3;
|
const TRACE_HYDRATION_MAX_ATTEMPTS = 3;
|
||||||
const TRACE_HYDRATION_RETRY_DELAY_MS = 700;
|
const TRACE_HYDRATION_RETRY_DELAY_MS = 700;
|
||||||
const ACTIVE_TURN_GAP_DELAY_MS = 1_000;
|
const ACTIVE_TURN_GAP_INITIAL_DELAY_MS = 1_000;
|
||||||
const ACTIVE_TURN_GAP_MAX_ATTEMPTS = 12;
|
const ACTIVE_TURN_GAP_MAX_DELAY_MS = 5_000;
|
||||||
const SESSION_LIST_PAGE_LIMIT = 20;
|
const SESSION_LIST_PAGE_LIMIT = 20;
|
||||||
const WORKBENCH_READ_HYDRATION_CONCURRENCY = 3;
|
const WORKBENCH_READ_HYDRATION_CONCURRENCY = 3;
|
||||||
|
|
||||||
@@ -726,14 +726,29 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
lastEventLabel: result.lastEventLabel ?? undefined,
|
lastEventLabel: result.lastEventLabel ?? undefined,
|
||||||
updatedAt: new Date().toISOString()
|
updatedAt: new Date().toISOString()
|
||||||
} as NonNullable<ChatMessage["runnerTrace"]>;
|
} as NonNullable<ChatMessage["runnerTrace"]>;
|
||||||
const runnerTrace = mergeRunnerTrace(message.runnerTrace, nextTrace);
|
const resultStatus = normalizedStatusText(result.status) ?? null;
|
||||||
const error = isCompletedResultWithFinalResponse(result) ? null : normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
const terminal = result.terminal === true || isTerminalMessageStatus(resultStatus);
|
||||||
const projection = projectionFromResult(result) ?? runnerTrace.projection ?? message.projection ?? null;
|
const resultError = normalizeAgentError(result.error ?? null);
|
||||||
|
const resultProjection = projectionFromResult(result);
|
||||||
|
const mergedRunnerTrace = mergeRunnerTrace(message.runnerTrace, nextTrace);
|
||||||
|
const runnerTrace = terminal && !resultError && !resultProjection ? clearRunnerTraceTransientDiagnostics(mergedRunnerTrace) : mergedRunnerTrace;
|
||||||
|
const error = resultError ?? (terminal ? null : normalizeAgentError(runnerTrace?.error ?? message.error));
|
||||||
|
const projection = resultProjection ?? (terminal ? null : runnerTrace.projection ?? message.projection ?? null);
|
||||||
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
||||||
rememberTraceAuthority(runnerTrace);
|
rememberTraceAuthority(runnerTrace);
|
||||||
return { ...message, ...messageTimingPatch(result), runnerTrace, error: error ?? message.error ?? null, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
return { ...message, ...messageTimingPatch(result), runnerTrace, error, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||||
}));
|
}));
|
||||||
markWorkbenchTraceProjected(traceId);
|
markWorkbenchTraceProjected(traceId);
|
||||||
|
if (result.terminal === true || isTerminalMessageStatus(result.status)) {
|
||||||
|
rememberTurnStatus(traceId, result);
|
||||||
|
clearActiveTurnGapRefresh(traceId);
|
||||||
|
if (ownerSessionId === activeSessionId.value) {
|
||||||
|
chatPending.value = false;
|
||||||
|
currentRequest.value = null;
|
||||||
|
void clearActiveTrace(traceId, "trace-hydration-terminal");
|
||||||
|
restartRealtime("trace-hydration-terminal");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setProviderProfile(value: ProviderProfile): void {
|
function setProviderProfile(value: ProviderProfile): void {
|
||||||
@@ -983,13 +998,14 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
}
|
}
|
||||||
if (activeTurnGapTimer) return;
|
if (activeTurnGapTimer) return;
|
||||||
const attempts = activeTurnGapAttempts.get(traceId) ?? 0;
|
const attempts = activeTurnGapAttempts.get(traceId) ?? 0;
|
||||||
if (attempts >= ACTIVE_TURN_GAP_MAX_ATTEMPTS) return;
|
const nextAttempt = attempts + 1;
|
||||||
activeTurnGapAttempts.set(traceId, attempts + 1);
|
activeTurnGapAttempts.set(traceId, nextAttempt);
|
||||||
if (typeof window === "undefined") return;
|
if (typeof window === "undefined") return;
|
||||||
|
const delayMs = Math.min(ACTIVE_TURN_GAP_MAX_DELAY_MS, ACTIVE_TURN_GAP_INITIAL_DELAY_MS * Math.max(1, nextAttempt));
|
||||||
activeTurnGapTimer = window.setTimeout(() => {
|
activeTurnGapTimer = window.setTimeout(() => {
|
||||||
activeTurnGapTimer = null;
|
activeTurnGapTimer = null;
|
||||||
void hydrateRealtimeGap(`active-turn-gap:${reason}`);
|
void hydrateRealtimeGap(`active-turn-gap:${reason}`);
|
||||||
}, ACTIVE_TURN_GAP_DELAY_MS);
|
}, delayMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
function activeTurnGapTraceId(): string | null {
|
function activeTurnGapTraceId(): string | null {
|
||||||
@@ -1048,12 +1064,17 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
if (ownerSessionId === activeSessionId.value) recordActivity(`trace:terminal:${firstNonEmptyString(result.lastEventLabel, result.status, "completed") ?? "completed"}`);
|
if (ownerSessionId === activeSessionId.value) recordActivity(`trace:terminal:${firstNonEmptyString(result.lastEventLabel, result.status, "completed") ?? "completed"}`);
|
||||||
updateSessionMessages(ownerSessionId, (source) => source.map((message) => {
|
updateSessionMessages(ownerSessionId, (source) => source.map((message) => {
|
||||||
if (!messageMatchesTraceAuthority(message, traceId, authoritySessionId, ownerSessionId)) return message;
|
if (!messageMatchesTraceAuthority(message, traceId, authoritySessionId, ownerSessionId)) return message;
|
||||||
const runnerTrace = mergeTerminalResultTrace(message.runnerTrace, result);
|
const resultStatus = normalizedStatusText(result.status) ?? null;
|
||||||
|
const terminal = result.terminal === true || isTerminalMessageStatus(resultStatus);
|
||||||
|
const resultError = normalizeAgentError(result.error ?? null);
|
||||||
|
const resultProjection = projectionFromResult(result);
|
||||||
|
const mergedRunnerTrace = mergeTerminalResultTrace(message.runnerTrace, result);
|
||||||
|
const runnerTrace = terminal && !resultError && !resultProjection ? clearRunnerTraceTransientDiagnostics(mergedRunnerTrace) : mergedRunnerTrace;
|
||||||
rememberTraceAuthority(runnerTrace);
|
rememberTraceAuthority(runnerTrace);
|
||||||
const error = isCompletedResultWithFinalResponse(result) ? null : normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
const error = resultError ?? (terminal ? null : normalizeAgentError(runnerTrace?.error ?? message.error));
|
||||||
const projection = projectionFromResult(result) ?? runnerTrace.projection ?? message.projection ?? null;
|
const projection = resultProjection ?? (terminal ? null : runnerTrace.projection ?? message.projection ?? null);
|
||||||
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
||||||
return { ...message, ...messageTimingPatch(result), runnerTrace, error: error ?? message.error ?? null, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
return { ...message, ...messageTimingPatch(result), runnerTrace, error, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||||
}));
|
}));
|
||||||
rememberTurnStatus(traceId, result);
|
rememberTurnStatus(traceId, result);
|
||||||
markWorkbenchTraceProjected(traceId);
|
markWorkbenchTraceProjected(traceId);
|
||||||
@@ -1087,12 +1108,17 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
if (!ownerSessionId) return;
|
if (!ownerSessionId) return;
|
||||||
updateSessionMessages(ownerSessionId, (source) => source.map((message) => {
|
updateSessionMessages(ownerSessionId, (source) => source.map((message) => {
|
||||||
if (!messageMatchesTraceAuthority(message, traceId, authoritySessionId, ownerSessionId)) return message;
|
if (!messageMatchesTraceAuthority(message, traceId, authoritySessionId, ownerSessionId)) return message;
|
||||||
const runnerTrace = mergeTerminalResultTrace(message.runnerTrace, result);
|
const resultStatus = normalizedStatusText(result.status) ?? null;
|
||||||
|
const terminal = result.terminal === true || isTerminalMessageStatus(resultStatus);
|
||||||
|
const resultError = normalizeAgentError(result.error ?? null);
|
||||||
|
const resultProjection = projectionFromResult(result);
|
||||||
|
const mergedRunnerTrace = mergeTerminalResultTrace(message.runnerTrace, result);
|
||||||
|
const runnerTrace = terminal && !resultError && !resultProjection ? clearRunnerTraceTransientDiagnostics(mergedRunnerTrace) : mergedRunnerTrace;
|
||||||
rememberTraceAuthority(runnerTrace);
|
rememberTraceAuthority(runnerTrace);
|
||||||
const error = isCompletedResultWithFinalResponse(result) ? null : normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
const error = resultError ?? (terminal ? null : normalizeAgentError(runnerTrace?.error ?? message.error));
|
||||||
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
||||||
const projection = projectionFromResult(result) ?? runnerTrace.projection ?? message.projection ?? null;
|
const projection = resultProjection ?? (terminal ? null : runnerTrace.projection ?? message.projection ?? null);
|
||||||
return { ...message, ...messageTimingPatch(result), title: normalizeWorkbenchMessageTitle(message.role, message.title), runnerTrace, error: error ?? message.error ?? null, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
return { ...message, ...messageTimingPatch(result), title: normalizeWorkbenchMessageTitle(message.role, message.title), runnerTrace, error, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||||
}));
|
}));
|
||||||
void hydrateTraceEvents(serverState.value.messagesBySessionId[ownerSessionId] ?? []);
|
void hydrateTraceEvents(serverState.value.messagesBySessionId[ownerSessionId] ?? []);
|
||||||
void refreshSessions(ownerSessionId);
|
void refreshSessions(ownerSessionId);
|
||||||
@@ -1765,14 +1791,6 @@ function projectedAgentMessageText(input: { status: unknown; finalText?: string
|
|||||||
return firstNonEmptyString(input.errorText, text) ?? "";
|
return firstNonEmptyString(input.errorText, text) ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCompletedResultWithFinalResponse(value: unknown): boolean {
|
|
||||||
const record = recordValue(value);
|
|
||||||
if (!record) return false;
|
|
||||||
const status = normalizedStatusText(record.status);
|
|
||||||
if (status !== "completed") return false;
|
|
||||||
return Boolean(finalResponseText(record.finalResponse) ?? messageText(record.assistantText) ?? messageText(record.text) ?? messageText(record.summary));
|
|
||||||
}
|
|
||||||
|
|
||||||
function isFailureTerminalStatus(value: unknown): boolean {
|
function isFailureTerminalStatus(value: unknown): boolean {
|
||||||
return ["failed", "blocked", "timeout", "canceled", "cancelled", "stale", "thread-resume-failed"].includes(String(value ?? "").trim().toLowerCase().replace(/_/gu, "-"));
|
return ["failed", "blocked", "timeout", "canceled", "cancelled", "stale", "thread-resume-failed"].includes(String(value ?? "").trim().toLowerCase().replace(/_/gu, "-"));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user