mirror of https://github.com/openclaw/openclaw.git
test: untangle provider tests from extension internals
This commit is contained in:
parent
f64a058348
commit
511e6c4189
|
|
@ -2,19 +2,16 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildAnthropicVertexProvider,
|
||||
mergeImplicitAnthropicVertexProvider,
|
||||
resolveImplicitAnthropicVertexProvider,
|
||||
} from "../../extensions/anthropic-vertex/api.js";
|
||||
import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js";
|
||||
|
||||
describe("anthropic-vertex implicit provider", () => {
|
||||
it("offers Claude models when GOOGLE_CLOUD_PROJECT_ID is set", () => {
|
||||
const provider = resolveImplicitAnthropicVertexProvider({
|
||||
it("does not auto-enable from GOOGLE_CLOUD_PROJECT_ID alone", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: { GOOGLE_CLOUD_PROJECT_ID: "vertex-project" },
|
||||
});
|
||||
expect(provider).toBeNull();
|
||||
expect(providers?.["anthropic-vertex"]).toBeUndefined();
|
||||
});
|
||||
|
||||
it("accepts ADC credentials when the file includes a project_id", async () => {
|
||||
|
|
@ -127,32 +124,41 @@ describe("anthropic-vertex implicit provider", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("accepts explicit metadata auth opt-in without local credential files", () => {
|
||||
const provider = resolveImplicitAnthropicVertexProvider({
|
||||
it("accepts explicit metadata auth opt-in without local credential files", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: {
|
||||
ANTHROPIC_VERTEX_USE_GCP_METADATA: "true",
|
||||
GOOGLE_CLOUD_LOCATION: "us-east5",
|
||||
},
|
||||
});
|
||||
expect(provider?.baseUrl).toBe("https://us-east5-aiplatform.googleapis.com");
|
||||
expect(providers?.["anthropic-vertex"]?.baseUrl).toBe(
|
||||
"https://us-east5-aiplatform.googleapis.com",
|
||||
);
|
||||
});
|
||||
|
||||
it("merges the bundled catalog into explicit anthropic-vertex provider overrides", async () => {
|
||||
const provider = mergeImplicitAnthropicVertexProvider({
|
||||
existing: {
|
||||
baseUrl: "https://europe-west4-aiplatform.googleapis.com",
|
||||
headers: { "x-test-header": "1" },
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: {
|
||||
ANTHROPIC_VERTEX_USE_GCP_METADATA: "true",
|
||||
GOOGLE_CLOUD_LOCATION: "us-east5",
|
||||
},
|
||||
implicit: buildAnthropicVertexProvider({
|
||||
env: {
|
||||
GOOGLE_CLOUD_LOCATION: "us-east5",
|
||||
explicitProviders: {
|
||||
"anthropic-vertex": {
|
||||
baseUrl: "https://europe-west4-aiplatform.googleapis.com",
|
||||
headers: { "x-test-header": "1" },
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
expect(provider.baseUrl).toBe("https://europe-west4-aiplatform.googleapis.com");
|
||||
expect(provider.headers).toEqual({ "x-test-header": "1" });
|
||||
expect(provider.models?.map((model) => model.id)).toEqual([
|
||||
expect(providers?.["anthropic-vertex"]?.baseUrl).toBe(
|
||||
"https://europe-west4-aiplatform.googleapis.com",
|
||||
);
|
||||
expect(providers?.["anthropic-vertex"]?.headers).toEqual({ "x-test-header": "1" });
|
||||
expect(providers?.["anthropic-vertex"]?.models?.map((model) => model.id)).toEqual([
|
||||
"claude-opus-4-6",
|
||||
"claude-sonnet-4-6",
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,42 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { buildCloudflareAiGatewayCatalogProvider } from "../../extensions/cloudflare-ai-gateway/api.js";
|
||||
import { captureEnv } from "../test-utils/env.js";
|
||||
import { NON_ENV_SECRETREF_MARKER } from "./model-auth-markers.js";
|
||||
import { resolveApiKeyFromCredential } from "./models-config.providers.secrets.js";
|
||||
|
||||
function expectedCloudflareGatewayBaseUrl(accountId: string, gatewayId: string): string {
|
||||
return `https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/anthropic`;
|
||||
}
|
||||
|
||||
function buildCloudflareAiGatewayCatalogProvider(params: {
|
||||
credential:
|
||||
| Parameters<typeof resolveApiKeyFromCredential>[0]
|
||||
| {
|
||||
metadata?: {
|
||||
accountId?: string;
|
||||
gatewayId?: string;
|
||||
};
|
||||
}
|
||||
| undefined;
|
||||
envApiKey?: string;
|
||||
}) {
|
||||
const apiKey =
|
||||
params.envApiKey?.trim() ||
|
||||
resolveApiKeyFromCredential(
|
||||
params.credential as Parameters<typeof resolveApiKeyFromCredential>[0],
|
||||
)?.apiKey;
|
||||
const accountId = params.credential?.metadata?.accountId?.trim();
|
||||
const gatewayId = params.credential?.metadata?.gatewayId?.trim();
|
||||
if (!apiKey || !accountId || !gatewayId) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
baseUrl: expectedCloudflareGatewayBaseUrl(accountId, gatewayId),
|
||||
api: "anthropic-messages",
|
||||
apiKey,
|
||||
models: [{ id: "cloudflare-ai-gateway" }],
|
||||
};
|
||||
}
|
||||
|
||||
describe("cloudflare-ai-gateway profile provenance", () => {
|
||||
it("prefers env keyRef marker over runtime plaintext for persistence", () => {
|
||||
const envSnapshot = captureEnv(["CLOUDFLARE_AI_GATEWAY_API_KEY"]);
|
||||
|
|
|
|||
|
|
@ -1,50 +1,9 @@
|
|||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { buildHuggingfaceProvider } from "../../extensions/huggingface/provider-catalog.js";
|
||||
import { buildVllmProvider } from "../../extensions/vllm/models.js";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { NON_ENV_SECRETREF_MARKER } from "./model-auth-markers.js";
|
||||
import { resolveApiKeyFromCredential } from "./models-config.providers.secrets.js";
|
||||
|
||||
describe("provider discovery auth marker guardrails", () => {
|
||||
let originalVitest: string | undefined;
|
||||
let originalNodeEnv: string | undefined;
|
||||
let originalFetch: typeof globalThis.fetch | undefined;
|
||||
|
||||
afterEach(() => {
|
||||
if (originalVitest !== undefined) {
|
||||
process.env.VITEST = originalVitest;
|
||||
} else {
|
||||
delete process.env.VITEST;
|
||||
}
|
||||
if (originalNodeEnv !== undefined) {
|
||||
process.env.NODE_ENV = originalNodeEnv;
|
||||
} else {
|
||||
delete process.env.NODE_ENV;
|
||||
}
|
||||
if (originalFetch) {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
function enableDiscovery() {
|
||||
originalVitest = process.env.VITEST ?? "true";
|
||||
originalNodeEnv = process.env.NODE_ENV ?? "test";
|
||||
originalFetch = globalThis.fetch;
|
||||
delete process.env.VITEST;
|
||||
delete process.env.NODE_ENV;
|
||||
}
|
||||
|
||||
function installFetchMock(response?: unknown) {
|
||||
const fetchMock =
|
||||
response === undefined
|
||||
? vi.fn()
|
||||
: vi.fn().mockResolvedValue({ ok: true, json: async () => response });
|
||||
globalThis.fetch = fetchMock as unknown as typeof fetch;
|
||||
return fetchMock;
|
||||
}
|
||||
|
||||
it("does not send marker value as vLLM bearer token during discovery", async () => {
|
||||
enableDiscovery();
|
||||
const fetchMock = installFetchMock({ data: [] });
|
||||
it("suppresses discovery secrets for marker-backed vLLM credentials", () => {
|
||||
const resolved = resolveApiKeyFromCredential({
|
||||
type: "api_key",
|
||||
provider: "vllm",
|
||||
|
|
@ -52,16 +11,10 @@ describe("provider discovery auth marker guardrails", () => {
|
|||
});
|
||||
|
||||
expect(resolved?.apiKey).toBe(NON_ENV_SECRETREF_MARKER);
|
||||
await buildVllmProvider({ apiKey: resolved?.discoveryApiKey });
|
||||
const request = fetchMock.mock.calls[0]?.[1] as
|
||||
| { headers?: Record<string, string> }
|
||||
| undefined;
|
||||
expect(request?.headers?.Authorization).toBeUndefined();
|
||||
expect(resolved?.discoveryApiKey).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not call Hugging Face discovery with marker-backed credentials", async () => {
|
||||
enableDiscovery();
|
||||
const fetchMock = installFetchMock();
|
||||
it("suppresses discovery secrets for marker-backed Hugging Face credentials", () => {
|
||||
const resolved = resolveApiKeyFromCredential({
|
||||
type: "api_key",
|
||||
provider: "huggingface",
|
||||
|
|
@ -69,16 +22,10 @@ describe("provider discovery auth marker guardrails", () => {
|
|||
});
|
||||
|
||||
expect(resolved?.apiKey).toBe(NON_ENV_SECRETREF_MARKER);
|
||||
await buildHuggingfaceProvider(resolved?.discoveryApiKey);
|
||||
const huggingfaceCalls = fetchMock.mock.calls.filter(([url]) =>
|
||||
String(url).includes("router.huggingface.co"),
|
||||
);
|
||||
expect(huggingfaceCalls).toHaveLength(0);
|
||||
expect(resolved?.discoveryApiKey).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps all-caps plaintext API keys for authenticated discovery", async () => {
|
||||
enableDiscovery();
|
||||
const fetchMock = installFetchMock({ data: [{ id: "vllm/test-model" }] });
|
||||
it("keeps all-caps plaintext API keys for authenticated discovery", () => {
|
||||
const resolved = resolveApiKeyFromCredential({
|
||||
type: "api_key",
|
||||
provider: "vllm",
|
||||
|
|
@ -86,9 +33,6 @@ describe("provider discovery auth marker guardrails", () => {
|
|||
});
|
||||
|
||||
expect(resolved?.apiKey).toBe("ALLCAPS_SAMPLE");
|
||||
await buildVllmProvider({ apiKey: resolved?.discoveryApiKey });
|
||||
const vllmCall = fetchMock.mock.calls.find(([url]) => String(url).includes(":8000"));
|
||||
const request = vllmCall?.[1] as { headers?: Record<string, string> } | undefined;
|
||||
expect(request?.headers?.Authorization).toBe("Bearer ALLCAPS_SAMPLE");
|
||||
expect(resolved?.discoveryApiKey).toBe("ALLCAPS_SAMPLE");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,11 +1,28 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
applyMoonshotNativeStreamingUsageCompat,
|
||||
buildMoonshotProvider,
|
||||
MOONSHOT_CN_BASE_URL,
|
||||
} from "../../extensions/moonshot/api.js";
|
||||
import { applyProviderNativeStreamingUsageCompat } from "../plugin-sdk/provider-catalog-shared.js";
|
||||
import { resolveMissingProviderApiKey } from "./models-config.providers.secrets.js";
|
||||
|
||||
const MOONSHOT_BASE_URL = "https://api.moonshot.ai/v1";
|
||||
const MOONSHOT_CN_BASE_URL = "https://api.moonshot.cn/v1";
|
||||
|
||||
function buildMoonshotProvider() {
|
||||
return {
|
||||
baseUrl: MOONSHOT_BASE_URL,
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
{
|
||||
id: "kimi-k2.5",
|
||||
name: "Kimi K2.5",
|
||||
reasoning: false,
|
||||
input: ["text", "image"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 262144,
|
||||
maxTokens: 262144,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
describe("moonshot implicit provider (#33637)", () => {
|
||||
it("uses explicit CN baseUrl when provided", () => {
|
||||
const provider = {
|
||||
|
|
@ -16,8 +33,10 @@ describe("moonshot implicit provider (#33637)", () => {
|
|||
expect(provider.baseUrl).toBe(MOONSHOT_CN_BASE_URL);
|
||||
expect(provider.models?.[0]?.compat?.supportsUsageInStreaming).toBeUndefined();
|
||||
expect(
|
||||
applyMoonshotNativeStreamingUsageCompat(provider).models?.[0]?.compat
|
||||
?.supportsUsageInStreaming,
|
||||
applyProviderNativeStreamingUsageCompat({
|
||||
providerId: "moonshot",
|
||||
providerConfig: provider,
|
||||
}).models?.[0]?.compat?.supportsUsageInStreaming,
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
|
|
@ -30,8 +49,10 @@ describe("moonshot implicit provider (#33637)", () => {
|
|||
expect(provider.baseUrl).toBe("https://proxy.example.com/v1");
|
||||
expect(provider.models?.[0]?.compat?.supportsUsageInStreaming).toBeUndefined();
|
||||
expect(
|
||||
applyMoonshotNativeStreamingUsageCompat(provider).models?.[0]?.compat
|
||||
?.supportsUsageInStreaming,
|
||||
applyProviderNativeStreamingUsageCompat({
|
||||
providerId: "moonshot",
|
||||
providerConfig: provider,
|
||||
}).models?.[0]?.compat?.supportsUsageInStreaming,
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,38 @@
|
|||
import { mkdtempSync } from "node:fs";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { withEnvAsync } from "../test-utils/env.js";
|
||||
import { resolveApiKeyForProvider } from "./model-auth.js";
|
||||
import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js";
|
||||
import {
|
||||
installModelsConfigTestHooks,
|
||||
resolveImplicitProvidersForTest,
|
||||
} from "./models-config.e2e-harness.js";
|
||||
import {
|
||||
resolveEnvApiKeyVarName,
|
||||
resolveMissingProviderApiKey,
|
||||
} from "./models-config.providers.secrets.js";
|
||||
|
||||
const NVIDIA_BASE_URL = "https://integrate.api.nvidia.com/v1";
|
||||
const MINIMAX_BASE_URL = "https://api.minimax.io/anthropic";
|
||||
const VLLM_DEFAULT_BASE_URL = "http://127.0.0.1:8000/v1";
|
||||
|
||||
installModelsConfigTestHooks();
|
||||
|
||||
describe("NVIDIA provider", () => {
|
||||
it("should include nvidia when NVIDIA_API_KEY is configured", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: { NVIDIA_API_KEY: "test-key" },
|
||||
it("should include nvidia when NVIDIA_API_KEY is configured", () => {
|
||||
const provider = resolveMissingProviderApiKey({
|
||||
providerKey: "nvidia",
|
||||
provider: {
|
||||
baseUrl: NVIDIA_BASE_URL,
|
||||
api: "openai-completions",
|
||||
models: [{ id: "nvidia/test-model" }],
|
||||
},
|
||||
env: { NVIDIA_API_KEY: "test-key" } as NodeJS.ProcessEnv,
|
||||
profileApiKey: undefined,
|
||||
});
|
||||
expect(providers?.nvidia).toBeDefined();
|
||||
expect(providers?.nvidia?.models?.length).toBeGreaterThan(0);
|
||||
expect(provider.apiKey).toBe("NVIDIA_API_KEY");
|
||||
expect(provider.models?.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("resolves the nvidia api key value from env", async () => {
|
||||
|
|
@ -34,16 +51,23 @@ describe("NVIDIA provider", () => {
|
|||
});
|
||||
|
||||
describe("MiniMax implicit provider (#15275)", () => {
|
||||
it("should use anthropic-messages API for API-key provider", { timeout: 240_000 }, async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: { MINIMAX_API_KEY: "test-key" },
|
||||
it("should use anthropic-messages API for API-key provider", () => {
|
||||
const provider = resolveMissingProviderApiKey({
|
||||
providerKey: "minimax",
|
||||
provider: {
|
||||
baseUrl: MINIMAX_BASE_URL,
|
||||
api: "anthropic-messages",
|
||||
authHeader: true,
|
||||
models: [{ id: "MiniMax-M2.7" }],
|
||||
},
|
||||
env: { MINIMAX_API_KEY: "test-key" } as NodeJS.ProcessEnv,
|
||||
profileApiKey: undefined,
|
||||
});
|
||||
expect(providers?.minimax).toBeDefined();
|
||||
expect(providers?.minimax?.api).toBe("anthropic-messages");
|
||||
expect(providers?.minimax?.authHeader).toBe(true);
|
||||
expect(providers?.minimax?.baseUrl).toBe("https://api.minimax.io/anthropic");
|
||||
|
||||
expect(provider.api).toBe("anthropic-messages");
|
||||
expect(provider.authHeader).toBe(true);
|
||||
expect(provider.apiKey).toBe("MINIMAX_API_KEY");
|
||||
expect(provider.baseUrl).toBe("https://api.minimax.io/anthropic");
|
||||
});
|
||||
|
||||
it("should respect MINIMAX_API_HOST env var for CN endpoint (#34487)", async () => {
|
||||
|
|
@ -55,69 +79,55 @@ describe("MiniMax implicit provider (#15275)", () => {
|
|||
MINIMAX_API_HOST: "https://api.minimaxi.com",
|
||||
},
|
||||
});
|
||||
expect(providers?.minimax).toBeDefined();
|
||||
|
||||
expect(providers?.minimax?.baseUrl).toBe("https://api.minimaxi.com/anthropic");
|
||||
expect(providers?.["minimax-portal"]?.baseUrl).toBe("https://api.minimaxi.com/anthropic");
|
||||
});
|
||||
|
||||
it("should set authHeader for minimax portal provider", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
await writeFile(
|
||||
join(agentDir, "auth-profiles.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
profiles: {
|
||||
"minimax-portal:default": {
|
||||
type: "oauth",
|
||||
provider: "minimax-portal",
|
||||
access: "token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const providers = await resolveImplicitProvidersForTest({ agentDir });
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: { MINIMAX_OAUTH_TOKEN: "portal-token" },
|
||||
});
|
||||
expect(providers?.["minimax-portal"]?.authHeader).toBe(true);
|
||||
});
|
||||
|
||||
it("should include minimax portal provider when MINIMAX_OAUTH_TOKEN is configured", async () => {
|
||||
expect(
|
||||
resolveEnvApiKeyVarName("minimax-portal", {
|
||||
MINIMAX_OAUTH_TOKEN: "portal-token",
|
||||
} as NodeJS.ProcessEnv),
|
||||
).toBe("MINIMAX_OAUTH_TOKEN");
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: { MINIMAX_OAUTH_TOKEN: "portal-token" },
|
||||
});
|
||||
expect(providers?.["minimax-portal"]).toBeDefined();
|
||||
expect(providers?.["minimax-portal"]?.authHeader).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("vLLM provider", () => {
|
||||
it("should not include vllm when no API key is configured", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = await resolveImplicitProvidersForTest({ agentDir, env: {} });
|
||||
expect(providers?.vllm).toBeUndefined();
|
||||
it("should not include vllm when no API key is configured", () => {
|
||||
expect(resolveEnvApiKeyVarName("vllm", {} as NodeJS.ProcessEnv)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should include vllm when VLLM_API_KEY is set", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: { VLLM_API_KEY: "test-key" },
|
||||
it("should include vllm when VLLM_API_KEY is set", () => {
|
||||
const provider = resolveMissingProviderApiKey({
|
||||
providerKey: "vllm",
|
||||
provider: {
|
||||
baseUrl: VLLM_DEFAULT_BASE_URL,
|
||||
api: "openai-completions",
|
||||
models: [],
|
||||
},
|
||||
env: { VLLM_API_KEY: "test-key" } as NodeJS.ProcessEnv,
|
||||
profileApiKey: undefined,
|
||||
});
|
||||
|
||||
expect(providers?.vllm).toBeDefined();
|
||||
expect(providers?.vllm?.apiKey).toBe("VLLM_API_KEY");
|
||||
expect(providers?.vllm?.baseUrl).toBe("http://127.0.0.1:8000/v1");
|
||||
expect(providers?.vllm?.api).toBe("openai-completions");
|
||||
|
||||
// Note: discovery is disabled in test environments (VITEST check)
|
||||
expect(providers?.vllm?.models).toEqual([]);
|
||||
expect(provider.apiKey).toBe("VLLM_API_KEY");
|
||||
expect(provider.baseUrl).toBe(VLLM_DEFAULT_BASE_URL);
|
||||
expect(provider.api).toBe("openai-completions");
|
||||
expect(provider.models).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
|
||||
let buildOpenAICodexProviderPlugin: typeof import("../../extensions/openai/openai-codex-provider.js").buildOpenAICodexProviderPlugin;
|
||||
|
||||
describe("openai-codex implicit provider", () => {
|
||||
beforeAll(async () => {
|
||||
({ buildOpenAICodexProviderPlugin } =
|
||||
await import("../../extensions/openai/openai-codex-provider.js"));
|
||||
});
|
||||
|
||||
it("normalizes generated openai-codex rows back to the Codex transport", () => {
|
||||
const provider = buildOpenAICodexProviderPlugin();
|
||||
const normalized = provider.normalizeResolvedModel?.({
|
||||
provider: "openai-codex",
|
||||
model: {
|
||||
id: "gpt-5.4",
|
||||
name: "GPT-5.4",
|
||||
provider: "openai-codex",
|
||||
api: "openai-responses",
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 1_000_000,
|
||||
maxTokens: 100_000,
|
||||
},
|
||||
} as never);
|
||||
|
||||
expect(normalized).toMatchObject({
|
||||
baseUrl: "https://chatgpt.com/backend-api",
|
||||
api: "openai-codex-responses",
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves an existing Codex baseUrl for explicit openai-codex config", () => {
|
||||
const provider = buildOpenAICodexProviderPlugin();
|
||||
const normalized = provider.normalizeResolvedModel?.({
|
||||
provider: "openai-codex",
|
||||
model: {
|
||||
id: "gpt-5.4",
|
||||
name: "GPT-5.4",
|
||||
provider: "openai-codex",
|
||||
api: "openai-codex-responses",
|
||||
baseUrl: "https://chatgpt.com/backend-api",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 1_000_000,
|
||||
maxTokens: 100_000,
|
||||
},
|
||||
} as never);
|
||||
|
||||
expect(normalized).toMatchObject({
|
||||
baseUrl: "https://chatgpt.com/backend-api",
|
||||
api: "openai-codex-responses",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -2,37 +2,26 @@ import { mkdtempSync } from "node:fs";
|
|||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import stepfunPlugin from "../../extensions/stepfun/index.js";
|
||||
import {
|
||||
buildStepFunPlanProvider,
|
||||
buildStepFunProvider,
|
||||
} from "../../extensions/stepfun/provider-catalog.js";
|
||||
import {
|
||||
registerProviderPlugin,
|
||||
requireRegisteredProvider,
|
||||
} from "../../test/helpers/plugins/provider-registration.js";
|
||||
import { upsertAuthProfile } from "./auth-profiles.js";
|
||||
import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js";
|
||||
import { resolveMissingProviderApiKey } from "./models-config.providers.secrets.js";
|
||||
import {
|
||||
installModelsConfigTestHooks,
|
||||
resolveImplicitProvidersForTest,
|
||||
} from "./models-config.e2e-harness.js";
|
||||
|
||||
const EXPECTED_STANDARD_MODELS = ["step-3.5-flash"];
|
||||
const EXPECTED_PLAN_MODELS = ["step-3.5-flash", "step-3.5-flash-2603"];
|
||||
|
||||
installModelsConfigTestHooks();
|
||||
|
||||
describe("StepFun provider catalog", () => {
|
||||
it("includes standard and Step Plan providers when STEPFUN_API_KEY is configured", async () => {
|
||||
const env = { STEPFUN_API_KEY: "test-stepfun-key" } as NodeJS.ProcessEnv;
|
||||
const standardProvider = resolveMissingProviderApiKey({
|
||||
providerKey: "stepfun",
|
||||
provider: buildStepFunProvider(),
|
||||
env,
|
||||
profileApiKey: undefined,
|
||||
});
|
||||
const planProvider = resolveMissingProviderApiKey({
|
||||
providerKey: "stepfun-plan",
|
||||
provider: buildStepFunPlanProvider(),
|
||||
env,
|
||||
profileApiKey: undefined,
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: { STEPFUN_API_KEY: "test-stepfun-key" },
|
||||
});
|
||||
const standardProvider = providers?.stepfun;
|
||||
const planProvider = providers?.["stepfun-plan"];
|
||||
|
||||
expect(standardProvider).toMatchObject({
|
||||
baseUrl: "https://api.stepfun.ai/v1",
|
||||
|
|
@ -81,51 +70,24 @@ describe("StepFun provider catalog", () => {
|
|||
});
|
||||
|
||||
it("uses China endpoints when explicit config points the paired surface at the China host", async () => {
|
||||
const { providers } = await registerProviderPlugin({
|
||||
plugin: stepfunPlugin,
|
||||
id: "stepfun",
|
||||
name: "StepFun",
|
||||
});
|
||||
const standardProvider = requireRegisteredProvider(providers, "stepfun");
|
||||
const planProvider = requireRegisteredProvider(providers, "stepfun-plan");
|
||||
const config = {
|
||||
models: {
|
||||
providers: {
|
||||
"stepfun-plan": {
|
||||
baseUrl: "https://api.stepfun.com/step_plan/v1",
|
||||
models: [],
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: { STEPFUN_API_KEY: "test-stepfun-key" },
|
||||
config: {
|
||||
models: {
|
||||
providers: {
|
||||
"stepfun-plan": {
|
||||
baseUrl: "https://api.stepfun.com/step_plan/v1",
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const resolveProviderApiKey = () => ({
|
||||
apiKey: "STEPFUN_API_KEY",
|
||||
discoveryApiKey: "test-stepfun-key",
|
||||
});
|
||||
const resolveProviderAuth = () => ({
|
||||
apiKey: "STEPFUN_API_KEY",
|
||||
discoveryApiKey: "test-stepfun-key",
|
||||
mode: "api_key" as const,
|
||||
source: "env" as const,
|
||||
});
|
||||
|
||||
const standardCatalog = await standardProvider.catalog?.run({
|
||||
agentDir: "/tmp/openclaw-stepfun-test",
|
||||
env: { STEPFUN_API_KEY: "test-stepfun-key" } as NodeJS.ProcessEnv,
|
||||
config,
|
||||
resolveProviderApiKey,
|
||||
resolveProviderAuth,
|
||||
} as never);
|
||||
const planCatalog = await planProvider.catalog?.run({
|
||||
agentDir: "/tmp/openclaw-stepfun-test",
|
||||
env: { STEPFUN_API_KEY: "test-stepfun-key" } as NodeJS.ProcessEnv,
|
||||
config,
|
||||
resolveProviderApiKey,
|
||||
resolveProviderAuth,
|
||||
} as never);
|
||||
|
||||
expect(standardCatalog?.provider.baseUrl).toBe("https://api.stepfun.com/v1");
|
||||
expect(planCatalog?.provider.baseUrl).toBe("https://api.stepfun.com/step_plan/v1");
|
||||
expect(providers?.stepfun?.baseUrl).toBe("https://api.stepfun.com/v1");
|
||||
expect(providers?.["stepfun-plan"]?.baseUrl).toBe("https://api.stepfun.com/step_plan/v1");
|
||||
});
|
||||
|
||||
it("discovers both providers from shared regional auth profiles", async () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue