Ctrl K
ComunidadGuías…

lib/ai-model-config-file.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { describe, expect, it } from "vitest";

import {
  buildModelConfigFile,
  diffModelConfig,
  MODEL_CONFIG_FILE_FORMAT,
  MODEL_CONFIG_FILE_VERSION,
  parseModelConfigFile,
} from "@/lib/ai-model-config-file";

/**
 * The model configuration file (MIN-829): what one instance exports must
 * import into another as the same configuration, a newer file is refused,
 * and an import replaces rather than merges.
 */

const STORED = {
  assistant_model: "openai/gpt-5-mini",
  issue_embedding_model: "google/gemini-embedding-001",
  jev_model: "liquid/d1",
  assistant_reasoning_level: "high",
  byok_default_openai_assistant_model: "gpt-5",
  smart_fill_model: null,
};

describe("model configuration file", () => {
  it("writes only what is set, under its version", () => {
    const file = buildModelConfigFile(STORED, new Date("2026-10-29T00:00:00Z"));
    expect(file).toEqual({
      format: MODEL_CONFIG_FILE_FORMAT,
      version: MODEL_CONFIG_FILE_VERSION,
      exportedAt: "2026-10-29T00:00:00.000Z",
      tasks: {
        assistant_model: { model: "openai/gpt-5-mini" },
        issue_embedding_model: { model: "google/gemini-embedding-001" },
        jev_model: { model: "liquid/d1" },
      },
      reasoningLevel: "high",
      providerDefaults: { byok_default_openai_assistant_model: "gpt-5" },
    });
  });

  it("imports an export as the same configuration", () => {
    const parsed = parseModelConfigFile(JSON.stringify(buildModelConfigFile(STORED)));
    expect(parsed.ok).toBe(true);
    if (!parsed.ok) return;
    expect(diffModelConfig(STORED, parsed.values)).toEqual([]);
    expect(parsed.upgraded).toBe(false);
  });

  it("returns every setting the file leaves out to its default", () => {
    const parsed = parseModelConfigFile({
      format: MODEL_CONFIG_FILE_FORMAT,
      version: MODEL_CONFIG_FILE_VERSION,
      tasks: { assistant_model: { model: "openai/gpt-5" } },
    });
    if (!parsed.ok) throw new Error(parsed.error);
    const changes = diffModelConfig(STORED, parsed.values);
    expect(changes).toContainEqual({ key: "assistant_model", from: "openai/gpt-5-mini", to: "openai/gpt-5" });
    expect(changes).toContainEqual({ key: "jev_model", from: "liquid/d1", to: null });
    expect(changes).toContainEqual({ key: "issue_embedding_model", from: "google/gemini-embedding-001", to: null });
  });

  it("upgrades a version 1 file: categories fill their role, the rest is named (MIN-954)", () => {
    const parsed = parseModelConfigFile({
      format: MODEL_CONFIG_FILE_FORMAT,
      version: 1,
      categories: {
        assistant: { model: "anthropic/claude-haiku-5.5", suffix: "nitro" },
        automations: { model: "openai/gpt-5-mini" },
      },
      tasks: { brief_model: { model: "openai/gpt-5" }, transcription_model: { model: "openai/whisper-large-v3" } },
      recommendedModels: ["openai/gpt-5-mini"],
    });
    if (!parsed.ok) throw new Error(parsed.error);
    expect(parsed.upgraded).toBe(true);
    expect(parsed.values).toMatchObject({
      assistant_model: "anthropic/claude-haiku-5.5",
      transcription_model: "openai/whisper-large-v3",
    });
    expect(parsed.values).not.toHaveProperty("assistant_model_suffix");
    expect(parsed.ignored).toEqual([
      "categories.automations",
      "tasks.brief_model",
      "tasks.assistant_model.suffix",
      "recommendedModels",
    ]);
  });

  it("refuses a file written by a newer release", () => {
    expect(
      parseModelConfigFile({ format: MODEL_CONFIG_FILE_FORMAT, version: MODEL_CONFIG_FILE_VERSION + 1 }),
    ).toEqual({ ok: false, error: "newer_version" });
  });

  it.each([
    ["not JSON", "{", "invalid_json"],
    ["another file", { format: "something-else", version: 1 }, "not_a_model_config"],
    ["no version", { format: MODEL_CONFIG_FILE_FORMAT }, "not_a_model_config"],
    [
      "an invalid model id",
      { format: MODEL_CONFIG_FILE_FORMAT, version: 2, tasks: { assistant_model: { model: 42 } } },
      "invalid_value",
    ],
    [
      "an unknown reasoning level",
      { format: MODEL_CONFIG_FILE_FORMAT, version: 2, reasoningLevel: "extreme" },
      "invalid_value",
    ],
  ])("refuses %s", (_label, input, error) => {
    const parsed = parseModelConfigFile(input);
    expect(parsed.ok).toBe(false);
    if (!parsed.ok) expect(parsed.error).toBe(error);
  });

  it("leaves out, and names, the entries this release does not know", () => {
    const parsed = parseModelConfigFile({
      format: MODEL_CONFIG_FILE_FORMAT,
      version: MODEL_CONFIG_FILE_VERSION,
      categories: { video: { model: "x/y" } },
      tasks: { fallback_model: { model: "x/y" } },
    });
    expect(parsed.ok && parsed.ignored).toEqual(["categories.video", "tasks.fallback_model"]);
  });
});