registerDefaultTools(); } /** * Enregistre les outils disponibles */ private function registerDefaultTools(): void { $this->tools = [ 'web_search' => [ 'name' => 'web_search', 'description' => 'Recherche sur internet via SearXNG', 'parameters' => ['query' => 'string', 'max_results' => 'int'], 'handler' => [$this, 'toolWebSearch'] ], 'execute_code' => [ 'name' => 'execute_code', 'description' => 'Exécute du code Python/Bash/PHP', 'parameters' => ['language' => 'string', 'code' => 'string'], 'handler' => [$this, 'toolExecuteCode'] ], 'database_query' => [ 'name' => 'database_query', 'description' => 'Exécute une requête SQL SELECT', 'parameters' => ['query' => 'string', 'database' => 'string'], 'handler' => [$this, 'toolDatabaseQuery'] ], 'file_read' => [ 'name' => 'file_read', 'description' => 'Lit le contenu d\'un fichier', 'parameters' => ['path' => 'string'], 'handler' => [$this, 'toolFileRead'] ], 'file_write' => [ 'name' => 'file_write', 'description' => 'Écrit du contenu dans un fichier', 'parameters' => ['path' => 'string', 'content' => 'string'], 'handler' => [$this, 'toolFileWrite'] ], 'calculator' => [ 'name' => 'calculator', 'description' => 'Calculs mathématiques', 'parameters' => ['expression' => 'string'], 'handler' => [$this, 'toolCalculator'] ], 'system_info' => [ 'name' => 'system_info', 'description' => 'Information système (CPU, RAM, disk, services)', 'parameters' => ['metric' => 'string'], 'handler' => [$this, 'toolSystemInfo'] ] ]; } /** * Génère la description des outils pour le system prompt */ public function getToolsPrompt(): string { $prompt = "Tu as accès aux outils suivants:\n\n"; foreach ($this->tools as $tool) { $params = json_encode($tool['parameters']); $prompt .= "- **{$tool['name']}**: {$tool['description']}\n Paramètres: $params\n\n"; } $prompt .= "Pour utiliser un outil, réponds avec:\n"; $prompt .= "{\"name\": \"tool_name\", \"params\": {...}}\n"; return $prompt; } /** * Parse et exécute les tool calls dans une réponse LLM */ public function processResponse(string $response): array { $calls = []; $results = []; preg_match_all('/(.*?)<\/tool_call>/s', $response, $matches); foreach ($matches[1] as $callJson) { $call = json_decode($callJson, true); if (!$call || !isset($call['name'])) continue; $calls[] = $call; if (isset($this->tools[$call['name']])) { $handler = $this->tools[$call['name']]['handler']; $params = $call['params'] ?? []; try { $result = call_user_func($handler, $params); $results[] = ['tool' => $call['name'], 'success' => true, 'result' => $result]; } catch (\Exception $e) { $results[] = ['tool' => $call['name'], 'success' => false, 'error' => $e->getMessage()]; } } } $this->history = array_merge($this->history, $results); return ['calls' => $calls, 'results' => $results, 'had_tools' => !empty($calls)]; } // Tool implementations public function toolWebSearch(array $params): string { $query = $params['query'] ?? ''; $max = min($params['max_results'] ?? 5, 10); $ch = curl_init("http://127.0.0.1:8888/search?q=" . urlencode($query) . "&format=json&engines=google,duckduckgo&results=$max"); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15]); $raw = curl_exec($ch); curl_close($ch); $data = json_decode($raw, true); $results = []; foreach (($data['results'] ?? []) as $r) { $results[] = "**{$r['title']}**\n{$r['content']}\nURL: {$r['url']}"; } return implode("\n---\n", $results) ?: "Aucun résultat trouvé."; } public function toolExecuteCode(array $params): string { $lang = $params['language'] ?? 'python'; $code = $params['code'] ?? ''; $tmp = '/tmp/wevia_exec_' . uniqid(); switch ($lang) { case 'python': file_put_contents("$tmp.py", $code); $out = shell_exec("timeout 30 python3 $tmp.py 2>&1"); @unlink("$tmp.py"); break; case 'bash': file_put_contents("$tmp.sh", $code); $out = shell_exec("timeout 30 bash $tmp.sh 2>&1"); @unlink("$tmp.sh"); break; case 'php': file_put_contents("$tmp.php", "&1"); @unlink("$tmp.php"); break; default: return "Langage non supporté: $lang"; } return trim($out ?? '(no output)'); } public function toolDatabaseQuery(array $params): string { $query = $params['query'] ?? ''; $db = $params['database'] ?? 'wevia_db'; // Security: SELECT only if (!preg_match('/^\s*SELECT/i', $query)) { return "ERROR: Seules les requêtes SELECT sont autorisées."; } try { $pdo = new PDO("pgsql:host=127.0.0.1;dbname=$db", "postgres", ""); $stmt = $pdo->query($query); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); return json_encode($rows, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); } catch (\Exception $e) { return "SQL ERROR: " . $e->getMessage(); } } public function toolFileRead(array $params): string { $path = $params['path'] ?? ''; if (!file_exists($path)) return "Fichier non trouvé: $path"; $content = file_get_contents($path); if (strlen($content) > 10000) { return substr($content, 0, 5000) . "\n...[TRUNCATED]...\n" . substr($content, -2000); } return $content; } public function toolFileWrite(array $params): string { $path = $params['path'] ?? ''; $content = $params['content'] ?? ''; $dir = dirname($path); if (!is_dir($dir)) @mkdir($dir, 0755, true); file_put_contents($path, $content); return "Fichier écrit: $path (" . strlen($content) . " bytes)"; } public function toolCalculator(array $params): string { $expr = $params['expression'] ?? ''; $safe = preg_replace('/[^0-9+\-*\/().%\s]/', '', $expr); try { $result = eval("return $safe;"); return "$expr = $result"; } catch (\Throwable $e) { return "Erreur calcul: " . $e->getMessage(); } } public function toolSystemInfo(array $params): string { $metric = $params['metric'] ?? 'all'; $info = []; if (in_array($metric, ['all', 'cpu'])) { $info[] = "CPU: " . trim(shell_exec("nproc")) . " cores, load: " . trim(shell_exec("uptime | awk -F'load average:' '{print \$2}'")); } if (in_array($metric, ['all', 'ram'])) { $info[] = "RAM: " . trim(shell_exec("free -h | grep Mem | awk '{print \$3\"/\"\$2}'")); } if (in_array($metric, ['all', 'disk'])) { $info[] = "Disk: " . trim(shell_exec("df -h / | tail -1 | awk '{print \$3\"/\"\$2\" (\"\$5\" used)\"}'")); } if (in_array($metric, ['all', 'gpu'])) { $info[] = "GPU: " . trim(shell_exec("nvidia-smi --query-gpu=name,memory.used,memory.total --format=csv,noheader 2>/dev/null") ?: "N/A"); } return implode("\n", $info); } }