fix: throttle workbench history hydration
This commit is contained in:
@@ -22,6 +22,7 @@ const TRACE_HYDRATION_RETRY_DELAY_MS = 700;
|
||||
const ACTIVE_TURN_GAP_DELAY_MS = 1_000;
|
||||
const ACTIVE_TURN_GAP_MAX_ATTEMPTS = 12;
|
||||
const SESSION_LIST_PAGE_LIMIT = 20;
|
||||
const WORKBENCH_READ_HYDRATION_CONCURRENCY = 3;
|
||||
|
||||
interface HydrateOptions {
|
||||
sessionId?: string | null;
|
||||
@@ -69,6 +70,8 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
let realtimeGapTimer: number | null = null;
|
||||
let activeTurnGapTimer: number | null = null;
|
||||
const sessionListRefreshInFlight = new Map<string, Promise<void>>();
|
||||
let workbenchReadHydrationActive = 0;
|
||||
const workbenchReadHydrationQueue: Array<() => void> = [];
|
||||
|
||||
const activeSession = computed(() => selectActiveSession(serverState.value, explicitSessionId.value));
|
||||
const activeSessionSelectionSource = computed(() => activeSelectionSource.value);
|
||||
@@ -370,6 +373,35 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
void source;
|
||||
}
|
||||
|
||||
function runWorkbenchReadHydration<T>(task: () => Promise<T>): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const run = () => {
|
||||
workbenchReadHydrationActive += 1;
|
||||
Promise.resolve()
|
||||
.then(task)
|
||||
.then(resolve, reject)
|
||||
.finally(() => {
|
||||
workbenchReadHydrationActive = Math.max(0, workbenchReadHydrationActive - 1);
|
||||
const next = workbenchReadHydrationQueue.shift();
|
||||
if (next) next();
|
||||
});
|
||||
};
|
||||
if (workbenchReadHydrationActive < WORKBENCH_READ_HYDRATION_CONCURRENCY) {
|
||||
run();
|
||||
return;
|
||||
}
|
||||
workbenchReadHydrationQueue.push(run);
|
||||
});
|
||||
}
|
||||
|
||||
function fetchWorkbenchTurnStatus(traceId: string): Promise<ApiResult<AgentChatResultResponse>> {
|
||||
return runWorkbenchReadHydration(() => api.workbench.turn(traceId, 8000, () => activityRef.value));
|
||||
}
|
||||
|
||||
function fetchWorkbenchTraceEvents(traceId: string, sinceSeq: number): Promise<ApiResult<AgentChatResultResponse>> {
|
||||
return runWorkbenchReadHydration(() => api.workbench.traceEvents(traceId, codeAgentTimeoutMs.value, () => activityRef.value, { sinceSeq, limit: TRACE_HYDRATION_PAGE_LIMIT }));
|
||||
}
|
||||
|
||||
async function hydrateTurnStatusAuthority(source: ChatMessage[] = messages.value): Promise<void> {
|
||||
await Promise.all(uniqueTraceIds(source).slice(-12).map((traceId) => refreshTurnStatusByTraceId(traceId)));
|
||||
}
|
||||
@@ -377,7 +409,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
async function refreshTurnStatusByTraceId(traceId: string | null | undefined): Promise<void> {
|
||||
const id = firstNonEmptyString(traceId);
|
||||
if (!id) return;
|
||||
const response = await api.workbench.turn(id, 8000, () => activityRef.value);
|
||||
const response = await fetchWorkbenchTurnStatus(id);
|
||||
if (response.ok && response.data) {
|
||||
applyTurnStatusSnapshot(id, response.data);
|
||||
if (response.data.terminal === true || isTerminalMessageStatus(response.data.status)) completeTrace(id, response.data);
|
||||
@@ -556,7 +588,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
async function fetchTraceHydrationPage(traceId: string, sinceSeq: number): Promise<ApiResult<AgentChatResultResponse>> {
|
||||
let lastResult: ApiResult<AgentChatResultResponse> | null = null;
|
||||
for (let attempt = 0; attempt < TRACE_HYDRATION_MAX_ATTEMPTS; attempt += 1) {
|
||||
const result = await api.workbench.traceEvents(traceId, codeAgentTimeoutMs.value, () => activityRef.value, { sinceSeq, limit: TRACE_HYDRATION_PAGE_LIMIT });
|
||||
const result = await fetchWorkbenchTraceEvents(traceId, sinceSeq);
|
||||
if (result.ok && result.data) return result;
|
||||
lastResult = result;
|
||||
if (attempt < TRACE_HYDRATION_MAX_ATTEMPTS - 1) await delayTraceHydrationRetry(TRACE_HYDRATION_RETRY_DELAY_MS * (attempt + 1));
|
||||
@@ -685,7 +717,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
}
|
||||
|
||||
async function validateAndReattachTrace(traceId: string): Promise<void> {
|
||||
const result = await api.workbench.turn(traceId, 8000, () => activityRef.value);
|
||||
const result = await fetchWorkbenchTurnStatus(traceId);
|
||||
if (!result.ok || !result.data) {
|
||||
await clearActiveTrace(traceId, result.status === 404 ? "reattach-result-not-found" : "reattach-result-unavailable");
|
||||
return;
|
||||
@@ -828,7 +860,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
if (terminalRealtimeRefreshInFlight.has(traceId)) return;
|
||||
terminalRealtimeRefreshInFlight.add(traceId);
|
||||
try {
|
||||
const result = await api.workbench.turn(traceId, 8000, () => activityRef.value);
|
||||
const result = await fetchWorkbenchTurnStatus(traceId);
|
||||
if (!result.ok || !result.data) return;
|
||||
applyTurnStatusSnapshot(traceId, result.data);
|
||||
if (result.data.terminal === true || isTerminalMessageStatus(result.data.status)) completeTrace(traceId, result.data);
|
||||
@@ -967,7 +999,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
await Promise.all(targets.map(async (message) => {
|
||||
const traceId = firstNonEmptyString(message.traceId, message.runnerTrace?.traceId);
|
||||
if (!traceId) return;
|
||||
const result = await api.workbench.turn(traceId, 8000, () => activityRef.value);
|
||||
const result = await fetchWorkbenchTurnStatus(traceId);
|
||||
if (!result.ok || !result.data) return;
|
||||
applyTurnStatusSnapshot(traceId, result.data);
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user