rename(diagnostic): Pipeline-Tab → Trace (End-to-End-Mitschnitt einer Anfrage)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+16
-16
@@ -57,10 +57,10 @@
|
||||
.log-entry.warn { color: #FFD60A; }
|
||||
.log-entry.info { color: #AAB; }
|
||||
.log-entry.debug { color: #555570; }
|
||||
.log-entry.pipeline-step { color: #0096FF; border-left: 2px solid #0096FF; padding-left: 6px; margin: 2px 0; }
|
||||
.log-entry.pipeline-ok { color: #34C759; border-left: 2px solid #34C759; padding-left: 6px; margin: 2px 0; }
|
||||
.log-entry.pipeline-err { color: #FF3B30; border-left: 2px solid #FF3B30; padding-left: 6px; margin: 2px 0; }
|
||||
.log-entry.pipeline-sep { color: #333; margin: 6px 0 2px; }
|
||||
.log-entry.trace-step { color: #0096FF; border-left: 2px solid #0096FF; padding-left: 6px; margin: 2px 0; }
|
||||
.log-entry.trace-ok { color: #34C759; border-left: 2px solid #34C759; padding-left: 6px; margin: 2px 0; }
|
||||
.log-entry.trace-err { color: #FF3B30; border-left: 2px solid #FF3B30; padding-left: 6px; margin: 2px 0; }
|
||||
.log-entry.trace-sep { color: #333; margin: 6px 0 2px; }
|
||||
|
||||
.chat-box { background: #080810; border: 1px solid #1E1E2E; border-radius: 6px;
|
||||
min-height: 120px; max-height: 250px; overflow-y: auto;
|
||||
@@ -379,7 +379,7 @@
|
||||
<button class="tab-btn" data-tab="proxy" onclick="switchTab('proxy')">Proxy <span class="tab-count" id="count-proxy">0</span></button>
|
||||
<button class="tab-btn" data-tab="bridge" onclick="switchTab('bridge')">Bridge <span class="tab-count" id="count-bridge">0</span></button>
|
||||
<button class="tab-btn" data-tab="server" onclick="switchTab('server')">Server <span class="tab-count" id="count-server">0</span></button>
|
||||
<button class="tab-btn" data-tab="pipeline" onclick="switchTab('pipeline')" style="margin-left:auto;border-color:#0096FF44;color:#0096FF">Pipeline <span class="tab-count" id="count-pipeline">0</span></button>
|
||||
<button class="tab-btn" data-tab="trace" onclick="switchTab('trace')" style="margin-left:auto;border-color:#0096FF44;color:#0096FF" title="End-to-End-Mitschnitt einer einzelnen Anfrage mit Zeitstempeln">Trace <span class="tab-count" id="count-trace">0</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-panel">
|
||||
@@ -398,7 +398,7 @@
|
||||
<div class="log-box hidden" id="log-proxy"></div>
|
||||
<div class="log-box hidden" id="log-bridge"></div>
|
||||
<div class="log-box hidden" id="log-server"></div>
|
||||
<div class="log-box hidden" id="log-pipeline"></div>
|
||||
<div class="log-box hidden" id="log-trace"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -729,8 +729,8 @@
|
||||
let ws;
|
||||
let activeTab = 'all';
|
||||
const DOCKER_TABS = ['gateway', 'proxy', 'bridge'];
|
||||
const autoScroll = { all: true, gateway: true, rvs: true, proxy: true, bridge: true, server: true, pipeline: true };
|
||||
const logCounts = { all: 0, gateway: 0, rvs: 0, proxy: 0, bridge: 0, server: 0, pipeline: 0 };
|
||||
const autoScroll = { all: true, gateway: true, rvs: true, proxy: true, bridge: true, server: true, trace: true };
|
||||
const logCounts = { all: 0, gateway: 0, rvs: 0, proxy: 0, bridge: 0, server: 0, trace: 0 };
|
||||
|
||||
const logBoxes = {
|
||||
all: document.getElementById('log-all'),
|
||||
@@ -739,7 +739,7 @@
|
||||
proxy: document.getElementById('log-proxy'),
|
||||
bridge: document.getElementById('log-bridge'),
|
||||
server: document.getElementById('log-server'),
|
||||
pipeline: document.getElementById('log-pipeline'),
|
||||
trace: document.getElementById('log-trace'),
|
||||
tts: document.getElementById('log-tts'),
|
||||
};
|
||||
|
||||
@@ -794,7 +794,7 @@
|
||||
if (source === 'proxy') return 'proxy';
|
||||
if (source === 'bridge') return 'bridge';
|
||||
if (source === 'server' || source === 'browser') return 'server';
|
||||
if (source === 'pipeline') return 'pipeline';
|
||||
if (source === 'trace') return 'trace';
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1333,12 +1333,12 @@
|
||||
const time = ts ? new Date(ts).toLocaleTimeString('de-DE') : new Date().toLocaleTimeString('de-DE');
|
||||
const line = `${time} [${source}] ${message}`;
|
||||
|
||||
// Pipeline-Eintraege nur in Pipeline-Tab (nicht in Alle)
|
||||
if (source === 'pipeline') {
|
||||
const pipeLevel = level === 'error' ? 'pipeline-err' : level === 'info' && message.includes('>>>') ? 'pipeline-ok' : 'pipeline-step';
|
||||
appendToLog('pipeline', pipeLevel, `${time} ${message}`);
|
||||
logCounts.pipeline++;
|
||||
document.getElementById('count-pipeline').textContent = logCounts.pipeline;
|
||||
// Trace-Eintraege nur in Trace-Tab (nicht in Alle)
|
||||
if (source === 'trace') {
|
||||
const pipeLevel = level === 'error' ? 'trace-err' : level === 'info' && message.includes('>>>') ? 'trace-ok' : 'trace-step';
|
||||
appendToLog('trace', pipeLevel, `${time} ${message}`);
|
||||
logCounts.trace++;
|
||||
document.getElementById('count-trace').textContent = logCounts.trace;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+42
-42
@@ -113,9 +113,9 @@ let rvsWs = null;
|
||||
let reqIdCounter = 0;
|
||||
const browserClients = new Set();
|
||||
|
||||
// ── Pipeline Tracking ──────────────────────────────────
|
||||
let pipelineActive = false;
|
||||
let pipelineStartTime = 0;
|
||||
// ── Trace-Tracking (End-to-End-Mitschnitt einer Anfrage) ──────────────────────────────────
|
||||
let traceActive = false;
|
||||
let traceStartTime = 0;
|
||||
|
||||
// Nach chat:final kommen oft noch Trailing Agent-Events. Waehrend dieses
|
||||
// Fensters unterdruecken wir agent_activity-Broadcasts, damit der
|
||||
@@ -124,40 +124,40 @@ let lastChatFinalAt = 0;
|
||||
const SETTLED_WINDOW_MS = 3000;
|
||||
|
||||
function plog(message, level) {
|
||||
const elapsed = pipelineActive ? `+${Date.now() - pipelineStartTime}ms` : "";
|
||||
const entry = { ts: new Date().toISOString(), level: level || "info", source: "pipeline", message: `${elapsed ? `[${elapsed}] ` : ""}${message}` };
|
||||
const elapsed = traceActive ? `+${Date.now() - traceStartTime}ms` : "";
|
||||
const entry = { ts: new Date().toISOString(), level: level || "info", source: "trace", message: `${elapsed ? `[${elapsed}] ` : ""}${message}` };
|
||||
logs.push(entry);
|
||||
if (logs.length > 500) logs.shift();
|
||||
console.log(`[PIPELINE] ${entry.message}`);
|
||||
console.log(`[TRACE] ${entry.message}`);
|
||||
broadcast({ type: "log", entry });
|
||||
}
|
||||
|
||||
let pipelineTimeout = null;
|
||||
let traceTimeout = null;
|
||||
|
||||
function pipelineStart(method, text) {
|
||||
// Falls noch eine Pipeline laeuft, beenden
|
||||
if (pipelineActive) pipelineEnd(false, "Abgebrochen (neue Nachricht)");
|
||||
pipelineActive = true;
|
||||
pipelineStartTime = Date.now();
|
||||
if (pipelineTimeout) clearTimeout(pipelineTimeout);
|
||||
pipelineTimeout = setTimeout(() => {
|
||||
if (pipelineActive) pipelineEnd(false, "Timeout — keine Antwort nach 10min");
|
||||
function traceStart(method, text) {
|
||||
// Falls noch ein Trace laeuft, beenden
|
||||
if (traceActive) traceEnd(false, "Abgebrochen (neue Nachricht)");
|
||||
traceActive = true;
|
||||
traceStartTime = Date.now();
|
||||
if (traceTimeout) clearTimeout(traceTimeout);
|
||||
traceTimeout = setTimeout(() => {
|
||||
if (traceActive) traceEnd(false, "Timeout — keine Antwort nach 10min");
|
||||
}, 600000);
|
||||
plog(`━━━ Pipeline Start: ${method} ━━━`);
|
||||
plog(`━━━ Trace Start: ${method} ━━━`);
|
||||
plog(`Nachricht: "${text}"`);
|
||||
}
|
||||
|
||||
function pipelineEnd(ok, detail) {
|
||||
if (!pipelineActive) return;
|
||||
if (pipelineTimeout) { clearTimeout(pipelineTimeout); pipelineTimeout = null; }
|
||||
const elapsed = Date.now() - pipelineStartTime;
|
||||
function traceEnd(ok, detail) {
|
||||
if (!traceActive) return;
|
||||
if (traceTimeout) { clearTimeout(traceTimeout); traceTimeout = null; }
|
||||
const elapsed = Date.now() - traceStartTime;
|
||||
if (ok) {
|
||||
plog(`>>> Fertig (${elapsed}ms): ${detail}`);
|
||||
} else {
|
||||
plog(`>>> FEHLER (${elapsed}ms): ${detail}`, "error");
|
||||
}
|
||||
plog(`━━━ Pipeline Ende ━━━`);
|
||||
pipelineActive = false;
|
||||
plog(`━━━ Trace Ende ━━━`);
|
||||
traceActive = false;
|
||||
// Thinking-Indikator IMMER zuruecksetzen — auch bei Timeout/Fehler/Abbruch
|
||||
broadcast({ type: "agent_activity", activity: "idle" });
|
||||
pendingMessageTime = 0;
|
||||
@@ -327,8 +327,8 @@ async function connectGateway() {
|
||||
state.gateway.handshakeOk = false;
|
||||
gatewayWs = null;
|
||||
broadcastState();
|
||||
// Stuck "ARIA denkt..." vermeiden, falls Gateway waehrend Pipeline abkackt
|
||||
if (pipelineActive) pipelineEnd(false, `Gateway-Verbindung verloren (${code})`);
|
||||
// Stuck "ARIA denkt..." vermeiden, falls Gateway waehrend Trace abkackt
|
||||
if (traceActive) traceEnd(false, `Gateway-Verbindung verloren (${code})`);
|
||||
else broadcast({ type: "agent_activity", activity: "idle" });
|
||||
checkGatewayHealth();
|
||||
setTimeout(connectGateway, 5000);
|
||||
@@ -375,7 +375,7 @@ function handleGatewayMessage(msg) {
|
||||
if (msg.type === "res") {
|
||||
const status = msg.ok ? "OK" : `FEHLER: ${JSON.stringify(msg.error).slice(0, 100)}`;
|
||||
log("info", "gateway", `Response [${msg.id}]: ${status}`);
|
||||
if (pipelineActive) {
|
||||
if (traceActive) {
|
||||
if (msg.ok) plog(`Gateway ACK [${msg.id}] — Nachricht angenommen`);
|
||||
else plog(`Gateway NACK [${msg.id}] — ${JSON.stringify(msg.error).slice(0, 100)}`, "error");
|
||||
}
|
||||
@@ -427,12 +427,12 @@ function handleGatewayMessage(msg) {
|
||||
if (runId && seenFinalRuns.has(runId)) return; // Duplikat
|
||||
if (runId) { seenFinalRuns.add(runId); setTimeout(() => seenFinalRuns.delete(runId), 60000); }
|
||||
|
||||
// NO_REPLY → ARIA signalisiert "nicht antworten", Pipeline beenden aber nichts zeigen
|
||||
// NO_REPLY → ARIA signalisiert "nicht antworten", Trace beenden aber nichts zeigen
|
||||
const trimmed = (text || "").trim().replace(/^["'`*.\s]+|["'`*.\s]+$/g, "").toUpperCase();
|
||||
if (trimmed === "NO_REPLY" || trimmed.startsWith("NO_REPLY")) {
|
||||
log("info", "gateway", "NO_REPLY empfangen — still verworfen");
|
||||
lastChatFinalAt = Date.now();
|
||||
if (pipelineActive) pipelineEnd(true, "NO_REPLY (stumm)");
|
||||
if (traceActive) traceEnd(true, "NO_REPLY (stumm)");
|
||||
broadcast({ type: "agent_activity", activity: "idle" });
|
||||
pendingMessageTime = 0;
|
||||
updateAgentActivity();
|
||||
@@ -441,7 +441,7 @@ function handleGatewayMessage(msg) {
|
||||
|
||||
log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`);
|
||||
lastChatFinalAt = Date.now();
|
||||
if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`);
|
||||
if (traceActive) traceEnd(true, `"${text.slice(0, 120)}"`);
|
||||
broadcast({ type: "chat_final", text, payload });
|
||||
broadcast({ type: "agent_activity", activity: "idle" });
|
||||
pendingMessageTime = 0; // Watchdog: Antwort erhalten
|
||||
@@ -462,7 +462,7 @@ function handleGatewayMessage(msg) {
|
||||
if (state === "error") {
|
||||
const error = payload.error || text || "Unbekannt";
|
||||
log("error", "gateway", `Chat-Fehler: ${error}`);
|
||||
if (pipelineActive) pipelineEnd(false, error);
|
||||
if (traceActive) traceEnd(false, error);
|
||||
else broadcast({ type: "agent_activity", activity: "idle" });
|
||||
broadcast({ type: "chat_error", error, payload });
|
||||
return;
|
||||
@@ -485,7 +485,7 @@ function handleGatewayMessage(msg) {
|
||||
const text = extractChatText(payload) || payload.text || "";
|
||||
log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`);
|
||||
lastChatFinalAt = Date.now();
|
||||
if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`);
|
||||
if (traceActive) traceEnd(true, `"${text.slice(0, 120)}"`);
|
||||
else broadcast({ type: "agent_activity", activity: "idle" });
|
||||
broadcast({ type: "chat_final", text, payload });
|
||||
return;
|
||||
@@ -493,7 +493,7 @@ function handleGatewayMessage(msg) {
|
||||
if (event === "chat:error") {
|
||||
const error = payload.error || payload.message || "Unbekannt";
|
||||
log("error", "gateway", `Chat-Fehler: ${error}`);
|
||||
if (pipelineActive) pipelineEnd(false, error);
|
||||
if (traceActive) traceEnd(false, error);
|
||||
else broadcast({ type: "agent_activity", activity: "idle" });
|
||||
broadcast({ type: "chat_error", error, payload });
|
||||
return;
|
||||
@@ -505,10 +505,10 @@ function handleGatewayMessage(msg) {
|
||||
}
|
||||
}
|
||||
|
||||
function sendToGateway(text, isPipeline) {
|
||||
function sendToGateway(text, isTrace) {
|
||||
if (!gatewayWs || gatewayWs.readyState !== WebSocket.OPEN) {
|
||||
log("error", "gateway", "Nicht verbunden — kann nicht senden");
|
||||
if (isPipeline) pipelineEnd(false, "Gateway nicht verbunden");
|
||||
if (isTrace) traceEnd(false, "Gateway nicht verbunden");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -535,7 +535,7 @@ function sendToGateway(text, isPipeline) {
|
||||
fs.appendFileSync("/shared/config/chat_backup.jsonl", entry);
|
||||
} catch {}
|
||||
log("info", "gateway", `chat.send [${reqId}]: "${text}"`);
|
||||
if (isPipeline) plog(`chat.send [${reqId}] an Gateway gesendet — warte auf ACK...`);
|
||||
if (isTrace) plog(`chat.send [${reqId}] an Gateway gesendet — warte auf ACK...`);
|
||||
|
||||
// Gateway-Nachrichten NICHT an RVS senden (sonst doppelter ARIA-Request via Bridge)
|
||||
return true;
|
||||
@@ -605,8 +605,8 @@ function connectRVS(forcePlain) {
|
||||
// Eigene Nachrichten ignorieren (Echo)
|
||||
if (sender === "diagnostic") return;
|
||||
log("info", "rvs", `Chat von ${sender}: "${(msg.payload.text || "").slice(0, 100)}"`);
|
||||
if (pipelineActive) {
|
||||
pipelineEnd(true, `Antwort via RVS von ${sender}: "${(msg.payload.text || "").slice(0, 120)}"`);
|
||||
if (traceActive) {
|
||||
traceEnd(true, `Antwort via RVS von ${sender}: "${(msg.payload.text || "").slice(0, 120)}"`);
|
||||
}
|
||||
broadcast({ type: "rvs_chat", msg });
|
||||
} else if (msg.type === "file_saved" && msg.payload) {
|
||||
@@ -744,14 +744,14 @@ function sendToRVS_raw(msgObj) {
|
||||
freshWs.on("error", () => {});
|
||||
}
|
||||
|
||||
function sendToRVS(text, isPipeline) {
|
||||
function sendToRVS(text, isTrace) {
|
||||
// Ueber Gateway senden (zuverlaessig) UND an RVS fuer App-Sichtbarkeit
|
||||
// Die Bridge empfaengt RVS-Nachrichten von der App zuverlaessig,
|
||||
// aber die Diagnostic→RVS→Bridge Route hat Zombie-Probleme.
|
||||
// Deshalb: Gateway fuer ARIA, RVS nur fuer App-Anzeige.
|
||||
|
||||
// 1. An Gateway senden (damit ARIA antwortet)
|
||||
const gatewayOk = sendToGateway(text, isPipeline);
|
||||
const gatewayOk = sendToGateway(text, isTrace);
|
||||
|
||||
// 2. An RVS senden (damit die App die Nachricht sieht)
|
||||
sendToRVS_raw({
|
||||
@@ -1337,7 +1337,7 @@ const server = http.createServer((req, res) => {
|
||||
pendingMessageTime = 0;
|
||||
watchdogWarned = false;
|
||||
watchdogFixAttempted = false;
|
||||
if (pipelineActive) pipelineEnd(false, "Vom Benutzer abgebrochen (App)");
|
||||
if (traceActive) traceEnd(false, "Vom Benutzer abgebrochen (App)");
|
||||
else broadcast({ type: "agent_activity", activity: "idle" });
|
||||
dockerExec("aria-core", "openclaw doctor --fix 2>/dev/null || true").catch(() => {});
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
@@ -1475,10 +1475,10 @@ wss.on("connection", (ws) => {
|
||||
const msg = JSON.parse(raw.toString());
|
||||
|
||||
if (msg.action === "test_gateway") {
|
||||
pipelineStart("Gateway", msg.text || "aria lebst du noch?");
|
||||
traceStart("Gateway", msg.text || "aria lebst du noch?");
|
||||
sendToGateway(msg.text || "aria lebst du noch?", true);
|
||||
} else if (msg.action === "test_rvs") {
|
||||
pipelineStart("RVS", msg.text || "aria lebst du noch?");
|
||||
traceStart("RVS", msg.text || "aria lebst du noch?");
|
||||
sendToRVS(msg.text || "aria lebst du noch?", true);
|
||||
} else if (msg.action === "reconnect_gateway") {
|
||||
connectGateway();
|
||||
@@ -1521,7 +1521,7 @@ wss.on("connection", (ws) => {
|
||||
pendingMessageTime = 0;
|
||||
watchdogWarned = false;
|
||||
watchdogFixAttempted = false;
|
||||
if (pipelineActive) pipelineEnd(false, "Vom Benutzer abgebrochen");
|
||||
if (traceActive) traceEnd(false, "Vom Benutzer abgebrochen");
|
||||
broadcast({ type: "agent_activity", activity: "idle" });
|
||||
dockerExec("aria-core", "openclaw doctor --fix 2>/dev/null || true").catch(() => {});
|
||||
} else if (msg.action === "voice_upload") {
|
||||
|
||||
Reference in New Issue
Block a user