Protokoll #7900

ID7900
Zeitstempel2025-12-23 08:50:18.032353
Clientroot
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
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens2,952 (Input: 0, Output: 0)
Dauer93 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
    }
}
← Vorheriger Zur Liste Nächster →