Files
html/api/opus5-plan-from-text.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);