Protokoll #7900
| ID | 7900 |
|---|---|
| Zeitstempel | 2025-12-23 08:50:18.032353 |
| Client | root |
| IP | Warning: Undefined array key "request_ip" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13 Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13 |
| Modell | claude-sonnet-4-20250514 |
| Status | completed |
| Tokens | 2,952 (Input: 0, Output: 0) |
| Dauer | 93 ms |
| Request-Zeit | Warning: Undefined array key "request_timestamp" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 18 |
| Response-Zeit | - |
Warning: Undefined array key "error_message" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 22
Request
{
"event": "PreToolUse",
"tool_name": "Edit",
"tool_input": {
"file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/AI\/ModelRegistry.php",
"old_string": "\/\/ @responsibility: Zentrale Modell-Registry aus ki_dev.ai_models\n\nuse PDO;\n\nfinal class ModelRegistry\n{\n private static ?array $cache = null;\n\n public function __construct(\n private PDO $pdo\n ) {\n }",
"new_string": "\/\/ @responsibility: Zentrale Modell-Registry aus ki_dev.ai_models\n\nuse Infrastructure\\Config\\DatabaseFactory;\nuse PDO;\n\nfinal class ModelRegistry\n{\n private static ?array $cache = null;\n private static ?self $instance = null;\n\n public function __construct(\n private PDO $pdo\n ) {\n }\n\n \/**\n * Get singleton instance (for static facade compatibility).\n * Prefers DI but falls back to DatabaseFactory if needed.\n *\/\n public static function getInstance(): self\n {\n if (self::$instance === null) {\n self::$instance = new self(DatabaseFactory::dev());\n }\n\n return self::$instance;\n }\n\n \/**\n * Set the singleton instance (called by DI container).\n *\/\n public static function setInstance(self $instance): void\n {\n self::$instance = $instance;\n }"
}
}
Response
{
"tool_response": {
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/AI\/ModelRegistry.php",
"oldString": "\/\/ @responsibility: Zentrale Modell-Registry aus ki_dev.ai_models\n\nuse PDO;\n\nfinal class ModelRegistry\n{\n private static ?array $cache = null;\n\n public function __construct(\n private PDO $pdo\n ) {\n }",
"newString": "\/\/ @responsibility: Zentrale Modell-Registry aus ki_dev.ai_models\n\nuse Infrastructure\\Config\\DatabaseFactory;\nuse PDO;\n\nfinal class ModelRegistry\n{\n private static ?array $cache = null;\n private static ?self $instance = null;\n\n public function __construct(\n private PDO $pdo\n ) {\n }\n\n \/**\n * Get singleton instance (for static facade compatibility).\n * Prefers DI but falls back to DatabaseFactory if needed.\n *\/\n public static function getInstance(): self\n {\n if (self::$instance === null) {\n self::$instance = new self(DatabaseFactory::dev());\n }\n\n return self::$instance;\n }\n\n \/**\n * Set the singleton instance (called by DI container).\n *\/\n public static function setInstance(self $instance): void\n {\n self::$instance = $instance;\n }",
"originalFile": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\AI;\n\n\/\/ @responsibility: Zentrale Modell-Registry aus ki_dev.ai_models\n\nuse PDO;\n\nfinal class ModelRegistry\n{\n private static ?array $cache = null;\n\n public function __construct(\n private PDO $pdo\n ) {\n }\n\n \/**\n * Clear cache (e.g., after Ollama sync).\n *\/\n public static function clearCache(): void\n {\n self::$cache = null;\n }\n\n \/**\n * Get all available chat models.\n *\n * @return array<string, string> [full_key => display_name]\n *\/\n public function getChatModels(): array\n {\n return $this->getModels(chat: true);\n }\n\n \/**\n * Get all vision-capable models.\n *\n * @return array<string, string> [full_key => display_name]\n *\/\n public function getVisionModels(): array\n {\n return $this->getModels(vision: true);\n }\n\n \/**\n * Get all embedding models.\n *\n * @return array<string, string> [full_key => display_name]\n *\/\n public function getEmbeddingModels(): array\n {\n return $this->getModels(embedding: true);\n }\n\n \/**\n * Get models with optional filters.\n *\n * @return array<string, string> [full_key => display_name]\n *\/\n public function getModels(\n ?bool $chat = null,\n ?bool $vision = null,\n ?bool $embedding = null,\n ?string $provider = null\n ): array {\n $allModels = $this->loadAllModels();\n $result = [];\n\n foreach ($allModels as $model) {\n if (!$model['is_available']) {\n continue;\n }\n if ($chat !== null && (bool) $model['is_chat'] !== $chat) {\n continue;\n }\n if ($vision !== null && (bool) $model['is_vision'] !== $vision) {\n continue;\n }\n if ($embedding !== null && (bool) $model['is_embedding'] !== $embedding) {\n continue;\n }\n if ($provider !== null && $model['provider'] !== $provider) {\n continue;\n }\n\n $result[$model['full_key']] = $model['display_name'];\n }\n\n return $result;\n }\n\n \/**\n * Get a single model by full_key.\n *\/\n public function getModel(string $fullKey): ?array\n {\n $allModels = $this->loadAllModels();\n\n foreach ($allModels as $model) {\n if ($model['full_key'] === $fullKey) {\n return $model;\n }\n }\n\n return null;\n }\n\n \/**\n * Get display label for a model.\n *\/\n public function getLabel(string $fullKey): string\n {\n $model = $this->getModel($fullKey);\n\n return $model['display_name'] ?? $fullKey;\n }\n\n \/**\n * Check if model exists and is available.\n *\/\n public function isValid(string $fullKey): bool\n {\n $model = $this->getModel($fullKey);\n\n return $model !== null && $model['is_available'];\n }\n\n \/**\n * Check if model is local (Ollama).\n *\/\n public function isLocal(string $fullKey): bool\n {\n return str_starts_with($fullKey, 'ollama:');\n }\n\n \/**\n * Get default chat model (first available by priority).\n *\/\n public function getDefaultChatModel(): string\n {\n $chatModels = $this->getChatModels();\n\n return array_key_first($chatModels) ?? 'ollama:mistral:latest';\n }\n\n \/**\n * Get default vision model.\n *\/\n public function getDefaultVisionModel(): string\n {\n $visionModels = $this->getVisionModels();\n \/\/ Prefer local vision model\n foreach (array_keys($visionModels) as $key) {\n if (str_starts_with($key, 'ollama:')) {\n return $key;\n }\n }\n\n return array_key_first($visionModels) ?? 'ollama:minicpm-v:latest';\n }\n\n \/**\n * Load all models from database (with caching).\n *\/\n private function loadAllModels(): array\n {\n if (self::$cache !== null) {\n return self::$cache;\n }\n\n $stmt = $this->pdo->query(\n 'SELECT id, provider, model_id, display_name, full_key,\n is_available, is_chat, is_embedding, is_vision,\n context_length, parameters, priority\n FROM ai_models\n WHERE is_available = 1\n ORDER BY priority ASC'\n );\n\n self::$cache = $stmt->fetchAll(PDO::FETCH_ASSOC);\n\n return self::$cache;\n }\n\n \/**\n * Sync models from Ollama (call after `ollama pull`).\n * Updates is_available and last_seen_at for Ollama models.\n *\/\n public function syncFromOllama(): array\n {\n $output = [];\n exec('ollama list 2>\/dev\/null', $output, $returnCode);\n\n if ($returnCode !== 0) {\n return ['error' => 'Could not run ollama list'];\n }\n\n $ollamaModels = [];\n foreach ($output as $line) {\n if (preg_match('\/^(\\S+)\\s+\/', $line, $matches)) {\n $modelName = $matches[1];\n if ($modelName !== 'NAME') {\n $ollamaModels[] = $modelName;\n }\n }\n }\n\n \/\/ Mark all Ollama models as unavailable first\n $this->pdo->exec(\n \"UPDATE ai_models SET is_available = 0 WHERE provider = 'ollama'\"\n );\n\n $added = [];\n $updated = [];\n\n foreach ($ollamaModels as $modelId) {\n \/\/ Check if model exists\n $stmt = $this->pdo->prepare(\n 'SELECT id FROM ai_models WHERE provider = ? AND model_id = ?'\n );\n $stmt->execute(['ollama', $modelId]);\n $existing = $stmt->fetch();\n\n if ($existing) {\n \/\/ Update existing\n $stmt = $this->pdo->prepare(\n 'UPDATE ai_models SET is_available = 1, last_seen_at = NOW()\n WHERE provider = ? AND model_id = ?'\n );\n $stmt->execute(['ollama', $modelId]);\n $updated[] = $modelId;\n } else {\n \/\/ Insert new\n $displayName = $this->generateDisplayName($modelId);\n $isEmbedding = str_contains($modelId, 'embed');\n $isVision = str_contains($modelId, 'vision') || str_contains($modelId, 'minicpm-v');\n\n $stmt = $this->pdo->prepare(\n 'INSERT INTO ai_models\n (provider, model_id, display_name, is_available, is_chat, is_embedding, is_vision, last_seen_at, priority)\n VALUES (?, ?, ?, 1, ?, ?, ?, NOW(), 90)'\n );\n $stmt->execute([\n 'ollama',\n $modelId,\n $displayName,\n $isEmbedding ? 0 : 1,\n $isEmbedding ? 1 : 0,\n $isVision ? 1 : 0,\n ]);\n $added[] = $modelId;\n }\n }\n\n self::clearCache();\n\n return [\n 'ollama_models' => $ollamaModels,\n 'added' => $added,\n 'updated' => $updated,\n ];\n }\n\n \/**\n * Generate display name from model ID.\n *\/\n private function generateDisplayName(string $modelId): string\n {\n \/\/ Remove version tags\n $name = preg_replace('\/:latest$\/', '', $modelId);\n\n \/\/ Capitalize and format\n $parts = explode(':', $name);\n $baseName = ucfirst($parts[0]);\n\n if (isset($parts[1])) {\n $baseName .= ' ' . strtoupper($parts[1]);\n }\n\n return $baseName . ' (lokal)';\n }\n}\n",
"structuredPatch": [
{
"oldStart": 6,
"oldLines": 11,
"newStart": 6,
"newLines": 13,
"lines": [
" ",
" \/\/ @responsibility: Zentrale Modell-Registry aus ki_dev.ai_models",
" ",
"+use Infrastructure\\Config\\DatabaseFactory;",
" use PDO;",
" ",
" final class ModelRegistry",
" {",
" private static ?array $cache = null;",
"+ private static ?self $instance = null;",
" ",
" public function __construct(",
" private PDO $pdo"
]
},
{
"oldStart": 18,
"oldLines": 6,
"newStart": 20,
"newLines": 27,
"lines": [
" }",
" ",
" \/**",
"+ * Get singleton instance (for static facade compatibility).",
"+ * Prefers DI but falls back to DatabaseFactory if needed.",
"+ *\/",
"+ public static function getInstance(): self",
"+ {",
"+ if (self::$instance === null) {",
"+ self::$instance = new self(DatabaseFactory::dev());",
"+ }",
"+",
"+ return self::$instance;",
"+ }",
"+",
"+ \/**",
"+ * Set the singleton instance (called by DI container).",
"+ *\/",
"+ public static function setInstance(self $instance): void",
"+ {",
"+ self::$instance = $instance;",
"+ }",
"+",
"+ \/**",
" * Clear cache (e.g., after Ollama sync).",
" *\/",
" public static function clearCache(): void"
]
}
],
"userModified": false,
"replaceAll": false
}
}