Files
html/api/opus5-autonomy-kpi.php
2026-04-19 16:40:01 +02:00

119 lines
6.1 KiB
PHP

<?php
// OPUS5 — KPI Autonomie Consolidé (doctrine 90)
// Agrège truth registry + plans PG + intents Phase 1/2 + health stack
// Lecture seule, zero écrasement, zero dépendance nouvelle
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$t0 = microtime(true);
$R = ['ts'=>date('c'), 'source'=>'opus5-autonomy-kpi'];
// === 1. Truth registry ===
$truth_raw = @file_get_contents('/var/www/html/api/wevia-truth-registry.json');
$truth = @json_decode((string)$truth_raw, true) ?: [];
$R['truth'] = [
'agents_unique' => $truth['agents']['count_unique'] ?? 0,
'agents_overlaps' => $truth['agents']['count_with_overlaps'] ?? 0,
'apis_php' => $truth['apis_php_count'] ?? 0,
'autonomy_score' => $truth['autonomy_score'] ?? 0,
'intents_count' => $truth['intents']['count'] ?? 0,
'doctrines_count' => $truth['doctrines']['count'] ?? 0,
'dashboards_count' => $truth['dashboards']['count'] ?? 0
];
// === 2. Plans PG (Phase 1 autonomy) ===
try {
$db = new PDO('pgsql:host=10.1.0.3;port=5432;dbname=adx_system;user=admin;password=admin123', null, null, [PDO::ATTR_TIMEOUT=>5]);
$plans_stats = $db->query("SELECT
COUNT(*) AS total,
COUNT(*) FILTER (WHERE status='done') AS done,
COUNT(*) FILTER (WHERE status='failed') AS failed,
COUNT(*) FILTER (WHERE status='draft') AS draft,
COUNT(*) FILTER (WHERE status='running') AS running,
COUNT(*) FILTER (WHERE status='archived') AS archived,
COUNT(*) FILTER (WHERE created_at > NOW() - INTERVAL '1 hour') AS last_hour,
COUNT(*) FILTER (WHERE name LIKE 'AutoPlan%') AS auto_generated
FROM admin.wevia_plans")->fetch(PDO::FETCH_ASSOC);
$steps_stats = $db->query("SELECT
COUNT(*) AS total_steps,
COUNT(*) FILTER (WHERE status='done') AS done_steps,
COUNT(*) FILTER (WHERE status='failed') AS failed_steps
FROM admin.wevia_plan_steps")->fetch(PDO::FETCH_ASSOC);
$R['plans'] = array_merge($plans_stats, $steps_stats);
$R['plans']['success_rate_pct'] = $plans_stats['total'] > 0 ? round(($plans_stats['done'] / $plans_stats['total']) * 100, 1) : 0;
} catch (Throwable $e) {
$R['plans'] = ['err'=>$e->getMessage()];
}
// === 3. Phase status ===
$R['phases'] = [
'P0_cache_stream_sandbox' => file_exists('/var/www/html/api/opus5-predictive-cache.php') && file_exists('/var/www/html/api/opus5-task-stream.php') && file_exists('/var/www/html/api/opus5-python-sandbox.php') ? 'LIVE' : 'MISSING',
'P1_plugin_n8n_graph' => file_exists('/var/www/html/api/opus5-plugin-store.php') && file_exists('/var/www/html/api/opus5-n8n-generator.php') && file_exists('/var/www/html/api/opus5-knowledge-graph.php') ? 'LIVE' : 'MISSING',
'P2_tmux_grid_orch3' => file_exists('/var/www/html/api/opus5-ssh-tmux-stream.php') && file_exists('/var/www/html/api/opus5-gpu-grid.php') && file_exists('/var/www/html/api/opus5-autonomous-orchestrator-v3.php') ? 'LIVE' : 'MISSING',
'Phase1_registry_orch' => file_exists('/var/www/html/api/opus5-plan-registry.php') && file_exists('/var/www/html/api/opus5-plan-orchestrator.php') ? 'LIVE' : 'MISSING',
'Phase2_nl_parser' => file_exists('/var/www/html/api/opus5-plan-from-text.php') && file_exists('/var/www/html/api/opus5-plan-from-text-action.php') ? 'LIVE' : 'MISSING'
];
// === 4. Health stack ===
$R['health'] = [];
// PHP endpoints via curl, JSON files via filesystem
$php_endpoints = [
'cache' => 'http://127.0.0.1/api/opus5-predictive-cache.php?action=stats',
'nonreg' => 'http://127.0.0.1/api/nonreg-api.php?cat=all',
'plan_registry' => 'http://127.0.0.1/api/opus5-plan-registry.php?action=list&limit=1',
'plan_from_text' => 'http://127.0.0.1/api/opus5-plan-from-text-action.php?dry_run=1'
];
foreach ($php_endpoints as $k => $url) {
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>5, CURLOPT_FOLLOWLOCATION=>true]);
$body = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$R['health'][$k] = $http === 200 ? 'OK' : "HTTP$http";
}
$json_files = [
'l99' => '/var/www/html/api/l99-state.json',
'truth' => '/var/www/html/api/wevia-truth-registry.json'
];
foreach ($json_files as $k => $path) {
$R['health'][$k] = (file_exists($path) && filesize($path) > 100) ? 'OK' : 'MISSING';
}
// === 5. Intents Phase 1/2 mapped ===
$R['intents_autonomy'] = [
'implement_plan' => file_exists('/var/www/html/api/wired-pending/intent-opus4-implement_plan.php') ? 'WIRED' : 'MISSING',
'plan_list' => file_exists('/var/www/html/api/wired-pending/intent-opus4-plan_list.php') ? 'WIRED' : 'MISSING',
'plan_status' => file_exists('/var/www/html/api/wired-pending/intent-opus4-plan_status.php') ? 'WIRED' : 'MISSING',
'plan_from_text' => file_exists('/var/www/html/api/wired-pending/intent-opus4-plan_from_text.php') ? 'WIRED' : 'MISSING'
];
// === 6. Score synthèse ===
$phases_live = 0;
foreach ($R['phases'] as $v) if ($v === 'LIVE') $phases_live++;
$intents_wired = 0;
foreach ($R['intents_autonomy'] as $v) if ($v === 'WIRED') $intents_wired++;
$health_ok = 0;
foreach ($R['health'] as $v) if ($v === 'OK') $health_ok++;
$R['synthesis'] = [
'phases_live' => "$phases_live/5",
'phases_pct' => round($phases_live / 5 * 100, 1),
'intents_wired' => "$intents_wired/4",
'intents_pct' => round($intents_wired / 4 * 100, 1),
'health_ok' => "$health_ok/6",
'health_pct' => round($health_ok / 6 * 100, 1),
'overall_autonomy_pct' => round(
($phases_live / 5 * 30) + // 30% poids phases
($intents_wired / 4 * 25) + // 25% intents
($health_ok / 6 * 20) + // 20% health
($R['truth']['autonomy_score'] / 100 * 25), // 25% truth registry
1
),
'grade' => 'computed below'
];
$pct = $R['synthesis']['overall_autonomy_pct'];
$R['synthesis']['grade'] = $pct >= 95 ? 'A+ GODMODE' : ($pct >= 85 ? 'A EXCELLENT' : ($pct >= 70 ? 'B GOOD' : ($pct >= 50 ? 'C AVERAGE' : 'D NEEDS WORK')));
$R['doctrine'] = '90 — KPI autonomie consolidé (lecture seule, agrège truth+plans+phases+health)';
$R['total_ms'] = round((microtime(true) - $t0) * 1000);
echo json_encode($R, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);