import { describe, expect, it } from "vitest"; import type { Api, Model, Provider } from "@earendil-works/pi-ai"; import { resolveReviewModel, type ReviewModelRegistry } from "#src/auto-review/model"; import { DEFAULT_AUTO_REVIEW_CONFIG, type AutoReviewConfig } from "#src/auto-review/config"; const model: Model = { id: "reviewer-model", name: "Reviewer Model", api: "openai-completions", provider: "vendor", baseUrl: "https://example.invalid", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1000, maxTokens: 100, }; function registryWith(providerId: string | undefined): ReviewModelRegistry { const provider = { id: providerId ?? "vendor", getModels: () => [model] } as unknown as Provider; return { find: (candidateProvider, modelId) => candidateProvider === providerId && modelId === model.id ? model : undefined, getAll: () => [model], getProvider: (candidate) => (candidate === providerId ? provider : undefined), getApiKeyAndHeaders: async () => ({ ok: true, apiKey: "key" }), }; } function config(overrides: Partial): AutoReviewConfig { return { ...DEFAULT_AUTO_REVIEW_CONFIG, ...overrides }; } describe("resolveReviewModel", () => { it("resolves the operator-named model from the registry", () => { const result = resolveReviewModel( registryWith("vendor"), config({ provider: "vendor", model: "reviewer-model" }), ); expect(result.ok).toBe(true); if (!result.ok) throw new Error("expected success"); expect(result.value.model.id).toBe("reviewer-model"); expect(result.value.provider.id).toBe("vendor"); }); it("reports model-unconfigured when no provider is named", () => { const result = resolveReviewModel(registryWith("vendor"), config({ model: "reviewer-model" })); expect(result).toEqual({ ok: false, category: "model-unconfigured" }); }); it("reports model-unconfigured when no model is named", () => { const result = resolveReviewModel(registryWith("vendor"), config({ provider: "vendor" })); expect(result).toEqual({ ok: false, category: "model-unconfigured" }); }); it("reports model-unconfigured for the default config with no named pair", () => { const result = resolveReviewModel(registryWith("vendor"), DEFAULT_AUTO_REVIEW_CONFIG); expect(result).toEqual({ ok: false, category: "model-unconfigured" }); }); it("reports provider-unresolved for an unknown provider", () => { const result = resolveReviewModel( registryWith("vendor"), config({ provider: "missing", model: "reviewer-model" }), ); expect(result).toEqual({ ok: false, category: "provider-unresolved" }); }); it("reports model-unresolved for a model the provider does not publish", () => { const result = resolveReviewModel( registryWith("vendor"), config({ provider: "vendor", model: "missing-model" }), ); expect(result).toEqual({ ok: false, category: "model-unresolved" }); }); it("never synthesizes a model the provider does not publish", () => { // The reviewer used to fall back to a synthesized Codex stand-in. It must // now refuse instead, so the ask defers to the human prompt. const result = resolveReviewModel( registryWith("vendor"), config({ provider: "vendor", model: "codex-auto-review" }), ); expect(result.ok).toBe(false); }); });