diff --git a/docs/ap-a-token-tracking-zusammenfassung.md b/docs/ap-a-token-tracking-zusammenfassung.md new file mode 100644 index 0000000..4eb6148 --- /dev/null +++ b/docs/ap-a-token-tracking-zusammenfassung.md @@ -0,0 +1,168 @@ +# AP-A Token-Tracking Fundament – Zusammenfassung + +Dieses Dokument fasst alle Klassen, Methoden und Dateien zusammen, die im Zuge +von AP-A (Token- und Kosten-Tracking-Fundament der V3.3-Spezifikation, #74) +neu erstellt oder substanziell erweitert wurden. + +## Schema-Migration + +- `pdf-umbenenner-adapter-out/src/main/resources/db/migration/V2__token_tracking.sql` + - Sechs neue Spalten in `processing_attempt`: + `input_tokens`, `output_tokens`, + `cache_creation_input_tokens`, `cache_read_input_tokens`, + `price_input_per_token_nano_usd`, `price_output_per_token_nano_usd`. + - Neue Tabelle `model_price` mit Composite Primary Key + `(provider, model_name)`, NOT-NULL-Preisen, Currency-CHECK auf `'USD'`, + `updated_at`-Spalte. + - Zwei zusätzliche Indizes auf `processing_attempt`: + `idx_processing_attempt_started_at_provider_fp_model`, + `idx_processing_attempt_run_id_provider_model`. + - Default-Preise für gpt-4o-mini, gpt-4o, gpt-4.1*, gpt-5*, claude-haiku-4-5, + claude-sonnet-4-6 und claude-opus-4-7 (`ON CONFLICT DO NOTHING`). + +## Application-Modul + +### DTOs (`application/dto`) +- `AiUsageMetadata` – Token-Verbrauchsmetadaten mit `empty()`, + `hasAnyTokenData()`, `hasCacheTokens()`. +- `ModelPriceEntry` – Schreib-/Validierungs-DTO mit Wertgrenzen-Validierung im + Konstruktor. +- `ModelPriceView` – Lese-/Anzeige-DTO mit nullable `updatedAt` und + `invalidUpdatedAt`-Flag. +- `ModelPriceKey` – Composite-Key für Löschungen. +- `ModelPriceChangeSet` – atomarer Block aus Upserts und Deletions, defensive + Listen-Kopie. + +### Cost-Komponenten (`application/cost`) +- `CostResult` – interpretierte Kosten-Anzeige mit Status-Flags. +- `CostCalculator` – `formatRow(...)` und `calculateAttempt(...)` (echt + implementiert), `formatTotal(...)` als Stub für AP-B. + +### Ports (`application/port/out`) +- `ModelPriceRepository` – `findAll`, `findByProviderAndModelName`, `upsert`, + `delete`, `saveAllChanges`. +- `AiInvocationSuccess` (erweitert) – neues Feld `usageMetadata`. + +### Use Cases (`application/usecase`) +- `DefaultManageModelPricesUseCase` – CRUD-Fassade mit ChangeSet-Konflikt- + validierung (vier Regeln) und Provider-Whitelist beim Upsert. +- `ModelPriceValidationException` – deutsche Validierungsfehler-Exception. + +### Application-Service-Anpassungen +- `AiNamingService` (erweitert) – reicht `AiUsageMetadata` aus dem + `AiInvocationSuccess` als Token-Felder in den `AiAttemptContext` weiter. +- `DocumentProcessingCoordinator` (erweitert) – + - neuer optionaler Konstruktor mit `ModelPriceRepository` und + `headlessMode`-Flag. + - `loadPriceSnapshot(modelName)` lädt Snapshot-Preis pro Versuch; Lookup- + Fehler liefern leeren Snapshot ohne Attempt-Verlust. + - `buildAttempt(...)` befüllt jetzt Token- und Preis-Snapshot-Felder im + `ProcessingAttempt`. + +### Domain-Anpassungen +- `AiAttemptContext` (erweitert) – vier nullable Token-Felder + (`inputTokens`, `outputTokens`, `cacheCreationInputTokens`, + `cacheReadInputTokens`); Backward-compatible Convenience-Konstruktor. +- `ProcessingAttempt` (erweitert) – sechs nullable Token-/Preis-Snapshot- + Felder; Convenience-Konstruktor und `withoutAiFields(...)` ohne Verhaltens- + änderung. + +## Adapter-Out-Modul + +- `SqliteConnectionFactory` (neu) – zentrale Connection-Factory; setzt + `PRAGMA journal_mode=WAL` und `PRAGMA busy_timeout=5000`. + Foreign-Key-Pragma wird bewusst nicht implizit gesetzt (Verhalten der + bisherigen `DriverManager.getConnection`-Stellen erhalten). +- `SqliteUnitOfWorkAdapter`, `SqliteProcessingAttemptRepositoryAdapter`, + `SqliteHistoryQueryAdapter`, `SqliteDocumentRecordRepositoryAdapter` + (jeweils geändert) – nutzen die neue Factory. +- `SqliteProcessingAttemptRepositoryAdapter.save()` (erweitert) – + INSERT um sechs neue Spalten erweitert, neue Hilfsmethode + `setNullableLong(...)`. +- `SqliteHistoryQueryAdapter.mapToProcessingAttempt(...)` (erweitert) – + liest die sechs neuen Spalten via `readNullableLong(...)`. +- `SqliteSchemaInitializationAdapter` (geändert) – erwartete Spalten/Indizes + bleiben am V1-Zielschema; Doc-Klarstellung, dass V2 additiv auf der Baseline + arbeitet. +- `SqliteModelPriceRepositoryAdapter` (neu) – `findAll`, + `findByProviderAndModelName`, `upsert`, `delete`, `saveAllChanges` + (UPSERT via `ON CONFLICT(provider, model_name) DO UPDATE`, transaktionaler + Batch). Lese-Mapping behandelt `DateTimeParseException` als + `invalidUpdatedAt`. +- `ModelPriceRepositoryException` (neu) – technischer JDBC-Fehler. + +### KI-Adapter +- `AnthropicClaudeHttpAdapter` (geändert) – neue Methode + `extractTokenUsageFromResponse(JSONObject)` für `usage.input_tokens`, + `usage.output_tokens`, `usage.cache_creation_input_tokens`, + `usage.cache_read_input_tokens` mit Validierung (negativ, > 10 Mio., + nicht-numerisch → NULL + WARN). +- `OpenAiHttpAdapter` (geändert) – analoge Methode mit Mapping + `prompt_tokens → input_tokens`, `completion_tokens → output_tokens`; + Cache-Felder bleiben null. + +## GUI-Modul + +### Neuer Tab "Modell-Preise" +- `adapter-in-gui/modelprices/GuiModelPriceManagementPort` (neu) – + Bridge-Port für GUI-Zugriff auf Modell-Preise. +- `adapter-in-gui/modelprices/GuiModelPricesTab` (neu) – TableView mit + editierbaren Preisspalten (`In/1M USD`, `Out/1M USD`), Lösch-Button mit + Bestätigungsdialog, Add-Dialog mit Provider-Auswahl, Speichern-Aktion über + `ModelPriceChangeSet`. Konvertierung Nano-USD ↔ `$/1M Tokens` mit + HALF-UP-Rundung; unbekannte Provider werden read-only mit Tooltip + angezeigt; `updatedAt = null` als "ungueltig". + +### Anbindung im Workspace +- `GuiConfigurationEditorWorkspace` (geändert) – sechster Tab "Modell- + Preise" wird angelegt; neue Methode `warnIfActiveModelHasNoPriceEntry()` + zeigt vor dem Speichern eine deutsche Warnung an, wenn das aktuell + ausgewählte Modell keinen Preis-Eintrag besitzt. +- `GuiStartupContext` (geändert) – neues optionales Feld + `modelPriceManagementPort` mit Backward-Kompatibilität. +- `BootstrapRunner` (geändert) – neue Methode + `buildGuiModelPriceManagementPort()` und Helfer für die Verdrahtung; + Coordinator wird mit `ModelPriceRepository` und `headlessMode`-Flag + versorgt. + +### History-Tab +- `GuiHistoryTab` (geändert) – drei zusätzliche Spalten in der + Versuchstabelle: Input-Tokens, Output-Tokens, Kosten. Cache-only-Versuche + zeigen "nur Cache-Tokens, keine Standardkosten"; fehlender Preis-Snapshot + führt zu "Preis fehlt"; Mikrobeträge als "< $0.0001"; Cache-Beteiligung + ergänzt Suffix "(ohne Cache-Anteil)". + +### Summary-Banner +- `BatchRunSummaryBanner` (geändert) – aus einzeiliger HBox wurde eine + vierzeilige VBox: Status-Zeile, Token-Zeile, Kosten-Zeile, optionale + Cache-only-Zeile. Neue Record-Klasse `BatchRunTokenSummary` mit + `empty()`-Default; bestehende `update(Map)`-Aufrufer bleiben funktionsfähig. + +## Testanpassungen + +- `pdf-umbenenner-application/.../service/AiNamingServiceTest` und + `pdf-umbenenner-bootstrap/.../e2e/StubAiInvocationPort` – alte + `AiInvocationSuccess`-Konstruktoraufrufe um `AiUsageMetadata.empty()` + ergänzt. +- `SqliteSchemaInitializationAdapterTest.fall1_leereDb_processingAttemptHatAlleErwartetenSpalten` + prüft jetzt zusätzlich die sechs Token-/Preis-Spalten. +- `GuiAdapterSmokeTest.editorWorkspace_startStateShowsEmptyHeaderDefaultsAndOneTab` + erwartet jetzt sechs Tabs inkl. "Modell-Preise". + +## Build und Verifizierung + +- `mvn clean verify` läuft auf dem Reactor `pdf-umbenenner-parent` durch + (Tests grün auf allen Modulen). +- Commit `08ec021` auf `main` gepusht. + +## Bewusst ausgesparte Bereiche (für AP-B / AP-C) + +- `CostCalculator.formatTotal(...)` ist ein Stub und wirft + `UnsupportedOperationException`. +- `TokenStatisticsReadModelPort`, `QueryCostAnalysisFullUseCase`, + `QueryCostAnalysisHeaderOnlyUseCase`, `QueryRunSummaryUseCase`, + `SqliteTokenStatisticsReadModelAdapter` sind nicht enthalten. +- Summary-Banner zeigt aktuell `0/0` Tokens und `$0.0000` Kosten, da das + Read-Model erst in AP-B verdrahtet wird. +- CLI-Befehle für Modell-Preise (#99) und Modell-Combobox-Filter (#98) + sind AP-C.