{
"event": "PostToolUse",
"raw_data": {
"session_id": "f0f145c4-ac1d-417a-b15b-271983ae648b",
"transcript_path": "\/root\/.claude\/projects\/-var-www\/f0f145c4-ac1d-417a-b15b-271983ae648b.jsonl",
"cwd": "\/var\/www\/dev.campus.systemische-tools.de",
"permission_mode": "acceptEdits",
"hook_event_name": "PostToolUse",
"tool_name": "Read",
"tool_input": {
"file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/AI\/QdrantClient.php"
},
"tool_response": {
"type": "text",
"file": {
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/AI\/QdrantClient.php",
"content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\AI;\n\n\/\/ @responsibility: HTTP-Client für Qdrant Vector-Datenbank\n\nuse Infrastructure\\Config\\CredentialService;\nuse RuntimeException;\n\nfinal class QdrantClient\n{\n private const int TIMEOUT = 60;\n\n private string $host;\n\n public function __construct(?string $host = null)\n {\n $this->host = $host ?? CredentialService::getQdrantHost();\n }\n\n \/**\n * Ensures a collection exists with proper configuration.\n *\/\n public function ensureCollection(string $collection, int $vectorSize = 1024): bool\n {\n $url = sprintf('%s\/collections\/%s', $this->host, $collection);\n\n try {\n $response = $this->request($url, [], 'GET');\n if (isset($response['result'])) {\n return true;\n }\n } catch (RuntimeException) {\n \/\/ Collection doesn't exist, create it\n }\n\n $payload = [\n 'vectors' => [\n 'size' => $vectorSize,\n 'distance' => 'Cosine',\n ],\n ];\n\n try {\n $this->request($url, $payload, 'PUT');\n\n return true;\n } catch (RuntimeException $e) {\n throw new RuntimeException('Failed to create collection: ' . $e->getMessage());\n }\n }\n\n \/**\n * Gets collection statistics.\n *\n * @return array{points_count: int, status: string}|null\n *\/\n public function getCollectionStats(string $collection): ?array\n {\n $url = sprintf('%s\/collections\/%s', $this->host, $collection);\n\n try {\n $response = $this->request($url, [], 'GET');\n\n if (!isset($response['result'])) {\n return null;\n }\n\n return [\n 'points_count' => (int) ($response['result']['points_count'] ?? 0),\n 'status' => (string) ($response['result']['status'] ?? 'unknown'),\n ];\n } catch (RuntimeException) {\n return null;\n }\n }\n\n \/**\n * Upserts a point to a collection.\n *\n * @param array<int, float> $vector\n * @param array<string, mixed> $payload\n *\/\n public function upsertPoint(string $collection, string $id, array $vector, array $payload): bool\n {\n $url = sprintf('%s\/collections\/%s\/points', $this->host, $collection);\n\n $data = [\n 'points' => [\n [\n 'id' => $id,\n 'vector' => array_values($vector),\n 'payload' => $payload,\n ],\n ],\n ];\n\n try {\n $this->request($url, $data, 'PUT');\n\n return true;\n } catch (RuntimeException) {\n return false;\n }\n }\n\n \/**\n * Searches for similar vectors.\n *\n * @param array<int, float> $vector\n * @param array<string, mixed>|null $filter\n * @return array<array<string, mixed>>\n *\/\n public function search(string $collection, array $vector, int $limit = 5, ?array $filter = null): array\n {\n $url = sprintf('%s\/collections\/%s\/points\/search', $this->host, $collection);\n\n $payload = [\n 'vector' => array_values($vector),\n 'limit' => $limit,\n 'with_payload' => true,\n ];\n\n if ($filter !== null) {\n $payload['filter'] = $filter;\n }\n\n $response = $this->request($url, $payload, 'POST');\n\n if (!isset($response['result']) || !is_array($response['result'])) {\n return [];\n }\n\n return $response['result'];\n }\n\n \/**\n * Deletes points by IDs.\n *\n * @param array<string> $ids\n *\/\n public function deletePoints(string $collection, array $ids): bool\n {\n $url = sprintf('%s\/collections\/%s\/points\/delete', $this->host, $collection);\n\n $data = [\n 'points' => $ids,\n ];\n\n try {\n $this->request($url, $data, 'POST');\n\n return true;\n } catch (RuntimeException) {\n return false;\n }\n }\n\n \/**\n * Generates a UUID v4.\n *\/\n public function generateUuid(): string\n {\n $data = random_bytes(16);\n $data[6] = chr((ord($data[6]) & 0x0f) | 0x40);\n $data[8] = chr((ord($data[8]) & 0x3f) | 0x80);\n\n return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));\n }\n\n \/**\n * Makes an HTTP request to Qdrant.\n *\n * @param array<string, mixed> $payload\n * @return array<string, mixed>\n *\/\n private function request(string $url, array $payload, string $method): array\n {\n $ch = curl_init($url);\n\n if ($ch === false) {\n throw new RuntimeException('Failed to initialize cURL');\n }\n\n $headers = ['Content-Type: application\/json'];\n\n if ($method === 'GET') {\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_TIMEOUT => self::TIMEOUT,\n CURLOPT_CONNECTTIMEOUT => 10,\n CURLOPT_HTTPHEADER => $headers,\n CURLOPT_CUSTOMREQUEST => 'GET',\n ]);\n } else {\n $jsonPayload = json_encode($payload);\n\n if ($jsonPayload === false) {\n curl_close($ch);\n\n throw new RuntimeException('Failed to encode JSON payload');\n }\n\n $headers[] = 'Content-Length: ' . strlen($jsonPayload);\n\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_TIMEOUT => self::TIMEOUT,\n CURLOPT_CONNECTTIMEOUT => 10,\n CURLOPT_HTTPHEADER => $headers,\n CURLOPT_CUSTOMREQUEST => $method,\n CURLOPT_POSTFIELDS => $jsonPayload,\n ]);\n }\n\n $result = curl_exec($ch);\n $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n $curlError = curl_error($ch);\n\n curl_close($ch);\n\n if ($result === false) {\n throw new RuntimeException(sprintf('cURL request failed: %s', $curlError ?: 'Unknown error'));\n }\n\n if ($httpCode >= 400) {\n throw new RuntimeException(sprintf('Qdrant API returned HTTP %d: %s', $httpCode, $result));\n }\n\n $decoded = json_decode((string) $result, true);\n\n return is_array($decoded) ? $decoded : [];\n }\n}\n",
"numLines": 237,
"startLine": 1,
"totalLines": 237
}
},
"tool_use_id": "toolu_018VyHDxqAxxiPTR4VKiuehJ"
}
}