feat(diagnostic): Sprachmodell per Tier-Dropdown waehlen (Opus/Sonnet/Haiku)
ARIA laeuft ueber das Claude-Max-Abo via CLI — waehlbar ist der Tier, nicht eine feste Modellversion (die CLI nimmt automatisch das aktuelle Modell des Tiers). Kein API-Key → keine echte Anthropic-Models-API; daher kuratierte Liste. - proxy routes.js handleModels: kuratierte /v1/models mit tier/display_name/ description (ids bleiben MODEL_MAP-kompatibel: claude-sonnet-4/opus-4/haiku-4). - diagnostic server.js: neuer GET /api/models-list — holt die Liste vom Proxy (Frontend erreicht den Proxy nicht direkt). - Frontend: Dropdown statt Freitext + „↻ Aktualisieren"-Button; markiert das aktive brainModel (get_model), zeigt Beschreibung, „Setzen" schreibt brainModel und weist auf Brain-Neustart hin. Freitext-Override bleibt unter „Erweitert". Liste wird bei WS-Connect automatisch geladen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+78
-6
@@ -878,20 +878,32 @@
|
||||
<h2>Sprachmodell (Brain)</h2>
|
||||
<div class="card" style="max-width:500px;">
|
||||
<div style="font-size:11px;color:#8888AA;margin-bottom:10px;line-height:1.5;">
|
||||
Welches Claude-Model nutzt das Brain pro Anfrage. Wert wird in
|
||||
Welches Claude-Model das Brain pro Anfrage nutzt. Wert wird in
|
||||
<code>/shared/config/runtime.json</code> als <code>brainModel</code> persistiert.
|
||||
Bei Aenderung: <strong>aria-brain restarten</strong> (Reparatur-Section oben), damit's greift.
|
||||
<br><br>
|
||||
Verfuegbar via Proxy: <code>claude-sonnet-4</code> (Default — schnell, gut),
|
||||
<code>claude-opus-4</code> (langsam, smarter), <code>claude-haiku-4-5</code> (sehr schnell, kleiner Kontext).
|
||||
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
|
||||
jeweils aktuelle Modell des Tiers.
|
||||
</div>
|
||||
<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>
|
||||
<input type="text" id="setting-model" placeholder="claude-sonnet-4" style="flex:1;background:#1E1E2E;border:1px solid #333;border-radius:4px;padding:6px 8px;color:#E0E0F0;font-family:inherit;font-size:12px;">
|
||||
<button class="btn secondary" onclick="loadModel()" style="padding:4px 8px;font-size:10px;">Laden</button>
|
||||
<select id="setting-model-select" onchange="onModelSelectChange()" style="flex:1;background:#1E1E2E;border:1px solid #333;border-radius:4px;padding:6px 8px;color:#E0E0F0;font-family:inherit;font-size:12px;">
|
||||
<option value="">(lade Liste…)</option>
|
||||
</select>
|
||||
<button class="btn secondary" onclick="loadModelList()" title="Liste vom Proxy neu holen" style="padding:4px 8px;font-size:10px;">↻ Aktualisieren</button>
|
||||
<button class="btn" onclick="saveModel()" style="padding:4px 8px;font-size:10px;">Setzen</button>
|
||||
</div>
|
||||
<div id="model-status" style="font-size:10px;color:#8888AA;"></div>
|
||||
<div id="model-desc" style="font-size:10px;color:#6a6a88;margin-bottom:6px;min-height:12px;"></div>
|
||||
<div id="model-status" style="font-size:10px;color:#8888AA;margin-bottom:8px;"></div>
|
||||
<details style="font-size:11px;color:#8888AA;">
|
||||
<summary style="cursor:pointer;">Erweitert: freie Model-ID</summary>
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-top:6px;">
|
||||
<input type="text" id="setting-model" placeholder="z.B. claude-opus-4" style="flex:1;background:#1E1E2E;border:1px solid #333;border-radius:4px;padding:6px 8px;color:#E0E0F0;font-family:inherit;font-size:12px;">
|
||||
<button class="btn secondary" onclick="loadModel()" style="padding:4px 8px;font-size:10px;">Laden</button>
|
||||
<button class="btn" onclick="saveModelFreeText()" style="padding:4px 8px;font-size:10px;">Setzen</button>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1504,6 +1516,8 @@
|
||||
send({ action: 'load_chat_history' });
|
||||
// Brain-Card initial laden (sonst zeigt sie "Lade...")
|
||||
try { loadBrainStatus(); } catch {}
|
||||
// Sprachmodell-Dropdown befuellen (kuratierte Tier-Liste vom Proxy)
|
||||
try { loadModelList(); } catch {}
|
||||
};
|
||||
|
||||
// Brain-Status periodisch refreshen damit die Card live bleibt
|
||||
@@ -1966,8 +1980,14 @@
|
||||
// session_restarted / openclaw_config WS-Events entfernt — aria-core ist raus.
|
||||
if (msg.type === 'model_info') {
|
||||
const el = document.getElementById('setting-model');
|
||||
const sel = document.getElementById('setting-model-select');
|
||||
const st = document.getElementById('model-status');
|
||||
if (el && msg.model) el.value = msg.model;
|
||||
// Dropdown auf das aktuelle Model stellen (falls in der Liste).
|
||||
if (sel && msg.model) {
|
||||
const has = Array.from(sel.options).some(o => o.value === msg.model);
|
||||
if (has) { sel.value = msg.model; onModelSelectChange(); }
|
||||
}
|
||||
if (st) {
|
||||
st.textContent = msg.info || msg.error || '';
|
||||
st.style.color = msg.error ? '#FF6B6B' : '#34C759';
|
||||
@@ -6062,14 +6082,66 @@
|
||||
|
||||
// ── Einstellungen: Model ────────────────────────────────
|
||||
|
||||
let _modelListCache = [];
|
||||
|
||||
function loadModel() {
|
||||
send({ action: 'get_model' });
|
||||
}
|
||||
|
||||
// Kuratierte Tier-Liste vom Proxy holen (via Diagnostic-Server) und ins
|
||||
// Dropdown fuellen. Danach get_model, damit das aktive Model markiert wird.
|
||||
async function loadModelList() {
|
||||
const sel = document.getElementById('setting-model-select');
|
||||
const st = document.getElementById('model-status');
|
||||
if (!sel) return;
|
||||
try {
|
||||
const r = await fetch('/api/models-list');
|
||||
const d = await r.json();
|
||||
if (!d.ok) throw new Error(d.error || 'Fehler');
|
||||
_modelListCache = d.models || [];
|
||||
sel.innerHTML = '';
|
||||
for (const m of _modelListCache) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = m.id;
|
||||
opt.textContent = m.displayName || m.id;
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
if (!_modelListCache.length) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = ''; opt.textContent = '(keine Modelle vom Proxy)';
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
onModelSelectChange();
|
||||
// Aktuelles Model vom Brain holen → markiert die richtige Option
|
||||
loadModel();
|
||||
} catch (e) {
|
||||
if (st) { st.textContent = 'Model-Liste laden fehlgeschlagen: ' + e.message; st.style.color = '#FF6B6B'; }
|
||||
}
|
||||
}
|
||||
|
||||
function onModelSelectChange() {
|
||||
const sel = document.getElementById('setting-model-select');
|
||||
const desc = document.getElementById('model-desc');
|
||||
if (!sel || !desc) return;
|
||||
const m = _modelListCache.find(x => x.id === sel.value);
|
||||
desc.textContent = m && m.description ? m.description : '';
|
||||
}
|
||||
|
||||
function saveModel() {
|
||||
const sel = document.getElementById('setting-model-select');
|
||||
const model = sel && sel.value ? sel.value : '';
|
||||
if (!model) return;
|
||||
send({ action: 'set_model', model });
|
||||
const st = document.getElementById('model-status');
|
||||
if (st) { st.textContent = 'Gesetzt — aria-brain neu starten (Reparatur oben), damit es greift.'; st.style.color = '#FFD60A'; }
|
||||
}
|
||||
|
||||
function saveModelFreeText() {
|
||||
const model = document.getElementById('setting-model').value.trim();
|
||||
if (!model) return;
|
||||
send({ action: 'set_model', model });
|
||||
const st = document.getElementById('model-status');
|
||||
if (st) { st.textContent = 'Gesetzt (Freitext) — aria-brain neu starten, damit es greift.'; st.style.color = '#FFD60A'; }
|
||||
}
|
||||
|
||||
// ── Einstellungen: OpenClaw Config ──────────────────────
|
||||
|
||||
@@ -1625,6 +1625,28 @@ const server = http.createServer((req, res) => {
|
||||
res.end(JSON.stringify({ ok: false, error: err.message }));
|
||||
}
|
||||
return;
|
||||
} else if (req.url === "/api/models-list" && req.method === "GET") {
|
||||
// Kuratierte Model-Liste vom Proxy (/v1/models) — Tier-Auswahl fuers
|
||||
// Sprachmodell-Dropdown. ARIA laeuft ueber das Max-Abo/CLI, waehlbar ist
|
||||
// der Tier (opus/sonnet/haiku), keine feste Version.
|
||||
(async () => {
|
||||
try {
|
||||
const r = await fetch(`${PROXY_URL}/v1/models`);
|
||||
const d = await r.json();
|
||||
const models = (d.data || []).map(m => ({
|
||||
id: m.id,
|
||||
tier: m.tier || m.id,
|
||||
displayName: m.display_name || m.id,
|
||||
description: m.description || "",
|
||||
}));
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: true, models }));
|
||||
} catch (err) {
|
||||
res.writeHead(502, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: false, error: String(err && err.message || err) }));
|
||||
}
|
||||
})();
|
||||
return;
|
||||
} else if (req.url === "/api/files-list" && req.method === "GET") {
|
||||
// Liste alle Dateien in /shared/uploads/ — die kommen entweder vom User
|
||||
// (Upload aus App/Diagnostic) oder von ARIA (aria_<name>.<ext> Pattern).
|
||||
|
||||
+18
-12
@@ -453,26 +453,32 @@ async function handleNonStreamingResponse(res, subprocess, cliInput, requestId)
|
||||
* Returns available 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);
|
||||
res.json({
|
||||
object: "list",
|
||||
data: [
|
||||
{
|
||||
id: "claude-opus-4",
|
||||
object: "model",
|
||||
owned_by: "anthropic",
|
||||
created: Math.floor(Date.now() / 1000),
|
||||
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-sonnet-4",
|
||||
object: "model",
|
||||
owned_by: "anthropic",
|
||||
created: Math.floor(Date.now() / 1000),
|
||||
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: Math.floor(Date.now() / 1000),
|
||||
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.",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user