diagnostic/server.js — Handler umgebaut: event: "agent" für Deltas, event: "chat" mit state: "final" für Antworten, extractChatText() parst das content[] Array
bridge/aria_bridge.py — Gleicher Fix: _extract_chat_text() Methode, neue Event-Handler für agent und chat mit state, Legacy-Namen als Fallback
This commit is contained in:
parent
fcb22f60d3
commit
9cad631015
|
|
@ -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
|
- 60s Timeout — markiert Pipeline als fehlgeschlagen wenn keine Antwort kommt
|
||||||
- Funktioniert für Gateway-direkt und RVS-Nachrichten
|
- 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
|
### Geändert
|
||||||
|
|
||||||
**OpenClaw Config — Custom Provider Format**
|
**OpenClaw Config — Custom Provider Format**
|
||||||
|
|
|
||||||
|
|
@ -625,28 +625,64 @@ class ARIABridge:
|
||||||
event_name = message.get("event", "")
|
event_name = message.get("event", "")
|
||||||
payload = message.get("payload", {})
|
payload = message.get("payload", {})
|
||||||
|
|
||||||
if event_name == "chat:delta":
|
# ── agent Events: Streaming-Deltas vom LLM ──
|
||||||
# Streaming-Delta — fuer spaeter (Live-Typing in der App)
|
if event_name == "agent":
|
||||||
delta = payload.get("delta", payload.get("text", ""))
|
data = payload.get("data", {})
|
||||||
if delta:
|
delta = data.get("delta", "")
|
||||||
|
if delta and payload.get("stream") == "assistant":
|
||||||
logger.debug("[core] Delta: '%s'", delta[:40])
|
logger.debug("[core] Delta: '%s'", delta[:40])
|
||||||
return
|
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":
|
if event_name == "chat:final":
|
||||||
# Fertige Antwort von aria-core
|
|
||||||
text = payload.get("text", payload.get("message", ""))
|
text = payload.get("text", payload.get("message", ""))
|
||||||
|
if not text:
|
||||||
|
text = self._extract_chat_text(payload)
|
||||||
if not text:
|
if not text:
|
||||||
logger.warning("[core] chat:final ohne Text: %s", json.dumps(payload)[:200])
|
logger.warning("[core] chat:final ohne Text: %s", json.dumps(payload)[:200])
|
||||||
return
|
return
|
||||||
|
logger.info("[core] Antwort (legacy): '%s'", text[:80])
|
||||||
logger.info("[core] Antwort: '%s'", text[:80])
|
|
||||||
await self._process_core_response(text, payload)
|
await self._process_core_response(text, payload)
|
||||||
return
|
return
|
||||||
|
|
||||||
if event_name == "chat:error":
|
if event_name == "chat:error":
|
||||||
error = payload.get("error", payload.get("message", "Unbekannt"))
|
error = payload.get("error", payload.get("message", "Unbekannt"))
|
||||||
logger.error("[core] Chat-Fehler: %s", error)
|
logger.error("[core] Chat-Fehler (legacy): %s", error)
|
||||||
# Fehler auch an die App melden
|
|
||||||
await self._send_to_rvs({
|
await self._send_to_rvs({
|
||||||
"type": "chat",
|
"type": "chat",
|
||||||
"payload": {
|
"payload": {
|
||||||
|
|
@ -657,9 +693,27 @@ class ARIABridge:
|
||||||
})
|
})
|
||||||
return
|
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)
|
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:
|
async def _process_core_response(self, text: str, payload: dict) -> None:
|
||||||
"""Verarbeitet eine fertige Antwort von aria-core.
|
"""Verarbeitet eine fertige Antwort von aria-core.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
function handleGatewayMessage(msg) {
|
||||||
if (msg.type === "res") {
|
if (msg.type === "res") {
|
||||||
const status = msg.ok ? "OK" : `FEHLER: ${JSON.stringify(msg.error).slice(0, 100)}`;
|
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 event = msg.event || "?";
|
||||||
const payload = msg.payload || {};
|
const payload = msg.payload || {};
|
||||||
|
|
||||||
if (event === "chat:delta") {
|
// ── agent Events: Streaming-Deltas vom LLM ──
|
||||||
const delta = payload.delta || payload.text || "";
|
if (event === "agent") {
|
||||||
if (delta) {
|
const data = payload.data || {};
|
||||||
log("info", "gateway", `Delta: "${delta.slice(0, 60)}"`);
|
const delta = data.delta || "";
|
||||||
if (pipelineActive) plog(`Streaming Delta: "${delta.slice(0, 80)}"`);
|
if (delta && payload.stream === "assistant") {
|
||||||
broadcast({ type: "chat_delta", delta, payload });
|
broadcast({ type: "chat_delta", delta, payload });
|
||||||
}
|
}
|
||||||
|
// agent Events nicht einzeln loggen (zu viele)
|
||||||
return;
|
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") {
|
if (event === "chat:final") {
|
||||||
const text = payload.text || payload.message || "";
|
const text = extractChatText(payload) || payload.text || "";
|
||||||
log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`);
|
log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`);
|
||||||
if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`);
|
if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`);
|
||||||
broadcast({ type: "chat_final", text, payload });
|
broadcast({ type: "chat_final", text, payload });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event === "chat:error") {
|
if (event === "chat:error") {
|
||||||
const error = payload.error || payload.message || "Unbekannt";
|
const error = payload.error || payload.message || "Unbekannt";
|
||||||
log("error", "gateway", `Chat-Fehler: ${error}`);
|
log("error", "gateway", `Chat-Fehler: ${error}`);
|
||||||
|
|
@ -283,28 +333,9 @@ function handleGatewayMessage(msg) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alle anderen Events — vollstaendig loggen fuer Debugging
|
// ── Andere Events (tick, health, presence) ──
|
||||||
const payloadStr = JSON.stringify(payload).slice(0, 500);
|
if (event === "tick" || event === "health") return; // Noise
|
||||||
log("info", "gateway", `Event: ${event} | ${payloadStr}`);
|
log("debug", "gateway", `Event: ${event}`);
|
||||||
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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue