test(extensions): move provider contracts to owners

This commit is contained in:
Peter Steinberger
2026-04-20 20:19:13 +01:00
parent f587887122
commit 0f1ce47033
21 changed files with 254 additions and 348 deletions

View File

@@ -0,0 +1,3 @@
import { describeAnthropicProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
describeAnthropicProviderRuntimeContract(() => import("./index.js"));

View File

@@ -0,0 +1,3 @@
import { describeCloudflareAiGatewayProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
describeCloudflareAiGatewayProviderDiscoveryContract(() => import("./index.js"));

View File

@@ -0,0 +1,3 @@
import { describeGithubCopilotProviderAuthContract } from "../../test/helpers/plugins/provider-auth-contract.js";
describeGithubCopilotProviderAuthContract(() => import("./index.js"));

View File

@@ -0,0 +1,7 @@
import { fileURLToPath } from "node:url";
import { describeGithubCopilotProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
describeGithubCopilotProviderDiscoveryContract({
load: () => import("./index.js"),
registerRuntimeModuleId: fileURLToPath(new URL("./register.runtime.js", import.meta.url)),
});

View File

@@ -0,0 +1,3 @@
import { describeGithubCopilotProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
describeGithubCopilotProviderRuntimeContract(() => import("./index.js"));

View File

@@ -0,0 +1,3 @@
import { describeGoogleProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
describeGoogleProviderRuntimeContract(() => import("./index.js"));

View File

@@ -0,0 +1,3 @@
import { describeMinimaxProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
describeMinimaxProviderDiscoveryContract(() => import("./index.js"));

View File

@@ -0,0 +1,3 @@
import { describeOpenAICodexProviderAuthContract } from "../../test/helpers/plugins/provider-auth-contract.js";
describeOpenAICodexProviderAuthContract(() => import("./index.js"));

View File

@@ -0,0 +1,3 @@
import { describeOpenAIProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
describeOpenAIProviderRuntimeContract(() => import("./index.js"));

View File

@@ -0,0 +1,3 @@
import { describeOpenRouterProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
describeOpenRouterProviderRuntimeContract(() => import("./index.js"));

View File

@@ -0,0 +1,3 @@
import { describeModelStudioProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
describeModelStudioProviderDiscoveryContract(() => import("./index.js"));

View File

@@ -0,0 +1,7 @@
import { fileURLToPath } from "node:url";
import { describeSglangProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
describeSglangProviderDiscoveryContract({
load: () => import("./index.js"),
apiModuleId: fileURLToPath(new URL("./api.js", import.meta.url)),
});

View File

@@ -0,0 +1,3 @@
import { describeVeniceProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
describeVeniceProviderRuntimeContract(() => import("./index.js"));

View File

@@ -0,0 +1,7 @@
import { fileURLToPath } from "node:url";
import { describeVllmProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
describeVllmProviderDiscoveryContract({
load: () => import("./index.js"),
apiModuleId: fileURLToPath(new URL("./api.js", import.meta.url)),
});

View File

@@ -0,0 +1,3 @@
import { describeZAIProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
describeZAIProviderRuntimeContract(() => import("./index.js"));

View File

@@ -1,7 +0,0 @@
import {
describeGithubCopilotProviderAuthContract,
describeOpenAICodexProviderAuthContract,
} from "../../../test/helpers/plugins/provider-auth-contract.js";
describeOpenAICodexProviderAuthContract();
describeGithubCopilotProviderAuthContract();

View File

@@ -1,15 +0,0 @@
import {
describeCloudflareAiGatewayProviderDiscoveryContract,
describeGithubCopilotProviderDiscoveryContract,
describeMinimaxProviderDiscoveryContract,
describeModelStudioProviderDiscoveryContract,
describeSglangProviderDiscoveryContract,
describeVllmProviderDiscoveryContract,
} from "../../../test/helpers/plugins/provider-discovery-contract.js";
describeCloudflareAiGatewayProviderDiscoveryContract();
describeGithubCopilotProviderDiscoveryContract();
describeMinimaxProviderDiscoveryContract();
describeModelStudioProviderDiscoveryContract();
describeSglangProviderDiscoveryContract();
describeVllmProviderDiscoveryContract();

View File

@@ -1,17 +0,0 @@
import {
describeAnthropicProviderRuntimeContract,
describeGithubCopilotProviderRuntimeContract,
describeGoogleProviderRuntimeContract,
describeOpenAIProviderRuntimeContract,
describeOpenRouterProviderRuntimeContract,
describeVeniceProviderRuntimeContract,
describeZAIProviderRuntimeContract,
} from "../../../test/helpers/plugins/provider-runtime-contract.js";
describeAnthropicProviderRuntimeContract();
describeGithubCopilotProviderRuntimeContract();
describeGoogleProviderRuntimeContract();
describeOpenAIProviderRuntimeContract();
describeOpenRouterProviderRuntimeContract();
describeVeniceProviderRuntimeContract();
describeZAIProviderRuntimeContract();

View File

@@ -2,7 +2,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { clearRuntimeAuthProfileStoreSnapshots } from "../../../src/agents/auth-profiles/store.js";
import type { AuthProfileStore } from "../../../src/agents/auth-profiles/types.js";
import { createNonExitingRuntime } from "../../../src/runtime.js";
import { resolveRelativeBundledPluginPublicModuleId } from "../../../src/test-utils/bundled-plugin-public-surface.js";
import type {
WizardMultiSelectParams,
WizardPrompter,
@@ -26,18 +25,10 @@ const loginOpenAICodexOAuthMock = vi.hoisted(() => vi.fn<LoginOpenAICodexOAuth>(
const githubCopilotLoginCommandMock = vi.hoisted(() => vi.fn<GithubCopilotLoginCommand>());
const ensureAuthProfileStoreMock = vi.hoisted(() => vi.fn<EnsureAuthProfileStore>());
const listProfilesForProviderMock = vi.hoisted(() => vi.fn<ListProfilesForProvider>());
const providerAuthContractModules = {
githubCopilotIndexModuleUrl: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "github-copilot",
artifactBasename: "index.js",
}),
openAIIndexModuleUrl: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "openai",
artifactBasename: "index.js",
}),
};
export type ProviderAuthContractPluginLoader = () => Promise<{
default: Parameters<typeof registerProviders>[0];
}>;
vi.mock("openclaw/plugin-sdk/provider-auth-login", async () => {
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/provider-auth-login")>(
@@ -61,10 +52,6 @@ vi.mock("openclaw/plugin-sdk/provider-auth", async () => {
};
});
async function importBundledProviderPlugin<T>(moduleUrl: string): Promise<T> {
return (await import(`${moduleUrl}?t=${Date.now()}`)) as T;
}
function buildPrompter(): WizardPrompter {
const progress: WizardProgress = {
update() {},
@@ -164,7 +151,7 @@ function installSharedAuthProfileStoreHooks(state: { authStore: AuthProfileStore
});
}
export function describeOpenAICodexProviderAuthContract() {
export function describeOpenAICodexProviderAuthContract(load: ProviderAuthContractPluginLoader) {
const state = {
authStore: { version: 1, profiles: {} } as AuthProfileStore,
};
@@ -173,9 +160,7 @@ export function describeOpenAICodexProviderAuthContract() {
installSharedAuthProfileStoreHooks(state);
async function expectStableFallbackProfile(params: { access: string; profileId: string }) {
const { default: openAIPlugin } = await importBundledProviderPlugin<{
default: Parameters<typeof registerProviders>[0];
}>(providerAuthContractModules.openAIIndexModuleUrl);
const { default: openAIPlugin } = await load();
const provider = requireProvider(await registerProviders(openAIPlugin), "openai-codex");
loginOpenAICodexOAuthMock.mockResolvedValueOnce({
refresh: "refresh-token",
@@ -194,9 +179,7 @@ export function describeOpenAICodexProviderAuthContract() {
}
async function getProvider() {
const { default: openAIPlugin } = await importBundledProviderPlugin<{
default: Parameters<typeof registerProviders>[0];
}>(providerAuthContractModules.openAIIndexModuleUrl);
const { default: openAIPlugin } = await load();
return requireProvider(await registerProviders(openAIPlugin), "openai-codex");
}
@@ -317,7 +300,7 @@ export function describeOpenAICodexProviderAuthContract() {
});
}
export function describeGithubCopilotProviderAuthContract() {
export function describeGithubCopilotProviderAuthContract(load: ProviderAuthContractPluginLoader) {
const state = {
authStore: { version: 1, profiles: {} } as AuthProfileStore,
};
@@ -326,9 +309,7 @@ export function describeGithubCopilotProviderAuthContract() {
installSharedAuthProfileStoreHooks(state);
async function getProvider() {
const { default: githubCopilotPlugin } = await importBundledProviderPlugin<{
default: Parameters<typeof registerProviders>[0];
}>(providerAuthContractModules.githubCopilotIndexModuleUrl);
const { default: githubCopilotPlugin } = await load();
return requireProvider(await registerProviders(githubCopilotPlugin), "github-copilot");
}

View File

@@ -1,10 +1,6 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { AuthProfileStore } from "../../../src/agents/auth-profiles/types.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import {
resolveBundledPluginPublicModulePath,
resolveRelativeBundledPluginPublicModuleId,
} from "../../../src/test-utils/bundled-plugin-public-surface.js";
import { registerProviders, requireProvider } from "./contracts-testkit.js";
const resolveCopilotApiTokenMock = vi.hoisted(() => vi.fn());
@@ -12,50 +8,10 @@ const buildVllmProviderMock = vi.hoisted(() => vi.fn());
const buildSglangProviderMock = vi.hoisted(() => vi.fn());
const ensureAuthProfileStoreMock = vi.hoisted(() => vi.fn());
const listProfilesForProviderMock = vi.hoisted(() => vi.fn());
const bundledProviderModules = {
cloudflareAiGatewayIndexModuleUrl: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "cloudflare-ai-gateway",
artifactBasename: "index.js",
}),
githubCopilotIndexModuleUrl: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "github-copilot",
artifactBasename: "index.js",
}),
githubCopilotRegisterRuntimeModuleId: resolveBundledPluginPublicModulePath({
pluginId: "github-copilot",
artifactBasename: "register.runtime.js",
}),
minimaxIndexModuleUrl: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "minimax",
artifactBasename: "index.js",
}),
qwenIndexModuleUrl: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "qwen",
artifactBasename: "index.js",
}),
sglangApiModuleId: resolveBundledPluginPublicModulePath({
pluginId: "sglang",
artifactBasename: "api.js",
}),
sglangIndexModuleUrl: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "sglang",
artifactBasename: "index.js",
}),
vllmApiModuleId: resolveBundledPluginPublicModulePath({
pluginId: "vllm",
artifactBasename: "api.js",
}),
vllmIndexModuleUrl: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "vllm",
artifactBasename: "index.js",
}),
};
export type ProviderDiscoveryContractPluginLoader = () => Promise<{
default: Parameters<typeof registerProviders>[0];
}>;
type ProviderHandle = Awaited<ReturnType<typeof requireProvider>>;
@@ -78,6 +34,19 @@ type BundledProviderUnderTest =
| "modelstudio"
| "cloudflare-ai-gateway";
type DiscoveryContractOptions = {
providerIds: readonly BundledProviderUnderTest[];
loadGithubCopilot?: ProviderDiscoveryContractPluginLoader;
loadVllm?: ProviderDiscoveryContractPluginLoader;
loadSglang?: ProviderDiscoveryContractPluginLoader;
loadMinimax?: ProviderDiscoveryContractPluginLoader;
loadModelStudio?: ProviderDiscoveryContractPluginLoader;
loadCloudflareAiGateway?: ProviderDiscoveryContractPluginLoader;
githubCopilotRegisterRuntimeModuleId?: string;
vllmApiModuleId?: string;
sglangApiModuleId?: string;
};
function setRuntimeAuthStore(store?: AuthProfileStore) {
const resolvedStore = store ?? {
version: 1,
@@ -140,14 +109,7 @@ function runCatalog(
});
}
async function importBundledProviderPlugin<T>(moduleUrl: string): Promise<T> {
return (await import(moduleUrl)) as T;
}
function installDiscoveryHooks(
state: DiscoveryState,
providerIds: readonly BundledProviderUnderTest[],
) {
function installDiscoveryHooks(state: DiscoveryState, options: DiscoveryContractOptions) {
beforeAll(async () => {
vi.resetModules();
vi.doMock("openclaw/plugin-sdk/agent-runtime", () => {
@@ -187,80 +149,72 @@ function installDiscoveryHooks(
validateApiKeyInput: () => undefined,
};
});
vi.doMock(bundledProviderModules.githubCopilotRegisterRuntimeModuleId, async () => {
const actual = await vi.importActual<object>(
bundledProviderModules.githubCopilotRegisterRuntimeModuleId,
);
return {
...actual,
resolveCopilotApiToken: resolveCopilotApiTokenMock,
};
});
vi.doMock(bundledProviderModules.vllmApiModuleId, async () => {
return {
VLLM_DEFAULT_API_KEY_ENV_VAR: "VLLM_API_KEY",
VLLM_DEFAULT_BASE_URL: "http://127.0.0.1:8000/v1",
VLLM_MODEL_PLACEHOLDER: "meta-llama/Meta-Llama-3-8B-Instruct",
VLLM_PROVIDER_LABEL: "vLLM",
buildVllmProvider: (...args: unknown[]) => buildVllmProviderMock(...args),
};
});
vi.doMock(bundledProviderModules.sglangApiModuleId, async () => {
return {
SGLANG_DEFAULT_API_KEY_ENV_VAR: "SGLANG_API_KEY",
SGLANG_DEFAULT_BASE_URL: "http://127.0.0.1:30000/v1",
SGLANG_MODEL_PLACEHOLDER: "Qwen/Qwen3-8B",
SGLANG_PROVIDER_LABEL: "SGLang",
buildSglangProvider: (...args: unknown[]) => buildSglangProviderMock(...args),
};
});
if (options.githubCopilotRegisterRuntimeModuleId) {
vi.doMock(options.githubCopilotRegisterRuntimeModuleId, async () => {
const actual = await vi.importActual<object>(options.githubCopilotRegisterRuntimeModuleId!);
return {
...actual,
resolveCopilotApiToken: resolveCopilotApiTokenMock,
};
});
}
if (options.vllmApiModuleId) {
vi.doMock(options.vllmApiModuleId, async () => {
return {
VLLM_DEFAULT_API_KEY_ENV_VAR: "VLLM_API_KEY",
VLLM_DEFAULT_BASE_URL: "http://127.0.0.1:8000/v1",
VLLM_MODEL_PLACEHOLDER: "meta-llama/Meta-Llama-3-8B-Instruct",
VLLM_PROVIDER_LABEL: "vLLM",
buildVllmProvider: (...args: unknown[]) => buildVllmProviderMock(...args),
};
});
}
if (options.sglangApiModuleId) {
vi.doMock(options.sglangApiModuleId, async () => {
return {
SGLANG_DEFAULT_API_KEY_ENV_VAR: "SGLANG_API_KEY",
SGLANG_DEFAULT_BASE_URL: "http://127.0.0.1:30000/v1",
SGLANG_MODEL_PLACEHOLDER: "Qwen/Qwen3-8B",
SGLANG_PROVIDER_LABEL: "SGLang",
buildSglangProvider: (...args: unknown[]) => buildSglangProviderMock(...args),
};
});
}
({ runProviderCatalog: state.runProviderCatalog } =
await import("../../../src/plugins/provider-discovery.js"));
if (providerIds.includes("github-copilot")) {
const { default: githubCopilotPlugin } = await importBundledProviderPlugin<{
default: Parameters<typeof registerProviders>[0];
}>(bundledProviderModules.githubCopilotIndexModuleUrl);
if (options.providerIds.includes("github-copilot")) {
const { default: githubCopilotPlugin } = await options.loadGithubCopilot!();
state.githubCopilotProvider = requireProvider(
await registerProviders(githubCopilotPlugin),
"github-copilot",
);
}
if (providerIds.includes("vllm")) {
const { default: vllmPlugin } = await importBundledProviderPlugin<{
default: Parameters<typeof registerProviders>[0];
}>(bundledProviderModules.vllmIndexModuleUrl);
if (options.providerIds.includes("vllm")) {
const { default: vllmPlugin } = await options.loadVllm!();
state.vllmProvider = requireProvider(await registerProviders(vllmPlugin), "vllm");
}
if (providerIds.includes("sglang")) {
const { default: sglangPlugin } = await importBundledProviderPlugin<{
default: Parameters<typeof registerProviders>[0];
}>(bundledProviderModules.sglangIndexModuleUrl);
if (options.providerIds.includes("sglang")) {
const { default: sglangPlugin } = await options.loadSglang!();
state.sglangProvider = requireProvider(await registerProviders(sglangPlugin), "sglang");
}
if (providerIds.includes("minimax")) {
const { default: minimaxPlugin } = await importBundledProviderPlugin<{
default: Parameters<typeof registerProviders>[0];
}>(bundledProviderModules.minimaxIndexModuleUrl);
if (options.providerIds.includes("minimax")) {
const { default: minimaxPlugin } = await options.loadMinimax!();
const registeredProviders = await registerProviders(minimaxPlugin);
state.minimaxProvider = requireProvider(registeredProviders, "minimax");
state.minimaxPortalProvider = requireProvider(registeredProviders, "minimax-portal");
}
if (providerIds.includes("modelstudio")) {
const { default: qwenPlugin } = await importBundledProviderPlugin<{
default: Parameters<typeof registerProviders>[0];
}>(bundledProviderModules.qwenIndexModuleUrl);
if (options.providerIds.includes("modelstudio")) {
const { default: qwenPlugin } = await options.loadModelStudio!();
state.modelStudioProvider = requireProvider(await registerProviders(qwenPlugin), "qwen");
}
if (providerIds.includes("cloudflare-ai-gateway")) {
const { default: cloudflareAiGatewayPlugin } = await importBundledProviderPlugin<{
default: Parameters<typeof registerProviders>[0];
}>(bundledProviderModules.cloudflareAiGatewayIndexModuleUrl);
if (options.providerIds.includes("cloudflare-ai-gateway")) {
const { default: cloudflareAiGatewayPlugin } = await options.loadCloudflareAiGateway!();
state.cloudflareAiGatewayProvider = requireProvider(
await registerProviders(cloudflareAiGatewayPlugin),
"cloudflare-ai-gateway",
@@ -283,11 +237,18 @@ function installDiscoveryHooks(
});
}
export function describeGithubCopilotProviderDiscoveryContract() {
export function describeGithubCopilotProviderDiscoveryContract(params: {
load: ProviderDiscoveryContractPluginLoader;
registerRuntimeModuleId: string;
}) {
const state = {} as DiscoveryState;
describe("github-copilot provider discovery contract", () => {
installDiscoveryHooks(state, ["github-copilot"]);
installDiscoveryHooks(state, {
providerIds: ["github-copilot"],
loadGithubCopilot: params.load,
githubCopilotRegisterRuntimeModuleId: params.registerRuntimeModuleId,
});
it("keeps catalog disabled without env tokens or profiles", async () => {
await expect(
@@ -341,11 +302,18 @@ export function describeGithubCopilotProviderDiscoveryContract() {
});
}
export function describeVllmProviderDiscoveryContract() {
export function describeVllmProviderDiscoveryContract(params: {
load: ProviderDiscoveryContractPluginLoader;
apiModuleId: string;
}) {
const state = {} as DiscoveryState;
describe("vllm provider discovery contract", () => {
installDiscoveryHooks(state, ["vllm"]);
installDiscoveryHooks(state, {
providerIds: ["vllm"],
loadVllm: params.load,
vllmApiModuleId: params.apiModuleId,
});
it("keeps self-hosted discovery provider-owned", async () => {
buildVllmProviderMock.mockResolvedValueOnce({
@@ -387,11 +355,18 @@ export function describeVllmProviderDiscoveryContract() {
});
}
export function describeSglangProviderDiscoveryContract() {
export function describeSglangProviderDiscoveryContract(params: {
load: ProviderDiscoveryContractPluginLoader;
apiModuleId: string;
}) {
const state = {} as DiscoveryState;
describe("sglang provider discovery contract", () => {
installDiscoveryHooks(state, ["sglang"]);
installDiscoveryHooks(state, {
providerIds: ["sglang"],
loadSglang: params.load,
sglangApiModuleId: params.apiModuleId,
});
it("keeps self-hosted discovery provider-owned", async () => {
buildSglangProviderMock.mockResolvedValueOnce({
@@ -433,11 +408,13 @@ export function describeSglangProviderDiscoveryContract() {
});
}
export function describeMinimaxProviderDiscoveryContract() {
export function describeMinimaxProviderDiscoveryContract(
load: ProviderDiscoveryContractPluginLoader,
) {
const state = {} as DiscoveryState;
describe("minimax provider discovery contract", () => {
installDiscoveryHooks(state, ["minimax"]);
installDiscoveryHooks(state, { providerIds: ["minimax"], loadMinimax: load });
it("keeps API catalog provider-owned", async () => {
await expect(
@@ -542,11 +519,13 @@ export function describeMinimaxProviderDiscoveryContract() {
});
}
export function describeModelStudioProviderDiscoveryContract() {
export function describeModelStudioProviderDiscoveryContract(
load: ProviderDiscoveryContractPluginLoader,
) {
const state = {} as DiscoveryState;
describe("modelstudio provider discovery contract", () => {
installDiscoveryHooks(state, ["modelstudio"]);
installDiscoveryHooks(state, { providerIds: ["modelstudio"], loadModelStudio: load });
it("keeps catalog provider-owned", async () => {
await expect(
@@ -589,11 +568,16 @@ export function describeModelStudioProviderDiscoveryContract() {
});
}
export function describeCloudflareAiGatewayProviderDiscoveryContract() {
export function describeCloudflareAiGatewayProviderDiscoveryContract(
load: ProviderDiscoveryContractPluginLoader,
) {
const state = {} as DiscoveryState;
describe("cloudflare-ai-gateway provider discovery contract", () => {
installDiscoveryHooks(state, ["cloudflare-ai-gateway"]);
installDiscoveryHooks(state, {
providerIds: ["cloudflare-ai-gateway"],
loadCloudflareAiGateway: load,
});
it("keeps catalog disabled without stored metadata", async () => {
await expect(

View File

@@ -3,7 +3,6 @@ import os from "node:os";
import path from "node:path";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { ProviderPlugin, ProviderRuntimeModel } from "../../../src/plugins/types.js";
import { resolveRelativeBundledPluginPublicModuleId } from "../../../src/test-utils/bundled-plugin-public-surface.js";
import {
createProviderUsageFetch,
makeResponse,
@@ -20,43 +19,6 @@ const getOAuthProvidersMock = vi.hoisted(() =>
{ id: "openai-codex", envApiKey: "OPENAI_API_KEY", oauthTokenEnv: "OPENAI_OAUTH_TOKEN" },
]),
);
const providerRuntimeContractModules = {
anthropicIndexModuleId: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "anthropic",
artifactBasename: "index.js",
}),
githubCopilotIndexModuleId: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "github-copilot",
artifactBasename: "index.js",
}),
googleIndexModuleId: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "google",
artifactBasename: "index.js",
}),
openAIIndexModuleId: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "openai",
artifactBasename: "index.js",
}),
openRouterIndexModuleId: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "openrouter",
artifactBasename: "index.js",
}),
veniceIndexModuleId: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "venice",
artifactBasename: "index.js",
}),
zaiIndexModuleId: resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "zai",
artifactBasename: "index.js",
}),
};
vi.mock("@mariozechner/pi-ai/oauth", async () => {
const actual = await vi.importActual<typeof import("@mariozechner/pi-ai/oauth")>(
@@ -69,10 +31,6 @@ vi.mock("@mariozechner/pi-ai/oauth", async () => {
};
});
async function importBundledProviderPlugin<T>(moduleUrl: string): Promise<T> {
return (await import(moduleUrl)) as T;
}
function createModel(overrides: Partial<ProviderRuntimeModel> & Pick<ProviderRuntimeModel, "id">) {
return {
id: overrides.id,
@@ -92,133 +50,77 @@ type ProviderRuntimeContractFixture = {
providerIds: string[];
pluginId: string;
name: string;
load: () => Promise<{ default: Parameters<typeof registerProviderPlugin>[0]["plugin"] }>;
load: ProviderRuntimeContractPluginLoader;
};
const PROVIDER_RUNTIME_CONTRACT_FIXTURES: readonly ProviderRuntimeContractFixture[] = [
{
providerIds: ["anthropic"],
pluginId: "anthropic",
name: "Anthropic",
load: async () =>
await importBundledProviderPlugin<{
default: Parameters<typeof registerProviderPlugin>[0]["plugin"];
}>(providerRuntimeContractModules.anthropicIndexModuleId),
},
{
providerIds: ["github-copilot"],
pluginId: "github-copilot",
name: "GitHub Copilot",
load: async () =>
await importBundledProviderPlugin<{
default: Parameters<typeof registerProviderPlugin>[0]["plugin"];
}>(providerRuntimeContractModules.githubCopilotIndexModuleId),
},
{
providerIds: ["google", "google-gemini-cli"],
pluginId: "google",
name: "Google",
load: async () =>
await importBundledProviderPlugin<{
default: Parameters<typeof registerProviderPlugin>[0]["plugin"];
}>(providerRuntimeContractModules.googleIndexModuleId),
},
{
providerIds: ["openai", "openai-codex"],
pluginId: "openai",
name: "OpenAI",
load: async () =>
await importBundledProviderPlugin<{
default: Parameters<typeof registerProviderPlugin>[0]["plugin"];
}>(providerRuntimeContractModules.openAIIndexModuleId),
},
{
providerIds: ["openrouter"],
pluginId: "openrouter",
name: "OpenRouter",
load: async () =>
await importBundledProviderPlugin<{
default: Parameters<typeof registerProviderPlugin>[0]["plugin"];
}>(providerRuntimeContractModules.openRouterIndexModuleId),
},
{
providerIds: ["venice"],
pluginId: "venice",
name: "Venice",
load: async () =>
await importBundledProviderPlugin<{
default: Parameters<typeof registerProviderPlugin>[0]["plugin"];
}>(providerRuntimeContractModules.veniceIndexModuleId),
},
{
providerIds: ["zai"],
pluginId: "zai",
name: "Z.AI",
load: async () =>
await importBundledProviderPlugin<{
default: Parameters<typeof registerProviderPlugin>[0]["plugin"];
}>(providerRuntimeContractModules.zaiIndexModuleId),
},
] as const;
export type ProviderRuntimeContractPluginLoader = () => Promise<{
default: Parameters<typeof registerProviderPlugin>[0]["plugin"];
}>;
const providerRuntimeContractProviders = new Map<string, ProviderPlugin>();
let providerRuntimeContractLoadPromise: Promise<void> | null = null;
function installRuntimeHooks(fixtures: readonly ProviderRuntimeContractFixture[]) {
const providers = new Map<string, ProviderPlugin>();
let loadPromise: Promise<void> | null = null;
function requireProviderContractProvider(providerId: string): ProviderPlugin {
const provider = providerRuntimeContractProviders.get(providerId);
if (!provider) {
throw new Error(`provider runtime contract fixture missing for ${providerId}`);
function requireProviderContractProvider(providerId: string): ProviderPlugin {
const provider = providers.get(providerId);
if (!provider) {
throw new Error(`provider runtime contract fixture missing for ${providerId}`);
}
return provider;
}
return provider;
}
async function ensureProviderRuntimeContractProvidersLoaded() {
if (!providerRuntimeContractLoadPromise) {
providerRuntimeContractLoadPromise = (async () => {
providerRuntimeContractProviders.clear();
const registeredFixtures = await Promise.all(
PROVIDER_RUNTIME_CONTRACT_FIXTURES.map(async (fixture) => {
const plugin = await fixture.load();
return {
fixture,
providers: (
await registerProviderPlugin({
plugin: plugin.default,
id: fixture.pluginId,
name: fixture.name,
})
).providers,
};
}),
);
for (const { fixture, providers } of registeredFixtures) {
for (const providerId of fixture.providerIds) {
providerRuntimeContractProviders.set(
providerId,
requireRegisteredProvider(providers, providerId, "provider"),
);
async function ensureProvidersLoaded() {
if (!loadPromise) {
loadPromise = (async () => {
providers.clear();
const registeredFixtures = await Promise.all(
fixtures.map(async (fixture) => {
const plugin = await fixture.load();
return {
fixture,
providers: (
await registerProviderPlugin({
plugin: plugin.default,
id: fixture.pluginId,
name: fixture.name,
})
).providers,
};
}),
);
for (const { fixture, providers: registeredProviders } of registeredFixtures) {
for (const providerId of fixture.providerIds) {
providers.set(
providerId,
requireRegisteredProvider(registeredProviders, providerId, "provider"),
);
}
}
}
})();
})();
}
await loadPromise;
}
await providerRuntimeContractLoadPromise;
}
function installRuntimeHooks() {
beforeAll(async () => {
await ensureProviderRuntimeContractProvidersLoaded();
await ensureProvidersLoaded();
}, CONTRACT_SETUP_TIMEOUT_MS);
beforeEach(() => {
refreshOpenAICodexTokenMock.mockReset();
getOAuthProvidersMock.mockClear();
}, CONTRACT_SETUP_TIMEOUT_MS);
return requireProviderContractProvider;
}
export function describeAnthropicProviderRuntimeContract() {
export function describeAnthropicProviderRuntimeContract(
load: ProviderRuntimeContractPluginLoader,
) {
describe("anthropic provider runtime contract", { timeout: CONTRACT_SETUP_TIMEOUT_MS }, () => {
installRuntimeHooks();
const requireProviderContractProvider = installRuntimeHooks([
{ providerIds: ["anthropic"], pluginId: "anthropic", name: "Anthropic", load },
]);
it("owns anthropic 4.6 forward-compat resolution", () => {
const provider = requireProviderContractProvider("anthropic");
@@ -329,12 +231,21 @@ export function describeAnthropicProviderRuntimeContract() {
});
}
export function describeGithubCopilotProviderRuntimeContract() {
export function describeGithubCopilotProviderRuntimeContract(
load: ProviderRuntimeContractPluginLoader,
) {
describe(
"github-copilot provider runtime contract",
{ timeout: CONTRACT_SETUP_TIMEOUT_MS },
() => {
installRuntimeHooks();
const requireProviderContractProvider = installRuntimeHooks([
{
providerIds: ["github-copilot"],
pluginId: "github-copilot",
name: "GitHub Copilot",
load,
},
]);
it("owns Copilot-specific forward-compat fallbacks", () => {
const provider = requireProviderContractProvider("github-copilot");
@@ -364,9 +275,11 @@ export function describeGithubCopilotProviderRuntimeContract() {
);
}
export function describeGoogleProviderRuntimeContract() {
export function describeGoogleProviderRuntimeContract(load: ProviderRuntimeContractPluginLoader) {
describe("google provider runtime contract", { timeout: CONTRACT_SETUP_TIMEOUT_MS }, () => {
installRuntimeHooks();
const requireProviderContractProvider = installRuntimeHooks([
{ providerIds: ["google", "google-gemini-cli"], pluginId: "google", name: "Google", load },
]);
it("owns google direct gemini 3.1 forward-compat resolution", () => {
const provider = requireProviderContractProvider("google");
@@ -494,9 +407,11 @@ export function describeGoogleProviderRuntimeContract() {
});
}
export function describeOpenAIProviderRuntimeContract() {
export function describeOpenAIProviderRuntimeContract(load: ProviderRuntimeContractPluginLoader) {
describe("openai provider runtime contract", { timeout: CONTRACT_SETUP_TIMEOUT_MS }, () => {
installRuntimeHooks();
const requireProviderContractProvider = installRuntimeHooks([
{ providerIds: ["openai", "openai-codex"], pluginId: "openai", name: "OpenAI", load },
]);
it("owns openai gpt-5.4 forward-compat resolution", () => {
const provider = requireProviderContractProvider("openai");
@@ -702,9 +617,13 @@ export function describeOpenAIProviderRuntimeContract() {
});
}
export function describeOpenRouterProviderRuntimeContract() {
export function describeOpenRouterProviderRuntimeContract(
load: ProviderRuntimeContractPluginLoader,
) {
describe("openrouter provider runtime contract", { timeout: CONTRACT_SETUP_TIMEOUT_MS }, () => {
installRuntimeHooks();
const requireProviderContractProvider = installRuntimeHooks([
{ providerIds: ["openrouter"], pluginId: "openrouter", name: "OpenRouter", load },
]);
it("owns dynamic OpenRouter model defaults", () => {
const provider = requireProviderContractProvider("openrouter");
@@ -727,9 +646,11 @@ export function describeOpenRouterProviderRuntimeContract() {
});
}
export function describeVeniceProviderRuntimeContract() {
export function describeVeniceProviderRuntimeContract(load: ProviderRuntimeContractPluginLoader) {
describe("venice provider runtime contract", { timeout: CONTRACT_SETUP_TIMEOUT_MS }, () => {
installRuntimeHooks();
const requireProviderContractProvider = installRuntimeHooks([
{ providerIds: ["venice"], pluginId: "venice", name: "Venice", load },
]);
it("owns xai downstream compat flags for grok-backed Venice models", () => {
const provider = requireProviderContractProvider("venice");
@@ -755,9 +676,11 @@ export function describeVeniceProviderRuntimeContract() {
});
}
export function describeZAIProviderRuntimeContract() {
export function describeZAIProviderRuntimeContract(load: ProviderRuntimeContractPluginLoader) {
describe("zai provider runtime contract", { timeout: CONTRACT_SETUP_TIMEOUT_MS }, () => {
installRuntimeHooks();
const requireProviderContractProvider = installRuntimeHooks([
{ providerIds: ["zai"], pluginId: "zai", name: "Z.AI", load },
]);
it("owns glm-5 forward-compat resolution", () => {
const provider = requireProviderContractProvider("zai");