1import { describe, expect, it } from "@effect/vitest";
2import { MuseSettings } from "../settings.ts";
3import * as Effect from "effect/Effect";
4import * as Schema from "effect/Schema";
5
6import type { MuseSdkHost } from "./sdk.ts";
7import {
8 discoverMuseModels,
9 museModelCapabilities,
10 resolveMuseReasoningEffort,
11} from "./modelCatalog.ts";
12
13const settings = Schema.decodeSync(MuseSettings)({});
14const host = (catalog: Record<string, unknown>): MuseSdkHost => ({
15 connection: {
16 request: async () => catalog,
17 command: async () => ({}),
18 mintCommandId: () => "test",
19 onNotification: () => {},
20 onServerRequest: () => {},
21 onProtocolError: () => {},
22 closed: new Promise<void>(() => {}),
23 },
24 initializeResult: { grantedCapabilities: [] },
25 exited: new Promise(() => {}),
26 close: async () => {},
27});
28
29const row = (modelId: string, extra: Record<string, unknown> = {}) => ({
30 modelId,
31 displayLabel: modelId,
32 providerId: "meta",
33 profileId: "tbh",
34 isDefault: false,
35 contextLimit: 1_000_000,
36 ...extra,
37});
38
39describe("Muse model catalog", () => {
40 it.effect("uses the labels, efforts and defaults that model/list sends", () =>
41 Effect.gen(function* () {
42 const models = yield* discoverMuseModels(settings, {}, undefined, async () =>
43 host({
44 providerId: "meta",
45 profileId: "tbh",
46 source: "providerCatalog",
47 models: [
48 row("restful-walrus", {
49 displayLabel: " Restful Walrus ",
50 isDefault: true,
51 variants: ["minimal", "low", "high", "future-tier"],
52 reasoningEffortVariants: [{ tier: "low", description: "Quick" }],
53 defaultReasoningEffort: "high",
54 }),
55 row("older-host"),
56 row("unknown-efforts", { variants: "unknown" }),
57 row("foreign", { providerId: "another" }),
58 ],
59 }),
60 ).pipe(Effect.scoped);
61 expect(models.map(({ slug, name, isDefault }) => [slug, name, isDefault])).toEqual([
62 ["restful-walrus", "Restful Walrus", true],
63 ["older-host", "older-host", false],
64 ["unknown-efforts", "unknown-efforts", false],
65 ]);
66 expect(models[0]?.capabilities?.optionDescriptors).toEqual([
67 {
68 id: "reasoningEffort",
69 label: "Reasoning",
70 type: "select",
71 currentValue: "high",
72 options: [
73 { id: "minimal", label: "Minimal" },
74 { id: "low", label: "Low", description: "Quick" },
75 { id: "high", label: "High", isDefault: true },
76 ],
77 },
78 ]);
79 // Older hosts and "unknown" fall back to Muse's documented tiers.
80 expect(models[1]?.capabilities).toEqual(museModelCapabilities());
81 expect(models[2]?.capabilities).toEqual(museModelCapabilities());
82 }),
83 );
84
85 it("normalizes saved and implicit efforts against the selected model before dispatch", () => {
86 const fallback = museModelCapabilities();
87 expect(resolveMuseReasoningEffort(fallback, undefined)).toBe("high");
88 expect(resolveMuseReasoningEffort(fallback, "max")).toBe("max");
89 expect(resolveMuseReasoningEffort(fallback, "ultra")).toBe("high");
90 const restricted = museModelCapabilities({ variants: ["xhigh"] });
91 expect(resolveMuseReasoningEffort(restricted, undefined)).toBe("xhigh");
92 expect(resolveMuseReasoningEffort(restricted, "low")).toBe("xhigh");
93 expect(
94 resolveMuseReasoningEffort(museModelCapabilities({ variants: [] }), "max"),
95 ).toBeUndefined();
96 expect(resolveMuseReasoningEffort(undefined, "max")).toBe("max");
97 });
98});