llmrouter: role/inka -> openrouter/qwen/qwen3.8-27b (Test, Andreas 2026-09-06); Inka-Keys dürfen die Rolle
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+3
-2
@@ -143,7 +143,7 @@
|
||||
"upstream": "litellm",
|
||||
"_doku": "Unbekannte Modellnamen (z.B. 42i/marvin-2608:*) gehen unverändert mit dem Client-Key an LiteLLM. Umstellung je Agent = Modellname 2608 -> 2609. Fällt weg, wenn LiteLLM abgeschaltet wird; dann kommen die 2608-Namen als altnamen zurück."
|
||||
},
|
||||
"_rollen_doku": "Rollen entkoppeln Agenten von Modellnamen: ein Agent ruft role/worker, welches Modell dahintersteht, stellt man HIER um -- aus der Ferne, ohne Container-Deploy. Ziele: 42i/*-Alias, openrouter/<id>[:effort] (1:1) oder alt/<id>[:effort] (guenstigste Alternative). Vorbelegung 2026-09-05 (Elton): Fuehrungsrollen high, Rest medium -- Andreas' Feinjustierung ausstehend.",
|
||||
"_rollen_doku": "Rollen entkoppeln Agenten von Modellnamen: ein Agent ruft role/worker, welches Modell dahintersteht, stellt man HIER um -- aus der Ferne, ohne Container-Deploy. Ziele: 42i/*-Alias, openrouter/<id>[:effort] (1:1) oder alt/<id>[:effort] (guenstigste Alternative). Vorbelegung 2026-09-05 (Elton): Fuehrungsrollen high, Rest medium -- Andreas' Feinjustierung ausstehend. role/inka (2026-09-06): Inkas Telefon-Hirn; Test Qwen 3.8 27B als Kandidat fuer die A6000 bei Uwe, vorher openrouter/openai/gpt-5.6-luna.",
|
||||
"rollen": {
|
||||
"xo": "42i/marvin-2609:high",
|
||||
"architect": "42i/marvin-2609:high",
|
||||
@@ -164,7 +164,8 @@
|
||||
"i18n": "42i/marvin-2609:medium",
|
||||
"docs": "42i/marvin-2609:medium",
|
||||
"ar": "42i/marvin-2609:medium",
|
||||
"archivist": "42i/marvin-2609:medium"
|
||||
"archivist": "42i/marvin-2609:medium",
|
||||
"inka": "openrouter/qwen/qwen3.8-27b"
|
||||
},
|
||||
"_namen_doku": "Namensraeume: 42i/* = konfigurierte Aliasse (aliases); deizo/* = Modelle im Prod-RZ deizo (kira, fest); openrouter/<id>[:effort] = 1:1 an OpenRouter, Effort als reasoning.effort; alt/<id>[:effort] = guenstigste Alternative derselben Klasse (Intelligence, Coding, Agentic je >= 95 % der Referenz, Kontext >= 90 %, billiger), aufgeloest bei der ersten Anfrage und im 12h-Takt erneuert, OHNE Last-Probe; role/<name> = Rolle (rollen). Alles andere -> fallback (LiteLLM).",
|
||||
"anthropicModus": "alt",
|
||||
|
||||
+4
-2
@@ -88,7 +88,8 @@
|
||||
"42i/marvin-stt-turbo",
|
||||
"42i/marvin-tts",
|
||||
"42i/marvin-tts-turbo",
|
||||
"openrouter/openai/gpt-5.6-luna"
|
||||
"openrouter/openai/gpt-5.6-luna",
|
||||
"role/inka"
|
||||
],
|
||||
"persona-inka-mx": [
|
||||
"42i/marvin-2609-core:none",
|
||||
@@ -102,7 +103,8 @@
|
||||
"42i/marvin-stt-turbo",
|
||||
"42i/marvin-tts",
|
||||
"42i/marvin-tts-turbo",
|
||||
"openrouter/openai/gpt-5.6-luna"
|
||||
"openrouter/openai/gpt-5.6-luna",
|
||||
"role/inka"
|
||||
],
|
||||
"persona-andreas": [],
|
||||
"nightjob": [
|
||||
|
||||
@@ -145,6 +145,9 @@ function jsonBody(orig: Record<string, unknown>, ziel: Ziel, pfad: string): Reco
|
||||
// Sprechtext vor der Synthese normalisieren (Postleitzahlen als Ziffernfolge mit Pause),
|
||||
// fuer jedes TTS-Ziel gleich -- die Modelle lesen Betraege und Daten, PLZ nicht.
|
||||
if (pfad === "/audio/speech" && typeof b.input === "string") b.input = sprechtextNormalisieren(b.input);
|
||||
// OpenRouter-TTS kennt nur mp3 und pcm. Clients wie Inkas Telefon-Front-End
|
||||
// verlangen WAV -- wir holen PCM und setzen den RIFF-Kopf selbst (s. unten).
|
||||
if (pfad === "/audio/speech" && ziel.upstreamName === "openrouter" && b.response_format === "wav") b.response_format = "pcm";
|
||||
if (pfad.startsWith("/messages")) {
|
||||
// Anthropic-Format: kein usage.include, Effort als reasoning.effort (OpenRouter nimmt es dort an)
|
||||
if (ziel.effortSetzen) { delete b.thinking; delete b.output_config; if (ziel.effort !== null) b.reasoning = { effort: ziel.effort }; }
|
||||
@@ -197,6 +200,18 @@ async function modellListe(keyName: string, auth: string): Promise<{ id: string;
|
||||
return data;
|
||||
}
|
||||
|
||||
/** Setzt einen RIFF/WAVE-Kopf (PCM 16 bit) vor rohe s16le-Daten. */
|
||||
function wavVerpacken(pcm: Uint8Array, rate: number, kanaele: number): Uint8Array {
|
||||
const kopf = new ArrayBuffer(44); const v = new DataView(kopf);
|
||||
const str = (o: number, t: string) => { for (let i = 0; i < t.length; i++) v.setUint8(o + i, t.charCodeAt(i)); };
|
||||
str(0, "RIFF"); v.setUint32(4, 36 + pcm.byteLength, true); str(8, "WAVE");
|
||||
str(12, "fmt "); v.setUint32(16, 16, true); v.setUint16(20, 1, true); v.setUint16(22, kanaele, true);
|
||||
v.setUint32(24, rate, true); v.setUint32(28, rate * kanaele * 2, true); v.setUint16(32, kanaele * 2, true); v.setUint16(34, 16, true);
|
||||
str(36, "data"); v.setUint32(40, pcm.byteLength, true);
|
||||
const aus = new Uint8Array(44 + pcm.byteLength); aus.set(new Uint8Array(kopf), 0); aus.set(pcm, 44);
|
||||
return aus;
|
||||
}
|
||||
|
||||
// --- Server -------------------------------------------------------------------
|
||||
Bun.serve({
|
||||
port: Number(process.env.PORT ?? config.port),
|
||||
@@ -224,6 +239,7 @@ Bun.serve({
|
||||
let angefragt = "";
|
||||
let upstreamBody: BodyInit;
|
||||
let stream = false;
|
||||
let wavGewuenscht = false;
|
||||
let ziel: Ziel;
|
||||
const headers = new Headers();
|
||||
|
||||
@@ -237,6 +253,7 @@ Bun.serve({
|
||||
if ("fehler" in z) return Response.json({ error: { message: z.fehler } }, { status: z.status });
|
||||
ziel = z;
|
||||
stream = orig.stream === true;
|
||||
wavGewuenscht = pfad === "/audio/speech" && orig.response_format === "wav";
|
||||
upstreamBody = JSON.stringify(ziel.upstream.clientKey === true ? orig : jsonBody(orig, ziel, pfad));
|
||||
headers.set("content-type", "application/json");
|
||||
} else {
|
||||
@@ -310,6 +327,16 @@ Bun.serve({
|
||||
return new Response(antwort.body.pipeThrough(tee), { status: antwort.status, headers: weiter });
|
||||
}
|
||||
freigeben();
|
||||
if (ct.startsWith("audio/pcm") && wavGewuenscht) {
|
||||
// PCM s16le -> WAV; Rate und Kanaele stehen im Content-Type (audio/pcm;rate=24000;channels=1)
|
||||
const rate = Number(/rate=(\d+)/u.exec(ct)?.[1] ?? 24000);
|
||||
const kanaele = Number(/channels=(\d+)/u.exec(ct)?.[1] ?? 1);
|
||||
const pcm = new Uint8Array(await antwort.arrayBuffer());
|
||||
const wav = wavVerpacken(pcm, rate, kanaele);
|
||||
weiter.set("content-type", "audio/wav"); weiter.set("content-length", String(wav.byteLength));
|
||||
abschliessen();
|
||||
return new Response(wav, { status: antwort.status, headers: weiter });
|
||||
}
|
||||
if (ct.includes("application/json")) {
|
||||
const text = await antwort.text();
|
||||
let usage: unknown; let fehler: string | undefined;
|
||||
|
||||
Reference in New Issue
Block a user