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);
}
}