perf: reduce Workbench session rail refresh cost
Pipelines as Code CI / hwlab-nc01-v03-ci-poll- Success
Pipelines as Code CI / hwlab-nc01-v03-ci-poll- Success
This commit is contained in:
@@ -43,8 +43,7 @@ export function reduceWorkbenchLiveKafkaMessageState(previous: WorkbenchLiveMess
|
||||
if (previous.terminal) return previous;
|
||||
const terminal = workbenchLiveKafkaEventIsTerminal(event);
|
||||
const status = terminal ? firstNonEmptyString(event.status, "completed") ?? "completed" : "running";
|
||||
const lifecycleEvents = [...previousEvents, event];
|
||||
const assistantText = traceAssistantLifecycleEventIsSuperseded(event, lifecycleEvents, lifecycleEvents.length - 1)
|
||||
const assistantText = traceAssistantLifecycleEventIsSuperseded(event, previousEvents, previousEvents.length)
|
||||
? null
|
||||
: workbenchLiveKafkaAssistantText(event);
|
||||
return {
|
||||
|
||||
@@ -134,24 +134,23 @@ function reduceSessionDetail(state: WorkbenchServerState, session: WorkbenchSess
|
||||
|
||||
function reduceSessionList(state: WorkbenchServerState, sessions: WorkbenchSessionRecord[]): WorkbenchServerState {
|
||||
const sessionIds = sessions.map((session) => session.sessionId).filter((sessionId): sessionId is string => Boolean(sessionId));
|
||||
const next = sessions.reduce((current, session) => reduceSessionListItem(current, session), state);
|
||||
return { ...next, sessionOrder: unique(sessionIds) };
|
||||
}
|
||||
|
||||
function reduceSessionListItem(state: WorkbenchServerState, session: WorkbenchSessionRecord): WorkbenchServerState {
|
||||
const sessionId = session.sessionId;
|
||||
if (!sessionId) return state;
|
||||
const existing = state.sessionsById[sessionId];
|
||||
const existingMessages = state.messagesBySessionId[sessionId] ?? existing?.messages;
|
||||
const messages = Array.isArray(existingMessages) ? existingMessages : Array.isArray(session.messages) ? session.messages : [];
|
||||
const merged = mergeSessionRecord(existing, { ...session, messages });
|
||||
const sessionStatus = sessionStatusAuthorityFromDetail(session);
|
||||
const sessionsById = { ...state.sessionsById };
|
||||
const sessionStatusById = { ...state.sessionStatusById };
|
||||
for (const session of sessions) {
|
||||
const sessionId = session.sessionId;
|
||||
if (!sessionId) continue;
|
||||
const existing = sessionsById[sessionId];
|
||||
const existingMessages = state.messagesBySessionId[sessionId] ?? existing?.messages;
|
||||
const messages = Array.isArray(existingMessages) ? existingMessages : Array.isArray(session.messages) ? session.messages : [];
|
||||
sessionsById[sessionId] = mergeSessionRecord(existing, { ...session, messages });
|
||||
const sessionStatus = sessionStatusAuthorityFromDetail(session);
|
||||
if (sessionStatus) sessionStatusById[sessionId] = mergeSessionStatusAuthority(sessionStatusById[sessionId], sessionStatus);
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
sessionsById: { ...state.sessionsById, [sessionId]: merged },
|
||||
sessionStatusById: sessionStatus
|
||||
? { ...state.sessionStatusById, [sessionId]: mergeSessionStatusAuthority(state.sessionStatusById[sessionId], sessionStatus) }
|
||||
: state.sessionStatusById
|
||||
sessionOrder: unique(sessionIds),
|
||||
sessionsById,
|
||||
sessionStatusById
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -146,13 +146,14 @@ export function shouldShowSessionDetailLoading(input: {
|
||||
|
||||
export function sessionToSessionTab(session: WorkbenchSessionRecord, activeSessionId: string | null, sessionStatusAuthority: SessionStatusAuthorityMap = {}): SessionTab {
|
||||
const sessionId = session.sessionId;
|
||||
const updatedAt = sessionDisplayUpdatedAt(session);
|
||||
const latestAgent = latestAgentMessage(session.messages);
|
||||
const messageProjection = projectSessionTabMessages(session.messages);
|
||||
const updatedAt = sessionDisplayUpdatedAtWithUserMessage(session, messageProjection.latestUserMessageAt);
|
||||
const latestAgent = messageProjection.latestAgent;
|
||||
const status = resolveSessionTabStatus(session, sessionStatusAuthority[sessionId], latestAgent);
|
||||
const projection = projectionFromSession(session, sessionStatusAuthority[sessionId]);
|
||||
const projectionText = projectionDiagnosticText(projection);
|
||||
const trace = firstNonEmptyString(latestAgent?.traceId, latestAgent?.runnerTrace?.traceId, session.messages?.at(-1)?.traceId, session.lastTraceId);
|
||||
const userMessage = session.messages?.find((message) => message.role === "user");
|
||||
const trace = firstNonEmptyString(latestAgent?.traceId, latestAgent?.runnerTrace?.traceId, messageProjection.lastMessageTraceId, session.lastTraceId);
|
||||
const userMessage = messageProjection.firstUser;
|
||||
const running = isRunningIndicatorStatus(status);
|
||||
const preview = firstReadableSentence(
|
||||
session.firstUserMessagePreview,
|
||||
@@ -316,7 +317,11 @@ function mergeSelectedSessionDetail(stable: WorkbenchSessionRecord[], selected:
|
||||
}
|
||||
|
||||
function latestAgentMessage(messages: ChatMessage[] | undefined): ChatMessage | null {
|
||||
return [...(messages ?? [])].reverse().find((message) => message.role === "agent") ?? null;
|
||||
for (let index = (messages?.length ?? 0) - 1; index >= 0; index -= 1) {
|
||||
const message = messages?.[index];
|
||||
if (message?.role === "agent") return message;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function latestMessageForTrace(messages: ChatMessage[], traceId: string): ChatMessage | null {
|
||||
@@ -443,8 +448,12 @@ function firstReadableSentence(...values: unknown[]): string | null {
|
||||
}
|
||||
|
||||
export function sessionDisplayUpdatedAt(session: WorkbenchSessionRecord): string | null {
|
||||
return sessionDisplayUpdatedAtWithUserMessage(session, latestUserMessageAtFromMessages(session.messages));
|
||||
}
|
||||
|
||||
function sessionDisplayUpdatedAtWithUserMessage(session: WorkbenchSessionRecord, latestUserMessageAt: string | null): string | null {
|
||||
return firstNonEmptyString(
|
||||
latestUserMessageAtFromMessages(session.messages ?? []),
|
||||
latestUserMessageAt,
|
||||
session.lastUserMessageAt,
|
||||
session.snapshot?.lastUserMessageAt,
|
||||
session.startedAt,
|
||||
@@ -454,22 +463,40 @@ export function sessionDisplayUpdatedAt(session: WorkbenchSessionRecord): string
|
||||
}
|
||||
|
||||
export function latestUserMessageAtFromMessages(messages: ChatMessage[] | undefined): string | null {
|
||||
const userMessageTimes = (messages ?? [])
|
||||
.filter((message) => message.role === "user")
|
||||
.map((message) => firstNonEmptyString(message.createdAt, message.updatedAt));
|
||||
return latestTimestamp(...userMessageTimes);
|
||||
let latest: { value: string; ms: number } | null = null;
|
||||
let fallback: string | null = null;
|
||||
for (const message of messages ?? []) {
|
||||
if (message.role !== "user") continue;
|
||||
const value = firstNonEmptyString(message.createdAt, message.updatedAt);
|
||||
fallback ??= value;
|
||||
if (!value) continue;
|
||||
const ms = timestampMs(value);
|
||||
if (ms && (!latest || ms > latest.ms)) latest = { value, ms };
|
||||
}
|
||||
return latest?.value ?? fallback;
|
||||
}
|
||||
|
||||
function latestTimestamp(...values: unknown[]): string | null {
|
||||
let latest: { value: string; ms: number } | null = null;
|
||||
for (const value of values) {
|
||||
const text = firstNonEmptyString(value);
|
||||
if (!text) continue;
|
||||
const ms = timestampMs(text);
|
||||
if (!ms) continue;
|
||||
if (!latest || ms > latest.ms) latest = { value: text, ms };
|
||||
function projectSessionTabMessages(messages: ChatMessage[] | undefined): { latestAgent: ChatMessage | null; firstUser: ChatMessage | null; latestUserMessageAt: string | null; lastMessageTraceId: string | null } {
|
||||
let latestAgent: ChatMessage | null = null;
|
||||
let firstUser: ChatMessage | null = null;
|
||||
let latestUserMessageAt: { value: string; ms: number } | null = null;
|
||||
let firstUserMessageAt: string | null = null;
|
||||
for (const message of messages ?? []) {
|
||||
if (message.role === "agent") latestAgent = message;
|
||||
if (message.role !== "user") continue;
|
||||
firstUser ??= message;
|
||||
const value = firstNonEmptyString(message.createdAt, message.updatedAt);
|
||||
firstUserMessageAt ??= value;
|
||||
if (!value) continue;
|
||||
const ms = timestampMs(value);
|
||||
if (ms && (!latestUserMessageAt || ms > latestUserMessageAt.ms)) latestUserMessageAt = { value, ms };
|
||||
}
|
||||
return latest?.value ?? firstNonEmptyString(...values) ?? null;
|
||||
return {
|
||||
latestAgent,
|
||||
firstUser,
|
||||
latestUserMessageAt: latestUserMessageAt?.value ?? firstUserMessageAt,
|
||||
lastMessageTraceId: firstNonEmptyString(messages?.at(-1)?.traceId)
|
||||
};
|
||||
}
|
||||
|
||||
function readableSentence(value: unknown): string | null {
|
||||
|
||||
Reference in New Issue
Block a user