true, CURLOPT_TIMEOUT => 10, CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], ]); if ($data) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); $r = curl_exec($ch); curl_close($ch); return json_decode($r ?: '{}', true); } $result = ['ok' => true, 'action' => $action, 'ts' => date('c')]; if ($action === 'sources') { // Source 1: Disk /skills/ (with and without SKILL.md) $all_dirs = glob('/var/www/html/skills/*', GLOB_ONLYDIR); $disk_skills = []; $big_catalogs = []; foreach ($all_dirs as $dir) { $name = basename($dir); $skill_md = "$dir/SKILL.md"; $has_md = file_exists($skill_md); $desc = ''; $sub_count = 0; if ($has_md) { $content = @file_get_contents($skill_md); if ($content) { foreach (explode("\n", $content) as $line) { $line = trim($line); if ($line && !str_starts_with($line, '#') && !str_starts_with($line, '---')) { $desc = substr($line, 0, 200); break; } } } $disk_skills[] = [ 'name' => $name, 'type' => 'skill', 'desc' => $desc, 'path' => "/skills/$name/SKILL.md", ]; } else { // Count sub-skills inside catalog folder $sub_skills = glob("$dir/*", GLOB_ONLYDIR); $sub_count = count($sub_skills); $readme = @file_get_contents("$dir/README.md") ?: @file_get_contents("$dir/CATALOG.md"); $readme_desc = ''; if ($readme) { foreach (explode("\n", $readme) as $line) { $line = trim($line); if ($line && !str_starts_with($line, '#')) { $readme_desc = substr($line, 0, 200); break; } } } $big_catalogs[] = [ 'name' => $name, 'type' => 'catalog', 'sub_count' => $sub_count, 'desc' => $readme_desc ?: "Catalog avec $sub_count sub-items", 'path' => "/skills/$name/", 'samples' => array_slice(array_map('basename', $sub_skills), 0, 5), ]; } } // Source 2: Qdrant weval_skills $qdrant_info = qdrant_call("$QDRANT/collections/weval_skills"); $qdrant_points = $qdrant_info['result']['points_count'] ?? 0; // Source 3: Tools registry $reg = @json_decode(@file_get_contents('/var/www/html/api/wevia-tool-registry.json'), true); $tools_count = isset($reg['tools']) && is_array($reg['tools']) ? count($reg['tools']) : 0; $tools_sample = []; if ($tools_count) { foreach (array_slice($reg['tools'], 0, 10) as $t) { $tools_sample[] = ['name' => $t['name'] ?? $t['id'] ?? '?', 'desc' => substr($t['desc'] ?? $t['description'] ?? $t['kw'] ?? $t['cmd'] ?? '', 0, 150)]; } } // Source 4: Arena declared $arena = @json_decode(@file_get_contents('/var/www/html/api/arena-intent-registry.json'), true); $arena_skills = $arena['total_skills'] ?? 0; $total = count($disk_skills) + $qdrant_points + $tools_count + $arena_skills; $result['sources'] = [ 'source_1_disk_skill_md' => [ 'label' => '/skills/ dossiers avec SKILL.md · vitrine visible', 'count' => count($disk_skills), 'url' => '/skills/', 'items' => $disk_skills, ], 'source_2_disk_big_catalogs' => [ 'label' => '/skills/ catalogs avec sub-skills · deer-flow/paperclip/etc', 'count' => count($big_catalogs), 'total_sub_items' => array_sum(array_column($big_catalogs, 'sub_count')), 'items' => $big_catalogs, ], 'source_3_qdrant_vectorized' => [ 'label' => 'Qdrant weval_skills · vectorisés pour semantic search', 'count' => $qdrant_points, 'url' => '/api/skills-explorer-api.php?action=qdrant_sample', 'note' => 'Indexés depuis /opt/deer-flow/skills/ · searchable via embeddings', ], 'source_4_tools_registry' => [ 'label' => 'wevia-tool-registry.json · tools callable', 'count' => $tools_count, 'url' => '/api/wevia-tool-registry.json', 'samples' => $tools_sample, ], 'source_5_arena_declared' => [ 'label' => 'Arena registry · skills déclarés (intents associés)', 'count' => $arena_skills, 'url' => '/api/arena-intent-registry.json', ], 'TOTAL_ALL_SOURCES' => $total, ]; } if ($action === 'qdrant_sample') { $limit = (int)($_GET['limit'] ?? 50); $limit = min(100, max(10, $limit)); $offset = $_GET['offset'] ?? null; $payload = ['limit' => $limit, 'with_payload' => true]; if ($offset) $payload['offset'] = $offset; $res = qdrant_call("$QDRANT/collections/weval_skills/points/scroll", 'POST', $payload); $points = $res['result']['points'] ?? []; $skills = []; foreach ($points as $p) { $pl = $p['payload'] ?? []; $skills[] = [ 'id' => $p['id'] ?? '?', 'name' => $pl['name'] ?? '?', 'desc' => substr($pl['desc'] ?? '', 0, 250), 'source' => $pl['source'] ?? '?', 'path' => $pl['path'] ?? '', ]; } $result['qdrant_skills'] = $skills; $result['count'] = count($skills); $result['next_offset'] = $res['result']['next_page_offset'] ?? null; } if ($action === 'search' && !empty($_GET['q'])) { $q = strtolower($_GET['q']); // Scroll Qdrant with payload filter $matches = []; $offset = null; $scanned = 0; $max_scans = 5; // scroll 5 pages max for ($i = 0; $i < $max_scans; $i++) { $payload = ['limit' => 200, 'with_payload' => true]; if ($offset) $payload['offset'] = $offset; $res = qdrant_call("$QDRANT/collections/weval_skills/points/scroll", 'POST', $payload); $points = $res['result']['points'] ?? []; $scanned += count($points); foreach ($points as $p) { $pl = $p['payload'] ?? []; $name = strtolower($pl['name'] ?? ''); $desc = strtolower($pl['desc'] ?? ''); if (strpos($name, $q) !== false || strpos($desc, $q) !== false) { $matches[] = [ 'id' => $p['id'], 'name' => $pl['name'] ?? '?', 'desc' => substr($pl['desc'] ?? '', 0, 250), 'source' => $pl['source'] ?? '?', 'path' => $pl['path'] ?? '', ]; if (count($matches) >= 50) break 2; } } $offset = $res['result']['next_page_offset'] ?? null; if (!$offset) break; } $result['query'] = $q; $result['scanned'] = $scanned; $result['matches_count'] = count($matches); $result['matches'] = $matches; } if ($action === 'tools') { $reg = @json_decode(@file_get_contents('/var/www/html/api/wevia-tool-registry.json'), true); $tools_list = []; if (isset($reg['tools']) && is_array($reg['tools'])) { $lim = (int)($_GET['limit'] ?? 0); $off = (int)($_GET['offset'] ?? 0); $all = $reg['tools']; $sliced = $lim > 0 ? array_slice($all, $off, $lim) : $all; foreach ($sliced as $t) { $tools_list[] = [ 'id' => $t['id'] ?? $t['name'] ?? '', 'name' => $t['name'] ?? $t['id'] ?? '', 'kw' => $t['kw'] ?? '', 'desc' => substr((string)($t['desc'] ?? $t['description'] ?? $t['kw'] ?? $t['cmd'] ?? ''), 0, 200), 'cmd' => isset($t['cmd']) ? substr((string)$t['cmd'], 0, 100) : '', 'category' => $t['category'] ?? $t['cat'] ?? 'general', ]; } } $result['total'] = isset($reg['tools']) ? count($reg['tools']) : 0; $result['returned'] = count($tools_list); $result['tools'] = $tools_list; } echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);