feat(diagnostic): Model-Tier-Liste aus /shared/config/models.json (editierbar ohne Code)

handleModels liest die kuratierte Tier-Liste jetzt pro Request aus
/shared/config/models.json statt sie hart zu codieren. Neue Tier-Namen oder
angepasste Beschreibungen sind damit eine reine Datei-Aenderung — kein
Code-Edit, kein Proxy-Neubau, kein Neustart (Datei bearbeiten → im Diagnostic
„↻ Aktualisieren").

- Fehlt/kaputt die Datei: eingebaute Defaults greifen und werden einmalig als
  models.json angelegt, damit es was zu editieren gibt.
- ids bleiben MODEL_MAP-kompatibel (extractModel), Validierung: nicht-leeres
  Array mit String-id, sonst Fallback auf Defaults.
- UI-Hinweis auf den Dateipfad ergaenzt.
- Load/Seed-Logik simuliert verifiziert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 22:17:38 +02:00
co-authored by Claude Opus 4.8
parent 44f4067834
commit de4d95a392
2 changed files with 58 additions and 25 deletions
+55 -25
View File
@@ -19,6 +19,7 @@
*/
import { v4 as uuidv4 } from "uuid";
import http from "http";
import fs from "fs";
import { ClaudeSubprocess } from "../subprocess/manager.js";
import { openaiToCli } from "../adapter/openai-to-cli.js";
import { cliResultToOpenai, createDoneChunk, } from "../adapter/cli-to-openai.js";
@@ -452,35 +453,64 @@ async function handleNonStreamingResponse(res, subprocess, cliInput, requestId)
*
* Returns available models
*/
// Kuratierte Tier-Liste. ARIA laeuft ueber das Claude-Max-Abo via CLI —
// waehlbar ist der TIER (opus/sonnet/haiku), nicht eine feste Modellversion;
// die CLI loest den Alias aufs aktuelle Modell des Tiers auf. Die id-Strings
// muessen von openai-to-cli.js extractModel() erkannt werden (MODEL_MAP).
//
// Quelle: /shared/config/models.json — damit neue Tier-Namen oder angepasste
// Beschreibungen eine reine DATEI-Aenderung sind (kein Code-Edit, kein Neubau,
// kein Neustart: handleModels liest pro Request neu; einfach die Datei
// bearbeiten und im Diagnostic „Aktualisieren" druecken). Fehlt/kaputt die
// Datei, greifen die eingebauten Defaults; die Datei wird dann einmalig mit
// diesen Defaults angelegt, damit es was zu editieren gibt.
const MODELS_FILE = process.env.ARIA_MODELS_FILE || "/shared/config/models.json";
const DEFAULT_MODELS = [
{ id: "claude-sonnet-4", tier: "sonnet", display_name: "Sonnet (aktuell: Sonnet 5)",
description: "Schnell & gut — Standard fuer den Alltag." },
{ id: "claude-opus-4", tier: "opus", display_name: "Opus (aktuell: Opus 4.8)",
description: "Langsamer, aber am schlausten — fuer schwere/lange Aufgaben." },
{ id: "claude-haiku-4", tier: "haiku", display_name: "Haiku (aktuell: Haiku 4.5)",
description: "Sehr schnell & guenstig, kleinerer Kontext — fuer einfache Tasks." },
];
function _loadModels() {
try {
const raw = fs.readFileSync(MODELS_FILE, "utf-8");
const arr = JSON.parse(raw);
if (Array.isArray(arr) && arr.length && arr.every(m => m && typeof m.id === "string")) {
return arr;
}
console.error("[aria-models] models.json ungueltig — nutze Defaults");
} catch (_) {
// Datei fehlt (oder unlesbar) → Defaults + einmalig seeden zum Editieren
try {
fs.mkdirSync("/shared/config", { recursive: true });
if (!fs.existsSync(MODELS_FILE)) {
fs.writeFileSync(MODELS_FILE, JSON.stringify(DEFAULT_MODELS, null, 2));
console.error("[aria-models] models.json mit Defaults angelegt:", MODELS_FILE);
}
} catch (e) {
console.error("[aria-models] Seeden fehlgeschlagen:", e && e.message);
}
}
return DEFAULT_MODELS;
}
export function handleModels(_req, res) {
// ARIA laeuft ueber das Claude-Max-Abo via CLI — waehlbar ist der TIER
// (opus/sonnet/haiku), nicht eine feste Modellversion. Die CLI loest den
// Alias auf das AKTUELLE Modell des Tiers auf. Die id-Strings muessen von
// openai-to-cli.js extractModel() erkannt werden (MODEL_MAP). display_name/
// tier/description sind ARIA-Zusatzfelder fuers Diagnostic-Dropdown.
const created = Math.floor(Date.now() / 1000);
const models = _loadModels();
res.json({
object: "list",
data: [
{
id: "claude-sonnet-4", object: "model", owned_by: "anthropic", created,
tier: "sonnet",
display_name: "Sonnet (aktuell: Sonnet 5)",
description: "Schnell & gut — Standard fuer den Alltag.",
},
{
id: "claude-opus-4", object: "model", owned_by: "anthropic", created,
tier: "opus",
display_name: "Opus (aktuell: Opus 4.8)",
description: "Langsamer, aber am schlausten — fuer schwere/lange Aufgaben.",
},
{
id: "claude-haiku-4", object: "model", owned_by: "anthropic", created,
tier: "haiku",
display_name: "Haiku (aktuell: Haiku 4.5)",
description: "Sehr schnell & guenstig, kleinerer Kontext — fuer einfache Tasks.",
},
],
data: models.map(m => ({
id: m.id,
object: "model",
owned_by: "anthropic",
created,
tier: m.tier || m.id,
display_name: m.display_name || m.id,
description: m.description || "",
})),
});
}
/**