Files
routstrd/tests/integrations/pi.test.ts
T
redshift 154c690bdf feat(integrations): derive pi thinking levels from the daemon's reasoning metadata
The generated models.json previously left reasoning/thinkingLevelMap to
hand curation, so a fresh install advertised no thinking levels at all and
a stale hand-written map could disagree with what the model accepts.

routstr-core now publishes a per-model `reasoning` object (OpenRouter
shape) with `supported_efforts`. Derive pi's thinking fields from it:

- write every pi level explicitly, because pi reads an omitted level as
  "provider default" for levels through `high` and as "unsupported" for
  `xhigh`/`max` — a partial map would advertise levels the model rejects
- map pi's `off` to the provider's `none`, and never offer `off` on
  models that report `mandatory: true`
- return null when the daemon publishes no allowlist (models that only
  report `mandatory`, or no reasoning at all), and keep the user's
  hand-curated values in that case

Extract buildPiModelEntry so the projection is testable, and cover the
derivation plus the preserve/overwrite behaviour in tests.
2026-09-17 18:45:23 +02:00

204 lines
5.4 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import type { RoutstrModel } from "../../src/integrations/registry";
import {
buildPiModelEntry,
deriveThinkingFields,
type PiModelEntry,
} from "../../src/integrations/pi";
const model = (over: Partial<RoutstrModel>): RoutstrModel => ({
id: "test-model",
...over,
});
describe("deriveThinkingFields", () => {
it("writes every level explicitly, mapping off to the provider's none", () => {
const derived = deriveThinkingFields(
model({
reasoning: {
mandatory: false,
default_enabled: true,
supported_efforts: ["max", "xhigh", "high", "medium", "low", "none"],
default_effort: "medium",
},
}),
);
expect(derived).toEqual({
reasoning: true,
thinkingLevelMap: {
off: "none",
minimal: null,
low: "low",
medium: "medium",
high: "high",
xhigh: "xhigh",
max: "max",
},
});
});
it("hides off on mandatory models and nulls the gaps in the allowlist", () => {
const derived = deriveThinkingFields(
model({
reasoning: {
mandatory: true,
default_enabled: true,
supported_efforts: ["max", "high", "low"],
default_effort: "max",
},
}),
);
expect(derived?.thinkingLevelMap).toEqual({
off: null,
minimal: null,
low: "low",
medium: null,
high: "high",
xhigh: null,
max: "max",
});
});
it("never offers off on a mandatory model even if none is listed", () => {
const derived = deriveThinkingFields(
model({ reasoning: { mandatory: true, supported_efforts: ["high", "none"] } }),
);
expect(derived?.thinkingLevelMap).toEqual({
off: null,
minimal: null,
low: null,
medium: null,
high: "high",
xhigh: null,
max: null,
});
});
it("keeps a sparse allowlist sparse", () => {
const derived = deriveThinkingFields(
model({ reasoning: { mandatory: false, supported_efforts: ["high", "minimal"] } }),
);
expect(derived?.thinkingLevelMap).toEqual({
off: null,
minimal: "minimal",
low: null,
medium: null,
high: "high",
xhigh: null,
max: null,
});
});
it("returns null when the upstream publishes no allowlist", () => {
expect(deriveThinkingFields(model({ reasoning: { mandatory: false } }))).toBeNull();
expect(deriveThinkingFields(model({ reasoning: null }))).toBeNull();
expect(deriveThinkingFields(model({}))).toBeNull();
expect(
deriveThinkingFields(model({ reasoning: { supported_efforts: [] } })),
).toBeNull();
});
it("ignores unknown levels and normalizes case and padding", () => {
const derived = deriveThinkingFields(
model({ reasoning: { supported_efforts: [" HIGH ", "auto", "low"] } }),
);
expect(derived?.thinkingLevelMap).toEqual({
off: null,
minimal: null,
low: "low",
medium: null,
high: "high",
xhigh: null,
max: null,
});
});
});
describe("buildPiModelEntry", () => {
it("derives thinking fields alongside the rest of the entry", () => {
const entry = buildPiModelEntry(
model({
id: "gpt-5.6-sol",
name: "OpenAI: GPT-5.6 Sol",
context_length: 1050000,
architecture: { input_modalities: ["file", "image", "text"] },
reasoning: { supported_efforts: ["max", "high", "none"] },
}),
);
expect(entry).toEqual({
id: "gpt-5.6-sol",
name: "OpenAI: GPT-5.6 Sol",
contextWindow: 1050000,
input: ["text", "image"],
reasoning: true,
thinkingLevelMap: {
off: "none",
minimal: null,
low: null,
medium: null,
high: "high",
xhigh: null,
max: "max",
},
});
});
it("overwrites a stale hand-written map when the daemon has an allowlist", () => {
const previous: PiModelEntry = {
id: "glm-5.3",
reasoning: true,
thinkingLevelMap: { off: "none", low: "low" },
compat: { supportsReasoningEffort: true },
};
const entry = buildPiModelEntry(
model({ id: "glm-5.3", reasoning: { mandatory: true, supported_efforts: ["max", "high", "low"] } }),
previous,
);
expect(entry.thinkingLevelMap).toEqual({
off: null,
minimal: null,
low: "low",
medium: null,
high: "high",
xhigh: null,
max: "max",
});
// compat is never published by the daemon, so it survives.
expect(entry.compat).toEqual({ supportsReasoningEffort: true });
});
it("preserves hand-curated thinking fields when the daemon cannot decide", () => {
const previous: PiModelEntry = {
id: "minimax-m3",
reasoning: true,
thinkingLevelMap: { off: null },
compat: { supportsReasoningEffort: false },
};
const entry = buildPiModelEntry(
model({ id: "minimax-m3", reasoning: { mandatory: false } }),
previous,
);
expect(entry.reasoning).toBe(true);
expect(entry.thinkingLevelMap).toEqual({ off: null });
expect(entry.compat).toEqual({ supportsReasoningEffort: false });
});
it("omits thinking fields entirely when neither side has any", () => {
const entry = buildPiModelEntry(model({ id: "gemma-4-uncensored" }));
expect(entry).toEqual({ id: "gemma-4-uncensored", input: [] });
expect("reasoning" in entry).toBe(false);
expect("thinkingLevelMap" in entry).toBe(false);
});
});