fix(diagnostic+brain): Sprachmodell-Einstellung auf runtime.json umgestellt

War kaputt nach OpenClaw-Abriss: handleGetModel/handleSetModel haben gegen
aria-core (dockerExec + node-script in den Container) gearbeitet, der gibt's
nicht mehr.

diagnostic/server.js
  - handleGetModel/handleSetModel lesen/schreiben jetzt brainModel in
    /shared/config/runtime.json
  - RUNTIME_CONFIG_FIELDS um "brainModel" erweitert
  - Tote Variante (findSettingsFile + base64-node-script) komplett raus

aria-brain/proxy_client.py
  - Liest brainModel aus runtime.json beim Container-Start
  - Fallback: BRAIN_MODEL env → "claude-sonnet-4" Default
  - Bei Aenderung in Diagnostic: aria-brain restarten damit's greift
    (Hinweis steht in der UI)

diagnostic/index.html
  - Section "Model" → "Sprachmodell (Brain)"
  - Hinweis-Block mit Default-Erklaerung und Restart-Hinweis
  - Modelle: claude-sonnet-4 (default), claude-opus-4, claude-haiku-4-5

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 22:36:14 +02:00
parent 094bd6e4f1
commit aa077f60e6
3 changed files with 53 additions and 25 deletions
+20 -1
View File
@@ -9,8 +9,10 @@ neuen CLI-Prozess (Cold-Start), das dauert.
from __future__ import annotations from __future__ import annotations
import json
import logging import logging
import os import os
from pathlib import Path
from typing import List, Optional from typing import List, Optional
import httpx import httpx
@@ -18,11 +20,28 @@ from pydantic import BaseModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
DEFAULT_MODEL = os.environ.get("BRAIN_MODEL", "claude-sonnet-4") RUNTIME_CONFIG_FILE = Path("/shared/config/runtime.json")
ENV_MODEL = os.environ.get("BRAIN_MODEL", "claude-sonnet-4")
PROXY_URL = os.environ.get("PROXY_URL", "http://proxy:3456") PROXY_URL = os.environ.get("PROXY_URL", "http://proxy:3456")
PROXY_TIMEOUT_SEC = float(os.environ.get("PROXY_TIMEOUT_SEC", "300")) PROXY_TIMEOUT_SEC = float(os.environ.get("PROXY_TIMEOUT_SEC", "300"))
def _read_model_from_runtime() -> str:
"""Liest brainModel aus runtime.json. Fallback: ENV BRAIN_MODEL."""
try:
if RUNTIME_CONFIG_FILE.exists():
data = json.loads(RUNTIME_CONFIG_FILE.read_text(encoding="utf-8"))
m = (data.get("brainModel") or "").strip()
if m:
return m
except Exception as exc:
logger.warning("runtime.json lesen fehlgeschlagen: %s", exc)
return ENV_MODEL
DEFAULT_MODEL = _read_model_from_runtime()
class Message(BaseModel): class Message(BaseModel):
role: str # "system" | "user" | "assistant" | "tool" role: str # "system" | "user" | "assistant" | "tool"
content: Optional[str] = None content: Optional[str] = None
+11 -3
View File
@@ -659,13 +659,21 @@
selbst (Skills + skill_create Meta-Tool). Es gibt keine granulare selbst (Skills + skill_create Meta-Tool). Es gibt keine granulare
Permission-Maske, Brain weiss zur Laufzeit welche Tools es hat. --> Permission-Maske, Brain weiss zur Laufzeit welche Tools es hat. -->
<!-- Model-Einstellungen --> <!-- Brain-Model-Einstellung -->
<div class="settings-section"> <div class="settings-section">
<h2>Model</h2> <h2>Sprachmodell (Brain)</h2>
<div class="card" style="max-width:500px;"> <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
<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).
</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>
<input type="text" id="setting-model" placeholder="z.B. proxy/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;"> <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> <button class="btn secondary" onclick="loadModel()" style="padding:4px 8px;font-size:10px;">Laden</button>
<button class="btn" onclick="saveModel()" style="padding:4px 8px;font-size:10px;">Setzen</button> <button class="btn" onclick="saveModel()" style="padding:4px 8px;font-size:10px;">Setzen</button>
</div> </div>
+22 -21
View File
@@ -65,6 +65,7 @@ const RUNTIME_CONFIG_FILE = "/shared/config/runtime.json";
const RUNTIME_CONFIG_FIELDS = [ const RUNTIME_CONFIG_FIELDS = [
"RVS_HOST", "RVS_PORT", "RVS_TLS", "RVS_TOKEN", "RVS_HOST", "RVS_PORT", "RVS_TLS", "RVS_TOKEN",
"ARIA_AUTH_TOKEN", "WHISPER_MODEL", "WHISPER_LANGUAGE", "ARIA_AUTH_TOKEN", "WHISPER_MODEL", "WHISPER_LANGUAGE",
"brainModel",
]; ];
function readRuntimeConfig() { function readRuntimeConfig() {
const envDefaults = { const envDefaults = {
@@ -2164,42 +2165,42 @@ async function handleRestartSession(clientWs) {
// Claude Code laeuft mit --dangerously-skip-permissions (Alles oder Nichts). // Claude Code laeuft mit --dangerously-skip-permissions (Alles oder Nichts).
// Root-Check wird via CLAUDE_CODE_BUBBLEWRAP=1 in docker-compose.yml umgangen. // Root-Check wird via CLAUDE_CODE_BUBBLEWRAP=1 in docker-compose.yml umgangen.
// ── Einstellungen: Model ──────────────────────────────── // ── Einstellungen: Model (Brain) ────────────────────────
// Liest/schreibt brainModel in /shared/config/runtime.json — Brain liest
// das beim Container-Start. Bei Aenderung: aria-brain restarten
// (Einstellungen → Reparatur → 🚨 aria-brain neu).
async function handleGetModel(clientWs) { function handleGetModel(clientWs) {
try { try {
const raw = await dockerExec("aria-core", `echo $DEFAULT_MODEL`); const cfg = readRuntimeConfig();
clientWs.send(JSON.stringify({ type: "model_info", model: raw.trim(), info: "Aktuelles Model (ENV)" })); const model = (cfg.brainModel || "").trim() || "claude-sonnet-4";
clientWs.send(JSON.stringify({
type: "model_info",
model,
info: "Brain-Model (runtime.json) — bei Aenderung: aria-brain restarten",
}));
} catch (err) { } catch (err) {
clientWs.send(JSON.stringify({ type: "model_info", error: err.message })); clientWs.send(JSON.stringify({ type: "model_info", error: err.message }));
} }
} }
async function handleSetModel(clientWs, model) { function handleSetModel(clientWs, model) {
if (!model || typeof model !== "string") { if (!model || typeof model !== "string") {
clientWs.send(JSON.stringify({ type: "model_info", error: "Kein Model angegeben" })); clientWs.send(JSON.stringify({ type: "model_info", error: "Kein Model angegeben" }));
return; return;
} }
try { try {
// Model in Settings speichern (OpenClaw liest das) writeRuntimeConfig({ brainModel: model.trim() });
const settingsPath = await findSettingsFile(); log("info", "server", `Brain-Model gesetzt: ${model.trim()}`);
const script = [ clientWs.send(JSON.stringify({
'const fs=require("fs");', type: "model_info",
`const f="${settingsPath}";`, model: model.trim(),
'let s={};try{s=JSON.parse(fs.readFileSync(f,"utf8"));}catch(e){}', info: `Model auf "${model.trim()}" gesetzt — aria-brain neu starten damit's greift`,
`s.model=${JSON.stringify(model)};`, }));
`const dir=f.substring(0,f.lastIndexOf("/"));`,
'try{fs.mkdirSync(dir,{recursive:true});}catch(e){}',
'fs.writeFileSync(f,JSON.stringify(s,null,2));',
].join("");
const b64 = Buffer.from(script).toString("base64");
await dockerExec("aria-core", `echo ${b64} | base64 -d | node`);
clientWs.send(JSON.stringify({ type: "model_info", model, info: `Model auf "${model}" gesetzt (Neustart noetig)` }));
log("info", "server", `Model gesetzt: ${model}`);
} catch (err) { } catch (err) {
clientWs.send(JSON.stringify({ type: "model_info", error: err.message })); clientWs.send(JSON.stringify({ type: "model_info", error: err.message }));
} }
return;
} }
// OpenClaw-Config-Handler entfernt — aria-core ist raus. // OpenClaw-Config-Handler entfernt — aria-core ist raus.