File size: 3,736 Bytes
fc93158 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | import { beforeEach, describe, expect, it, vi } from "vitest";
import {
buildProviderPluginMethodChoice,
resolveProviderModelPickerEntries,
resolveProviderPluginChoice,
resolveProviderWizardOptions,
runProviderModelSelectedHook,
} from "./provider-wizard.js";
import type { ProviderPlugin } from "./types.js";
const resolvePluginProviders = vi.hoisted(() => vi.fn<() => ProviderPlugin[]>(() => []));
vi.mock("./providers.js", () => ({
resolvePluginProviders,
}));
function makeProvider(overrides: Partial<ProviderPlugin> & Pick<ProviderPlugin, "id" | "label">) {
return {
auth: [],
...overrides,
} satisfies ProviderPlugin;
}
describe("provider wizard boundaries", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("uses explicit onboarding choice ids and bound method ids", () => {
const provider = makeProvider({
id: "vllm",
label: "vLLM",
auth: [
{ id: "local", label: "Local", kind: "custom", run: vi.fn() },
{ id: "cloud", label: "Cloud", kind: "custom", run: vi.fn() },
],
wizard: {
onboarding: {
choiceId: "self-hosted-vllm",
methodId: "local",
choiceLabel: "vLLM local",
groupId: "local-runtimes",
groupLabel: "Local runtimes",
},
},
});
resolvePluginProviders.mockReturnValue([provider]);
expect(resolveProviderWizardOptions({})).toEqual([
{
value: "self-hosted-vllm",
label: "vLLM local",
groupId: "local-runtimes",
groupLabel: "Local runtimes",
},
]);
expect(
resolveProviderPluginChoice({
providers: [provider],
choice: "self-hosted-vllm",
}),
).toEqual({
provider,
method: provider.auth[0],
});
});
it("builds model-picker entries from plugin metadata and provider-method choices", () => {
const provider = makeProvider({
id: "sglang",
label: "SGLang",
auth: [
{ id: "server", label: "Server", kind: "custom", run: vi.fn() },
{ id: "cloud", label: "Cloud", kind: "custom", run: vi.fn() },
],
wizard: {
modelPicker: {
label: "SGLang server",
hint: "OpenAI-compatible local runtime",
methodId: "server",
},
},
});
resolvePluginProviders.mockReturnValue([provider]);
expect(resolveProviderModelPickerEntries({})).toEqual([
{
value: buildProviderPluginMethodChoice("sglang", "server"),
label: "SGLang server",
hint: "OpenAI-compatible local runtime",
},
]);
});
it("routes model-selected hooks only to the matching provider", async () => {
const matchingHook = vi.fn(async () => {});
const otherHook = vi.fn(async () => {});
resolvePluginProviders.mockReturnValue([
makeProvider({
id: "ollama",
label: "Ollama",
onModelSelected: otherHook,
}),
makeProvider({
id: "vllm",
label: "vLLM",
onModelSelected: matchingHook,
}),
]);
const env = { OPENCLAW_HOME: "/tmp/openclaw-home" } as NodeJS.ProcessEnv;
await runProviderModelSelectedHook({
config: {},
model: "vllm/qwen3-coder",
prompter: {} as never,
agentDir: "/tmp/agent",
workspaceDir: "/tmp/workspace",
env,
});
expect(resolvePluginProviders).toHaveBeenCalledWith({
config: {},
workspaceDir: "/tmp/workspace",
env,
});
expect(matchingHook).toHaveBeenCalledWith({
config: {},
model: "vllm/qwen3-coder",
prompter: {},
agentDir: "/tmp/agent",
workspaceDir: "/tmp/workspace",
});
expect(otherHook).not.toHaveBeenCalled();
});
});
|