perf: avoid runtime catalog load for reasoning defaults

This commit is contained in:
Peter Steinberger
2026-05-28 08:43:40 +01:00
parent a0cf1858a2
commit 5da34a982b
2 changed files with 58 additions and 6 deletions

View File

@@ -54,8 +54,8 @@ vi.mock("../../agents/auth-profiles.runtime.js", () => ({
afterEach(() => {
MODEL_CONTEXT_TOKEN_CACHE.clear();
vi.mocked(loadManifestModelCatalog).mockReset();
vi.mocked(loadManifestModelCatalog).mockReturnValue([]);
vi.mocked(loadManifestModelCatalog).mockClear();
authProfileStoreMock.reset();
});
@@ -1524,6 +1524,30 @@ describe("createModelSelectionState auto-failover overrides", () => {
});
describe("createModelSelectionState resolveDefaultReasoningLevel", () => {
it("uses manifest metadata before hydrating the runtime reasoning catalog", async () => {
vi.mocked(loadModelCatalog).mockClear();
vi.mocked(loadManifestModelCatalog).mockClear();
vi.mocked(loadManifestModelCatalog).mockReturnValueOnce([
{ provider: "local", id: "fast-reasoner", name: "Fast Reasoner", reasoning: true },
]);
const state = await createModelSelectionState({
cfg: {} as OpenClawConfig,
agentCfg: undefined,
defaultProvider: "local",
defaultModel: "fast-reasoner",
provider: "local",
model: "fast-reasoner",
hasModelDirective: false,
});
await expect(state.resolveDefaultReasoningLevel()).resolves.toBe("on");
expect(loadManifestModelCatalog).toHaveBeenCalledWith({
config: {},
fallbackToMetadataScan: false,
});
expect(loadModelCatalog).not.toHaveBeenCalled();
});
it("returns on when catalog model has reasoning true", async () => {
const { loadModelCatalog } = await import("../../agents/model-catalog.runtime.js");
vi.mocked(loadModelCatalog).mockResolvedValueOnce([

View File

@@ -388,7 +388,7 @@ export async function createModelSelectionState(params: {
agentId: params.agentId,
...RUNTIME_MODEL_VISIBILITY_NORMALIZATION,
}).allowedCatalog;
const loadManifestCatalogForThinking = async () => {
const loadManifestCatalog = async () => {
if (manifestModelCatalog) {
return manifestModelCatalog;
}
@@ -397,7 +397,7 @@ export async function createModelSelectionState(params: {
config: cfg,
fallbackToMetadataScan: false,
});
logStage("manifest-catalog-loaded-for-thinking", `entries=${manifestModelCatalog.length}`);
logStage("manifest-catalog-loaded", `entries=${manifestModelCatalog.length}`);
return manifestModelCatalog;
};
const resolveThinkingCatalog = async () => {
@@ -419,7 +419,7 @@ export async function createModelSelectionState(params: {
// allowlist rows know only provider/id; manifest rows can prove reasoning
// support without opening the Pi auth-backed model registry.
if (!modelCatalog && selectedCatalogEntry?.reasoning === undefined) {
const manifestCatalog = buildThinkingCatalog(await loadManifestCatalogForThinking());
const manifestCatalog = buildThinkingCatalog(await loadManifestCatalog());
const manifestSelectedEntry = findSelectedCatalogEntry({
catalog: manifestCatalog,
provider,
@@ -475,18 +475,46 @@ export async function createModelSelectionState(params: {
return defaultThinkingLevel;
};
let defaultReasoningLevel: "on" | "off" | undefined;
const resolveDefaultReasoningLevel = async (): Promise<"on" | "off"> => {
if (defaultReasoningLevel) {
return defaultReasoningLevel;
}
let catalogForReasoning = modelCatalog ?? allowedModelCatalog;
if (!catalogForReasoning || catalogForReasoning.length === 0) {
let selectedReasoningEntry = findSelectedCatalogEntry({
catalog: catalogForReasoning,
provider,
model,
});
if (!modelCatalog && selectedReasoningEntry?.reasoning === undefined) {
const manifestCatalog = await loadManifestCatalog();
const manifestReasoningCatalog = hasAllowlist
? buildThinkingCatalog(manifestCatalog)
: manifestCatalog;
const manifestSelectedEntry = findSelectedCatalogEntry({
catalog: manifestReasoningCatalog,
provider,
model,
});
if (manifestSelectedEntry?.reasoning !== undefined) {
catalogForReasoning = manifestReasoningCatalog;
selectedReasoningEntry = manifestSelectedEntry;
}
}
if (
(!catalogForReasoning || catalogForReasoning.length === 0) &&
selectedReasoningEntry?.reasoning === undefined
) {
modelCatalog = await (await loadModelCatalogRuntime()).loadModelCatalog({ config: cfg });
logStage("catalog-loaded-for-reasoning", `entries=${modelCatalog.length}`);
catalogForReasoning = modelCatalog;
}
return resolveReasoningDefault({
defaultReasoningLevel = resolveReasoningDefault({
provider,
model,
catalog: catalogForReasoning,
});
return defaultReasoningLevel;
};
return {