fix(guardian): resolve well-known provider baseUrl from pi-ai model database

When a provider (e.g. anthropic, openai) is not explicitly configured in
openclaw.json, fall back to pi-ai's built-in model database to resolve
baseUrl and api type. This avoids requiring users to manually configure
well-known providers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ShengtongZhu 2026-03-15 19:33:14 +08:00
parent 474a41a3ee
commit e55c4c4044
2 changed files with 27 additions and 7 deletions

View File

@ -463,12 +463,13 @@ describe("guardian index — resolveModelFromConfig", () => {
expect(result.api).toBe("openai-completions"); // default
});
it("returns partial model for known providers not in explicit config — pending SDK resolution", () => {
it("resolves known providers from pi-ai built-in database when not in explicit config", () => {
const result = resolveModelFromConfig("anthropic", "claude-haiku-4-5", {});
expect(result).toBeDefined();
expect(result.provider).toBe("anthropic");
expect(result.modelId).toBe("claude-haiku-4-5");
expect(result.baseUrl).toBeUndefined(); // will be resolved via SDK
expect(result.baseUrl).toBe("https://api.anthropic.com");
expect(result.api).toBe("anthropic-messages");
});
it("inline config provider with baseUrl is fully resolved", () => {
@ -489,12 +490,12 @@ describe("guardian index — resolveModelFromConfig", () => {
expect(result.apiKey).toBe("custom-key");
});
it("preserves api type from config even without baseUrl", () => {
it("falls back to pi-ai database when config has empty baseUrl", () => {
const result = resolveModelFromConfig("anthropic", "claude-haiku-4-5", {
models: {
providers: {
anthropic: {
baseUrl: "", // empty — treated as missing
baseUrl: "", // empty — falls through to pi-ai
api: "anthropic-messages",
models: [],
},
@ -502,7 +503,8 @@ describe("guardian index — resolveModelFromConfig", () => {
},
});
expect(result.baseUrl).toBeUndefined();
// pi-ai resolves the baseUrl for known providers
expect(result.baseUrl).toBe("https://api.anthropic.com");
expect(result.api).toBe("anthropic-messages");
});
});

View File

@ -1,3 +1,4 @@
import { getModels as piGetModels } from "@mariozechner/pi-ai";
import type { OpenClawPluginApi, PluginRuntime } from "openclaw/plugin-sdk/core";
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
import { callGuardian } from "./guardian-client.js";
@ -334,8 +335,25 @@ function resolveModelFromConfig(
};
}
// No explicit provider config — return partial model.
// baseUrl and api will be resolved lazily via SDK's resolveProviderInfo.
// No explicit provider config — try pi-ai's built-in model database.
// This covers well-known providers (anthropic, openai, google, etc.)
// that don't need explicit baseUrl config.
try {
const knownModels = piGetModels(provider as Parameters<typeof piGetModels>[0]);
if (knownModels.length > 0) {
const match = knownModels.find((m) => m.id === modelId) ?? knownModels[0];
return {
provider,
modelId,
baseUrl: match.baseUrl,
api: match.api,
headers: extractStringHeaders(providerConfig?.headers, match.headers),
};
}
} catch {
// Provider not in pi-ai's database — fall through
}
return {
provider,
modelId,