Files
pdf-umbenenner/pdf-umbenenner-adapter-out/src/main/resources/db/migration/V2__token_tracking.sql
T
marcus 08ec021b5f feat: AP-A Token-Tracking Fundament - Schema, Adapter, Use Cases, GUI (#74)
Erste Stufe der V3.3-Spezifikation: Token- und Kosten-Tracking-Fundament.

Schema und Persistenz:
- Neue Flyway-Migration V2__token_tracking.sql mit sechs Token-/Preis-Snapshot-
  Spalten in processing_attempt, neuer model_price-Tabelle (Composite-Key
  provider+model_name) und Default-Preisen fuer beide Provider-Familien.
- SqliteModelPriceRepositoryAdapter mit UPSERT, transaktionalem Batch und
  invalidUpdatedAt-Mapping.
- Zentrale SqliteConnectionFactory; alle direkten DriverManager.getConnection-
  Stellen in den Repository-Adaptern (Document, Attempt, History, UnitOfWork)
  auf die Factory umgezogen, damit WAL und busy_timeout pro Connection greifen.

Application und Domain:
- Neue DTOs AiUsageMetadata, ModelPriceEntry/View/Key/ChangeSet, CostResult.
- AiInvocationSuccess um usageMetadata erweitert; AiAttemptContext um vier
  nullable Token-Felder.
- ProcessingAttempt um sechs Token-/Preis-Snapshot-Felder erweitert
  (Convenience-Konstruktor und withoutAiFields-Factory unveraendert).
- ModelPriceRepository-Port mit Schreib-/Lese-Trennung.
- DefaultManageModelPricesUseCase mit ChangeSet-Konfliktvalidierung,
  Provider-Whitelist und Clock-Stempel.
- CostCalculator (formatRow + calculateAttempt; formatTotal als Stub fuer AP-B).

KI-Adapter:
- AnthropicClaudeHttpAdapter und OpenAiHttpAdapter extrahieren Token-Daten
  aus den Response-Bodies inklusive Validierung (negativ, > 10 Mio., nicht
  numerisch -> NULL + WARN-Log).

BatchRunProcessingUseCase-Hook:
- DocumentProcessingCoordinator erhaelt optional ModelPriceRepository und ein
  Headless-Flag. Beim Bau eines KI-Versuchs wird der Snapshot-Preis fuer
  (Provider, Modell) geladen und mit den Token-Daten am ProcessingAttempt
  persistiert. Lookup-Fehler verlieren keinen Attempt.

GUI:
- Neuer Tab "Modell-Preise" (TableView mit Editierfeldern, Add-Dialog,
  Loesch-Bestaetigung, Konvertierung Nano-USD <-> $/1M Tokens).
- History-Tab um drei Spalten erweitert: Input-Tokens, Output-Tokens, Kosten.
- Summary-Banner um Token-, Kosten- und Cache-only-Zeile erweitert
  (Default-Werte; AP-B liefert spaeter die echten Aggregate).
- Konfigurations-Tab warnt beim Speichern, wenn das aktive Modell keinen
  Preis-Eintrag hat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 09:49:50 +02:00

65 lines
3.7 KiB
SQL

-- V2: Token-Erfassung mit Preis-Snapshot in processing_attempt;
-- neue model_price-Tabelle mit Composite Primary Key.
-- Verifizierter Stand: V1__initial_schema.sql ist die einzige bisherige
-- Migration im Projekt.
ALTER TABLE processing_attempt
ADD COLUMN input_tokens INTEGER
CHECK (input_tokens IS NULL OR (input_tokens >= 0 AND input_tokens <= 10000000));
ALTER TABLE processing_attempt
ADD COLUMN output_tokens INTEGER
CHECK (output_tokens IS NULL OR (output_tokens >= 0 AND output_tokens <= 10000000));
ALTER TABLE processing_attempt
ADD COLUMN cache_creation_input_tokens INTEGER
CHECK (cache_creation_input_tokens IS NULL OR (cache_creation_input_tokens >= 0 AND cache_creation_input_tokens <= 10000000));
ALTER TABLE processing_attempt
ADD COLUMN cache_read_input_tokens INTEGER
CHECK (cache_read_input_tokens IS NULL OR (cache_read_input_tokens >= 0 AND cache_read_input_tokens <= 10000000));
ALTER TABLE processing_attempt
ADD COLUMN price_input_per_token_nano_usd INTEGER
CHECK (price_input_per_token_nano_usd IS NULL OR (price_input_per_token_nano_usd >= 0 AND price_input_per_token_nano_usd <= 100000000));
ALTER TABLE processing_attempt
ADD COLUMN price_output_per_token_nano_usd INTEGER
CHECK (price_output_per_token_nano_usd IS NULL OR (price_output_per_token_nano_usd >= 0 AND price_output_per_token_nano_usd <= 100000000));
CREATE TABLE model_price (
provider TEXT NOT NULL,
model_name TEXT NOT NULL,
price_input_per_token_nano_usd INTEGER NOT NULL CHECK (price_input_per_token_nano_usd >= 0 AND price_input_per_token_nano_usd <= 100000000),
price_output_per_token_nano_usd INTEGER NOT NULL CHECK (price_output_per_token_nano_usd >= 0 AND price_output_per_token_nano_usd <= 100000000),
currency TEXT NOT NULL DEFAULT 'USD' CHECK (currency = 'USD'),
updated_at TEXT NOT NULL,
PRIMARY KEY (provider, model_name)
);
CREATE INDEX idx_processing_attempt_started_at_provider_fp_model
ON processing_attempt (started_at, ai_provider, fingerprint, model_name);
CREATE INDEX idx_processing_attempt_run_id_provider_model
ON processing_attempt (run_id, ai_provider, model_name);
-- Default-Preise (Stand 2026-05-08, in Nano-USD pro Token)
-- Quellen (abgerufen 2026-05-08):
-- OpenAI: https://openai.com/api/pricing/
-- Anthropic: https://www.anthropic.com/pricing
-- ON CONFLICT DO NOTHING: schuetzt vor manuell vorhandenen Default-Zeilen.
INSERT INTO model_price
(provider, model_name, price_input_per_token_nano_usd, price_output_per_token_nano_usd, currency, updated_at)
VALUES
('openai-compatible', 'gpt-4o-mini', 150, 600, 'USD', '2026-05-08T00:00:00Z'),
('openai-compatible', 'gpt-4o', 2500, 10000, 'USD', '2026-05-08T00:00:00Z'),
('openai-compatible', 'gpt-4.1', 2000, 8000, 'USD', '2026-05-08T00:00:00Z'),
('openai-compatible', 'gpt-4.1-mini', 400, 1600, 'USD', '2026-05-08T00:00:00Z'),
('openai-compatible', 'gpt-4.1-nano', 100, 400, 'USD', '2026-05-08T00:00:00Z'),
('openai-compatible', 'gpt-5', 1250, 10000, 'USD', '2026-05-08T00:00:00Z'),
('openai-compatible', 'gpt-5-mini', 250, 2000, 'USD', '2026-05-08T00:00:00Z'),
('claude', 'claude-haiku-4-5-20251001', 1000, 5000, 'USD', '2026-05-08T00:00:00Z'),
('claude', 'claude-sonnet-4-6', 3000, 15000, 'USD', '2026-05-08T00:00:00Z'),
('claude', 'claude-opus-4-7', 5000, 25000, 'USD', '2026-05-08T00:00:00Z')
ON CONFLICT (provider, model_name) DO NOTHING;