mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 11:25:50 -06:00
288 lines
8.6 KiB
TypeScript
288 lines
8.6 KiB
TypeScript
// Verifies CLI runtime alias resolution and runtime model-ref equivalence.
|
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
|
import { testing as cliBackendsTesting } from "./cli-backends.test-support.js";
|
|
import {
|
|
createModelPickerVisibleProviderPredicate,
|
|
isRetiredModelPickerProvider,
|
|
} from "./model-runtime-aliases.js";
|
|
import {
|
|
areRuntimeModelRefsEquivalent,
|
|
isCliRuntimeProvider,
|
|
resolveCliRuntimeExecutionProvider as resolveCliRuntimeExecutionProviderBase,
|
|
} from "./model-runtime-aliases.js";
|
|
|
|
const anthropicAuthAliasMetadata = {
|
|
plugins: [
|
|
{
|
|
id: "anthropic",
|
|
origin: "bundled",
|
|
providerAuthChoices: [
|
|
{
|
|
provider: "anthropic",
|
|
method: "cli",
|
|
choiceId: "anthropic-cli",
|
|
deprecatedChoiceIds: ["claude-cli"],
|
|
choiceLabel: "Anthropic Claude CLI",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
} as never;
|
|
|
|
function resolveCliRuntimeExecutionProvider(
|
|
params: Omit<Parameters<typeof resolveCliRuntimeExecutionProviderBase>[0], "metadataSnapshot">,
|
|
) {
|
|
return resolveCliRuntimeExecutionProviderBase({
|
|
...params,
|
|
metadataSnapshot: anthropicAuthAliasMetadata,
|
|
});
|
|
}
|
|
|
|
function createAnthropicAuthConfig(params: {
|
|
order?: string[];
|
|
models?: NonNullable<NonNullable<OpenClawConfig["agents"]>["defaults"]>["models"];
|
|
}): OpenClawConfig {
|
|
// Auth order controls whether Anthropic execution is direct API or Claude
|
|
// CLI-backed when no explicit runtime policy overrides it.
|
|
return {
|
|
auth: {
|
|
order: params.order ? { anthropic: params.order } : undefined,
|
|
profiles: {
|
|
"anthropic:api": { provider: "anthropic", mode: "api_key" },
|
|
"anthropic:claude-cli": { provider: "claude-cli", mode: "oauth" },
|
|
},
|
|
},
|
|
agents: {
|
|
defaults: {
|
|
models: params.models,
|
|
},
|
|
},
|
|
} as OpenClawConfig;
|
|
}
|
|
|
|
describe("resolveCliRuntimeExecutionProvider", () => {
|
|
beforeEach(() => {
|
|
cliBackendsTesting.setDepsForTest({
|
|
resolvePluginSetupCliBackend: () => undefined,
|
|
resolvePluginSetupRegistry: () => ({
|
|
providers: [],
|
|
cliBackends: [],
|
|
configMigrations: [],
|
|
autoEnableProbes: [],
|
|
diagnostics: [],
|
|
}),
|
|
resolveRuntimeCliBackends: () => [
|
|
{
|
|
id: "claude-cli",
|
|
modelProvider: "anthropic",
|
|
pluginId: "anthropic",
|
|
config: { command: "claude" },
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
cliBackendsTesting.resetDepsForTest();
|
|
});
|
|
|
|
it("routes Anthropic execution to Claude CLI when the selected auth profile is Claude CLI", () => {
|
|
expect(
|
|
resolveCliRuntimeExecutionProvider({
|
|
cfg: createAnthropicAuthConfig({ order: ["anthropic:claude-cli"] }),
|
|
provider: "anthropic",
|
|
modelId: "opus-4.7",
|
|
}),
|
|
).toBe("claude-cli");
|
|
});
|
|
|
|
it("keeps direct Anthropic execution when the selected auth profile is direct Anthropic", () => {
|
|
expect(
|
|
resolveCliRuntimeExecutionProvider({
|
|
cfg: createAnthropicAuthConfig({
|
|
order: ["anthropic:api", "anthropic:claude-cli"],
|
|
}),
|
|
provider: "anthropic",
|
|
modelId: "opus-4.7",
|
|
}),
|
|
).toBeUndefined();
|
|
});
|
|
|
|
it("honors an explicit direct Anthropic auth profile over CLI auth order", () => {
|
|
expect(
|
|
resolveCliRuntimeExecutionProvider({
|
|
authProfileId: "anthropic:api",
|
|
cfg: createAnthropicAuthConfig({ order: ["anthropic:claude-cli"] }),
|
|
provider: "anthropic",
|
|
modelId: "opus-4.7",
|
|
}),
|
|
).toBeUndefined();
|
|
});
|
|
|
|
it("uses an explicit Claude CLI auth profile without a model-runtime entry", () => {
|
|
expect(
|
|
resolveCliRuntimeExecutionProvider({
|
|
authProfileId: "anthropic:claude-cli",
|
|
cfg: createAnthropicAuthConfig({ order: ["anthropic:api"] }),
|
|
provider: "anthropic",
|
|
modelId: "opus-4.7",
|
|
}),
|
|
).toBe("claude-cli");
|
|
});
|
|
|
|
it("does not override an explicit OpenClaw model-runtime policy with CLI auth", () => {
|
|
// Runtime policy is more explicit than profile order, so CLI auth cannot
|
|
// force a model onto the CLI harness when config says OpenClaw.
|
|
expect(
|
|
resolveCliRuntimeExecutionProvider({
|
|
cfg: createAnthropicAuthConfig({
|
|
order: ["anthropic:claude-cli"],
|
|
models: {
|
|
"anthropic/opus-4.7": { agentRuntime: { id: "openclaw" } },
|
|
},
|
|
}),
|
|
provider: "anthropic",
|
|
modelId: "opus-4.7",
|
|
}),
|
|
).toBeUndefined();
|
|
});
|
|
|
|
it("matches a configured claude-cli policy when the caller provider is empty", () => {
|
|
expect(
|
|
resolveCliRuntimeExecutionProvider({
|
|
cfg: createAnthropicAuthConfig({
|
|
models: {
|
|
"anthropic/opus-4.7": { agentRuntime: { id: "claude-cli" } },
|
|
},
|
|
}),
|
|
provider: "",
|
|
modelId: "opus-4.7",
|
|
}),
|
|
).toBe("claude-cli");
|
|
});
|
|
|
|
it("matches provider runtime policy from a provider-qualified model when the caller provider is empty", () => {
|
|
expect(
|
|
resolveCliRuntimeExecutionProvider({
|
|
cfg: {
|
|
models: {
|
|
providers: {
|
|
anthropic: {
|
|
baseUrl: "https://api.anthropic.example/v1",
|
|
agentRuntime: { id: "claude-cli" },
|
|
models: [],
|
|
},
|
|
},
|
|
},
|
|
} as OpenClawConfig,
|
|
provider: "",
|
|
modelId: "anthropic/opus-4.7",
|
|
}),
|
|
).toBe("claude-cli");
|
|
});
|
|
|
|
it("does not return a CLI runtime when the matched entry's provider is incompatible with the runtime alias", () => {
|
|
expect(
|
|
resolveCliRuntimeExecutionProvider({
|
|
cfg: createAnthropicAuthConfig({
|
|
models: {
|
|
"openrouter/opus-4.7": { agentRuntime: { id: "claude-cli" } },
|
|
},
|
|
}),
|
|
provider: "",
|
|
modelId: "opus-4.7",
|
|
}),
|
|
).toBeUndefined();
|
|
});
|
|
|
|
it("keeps standalone CLI backend provider refs visible", () => {
|
|
cliBackendsTesting.setDepsForTest({
|
|
resolveRuntimeCliBackends: () => [
|
|
{
|
|
id: "claude-cli",
|
|
modelProvider: "anthropic",
|
|
pluginId: "anthropic",
|
|
config: { command: "claude" },
|
|
},
|
|
{
|
|
id: "acme-cli",
|
|
pluginId: "acme",
|
|
config: { command: "acme" },
|
|
},
|
|
],
|
|
});
|
|
|
|
const isVisibleProvider = createModelPickerVisibleProviderPredicate();
|
|
|
|
expect(isCliRuntimeProvider("claude-cli")).toBe(true);
|
|
expect(isVisibleProvider("claude-cli")).toBe(false);
|
|
expect(isCliRuntimeProvider("acme-cli")).toBe(false);
|
|
expect(isVisibleProvider("acme-cli")).toBe(true);
|
|
});
|
|
|
|
it("recognizes retired picker providers without loading CLI backend metadata", () => {
|
|
cliBackendsTesting.setDepsForTest({
|
|
resolvePluginSetupRegistry: () => {
|
|
throw new Error("retired provider checks should not load setup metadata");
|
|
},
|
|
resolveRuntimeCliBackends: () => {
|
|
throw new Error("retired provider checks should not load runtime metadata");
|
|
},
|
|
});
|
|
|
|
expect(isRetiredModelPickerProvider("CODEX-CLI")).toBe(true);
|
|
expect(isRetiredModelPickerProvider("anthropic")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("areRuntimeModelRefsEquivalent", () => {
|
|
afterEach(() => {
|
|
cliBackendsTesting.resetDepsForTest();
|
|
});
|
|
|
|
it("does not load setup runtime aliases for already-identical refs", () => {
|
|
cliBackendsTesting.setDepsForTest({
|
|
resolvePluginSetupRegistry: () => {
|
|
throw new Error("setup registry should not load for identical refs");
|
|
},
|
|
resolveRuntimeCliBackends: () => [],
|
|
});
|
|
|
|
expect(
|
|
areRuntimeModelRefsEquivalent("anthropic/claude", "anthropic/claude", {
|
|
config: {},
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("resolves one setup runtime alias without loading the full setup registry", () => {
|
|
// Equivalence checks use targeted setup lookup so hot model comparisons do
|
|
// not load the full plugin setup registry.
|
|
cliBackendsTesting.setDepsForTest({
|
|
resolvePluginSetupCliBackend: ({ backend }) =>
|
|
backend === "claude-cli"
|
|
? {
|
|
pluginId: "anthropic",
|
|
backend: {
|
|
id: "claude-cli",
|
|
modelProvider: "anthropic",
|
|
config: { command: "claude" },
|
|
bundleMcp: false,
|
|
},
|
|
}
|
|
: undefined,
|
|
resolvePluginSetupRegistry: () => {
|
|
throw new Error("setup registry should not load for a single runtime alias");
|
|
},
|
|
resolveRuntimeCliBackends: () => [],
|
|
});
|
|
|
|
expect(
|
|
areRuntimeModelRefsEquivalent("anthropic/claude-opus-4-7", "claude-cli/claude-opus-4-7", {
|
|
config: {},
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
});
|