fixed chat textjson format, selected session for all, fixed android echo

This commit is contained in:
2026-03-29 03:18:02 +02:00
parent f2aebcbad9
commit 1972c4d1b4
3 changed files with 50 additions and 12 deletions
+17 -8
View File
@@ -96,14 +96,23 @@ const ChatScreen: React.FC = () => {
const sender = (message.payload.sender as string) || ''; const sender = (message.payload.sender as string) || '';
if (sender === 'user' || sender === 'diagnostic') return; if (sender === 'user' || sender === 'diagnostic') return;
const ariaMsg: ChatMessage = { const text = (message.payload.text as string) || '';
id: nextId(), const ts = message.timestamp;
sender: 'aria', // Duplikat-Schutz: gleicher Text innerhalb 5s ignorieren
text: (message.payload.text as string) || '', setMessages(prev => {
timestamp: message.timestamp, const isDuplicate = prev.some(m =>
attachments: message.payload.attachments as Attachment[] | undefined, m.sender === 'aria' && m.text === text && Math.abs(m.timestamp - ts) < 5000
}; );
setMessages(prev => [...prev, ariaMsg]); 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 // TTS-Audio abspielen wenn vorhanden
+20 -1
View File
@@ -30,6 +30,7 @@ import wave
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
import urllib.request
import numpy as np import numpy as np
import sounddevice as sd import sounddevice as sd
import websockets 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 = os.getenv("RVS_TLS", "true") # true = wss://, false = ws://
RVS_TLS_FALLBACK = os.getenv("RVS_TLS_FALLBACK", "true") # Bei TLS-Fehler ws:// versuchen 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) 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_MODEL = os.getenv("WHISPER_MODEL", "small")
WHISPER_LANGUAGE = os.getenv("WHISPER_LANGUAGE", "de") 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.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.core_auth_token = self.config.get("ARIA_AUTH_TOKEN", CORE_AUTH_TOKEN)
self._req_id_counter = 0 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-Verbindungsinfo aus Config oder Env
rvs_host = self.config.get("RVS_HOST", RVS_HOST) rvs_host = self.config.get("RVS_HOST", RVS_HOST)
rvs_port = self.config.get("RVS_PORT", RVS_PORT) rvs_port = self.config.get("RVS_PORT", RVS_PORT)
@@ -790,12 +793,28 @@ class ARIABridge:
else: else:
logger.info("[core] TTS unterdrueckt (Modus: %s)", self.current_mode.config.name) 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: async def send_to_core(self, text: str, source: str = "bridge") -> None:
"""Sendet Text an aria-core (OpenClaw chat.send Protokoll).""" """Sendet Text an aria-core (OpenClaw chat.send Protokoll)."""
if self.ws_core is None: if self.ws_core is None:
logger.error("[core] Nicht verbunden — Nachricht verworfen: '%s'", text[:60]) logger.error("[core] Nicht verbunden — Nachricht verworfen: '%s'", text[:60])
return return
# Aktive Session vom Diagnostic holen
self._fetch_active_session()
req_id = self._next_req_id() req_id = self._next_req_id()
message = json.dumps({ message = json.dumps({
"type": "req", "type": "req",
+13 -3
View File
@@ -35,7 +35,10 @@ const state = {
rvs: { status: "disconnected", lastError: null }, rvs: { status: "disconnected", lastError: null },
proxy: { status: "unknown", 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 = []; const logs = [];
let gatewayWs = null; let gatewayWs = null;
let rvsWs = null; let rvsWs = null;
@@ -924,6 +927,9 @@ const server = http.createServer((req, res) => {
} else if (req.url === "/api/state") { } else if (req.url === "/api/state") {
res.writeHead(200, { "Content-Type": "application/json" }); res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ state, logs: logs.slice(-100) })); 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 { } else {
res.writeHead(404); res.writeHead(404);
res.end("Not Found"); res.end("Not Found");
@@ -1375,8 +1381,10 @@ async function handleLoadChatHistory(clientWs) {
if (!text) continue; if (!text) continue;
if (role === "user") { if (role === "user") {
// Metadata-Prefix entfernen: "Sender (untrusted metadata):\n```json\n{...}\n```\n\n[timestamp] " // Metadata-Prefix entfernen: "Sender (untrusted metadata):\n```json\n{...}\n```\n\n[timestamp] Text"
text = text.replace(/^Sender \(untrusted metadata\):[\s\S]*?```\s*\n*(?:\[.*?\]\s*)?/m, "").trim(); 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 }); chatMessages.push({ type: "sent", text, meta: "Gateway direkt", ts: msg.timestamp || obj.timestamp || 0 });
} else if (role === "assistant") { } else if (role === "assistant") {
// Reply-Prefix entfernen: "[[reply_to_current]] " // Reply-Prefix entfernen: "[[reply_to_current]] "
@@ -1401,6 +1409,7 @@ function handleSetActiveSession(clientWs, sessionKey) {
return; return;
} }
activeSessionKey = sessionKey; activeSessionKey = sessionKey;
try { fs.writeFileSync(SESSION_KEY_FILE, activeSessionKey); } catch {}
log("info", "server", `Aktive Session: ${activeSessionKey}`); log("info", "server", `Aktive Session: ${activeSessionKey}`);
// Allen Clients mitteilen // Allen Clients mitteilen
for (const c of browserClients) { for (const c of browserClients) {
@@ -1417,6 +1426,7 @@ async function handleCreateSession(clientWs, sessionName) {
try { try {
// Session wird automatisch erstellt wenn man die erste Nachricht sendet // Session wird automatisch erstellt wenn man die erste Nachricht sendet
activeSessionKey = sessionName; activeSessionKey = sessionName;
try { fs.writeFileSync(SESSION_KEY_FILE, activeSessionKey); } catch {}
log("info", "server", `Neue Session erstellt und aktiviert: ${sessionName}`); log("info", "server", `Neue Session erstellt und aktiviert: ${sessionName}`);
// Allen Clients mitteilen // Allen Clients mitteilen
for (const c of browserClients) { for (const c of browserClients) {