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
+3
View File
@@ -885,6 +885,9 @@
ARIA laeuft ueber dein Claude-Max-Abo (CLI) — waehlbar ist der <strong>Tier</strong> ARIA laeuft ueber dein Claude-Max-Abo (CLI) — waehlbar ist der <strong>Tier</strong>
(Opus/Sonnet/Haiku), nicht eine feste Version. Die CLI nimmt automatisch das (Opus/Sonnet/Haiku), nicht eine feste Version. Die CLI nimmt automatisch das
jeweils aktuelle Modell des Tiers. jeweils aktuelle Modell des Tiers.
<br><br>
Die Auswahlliste kommt aus <code>/shared/config/models.json</code> — dort kannst du
Tiers/Beschreibungen anpassen (kein Neustart noetig, danach „↻ Aktualisieren").
</div> </div>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
<span style="font-size:12px;color:#8888AA;white-space:nowrap;">Aktives Model:</span> <span style="font-size:12px;color:#8888AA;white-space:nowrap;">Aktives Model:</span>
+55 -25
View File
@@ -19,6 +19,7 @@
*/ */
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import http from "http"; import http from "http";
import fs from "fs";
import { ClaudeSubprocess } from "../subprocess/manager.js"; import { ClaudeSubprocess } from "../subprocess/manager.js";
import { openaiToCli } from "../adapter/openai-to-cli.js"; import { openaiToCli } from "../adapter/openai-to-cli.js";
import { cliResultToOpenai, createDoneChunk, } from "../adapter/cli-to-openai.js"; import { cliResultToOpenai, createDoneChunk, } from "../adapter/cli-to-openai.js";
@@ -452,35 +453,64 @@ async function handleNonStreamingResponse(res, subprocess, cliInput, requestId)
* *
* Returns available models * 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) { 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 created = Math.floor(Date.now() / 1000);
const models = _loadModels();
res.json({ res.json({
object: "list", object: "list",
data: [ data: models.map(m => ({
{ id: m.id,
id: "claude-sonnet-4", object: "model", owned_by: "anthropic", created, object: "model",
tier: "sonnet", owned_by: "anthropic",
display_name: "Sonnet (aktuell: Sonnet 5)", created,
description: "Schnell & gut — Standard fuer den Alltag.", tier: m.tier || m.id,
}, display_name: m.display_name || m.id,
{ description: m.description || "",
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.",
},
],
}); });
} }
/** /**