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:
@@ -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
@@ -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
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user