Files
openclaw/src/plugins/provider-runtime.test.ts
2026-03-15 16:09:40 -07:00

193 lines
4.8 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ProviderPlugin, ProviderRuntimeModel } from "./types.js";
const resolvePluginProvidersMock = vi.fn((_: unknown) => [] as ProviderPlugin[]);
vi.mock("./providers.js", () => ({
resolvePluginProviders: (params: unknown) => resolvePluginProvidersMock(params as never),
}));
import {
prepareProviderExtraParams,
resolveProviderCacheTtlEligibility,
resolveProviderCapabilitiesWithPlugin,
normalizeProviderResolvedModelWithPlugin,
prepareProviderDynamicModel,
prepareProviderRuntimeAuth,
resolveProviderRuntimePlugin,
runProviderDynamicModel,
wrapProviderStreamFn,
} from "./provider-runtime.js";
const MODEL: ProviderRuntimeModel = {
id: "demo-model",
name: "Demo Model",
api: "openai-responses",
provider: "demo",
baseUrl: "https://api.example.com/v1",
reasoning: true,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128_000,
maxTokens: 8_192,
};
describe("provider-runtime", () => {
beforeEach(() => {
resolvePluginProvidersMock.mockReset();
resolvePluginProvidersMock.mockReturnValue([]);
});
it("matches providers by alias for runtime hook lookup", () => {
resolvePluginProvidersMock.mockReturnValue([
{
id: "openrouter",
label: "OpenRouter",
aliases: ["Open Router"],
auth: [],
},
]);
const plugin = resolveProviderRuntimePlugin({ provider: "Open Router" });
expect(plugin?.id).toBe("openrouter");
expect(resolvePluginProvidersMock).toHaveBeenCalledWith(
expect.objectContaining({
provider: "Open Router",
bundledProviderAllowlistCompat: true,
}),
);
});
it("dispatches runtime hooks for the matched provider", async () => {
const prepareDynamicModel = vi.fn(async () => undefined);
const prepareRuntimeAuth = vi.fn(async () => ({
apiKey: "runtime-token",
baseUrl: "https://runtime.example.com/v1",
expiresAt: 123,
}));
resolvePluginProvidersMock.mockReturnValue([
{
id: "demo",
label: "Demo",
auth: [],
resolveDynamicModel: () => MODEL,
prepareDynamicModel,
capabilities: {
providerFamily: "openai",
},
prepareExtraParams: ({ extraParams }) => ({
...extraParams,
transport: "auto",
}),
wrapStreamFn: ({ streamFn }) => streamFn,
normalizeResolvedModel: ({ model }) => ({
...model,
api: "openai-codex-responses",
}),
prepareRuntimeAuth,
isCacheTtlEligible: ({ modelId }) => modelId.startsWith("anthropic/"),
},
]);
expect(
runProviderDynamicModel({
provider: "demo",
context: {
provider: "demo",
modelId: MODEL.id,
modelRegistry: { find: () => null } as never,
},
}),
).toMatchObject(MODEL);
await prepareProviderDynamicModel({
provider: "demo",
context: {
provider: "demo",
modelId: MODEL.id,
modelRegistry: { find: () => null } as never,
},
});
expect(
resolveProviderCapabilitiesWithPlugin({
provider: "demo",
}),
).toMatchObject({
providerFamily: "openai",
});
expect(
prepareProviderExtraParams({
provider: "demo",
context: {
provider: "demo",
modelId: MODEL.id,
extraParams: { temperature: 0.3 },
},
}),
).toMatchObject({
temperature: 0.3,
transport: "auto",
});
expect(
wrapProviderStreamFn({
provider: "demo",
context: {
provider: "demo",
modelId: MODEL.id,
streamFn: vi.fn(),
},
}),
).toBeTypeOf("function");
expect(
normalizeProviderResolvedModelWithPlugin({
provider: "demo",
context: {
provider: "demo",
modelId: MODEL.id,
model: MODEL,
},
}),
).toMatchObject({
...MODEL,
api: "openai-codex-responses",
});
await expect(
prepareProviderRuntimeAuth({
provider: "demo",
env: process.env,
context: {
env: process.env,
provider: "demo",
modelId: MODEL.id,
model: MODEL,
apiKey: "source-token",
authMode: "api-key",
},
}),
).resolves.toMatchObject({
apiKey: "runtime-token",
baseUrl: "https://runtime.example.com/v1",
expiresAt: 123,
});
expect(
resolveProviderCacheTtlEligibility({
provider: "demo",
context: {
provider: "demo",
modelId: "anthropic/claude-sonnet-4-5",
},
}),
).toBe(true);
expect(prepareDynamicModel).toHaveBeenCalledTimes(1);
expect(prepareRuntimeAuth).toHaveBeenCalledTimes(1);
});
});