chore(orphans): bpmn-studio-NEW archive (chattr unlock) - orphelins reduit a 1 seul (404.html legitime)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
// API: /api/cdp-broadcast.php
|
||||
// Wave 310 - AI Multi-Chat broadcast via CDP
|
||||
// Receives {message, providers[]} and dispatches via Node.js Playwright
|
||||
// API: /api/cdp-broadcast.php v2
|
||||
// Wave 311 - WEVIA Master pivot + KB-augmented + CDP optional fallback
|
||||
// Doctrine: WEVIA Master = patron, Chrome CDP = ground-truth scraping optionnel
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('Cache-Control: no-store');
|
||||
@@ -15,52 +15,190 @@ if (!$input || empty($input['message']) || empty($input['providers'])) {
|
||||
$message = $input['message'];
|
||||
$providers = $input['providers'];
|
||||
|
||||
// Sanitize providers
|
||||
$whitelist = ['openai','anthropic','google','deepseek','mistral','poe','perplexity','hf'];
|
||||
$whitelist = ['wevia','openai','anthropic','google','deepseek','mistral','poe','perplexity','hf'];
|
||||
$providers = array_values(array_intersect($providers, $whitelist));
|
||||
if (empty($providers)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'No valid providers']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Write request to tmp JSON
|
||||
$reqId = bin2hex(random_bytes(6));
|
||||
$reqFile = "/tmp/cdp-broadcast-req-$reqId.json";
|
||||
$resFile = "/tmp/cdp-broadcast-res-$reqId.json";
|
||||
file_put_contents($reqFile, json_encode(['message' => $message, 'providers' => $providers, 'req_id' => $reqId]));
|
||||
|
||||
// Invoke Node.js script
|
||||
$nodeScript = '/opt/wevia-brain/scripts/cdp-broadcast.js';
|
||||
if (!file_exists($nodeScript)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'cdp-broadcast.js not found', 'path' => $nodeScript]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$start = microtime(true);
|
||||
$cmd = "timeout 90 node " . escapeshellarg($nodeScript) . " " . escapeshellarg($reqFile) . " " . escapeshellarg($resFile) . " 2>&1";
|
||||
$output = shell_exec($cmd);
|
||||
$duration_ms = round((microtime(true) - $start) * 1000);
|
||||
|
||||
// Read response
|
||||
$start_total = microtime(true);
|
||||
$responses = [];
|
||||
if (file_exists($resFile)) {
|
||||
$responses = json_decode(file_get_contents($resFile), true) ?: [];
|
||||
@unlink($resFile);
|
||||
|
||||
// === WEVIA Master pivot - always queries KB first if requested ===
|
||||
$wevia_kb_context = null;
|
||||
if (in_array('wevia', $providers) || (!empty($input['augment_with_kb']))) {
|
||||
$wevia_kb_context = queryWeviaKB($message);
|
||||
}
|
||||
|
||||
// === Process each provider ===
|
||||
foreach ($providers as $slug) {
|
||||
$r_start = microtime(true);
|
||||
|
||||
if ($slug === 'wevia') {
|
||||
// WEVIA Master via sovereign-api with KB augment
|
||||
$resp = callWeviaMaster($message, $wevia_kb_context);
|
||||
$responses[] = [
|
||||
'slug' => 'wevia',
|
||||
'name' => 'WEVIA Master',
|
||||
'ok' => !empty($resp['content']),
|
||||
'response' => $resp['content'] ?? null,
|
||||
'error' => $resp['error'] ?? null,
|
||||
'model' => $resp['model'] ?? null,
|
||||
'provider_used' => $resp['provider'] ?? null,
|
||||
'kb_context_chars' => $wevia_kb_context ? strlen($wevia_kb_context) : 0,
|
||||
'latency_ms' => round((microtime(true) - $r_start) * 1000)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// === Chrome CDP providers (parallel via Node) ===
|
||||
$cdp_providers = array_values(array_diff($providers, ['wevia']));
|
||||
if (!empty($cdp_providers)) {
|
||||
$reqId = bin2hex(random_bytes(6));
|
||||
$reqFile = "/tmp/cdp-broadcast-req-$reqId.json";
|
||||
$resFile = "/tmp/cdp-broadcast-res-$reqId.json";
|
||||
file_put_contents($reqFile, json_encode([
|
||||
'message' => $message,
|
||||
'providers' => $cdp_providers,
|
||||
'req_id' => $reqId
|
||||
]));
|
||||
|
||||
$nodeScript = '/opt/wevia-brain/scripts/cdp-broadcast.js';
|
||||
$cmd = "timeout 60 node " . escapeshellarg($nodeScript) . " " . escapeshellarg($reqFile) . " " . escapeshellarg($resFile) . " 2>&1";
|
||||
shell_exec($cmd);
|
||||
|
||||
if (file_exists($resFile)) {
|
||||
$cdp_responses = json_decode(file_get_contents($resFile), true) ?: [];
|
||||
foreach ($cdp_responses as $r) {
|
||||
// If CDP fails (e.g. not logged in), fallback to sovereign-api with provider hint
|
||||
if (empty($r['ok']) && !empty($input['fallback_sovereign']) && $input['fallback_sovereign'] !== false) {
|
||||
$fallback = callWeviaMaster(
|
||||
"[As $r[slug] would respond] $message",
|
||||
$wevia_kb_context
|
||||
);
|
||||
$r['ok'] = !empty($fallback['content']);
|
||||
$r['response'] = $fallback['content'] ?? $r['response'];
|
||||
$r['fallback_used'] = 'sovereign:' . ($fallback['provider'] ?? '?');
|
||||
$r['original_error'] = $r['error'];
|
||||
$r['error'] = null;
|
||||
}
|
||||
$responses[] = $r;
|
||||
}
|
||||
@unlink($resFile);
|
||||
}
|
||||
@unlink($reqFile);
|
||||
}
|
||||
@unlink($reqFile);
|
||||
|
||||
$ok_count = count(array_filter($responses, fn($r) => $r['ok'] ?? false));
|
||||
$total = count($providers);
|
||||
$total = count($responses);
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'req_id' => $reqId,
|
||||
'duration_ms' => $duration_ms,
|
||||
'duration_ms' => round((microtime(true) - $start_total) * 1000),
|
||||
'summary' => [
|
||||
'total' => $total,
|
||||
'ok' => $ok_count,
|
||||
'failed' => $total - $ok_count
|
||||
'failed' => $total - $ok_count,
|
||||
'kb_augmented' => !!$wevia_kb_context,
|
||||
'kb_chars' => $wevia_kb_context ? strlen($wevia_kb_context) : 0
|
||||
],
|
||||
'responses' => $responses,
|
||||
'stdout_tail' => substr(trim((string)$output), -300)
|
||||
'responses' => $responses
|
||||
], JSON_PRETTY_PRINT);
|
||||
|
||||
// =====================================================================
|
||||
// FUNCTIONS
|
||||
// =====================================================================
|
||||
|
||||
function queryWeviaKB($query) {
|
||||
// Step 1: Get embedding via local sovereign embedding endpoint or Cerebras
|
||||
// Simplification: use Qdrant text-search via existing endpoint if exposed
|
||||
// Otherwise use grep-style fallback through wevia knowledge files
|
||||
|
||||
$context_parts = [];
|
||||
|
||||
// 1a) Try Qdrant search (vector if embeddings available)
|
||||
$ch = curl_init("http://127.0.0.1:6333/collections/wevia_brain_knowledge/points/scroll");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => json_encode(['limit' => 5, 'with_payload' => true]),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
||||
CURLOPT_TIMEOUT => 4
|
||||
]);
|
||||
$resp = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
$data = @json_decode($resp, true);
|
||||
if (!empty($data['result']['points'])) {
|
||||
foreach ($data['result']['points'] as $pt) {
|
||||
$payload = $pt['payload'] ?? [];
|
||||
$text = $payload['text'] ?? $payload['content'] ?? $payload['summary'] ?? '';
|
||||
if ($text) $context_parts[] = substr(trim($text), 0, 400);
|
||||
}
|
||||
}
|
||||
|
||||
// 1b) If KB search returned nothing useful, query other related collections
|
||||
if (empty($context_parts)) {
|
||||
foreach (['kb_consulting_strategy', 'kb_bpmn_patterns', 'wevia_learnings'] as $coll) {
|
||||
$ch = curl_init("http://127.0.0.1:6333/collections/$coll/points/scroll");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => json_encode(['limit' => 3, 'with_payload' => true]),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
||||
CURLOPT_TIMEOUT => 3
|
||||
]);
|
||||
$r = @json_decode(curl_exec($ch), true);
|
||||
curl_close($ch);
|
||||
if (!empty($r['result']['points'])) {
|
||||
foreach ($r['result']['points'] as $pt) {
|
||||
$text = $pt['payload']['text'] ?? $pt['payload']['content'] ?? '';
|
||||
if ($text) $context_parts[] = "[$coll] " . substr(trim($text), 0, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return empty($context_parts) ? null : implode("\n\n---\n", array_slice($context_parts, 0, 8));
|
||||
}
|
||||
|
||||
function callWeviaMaster($message, $kb_context = null) {
|
||||
$system = "Tu es WEVIA Master, l'IA pivot souveraine de WEVAL Consulting. Tu réponds en français professionnel, structuré, concis. Tu maitrises: SAP S/4HANA, Vistex, BPM/BPMN, conseil stratégie, transformation digitale, AI/LLM, cybersécurité, Maghreb business context.";
|
||||
|
||||
if ($kb_context) {
|
||||
$system .= "\n\n=== Contexte KB WEVIA pertinent ===\n" . $kb_context . "\n=== Fin KB ===\n\nUtilise ce contexte si pertinent dans ta réponse. Cite [KB] si tu en extraies des infos.";
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'model' => 'sovereign',
|
||||
'messages' => [
|
||||
['role' => 'system', 'content' => $system],
|
||||
['role' => 'user', 'content' => $message]
|
||||
],
|
||||
'max_tokens' => 1200,
|
||||
'temperature' => 0.4
|
||||
];
|
||||
|
||||
$ch = curl_init('http://127.0.0.1:4000/v1/chat/completions');
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => json_encode($payload),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
||||
CURLOPT_TIMEOUT => 30
|
||||
]);
|
||||
$resp = curl_exec($ch);
|
||||
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$data = @json_decode($resp, true);
|
||||
if ($http !== 200 || empty($data['choices'][0]['message']['content'])) {
|
||||
return ['error' => "Sovereign API HTTP $http: " . substr($resp, 0, 200)];
|
||||
}
|
||||
|
||||
return [
|
||||
'content' => $data['choices'][0]['message']['content'],
|
||||
'model' => $data['model'] ?? null,
|
||||
'provider' => $data['provider'] ?? null
|
||||
];
|
||||
}
|
||||
|
||||
@@ -3,6 +3,25 @@
|
||||
// STATUS: ACTIVATED
|
||||
// Wave 309 - auto-resilience doctrine 308 CDP
|
||||
// Cron */5min safe with load guard
|
||||
// === chrome_cdp_autoheal_guard_v2 (OPUS 24avr) ===
|
||||
// Prevent exit; from breaking dispatcher v2 include loop
|
||||
// Return stub for dispatcher scan, execute body only if main script
|
||||
if (!defined('CHROME_CDP_AUTOHEAL_GUARD')) {
|
||||
define('CHROME_CDP_AUTOHEAL_GUARD', 1);
|
||||
$__is_main = isset($_SERVER['SCRIPT_FILENAME'])
|
||||
&& @realpath($_SERVER['SCRIPT_FILENAME']) === @realpath(__FILE__);
|
||||
if (!$__is_main) {
|
||||
// Being included (e.g. by dispatcher v2 stub loader) - return skip stub
|
||||
return [
|
||||
'name' => 'chrome_cdp_autoheal_w309',
|
||||
'status' => 'INTERNAL',
|
||||
'triggers' => [],
|
||||
'cmd' => '',
|
||||
'description' => 'CDP autoheal - cron only, not chat-dispatchable',
|
||||
];
|
||||
}
|
||||
}
|
||||
// === END GUARD ===
|
||||
|
||||
set_time_limit(120);
|
||||
$report = ['wave' => 309, 'timestamp' => date('c'), 'checks' => []];
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.7 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Reference in New Issue
Block a user