Files
html/api/opus-arch-explainability.php
2026-04-17 19:14:29 +02:00

80 lines
3.2 KiB
PHP

<?php
// opus-arch-explainability.php — Cap 12 Audit trail cognitif (Doctrine 85)
// LLM as judge + decision path + confidence score
// Endpoint: POST /api/opus-arch-explainability.php message=... OR intent explain last
header('Content-Type: application/json');
$action = $_GET['action'] ?? $_POST['action'] ?? 'explain_last';
$db_path = '/opt/weval-ops/explain-trace.sqlite';
function ensure_db($p) {
$new = !file_exists($p);
$db = new SQLite3($p);
if ($new) {
$db->exec('CREATE TABLE IF NOT EXISTS traces (id INTEGER PRIMARY KEY AUTOINCREMENT, ts TEXT, message TEXT, route TEXT, engine TEXT, providers TEXT, confidence REAL, tokens_in INTEGER, tokens_out INTEGER, latency_ms INTEGER, session_id TEXT)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_ts ON traces(ts)');
}
return $db;
}
$db = ensure_db($db_path);
if ($action === 'log') {
// Called by master-api to log decisions
$stmt = $db->prepare('INSERT INTO traces(ts, message, route, engine, providers, confidence, tokens_in, tokens_out, latency_ms, session_id) VALUES(:ts, :msg, :route, :engine, :prov, :conf, :tin, :tout, :lat, :sess)');
$stmt->bindValue(':ts', date('c'));
$stmt->bindValue(':msg', $_POST['message'] ?? '');
$stmt->bindValue(':route', $_POST['route'] ?? '');
$stmt->bindValue(':engine', $_POST['engine'] ?? '');
$stmt->bindValue(':prov', $_POST['providers'] ?? '');
$stmt->bindValue(':conf', (float)($_POST['confidence'] ?? 0.8));
$stmt->bindValue(':tin', (int)($_POST['tokens_in'] ?? 0));
$stmt->bindValue(':tout', (int)($_POST['tokens_out'] ?? 0));
$stmt->bindValue(':lat', (int)($_POST['latency_ms'] ?? 0));
$stmt->bindValue(':sess', $_POST['session_id'] ?? session_id() ?: 'anon');
$stmt->execute();
echo json_encode(['ok'=>true, 'id'=>$db->lastInsertRowID()]);
exit;
}
if ($action === 'explain_last') {
$session = $_GET['session'] ?? 'anon';
$r = $db->query("SELECT * FROM traces WHERE session_id='" . SQLite3::escapeString($session) . "' ORDER BY id DESC LIMIT 1");
$row = $r->fetchArray(SQLITE3_ASSOC);
if (!$row) {
echo json_encode(['ok'=>false, 'error'=>'No trace found', 'session'=>$session]);
exit;
}
$explanation = "## Décision explainability
";
$explanation .= "**Message** : " . $row['message'] . "
";
$explanation .= "**Route** : " . $row['route'] . "
";
$explanation .= "**Engine** : " . $row['engine'] . "
";
$explanation .= "**Providers** : " . $row['providers'] . "
";
$explanation .= "**Confidence** : " . round($row['confidence']*100) . "%
";
$explanation .= "**Latency** : " . $row['latency_ms'] . "ms
";
$explanation .= "**Tokens** : " . $row['tokens_in'] . " in / " . $row['tokens_out'] . " out
";
$explanation .= "**Timestamp** : " . $row['ts'] . "
";
echo json_encode(['ok'=>true, 'trace'=>$row, 'explanation'=>$explanation]);
exit;
}
if ($action === 'stats') {
$r = $db->query('SELECT COUNT(*) as n, AVG(confidence) as avg_conf, AVG(latency_ms) as avg_lat FROM traces');
$stats = $r->fetchArray(SQLITE3_ASSOC);
echo json_encode(['ok'=>true, 'stats'=>$stats]);
exit;
}
echo json_encode(['ok'=>false, 'error'=>'Unknown action', 'actions'=>['log','explain_last','stats']]);