evOo-local/apps/web/src/lib/connected-models.test.ts
Claude f28f9b7d55
feat(models): map model tiers per space and let bots and helpers ask for one
A space can now map the tiers fast, standard and code to connected models,
including several models served by one OpenAI-compatible router endpoint
(one credential, many model ids). A bot can ask for a tier, and a single
run_subagent call can ask for one. A tier with no model mapped falls back
to the usual model choice, so nothing breaks before tiers are set up.

- db: SpaceModelTier repo (list, find, set or clear); tier mappings are
  copied into a new space like model and voice preferences; createBot
  carries a tier, so duplicates keep it and spawned bots inherit it.
- contracts/api: models.tiers.list and models.tiers.set;
  models.probeOpenAiCompatible can list a connected server's models by
  credential id with its stored address and key; bots.update accepts
  modelTier and keeps a pinned model and a tier mutually exclusive, so a
  model picked on a surface that knows no tiers (mobile) is never silently
  overridden. Thinking-level validation follows the tier's model. AI
  consent status discloses tier models; desktop local settings allow the
  tier procedures.
- adapters: selectConfiguredModel runs a mapped tier ahead of the pinned
  model and the space default; the executor resolves bot tiers for runs
  and model lookups, and helper tiers through resolveModelTier.
  run_subagent takes model_tier; an explicit model wins over it.
- web: a collapsed Tiers section in model settings (shown once a model is
  connected) and mapped tiers as choices in the bot Model select.
- docs: tiers in self-host model setup.
- tests: resolution order and fallbacks (selection, executor, Pi helper),
  repo, API and contract units, authorization and offline Pi Postgres
  suites, and a model-tiers web E2E spec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-02 22:07:32 +00:00

59 lines
2.0 KiB
TypeScript

import type { ModelCatalogEntry, ModelCredential } from "@rakazo/contracts";
import { describe, expect, it } from "vitest";
import { connectedModelOptions } from "./connected-models";
const catalog: ModelCatalogEntry[] = [
{ provider: "xai", providerName: "xAI", id: "grok-4.6", label: "Grok 4.6", billing: "" },
{ provider: "xai", providerName: "xAI", id: "grok-mini", label: "Grok mini", billing: "" },
{
provider: "openai-compatible",
providerName: "OpenAI-compatible",
id: "custom",
label: "Custom",
billing: "",
placeholder: true,
},
];
const credentials: ModelCredential[] = [
{
id: "router",
provider: "openai-compatible",
label: "OpenAI-compatible",
hasKey: true,
isDefault: true,
modelId: "router-standard",
},
{ id: "xai", provider: "xai", label: "xAI", hasKey: true, isDefault: false, modelId: "grok-4.6" },
];
describe("connected model options", () => {
it("expands catalog providers and keeps a free-form connection's saved model", () => {
expect(connectedModelOptions(credentials, catalog)).toEqual([
{
credentialId: "router",
provider: "openai-compatible",
modelId: "router-standard",
label: "OpenAI-compatible · router-standard",
},
{ credentialId: "xai", provider: "xai", modelId: "grok-4.6", label: "xAI · Grok 4.6" },
{ credentialId: "xai", provider: "xai", modelId: "grok-mini", label: "xAI · Grok mini" },
]);
});
it("adds every model a router endpoint lists, once each", () => {
const options = connectedModelOptions(credentials, catalog, {
router: ["router-standard", "router-fast", "router-code"],
});
expect(options.filter((option) => option.credentialId === "router")).toEqual(
["router-standard", "router-fast", "router-code"].map((modelId) => ({
credentialId: "router",
provider: "openai-compatible",
modelId,
label: `OpenAI-compatible · ${modelId}`,
})),
);
});
});