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>
This commit is contained in:
+42
-7
@@ -11,25 +11,37 @@ import java.util.Objects;
|
||||
* <li>AI infrastructure details (model name, prompt identifier)</li>
|
||||
* <li>Request size metrics (processed pages, sent character count)</li>
|
||||
* <li>Raw AI output (for audit and diagnostics; stored in SQLite, not in log files)</li>
|
||||
* <li>Token-Verbrauch (Standard- und Anthropic-Cache-Tokens) – nullable, da nicht
|
||||
* jeder Provider Token-Counts liefert und einzelne Felder vom Adapter wegen
|
||||
* ungueltiger Werte ({@code null}, < 0, > 10 Mio.) auf {@code null}
|
||||
* gesetzt werden koennen.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This context is produced whenever an AI call is attempted, regardless of whether
|
||||
* the call succeeded or failed. Fields that could not be determined (e.g. raw response
|
||||
* on connection failure) may be {@code null}.
|
||||
*
|
||||
* @param modelName the AI model name used in the request; never null
|
||||
* @param promptIdentifier stable identifier of the prompt template; never null
|
||||
* @param processedPageCount number of PDF pages included in the extraction; must be >= 1
|
||||
* @param sentCharacterCount number of document-text characters sent to the AI; must be >= 0
|
||||
* @param aiRawResponse the complete raw AI response body; {@code null} if the call did
|
||||
* not return a response body (e.g. timeout or connection error)
|
||||
* @param modelName the AI model name used in the request; never null
|
||||
* @param promptIdentifier stable identifier of the prompt template; never null
|
||||
* @param processedPageCount number of PDF pages included in the extraction; must be >= 1
|
||||
* @param sentCharacterCount number of document-text characters sent to the AI; must be >= 0
|
||||
* @param aiRawResponse the complete raw AI response body; {@code null} if the call did
|
||||
* not return a response body (e.g. timeout or connection error)
|
||||
* @param inputTokens Anzahl Standard-Input-Tokens; {@code null} wenn nicht ermittelbar
|
||||
* @param outputTokens Anzahl Standard-Output-Tokens; {@code null} wenn nicht ermittelbar
|
||||
* @param cacheCreationInputTokens Anzahl Cache-Schreib-Tokens (Anthropic); {@code null} bei OpenAI oder fehlend
|
||||
* @param cacheReadInputTokens Anzahl Cache-Lese-Tokens (Anthropic); {@code null} bei OpenAI oder fehlend
|
||||
*/
|
||||
public record AiAttemptContext(
|
||||
String modelName,
|
||||
String promptIdentifier,
|
||||
int processedPageCount,
|
||||
int sentCharacterCount,
|
||||
String aiRawResponse) {
|
||||
String aiRawResponse,
|
||||
Long inputTokens,
|
||||
Long outputTokens,
|
||||
Long cacheCreationInputTokens,
|
||||
Long cacheReadInputTokens) {
|
||||
|
||||
/**
|
||||
* Compact constructor validating mandatory fields.
|
||||
@@ -50,4 +62,27 @@ public record AiAttemptContext(
|
||||
"sentCharacterCount must be >= 0, but was: " + sentCharacterCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience-Konstruktor ohne Token-Felder.
|
||||
*
|
||||
* <p>Setzt alle vier Token-Felder auf {@code null}. Wird von Aufrufern verwendet,
|
||||
* die noch keine Token-Daten beistellen (z.B. KI-Aufrufe ohne usage-Antwort,
|
||||
* frueher V3.2-Bestand oder Tests, die das Token-Tracking nicht beruehren).
|
||||
*
|
||||
* @param modelName AI model name; never null
|
||||
* @param promptIdentifier stable prompt identifier; never null
|
||||
* @param processedPageCount number of PDF pages included; must be >= 1
|
||||
* @param sentCharacterCount number of characters sent to AI; must be >= 0
|
||||
* @param aiRawResponse raw AI response body or {@code null}
|
||||
*/
|
||||
public AiAttemptContext(
|
||||
String modelName,
|
||||
String promptIdentifier,
|
||||
int processedPageCount,
|
||||
int sentCharacterCount,
|
||||
String aiRawResponse) {
|
||||
this(modelName, promptIdentifier, processedPageCount, sentCharacterCount, aiRawResponse,
|
||||
null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user