feat(flux): Modell-Wahl per Diagnostic + raw/switch-Keywords + Download-Hinweis

Diagnostic-Einstellungen fuer FLUX:
- Default-Modell (dev | schnell) — wird via RVS gepusht, flux-bridge
  hot-swappt die Pipeline aus dem HF-Cache (~15-30s)
- Raw-Keyword (Default 'flux') — Pipe-Modus, Brain leitet Stefans Text
  1:1 als prompt durch, kein Rewriting/Beautify
- Switch-Keyword (Default 'fix') — zwingt das ANDERE Modell als Default

Brain-Tool flux_generate um model + raw erweitert, System-Prompt-Block
mit den aktuellen Diagnostic-Settings + Whisper-Toleranz-Hinweis.

Kein eager Bootstrap-Load: flux-bridge wartet auf config oder ersten
Request. Bei erstem HF-Download zeigt Banner "laedt erstmalig runter"
mit Pfeil-Icon, Toast in der App wenn fertig.

FLUX_MODEL aus der .env entfernt (Steuerung jetzt komplett ueber
Diagnostic). HF_TOKEN-Kommentar erklaert warum trotz lokaler Inference
noetig (HF Gate-Mechanismus fuer FLUX.1-dev).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 23:11:22 +02:00
parent 7e53dcfed3
commit 2d348aeec7
8 changed files with 440 additions and 72 deletions
+70 -8
View File
@@ -36,6 +36,27 @@ BRIDGE_URL = os.environ.get("BRIDGE_URL", "http://aria-bridge:8090")
# laedt die flux-bridge zudem ~24 GB Modell von HF (~5-10 min). Brain wartet
# synchron — Stefan kuendigt es vorher an wenn er weiss dass es feuert.
FLUX_HTTP_TIMEOUT_SEC = 1200
# Diagnostic-Settings fuer FLUX (Default-Modell + User-Keywords) liegen im
# selben File wie F5-TTS/Whisper Config — von der aria-bridge geschrieben.
VOICE_CONFIG_PATH = "/shared/config/voice_config.json"
def _load_flux_config() -> dict:
"""Liest fluxXxx-Felder aus der Voice-Config. Default-Werte wenn nichts
persistiert ist — Stefan hat in Diagnostic vielleicht noch nichts gesetzt."""
try:
with open(VOICE_CONFIG_PATH, encoding="utf-8") as f:
data = json.load(f) or {}
except (FileNotFoundError, json.JSONDecodeError):
data = {}
except Exception as exc:
logger.debug("Voice-Config lesen fehlgeschlagen: %s", exc)
data = {}
return {
"fluxDefaultModel": data.get("fluxDefaultModel", "dev"),
"fluxKeywordRaw": data.get("fluxKeywordRaw", "flux"),
"fluxKeywordSwitch": data.get("fluxKeywordSwitch", "fix"),
}
logger = logging.getLogger(__name__)
@@ -229,10 +250,10 @@ META_TOOLS = [
"function": {
"name": "flux_generate",
"description": (
"Generiere ein Bild aus einem Text-Prompt via FLUX.1-dev auf der Gamebox-"
"GPU. Brauchbar fuer 'mal mir ein X', 'wie sieht ein Y aus?', "
"Mockups, Konzept-Skizzen. Render dauert 20-90s — Stefan kuendigt "
"es an wenn er weiss dass es laeuft.\n\n"
"Generiere ein Bild aus einem Text-Prompt via FLUX auf der Gamebox-GPU. "
"Brauchbar fuer 'mal mir ein X', 'wie sieht ein Y aus?', Mockups, "
"Konzept-Skizzen, Memes. Render dauert 20-90s — kuendige es Stefan "
"kurz an, dann ist er nicht ueberrascht.\n\n"
"**Schreibe deine Antwort wie immer auf Deutsch**, und referenziere das "
"fertige Bild MIT dem `[FILE: ...]`-Marker, GENAU im Pfad-Format das das "
"Tool zurueckgibt. Beispiel:\n"
@@ -241,10 +262,26 @@ META_TOOLS = [
"inline als Anhang gezeigt.\n\n"
"**Prompt-Sprache: bevorzugt Englisch.** FLUX versteht zwar Deutsch, "
"liefert aber mit englischen Prompts deutlich konsistentere Ergebnisse. "
"Uebersetze Stefans deutsche Beschreibung selbststaendig.\n\n"
"Uebersetze Stefans deutsche Beschreibung selbststaendig — AUSSER `raw=true`.\n\n"
"**Modus `raw=true` (Pipe-Modus):** Wenn Stefan das Raw-Keyword aus dem "
"FLUX-Settings-Block im System-Prompt nutzt (typischerweise `flux`), "
"leite seinen Text 1:1 als prompt durch — KEIN Uebersetzen, KEIN "
"Beautify, KEINE Qualitaets-Keywords. Stefan formuliert dann selbst und "
"der Prompt geht roh an FLUX. Brauchbar wenn er den vollen Output ohne "
"ARIAs Filter haben will.\n\n"
"**Modell-Wahl (`model`):** \n"
"- `default` (oder weglassen): das in den Diagnostic-Settings eingestellte "
"Default-Modell (steht im FLUX-Block im System-Prompt).\n"
"- `dev`: hochqualitatives FLUX.1-dev, 20-90s, ~28 steps.\n"
"- `schnell`: FLUX.1-schnell, 4-step distillation, ~5-15s.\n"
"Wenn Stefan das Switch-Keyword (steht ebenfalls im FLUX-Block) im Prompt "
"verwendet → setze `model` auf das ANDERE Modell als das Default. Bei "
"'in hoher Qualitaet'/'detailliert' → `dev`. Bei 'schnell mal'/'fix' → `schnell`.\n\n"
"Modell-Switch kostet einmalig 15-30s (Pipeline-Reload aus HF-Cache). "
"Stefan sieht den Status im Diagnostic-Banner.\n\n"
"Caps:\n"
"- `width`/`height`: 256-1536, wird auf Vielfache von 64 gesnappt (Default 1024)\n"
"- `steps`: 1-50 (Default 28 fuer FLUX.1-dev, 4 fuer schnell)\n"
"- `steps`: 1-50 (Default 28 fuer dev, 4 fuer schnell)\n"
"- `guidance_scale`: 0.0-20.0 (Default 3.5)\n"
"- `seed`: optional, gleicher seed + gleicher prompt → gleiches Bild"
),
@@ -253,7 +290,22 @@ META_TOOLS = [
"properties": {
"prompt": {
"type": "string",
"description": "Englischer Bild-Prompt. So konkret wie moeglich (Motiv, Stil, Licht, Kamera).",
"description": (
"Bei raw=false (Default): englischer Bild-Prompt, von dir aus Stefans Worten gebaut, "
"mit Stil/Licht/Kamera-Stichworten. Bei raw=true: Stefans Text 1:1 ohne Aenderung."
),
},
"raw": {
"type": "boolean",
"description": (
"true = Pipe-Modus, kein Rewriting. Setzen wenn Stefan das Raw-Keyword "
"(siehe FLUX-Block im System-Prompt) am Anfang seiner Nachricht verwendet."
),
},
"model": {
"type": "string",
"enum": ["default", "dev", "schnell"],
"description": "Default-Modell oder explizit dev/schnell. Default = Diagnostic-Setting.",
},
"width": {"type": "integer", "description": "Breite in px (Default 1024, max 1536)"},
"height": {"type": "integer", "description": "Hoehe in px (Default 1024, max 1536)"},
@@ -487,10 +539,12 @@ class Agent:
condition_funcs = watcher_mod.describe_functions()
# 5. System-Prompt + Window-Messages
flux_config = _load_flux_config()
system_prompt = build_system_prompt(hot, cold, skills=all_skills,
triggers=all_triggers,
condition_vars=condition_vars,
condition_funcs=condition_funcs)
condition_funcs=condition_funcs,
flux_config=flux_config)
messages = [ProxyMessage(role="system", content=system_prompt)]
for t in self.conversation.window():
messages.append(ProxyMessage(role=t.role, content=t.content))
@@ -673,6 +727,14 @@ class Agent:
req["guidance_scale"] = float(arguments["guidance_scale"])
except (TypeError, ValueError):
pass
# Modell-Wahl: 'default' (oder weglassen) → flux-bridge nimmt Diagnostic-Default.
# 'dev' / 'schnell' → expliziter Override.
model_arg = (arguments.get("model") or "").strip().lower()
if model_arg in ("dev", "schnell"):
req["model"] = model_arg
# `raw` ist Brain-Domain (kein Rewriting des prompt) und wird hier
# nicht durchgereicht — der prompt enthaelt bei raw=true bereits
# Stefans Originaltext.
try:
body = json.dumps(req).encode("utf-8")
http_req = urllib.request.Request(