Files
llmrouter/test/auswahl.test.ts
T
elton 538e01a239
llmrouter CI / bun test + build (push) Successful in 3s
llmrouter CI / Build and publish image (push) Successful in 21s
llmrouter-Auswahl: Gate 65536 Output-Token für Arbeitsmodelle (42i/intern#1283)
Port aus 42i/agents llmlite/ (Merge 78c3c57, Ursprung 5730cfd) — der
Router lebt seit dem 2026-09-08 in diesem Repo. Arbeitsmodelle (nicht
TTS) brauchen mindestens 65536 Output-Token; inclusionai/ling-3.0-flash
(hart 32768) fällt damit aus der Auswahl. Geprüfte Kette: Rot-Beweis
(Produktion wählte ling für low + haiku, state.json 06:44), Nachher
(Ablehnung "max_output < 65536 (32768)"), Gegenprobe (beschädigtes Gate
quittiert die Testsuite rot mit benanntem Schaden).

Refs 42i/intern#1283
2026-09-08 20:40:28 +02:00

127 lines
3.7 KiB
TypeScript

process.env.OPENROUTER_API_URL = "http://127.0.0.1:1";
import { describe, it, expect } from "bun:test";
import {
MIN_OUTPUT,
kandidatenBauen,
billigsterUeberSchwelle,
type Model,
} from "../src/openrouter-auswahl.ts";
const modelle: Model[] = [
{
id: "anthropic/claude-haiku-4.5",
context_length: 131072,
pricing: { prompt: "0.01", completion: "0.01" },
supported_parameters: ["tools"],
benchmarks: {
artificial_analysis: { intelligence_index: 90 },
},
top_provider: { max_completion_tokens: 131072 },
},
{
id: "inclusionai/ling-3.0-flash",
context_length: 32768,
pricing: { prompt: "0.001", completion: "0.001" },
supported_parameters: ["tools"],
benchmarks: {
artificial_analysis: { intelligence_index: 50 },
},
top_provider: { max_completion_tokens: 32768 },
},
{
id: "z-ai/glm-5.3-flash",
context_length: 131072,
pricing: { prompt: "0.002", completion: "0.002" },
supported_parameters: ["tools"],
benchmarks: {
artificial_analysis: { intelligence_index: 55 },
},
top_provider: { max_completion_tokens: 131072 },
},
{
id: "foo/ohne-angabe",
context_length: 131072,
pricing: { prompt: "0.0015", completion: "0.0015" },
supported_parameters: ["tools"],
benchmarks: {
artificial_analysis: { intelligence_index: 60 },
},
},
];
describe("MIN_OUTPUT", () => {
it("ist genau 65536", () => {
expect(MIN_OUTPUT).toBe(65536);
});
});
describe("kandidatenBauen", () => {
it("fuellt maxOutput", () => {
const { alle } = kandidatenBauen({ voll: modelle, konto: null });
expect(alle.get("inclusionai/ling-3.0-flash")?.maxOutput).toBe(32768);
expect(alle.get("anthropic/claude-haiku-4.5")?.maxOutput).toBe(131072);
expect(alle.get("foo/ohne-angabe")?.maxOutput).toBe(0);
});
});
describe("billigsterUeberSchwelle", () => {
it("wahlt glm-5.3-flash und lehnt ling sowie foo ab", async () => {
const { alle } = kandidatenBauen({ voll: modelle, konto: null });
const abgelehnt: Array<{ id: string; grund: string }> = [];
const ablehnen = (id: string, grund: string) => {
abgelehnt.push({ id, grund });
};
const result = await billigsterUeberSchwelle(
40,
0,
alle,
undefined,
ablehnen,
new Map(),
{ varianten: [] },
);
expect(result.auswahl?.candidate.id).toBe("z-ai/glm-5.3-flash");
const ling = abgelehnt.find((e) => e.id === "inclusionai/ling-3.0-flash");
expect(ling).toBeDefined();
expect(ling!.grund).toContain("max_output");
expect(ling!.grund).toContain("32768");
const foo = abgelehnt.find((e) => e.id === "foo/ohne-angabe");
expect(foo).toBeDefined();
expect(foo!.grund).toBe("max_output unbekannt");
});
});
describe("Grenze maxOutput", () => {
it("ein Kandidat mit maxOutput genau 65536 bleibt infrage", async () => {
const grenzwert: Model[] = [
{
id: "bar/grenzwertig",
context_length: 131072,
pricing: { prompt: "0.001", completion: "0.001" },
supported_parameters: ["tools"],
benchmarks: {
artificial_analysis: { intelligence_index: 70 },
},
top_provider: { max_completion_tokens: 65536 },
},
];
const { alle } = kandidatenBauen({ voll: grenzwert, konto: null });
const abgelehnt: Array<{ id: string; grund: string }> = [];
const ablehnen = (id: string, grund: string) => {
abgelehnt.push({ id, grund });
};
const result = await billigsterUeberSchwelle(
0,
0,
alle,
undefined,
ablehnen,
new Map(),
{ varianten: [] },
);
expect(result.auswahl?.candidate.id).toBe("bar/grenzwertig");
expect(abgelehnt).toHaveLength(0);
});
});