mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:20:43 +00:00
fix(gateway): add lightweight row path for sessions.list to reduce event-loop blocking
sessions.list calls buildGatewaySessionRow for every visible session, running transcript usage fallback, display model inference, cost/context recomputation, thinking level enumeration, agent runtime metadata, and plugin extension projection per row. On installs with 30-50+ sessions this blocks the event loop for 20-80+ seconds, starving Discord heartbeats and Control UI RPCs. Add skipTranscriptUsageFallback and lightweightListRow flags to buildGatewaySessionRow. In lightweight mode, skip transcript usage fallback, display model inference, cost/context recomputation, thinking level options, agent runtime metadata, and plugin extension projection. Use persisted entry fields directly for cost, tokens, and model identity. listSessionsFromStoreAsync now passes both flags for bulk list rows. Detail endpoints and single-row loads are unaffected. Observed improvement on a production install (33 sessions): sessions.list row construction dropped from ~82s to ~6s. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1430,8 +1430,12 @@ export function buildGatewaySessionRow(params: {
|
||||
transcriptUsageMaxBytes?: number;
|
||||
storeChildSessionsByKey?: Map<string, string[]>;
|
||||
rowContext?: SessionListRowContext;
|
||||
skipTranscriptUsageFallback?: boolean;
|
||||
lightweightListRow?: boolean;
|
||||
}): GatewaySessionRow {
|
||||
const { cfg, storePath, store, key, entry } = params;
|
||||
const lightweight = params.lightweightListRow === true;
|
||||
const skipTranscriptUsage = params.skipTranscriptUsageFallback === true;
|
||||
const now = params.now ?? Date.now();
|
||||
const updatedAt = entry?.updatedAt ?? null;
|
||||
const parsed = parseGroupKey(key);
|
||||
@@ -1533,7 +1537,8 @@ export function buildGatewaySessionRow(params: {
|
||||
entry,
|
||||
}) === undefined;
|
||||
const transcriptUsage =
|
||||
needsTranscriptTotalTokens || needsTranscriptContextTokens || needsTranscriptEstimatedCostUsd
|
||||
!skipTranscriptUsage &&
|
||||
(needsTranscriptTotalTokens || needsTranscriptContextTokens || needsTranscriptEstimatedCostUsd)
|
||||
? resolveTranscriptUsageFallback({
|
||||
cfg,
|
||||
key,
|
||||
@@ -1577,36 +1582,39 @@ export function buildGatewaySessionRow(params: {
|
||||
const latestCompactionCheckpoint = buildCompactionCheckpointPreview(
|
||||
resolveLatestCompactionCheckpoint(entry),
|
||||
);
|
||||
const agentRuntime = resolveAgentRuntimeMetadata(cfg, sessionAgentId);
|
||||
const agentRuntime = lightweight ? undefined : resolveAgentRuntimeMetadata(cfg, sessionAgentId);
|
||||
const selectedOrRuntimeModelProvider = selectedModel?.provider ?? modelProvider;
|
||||
const selectedOrRuntimeModel = selectedModel?.model ?? model;
|
||||
const rowModelIdentity = resolveSessionDisplayModelIdentityRef({
|
||||
cfg,
|
||||
agentId: sessionAgentId,
|
||||
provider: selectedOrRuntimeModelProvider,
|
||||
model: selectedOrRuntimeModel,
|
||||
});
|
||||
const rowModelIdentity = lightweight
|
||||
? { provider: selectedOrRuntimeModelProvider, model: selectedOrRuntimeModel }
|
||||
: resolveSessionDisplayModelIdentityRef({
|
||||
cfg,
|
||||
agentId: sessionAgentId,
|
||||
provider: selectedOrRuntimeModelProvider,
|
||||
model: selectedOrRuntimeModel,
|
||||
});
|
||||
const rowModelProvider = rowModelIdentity.provider;
|
||||
const rowModel = rowModelIdentity.model;
|
||||
const estimatedCostUsd =
|
||||
resolveEstimatedSessionCostUsd({
|
||||
cfg,
|
||||
provider: rowModelProvider,
|
||||
model: rowModel,
|
||||
entry,
|
||||
}) ?? resolveNonNegativeNumber(transcriptUsage?.estimatedCostUsd);
|
||||
const contextTokens =
|
||||
resolvePositiveNumber(entry?.contextTokens) ??
|
||||
resolvePositiveNumber(transcriptUsage?.contextTokens) ??
|
||||
resolvePositiveNumber(
|
||||
resolveContextTokensForModel({
|
||||
const estimatedCostUsd = lightweight
|
||||
? resolveNonNegativeNumber(entry?.estimatedCostUsd)
|
||||
: resolveEstimatedSessionCostUsd({
|
||||
cfg,
|
||||
provider: rowModelProvider,
|
||||
model: rowModel,
|
||||
// Gateway/session listing is read-only; don't start async model discovery.
|
||||
allowAsyncLoad: false,
|
||||
}),
|
||||
);
|
||||
entry,
|
||||
}) ?? resolveNonNegativeNumber(transcriptUsage?.estimatedCostUsd);
|
||||
const contextTokens = lightweight
|
||||
? resolvePositiveNumber(entry?.contextTokens)
|
||||
: resolvePositiveNumber(entry?.contextTokens) ??
|
||||
resolvePositiveNumber(transcriptUsage?.contextTokens) ??
|
||||
resolvePositiveNumber(
|
||||
resolveContextTokensForModel({
|
||||
cfg,
|
||||
provider: rowModelProvider,
|
||||
model: rowModel,
|
||||
allowAsyncLoad: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let derivedTitle: string | undefined;
|
||||
let lastMessagePreview: string | undefined;
|
||||
@@ -1627,14 +1635,11 @@ export function buildGatewaySessionRow(params: {
|
||||
|
||||
const thinkingProvider = rowModelProvider ?? DEFAULT_PROVIDER;
|
||||
const thinkingModel = rowModel ?? DEFAULT_MODEL;
|
||||
const thinkingLevels = listThinkingLevelOptions(
|
||||
thinkingProvider,
|
||||
thinkingModel,
|
||||
params.modelCatalog,
|
||||
);
|
||||
const pluginExtensions = entry
|
||||
? projectPluginSessionExtensionsSync({ sessionKey: key, entry })
|
||||
: [];
|
||||
const thinkingLevels = lightweight
|
||||
? []
|
||||
: listThinkingLevelOptions(thinkingProvider, thinkingModel, params.modelCatalog);
|
||||
const pluginExtensions =
|
||||
!lightweight && entry ? projectPluginSessionExtensionsSync({ sessionKey: key, entry }) : [];
|
||||
|
||||
return {
|
||||
key,
|
||||
@@ -1662,13 +1667,15 @@ export function buildGatewaySessionRow(params: {
|
||||
thinkingLevel: entry?.thinkingLevel,
|
||||
thinkingLevels,
|
||||
thinkingOptions: thinkingLevels.map((level) => level.label),
|
||||
thinkingDefault: resolveGatewaySessionThinkingDefault({
|
||||
cfg,
|
||||
provider: thinkingProvider,
|
||||
model: thinkingModel,
|
||||
agentId: sessionAgentId,
|
||||
modelCatalog: params.modelCatalog,
|
||||
}),
|
||||
thinkingDefault: lightweight
|
||||
? entry?.thinkingLevel
|
||||
: resolveGatewaySessionThinkingDefault({
|
||||
cfg,
|
||||
provider: thinkingProvider,
|
||||
model: thinkingModel,
|
||||
agentId: sessionAgentId,
|
||||
modelCatalog: params.modelCatalog,
|
||||
}),
|
||||
fastMode: entry?.fastMode,
|
||||
verboseLevel: entry?.verboseLevel,
|
||||
traceLevel: entry?.traceLevel,
|
||||
@@ -2016,6 +2023,8 @@ export async function listSessionsFromStoreAsync(params: {
|
||||
transcriptUsageMaxBytes: sessionListTranscriptUsageMaxBytes,
|
||||
storeChildSessionsByKey: getRowContext().storeChildSessionsByKey,
|
||||
rowContext: getRowContext(),
|
||||
skipTranscriptUsageFallback: true,
|
||||
lightweightListRow: true,
|
||||
});
|
||||
if (
|
||||
entry?.sessionId &&
|
||||
|
||||
Reference in New Issue
Block a user