From 9aa2bbcda48921b180ad4bbea5e87bbffc436152 Mon Sep 17 00:00:00 2001 From: team 1 Date: Wed, 18 Feb 2026 10:26:44 +0100 Subject: [PATCH] add health check --- config/services.yaml | 6 + src/Command/VectorHealthCheckCommand.php | 35 +++ src/Controller/Admin/DashboardController.php | 10 +- src/Service/Admin/IndexNdjsonInspector.php | 4 +- src/Vector/VectorIndexHealthService.php | 93 ++++++ templates/admin/dashboard/index.html.twig | 279 +++++++++++------- templates/admin/ingest_profile/list.html.twig | 4 +- 7 files changed, 323 insertions(+), 108 deletions(-) create mode 100644 src/Command/VectorHealthCheckCommand.php create mode 100644 src/Vector/VectorIndexHealthService.php diff --git a/config/services.yaml b/config/services.yaml index dc45f61..3275c52 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -172,3 +172,9 @@ services: arguments: $ndJsonPath: '%mto.knowledge.ndjson%' $indexMetaPath: '%mto.knowledge.index_meta%' + + App\Vector\VectorIndexHealthService: + arguments: + $indexNdjsonPath: '%kernel.project_dir%/var/knowledge/index.ndjson' + $vectorIndexPath: '%kernel.project_dir%/var/knowledge/vector.index' + $vectorMetaPath: '%kernel.project_dir%/var/knowledge/vector.index.meta.json' \ No newline at end of file diff --git a/src/Command/VectorHealthCheckCommand.php b/src/Command/VectorHealthCheckCommand.php new file mode 100644 index 0000000..2774651 --- /dev/null +++ b/src/Command/VectorHealthCheckCommand.php @@ -0,0 +1,35 @@ +health->check(); + + $output->writeln(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + + return str_starts_with($result['status'], 'OK') + ? Command::SUCCESS + : Command::FAILURE; + } +} diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php index b5a6a16..5ce6a71 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/Admin/DashboardController.php @@ -5,6 +5,7 @@ namespace App\Controller\Admin; use App\Index\IndexMetaManager; use App\Ingest\IngestFlow; +use App\Vector\VectorIndexHealthService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; @@ -12,13 +13,15 @@ use Symfony\Component\Routing\Attribute\Route; final class DashboardController extends AbstractController { #[Route('/admin', name: 'admin_dashboard')] - public function index(): Response + public function index(VectorIndexHealthService $health): Response { - return $this->render('admin/dashboard/index.html.twig'); + return $this->render('admin/dashboard/index.html.twig', [ + 'vectorHealth' => $health->check() + ]); } #[Route('/admin/dashboard', name: 'admin_dashboard')] - public function dashboard(IndexMetaManager $metaManager): Response + public function dashboard(IndexMetaManager $metaManager,VectorIndexHealthService $health): Response { $chunkCount = $metaManager->getRuntimeChunkCount(); $limit = IngestFlow::CHUNK_LIMIT_HARD; @@ -26,6 +29,7 @@ final class DashboardController extends AbstractController return $this->render('admin/dashboard/index.html.twig', [ 'chunkCount' => $chunkCount, 'chunkLimit' => $limit, + 'vectorHealth' => $health->check(), ]); } } diff --git a/src/Service/Admin/IndexNdjsonInspector.php b/src/Service/Admin/IndexNdjsonInspector.php index d134783..a2154ab 100644 --- a/src/Service/Admin/IndexNdjsonInspector.php +++ b/src/Service/Admin/IndexNdjsonInspector.php @@ -30,7 +30,7 @@ final class IndexNdjsonInspector { if (!is_file($this->metaPath)) { return [ - 'error' => 'index_meta.json nicht gefunden', + 'error' => 'index_meta.json nicht gefunden. Bitte laden Sie min. ein Dokument in das System.', 'path' => $this->metaPath, ]; } @@ -67,7 +67,7 @@ final class IndexNdjsonInspector if (!is_file($this->ndjsonPath)) { return [ - 'error' => 'index.ndjson nicht gefunden', + 'error' => 'index.ndjson nicht gefunden. Bitte laden Sie min. ein Dokument in das System.', 'path' => $this->ndjsonPath, 'items' => [], ]; diff --git a/src/Vector/VectorIndexHealthService.php b/src/Vector/VectorIndexHealthService.php new file mode 100644 index 0000000..b4b23f5 --- /dev/null +++ b/src/Vector/VectorIndexHealthService.php @@ -0,0 +1,93 @@ +indexNdjsonPath); + $vectorExists = is_file($this->vectorIndexPath); + $metaExists = is_file($this->vectorMetaPath); + + $ndjsonChunkCount = 0; + + if ($ndjsonExists) { + $h = @fopen($this->indexNdjsonPath, 'r'); + if ($h !== false) { + while (($line = fgets($h)) !== false) { + $line = trim($line); + if ($line === '') { + continue; + } + $data = json_decode($line, true); + if (is_array($data) && !empty($data['chunk_id']) && !empty($data['text'])) { + $ndjsonChunkCount++; + } + } + fclose($h); + } + } + + $vectorChunkCount = 0; + if ($metaExists) { + $meta = json_decode((string) file_get_contents($this->vectorMetaPath), true); + if (is_array($meta)) { + $vectorChunkCount = count($meta); + } + } + + $status = $this->determineStatus( + $ndjsonChunkCount, + $vectorExists, + $metaExists, + $vectorChunkCount + ); + + return [ + 'ndjson_exists' => $ndjsonExists, + 'ndjson_chunk_count' => $ndjsonChunkCount, + 'vector_exists' => $vectorExists, + 'meta_exists' => $metaExists, + 'vector_chunk_count' => $vectorChunkCount, + 'status' => $status, + ]; + } + + private function determineStatus( + int $ndjsonChunkCount, + bool $vectorExists, + bool $metaExists, + int $vectorChunkCount + ): string { + if ($ndjsonChunkCount === 0 && !$vectorExists && !$metaExists) { + return 'OK_EMPTY'; + } + + if ($ndjsonChunkCount > 0 && $vectorExists && $metaExists && $vectorChunkCount === $ndjsonChunkCount) { + return 'OK'; + } + + if ($ndjsonChunkCount === 0 && ($vectorExists || $metaExists)) { + return 'INCONSISTENT_STALE_VECTOR'; + } + + if ($ndjsonChunkCount > 0 && (!$vectorExists || !$metaExists)) { + return 'INCONSISTENT_MISSING_VECTOR'; + } + + if ($ndjsonChunkCount !== $vectorChunkCount) { + return 'INCONSISTENT_COUNT_MISMATCH'; + } + + return 'UNKNOWN'; + } +} diff --git a/templates/admin/dashboard/index.html.twig b/templates/admin/dashboard/index.html.twig index 9ae67a7..9b492f1 100644 --- a/templates/admin/dashboard/index.html.twig +++ b/templates/admin/dashboard/index.html.twig @@ -1,130 +1,207 @@ {% extends 'admin/base.html.twig' %} -{% block title %}Admin Dashboard{% endblock %} +{% block title %}System Dashboard{% endblock %} {% block body %} -
-

Dashboard

-
+
- {# ============================= #} - {# USER INFO CARD #} - {# ============================= #} - -
-
- -
System Benutzer
- -
- User: - {{ app.user.userIdentifier }} -
- -
- Rollen: - {{ app.user.roles|join(', ') }} -
+ {# ===================================================== #} + {# HEADER #} + {# ===================================================== #} +
+

System Overview

+ RAG Enterprise
-
- {# ============================= #} - {# SYSTEM RESET CARD #} - {# ============================= #} + {# ===================================================== #} + {# GOVERNANCE BLOCK #} + {# ===================================================== #} - {% if is_granted('ROLE_SUPER_ADMIN') %} - -
+
-
System Reset
+
System Governance
-
- Der Reset entfernt: -
    -
  • Alle Dokumente und Versionen
  • -
  • Den gesamten NDJSON-Index
  • -
  • Den FAISS-Vektorindex
  • -
  • Alle Ingest-Jobs
  • -
- Diese Aktion ist irreversibel. +
+
+ Current User:
+ {{ app.user.userIdentifier }} +
+ +
+ Roles:
+ {{ app.user.roles|join(', ') }} +
- {% for label, messages in app.flashes %} - {% for message in messages %} -
- {{ message }} +
+
+ + + {# ===================================================== #} + {# VECTOR INFRASTRUCTURE #} + {# ===================================================== #} + + {% if vectorHealth is defined %} + + {% set status = vectorHealth.status %} + + {% set badgeClass = + status starts with 'OK' + ? 'bg-success' + : (status == 'INCONSISTENT_MISSING_VECTOR' + ? 'bg-warning text-dark' + : 'bg-danger') + %} + +
+
+ +
Vector Infrastructure
+ +
+ +
+ Status
+ + {{ status }} +
- {% endfor %} - {% endfor %} -
+
+ NDJSON Chunks
+ {{ vectorHealth.ndjson_chunk_count|number_format(0, ',', '.') }} +
- +
+ Vector Index Chunks
+ {{ vectorHealth.vector_chunk_count|number_format(0, ',', '.') }} +
- -
+
-
-
+ {% if status starts with 'OK' %} +
+ Infrastructure is consistent. +
+ {% elseif status == 'INCONSISTENT_MISSING_VECTOR' %} +
+ Vector index missing. Rebuild recommended. +
+ {% else %} +
+ Index inconsistency detected. Immediate review required. +
+ {% endif %} - {% endif %} - - {# ============================= #} - {# KNOWLEDGE INDEX STATUS #} - {# ============================= #} - - {% set percent = chunkLimit > 0 ? (chunkCount / chunkLimit * 100)|round(1) : 0 %} - -
-
- -
Knowledge Index Status
- -
- Chunks: - {{ chunkCount|number_format(0, ',', '.') }} - / - {{ chunkLimit|number_format(0, ',', '.') }} -
- -
-
- {{ percent }}%
-
- System ist für maximal {{ chunkLimit|number_format(0, ',', '.') }} Chunks optimiert. - {% if percent >= 95 %} -
Kapazitätsgrenze nahezu erreicht. - {% endif %} + {% endif %} + + + {# ===================================================== #} + {# KNOWLEDGE CAPACITY #} + {# ===================================================== #} + + {% set percent = chunkLimit > 0 ? (chunkCount / chunkLimit * 100)|round(1) : 0 %} + +
+
+ +
Knowledge Capacity
+ +
+ Chunks: + {{ chunkCount|number_format(0, ',', '.') }} + / + {{ chunkLimit|number_format(0, ',', '.') }} +
+ +
+
+ {{ percent }}% +
+
+ +
+ System optimized for maximum + {{ chunkLimit|number_format(0, ',', '.') }} chunks. + {% if percent >= 95 %} +
Capacity limit nearly reached. + {% endif %} +
+ +
+
+ + + {# ===================================================== #} + {# CRITICAL OPERATIONS (SUPER ADMIN ONLY) #} + {# ===================================================== #} + + {% if is_granted('ROLE_SUPER_ADMIN') %} + +
+
+ +
Critical Operations
+ +
+ Full system reset removes: +
    +
  • All documents & versions
  • +
  • NDJSON index
  • +
  • FAISS vector index
  • +
  • All ingest jobs
  • +
+ This action is irreversible. +
+ + {% for label, messages in app.flashes %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endfor %} + +
+ + + + + +
+ +
-
+ {% endif %} +
{% endblock %} diff --git a/templates/admin/ingest_profile/list.html.twig b/templates/admin/ingest_profile/list.html.twig index 49118e7..67e06fc 100644 --- a/templates/admin/ingest_profile/list.html.twig +++ b/templates/admin/ingest_profile/list.html.twig @@ -21,10 +21,10 @@
Strukturabweichung erkannt. Die aktuelle Indexstruktur entspricht nicht dem aktiven Profil. - Eine globale Neuindizierung ist erforderlich. + Eine globale Neuindizierung ist erforderlich oder Sie haben kein indexiertes Dokument im System.
+ class="btn btn-sm btn-outline-danger"> Global Reindex starten