llmrouter: Alias-Art schwelle (Index + Mindestkontext), Stufen 25/40/47/50
billigsterUeberSchwelle in openrouter-auswahl, gemeinsamer klasseFiltern mit dem Referenzmodus. Stufen jetzt als Schwelle: low 25/256k, medium 40/256k, high 47/500k, max 50/500k (Andreas 2026-09-05). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,10 @@ Zwei Arten:
|
|||||||
Die Effort-Stufe des Ziels wird als `reasoning.effort` gesetzt; ein vom
|
Die Effort-Stufe des Ziels wird als `reasoning.effort` gesetzt; ein vom
|
||||||
Client mitgeschicktes `reasoning_effort` wird verworfen, die Klasse gehört
|
Client mitgeschicktes `reasoning_effort` wird verworfen, die Klasse gehört
|
||||||
zum Alias.
|
zum Alias.
|
||||||
|
- **`schwelle`** — Index-Schwelle (0–100) plus Mindestkontext, ohne Referenzmodell:
|
||||||
|
das günstigste Modell, das beides erfüllt, mit der niedrigsten ausreichenden
|
||||||
|
Effort-Stufe. Stand 2026-09-05: low 25/256k, medium 40/256k, high 47/500k,
|
||||||
|
max 50/500k. Stabiler als eine Referenz, sagt aber weniger über die Bedeutung.
|
||||||
- **`fest`** — Upstream und Modell stehen fest (lokales Qwen, TTS/STT,
|
- **`fest`** — Upstream und Modell stehen fest (lokales Qwen, TTS/STT,
|
||||||
Embeddings, kira auf deizo). Optional ein `body`-Patch, etwa
|
Embeddings, kira auf deizo). Optional ein `body`-Patch, etwa
|
||||||
`chat_template_kwargs.enable_thinking` für die Qwen-Stufen.
|
`chat_template_kwargs.enable_thinking` für die Qwen-Stufen.
|
||||||
|
|||||||
+14
-9
@@ -35,20 +35,24 @@
|
|||||||
},
|
},
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"marvin:low": {
|
"marvin:low": {
|
||||||
"art": "alternative",
|
"art": "schwelle",
|
||||||
"referenz": "anthropic/claude-haiku-4.5"
|
"index": 25,
|
||||||
|
"minContext": 256000
|
||||||
},
|
},
|
||||||
"marvin:medium": {
|
"marvin:medium": {
|
||||||
"art": "alternative",
|
"art": "schwelle",
|
||||||
"referenz": "anthropic/claude-sonnet-5"
|
"index": 40,
|
||||||
|
"minContext": 256000
|
||||||
},
|
},
|
||||||
"marvin:high": {
|
"marvin:high": {
|
||||||
"art": "alternative",
|
"art": "schwelle",
|
||||||
"referenz": "anthropic/claude-opus-5:medium"
|
"index": 47,
|
||||||
|
"minContext": 500000
|
||||||
},
|
},
|
||||||
"marvin:max": {
|
"marvin:max": {
|
||||||
"art": "alternative",
|
"art": "schwelle",
|
||||||
"referenz": "anthropic/claude-fable-5.1:high"
|
"index": 50,
|
||||||
|
"minContext": 500000
|
||||||
},
|
},
|
||||||
"marvin-free": {
|
"marvin-free": {
|
||||||
"art": "fest",
|
"art": "fest",
|
||||||
@@ -133,5 +137,6 @@
|
|||||||
"42i/marvin-2608-core:medium": "marvin-core:medium",
|
"42i/marvin-2608-core:medium": "marvin-core:medium",
|
||||||
"marvin-reasoning": "marvin-core:medium",
|
"marvin-reasoning": "marvin-core:medium",
|
||||||
"marvin-core": "marvin-core:none"
|
"marvin-core": "marvin-core:none"
|
||||||
}
|
},
|
||||||
|
"_stufen_doku": "Stufen als Index-Schwelle (Artificial-Analysis-Intelligence-Index, 0-100) mit Mindestkontext (Andreas 2026-09-05): low 25/256k, medium 40/256k, high 47/500k, max 50/500k. Die Art \"alternative\" mit Referenzmodell (z.B. anthropic/claude-opus-5:medium) bleibt möglich."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { dirname, resolve } from "path";
|
|||||||
export type Upstream = { base: string; keyEnv?: string; maxParallel?: number };
|
export type Upstream = { base: string; keyEnv?: string; maxParallel?: number };
|
||||||
export type Alias =
|
export type Alias =
|
||||||
| { art: "alternative"; referenz: string }
|
| { art: "alternative"; referenz: string }
|
||||||
|
| { art: "schwelle"; index: number; minContext: number }
|
||||||
| { art: "fest"; upstream: string; model: string; body?: Record<string, unknown> };
|
| { art: "fest"; upstream: string; model: string; body?: Record<string, unknown> };
|
||||||
export type Config = {
|
export type Config = {
|
||||||
port: number;
|
port: number;
|
||||||
|
|||||||
+15
-7
@@ -12,7 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, statSync } from "fs";
|
import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, statSync } from "fs";
|
||||||
import {
|
import {
|
||||||
apiKeyHolen, aaKeyHolen, listenHolen, kandidatenBauen, sperrliste, guenstigereAlternative,
|
apiKeyHolen, aaKeyHolen, listenHolen, kandidatenBauen, sperrliste, guenstigereAlternative, billigsterUeberSchwelle,
|
||||||
variantenHolen, referenzParsen, referenzFormat, type Variante, type Effort,
|
variantenHolen, referenzParsen, referenzFormat, type Variante, type Effort,
|
||||||
} from "../../llmlite/openrouter-auswahl.ts";
|
} from "../../llmlite/openrouter-auswahl.ts";
|
||||||
import { configLaden, type Config } from "./config.ts";
|
import { configLaden, type Config } from "./config.ts";
|
||||||
@@ -74,20 +74,28 @@ export async function aufloesen(c: Config, bisher: State, opts?: { dryRun?: bool
|
|||||||
const neu: State = { aktualisiert: new Date().toISOString(), zuordnung: { ...bisher.zuordnung } };
|
const neu: State = { aktualisiert: new Date().toISOString(), zuordnung: { ...bisher.zuordnung } };
|
||||||
|
|
||||||
for (const [alias, def] of Object.entries(c.aliases)) {
|
for (const [alias, def] of Object.entries(c.aliases)) {
|
||||||
if (def.art !== "alternative") continue;
|
if (def.art === "fest") continue;
|
||||||
const alt = bisher.zuordnung[alias];
|
const alt = bisher.zuordnung[alias];
|
||||||
try {
|
try {
|
||||||
const abgelehnt: string[] = [];
|
const abgelehnt: string[] = [];
|
||||||
const { referenz, auswahl } = await guenstigereAlternative(def.referenz, alle, key,
|
const melden = (id: string, grund: string) => abgelehnt.push(`${id}: ${grund}`);
|
||||||
(id, grund) => abgelehnt.push(`${id}: ${grund}`), gesperrt,
|
const o = { varianten, lasttest: c.lasttest, bestand: alt?.model };
|
||||||
{ varianten, lasttest: c.lasttest, bestand: alt?.model });
|
let ziel: Zuordnung;
|
||||||
const ziel: Zuordnung = auswahl === null
|
if (def.art === "alternative") {
|
||||||
|
const { referenz, auswahl } = await guenstigereAlternative(def.referenz, alle, key, melden, gesperrt, o);
|
||||||
|
ziel = auswahl === null
|
||||||
? { model: referenz.id, effort: referenz.effort ?? null, index: referenz.index, weighted: referenz.weighted, referenz: def.referenz, seit: alt?.seit ?? neu.aktualisiert, istReferenz: true }
|
? { model: referenz.id, effort: referenz.effort ?? null, index: referenz.index, weighted: referenz.weighted, referenz: def.referenz, seit: alt?.seit ?? neu.aktualisiert, istReferenz: true }
|
||||||
: { model: auswahl.candidate.id, effort: auswahl.candidate.effort ?? null, index: auswahl.candidate.index, weighted: auswahl.candidate.weighted, referenz: def.referenz, seit: alt?.seit ?? neu.aktualisiert, istReferenz: false };
|
: { model: auswahl.candidate.id, effort: auswahl.candidate.effort ?? null, index: auswahl.candidate.index, weighted: auswahl.candidate.weighted, referenz: def.referenz, seit: alt?.seit ?? neu.aktualisiert, istReferenz: false };
|
||||||
|
} else {
|
||||||
|
const beschreibung = `Index>=${def.index}, Kontext>=${def.minContext}`;
|
||||||
|
const { auswahl } = await billigsterUeberSchwelle(def.index, def.minContext, alle, key, melden, gesperrt, o);
|
||||||
|
if (auswahl === null) throw new Error(`kein Modell erfuellt ${beschreibung}`);
|
||||||
|
ziel = { model: auswahl.candidate.id, effort: auswahl.candidate.effort ?? null, index: auswahl.candidate.index, weighted: auswahl.candidate.weighted, referenz: beschreibung, seit: alt?.seit ?? neu.aktualisiert, istReferenz: false };
|
||||||
|
}
|
||||||
const gleich = alt !== undefined && alt.model === ziel.model && alt.effort === ziel.effort;
|
const gleich = alt !== undefined && alt.model === ziel.model && alt.effort === ziel.effort;
|
||||||
if (!gleich) {
|
if (!gleich) {
|
||||||
ziel.seit = neu.aktualisiert;
|
ziel.seit = neu.aktualisiert;
|
||||||
const zeile = `${neu.aktualisiert} ${alias}: ${alt ? referenzFormat(alt.model, alt.effort) : "(neu)"} -> ${referenzFormat(ziel.model, ziel.effort)} (Index ${ziel.index.toFixed(1)}, ${ziel.weighted.toFixed(2)} $/1M gewichtet, Referenz ${def.referenz}${ziel.istReferenz ? ", = Referenz" : ""})`;
|
const zeile = `${neu.aktualisiert} ${alias}: ${alt ? referenzFormat(alt.model, alt.effort) : "(neu)"} -> ${referenzFormat(ziel.model, ziel.effort)} (Index ${ziel.index.toFixed(1)}, ${ziel.weighted.toFixed(2)} $/1M gewichtet, ${ziel.referenz}${ziel.istReferenz ? ", = Referenz" : ""})`;
|
||||||
log(zeile);
|
log(zeile);
|
||||||
if (!opts?.dryRun) { mkdirSync(c.logDir, { recursive: true }); appendFileSync(`${c.logDir}/changes.log`, zeile + "\n"); }
|
if (!opts?.dryRun) { mkdirSync(c.logDir, { recursive: true }); appendFileSync(`${c.logDir}/changes.log`, zeile + "\n"); }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user