157 lines
7.0 KiB
PHP
157 lines
7.0 KiB
PHP
<?php
|
|
// OPUS5 — Plan From Text (doctrine 89)
|
|
// Parse NL description → génère plan structuré (steps + depends_on) + crée dans registry
|
|
// Connecteurs NL: "puis", "ensuite", "après", "et", "parallèle", ","
|
|
// Actions reconnues: verifier/check/test/status/list/run + endpoint cible
|
|
header('Content-Type: application/json');
|
|
$R = ['ts'=>date('c'), 'source'=>'opus5-plan-from-text'];
|
|
|
|
$raw = file_get_contents('php://input');
|
|
$d = json_decode($raw, true) ?: [];
|
|
$text = (string)($d['text'] ?? $d['description'] ?? '');
|
|
$auto_create = !empty($d['auto_create']);
|
|
$auto_execute = !empty($d['auto_execute']);
|
|
|
|
if (!$text) { http_response_code(400); echo json_encode(['err'=>'no_text']); exit; }
|
|
|
|
// === STEP 1 : Tokenization + normalisation ===
|
|
$text_clean = trim(preg_replace('/\s+/', ' ', $text));
|
|
$R['input_text'] = $text_clean;
|
|
|
|
// Split sur connecteurs séquentiels (puis, ensuite, après, et, ",")
|
|
// Garder "en parallèle" comme signal pour depends_on vide
|
|
$parallel_mode = preg_match('/parall[eè]le|simultan[eé]/iu', $text_clean) > 0;
|
|
|
|
// Découpage phrases
|
|
$chunks = preg_split('/\s+(?:puis|ensuite|apr[eè]s|après|then|et|and|,)\s+/iu', $text_clean);
|
|
$chunks = array_values(array_filter(array_map('trim', $chunks), function($c) { return strlen($c) > 3; }));
|
|
|
|
$R['chunks'] = $chunks;
|
|
$R['chunks_count'] = count($chunks);
|
|
$R['parallel_mode'] = $parallel_mode;
|
|
|
|
// === STEP 2 : NER endpoints + actions ===
|
|
// Catalogue de patterns mappant NL → endpoints sûrs (whitelist-only)
|
|
$action_catalog = [
|
|
// Regex → [name, url, method, payload]
|
|
'/\b(non[\s-]?reg|nonreg|regression)\b/iu' => ['check_nonreg', '/api/nonreg-api.php?cat=all', 'GET', null],
|
|
'/\b(l99|layers)\b/iu' => ['check_l99', '/api/l99-state.json', 'GET', null],
|
|
'/\b(cache[\s-]?stat|predictive[\s-]?cache|cache)\b/iu' => ['check_cache', '/api/opus5-predictive-cache.php?action=stats', 'GET', null],
|
|
'/\b(task[\s-]?list|task[\s-]?stream)\b/iu' => ['list_tasks', '/api/opus5-task-stream.php?path=list&limit=10', 'GET', null],
|
|
'/\b(plan[\s-]?list|list[\s-]?plans|plans)\b/iu' => ['list_plans', '/api/opus5-plan-registry.php?action=list&limit=10', 'GET', null],
|
|
'/\b(ethica[\s-]?stat|hcps|ethica)\b/iu' => ['check_ethica', '/api/ethica-stats-api.php', 'GET', null],
|
|
'/\b(gpu[\s-]?grid|grid[\s-]?gpu|parallel)\b/iu' => ['check_gpu_grid', '/api/opus5-gpu-grid.php?action=health', 'GET', null],
|
|
'/\b(ssh[\s-]?tmux|tmux|s95[\s-]?health)\b/iu' => ['check_ssh_tmux', '/api/opus5-ssh-tmux-stream.php?action=health', 'GET', null],
|
|
'/\b(plugin[\s-]?store|plugin[\s-]?list|plugins)\b/iu' => ['list_plugins', '/api/opus5-plugin-store.php?action=list', 'GET', null],
|
|
'/\b(knowledge[\s-]?graph|kg[\s-]?stat|graph[\s-]?stat)\b/iu' => ['check_kg', '/api/opus5-knowledge-graph.php?action=stats', 'GET', null],
|
|
'/\b(orchestrator[\s-]?v3|orch[\s-]?v3|meta[\s-]?orch)\b/iu' => ['ping_orch_v3', '/api/opus5-autonomous-orchestrator-v3.php', 'POST', ['message'=>'ping','session'=>'plan-gen']],
|
|
'/\b(n8n|workflow[\s-]?list)\b/iu' => ['list_n8n_workflows', '/api/opus5-n8n-generator.php?action=list', 'GET', null],
|
|
'/\b(truth[\s-]?registry|truth[\s-]?table)\b/iu' => ['check_truth', '/api/wevia-truth-registry.json', 'GET', null],
|
|
];
|
|
|
|
$steps = [];
|
|
foreach ($chunks as $idx => $chunk) {
|
|
$chunk_lower = strtolower($chunk);
|
|
$matched = false;
|
|
foreach ($action_catalog as $pattern => $action) {
|
|
if (preg_match($pattern, $chunk_lower)) {
|
|
$step_def = [
|
|
'name' => $action[0] . '_' . ($idx + 1),
|
|
'type' => 'http_check',
|
|
'url' => $action[1],
|
|
'method' => $action[2],
|
|
];
|
|
if ($action[3] !== null) {
|
|
$step_def['payload'] = $action[3];
|
|
}
|
|
// Dependencies : séquentiel par défaut, sauf si parallel_mode
|
|
if (!$parallel_mode && $idx > 0) {
|
|
$step_def['depends_on'] = [$idx]; // step_order précédent
|
|
}
|
|
// Metadata : source chunk
|
|
$step_def['_source_chunk'] = $chunk;
|
|
$step_def['_matched_pattern'] = $pattern;
|
|
$steps[] = $step_def;
|
|
$matched = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!$matched) {
|
|
$R['unmatched_chunks'][] = $chunk;
|
|
}
|
|
}
|
|
|
|
$R['steps_generated'] = count($steps);
|
|
$R['steps'] = $steps;
|
|
|
|
if (count($steps) === 0) {
|
|
$R['err'] = 'no_actions_recognized';
|
|
$R['hint'] = 'Reformule avec mots-cles: nonreg, cache, plans, hcps, tmux, grid, plugin, graph, workflow, etc.';
|
|
echo json_encode($R, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
// === STEP 3 : Auto-create dans registry si demandé ===
|
|
if ($auto_create) {
|
|
$plan_name = substr("AutoPlan — " . $text_clean, 0, 255);
|
|
$create_payload = [
|
|
'name' => $plan_name,
|
|
'description' => $text_clean,
|
|
'priority' => 0,
|
|
'metadata' => [
|
|
'generated_by' => 'opus5-plan-from-text',
|
|
'source_text' => $text_clean,
|
|
'parallel_mode' => $parallel_mode
|
|
],
|
|
'steps' => $steps
|
|
];
|
|
|
|
$ch = curl_init('http://127.0.0.1/api/opus5-plan-registry.php?action=create');
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => json_encode($create_payload),
|
|
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 10
|
|
]);
|
|
$create_resp = curl_exec($ch);
|
|
$create_http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
$created = @json_decode((string)$create_resp, true);
|
|
|
|
$R['created'] = [
|
|
'http' => $create_http,
|
|
'plan_id' => $created['plan_id'] ?? null,
|
|
'steps_created' => $created['steps_created'] ?? 0
|
|
];
|
|
$pid = $created['plan_id'] ?? null;
|
|
|
|
// === STEP 4 : Auto-execute si demandé ===
|
|
if ($auto_execute && $pid) {
|
|
$exec_payload = ['plan_id' => $pid, 'dry_run' => false, 'max_parallel' => 5];
|
|
$ch = curl_init('http://127.0.0.1/api/opus5-plan-orchestrator.php?action=execute');
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => json_encode($exec_payload),
|
|
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 60
|
|
]);
|
|
$exec_resp = curl_exec($ch);
|
|
$exec_http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
$executed = @json_decode((string)$exec_resp, true);
|
|
|
|
$R['executed'] = [
|
|
'http' => $exec_http,
|
|
'rounds' => $executed['rounds'] ?? null,
|
|
'done' => $executed['done_count'] ?? 0,
|
|
'failed' => $executed['failed_count'] ?? 0,
|
|
'final_status' => $executed['final_status'] ?? null
|
|
];
|
|
}
|
|
}
|
|
|
|
$R['doctrine'] = '89 — plan from text NL parser (patterns → steps with depends_on auto)';
|
|
echo json_encode($R, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|