Files
html/api/cloudbot-social-feed.php
2026-04-23 22:00:05 +02:00

119 lines
4.3 KiB
PHP

<?php
/**
* Cloudbot Social SSE Live Feed - WAVE-278 / Doctrine 143
* Stream reel des conversations agents + activity + social signals
* Zero hardcode - tout depuis sources de verite live
*/
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache, no-transform');
header('X-Accel-Buffering: no');
header('Connection: keep-alive');
header('Access-Control-Allow-Origin: *');
@ini_set('output_buffering', 'off');
@ini_set('zlib.output_compression', 0);
while (@ob_end_flush()) {}
set_time_limit(180);
$start = time();
$max_duration = 120; // 2 min max par session SSE
$poll = 3; // poll interval
$last_activity_ts = '';
$last_msg_id = 0;
$last_social_ts = '';
function send_event($type, $data) {
echo "event: $type\n";
echo "data: " . json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n\n";
@ob_flush();
@flush();
}
// Initial hello
send_event('hello', [
'ok' => true,
'ts' => date('c'),
'streams' => ['router-activity', 'social-signals', 'wevia-conversations', 'ecosystem-health'],
'interval_s' => $poll
]);
while (time() - $start < $max_duration) {
// 1. ROUTER ACTIVITY (intents matches live) - source: /tmp/v103-router.log
$log = '/tmp/v103-router.log';
if (file_exists($log)) {
$lines = @file($log, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
$recent = array_slice($lines, -10);
foreach ($recent as $line) {
if (preg_match('/^(\S+)\s+MATCH\s+pattern=(\S+)\s+msg=(.*)$/', $line, $m)) {
if ($m[1] > $last_activity_ts) {
send_event('router_match', [
'ts' => $m[1],
'pattern' => substr($m[2], 0, 60),
'msg' => substr($m[3], 0, 180),
'agent' => 'V103-Router',
'type' => 'intent_routing'
]);
$last_activity_ts = $m[1];
}
}
}
}
// 2. WEVIA MESSAGES (real conversations) - public.messages
try {
$pdo = new PDO(
"pgsql:host=10.1.0.3;dbname=adx_system",
"admin",
"admin123",
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 3]
);
$stmt = $pdo->prepare("SELECT m.id, m.role, m.content, m.created_at, c.session_id, c.source
FROM public.messages m
LEFT JOIN public.conversations c ON c.id = m.conversation_id
WHERE m.id > :last
ORDER BY m.id ASC LIMIT 5");
$stmt->execute([':last' => $last_msg_id]);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
send_event('agent_msg', [
'id' => (int)$row['id'],
'ts' => $row['created_at'],
'role' => $row['role'],
'content' => mb_substr($row['content'] ?? '', 0, 200),
'session' => substr($row['session_id'] ?? '', 0, 20),
'source' => $row['source'] ?? 'unknown',
'agent' => $row['role'] === 'assistant' ? 'WEVIA' : 'User',
'type' => 'conversation'
]);
$last_msg_id = (int)$row['id'];
}
} catch (Exception $e) {
// silent fail - DB peut etre down temporairement
}
// 3. ECOSYSTEM HEALTH ping (agents status live)
static $last_health_tick = 0;
if (time() - $last_health_tick > 15) {
$ctx = stream_context_create(['http' => ['timeout' => 2]]);
$h = @file_get_contents('http://127.0.0.1/api/ecosystem-health.php', false, $ctx);
if ($h) {
$j = @json_decode($h, true);
if ($j) {
send_event('ecosystem_health', [
'ts' => date('c'),
'agents_up' => $j['agents_up'] ?? null,
'agents_total' => $j['agents_total'] ?? null,
'score' => $j['score'] ?? $j['health_score'] ?? null,
'type' => 'health_tick'
]);
}
}
$last_health_tick = time();
}
// 4. Heartbeat
send_event('ping', ['ts' => date('c'), 'uptime_s' => time() - $start]);
sleep($poll);
}
send_event('bye', ['ts' => date('c'), 'reason' => 'max_duration']);