Funktioniert

This commit is contained in:
2026-06-01 10:33:30 +02:00
parent ac9ab7ab15
commit fb185c1a71
11 changed files with 92 additions and 31 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
dc-backend/target

View File

@@ -7,25 +7,42 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import java.util.Map; 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. * REST-Endpunkte des Dokumenten-Check-Backend.
* *
* POST /check/{projectId} * POST /check/{projectId}
* → Startet die asynchrone Verarbeitung eines Projekts. * → Reiht das Projekt in eine sequentielle Warteschlange ein (202 sofort).
* → Gibt sofort 202 Accepted zurück; Fortschritt über ORDS-Endpunkte ablesbar. * → Projekte werden strikt nacheinander verarbeitet (kein paralleler Betrieb).
* *
* GET /check/health * GET /check/health
* → Einfacher Health-Check. * → Einfacher Health-Check inkl. Warteschlangenlänge.
*/ */
@Path("/check") @Path("/check")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Tag(name = "Dokumenten-Check", description = "KI-gestützte Dokumentenprüfung") @Tag(name = "Dokumenten-Check", description = "KI-gestützte Dokumentenprüfung")
public class CheckResource { 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 @Inject
CheckOrchestrationService orchestrationService; CheckOrchestrationService orchestrationService;
@@ -36,34 +53,33 @@ public class CheckResource {
@POST @POST
@Path("/{projectId}") @Path("/{projectId}")
@Operation( @Operation(
summary = "Startet die Dokumentenprüfung für ein Projekt", summary = "Reiht ein Projekt in die Verarbeitungswarteschlange ein",
description = """ description = """
Triggert den vollständigen Prüfablauf asynchron: Projekte werden strikt nacheinander verarbeitet.
1. OCR / Konvertierung der Dokumente (KI) Der Aufruf kehrt sofort mit 202 zurück; die eigentliche Verarbeitung
2. Übersetzung ins Deutsche (KI) startet sobald alle vorher eingereihten Projekte abgeschlossen sind.
3. Auswertung des Fragenkatalogs (KI)
4. Ergebnisse werden in ORDS gespeichert
Das Projekt muss im Status PENDING sein. Das Projekt muss im Status PENDING sein.
Fortschritt: GET {ords}/api/dc/projects/{id} Fortschritt: GET {ords}/api/dc/projects/{id}
""" """
) )
public Response startCheck(@PathParam("projectId") long projectId) { public Response startCheck(@PathParam("projectId") long projectId) {
// Asynchron starten Fire & Forget. int position = QUEUED.incrementAndGet();
// Der aufrufende Thread (z.B. APEX-Button) bekommt sofort 202.
// Der Verarbeitungs-Thread läuft im ForkJoin-CommonPool. QUEUE.submit(() -> {
CompletableFuture try {
.runAsync(() -> orchestrationService.process(projectId)) LOG.infof("Starte Projekt %d (Warteschlange: %d)", projectId, QUEUED.get());
.exceptionally(ex -> { orchestrationService.process(projectId);
// Der Orchestrator fängt intern; dies ist eine letzte Absicherung. } finally {
// Logging ist dort bereits vorhanden. QUEUED.decrementAndGet();
return null; }
}); });
return Response return Response
.accepted(Map.of( .accepted(Map.of(
"message", "Verarbeitung gestartet", "message", "Projekt in Warteschlange eingereiht",
"project_id", projectId, "project_id", projectId,
"queue_position", position,
"hint", "Fortschritt: GET /api/dc/projects/" + projectId "hint", "Fortschritt: GET /api/dc/projects/" + projectId
)) ))
.build(); .build();
@@ -75,8 +91,12 @@ public class CheckResource {
@GET @GET
@Path("/health") @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() { 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();
} }
} }

View File

@@ -18,13 +18,19 @@ dc.ai.base-url=https://ollama.aquantico.de
dc.ai.api-key=324GF44-50AA-4B57-9386-K435DLJ764DFR dc.ai.api-key=324GF44-50AA-4B57-9386-K435DLJ764DFR
# Hauptmodell: Auswertung + Übersetzung # 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.timeout=300s
dc.ai.main.max-retries=2 dc.ai.main.max-retries=2
dc.ai.main.temperature=0.1 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 # 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.timeout=120s
dc.ai.ocr.max-retries=2 dc.ai.ocr.max-retries=2
@@ -32,7 +38,7 @@ dc.ai.ocr.max-retries=2
# ORDS REST Client # ORDS REST Client
# ============================================================================= # =============================================================================
# Basis-URL des ORDS-Servers (Schema-Pfad anpassen) # 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.scope=jakarta.inject.Singleton
quarkus.rest-client.ords-api.connect-timeout=10000 quarkus.rest-client.ords-api.connect-timeout=10000
quarkus.rest-client.ords-api.read-timeout=60000 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) # KI-Kommunikation mitloggen (nur in Dev)
%dev.dc.ai.log-requests=true %dev.dc.ai.log-requests=true
%dev.dc.ai.log-responses=true %dev.dc.ai.log-responses=true
%prod.dc.ai.log-requests=false %prod.dc.ai.log-requests=true
%prod.dc.ai.log-responses=false %prod.dc.ai.log-responses=true
# ============================================================================= # =============================================================================
# Jackson: snake_case für ORDS-Responses # Jackson: snake_case für ORDS-Responses

View File

@@ -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/EvaluationService.class
de/frigosped/dc/service/MarkdownExportService.class
de/frigosped/dc/service/DocumentProcessingService.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/security/ApiKeyFilter.class
de/frigosped/dc/model/GeneratedCatalogStructure$GeneratedQuestion.class

View File

@@ -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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/EvaluationService.java
/mnt/c/source/Frigosped/Dokumenten-Check/dc-backend/src/main/java/de/frigosped/dc/service/MarkdownExportService.java