Muthukumarank commited on
Commit
fb6544c
·
verified ·
1 Parent(s): 2efb53a

Add LangGraph agent system: client.ts

Browse files
Files changed (1) hide show
  1. langgraph-agents/client.ts +137 -0
langgraph-agents/client.ts ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * ForensiX AI — TypeScript SSE Client for Next.js
3
+ *
4
+ * Usage in Next.js pages:
5
+ * import { streamInvestigation, resumeInvestigation } from '@/lib/forensic-client';
6
+ */
7
+
8
+ const API_BASE = process.env.NEXT_PUBLIC_AGENT_API_URL || 'http://localhost:8000';
9
+
10
+ export interface AgentEvent {
11
+ type: 'thread_id' | 'agent_update' | 'interrupt' | 'complete' | 'error';
12
+ thread_id?: string;
13
+ agent?: string;
14
+ completed_agents?: string[];
15
+ new_findings?: number;
16
+ new_correlations?: number;
17
+ risk_score?: number;
18
+ risk_level?: string;
19
+ findings?: any[];
20
+ correlations?: any[];
21
+ explanation?: string;
22
+ investigative_leads?: string[];
23
+ risk_factors?: Record<string, number>;
24
+ errors?: string[];
25
+ data?: any;
26
+ error?: string;
27
+ }
28
+
29
+ export interface InvestigateInput {
30
+ case_id: string;
31
+ report_text?: string;
32
+ evidence?: { timestamp: string; source: string; eventType: string; details: string; lat?: number; lon?: number }[];
33
+ cctv_frames?: string[];
34
+ toxicology_data?: { substance: string; level: string }[];
35
+ thread_id?: string;
36
+ }
37
+
38
+ /**
39
+ * Stream a full investigation through the 8-agent LangGraph pipeline.
40
+ * Returns an AbortController to cancel.
41
+ */
42
+ export function streamInvestigation(
43
+ input: InvestigateInput,
44
+ onEvent: (event: AgentEvent) => void,
45
+ onError?: (error: Error) => void,
46
+ ): AbortController {
47
+ const controller = new AbortController();
48
+
49
+ fetch(`${API_BASE}/api/investigate/stream`, {
50
+ method: 'POST',
51
+ headers: { 'Content-Type': 'application/json' },
52
+ body: JSON.stringify(input),
53
+ signal: controller.signal,
54
+ })
55
+ .then(async (res) => {
56
+ if (!res.ok) {
57
+ const text = await res.text();
58
+ throw new Error(`Server error ${res.status}: ${text}`);
59
+ }
60
+ const reader = res.body!.getReader();
61
+ const decoder = new TextDecoder();
62
+ let buffer = '';
63
+
64
+ while (true) {
65
+ const { done, value } = await reader.read();
66
+ if (done) break;
67
+ buffer += decoder.decode(value, { stream: true });
68
+ const lines = buffer.split('\n\n');
69
+ buffer = lines.pop() ?? '';
70
+
71
+ for (const line of lines) {
72
+ if (line.startsWith('data: ')) {
73
+ try {
74
+ const event: AgentEvent = JSON.parse(line.slice(6));
75
+ onEvent(event);
76
+ } catch (e) {
77
+ console.warn('Failed to parse SSE event:', line);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ })
83
+ .catch((err) => {
84
+ if (err.name !== 'AbortError') {
85
+ onError?.(err);
86
+ }
87
+ });
88
+
89
+ return controller;
90
+ }
91
+
92
+ /**
93
+ * Resume an investigation after human-in-the-loop interrupt.
94
+ */
95
+ export async function resumeInvestigation(
96
+ threadId: string,
97
+ response: string,
98
+ onEvent: (event: AgentEvent) => void,
99
+ ): Promise<void> {
100
+ const res = await fetch(`${API_BASE}/api/investigate/resume`, {
101
+ method: 'POST',
102
+ headers: { 'Content-Type': 'application/json' },
103
+ body: JSON.stringify({ thread_id: threadId, response }),
104
+ });
105
+
106
+ if (!res.ok) throw new Error(`Resume failed: ${res.status}`);
107
+
108
+ const reader = res.body!.getReader();
109
+ const decoder = new TextDecoder();
110
+ let buffer = '';
111
+
112
+ while (true) {
113
+ const { done, value } = await reader.read();
114
+ if (done) break;
115
+ buffer += decoder.decode(value, { stream: true });
116
+ const lines = buffer.split('\n\n');
117
+ buffer = lines.pop() ?? '';
118
+ for (const line of lines) {
119
+ if (line.startsWith('data: ')) {
120
+ try { onEvent(JSON.parse(line.slice(6))); } catch {}
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Get current state of an investigation thread.
128
+ */
129
+ export async function getInvestigationState(threadId: string) {
130
+ const res = await fetch(`${API_BASE}/api/investigate/state`, {
131
+ method: 'POST',
132
+ headers: { 'Content-Type': 'application/json' },
133
+ body: JSON.stringify({ thread_id: threadId }),
134
+ });
135
+ if (!res.ok) throw new Error(`State fetch failed: ${res.status}`);
136
+ return await res.json();
137
+ }