Skip to content

Commit 2c0efef

Browse files
committed
fix(agent): use static catalog for embedded model fast path
1 parent 1c65500 commit 2c0efef

4 files changed

Lines changed: 61 additions & 13 deletions

File tree

src/agents/embedded-agent-runner.e2e.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,12 @@ describe("runEmbeddedAgent", () => {
352352
expect(resolveModelCall?.[1]).toBe("openrouter/auto");
353353
expect(resolveModelCall?.[2]).toBe(agentDir);
354354
expect(resolveModelCall?.[3]).toBe(cfg);
355-
expect(
356-
(resolveModelCall?.[4] as { skipAgentDiscovery?: boolean } | undefined)?.skipAgentDiscovery,
357-
).toBe(true);
355+
expect(resolveModelCall?.[4]).toEqual(
356+
expect.objectContaining({
357+
allowBundledStaticCatalogFallback: true,
358+
skipAgentDiscovery: true,
359+
}),
360+
);
358361
expect(ensureOpenClawModelsJsonMock).not.toHaveBeenCalled();
359362
});
360363

src/agents/embedded-agent-runner/model.test.ts

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -544,19 +544,46 @@ describe("resolveModel", () => {
544544
maxTokens: 8192,
545545
});
546546

547+
const runtimeHooks = createRuntimeHooks();
548+
const prepareProviderDynamicModel = vi.fn(async () => {});
549+
const reasoningEffortMap = {
550+
off: "none",
551+
minimal: "none",
552+
low: "high",
553+
medium: "high",
554+
high: "high",
555+
xhigh: "high",
556+
adaptive: "high",
557+
max: "high",
558+
};
559+
const normalizeProviderResolvedModelWithPlugin = vi.fn(({ context }) => ({
560+
...context.model,
561+
compat: {
562+
...context.model.compat,
563+
supportsStore: false,
564+
supportsReasoningEffort: true,
565+
maxTokensField: "max_tokens",
566+
reasoningEffortMap,
567+
},
568+
}));
547569
const result = await resolveModelAsync(
548570
"mistral",
549571
"mistral-medium-3-5",
550572
"/tmp/agent",
551573
undefined,
552574
{
553575
allowBundledStaticCatalogFallback: true,
554-
runtimeHooks: createRuntimeHooks(),
576+
runtimeHooks: {
577+
...runtimeHooks,
578+
normalizeProviderResolvedModelWithPlugin,
579+
prepareProviderDynamicModel,
580+
},
555581
skipAgentDiscovery: true,
556582
},
557583
);
558584

559-
expectRecordFields(expectResolvedModel(result), {
585+
const model = expectResolvedModel(result);
586+
expectRecordFields(model, {
560587
provider: "mistral",
561588
id: "mistral-medium-3-5",
562589
api: "openai-completions",
@@ -565,6 +592,22 @@ describe("resolveModel", () => {
565592
contextWindow: 262144,
566593
maxTokens: 8192,
567594
});
595+
expect(model.compat).toMatchObject({
596+
supportsStore: false,
597+
supportsReasoningEffort: true,
598+
maxTokensField: "max_tokens",
599+
reasoningEffortMap,
600+
});
601+
expect(normalizeProviderResolvedModelWithPlugin).toHaveBeenCalledWith(
602+
expect.objectContaining({
603+
provider: "mistral",
604+
context: expect.objectContaining({
605+
provider: "mistral",
606+
modelId: "mistral-medium-3-5",
607+
}),
608+
}),
609+
);
610+
expect(prepareProviderDynamicModel).not.toHaveBeenCalled();
568611
expect(resolveBundledStaticCatalogModelMock).toHaveBeenCalledWith({
569612
provider: "mistral",
570613
modelId: "mistral-medium-3-5",
@@ -627,7 +670,7 @@ describe("resolveModel", () => {
627670
expect(discoverModels).not.toHaveBeenCalled();
628671
});
629672

630-
it("falls back to bundled static catalog rows without agent discovery", async () => {
673+
it("uses bundled static catalog rows before dynamic hooks without agent discovery", async () => {
631674
const cfg = {
632675
models: {
633676
providers: {
@@ -682,8 +725,8 @@ describe("resolveModel", () => {
682725
cfg,
683726
}),
684727
);
685-
expect(prepareProviderDynamicModel).toHaveBeenCalled();
686-
expect(runProviderDynamicModel).toHaveBeenCalled();
728+
expect(prepareProviderDynamicModel).not.toHaveBeenCalled();
729+
expect(runProviderDynamicModel).not.toHaveBeenCalled();
687730
expect(discoverAuthStorage).not.toHaveBeenCalled();
688731
expect(discoverModels).not.toHaveBeenCalled();
689732
});

src/agents/embedded-agent-runner/model.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1656,7 +1656,9 @@ export async function resolveModelAsync(
16561656
let model =
16571657
explicitModel?.kind === "resolved" && !providerRuntimeMetadataShouldWin
16581658
? explicitModel.model
1659-
: undefined;
1659+
: options?.skipAgentDiscovery && !explicitModel && !providerRuntimeMetadataShouldWin
1660+
? await resolveStaticCatalogFallbackModel()
1661+
: undefined;
16601662
model ??= await resolveDynamicAttempt();
16611663
if (!model && !explicitModel && options?.retryTransientProviderRuntimeMiss) {
16621664
// Startup can race the first provider-runtime snapshot load on a fresh

src/agents/embedded-agent-runner/run.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -743,11 +743,11 @@ export async function runEmbeddedAgent(
743743
agentDir,
744744
params.config,
745745
{
746-
// Plugin dynamic model hooks can resolve explicit model refs without
747-
// first generating OpenClaw models.json. This keeps one-shot model runs from
748-
// blocking on unrelated provider discovery.
746+
// Static catalogs and plugin dynamic model hooks can resolve explicit model refs
747+
// without first generating OpenClaw models.json. This keeps one-shot model runs
748+
// from blocking on unrelated provider discovery.
749+
allowBundledStaticCatalogFallback: true,
749750
skipAgentDiscovery: true,
750-
allowBundledStaticCatalogFallback: pluginHarnessOwnsTransport,
751751
preferBundledStaticCatalogTransport: pluginHarnessOwnsTransport,
752752
workspaceDir: resolvedWorkspace,
753753
authProfileId: params.authProfileId,

0 commit comments

Comments
 (0)