diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b0d0fd2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +dc-backend/target + diff --git a/dc-backend/src/main/java/de/frigosped/dc/resource/CheckResource.java b/dc-backend/src/main/java/de/frigosped/dc/resource/CheckResource.java index d83a02f..90a4db4 100644 --- a/dc-backend/src/main/java/de/frigosped/dc/resource/CheckResource.java +++ b/dc-backend/src/main/java/de/frigosped/dc/resource/CheckResource.java @@ -7,25 +7,42 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.jboss.logging.Logger; import java.util.Map; -import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; /** * REST-Endpunkte des Dokumenten-Check-Backend. * * POST /check/{projectId} - * → Startet die asynchrone Verarbeitung eines Projekts. - * → Gibt sofort 202 Accepted zurück; Fortschritt über ORDS-Endpunkte ablesbar. + * → Reiht das Projekt in eine sequentielle Warteschlange ein (202 sofort). + * → Projekte werden strikt nacheinander verarbeitet (kein paralleler Betrieb). * * GET /check/health - * → Einfacher Health-Check. + * → Einfacher Health-Check inkl. Warteschlangenlänge. */ @Path("/check") @Produces(MediaType.APPLICATION_JSON) @Tag(name = "Dokumenten-Check", description = "KI-gestützte Dokumentenprüfung") public class CheckResource { + private static final Logger LOG = Logger.getLogger(CheckResource.class); + + // Single-Thread-Executor: garantiert sequentielle Abarbeitung. + // Eingehende Aufrufe werden automatisch in die Warteschlange gelegt. + private static final ExecutorService QUEUE = + Executors.newSingleThreadExecutor(r -> { + Thread t = new Thread(r, "dc-processing-thread"); + t.setDaemon(true); + return t; + }); + + // Anzahl Projekte in der Warteschlange (inkl. laufendem) + private static final AtomicInteger QUEUED = new AtomicInteger(0); + @Inject CheckOrchestrationService orchestrationService; @@ -36,35 +53,34 @@ public class CheckResource { @POST @Path("/{projectId}") @Operation( - summary = "Startet die Dokumentenprüfung für ein Projekt", + summary = "Reiht ein Projekt in die Verarbeitungswarteschlange ein", description = """ - Triggert den vollständigen Prüfablauf asynchron: - 1. OCR / Konvertierung der Dokumente (KI) - 2. Übersetzung ins Deutsche (KI) - 3. Auswertung des Fragenkatalogs (KI) - 4. Ergebnisse werden in ORDS gespeichert + Projekte werden strikt nacheinander verarbeitet. + Der Aufruf kehrt sofort mit 202 zurück; die eigentliche Verarbeitung + startet sobald alle vorher eingereihten Projekte abgeschlossen sind. Das Projekt muss im Status PENDING sein. Fortschritt: GET {ords}/api/dc/projects/{id} """ ) public Response startCheck(@PathParam("projectId") long projectId) { - // Asynchron starten – Fire & Forget. - // Der aufrufende Thread (z.B. APEX-Button) bekommt sofort 202. - // Der Verarbeitungs-Thread läuft im ForkJoin-CommonPool. - CompletableFuture - .runAsync(() -> orchestrationService.process(projectId)) - .exceptionally(ex -> { - // Der Orchestrator fängt intern; dies ist eine letzte Absicherung. - // Logging ist dort bereits vorhanden. - return null; - }); + int position = QUEUED.incrementAndGet(); + + QUEUE.submit(() -> { + try { + LOG.infof("Starte Projekt %d (Warteschlange: %d)", projectId, QUEUED.get()); + orchestrationService.process(projectId); + } finally { + QUEUED.decrementAndGet(); + } + }); return Response .accepted(Map.of( - "message", "Verarbeitung gestartet", - "project_id", projectId, - "hint", "Fortschritt: GET /api/dc/projects/" + projectId + "message", "Projekt in Warteschlange eingereiht", + "project_id", projectId, + "queue_position", position, + "hint", "Fortschritt: GET /api/dc/projects/" + projectId )) .build(); } @@ -75,8 +91,12 @@ public class CheckResource { @GET @Path("/health") - @Operation(summary = "Health-Check", description = "Gibt UP zurück wenn der Service läuft.") + @Operation(summary = "Health-Check", description = "Gibt UP zurück inkl. Warteschlangenlänge.") public Response health() { - return Response.ok(Map.of("status", "UP", "service", "dc-backend")).build(); + return Response.ok(Map.of( + "status", "UP", + "service", "dc-backend", + "queued", QUEUED.get() + )).build(); } } diff --git a/dc-backend/target/classes/application.properties b/dc-backend/target/classes/application.properties index 2ffa717..b9d65bc 100644 --- a/dc-backend/target/classes/application.properties +++ b/dc-backend/target/classes/application.properties @@ -18,13 +18,19 @@ dc.ai.base-url=https://ollama.aquantico.de dc.ai.api-key=324GF44-50AA-4B57-9386-K435DLJ764DFR # Hauptmodell: Auswertung + Übersetzung -dc.ai.main.model=gpt-oss:20b +dc.ai.main.model=qwen3.6:35b-a3b-q4_K_M dc.ai.main.timeout=300s dc.ai.main.max-retries=2 dc.ai.main.temperature=0.1 +# Katalog-Generierungsmodell: längerer Timeout für vollständige Katalog-Analyse +dc.ai.catalog.model=qwen3.6:35b-a3b-q4_K_M +dc.ai.catalog.timeout=1200s +dc.ai.catalog.max-retries=1 +dc.ai.catalog.temperature=0.1 + # OCR-Modell: Bildtext-Extraktion aus PDF-Seiten -dc.ai.ocr.model=quen3.5:9b +dc.ai.ocr.model=qwen3.6:35b-a3b-q4_K_M dc.ai.ocr.timeout=120s dc.ai.ocr.max-retries=2 @@ -32,7 +38,7 @@ dc.ai.ocr.max-retries=2 # ORDS REST Client # ============================================================================= # Basis-URL des ORDS-Servers (Schema-Pfad anpassen) -quarkus.rest-client.ords-api.url=http://localhost:8080/ords/FRIGOSPED_APP +quarkus.rest-client.ords-api.url=http://ords:8080/ords/ai_dev quarkus.rest-client.ords-api.scope=jakarta.inject.Singleton quarkus.rest-client.ords-api.connect-timeout=10000 quarkus.rest-client.ords-api.read-timeout=60000 @@ -49,8 +55,8 @@ quarkus.log.category."de.frigosped".level=DEBUG # KI-Kommunikation mitloggen (nur in Dev) %dev.dc.ai.log-requests=true %dev.dc.ai.log-responses=true -%prod.dc.ai.log-requests=false -%prod.dc.ai.log-responses=false +%prod.dc.ai.log-requests=true +%prod.dc.ai.log-responses=true # ============================================================================= # Jackson: snake_case für ORDS-Responses diff --git a/dc-backend/target/classes/de/frigosped/dc/ai/AiModelProducer.class b/dc-backend/target/classes/de/frigosped/dc/ai/AiModelProducer.class index 5d58c5d..00a5781 100644 Binary files a/dc-backend/target/classes/de/frigosped/dc/ai/AiModelProducer.class and b/dc-backend/target/classes/de/frigosped/dc/ai/AiModelProducer.class differ diff --git a/dc-backend/target/classes/de/frigosped/dc/client/OrdsClient.class b/dc-backend/target/classes/de/frigosped/dc/client/OrdsClient.class index 7638612..57bbf49 100644 Binary files a/dc-backend/target/classes/de/frigosped/dc/client/OrdsClient.class and b/dc-backend/target/classes/de/frigosped/dc/client/OrdsClient.class differ diff --git a/dc-backend/target/classes/de/frigosped/dc/model/OrdsQuestion.class b/dc-backend/target/classes/de/frigosped/dc/model/OrdsQuestion.class index 528dd8f..d0c5668 100644 Binary files a/dc-backend/target/classes/de/frigosped/dc/model/OrdsQuestion.class and b/dc-backend/target/classes/de/frigosped/dc/model/OrdsQuestion.class differ diff --git a/dc-backend/target/classes/de/frigosped/dc/model/ProgressRequest.class b/dc-backend/target/classes/de/frigosped/dc/model/ProgressRequest.class index 94e0ad4..add1168 100644 Binary files a/dc-backend/target/classes/de/frigosped/dc/model/ProgressRequest.class and b/dc-backend/target/classes/de/frigosped/dc/model/ProgressRequest.class differ diff --git a/dc-backend/target/classes/de/frigosped/dc/resource/CheckResource.class b/dc-backend/target/classes/de/frigosped/dc/resource/CheckResource.class index 4373540..90d10fc 100644 Binary files a/dc-backend/target/classes/de/frigosped/dc/resource/CheckResource.class and b/dc-backend/target/classes/de/frigosped/dc/resource/CheckResource.class differ diff --git a/dc-backend/target/classes/de/frigosped/dc/service/CheckOrchestrationService.class b/dc-backend/target/classes/de/frigosped/dc/service/CheckOrchestrationService.class index 00be641..4d0beac 100644 Binary files a/dc-backend/target/classes/de/frigosped/dc/service/CheckOrchestrationService.class and b/dc-backend/target/classes/de/frigosped/dc/service/CheckOrchestrationService.class differ diff --git a/dc-backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/dc-backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst index 57f3904..b39f459 100644 --- a/dc-backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ b/dc-backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -1,4 +1,23 @@ -de/frigosped/dc/service/CheckOrchestrationService.class +de/frigosped/dc/model/GeneratedCatalogStructure$GeneratedCategory.class +de/frigosped/dc/resource/ExportResource.class +de/frigosped/dc/model/OrdsQuestionCreateRequest.class +de/frigosped/dc/model/OrdsCategoryCreateRequest.class +de/frigosped/dc/model/OrdsCatalogCreateRequest.class +de/frigosped/dc/model/OrdsCatalogStatusUpdateRequest.class de/frigosped/dc/service/EvaluationService.class +de/frigosped/dc/service/MarkdownExportService.class de/frigosped/dc/service/DocumentProcessingService.class +de/frigosped/dc/model/CatalogGenerateResponse.class +de/frigosped/dc/service/MarkdownExportService$Seg.class +de/frigosped/dc/model/OrdsCatalogStatusResponse.class +de/frigosped/dc/model/OrdsDocumentTexts.class +de/frigosped/dc/resource/CatalogResource.class +de/frigosped/dc/model/ExportRequest.class +de/frigosped/dc/service/CatalogGenerationService.class +de/frigosped/dc/service/CheckOrchestrationService.class +de/frigosped/dc/service/MarkdownExportService$SegType.class +de/frigosped/dc/model/GeneratedCatalogStructure.class +de/frigosped/dc/client/OrdsLoggingFilter.class +de/frigosped/dc/service/MarkdownExportService$PdfWriter.class de/frigosped/dc/security/ApiKeyFilter.class +de/frigosped/dc/model/GeneratedCatalogStructure$GeneratedQuestion.class diff --git a/dc-backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/dc-backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst index 0c64751..c3dd4dd 100644 --- a/dc-backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ b/dc-backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -1,16 +1,30 @@ /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/ai/AiModelProducer.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/client/OrdsClient.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/client/OrdsLoggingFilter.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/CatalogGenerateResponse.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/EvaluationResult.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/ExportRequest.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/GeneratedCatalogStructure.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsCatalogCreateRequest.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsCatalogStatusResponse.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsCatalogStatusUpdateRequest.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsCategoryCreateRequest.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsDocument.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsDocumentList.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsDocumentTexts.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsProject.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsQuestion.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsQuestionCreateRequest.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/OrdsQuestionList.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/ProgressRequest.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/ResultRequest.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/model/TextsRequest.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/resource/CatalogResource.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/resource/CheckResource.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/resource/ExportResource.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/security/ApiKeyFilter.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/service/CatalogGenerationService.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/service/CheckOrchestrationService.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/service/DocumentProcessingService.java /mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/service/EvaluationService.java +/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/service/MarkdownExportService.java