KI-Konzil/frontend/app/hooks/useCouncilWebSocket.ts
Claude 001649a364
Implement Phase 4: tools, God Mode, and missing features
Backend:
- Add Tavily web search tool wrapper (tools/web_search.py)
- Add PDF reader + ChromaDB vector store tool (tools/pdf_reader.py)
- Bind tools to LLM calls via .bind_tools() in dynamic_graph_builder
- Implement God Mode using LangGraph interrupt_before + MemorySaver
- Add approve/reject/modify API endpoints for God Mode
- Add PDF upload endpoint with ingestion pipeline
- Add persistent run history (CouncilRun model + run_service + API)
- Add Alembic migration for council_runs table
- Enhance WebSocket to emit run_paused and run_resumed events
- Add tests for tools, God Mode, and run history

Frontend:
- Add God Mode approval UI (GodModePanel component)
- Add Auto-Pilot / God Mode toggle in Konferenzzimmer
- Add functional PDF upload handler
- Add Conditional Edge editor (EdgeSettingsPanel component)
- Add edge click selection in ArchitectCanvas
- Update Zustand store with edge selection and update actions
- Update types for God Mode, execution modes, and WS events
- Update API client with God Mode, PDF upload, and blueprint run endpoints
- Update WebSocket hook for paused/resumed events
- Add Vitest config and frontend tests (store, parser, types, API)

https://claude.ai/code/session_017U6idFgaqnYTXzPxA7mxMv
2026-02-21 10:53:12 +00:00

98 lines
2.5 KiB
TypeScript

"use client";
import { useEffect, useRef, useCallback } from "react";
import { WSMessage } from "@/app/types/council";
import { wsUrl } from "@/app/utils/api-client";
import { useCouncilStore } from "@/app/store/council-store";
export interface PauseInfo {
next_nodes: string[];
current_draft: string;
critic_score?: number;
iteration_count?: number;
}
interface Options {
run_id: string | null;
onComplete?: (result: string) => void;
onError?: (error: string) => void;
onPaused?: (info: PauseInfo) => void;
onResumed?: () => void;
}
// WebSocket hook for live agent status updates during a council run
export function useCouncilWebSocket({
run_id,
onComplete,
onError,
onPaused,
onResumed,
}: Options) {
const ws = useRef<WebSocket | null>(null);
const markNodeActive = useCouncilStore((s) => s.markNodeActive);
const clearActiveNode = useCouncilStore((s) => s.clearActiveNode);
const setActiveRun = useCouncilStore((s) => s.setActiveRun);
const disconnect = useCallback(() => {
if (ws.current) {
ws.current.close();
ws.current = null;
}
clearActiveNode();
}, [clearActiveNode]);
useEffect(() => {
if (!run_id) return;
const socket = new WebSocket(wsUrl(run_id));
ws.current = socket;
socket.onmessage = (event: MessageEvent<string>) => {
let msg: WSMessage;
try {
msg = JSON.parse(event.data) as WSMessage;
} catch {
return;
}
switch (msg.event) {
case "node_active":
if (msg.node) markNodeActive(msg.node);
break;
case "run_paused":
clearActiveNode();
onPaused?.({
next_nodes: msg.next_nodes ?? [],
current_draft: msg.current_draft ?? "",
critic_score: msg.critic_score,
iteration_count: msg.iteration_count,
});
break;
case "run_resumed":
onResumed?.();
break;
case "run_complete":
clearActiveNode();
setActiveRun(null);
if (msg.final_draft) onComplete?.(msg.final_draft);
disconnect();
break;
case "run_failed":
clearActiveNode();
setActiveRun(null);
if (msg.error) onError?.(msg.error);
disconnect();
break;
}
};
socket.onerror = () => {
onError?.("WebSocket-Verbindungsfehler");
disconnect();
};
return () => disconnect();
}, [run_id]); // eslint-disable-line react-hooks/exhaustive-deps
return { disconnect };
}