80 lines
3.2 KiB
PHP
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']]);
|