From ed1dfe23d473f8ccb2e584af83db9a8fbe60eb77 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Tue, 14 Apr 2026 23:59:03 +0100 Subject: [PATCH] perf(commands): trim sessions cold-path imports --- src/commands/sessions-cleanup.ts | 8 +- src/commands/sessions-display-model.ts | 103 +++++++++++++++++++++++++ src/commands/sessions-table.ts | 28 ------- src/commands/sessions.ts | 74 ++++++++++++------ 4 files changed, 159 insertions(+), 54 deletions(-) create mode 100644 src/commands/sessions-display-model.ts diff --git a/src/commands/sessions-cleanup.ts b/src/commands/sessions-cleanup.ts index 7c539a856e5..1e55904caa5 100644 --- a/src/commands/sessions-cleanup.ts +++ b/src/commands/sessions-cleanup.ts @@ -19,13 +19,15 @@ import { resolveSessionStoreTargetsOrExit, type SessionStoreTarget, } from "./session-store-targets.js"; +import { + resolveSessionDisplayDefaults, + resolveSessionDisplayModel, +} from "./sessions-display-model.js"; import { formatSessionAgeCell, formatSessionFlagsCell, formatSessionKeyCell, formatSessionModelCell, - resolveSessionDisplayDefaults, - resolveSessionDisplayModel, SESSION_AGE_PAD, SESSION_KEY_PAD, SESSION_MODEL_PAD, @@ -280,7 +282,7 @@ function renderStoreDryRunPlan(params: { ].join(" "); params.runtime.log(rich ? theme.heading(header) : header); for (const actionRow of params.actionRows) { - const model = resolveSessionDisplayModel(params.cfg, actionRow, params.displayDefaults); + const model = resolveSessionDisplayModel(params.cfg, actionRow); const line = [ formatCleanupActionCell(actionRow.action, rich), formatSessionKeyCell(actionRow.key, rich), diff --git a/src/commands/sessions-display-model.ts b/src/commands/sessions-display-model.ts new file mode 100644 index 00000000000..da369ce4d71 --- /dev/null +++ b/src/commands/sessions-display-model.ts @@ -0,0 +1,103 @@ +import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js"; +import { resolveAgentModelPrimaryValue } from "../config/model-input.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; + +type SessionDisplayModelRow = { + key: string; + model?: string; + modelProvider?: string; + modelOverride?: string; + providerOverride?: string; +}; + +type SessionDisplayDefaults = { + model: string; +}; + +function parseModelRef(raw: string, defaultProvider: string): { provider: string; model: string } { + const trimmed = raw.trim(); + if (!trimmed) { + return { provider: defaultProvider, model: DEFAULT_MODEL }; + } + const slashIndex = trimmed.indexOf("/"); + if (slashIndex <= 0 || slashIndex === trimmed.length - 1) { + return { provider: defaultProvider, model: trimmed }; + } + return { + provider: trimmed.slice(0, slashIndex).trim() || defaultProvider, + model: trimmed.slice(slashIndex + 1).trim() || DEFAULT_MODEL, + }; +} + +function resolveAgentPrimaryModel( + cfg: OpenClawConfig, + agentId: string | undefined, +): string | undefined { + if (!agentId) { + return undefined; + } + const agentConfig = cfg.agents?.list?.find((agent) => agent.id === agentId); + return resolveAgentModelPrimaryValue(agentConfig?.model); +} + +function normalizeStoredOverrideModel(params: { + providerOverride?: string; + modelOverride?: string; +}): { providerOverride?: string; modelOverride?: string } { + const providerOverride = params.providerOverride?.trim(); + const modelOverride = params.modelOverride?.trim(); + if (!providerOverride || !modelOverride) { + return { providerOverride, modelOverride }; + } + + const providerPrefix = `${providerOverride.toLowerCase()}/`; + return { + providerOverride, + modelOverride: modelOverride.toLowerCase().startsWith(providerPrefix) + ? modelOverride.slice(providerOverride.length + 1).trim() || modelOverride + : modelOverride, + }; +} + +function resolveDefaultModelRef( + cfg: OpenClawConfig, + agentId?: string, +): { provider: string; model: string } { + const primary = + resolveAgentPrimaryModel(cfg, agentId) ?? + resolveAgentModelPrimaryValue(cfg.agents?.defaults?.model) ?? + DEFAULT_MODEL; + return parseModelRef(primary, DEFAULT_PROVIDER); +} + +export function resolveSessionDisplayDefaults( + cfg: OpenClawConfig, + agentId?: string, +): SessionDisplayDefaults { + return { + model: resolveDefaultModelRef(cfg, agentId).model, + }; +} + +export function resolveSessionDisplayModel( + cfg: OpenClawConfig, + row: SessionDisplayModelRow, +): string { + const agentId = row.key.startsWith("agent:") ? row.key.split(":")[1] : undefined; + const defaultRef = resolveDefaultModelRef(cfg, agentId); + const normalizedOverride = normalizeStoredOverrideModel({ + providerOverride: row.providerOverride, + modelOverride: row.modelOverride, + }); + + if (normalizedOverride.modelOverride) { + return parseModelRef( + normalizedOverride.modelOverride, + normalizedOverride.providerOverride ?? defaultRef.provider, + ).model; + } + if (row.model) { + return parseModelRef(row.model, row.modelProvider ?? defaultRef.provider).model; + } + return defaultRef.model; +} diff --git a/src/commands/sessions-table.ts b/src/commands/sessions-table.ts index d019361b3cb..1e2b0a357f0 100644 --- a/src/commands/sessions-table.ts +++ b/src/commands/sessions-table.ts @@ -1,10 +1,5 @@ -import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js"; -import { resolveConfiguredModelRef } from "../agents/model-selection.js"; import type { SessionEntry } from "../config/sessions.js"; -import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { resolveSessionModelRef } from "../gateway/session-utils.js"; import { formatTimeAgo } from "../infra/format-time/format-relative.ts"; -import { parseAgentSessionKey } from "../routing/session-key.js"; import { theme } from "../terminal/theme.js"; export type SessionDisplayRow = { @@ -72,29 +67,6 @@ export function toSessionDisplayRows(store: Record): Sessi .toSorted((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0)); } -export function resolveSessionDisplayDefaults(cfg: OpenClawConfig): SessionDisplayDefaults { - const resolved = resolveConfiguredModelRef({ - cfg, - defaultProvider: DEFAULT_PROVIDER, - defaultModel: DEFAULT_MODEL, - }); - return { - model: resolved.model ?? DEFAULT_MODEL, - }; -} - -export function resolveSessionDisplayModel( - cfg: OpenClawConfig, - row: Pick< - SessionDisplayRow, - "key" | "model" | "modelProvider" | "modelOverride" | "providerOverride" - >, - defaults: SessionDisplayDefaults, -): string { - const resolved = resolveSessionModelRef(cfg, row, parseAgentSessionKey(row.key)?.agentId); - return resolved.model ?? defaults.model; -} - function truncateSessionKey(key: string): string { if (key.length <= SESSION_KEY_PAD) { return key; diff --git a/src/commands/sessions.ts b/src/commands/sessions.ts index 2b6dd411a1e..2ca8db3e96f 100644 --- a/src/commands/sessions.ts +++ b/src/commands/sessions.ts @@ -1,20 +1,20 @@ -import { lookupContextTokens } from "../agents/context.js"; import { DEFAULT_CONTEXT_TOKENS } from "../agents/defaults.js"; import { loadConfig } from "../config/config.js"; import { loadSessionStore, resolveFreshSessionTotalTokens } from "../config/sessions.js"; -import { classifySessionKey } from "../gateway/session-utils.js"; import { info } from "../globals.js"; import { parseAgentSessionKey } from "../routing/session-key.js"; import { type RuntimeEnv, writeRuntimeJson } from "../runtime.js"; import { isRich, theme } from "../terminal/theme.js"; import { resolveSessionStoreTargetsOrExit } from "./session-store-targets.js"; +import { + resolveSessionDisplayDefaults, + resolveSessionDisplayModel, +} from "./sessions-display-model.js"; import { formatSessionAgeCell, formatSessionFlagsCell, formatSessionKeyCell, formatSessionModelCell, - resolveSessionDisplayDefaults, - resolveSessionDisplayModel, SESSION_AGE_PAD, SESSION_KEY_PAD, SESSION_MODEL_PAD, @@ -30,6 +30,7 @@ type SessionRow = SessionDisplayRow & { const AGENT_PAD = 10; const KIND_PAD = 6; const TOKENS_PAD = 20; +let contextLookupRuntimePromise: Promise | null = null; const formatKTokens = (value: number) => `${(value / 1000).toFixed(value >= 10_000 ? 0 : 1)}k`; @@ -67,6 +68,28 @@ const formatTokensCell = ( return colorByPct(padded, pct, rich); }; +async function lookupContextTokensForDisplay(model: string): Promise { + contextLookupRuntimePromise ??= import("../agents/context.js"); + const { lookupContextTokens } = await contextLookupRuntimePromise; + return lookupContextTokens(model, { allowAsyncLoad: false }); +} + +function classifySessionKey(key: string, entry?: { chatType?: string | null }): SessionRow["kind"] { + if (key === "global") { + return "global"; + } + if (key === "unknown") { + return "unknown"; + } + if (entry?.chatType === "group" || entry?.chatType === "channel") { + return "group"; + } + if (key.includes(":group:") || key.includes(":channel:")) { + return "group"; + } + return "direct"; +} + const formatKindCell = (kind: SessionRow["kind"], rich: boolean) => { const label = kind.padEnd(KIND_PAD); if (!rich) { @@ -91,9 +114,10 @@ export async function sessionsCommand( const aggregateAgents = opts.allAgents === true; const cfg = loadConfig(); const displayDefaults = resolveSessionDisplayDefaults(cfg); + const configuredContextTokens = cfg.agents?.defaults?.contextTokens; const configContextTokens = - cfg.agents?.defaults?.contextTokens ?? - lookupContextTokens(displayDefaults.model, { allowAsyncLoad: false }) ?? + configuredContextTokens ?? + (await lookupContextTokensForDisplay(displayDefaults.model)) ?? DEFAULT_CONTEXT_TOKENS; const targets = resolveSessionStoreTargetsOrExit({ cfg, @@ -153,21 +177,24 @@ export async function sessionsCommand( allAgents: aggregateAgents ? true : undefined, count: rows.length, activeMinutes: activeMinutes ?? null, - sessions: rows.map((r) => { - const model = resolveSessionDisplayModel(cfg, r, displayDefaults); - return { - ...r, - totalTokens: resolveFreshSessionTotalTokens(r) ?? null, - totalTokensFresh: - typeof r.totalTokens === "number" ? r.totalTokensFresh !== false : false, - contextTokens: - r.contextTokens ?? - lookupContextTokens(model, { allowAsyncLoad: false }) ?? - configContextTokens ?? - null, - model, - }; - }), + sessions: await Promise.all( + rows.map(async (r) => { + const model = resolveSessionDisplayModel(cfg, r); + return { + ...r, + totalTokens: resolveFreshSessionTotalTokens(r) ?? null, + totalTokensFresh: + typeof r.totalTokens === "number" ? r.totalTokensFresh !== false : false, + contextTokens: + r.contextTokens ?? + configuredContextTokens ?? + (await lookupContextTokensForDisplay(model)) ?? + configContextTokens ?? + null, + model, + }; + }), + ), }); return; } @@ -203,10 +230,11 @@ export async function sessionsCommand( runtime.log(rich ? theme.heading(header) : header); for (const row of rows) { - const model = resolveSessionDisplayModel(cfg, row, displayDefaults); + const model = resolveSessionDisplayModel(cfg, row); const contextTokens = row.contextTokens ?? - lookupContextTokens(model, { allowAsyncLoad: false }) ?? + configuredContextTokens ?? + (await lookupContextTokensForDisplay(model)) ?? configContextTokens; const total = resolveFreshSessionTotalTokens(row);