1
0

V1.1 Änderungen

This commit is contained in:
2026-04-09 05:42:02 +02:00
parent 39800b6ea8
commit 5099ff4aca
44 changed files with 4912 additions and 957 deletions

View File

@@ -53,39 +53,98 @@ Vorlagen für lokale und Test-Konfigurationen befinden sich in:
- `config/application-local.example.properties`
- `config/application-test.example.properties`
### Pflichtparameter
### Pflichtparameter (allgemein)
| Parameter | Beschreibung |
|------------------------|--------------|
| `source.folder` | Quellordner mit OCR-PDFs (muss vorhanden und lesbar sein) |
| `target.folder` | Zielordner für umbenannte Kopien (wird angelegt, wenn nicht vorhanden) |
| `sqlite.file` | SQLite-Datenbankdatei (übergeordnetes Verzeichnis muss existieren) |
| `api.baseUrl` | Basis-URL des KI-Dienstes (z. B. `https://api.openai.com/v1`) |
| `api.model` | Modellname (z. B. `gpt-4o-mini`) |
| `api.timeoutSeconds` | HTTP-Timeout für KI-Anfragen in Sekunden (ganzzahlig, > 0) |
| `max.retries.transient`| Maximale transiente Fehlversuche pro Dokument (ganzzahlig, >= 1) |
| `max.pages` | Maximale Seitenzahl pro Dokument (ganzzahlig, > 0) |
| `max.text.characters` | Maximale Zeichenanzahl des Dokumenttexts für KI-Anfragen (ganzzahlig, > 0) |
| `prompt.template.file` | Pfad zur externen Prompt-Datei (muss vorhanden sein) |
| Parameter | Beschreibung |
|-------------------------|--------------|
| `source.folder` | Quellordner mit OCR-PDFs (muss vorhanden und lesbar sein) |
| `target.folder` | Zielordner für umbenannte Kopien (wird angelegt, wenn nicht vorhanden) |
| `sqlite.file` | SQLite-Datenbankdatei (übergeordnetes Verzeichnis muss existieren) |
| `ai.provider.active` | Aktiver KI-Provider: `openai-compatible` oder `claude` |
| `max.retries.transient` | Maximale transiente Fehlversuche pro Dokument (ganzzahlig, >= 1) |
| `max.pages` | Maximale Seitenzahl pro Dokument (ganzzahlig, > 0) |
| `max.text.characters` | Maximale Zeichenanzahl des Dokumenttexts für KI-Anfragen (ganzzahlig, > 0) |
| `prompt.template.file` | Pfad zur externen Prompt-Datei (muss vorhanden sein) |
### Provider-Parameter
Nur der **aktive** Provider muss vollständig konfiguriert sein. Der inaktive Provider wird nicht validiert.
**OpenAI-kompatibler Provider** (`ai.provider.active=openai-compatible`):
| Parameter | Beschreibung |
|-----------|--------------|
| `ai.provider.openai-compatible.baseUrl` | Basis-URL des KI-Dienstes (z. B. `https://api.openai.com/v1`) |
| `ai.provider.openai-compatible.model` | Modellname (z. B. `gpt-4o-mini`) |
| `ai.provider.openai-compatible.timeoutSeconds` | HTTP-Timeout in Sekunden (ganzzahlig, > 0) |
| `ai.provider.openai-compatible.apiKey` | API-Schlüssel (Umgebungsvariable `OPENAI_COMPATIBLE_API_KEY` hat Vorrang) |
**Anthropic Claude-Provider** (`ai.provider.active=claude`):
| Parameter | Beschreibung |
|-----------|--------------|
| `ai.provider.claude.baseUrl` | Basis-URL (optional; Standard: `https://api.anthropic.com`) |
| `ai.provider.claude.model` | Modellname (z. B. `claude-3-5-sonnet-20241022`) |
| `ai.provider.claude.timeoutSeconds` | HTTP-Timeout in Sekunden (ganzzahlig, > 0) |
| `ai.provider.claude.apiKey` | API-Schlüssel (Umgebungsvariable `ANTHROPIC_API_KEY` hat Vorrang) |
### Optionale Parameter
| Parameter | Beschreibung | Standard |
|----------------------|--------------|---------|
| `api.key` | API-Schlüssel (alternativ: Umgebungsvariable `PDF_UMBENENNER_API_KEY`) | |
| `runtime.lock.file` | Lock-Datei für Startschutz | `pdf-umbenenner.lock` im Arbeitsverzeichnis |
| `log.directory` | Log-Verzeichnis | `./logs/` |
| `log.level` | Log-Level (`DEBUG`, `INFO`, `WARN`, `ERROR`) | `INFO` |
| `log.ai.sensitive` | KI-Rohantwort und Reasoning ins Log schreiben (`true`/`false`) | `false` |
| Parameter | Beschreibung | Standard |
|---------------------|--------------|---------|
| `runtime.lock.file` | Lock-Datei für Startschutz | `pdf-umbenenner.lock` im Arbeitsverzeichnis |
| `log.directory` | Log-Verzeichnis | `./logs/` |
| `log.level` | Log-Level (`DEBUG`, `INFO`, `WARN`, `ERROR`) | `INFO` |
| `log.ai.sensitive` | KI-Rohantwort und Reasoning ins Log schreiben (`true`/`false`) | `false` |
### API-Schlüssel
Der API-Schlüssel kann auf zwei Wegen gesetzt werden:
Pro Provider-Familie existiert eine eigene Umgebungsvariable, die Vorrang vor dem Properties-Wert hat:
1. **Umgebungsvariable `PDF_UMBENENNER_API_KEY`** (empfohlen, hat Vorrang)
2. Property `api.key` in `config/application.properties`
| Provider | Umgebungsvariable |
|---|---|
| `openai-compatible` | `OPENAI_COMPATIBLE_API_KEY` |
| `claude` | `ANTHROPIC_API_KEY` |
Die Umgebungsvariable hat immer Vorrang über die Properties-Datei.
Schlüssel verschiedener Provider-Familien werden niemals vermischt.
---
## Migration älterer Konfigurationsdateien
Ältere Konfigurationsdateien, die noch die flachen Schlüssel `api.baseUrl`, `api.model`,
`api.timeoutSeconds` und `api.key` verwenden, werden beim ersten Start **automatisch**
in das aktuelle Schema überführt.
### Was passiert
1. Die Anwendung erkennt die veraltete Form anhand der flachen `api.*`-Schlüssel.
2. **Vor jeder Änderung** wird eine Sicherungskopie der Originaldatei angelegt:
- Standardfall: `config/application.properties.bak`
- Falls `.bak` bereits existiert: `config/application.properties.bak.1`, `.bak.2`, …
- Bestehende Sicherungen werden **niemals überschrieben**.
3. Die Datei wird in-place in das neue Schema überführt:
- `api.baseUrl``ai.provider.openai-compatible.baseUrl`
- `api.model``ai.provider.openai-compatible.model`
- `api.timeoutSeconds``ai.provider.openai-compatible.timeoutSeconds`
- `api.key``ai.provider.openai-compatible.apiKey`
- `ai.provider.active=openai-compatible` wird ergänzt.
- Alle übrigen Schlüssel bleiben unverändert.
4. Die migrierte Datei wird über eine temporäre Datei (`*.tmp`) und atomischen
Move/Rename geschrieben. Das Original wird niemals teilbeschrieben.
5. Die migrierte Datei wird sofort neu eingelesen und validiert.
### Bei Migrationsfehler
Schlägt die Validierung der migrierten Datei fehl, bricht die Anwendung mit Exit-Code `1` ab.
Die Sicherungskopie (`.bak`) bleibt in diesem Fall erhalten und enthält die unveränderte
Originaldatei. Die Konfiguration muss dann manuell korrigiert werden.
### Betreiber-Hinweis
Die Umgebungsvariable `PDF_UMBENENNER_API_KEY` des Vorgängerstands wird **nicht** automatisch
umbenannt. Falls dieser Wert bislang verwendet wurde, muss er auf `OPENAI_COMPATIBLE_API_KEY`
umgestellt werden.
---

View File

@@ -0,0 +1,149 @@
# V1.1 Abschlussnachweis
## Datum und betroffene Module
**Datum:** 2026-04-09
**Betroffene Module:**
| Modul | Art der Änderung |
|---|---|
| `pdf-umbenenner-application` | Neue Konfigurationstypen (`MultiProviderConfiguration`, `ProviderConfiguration`, `AiProviderFamily`) |
| `pdf-umbenenner-adapter-out` | Neuer Anthropic-Adapter (`AnthropicClaudeHttpAdapter`), neuer Parser (`MultiProviderConfigurationParser`), neuer Validator (`MultiProviderConfigurationValidator`), Migrator (`LegacyConfigurationMigrator`), Schema-Migration (`ai_provider`-Spalte), aktualisierter OpenAI-Adapter (`OpenAiHttpAdapter`), aktualisierter Properties-Adapter (`PropertiesConfigurationPortAdapter`) |
| `pdf-umbenenner-bootstrap` | Provider-Selektor (`AiProviderSelector`), aktualisierter `BootstrapRunner` (Migration, Provider-Auswahl, Logging) |
| `pdf-umbenenner-adapter-in-cli` | Keine fachliche Änderung |
| `pdf-umbenenner-domain` | Keine Änderung |
| `config/` | Beispiel-Properties-Dateien auf neues Schema aktualisiert |
| `docs/betrieb.md` | Abschnitte KI-Provider-Auswahl und Migration ergänzt |
---
## Pflicht-Testfälle je Arbeitspaket
### AP-001 Konfigurations-Schema einführen
| Testfall | Klasse | Status |
|---|---|---|
| `parsesNewSchemaWithOpenAiCompatibleActive` | `MultiProviderConfigurationTest` | grün |
| `parsesNewSchemaWithClaudeActive` | `MultiProviderConfigurationTest` | grün |
| `claudeBaseUrlDefaultsWhenMissing` | `MultiProviderConfigurationTest` | grün |
| `rejectsMissingActiveProvider` | `MultiProviderConfigurationTest` | grün |
| `rejectsUnknownActiveProvider` | `MultiProviderConfigurationTest` | grün |
| `rejectsMissingMandatoryFieldForActiveProvider` | `MultiProviderConfigurationTest` | grün |
| `acceptsMissingMandatoryFieldForInactiveProvider` | `MultiProviderConfigurationTest` | grün |
| `envVarOverridesPropertiesApiKeyForActiveProvider` | `MultiProviderConfigurationTest` | grün |
| `envVarOnlyResolvesForActiveProvider` | `MultiProviderConfigurationTest` | grün |
| Bestehende Tests bleiben grün | `PropertiesConfigurationPortAdapterTest`, `StartConfigurationValidatorTest` | grün |
### AP-002 Legacy-Migration mit `.bak`
| Testfall | Klasse | Status |
|---|---|---|
| `migratesLegacyFileWithAllFlatKeys` | `LegacyConfigurationMigratorTest` | grün |
| `createsBakBeforeOverwriting` | `LegacyConfigurationMigratorTest` | grün |
| `bakSuffixIsIncrementedIfBakExists` | `LegacyConfigurationMigratorTest` | grün |
| `noOpForAlreadyMigratedFile` | `LegacyConfigurationMigratorTest` | grün |
| `reloadAfterMigrationSucceeds` | `LegacyConfigurationMigratorTest` | grün |
| `migrationFailureKeepsBak` | `LegacyConfigurationMigratorTest` | grün |
| `legacyDetectionRequiresAtLeastOneFlatKey` | `LegacyConfigurationMigratorTest` | grün |
| `legacyValuesEndUpInOpenAiCompatibleNamespace` | `LegacyConfigurationMigratorTest` | grün |
| `unrelatedKeysSurviveUnchanged` | `LegacyConfigurationMigratorTest` | grün |
| `inPlaceWriteIsAtomic` | `LegacyConfigurationMigratorTest` | grün |
### AP-003 Bootstrap-Provider-Auswahl und Umstellung des bestehenden OpenAI-Adapters
| Testfall | Klasse | Status |
|---|---|---|
| `bootstrapWiresOpenAiCompatibleAdapterWhenActive` | `AiProviderSelectorTest` | grün |
| `bootstrapFailsHardWhenActiveProviderUnknown` | `AiProviderSelectorTest` | grün |
| `bootstrapFailsHardWhenSelectedProviderHasNoImplementation` | `AiProviderSelectorTest` | grün |
| `openAiAdapterReadsValuesFromNewNamespace` | `OpenAiHttpAdapterTest` | grün |
| `openAiAdapterBehaviorIsUnchanged` | `OpenAiHttpAdapterTest` | grün |
| `activeProviderIsLoggedAtRunStart` | `BootstrapRunnerTest` | grün |
| `existingDocumentProcessingTestsRemainGreen` | `BatchRunEndToEndTest` | grün |
| `legacyFileEndToEndStillRuns` | `BootstrapRunnerTest` | grün |
### AP-004 Persistenz: Provider-Identifikator additiv
| Testfall | Klasse | Status |
|---|---|---|
| `addsProviderColumnOnFreshDb` | `SqliteAttemptProviderPersistenceTest` | grün |
| `addsProviderColumnOnExistingDbWithoutColumn` | `SqliteAttemptProviderPersistenceTest` | grün |
| `migrationIsIdempotent` | `SqliteAttemptProviderPersistenceTest` | grün |
| `existingRowsKeepNullProvider` | `SqliteAttemptProviderPersistenceTest` | grün |
| `newAttemptsWriteOpenAiCompatibleProvider` | `SqliteAttemptProviderPersistenceTest` | grün |
| `newAttemptsWriteClaudeProvider` | `SqliteAttemptProviderPersistenceTest` | grün |
| `repositoryReadsProviderColumn` | `SqliteAttemptProviderPersistenceTest` | grün |
| `legacyDataReadingDoesNotFail` | `SqliteAttemptProviderPersistenceTest` | grün |
| `existingHistoryTestsRemainGreen` | `SqliteAttemptProviderPersistenceTest` | grün |
### AP-005 Nativer Anthropic-Adapter implementieren und verdrahten
| Testfall | Klasse | Status |
|---|---|---|
| `claudeAdapterBuildsCorrectRequest` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterUsesEnvVarApiKey` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterFallsBackToPropertiesApiKey` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterFailsValidationWhenBothKeysMissing` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterParsesSingleTextBlock` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterConcatenatesMultipleTextBlocks` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterIgnoresNonTextBlocks` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterFailsOnEmptyTextContent` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterMapsHttp401AsTechnical` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterMapsHttp429AsTechnical` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterMapsHttp500AsTechnical` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterMapsTimeoutAsTechnical` | `AnthropicClaudeHttpAdapterTest` | grün |
| `claudeAdapterMapsUnparseableJsonAsTechnical` | `AnthropicClaudeHttpAdapterTest` | grün |
| `bootstrapSelectsClaudeWhenActive` | `AiProviderSelectorTest` | grün |
| `claudeProviderIdentifierLandsInAttemptHistory` | `AnthropicClaudeAdapterIntegrationTest` | grün |
| `existingOpenAiPathRemainsGreen` | alle `OpenAiHttpAdapterTest`-Tests | grün |
### AP-006 Regression, Smoke, Doku, Abschlussnachweis
| Testfall | Klasse | Status |
|---|---|---|
| `smokeBootstrapWithOpenAiCompatibleActive` | `BootstrapSmokeTest` | grün |
| `smokeBootstrapWithClaudeActive` | `BootstrapSmokeTest` | grün |
| `e2eMigrationFromLegacyDemoConfig` | `ProviderIdentifierE2ETest` | grün |
| `regressionExistingOpenAiSuiteGreen` | `ProviderIdentifierE2ETest` | grün |
| `e2eClaudeRunWritesProviderIdentifierToHistory` | `ProviderIdentifierE2ETest` | grün |
| `e2eOpenAiRunWritesProviderIdentifierToHistory` | `ProviderIdentifierE2ETest` | grün |
| `legacyDataFromBeforeV11RemainsReadable` | `ProviderIdentifierE2ETest` | grün |
---
## Belegte Eigenschaften
| Eigenschaft | Nachweis |
|---|---|
| Zwei Provider-Familien unterstützt | `AiProviderSelectorTest`, `BootstrapSmokeTest` |
| Genau einer aktiv pro Lauf | `MultiProviderConfigurationTest`, `BootstrapSmokeTest` |
| Kein automatischer Fallback | keine Fallback-Logik in `AiProviderSelector` oder Application-Schicht |
| Fachlicher Vertrag (`NamingProposal`) unverändert | `AiResponseParser`, `AiNamingService` unverändert; beide Adapter liefern denselben Domain-Typ |
| Persistenz rückwärtsverträglich | `SqliteAttemptProviderPersistenceTest`, `legacyDataFromBeforeV11RemainsReadable` |
| Migration nachgewiesen | `LegacyConfigurationMigratorTest`, `e2eMigrationFromLegacyDemoConfig` |
| `.bak`-Sicherung nachgewiesen | `LegacyConfigurationMigratorTest.createsBakBeforeOverwriting`, `e2eMigrationFromLegacyDemoConfig` |
| Aktiver Provider wird geloggt | `BootstrapRunnerTest.activeProviderIsLoggedAtRunStart` |
| Keine Architekturbrüche | kein `Application`- oder `Domain`-Code kennt OpenAI- oder Claude-spezifische Typen |
| Keine neuen Bibliotheken | Anthropic-Adapter nutzt Java HTTP Client und `org.json` (beides bereits im Repo etabliert) |
---
## Betreiberaufgabe
Wer bisher die Umgebungsvariable `PDF_UMBENENNER_API_KEY` oder eine andere eigene Variable für den
OpenAI-kompatiblen API-Schlüssel eingesetzt hat, muss diese auf **`OPENAI_COMPATIBLE_API_KEY`** umstellen.
Die Anwendung akzeptiert nur diese kanonische Umgebungsvariable; ältere proprietäre Namen werden
nicht automatisch ausgewertet.
---
## Build-Ergebnis
Build-Kommando:
```
.\mvnw.cmd clean verify -pl pdf-umbenenner-domain,pdf-umbenenner-application,pdf-umbenenner-adapter-out,pdf-umbenenner-adapter-in-cli,pdf-umbenenner-bootstrap --also-make
```
Build-Status: **ERFOLGREICH** — alle Tests grün, Mutationstests in allen Modulen ausgeführt.