





















@@ -79,6 +79,7 @@ import {
7979normalizeOptionalLowercaseString,
8080} from "../shared/string-coerce.js";
8181import { normalizeSessionDeliveryFields } from "../utils/delivery-context.shared.js";
82+import type { ModelCostConfig } from "../utils/usage-format.js";
8283import { estimateUsageCost, resolveModelCostConfig } from "../utils/usage-format.js";
8384import {
8485resolveSessionStoreAgentId,
@@ -294,6 +295,24 @@ function buildCompactionCheckpointPreview(
294295};
295296}
296297298+function resolveModelCostConfigCached(
299+provider: string | undefined,
300+model: string | undefined,
301+cfg: OpenClawConfig,
302+rowContext?: SessionListRowContext,
303+): ModelCostConfig | undefined {
304+if (!rowContext) {
305+return resolveModelCostConfig({ provider, model, config: cfg });
306+}
307+const key = createSessionRowModelCacheKey(provider, model);
308+if (rowContext.modelCostConfigByModelRef.has(key)) {
309+return rowContext.modelCostConfigByModelRef.get(key);
310+}
311+const value = resolveModelCostConfig({ provider, model, config: cfg });
312+rowContext.modelCostConfigByModelRef.set(key, value);
313+return value;
314+}
315+297316function resolveEstimatedSessionCostUsd(params: {
298317cfg: OpenClawConfig;
299318provider?: string;
@@ -303,6 +322,7 @@ function resolveEstimatedSessionCostUsd(params: {
303322"estimatedCostUsd" | "inputTokens" | "outputTokens" | "cacheRead" | "cacheWrite"
304323>;
305324explicitCostUsd?: number;
325+rowContext?: SessionListRowContext;
306326}): number | undefined {
307327const explicitCostUsd = resolveNonNegativeNumber(
308328params.explicitCostUsd ?? params.entry?.estimatedCostUsd,
@@ -322,11 +342,12 @@ function resolveEstimatedSessionCostUsd(params: {
322342) {
323343return undefined;
324344}
325-const cost = resolveModelCostConfig({
326-provider: params.provider,
327-model: params.model,
328-config: params.cfg,
329-});
345+const cost = resolveModelCostConfigCached(
346+params.provider,
347+params.model,
348+params.cfg,
349+params.rowContext,
350+);
330351if (!cost) {
331352return undefined;
332353}
@@ -372,13 +393,19 @@ type SessionListRowContext = {
372393subagentRuns: ReturnType<typeof buildSubagentRunReadIndex>;
373394storeChildSessionsByKey: Map<string, string[]>;
374395selectedModelByOverrideRef: Map<string, ReturnType<typeof resolveSessionModelRef>>;
396+// Per-list memoization for deterministic resolvers that scale linearly with
397+// session count but only depend on (provider, model[, agentId]). Sessions
398+// in a single list typically share a small set of those tuples, so caching
399+// here collapses the work to O(unique tuples) per call.
375400thinkingMetadataByModelRef: Map<
376401string,
377402{
378403levels: ReturnType<typeof listThinkingLevelOptions>;
379404defaultLevel: ReturnType<typeof resolveGatewaySessionThinkingDefault>;
380405}
381406>;
407+displayModelIdentityByKey: Map<string, { provider?: string; model?: string }>;
408+modelCostConfigByModelRef: Map<string, ModelCostConfig | undefined>;
382409};
383410384411function resolveRuntimeChildSessionKeys(
@@ -497,6 +524,8 @@ function buildSessionListRowContext(params: {
497524storeChildSessionsByKey: buildStoreChildSessionIndex(params.store, params.now, subagentRuns),
498525selectedModelByOverrideRef: new Map(),
499526thinkingMetadataByModelRef: new Map(),
527+displayModelIdentityByKey: new Map(),
528+modelCostConfigByModelRef: new Map(),
500529};
501530}
502531@@ -622,6 +651,7 @@ function resolveTranscriptUsageFallback(params: {
622651fallbackProvider?: string;
623652fallbackModel?: string;
624653maxTranscriptBytes?: number;
654+rowContext?: SessionListRowContext;
625655}): {
626656estimatedCostUsd?: number;
627657totalTokens?: number;
@@ -668,6 +698,7 @@ function resolveTranscriptUsageFallback(params: {
668698cacheRead: snapshot.cacheRead,
669699cacheWrite: snapshot.cacheWrite,
670700},
701+rowContext: params.rowContext,
671702});
672703return {
673704 modelProvider,
@@ -1508,6 +1539,30 @@ export function resolveSessionModelIdentityRef(
15081539return { provider: resolved.provider, model: resolved.model };
15091540}
151015411542+function resolveSessionDisplayModelIdentityRefCached(params: {
1543+cfg: OpenClawConfig;
1544+agentId: string;
1545+provider?: string;
1546+model?: string;
1547+rowContext?: SessionListRowContext;
1548+}): { provider?: string; model?: string } {
1549+const ctx = params.rowContext;
1550+if (!ctx) {
1551+return resolveSessionDisplayModelIdentityRef(params);
1552+}
1553+const key = `${params.agentId}\u0000${createSessionRowModelCacheKey(
1554+ params.provider,
1555+ params.model,
1556+ )}`;
1557+const cached = ctx.displayModelIdentityByKey.get(key);
1558+if (cached) {
1559+return cached;
1560+}
1561+const value = resolveSessionDisplayModelIdentityRef(params);
1562+ctx.displayModelIdentityByKey.set(key, value);
1563+return value;
1564+}
1565+15111566export function resolveSessionDisplayModelIdentityRef(params: {
15121567cfg: OpenClawConfig;
15131568agentId: string;
@@ -1671,6 +1726,7 @@ export function buildGatewaySessionRow(params: {
16711726provider: resolvedModel.provider,
16721727model: resolvedModel.model ?? DEFAULT_MODEL,
16731728 entry,
1729+ rowContext,
16741730}) === undefined;
16751731const transcriptUsage =
16761732!skipTranscriptUsage &&
@@ -1683,6 +1739,7 @@ export function buildGatewaySessionRow(params: {
16831739fallbackProvider: resolvedModel.provider,
16841740fallbackModel: resolvedModel.model ?? DEFAULT_MODEL,
16851741maxTranscriptBytes: params.transcriptUsageMaxBytes,
1742+rowContext: params.rowContext,
16861743})
16871744 : null;
16881745const preferLiveSubagentModelIdentity =
@@ -1722,11 +1779,12 @@ export function buildGatewaySessionRow(params: {
17221779const selectedOrRuntimeModel = selectedModel?.model ?? model;
17231780const rowModelIdentity = lightweight
17241781 ? { provider: selectedOrRuntimeModelProvider, model: selectedOrRuntimeModel }
1725- : resolveSessionDisplayModelIdentityRef({
1782+ : resolveSessionDisplayModelIdentityRefCached({
17261783 cfg,
17271784agentId: sessionAgentId,
17281785provider: selectedOrRuntimeModelProvider,
17291786model: selectedOrRuntimeModel,
1787+rowContext: params.rowContext,
17301788});
17311789const rowModelProvider = rowModelIdentity.provider;
17321790const rowModel = rowModelIdentity.model;
@@ -1746,6 +1804,7 @@ export function buildGatewaySessionRow(params: {
17461804provider: rowModelProvider,
17471805model: rowModel,
17481806 entry,
1807+rowContext: params.rowContext,
17491808}) ?? resolveNonNegativeNumber(transcriptUsage?.estimatedCostUsd));
17501809const contextTokens = lightweight
17511810 ? resolvePositiveNumber(entry?.contextTokens)
@@ -1817,7 +1876,7 @@ export function buildGatewaySessionRow(params: {
18171876abortedLastRun: entry?.abortedLastRun,
18181877thinkingLevel: entry?.thinkingLevel,
18191878 thinkingLevels,
1820-thinkingOptions: thinkingLevels?.map((level) => level.label),
1879+thinkingOptions: thinkingLevels.map((level) => level.label),
18211880 thinkingDefault,
18221881fastMode: entry?.fastMode,
18231882verboseLevel: entry?.verboseLevel,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。