Backup #789
| ID | 789 |
| Dateipfad | /var/www/dev.campus.systemische-tools.de/src/Infrastructure/AI/ModelRegistry.php |
| Version | 1 |
| Typ |
modified |
| Größe | 8.7 KB |
| Hash | f30b1bef9ed07530cf97330e902ba6db800c51450bd86f1ab76f9ccaafe356af |
| Datum | 2025-12-23 08:05:53 |
| Geändert von | claude-code-hook |
| Grund | Claude Code Pre-Hook Backup vor Edit-Operation |
| Datei existiert |
Ja
|
Dateiinhalt
<?php
declare(strict_types=1);
namespace Infrastructure\AI;
use PDO;
/**
* Central Model Registry - Single Source of Truth for AI Models.
*
* Reads available models from ki_dev.ai_models database table.
* Replaces static ModelConfig arrays with dynamic database lookup.
*/
final class ModelRegistry
{
private PDO $pdo;
private static ?array $cache = null;
private static ?self $instance = null;
public function __construct(?PDO $pdo = null)
{
if ($pdo !== null) {
$this->pdo = $pdo;
} else {
$this->pdo = $this->createConnection();
}
}
/**
* Get singleton instance for static method compatibility.
*/
public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Create database connection to ki_dev.
*/
private function createConnection(): PDO
{
$password = $_ENV['MARIADB_ROOT_PASSWORD'] ?? '';
if (empty($password) && file_exists('/var/www/dev.campus.systemische-tools.de/.env')) {
$envContent = file_get_contents('/var/www/dev.campus.systemische-tools.de/.env');
if (preg_match('/MARIADB_ROOT_PASSWORD=(.+)/', $envContent, $matches)) {
$password = trim($matches[1]);
}
}
return new PDO(
'mysql:host=localhost;dbname=ki_dev;charset=utf8mb4',
'root',
$password,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
}
/**
* Clear cache (e.g., after Ollama sync).
*/
public static function clearCache(): void
{
self::$cache = null;
}
/**
* Get all available chat models.
*
* @return array<string, string> [full_key => display_name]
*/
public function getChatModels(): array
{
return $this->getModels(chat: true);
}
/**
* Get all vision-capable models.
*
* @return array<string, string> [full_key => display_name]
*/
public function getVisionModels(): array
{
return $this->getModels(vision: true);
}
/**
* Get all embedding models.
*
* @return array<string, string> [full_key => display_name]
*/
public function getEmbeddingModels(): array
{
return $this->getModels(embedding: true);
}
/**
* Get models with optional filters.
*
* @return array<string, string> [full_key => display_name]
*/
public function getModels(
?bool $chat = null,
?bool $vision = null,
?bool $embedding = null,
?string $provider = null
): array {
$allModels = $this->loadAllModels();
$result = [];
foreach ($allModels as $model) {
if (!$model['is_available']) {
continue;
}
if ($chat !== null && (bool) $model['is_chat'] !== $chat) {
continue;
}
if ($vision !== null && (bool) $model['is_vision'] !== $vision) {
continue;
}
if ($embedding !== null && (bool) $model['is_embedding'] !== $embedding) {
continue;
}
if ($provider !== null && $model['provider'] !== $provider) {
continue;
}
$result[$model['full_key']] = $model['display_name'];
}
return $result;
}
/**
* Get a single model by full_key.
*/
public function getModel(string $fullKey): ?array
{
$allModels = $this->loadAllModels();
foreach ($allModels as $model) {
if ($model['full_key'] === $fullKey) {
return $model;
}
}
return null;
}
/**
* Get display label for a model.
*/
public function getLabel(string $fullKey): string
{
$model = $this->getModel($fullKey);
return $model['display_name'] ?? $fullKey;
}
/**
* Check if model exists and is available.
*/
public function isValid(string $fullKey): bool
{
$model = $this->getModel($fullKey);
return $model !== null && $model['is_available'];
}
/**
* Check if model is local (Ollama).
*/
public function isLocal(string $fullKey): bool
{
return str_starts_with($fullKey, 'ollama:');
}
/**
* Get default chat model (first available by priority).
*/
public function getDefaultChatModel(): string
{
$chatModels = $this->getChatModels();
return array_key_first($chatModels) ?? 'ollama:mistral:latest';
}
/**
* Get default vision model.
*/
public function getDefaultVisionModel(): string
{
$visionModels = $this->getVisionModels();
// Prefer local vision model
foreach (array_keys($visionModels) as $key) {
if (str_starts_with($key, 'ollama:')) {
return $key;
}
}
return array_key_first($visionModels) ?? 'ollama:minicpm-v:latest';
}
/**
* Load all models from database (with caching).
*/
private function loadAllModels(): array
{
if (self::$cache !== null) {
return self::$cache;
}
$stmt = $this->pdo->query(
'SELECT id, provider, model_id, display_name, full_key,
is_available, is_chat, is_embedding, is_vision,
context_length, parameters, priority
FROM ai_models
WHERE is_available = 1
ORDER BY priority ASC'
);
self::$cache = $stmt->fetchAll(PDO::FETCH_ASSOC);
return self::$cache;
}
/**
* Sync models from Ollama (call after `ollama pull`).
* Updates is_available and last_seen_at for Ollama models.
*/
public function syncFromOllama(): array
{
$output = [];
exec('ollama list 2>/dev/null', $output, $returnCode);
if ($returnCode !== 0) {
return ['error' => 'Could not run ollama list'];
}
$ollamaModels = [];
foreach ($output as $line) {
if (preg_match('/^(\S+)\s+/', $line, $matches)) {
$modelName = $matches[1];
if ($modelName !== 'NAME') {
$ollamaModels[] = $modelName;
}
}
}
// Mark all Ollama models as unavailable first
$this->pdo->exec(
"UPDATE ai_models SET is_available = 0 WHERE provider = 'ollama'"
);
$added = [];
$updated = [];
foreach ($ollamaModels as $modelId) {
// Check if model exists
$stmt = $this->pdo->prepare(
'SELECT id FROM ai_models WHERE provider = ? AND model_id = ?'
);
$stmt->execute(['ollama', $modelId]);
$existing = $stmt->fetch();
if ($existing) {
// Update existing
$stmt = $this->pdo->prepare(
'UPDATE ai_models SET is_available = 1, last_seen_at = NOW()
WHERE provider = ? AND model_id = ?'
);
$stmt->execute(['ollama', $modelId]);
$updated[] = $modelId;
} else {
// Insert new
$displayName = $this->generateDisplayName($modelId);
$isEmbedding = str_contains($modelId, 'embed');
$isVision = str_contains($modelId, 'vision') || str_contains($modelId, 'minicpm-v');
$stmt = $this->pdo->prepare(
'INSERT INTO ai_models
(provider, model_id, display_name, is_available, is_chat, is_embedding, is_vision, last_seen_at, priority)
VALUES (?, ?, ?, 1, ?, ?, ?, NOW(), 90)'
);
$stmt->execute([
'ollama',
$modelId,
$displayName,
$isEmbedding ? 0 : 1,
$isEmbedding ? 1 : 0,
$isVision ? 1 : 0,
]);
$added[] = $modelId;
}
}
self::clearCache();
return [
'ollama_models' => $ollamaModels,
'added' => $added,
'updated' => $updated,
];
}
/**
* Generate display name from model ID.
*/
private function generateDisplayName(string $modelId): string
{
// Remove version tags
$name = preg_replace('/:latest$/', '', $modelId);
// Capitalize and format
$parts = explode(':', $name);
$baseName = ucfirst($parts[0]);
if (isset($parts[1])) {
$baseName .= ' ' . strtoupper($parts[1]);
}
return $baseName . ' (lokal)';
}
}
Vollständig herunterladen
Aktionen
Andere Versionen dieser Datei
| ID |
Version |
Typ |
Größe |
Datum |
| 1507 |
5 |
modified |
8.2 KB |
2025-12-25 18:16 |
| 1211 |
4 |
modified |
8.1 KB |
2025-12-25 10:40 |
| 863 |
3 |
modified |
7.4 KB |
2025-12-23 08:50 |
| 859 |
2 |
modified |
8.6 KB |
2025-12-23 08:47 |
| 789 |
1 |
modified |
8.7 KB |
2025-12-23 08:05 |
← Zurück zur Übersicht