From 1972c4d1b4cbf8da7846017f0c2c41a3e0b4121e Mon Sep 17 00:00:00 2001 From: duffyduck Date: Sun, 29 Mar 2026 03:18:02 +0200 Subject: [PATCH] fixed chat textjson format, selected session for all, fixed android echo --- android/src/screens/ChatScreen.tsx | 25 +++++++++++++++++-------- bridge/aria_bridge.py | 21 ++++++++++++++++++++- diagnostic/server.js | 16 +++++++++++++--- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/android/src/screens/ChatScreen.tsx b/android/src/screens/ChatScreen.tsx index 88409a7..ab4ec03 100644 --- a/android/src/screens/ChatScreen.tsx +++ b/android/src/screens/ChatScreen.tsx @@ -96,14 +96,23 @@ const ChatScreen: React.FC = () => { const sender = (message.payload.sender as string) || ''; if (sender === 'user' || sender === 'diagnostic') return; - const ariaMsg: ChatMessage = { - id: nextId(), - sender: 'aria', - text: (message.payload.text as string) || '', - timestamp: message.timestamp, - attachments: message.payload.attachments as Attachment[] | undefined, - }; - setMessages(prev => [...prev, ariaMsg]); + const text = (message.payload.text as string) || ''; + const ts = message.timestamp; + // Duplikat-Schutz: gleicher Text innerhalb 5s ignorieren + setMessages(prev => { + const isDuplicate = prev.some(m => + m.sender === 'aria' && m.text === text && Math.abs(m.timestamp - ts) < 5000 + ); + if (isDuplicate) return prev; + const ariaMsg: ChatMessage = { + id: nextId(), + sender: 'aria', + text, + timestamp: ts, + attachments: message.payload.attachments as Attachment[] | undefined, + }; + return [...prev, ariaMsg]; + }); } // TTS-Audio abspielen wenn vorhanden diff --git a/bridge/aria_bridge.py b/bridge/aria_bridge.py index 82627d5..932bb13 100644 --- a/bridge/aria_bridge.py +++ b/bridge/aria_bridge.py @@ -30,6 +30,7 @@ import wave from pathlib import Path from typing import Optional +import urllib.request import numpy as np import sounddevice as sd import websockets @@ -59,6 +60,7 @@ RVS_PORT = os.getenv("RVS_PORT", "443") # Port des RVS RVS_TLS = os.getenv("RVS_TLS", "true") # true = wss://, false = ws:// RVS_TLS_FALLBACK = os.getenv("RVS_TLS_FALLBACK", "true") # Bei TLS-Fehler ws:// versuchen RVS_TOKEN = os.getenv("RVS_TOKEN", "") # Pairing-Token (gleich wie in der App) +DIAGNOSTIC_URL = os.getenv("DIAGNOSTIC_URL", "http://127.0.0.1:3001") # Diagnostic API WHISPER_MODEL = os.getenv("WHISPER_MODEL", "small") WHISPER_LANGUAGE = os.getenv("WHISPER_LANGUAGE", "de") @@ -409,7 +411,8 @@ class ARIABridge: self.ws_url = self.config.get("ARIA_CORE_WS", CORE_WS_URL) self.core_auth_token = self.config.get("ARIA_AUTH_TOKEN", CORE_AUTH_TOKEN) self._req_id_counter = 0 - self._session_key = "aria-bridge" # Feste Session fuer die Bridge + self._session_key = "main" # Fallback, wird per Diagnostic API aktualisiert + self._diagnostic_url = self.config.get("DIAGNOSTIC_URL", DIAGNOSTIC_URL) # RVS-Verbindungsinfo aus Config oder Env rvs_host = self.config.get("RVS_HOST", RVS_HOST) rvs_port = self.config.get("RVS_PORT", RVS_PORT) @@ -790,12 +793,28 @@ class ARIABridge: else: logger.info("[core] TTS unterdrueckt (Modus: %s)", self.current_mode.config.name) + def _fetch_active_session(self) -> None: + """Holt die aktive Session vom Diagnostic-Endpoint.""" + try: + req = urllib.request.Request(f"{self._diagnostic_url}/api/session", method="GET") + with urllib.request.urlopen(req, timeout=2) as resp: + data = json.loads(resp.read().decode()) + new_key = data.get("sessionKey", "") + if new_key and new_key != self._session_key: + logger.info("[session] Aktive Session gewechselt: %s -> %s", self._session_key, new_key) + self._session_key = new_key + except Exception as e: + logger.debug("[session] Diagnostic nicht erreichbar (%s) — nutze '%s'", e, self._session_key) + async def send_to_core(self, text: str, source: str = "bridge") -> None: """Sendet Text an aria-core (OpenClaw chat.send Protokoll).""" if self.ws_core is None: logger.error("[core] Nicht verbunden — Nachricht verworfen: '%s'", text[:60]) return + # Aktive Session vom Diagnostic holen + self._fetch_active_session() + req_id = self._next_req_id() message = json.dumps({ "type": "req", diff --git a/diagnostic/server.js b/diagnostic/server.js index 22336a6..b19284e 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -35,7 +35,10 @@ const state = { rvs: { status: "disconnected", lastError: null }, proxy: { status: "unknown", lastError: null }, }; -let activeSessionKey = "aria-diag-v3"; +const SESSION_KEY_FILE = "/tmp/aria-active-session"; +let activeSessionKey = (() => { + try { return fs.readFileSync(SESSION_KEY_FILE, "utf-8").trim(); } catch { return "main"; } +})(); const logs = []; let gatewayWs = null; let rvsWs = null; @@ -924,6 +927,9 @@ const server = http.createServer((req, res) => { } else if (req.url === "/api/state") { res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ state, logs: logs.slice(-100) })); + } else if (req.url === "/api/session") { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ sessionKey: activeSessionKey })); } else { res.writeHead(404); res.end("Not Found"); @@ -1375,8 +1381,10 @@ async function handleLoadChatHistory(clientWs) { if (!text) continue; if (role === "user") { - // Metadata-Prefix entfernen: "Sender (untrusted metadata):\n```json\n{...}\n```\n\n[timestamp] " - text = text.replace(/^Sender \(untrusted metadata\):[\s\S]*?```\s*\n*(?:\[.*?\]\s*)?/m, "").trim(); + // Metadata-Prefix entfernen: "Sender (untrusted metadata):\n```json\n{...}\n```\n\n[timestamp] Text" + text = text.replace(/^Sender \(untrusted metadata\):[\s\S]*?```[\s\S]*?```\s*\n*/m, "").trim(); + // Timestamp-Prefix entfernen: "[Sat 2026-03-28 14:51 UTC] " + text = text.replace(/^\[.*?\]\s*/, "").trim(); chatMessages.push({ type: "sent", text, meta: "Gateway direkt", ts: msg.timestamp || obj.timestamp || 0 }); } else if (role === "assistant") { // Reply-Prefix entfernen: "[[reply_to_current]] " @@ -1401,6 +1409,7 @@ function handleSetActiveSession(clientWs, sessionKey) { return; } activeSessionKey = sessionKey; + try { fs.writeFileSync(SESSION_KEY_FILE, activeSessionKey); } catch {} log("info", "server", `Aktive Session: ${activeSessionKey}`); // Allen Clients mitteilen for (const c of browserClients) { @@ -1417,6 +1426,7 @@ async function handleCreateSession(clientWs, sessionName) { try { // Session wird automatisch erstellt wenn man die erste Nachricht sendet activeSessionKey = sessionName; + try { fs.writeFileSync(SESSION_KEY_FILE, activeSessionKey); } catch {} log("info", "server", `Neue Session erstellt und aktiviert: ${sessionName}`); // Allen Clients mitteilen for (const c of browserClients) {