Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
141 lines
5.9 KiB
TypeScript
141 lines
5.9 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { catalogModelLabel, listPiCatalog, scriptedCatalogEntry } from "./pi-models.js";
|
|
|
|
describe("Pi model catalog", () => {
|
|
it("keeps the custom catalog independent of server model IDs", () => {
|
|
const custom = listPiCatalog().filter((entry) => entry.provider === "openai-compatible");
|
|
expect(custom).toHaveLength(1);
|
|
expect(custom[0]).toMatchObject({ id: "custom", placeholder: true, reasoning: false });
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllEnvs();
|
|
vi.resetModules();
|
|
});
|
|
|
|
it("lists real Pi providers instead of a two-option dropdown", () => {
|
|
const catalog = listPiCatalog();
|
|
const providers = new Set(catalog.map((entry) => entry.provider));
|
|
expect(catalog.length).toBeGreaterThan(20);
|
|
expect(providers.has("openrouter")).toBe(true);
|
|
expect(providers.size).toBeGreaterThan(5);
|
|
expect(
|
|
catalog.some(
|
|
(entry) => entry.auth === "oauth" || entry.auth === "both" || entry.subscription,
|
|
),
|
|
).toBe(true);
|
|
const chatgpt = catalog.find((entry) => entry.provider === "openai-codex");
|
|
expect(chatgpt?.signIn).toBe("device-code");
|
|
expect(chatgpt?.billing).toMatch(/ChatGPT Plus or Pro/);
|
|
const copilot = catalog.find((entry) => entry.provider === "github-copilot");
|
|
expect(copilot?.signIn).toBe("device-code");
|
|
const grok = catalog.find((entry) => entry.provider === "xai");
|
|
expect(grok?.signIn).toBe("device-code");
|
|
const claude = catalog.find((entry) => entry.provider === "anthropic");
|
|
expect(claude).toMatchObject({
|
|
signIn: "auth-url",
|
|
authHint: "Claude Pro/Max / key",
|
|
oauthLabel: "Sign in with Claude Pro/Max",
|
|
billing: "",
|
|
});
|
|
expect(scriptedCatalogEntry.provider).toBe("scripted");
|
|
});
|
|
|
|
it("lists current xAI and OpenCode Go models from the Pi catalog", () => {
|
|
const catalog = listPiCatalog();
|
|
const ids = (provider: string) =>
|
|
catalog.filter((entry) => entry.provider === provider).map((entry) => entry.id);
|
|
expect(ids("xai")).toContain("grok-4.6");
|
|
expect(ids("opencode-go")).toContain("glm-5.3");
|
|
const grok46 = catalog.find((entry) => entry.provider === "xai" && entry.id === "grok-4.6");
|
|
expect(grok46).toMatchObject({
|
|
reasoning: true,
|
|
thinkingLevels: ["low", "medium", "high", "xhigh"],
|
|
});
|
|
const openAiCompatible = catalog.find((entry) => entry.provider === "openai-compatible");
|
|
expect(openAiCompatible).toMatchObject({ id: "custom", placeholder: true });
|
|
});
|
|
|
|
it("adds a configured OpenRouter model that is newer than the static catalog", async () => {
|
|
vi.stubEnv("PI_DEFAULT_PROVIDER", " openrouter ");
|
|
vi.stubEnv("PI_DEFAULT_MODEL", " rakazo-test/unknown-future-model ");
|
|
vi.resetModules();
|
|
|
|
const { listPiCatalog: listConfiguredCatalog } = await import("./pi-models.js");
|
|
expect(listConfiguredCatalog()[0]).toMatchObject({
|
|
provider: "openrouter",
|
|
id: "rakazo-test/unknown-future-model",
|
|
label: "rakazo-test/unknown-future-model",
|
|
});
|
|
});
|
|
|
|
it("normalizes a PI_DEFAULT_MODEL id that ends in -latest", async () => {
|
|
vi.stubEnv("PI_DEFAULT_PROVIDER", "openrouter");
|
|
vi.stubEnv("PI_DEFAULT_MODEL", "foo-latest");
|
|
vi.resetModules();
|
|
|
|
const { listPiCatalog: listConfiguredCatalog } = await import("./pi-models.js");
|
|
expect(listConfiguredCatalog()[0]).toMatchObject({
|
|
provider: "openrouter",
|
|
id: "foo-latest",
|
|
label: "foo (auto-updates)",
|
|
});
|
|
});
|
|
|
|
it("does not advertise a synthetic model for providers the runtime cannot synthesize", async () => {
|
|
vi.stubEnv("PI_DEFAULT_PROVIDER", "anthropic");
|
|
vi.stubEnv("PI_DEFAULT_MODEL", "future/unknown-model");
|
|
vi.resetModules();
|
|
|
|
const { listPiCatalog: listConfiguredCatalog } = await import("./pi-models.js");
|
|
expect(
|
|
listConfiguredCatalog().some(
|
|
(entry) => entry.provider === "anthropic" && entry.id === "future/unknown-model",
|
|
),
|
|
).toBe(false);
|
|
});
|
|
|
|
it('never labels an older model "latest" and keeps aliases distinct from snapshots', () => {
|
|
const catalog = listPiCatalog();
|
|
const label = (id: string) =>
|
|
catalog.find((entry) => entry.provider === "anthropic" && entry.id === id)?.label;
|
|
expect(label("claude-opus-5")).toBeDefined();
|
|
expect(label("claude-opus-4-5")).toBe("Claude Opus 4.5 (auto-updates)");
|
|
expect(label("claude-haiku-4-5")).toBe("Claude Haiku 4.5 (auto-updates)");
|
|
expect(label("claude-haiku-4-5-20251001")).toBe("Claude Haiku 4.5");
|
|
expect(catalog.some((entry) => /\blatest\b/i.test(entry.label))).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("catalogModelLabel", () => {
|
|
const providerModelIds = [
|
|
"claude-opus-4-5",
|
|
"claude-opus-4-5-20251101",
|
|
"mistral-medium",
|
|
"mistral-medium-2508",
|
|
"mistral-small",
|
|
"mistral-small-260401",
|
|
"foo",
|
|
"foo-preview",
|
|
];
|
|
|
|
it.each([
|
|
// Alias: the id ends in `latest`, or a dated sibling proves the undated id floats.
|
|
["claude-opus-4-5", "Claude Opus 4.5 (latest)", "Claude Opus 4.5 (auto-updates)"],
|
|
["mistral-medium", "Mistral Medium Latest", "Mistral Medium (auto-updates)"],
|
|
["mistral-small", "Mistral Small Latest", "Mistral Small (auto-updates)"],
|
|
["gemini-flash-latest", "Gemini Flash Latest", "Gemini Flash (auto-updates)"],
|
|
["foo-latest", "foo-latest", "foo (auto-updates)"],
|
|
["foo/latest", "foo/latest", "foo (auto-updates)"],
|
|
// Pinned: `-preview` is its own model and a dated id is already a snapshot, so promise nothing.
|
|
["foo", "Foo Latest", "Foo"],
|
|
["claude-opus-4-5-20251101", "Claude Opus 4.5 (latest)", "Claude Opus 4.5"],
|
|
// Untouched: no marker, no name, or nothing left once the marker goes.
|
|
["claude-opus-5", "Claude Opus 5", "Claude Opus 5"],
|
|
["some-model", undefined, "some-model"],
|
|
["latest", "latest", "latest"],
|
|
])("labels %s / %s as %s", (id, name, expected) => {
|
|
expect(catalogModelLabel(id, name, providerModelIds)).toBe(expected);
|
|
});
|
|
});
|