fixed chat textjson format, selected session for all, fixed android echo
This commit is contained in:
@@ -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
@@ -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
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user