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

7.9 KiB
Raw Permalink Blame History

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.