From 9cad6310158454e44da637494bb05029cd4828c2 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Fri, 13 Mar 2026 08:14:52 +0100 Subject: [PATCH] =?UTF-8?q?diagnostic/server.js=20=E2=80=94=20Handler=20um?= =?UTF-8?q?gebaut:=20event:=20"agent"=20f=C3=BCr=20Deltas,=20event:=20"cha?= =?UTF-8?q?t"=20mit=20state:=20"final"=20f=C3=BCr=20Antworten,=20extractCh?= =?UTF-8?q?atText()=20parst=20das=20content[]=20Array=20bridge/aria=5Fbrid?= =?UTF-8?q?ge.py=20=E2=80=94=20Gleicher=20Fix:=20=5Fextract=5Fchat=5Ftext(?= =?UTF-8?q?)=20Methode,=20neue=20Event-Handler=20f=C3=BCr=20agent=20und=20?= =?UTF-8?q?chat=20mit=20state,=20Legacy-Namen=20als=20Fallback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 9 +++++ bridge/aria_bridge.py | 74 ++++++++++++++++++++++++++++++----- diagnostic/server.js | 89 +++++++++++++++++++++++++++++-------------- 3 files changed, 133 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 439e6cf..90e99e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,15 @@ Alle Änderungen am Projekt. Format: [Keep a Changelog](https://keepachangelog.c - 60s Timeout — markiert Pipeline als fehlgeschlagen wenn keine Antwort kommt - Funktioniert für Gateway-direkt und RVS-Nachrichten +### Behoben + +**OpenClaw Gateway Event-Format — ARIA antwortet jetzt** +- OpenClaw sendet `event: "agent"` (Streaming-Deltas in `payload.data.delta`) und `event: "chat"` mit `payload.state: "delta"|"final"|"error"` — **nicht** `chat:delta`/`chat:final`/`chat:error` wie angenommen +- Antworttext steckt in `payload.message.content[0].text` (Array von Content-Blöcken, nicht flacher String) — `text.slice is not a function` Fehler behoben +- `ackReactionScope` von `"group-mentions"` auf `"all"` geändert — Agent reagierte nur auf @mentions, nicht auf direkte Nachrichten +- Diagnostic Server und Bridge auf neues Event-Format umgestellt +- Legacy-Event-Namen (`chat:delta`, `chat:final`, `chat:error`) als Fallback beibehalten + ### Geändert **OpenClaw Config — Custom Provider Format** diff --git a/bridge/aria_bridge.py b/bridge/aria_bridge.py index 7f7b6a1..3f8283b 100644 --- a/bridge/aria_bridge.py +++ b/bridge/aria_bridge.py @@ -625,28 +625,64 @@ class ARIABridge: event_name = message.get("event", "") payload = message.get("payload", {}) - if event_name == "chat:delta": - # Streaming-Delta — fuer spaeter (Live-Typing in der App) - delta = payload.get("delta", payload.get("text", "")) - if delta: + # ── agent Events: Streaming-Deltas vom LLM ── + if event_name == "agent": + data = payload.get("data", {}) + delta = data.get("delta", "") + if delta and payload.get("stream") == "assistant": logger.debug("[core] Delta: '%s'", delta[:40]) return + # ── chat Events: Snapshots mit state=delta|final|error ── + if event_name == "chat": + state = payload.get("state", "") + + if state == "final": + text = self._extract_chat_text(payload) + if not text: + logger.warning("[core] chat final ohne Text: %s", json.dumps(payload)[:200]) + return + logger.info("[core] Antwort: '%s'", text[:80]) + await self._process_core_response(text, payload) + return + + if state == "error": + error = payload.get("error", "Unbekannt") + logger.error("[core] Chat-Fehler: %s", error) + await self._send_to_rvs({ + "type": "chat", + "payload": { + "text": f"[Fehler] {error}", + "sender": "aria", + }, + "timestamp": int(asyncio.get_event_loop().time() * 1000), + }) + return + + # state=delta — periodischer Snapshot, ignorieren + return + + # ── Legacy event names (chat:delta, chat:final, chat:error) ── + if event_name == "chat:delta": + delta = payload.get("delta", payload.get("text", "")) + if delta: + logger.debug("[core] Delta (legacy): '%s'", delta[:40]) + return + if event_name == "chat:final": - # Fertige Antwort von aria-core text = payload.get("text", payload.get("message", "")) + if not text: + text = self._extract_chat_text(payload) if not text: logger.warning("[core] chat:final ohne Text: %s", json.dumps(payload)[:200]) return - - logger.info("[core] Antwort: '%s'", text[:80]) + logger.info("[core] Antwort (legacy): '%s'", text[:80]) await self._process_core_response(text, payload) return if event_name == "chat:error": error = payload.get("error", payload.get("message", "Unbekannt")) - logger.error("[core] Chat-Fehler: %s", error) - # Fehler auch an die App melden + logger.error("[core] Chat-Fehler (legacy): %s", error) await self._send_to_rvs({ "type": "chat", "payload": { @@ -657,9 +693,27 @@ class ARIABridge: }) return - # Andere Events loggen (presence, tick, etc.) + # tick, health, etc. — ignorieren + if event_name in ("tick", "health"): + return + logger.debug("[core] Event: %s", event_name) + @staticmethod + def _extract_chat_text(payload: dict) -> str: + """Extrahiert Text aus OpenClaw chat-Event message.content Array.""" + try: + content = payload.get("message", {}).get("content", []) + if isinstance(content, list): + return "".join( + c.get("text", "") for c in content if c.get("type") == "text" + ) + if isinstance(content, str): + return content + except (AttributeError, TypeError): + pass + return payload.get("text", "") + async def _process_core_response(self, text: str, payload: dict) -> None: """Verarbeitet eine fertige Antwort von aria-core. diff --git a/diagnostic/server.js b/diagnostic/server.js index 608ef99..d583f5b 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -241,6 +241,21 @@ async function connectGateway() { } } +// Extrahiert Text aus OpenClaw chat-Event message.content Array +function extractChatText(payload) { + try { + const content = payload.message?.content; + if (Array.isArray(content)) { + return content + .filter(c => c.type === "text") + .map(c => c.text || "") + .join(""); + } + if (typeof payload.message === "string") return payload.message; + return payload.text || ""; + } catch { return ""; } +} + function handleGatewayMessage(msg) { if (msg.type === "res") { const status = msg.ok ? "OK" : `FEHLER: ${JSON.stringify(msg.error).slice(0, 100)}`; @@ -257,24 +272,59 @@ function handleGatewayMessage(msg) { const event = msg.event || "?"; const payload = msg.payload || {}; - if (event === "chat:delta") { - const delta = payload.delta || payload.text || ""; - if (delta) { - log("info", "gateway", `Delta: "${delta.slice(0, 60)}"`); - if (pipelineActive) plog(`Streaming Delta: "${delta.slice(0, 80)}"`); + // ── agent Events: Streaming-Deltas vom LLM ── + if (event === "agent") { + const data = payload.data || {}; + const delta = data.delta || ""; + if (delta && payload.stream === "assistant") { broadcast({ type: "chat_delta", delta, payload }); } + // agent Events nicht einzeln loggen (zu viele) return; } + // ── chat Events: Snapshots mit state=delta|final ── + if (event === "chat") { + const state = payload.state || ""; + const text = extractChatText(payload); + + if (state === "final") { + log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`); + if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`); + broadcast({ type: "chat_final", text, payload }); + return; + } + + if (state === "delta") { + // Periodischer Snapshot — nicht einzeln loggen + return; + } + + if (state === "error") { + const error = payload.error || text || "Unbekannt"; + log("error", "gateway", `Chat-Fehler: ${error}`); + if (pipelineActive) pipelineEnd(false, error); + broadcast({ type: "chat_error", error, payload }); + return; + } + + log("debug", "gateway", `chat state=${state}`); + return; + } + + // ── Legacy event names (chat:delta, chat:final, chat:error) ── + if (event === "chat:delta") { + const delta = payload.delta || payload.text || ""; + if (delta) broadcast({ type: "chat_delta", delta, payload }); + return; + } if (event === "chat:final") { - const text = payload.text || payload.message || ""; + const text = extractChatText(payload) || payload.text || ""; log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`); if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`); broadcast({ type: "chat_final", text, payload }); return; } - if (event === "chat:error") { const error = payload.error || payload.message || "Unbekannt"; log("error", "gateway", `Chat-Fehler: ${error}`); @@ -283,28 +333,9 @@ function handleGatewayMessage(msg) { return; } - // Alle anderen Events — vollstaendig loggen fuer Debugging - const payloadStr = JSON.stringify(payload).slice(0, 500); - log("info", "gateway", `Event: ${event} | ${payloadStr}`); - if (pipelineActive) plog(`Gateway Event: ${event} | ${payloadStr.slice(0, 200)}`); - - // "chat" Event koennte die Antwort sein (anderes Format als erwartet) - if (event === "chat") { - const text = payload.text || payload.message || payload.content || ""; - const delta = payload.delta || ""; - const error = payload.error || ""; - if (error) { - log("error", "gateway", `Chat-Fehler (event:chat): ${error}`); - if (pipelineActive) pipelineEnd(false, error); - broadcast({ type: "chat_error", error, payload }); - } else if (text) { - log("info", "gateway", `ANTWORT (event:chat): "${text.slice(0, 200)}"`); - if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`); - broadcast({ type: "chat_final", text, payload }); - } else if (delta) { - broadcast({ type: "chat_delta", delta, payload }); - } - } + // ── Andere Events (tick, health, presence) ── + if (event === "tick" || event === "health") return; // Noise + log("debug", "gateway", `Event: ${event}`); } }