File size: 4,480 Bytes
5a81b95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/**
 * Real Data Hook - Fetches live data from WidgetTDC Backend
 * Replaces mock/simulated data with real API data
 */

import { useState, useEffect, useCallback } from 'react';
import { API_URL } from '../config/api';

// Types for real data
export interface LogEntry {
  id: string;
  timestamp: string;
  level: 'debug' | 'info' | 'warn' | 'error';
  source: string;
  message: string;
}

export interface GraphStats {
  nodes: number;
  relationships: number;
  timestamp: string;
}

export interface SystemHealth {
  status: string;
  registeredTools: number;
  services: {
    neo4j: { healthy: boolean };
    vectorStore: { healthy: boolean; backend: string };
  };
}

export interface HyperMetrics {
  totalThoughts: number;
  toolUsageRate: number;
  activeAgents: number;
}

export interface RealDataState {
  logs: LogEntry[];
  graphStats: GraphStats | null;
  health: SystemHealth | null;
  hyperMetrics: HyperMetrics | null;
  isLoading: boolean;
  error: string | null;
  lastUpdated: number;
}

export const useRealData = (refreshInterval = 5000) => {
  const [state, setState] = useState<RealDataState>({
    logs: [],
    graphStats: null,
    health: null,
    hyperMetrics: null,
    isLoading: true,
    error: null,
    lastUpdated: 0,
  });

  const fetchData = useCallback(async () => {
    try {
      const [logsRes, graphRes, healthRes, hyperRes] = await Promise.all([
        fetch(`${API_URL}/api/logs`).catch(() => null),
        fetch(`${API_URL}/api/evolution/graph/stats`).catch(() => null),
        fetch(`${API_URL}/health`).catch(() => null),
        fetch(`${API_URL}/api/hyper/status`).catch(() => null),
      ]);

      const logs = logsRes?.ok ? (await logsRes.json()).entries?.slice(0, 50) || [] : [];
      const graphStats = graphRes?.ok ? await graphRes.json() : null;
      const health = healthRes?.ok ? await healthRes.json() : null;
      const hyperData = hyperRes?.ok ? await hyperRes.json() : null;

      setState({
        logs,
        graphStats: graphStats
          ? {
              nodes: graphStats.stats?.nodes || 0,
              relationships: graphStats.stats?.relationships || 0,
              timestamp: graphStats.timestamp,
            }
          : null,
        health: health
          ? {
              status: health.status,
              registeredTools: health.registeredTools || 0,
              services: health.services || {},
            }
          : null,
        hyperMetrics: hyperData?.metrics || null,
        isLoading: false,
        error: null,
        lastUpdated: Date.now(),
      });
    } catch (err) {
      setState(prev => ({
        ...prev,
        isLoading: false,
        error: (err as Error).message,
      }));
    }
  }, []);

  useEffect(() => {
    fetchData();
    const interval = setInterval(fetchData, refreshInterval);
    return () => clearInterval(interval);
  }, [fetchData, refreshInterval]);

  // Derived metrics from real data
  const metrics = {
    totalNodes: state.graphStats?.nodes || 0,
    totalRelationships: state.graphStats?.relationships || 0,
    totalTools: state.health?.registeredTools || 0,
    activeAgents: state.hyperMetrics?.activeAgents || 0,
    systemStatus: state.health?.status || 'unknown',
    neo4jHealthy: state.health?.services?.neo4j?.healthy || false,
    vectorStoreHealthy: state.health?.services?.vectorStore?.healthy || false,
    recentErrors: state.logs.filter(l => l.level === 'error').length,
    recentWarnings: state.logs.filter(l => l.level === 'warn').length,
  };

  // Transform logs to events for widgets
  const events = state.logs.map(log => ({
    id: log.id,
    time: new Date(log.timestamp).toLocaleTimeString('da-DK'),
    message: log.message,
    level: log.level,
    source: log.source,
  }));

  return {
    ...state,
    metrics,
    events,
    refresh: fetchData,
  };
};

// Hook for real-time log streaming
export const useLogStream = (maxLogs = 20) => {
  const [logs, setLogs] = useState<LogEntry[]>([]);

  useEffect(() => {
    const fetchLogs = async () => {
      try {
        const res = await fetch(`${API_URL}/api/logs`);
        if (res.ok) {
          const data = await res.json();
          setLogs(data.entries?.slice(0, maxLogs) || []);
        }
      } catch {
        // Silent fail
      }
    };

    fetchLogs();
    const interval = setInterval(fetchLogs, 3000);
    return () => clearInterval(interval);
  }, [maxLogs]);

  return logs;
};

export default useRealData;