{
"tool_response": {
"type": "update",
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/AI\/ModelConfig.php",
"content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\AI;\n\n\/**\n * Central configuration for available AI models.\n * Used by Chat, Content Studio, and other AI-powered features.\n *\n * NOW READS FROM DATABASE via ModelRegistry (Single Source of Truth).\n * Legacy static interface preserved for backward compatibility.\n *\/\nfinal class ModelConfig\n{\n \/**\n * Get all available chat models.\n *\n * @return array<string, string> [model_key => display_name]\n *\/\n public static function getAll(): array\n {\n return ModelRegistry::getInstance()->getChatModels();\n }\n\n \/**\n * Get vision-capable models.\n *\n * @return array<string, string> [model_key => display_name]\n *\/\n public static function getVisionModels(): array\n {\n return ModelRegistry::getInstance()->getVisionModels();\n }\n\n \/**\n * Get display label for a model ID.\n *\/\n public static function getLabel(string $modelId): string\n {\n return ModelRegistry::getInstance()->getLabel($modelId);\n }\n\n \/**\n * Check if a model ID is valid.\n *\/\n public static function isValid(string $modelId): bool\n {\n $registry = ModelRegistry::getInstance();\n\n \/\/ Check in registry first\n if ($registry->isValid($modelId)) {\n return true;\n }\n\n \/\/ Fallback: accept any claude- or ollama: prefixed model\n return str_starts_with($modelId, 'claude-')\n || str_starts_with($modelId, 'ollama:');\n }\n\n \/**\n * Validate and return model, or default if invalid.\n *\/\n public static function validate(string $modelId): string\n {\n return self::isValid($modelId) ? $modelId : self::getDefaultModel();\n }\n\n \/**\n * Check if model is a local (Ollama) model.\n *\/\n public static function isLocal(string $modelId): bool\n {\n return str_starts_with($modelId, 'ollama:');\n }\n\n \/**\n * Get default chat model.\n *\/\n public static function getDefaultModel(): string\n {\n return ModelRegistry::getInstance()->getDefaultChatModel();\n }\n\n \/**\n * Get default vision model.\n *\/\n public static function getDefaultVisionModel(): string\n {\n return ModelRegistry::getInstance()->getDefaultVisionModel();\n }\n\n \/**\n * Force refresh of model cache (e.g., after Ollama sync).\n *\/\n public static function clearCache(): void\n {\n ModelRegistry::clearCache();\n }\n\n \/**\n * Sync models from Ollama.\n *\n * @return array Sync results\n *\/\n public static function syncFromOllama(): array\n {\n return ModelRegistry::getInstance()->syncFromOllama();\n }\n}\n",
"structuredPatch": [
{
"oldStart": 7,
"oldLines": 60,
"newStart": 7,
"newLines": 30,
"lines": [
" \/**",
" * Central configuration for available AI models.",
" * Used by Chat, Content Studio, and other AI-powered features.",
"+ *",
"+ * NOW READS FROM DATABASE via ModelRegistry (Single Source of Truth).",
"+ * Legacy static interface preserved for backward compatibility.",
" *\/",
" final class ModelConfig",
" {",
" \/**",
"- * All available models with their display labels.",
"- * Format: 'model-id' => 'Display Label'",
"+ * Get all available chat models.",
"+ *",
"+ * @return array<string, string> [model_key => display_name]",
" *\/",
"- public const MODELS = [",
"- \/\/ Anthropic Claude",
"- 'claude-opus-4-5-20251101' => 'Claude Opus 4.5',",
"- 'claude-sonnet-4-20250514' => 'Claude Sonnet 4',",
"- \/\/ Local Ollama models (Text)",
"- 'ollama:gemma3:4b-it-qat' => 'Gemma 3 (lokal)',",
"- 'ollama:mistral:latest' => 'Mistral (lokal)',",
"- 'ollama:llama3.2:latest' => 'Llama 3.2 (lokal)',",
"- 'ollama:gpt-oss:20b' => 'GPT-OSS 20B (lokal)',",
"- \/\/ Local Ollama models (Vision)",
"- 'ollama:minicpm-v:latest' => 'MiniCPM-V (Vision)',",
"- ];",
"-",
"- \/**",
"- * Vision-capable models for image\/document analysis.",
"- *\/",
"- public const VISION_MODELS = [",
"- 'ollama:minicpm-v:latest' => 'MiniCPM-V (Vision, lokal)',",
"- 'claude-opus-4-5-20251101' => 'Claude Opus 4.5 (Vision)',",
"- 'claude-sonnet-4-20250514' => 'Claude Sonnet 4 (Vision)',",
"- ];",
"-",
"- \/**",
"- * Default vision model for document analysis.",
"- *\/",
"- public const DEFAULT_VISION_MODEL = 'ollama:minicpm-v:latest';",
"-",
"- \/**",
"- * Default model for new sessions\/orders.",
"- * Local Ollama model for cost-efficiency.",
"- *\/",
"- public const DEFAULT_MODEL = 'ollama:gemma3:4b-it-qat';",
"-",
"- \/**",
"- * Get all models as array.",
"- *\/",
" public static function getAll(): array",
" {",
"- return self::MODELS;",
"+ return ModelRegistry::getInstance()->getChatModels();",
" }",
" ",
" \/**",
" * Get vision-capable models.",
"+ *",
"+ * @return array<string, string> [model_key => display_name]",
" *\/",
" public static function getVisionModels(): array",
" {",
"- return self::VISION_MODELS;",
"+ return ModelRegistry::getInstance()->getVisionModels();",
" }",
" ",
" \/**"
]
},
{
"oldStart": 68,
"oldLines": 7,
"newStart": 38,
"newLines": 7,
"lines": [
" *\/",
" public static function getLabel(string $modelId): string",
" {",
"- return self::MODELS[$modelId] ?? $modelId;",
"+ return ModelRegistry::getInstance()->getLabel($modelId);",
" }",
" ",
" \/**"
]
},
{
"oldStart": 76,
"oldLines": 8,
"newStart": 46,
"newLines": 15,
"lines": [
" *\/",
" public static function isValid(string $modelId): bool",
" {",
"- return isset(self::MODELS[$modelId])",
"- || str_starts_with($modelId, 'claude-')",
"+ $registry = ModelRegistry::getInstance();",
"+",
"+ \/\/ Check in registry first",
"+ if ($registry->isValid($modelId)) {",
"+ return true;",
"+ }",
"+",
"+ \/\/ Fallback: accept any claude- or ollama: prefixed model",
"+ return str_starts_with($modelId, 'claude-')",
" || str_starts_with($modelId, 'ollama:');",
" }",
" "
]
},
{
"oldStart": 86,
"oldLines": 7,
"newStart": 63,
"newLines": 7,
"lines": [
" *\/",
" public static function validate(string $modelId): string",
" {",
"- return self::isValid($modelId) ? $modelId : self::DEFAULT_MODEL;",
"+ return self::isValid($modelId) ? $modelId : self::getDefaultModel();",
" }",
" ",
" \/**"
]
},
{
"oldStart": 96,
"oldLines": 4,
"newStart": 73,
"newLines": 38,
"lines": [
" {",
" return str_starts_with($modelId, 'ollama:');",
" }",
"+",
"+ \/**",
"+ * Get default chat model.",
"+ *\/",
"+ public static function getDefaultModel(): string",
"+ {",
"+ return ModelRegistry::getInstance()->getDefaultChatModel();",
"+ }",
"+",
"+ \/**",
"+ * Get default vision model.",
"+ *\/",
"+ public static function getDefaultVisionModel(): string",
"+ {",
"+ return ModelRegistry::getInstance()->getDefaultVisionModel();",
"+ }",
"+",
"+ \/**",
"+ * Force refresh of model cache (e.g., after Ollama sync).",
"+ *\/",
"+ public static function clearCache(): void",
"+ {",
"+ ModelRegistry::clearCache();",
"+ }",
"+",
"+ \/**",
"+ * Sync models from Ollama.",
"+ *",
"+ * @return array Sync results",
"+ *\/",
"+ public static function syncFromOllama(): array",
"+ {",
"+ return ModelRegistry::getInstance()->syncFromOllama();",
"+ }",
" }"
]
}
],
"originalFile": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\AI;\n\n\/**\n * Central configuration for available AI models.\n * Used by Chat, Content Studio, and other AI-powered features.\n *\/\nfinal class ModelConfig\n{\n \/**\n * All available models with their display labels.\n * Format: 'model-id' => 'Display Label'\n *\/\n public const MODELS = [\n \/\/ Anthropic Claude\n 'claude-opus-4-5-20251101' => 'Claude Opus 4.5',\n 'claude-sonnet-4-20250514' => 'Claude Sonnet 4',\n \/\/ Local Ollama models (Text)\n 'ollama:gemma3:4b-it-qat' => 'Gemma 3 (lokal)',\n 'ollama:mistral:latest' => 'Mistral (lokal)',\n 'ollama:llama3.2:latest' => 'Llama 3.2 (lokal)',\n 'ollama:gpt-oss:20b' => 'GPT-OSS 20B (lokal)',\n \/\/ Local Ollama models (Vision)\n 'ollama:minicpm-v:latest' => 'MiniCPM-V (Vision)',\n ];\n\n \/**\n * Vision-capable models for image\/document analysis.\n *\/\n public const VISION_MODELS = [\n 'ollama:minicpm-v:latest' => 'MiniCPM-V (Vision, lokal)',\n 'claude-opus-4-5-20251101' => 'Claude Opus 4.5 (Vision)',\n 'claude-sonnet-4-20250514' => 'Claude Sonnet 4 (Vision)',\n ];\n\n \/**\n * Default vision model for document analysis.\n *\/\n public const DEFAULT_VISION_MODEL = 'ollama:minicpm-v:latest';\n\n \/**\n * Default model for new sessions\/orders.\n * Local Ollama model for cost-efficiency.\n *\/\n public const DEFAULT_MODEL = 'ollama:gemma3:4b-it-qat';\n\n \/**\n * Get all models as array.\n *\/\n public static function getAll(): array\n {\n return self::MODELS;\n }\n\n \/**\n * Get vision-capable models.\n *\/\n public static function getVisionModels(): array\n {\n return self::VISION_MODELS;\n }\n\n \/**\n * Get display label for a model ID.\n *\/\n public static function getLabel(string $modelId): string\n {\n return self::MODELS[$modelId] ?? $modelId;\n }\n\n \/**\n * Check if a model ID is valid.\n *\/\n public static function isValid(string $modelId): bool\n {\n return isset(self::MODELS[$modelId])\n || str_starts_with($modelId, 'claude-')\n || str_starts_with($modelId, 'ollama:');\n }\n\n \/**\n * Validate and return model, or default if invalid.\n *\/\n public static function validate(string $modelId): string\n {\n return self::isValid($modelId) ? $modelId : self::DEFAULT_MODEL;\n }\n\n \/**\n * Check if model is a local (Ollama) model.\n *\/\n public static function isLocal(string $modelId): bool\n {\n return str_starts_with($modelId, 'ollama:');\n }\n}\n"
}
}