Files
pdf-umbenenner/docs/ap-a-token-tracking-zusammenfassung.md
2026-05-09 09:51:31 +02:00

169 lines
7.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.