mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:40:43 +00:00
Optimize session list model row resolution
This commit is contained in:
@@ -259,7 +259,7 @@ describe("gateway session utils", () => {
|
||||
expect(row.thinkingDefault).toBe("medium");
|
||||
});
|
||||
|
||||
test("session list memoizes repeated thinking enrichment per provider model", async () => {
|
||||
test("async session list skips per-row thinking enrichment for lightweight rows", async () => {
|
||||
const resolveThinkingProfile = vi.fn(() => ({
|
||||
levels: [{ id: "off" as const }, { id: "medium" as const }],
|
||||
defaultLevel: "medium" as const,
|
||||
@@ -298,7 +298,10 @@ describe("gateway session utils", () => {
|
||||
});
|
||||
|
||||
expect(result.sessions).toHaveLength(5);
|
||||
expect(resolveThinkingProfile).toHaveBeenCalledTimes(3);
|
||||
expect(result.sessions.every((session) => session.thinkingLevels === undefined)).toBe(true);
|
||||
expect(result.sessions.every((session) => session.thinkingOptions === undefined)).toBe(true);
|
||||
expect(result.sessions.every((session) => session.thinkingDefault === undefined)).toBe(true);
|
||||
expect(resolveThinkingProfile).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test("session list thinking cache preserves case-distinct model catalog entries", async () => {
|
||||
@@ -319,7 +322,7 @@ describe("gateway session utils", () => {
|
||||
compat: { supportedReasoningEfforts: ["low", "medium", "high"] },
|
||||
},
|
||||
];
|
||||
const result = await listSessionsFromStoreAsync({
|
||||
const result = listSessionsFromStore({
|
||||
cfg,
|
||||
storePath: "",
|
||||
modelCatalog,
|
||||
@@ -1289,7 +1292,10 @@ describe("listSessionsFromStore selected model display", () => {
|
||||
}),
|
||||
);
|
||||
expect(listed.sessions[0]?.agentRuntime).toEqual({ id: "pi", source: "implicit" });
|
||||
expect(listed.sessions[0]?.thinkingOptions?.length).toBeGreaterThan(0);
|
||||
expect(listed.sessions[0]?.thinkingLevel).toBeUndefined();
|
||||
expect(listed.sessions[0]?.thinkingLevels).toBeUndefined();
|
||||
expect(listed.sessions[0]?.thinkingOptions).toBeUndefined();
|
||||
expect(listed.sessions[0]?.thinkingDefault).toBeUndefined();
|
||||
} finally {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
@@ -1478,6 +1484,58 @@ describe("listSessionsFromStore selected model display", () => {
|
||||
["agent:review:review", "vercel-ai-gateway", "anthropic/claude-haiku-4-5"],
|
||||
]);
|
||||
});
|
||||
|
||||
test("uses persisted runtime model metadata before selected defaults", () => {
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: { model: { primary: "openai/gpt-5.4" } },
|
||||
list: [{ id: "main", model: { primary: "anthropic/claude-sonnet-4-6" } }],
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const result = listSessionsFromStore({
|
||||
cfg,
|
||||
storePath: "/tmp/sessions.json",
|
||||
store: {
|
||||
"agent:main:main": {
|
||||
sessionId: "sess-main",
|
||||
updatedAt: Date.now(),
|
||||
modelProvider: "openai-codex",
|
||||
model: "gpt-5.5",
|
||||
} as SessionEntry,
|
||||
},
|
||||
opts: {},
|
||||
});
|
||||
|
||||
expect(result.sessions[0]?.modelProvider).toBe("openai-codex");
|
||||
expect(result.sessions[0]?.model).toBe("gpt-5.5");
|
||||
});
|
||||
|
||||
test("uses complete model overrides without default-model fallback", () => {
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: { model: { primary: "openai/gpt-5.4" } },
|
||||
list: [{ id: "main", model: { primary: "anthropic/claude-sonnet-4-6" } }],
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const result = listSessionsFromStore({
|
||||
cfg,
|
||||
storePath: "/tmp/sessions.json",
|
||||
store: {
|
||||
"agent:main:main": {
|
||||
sessionId: "sess-main",
|
||||
updatedAt: Date.now(),
|
||||
providerOverride: "vercel-ai-gateway",
|
||||
modelOverride: "anthropic/claude-haiku-4-5",
|
||||
} as SessionEntry,
|
||||
},
|
||||
opts: {},
|
||||
});
|
||||
|
||||
expect(result.sessions[0]?.modelProvider).toBe("vercel-ai-gateway");
|
||||
expect(result.sessions[0]?.model).toBe("anthropic/claude-haiku-4-5");
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveSessionModelIdentityRef", () => {
|
||||
|
||||
@@ -578,6 +578,27 @@ function resolveSessionRowModelIdentityRef(params: {
|
||||
params.fallbackModelRef,
|
||||
);
|
||||
}
|
||||
const runtimeModel = normalizeOptionalString(params.entry?.model);
|
||||
const runtimeProvider = normalizeOptionalString(params.entry?.modelProvider);
|
||||
const fallbackModelRef = normalizeOptionalString(params.fallbackModelRef);
|
||||
if (runtimeModel && runtimeProvider && !fallbackModelRef) {
|
||||
return { provider: runtimeProvider, model: runtimeModel };
|
||||
}
|
||||
const normalizedOverride = normalizeStoredOverrideModel({
|
||||
providerOverride: params.entry?.providerOverride,
|
||||
modelOverride: params.entry?.modelOverride,
|
||||
});
|
||||
if (
|
||||
!runtimeModel &&
|
||||
!fallbackModelRef &&
|
||||
normalizedOverride.providerOverride &&
|
||||
normalizedOverride.modelOverride
|
||||
) {
|
||||
return {
|
||||
provider: normalizedOverride.providerOverride,
|
||||
model: normalizedOverride.modelOverride,
|
||||
};
|
||||
}
|
||||
const key = createSessionEntryModelCacheKey({
|
||||
cfg: params.cfg,
|
||||
agentId: params.agentId,
|
||||
@@ -589,14 +610,12 @@ function resolveSessionRowModelIdentityRef(params: {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const runtimeModel = normalizeOptionalString(params.entry?.model);
|
||||
const runtimeProvider = normalizeOptionalString(params.entry?.modelProvider);
|
||||
if (
|
||||
!runtimeModel &&
|
||||
!runtimeProvider &&
|
||||
!normalizeOptionalString(params.fallbackModelRef) &&
|
||||
!normalizeOptionalString(params.entry?.providerOverride) &&
|
||||
!normalizeOptionalString(params.entry?.modelOverride)
|
||||
!fallbackModelRef &&
|
||||
!normalizedOverride.providerOverride &&
|
||||
!normalizedOverride.modelOverride
|
||||
) {
|
||||
const resolved = resolveSessionDefaultModelRefForRow(params.cfg, params.agentId);
|
||||
params.rowContext.modelIdentityByEntryKey.set(key, resolved);
|
||||
@@ -623,6 +642,12 @@ function resolveSessionSelectedModelRef(params: {
|
||||
providerOverride: params.entry?.providerOverride,
|
||||
modelOverride: params.entry?.modelOverride,
|
||||
});
|
||||
if (override.providerOverride && override.modelOverride) {
|
||||
return {
|
||||
provider: override.providerOverride,
|
||||
model: override.modelOverride,
|
||||
};
|
||||
}
|
||||
if (!override.modelOverride) {
|
||||
return null;
|
||||
}
|
||||
@@ -1811,12 +1836,23 @@ export function buildGatewaySessionRow(params: {
|
||||
|
||||
const thinkingProvider = rowModelProvider ?? DEFAULT_PROVIDER;
|
||||
const thinkingModel = rowModel ?? DEFAULT_MODEL;
|
||||
const thinkingLevels = resolveSessionRowThinkingLevels({
|
||||
provider: thinkingProvider,
|
||||
model: thinkingModel,
|
||||
modelCatalog: params.modelCatalog,
|
||||
rowContext,
|
||||
});
|
||||
const thinkingLevels = lightweight
|
||||
? undefined
|
||||
: resolveSessionRowThinkingLevels({
|
||||
provider: thinkingProvider,
|
||||
model: thinkingModel,
|
||||
modelCatalog: params.modelCatalog,
|
||||
rowContext,
|
||||
});
|
||||
const thinkingDefault = lightweight
|
||||
? undefined
|
||||
: resolveGatewaySessionThinkingDefault({
|
||||
cfg,
|
||||
provider: thinkingProvider,
|
||||
model: thinkingModel,
|
||||
agentId: sessionAgentId,
|
||||
modelCatalog: params.modelCatalog,
|
||||
});
|
||||
const pluginExtensions =
|
||||
!lightweight && entry ? projectPluginSessionExtensionsSync({ sessionKey: key, entry }) : [];
|
||||
|
||||
@@ -1845,16 +1881,8 @@ export function buildGatewaySessionRow(params: {
|
||||
abortedLastRun: entry?.abortedLastRun,
|
||||
thinkingLevel: entry?.thinkingLevel,
|
||||
thinkingLevels,
|
||||
thinkingOptions: thinkingLevels.map((level) => level.label),
|
||||
thinkingDefault: lightweight
|
||||
? entry?.thinkingLevel
|
||||
: resolveGatewaySessionThinkingDefault({
|
||||
cfg,
|
||||
provider: thinkingProvider,
|
||||
model: thinkingModel,
|
||||
agentId: sessionAgentId,
|
||||
modelCatalog: params.modelCatalog,
|
||||
}),
|
||||
thinkingOptions: thinkingLevels?.map((level) => level.label),
|
||||
thinkingDefault,
|
||||
fastMode: entry?.fastMode,
|
||||
verboseLevel: entry?.verboseLevel,
|
||||
traceLevel: entry?.traceLevel,
|
||||
|
||||
Reference in New Issue
Block a user