Merge pull request #1598 from pikasTech/fix/1585-terminal-trace-cache
[P0] Workbench terminal trace 纯读缓存,降低长 trace 重复 GET 延迟
This commit is contained in:
@@ -183,8 +183,10 @@ test("workbench read model recovers trace events from durable projection without
|
|||||||
async ensureBootstrap() {},
|
async ensureBootstrap() {},
|
||||||
async authenticate() { return { ok: true, actor: ACTOR, session: { id: "uss_workbench_reader" } }; }
|
async authenticate() { return { ok: true, actor: ACTOR, session: { id: "uss_workbench_reader" } }; }
|
||||||
};
|
};
|
||||||
|
let traceQueryCount = 0;
|
||||||
const runtimeStore = {
|
const runtimeStore = {
|
||||||
async queryAgentTraceEvents(params = {}) {
|
async queryAgentTraceEvents(params = {}) {
|
||||||
|
traceQueryCount += 1;
|
||||||
assert.equal(params.traceId, traceId);
|
assert.equal(params.traceId, traceId);
|
||||||
return { events: durableEvents, count: durableEvents.length };
|
return { events: durableEvents, count: durableEvents.length };
|
||||||
}
|
}
|
||||||
@@ -210,6 +212,7 @@ test("workbench read model recovers trace events from durable projection without
|
|||||||
assert.equal(trace.body.events.length, 2);
|
assert.equal(trace.body.events.length, 2);
|
||||||
assert.equal(trace.body.traceStatus, "completed");
|
assert.equal(trace.body.traceStatus, "completed");
|
||||||
assert.equal(trace.body.hasMore, false);
|
assert.equal(trace.body.hasMore, false);
|
||||||
|
assert.equal(traceQueryCount, 1);
|
||||||
} finally {
|
} finally {
|
||||||
await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import { defaultCodeAgentTraceStore } from "./code-agent-trace-store.ts";
|
|||||||
import { safeConversationId, safeSessionId, safeTraceId } from "./server-http-utils.ts";
|
import { safeConversationId, safeSessionId, safeTraceId } from "./server-http-utils.ts";
|
||||||
import { durableTraceStatus, normalizeWorkbenchStatus, TERMINAL_STATUSES, traceTerminalEvidence } from "./workbench-turn-projection.ts";
|
import { durableTraceStatus, normalizeWorkbenchStatus, TERMINAL_STATUSES, traceTerminalEvidence } from "./workbench-turn-projection.ts";
|
||||||
|
|
||||||
|
const TERMINAL_DURABLE_TRACE_CACHE_MAX = 256;
|
||||||
|
const terminalDurableTraceCache = new Map();
|
||||||
|
|
||||||
export function createWorkbenchFactsStore(options = {}, actor = null) {
|
export function createWorkbenchFactsStore(options = {}, actor = null) {
|
||||||
const accessStore = options.accessController?.store ?? options.accessController ?? null;
|
const accessStore = options.accessController?.store ?? options.accessController ?? null;
|
||||||
const traceStore = options.traceStore ?? defaultCodeAgentTraceStore;
|
const traceStore = options.traceStore ?? defaultCodeAgentTraceStore;
|
||||||
@@ -101,21 +104,29 @@ export function createWorkbenchFactsStore(options = {}, actor = null) {
|
|||||||
|
|
||||||
async function durableTraceSnapshot(runtimeStore, traceId) {
|
async function durableTraceSnapshot(runtimeStore, traceId) {
|
||||||
if (!runtimeStore || typeof runtimeStore.queryAgentTraceEvents !== "function") return null;
|
if (!runtimeStore || typeof runtimeStore.queryAgentTraceEvents !== "function") return null;
|
||||||
|
const safeId = safeTraceId(traceId);
|
||||||
|
if (!safeId) return null;
|
||||||
|
const cached = terminalDurableTraceCache.get(safeId);
|
||||||
|
if (cached) {
|
||||||
|
terminalDurableTraceCache.delete(safeId);
|
||||||
|
terminalDurableTraceCache.set(safeId, cached);
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
result = await runtimeStore.queryAgentTraceEvents({ traceId });
|
result = await runtimeStore.queryAgentTraceEvents({ traceId: safeId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return projectionStoreUnavailableTrace(traceId, error);
|
return projectionStoreUnavailableTrace(safeId, error);
|
||||||
}
|
}
|
||||||
const events = Array.isArray(result?.events) ? result.events : [];
|
const events = Array.isArray(result?.events) ? result.events : [];
|
||||||
if (events.length === 0) return null;
|
if (events.length === 0) return null;
|
||||||
const normalizedEvents = events.map((event, index) => ({ ...event, traceId, seq: eventSeq(event, index) }));
|
const normalizedEvents = events.map((event, index) => ({ ...event, traceId: safeId, seq: eventSeq(event, index) }));
|
||||||
const firstEvent = normalizedEvents[0] ?? null;
|
const firstEvent = normalizedEvents[0] ?? null;
|
||||||
const lastEvent = normalizedEvents.at(-1) ?? null;
|
const lastEvent = normalizedEvents.at(-1) ?? null;
|
||||||
const status = durableTraceStatus(normalizedEvents);
|
const status = durableTraceStatus(normalizedEvents);
|
||||||
const terminalEvidence = traceTerminalEvidence({ events: normalizedEvents, status });
|
const terminalEvidence = traceTerminalEvidence({ events: normalizedEvents, status });
|
||||||
return {
|
const snapshot = {
|
||||||
traceId,
|
traceId: safeId,
|
||||||
status,
|
status,
|
||||||
createdAt: firstEvent?.createdAt ?? null,
|
createdAt: firstEvent?.createdAt ?? null,
|
||||||
updatedAt: lastEvent?.createdAt ?? firstEvent?.createdAt ?? null,
|
updatedAt: lastEvent?.createdAt ?? firstEvent?.createdAt ?? null,
|
||||||
@@ -130,6 +141,19 @@ async function durableTraceSnapshot(runtimeStore, traceId) {
|
|||||||
outputTruncated: normalizedEvents.some((event) => event.outputTruncated === true),
|
outputTruncated: normalizedEvents.some((event) => event.outputTruncated === true),
|
||||||
valuesPrinted: false
|
valuesPrinted: false
|
||||||
};
|
};
|
||||||
|
rememberTerminalDurableTrace(snapshot);
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rememberTerminalDurableTrace(snapshot) {
|
||||||
|
const traceId = safeTraceId(snapshot?.traceId);
|
||||||
|
if (!traceId || !TERMINAL_STATUSES.has(normalizeStatus(snapshot?.status))) return;
|
||||||
|
terminalDurableTraceCache.delete(traceId);
|
||||||
|
terminalDurableTraceCache.set(traceId, snapshot);
|
||||||
|
while (terminalDurableTraceCache.size > TERMINAL_DURABLE_TRACE_CACHE_MAX) {
|
||||||
|
const oldest = terminalDurableTraceCache.keys().next().value;
|
||||||
|
terminalDurableTraceCache.delete(oldest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function projectionStoreUnavailableTrace(traceId, error) {
|
function projectionStoreUnavailableTrace(traceId, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user