@@ -1,6 +1,6 @@
|
||||
# V3.3 – Token- und Kosten-Tracking
|
||||
|
||||
**Status:** Implementierungsvorbereitend – Code-Reads vor Implementierungsstart erforderlich (siehe Schlussabschnitt)
|
||||
**Status:** Implementierungsbereit – alle Code-Reads erledigt (siehe Schlussabschnitt)
|
||||
**Erstellt:** 2026-05-08
|
||||
**Aktualisiert:** 2026-05-08 (V6 nach fünftem Review)
|
||||
**Autor:** Marcus (mit Claude als Mentor)
|
||||
@@ -250,7 +250,7 @@ also auch Aggregationsschlüssel:
|
||||
|
||||
### V3.3-Provider-Whitelist
|
||||
|
||||
V3.3 unterstützt fachlich **nur** `openai-compatible` und `anthropic`.
|
||||
V3.3 unterstützt fachlich **nur** `openai-compatible` und `claude`.
|
||||
Die DB ist bewusst offen (keine CHECK-Constraint auf `provider`).
|
||||
Falls in `model_price` ein unbekannter Provider-Wert vorhanden ist,
|
||||
zeigt die GUI ihn **read-only** mit Hinweis „Unbekannter Provider" an.
|
||||
@@ -408,9 +408,9 @@ VALUES
|
||||
('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'),
|
||||
('anthropic', 'claude-haiku-4-5-20251001', 1000, 5000, 'USD', '2026-05-08T00:00:00Z'),
|
||||
('anthropic', 'claude-sonnet-4-6', 3000, 15000, 'USD', '2026-05-08T00:00:00Z'),
|
||||
('anthropic', 'claude-opus-4-7', 5000, 25000, '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;
|
||||
```
|
||||
|
||||
@@ -951,6 +951,13 @@ performanter; V3.x falls Performance-Probleme in der Praxis auftreten.
|
||||
| RunLock-Mechanismus | Bleibt unverändert |
|
||||
| SQL-Boolean-Resultsets | Adapter mappt via `getInt(...) != 0` |
|
||||
|
||||
**Implementierungshinweis:** Code-Read hat ergeben, dass mehrere Repository-Adapter direkte
|
||||
`DriverManager.getConnection`-Aufrufe ohne `SQLiteConfig` verwenden
|
||||
(`SqliteProcessingAttemptRepositoryAdapter`, `SqliteUnitOfWorkAdapter` u.a.). Eine zentrale
|
||||
DataSource würde diese nicht abdecken. AP-A umfasst daher zwingend die Einführung einer
|
||||
zentralen Connection-Factory sowie den Umzug aller direkten `DriverManager.getConnection`-Stellen
|
||||
auf diese Factory, bevor WAL und `busy_timeout` wirksam sind.
|
||||
|
||||
### Hook im `BatchRunProcessingUseCase`
|
||||
|
||||
```
|
||||
@@ -986,9 +993,9 @@ performanter; V3.x falls Performance-Probleme in der Praxis auftreten.
|
||||
|
||||
- **Tabelle** in `$ pro 1M Tokens`; intern Nano-USD/Token. Eingabe max 6 Nachkommastellen.
|
||||
- **Provider und Modellname** read-only nach Speichern (Composite Primary Key)
|
||||
- **Unbekannte Provider** (nicht `openai-compatible`/`anthropic`): Zeile **read-only mit Tooltip-Icon** `ⓘ` „Unbekannter Provider – Bearbeitung in V3.3 nicht unterstützt"; **Löschen ist erlaubt**
|
||||
- **Unbekannte Provider** (nicht `openai-compatible`/`claude`): Zeile **read-only mit Tooltip-Icon** `ⓘ` „Unbekannter Provider – Bearbeitung in V3.3 nicht unterstützt"; **Löschen ist erlaubt**
|
||||
- **Lösch-Button** mit Bestätigungsdialog
|
||||
- **„Modell hinzufügen"**-Dialog: Provider-Combobox nur `openai-compatible` und `anthropic`
|
||||
- **„Modell hinzufügen"**-Dialog: Provider-Combobox nur `openai-compatible` und `claude`
|
||||
- **„Speichern"**: transaktionaler Batch
|
||||
- **`updatedAt = null`** → Spalte zeigt „ungültig" mit Tooltip
|
||||
|
||||
@@ -1063,9 +1070,13 @@ Pro-Attempt-Snapshot. Identischer Modellname zwei Provider → zwei Zeilen.
|
||||
|
||||
#### Quellname / Neuster Name
|
||||
|
||||
`last_known_source_file_name` (LEFT JOIN, NULL→„–"); `final_target_file_name`
|
||||
`last_known_source_file_name` (mutabel – wird bei jedem Verarbeitungslauf auf
|
||||
den aktuellen Quellpfad aktualisiert; zeigt daher den zuletzt bekannten Quellnamen,
|
||||
nicht zwingend den ursprünglichen) (LEFT JOIN, NULL→„–"); `final_target_file_name`
|
||||
des jüngsten Versuchs (NULL→„–").
|
||||
|
||||
Tooltip Quellname-Spalte: „letzter bekannter Quellname (wird bei Wiederholung aktualisiert)".
|
||||
|
||||
#### Banner und Gesamt-Kosten-Kopfzeile
|
||||
|
||||
Gemäß Anzeige-Semantik. Kopfzeile bezieht sich auf vollständigen Zeitraum.
|
||||
@@ -1210,7 +1221,7 @@ Anthropic-Modell-IDs mit achtstelligem Datumssuffix ohne Trennstriche
|
||||
| Flyway-Migration | Läuft **vor** jedem CLI-DB-Zugriff (Schema-Aktualität sicherstellen) |
|
||||
| Argumentquoting | Standard-Shell-Verhalten. Modellnamen mit Sonderzeichen oder Leerzeichen müssen vom User in Anführungszeichen gesetzt werden (z.B. `--upsert-model-price openai-compatible "gpt-4o-mini" 0.15 0.60`). Java-`args[]`-Parsing verwendet die bereits durch die Shell aufgespaltenen Argumente. |
|
||||
| Validierung | Identisch zur GUI-Validierung (`ManageModelPricesUseCase`). Verstöße → deutsche Fehlermeldung auf STDERR, Exit-Code 1. |
|
||||
| Whitelist-Validierung | `--upsert-model-price` lehnt unbekannte Provider ab (nur `openai-compatible`/`anthropic` erlaubt). `--delete-model-price` akzeptiert auch unbekannte Provider, damit verwaiste Einträge entfernbar bleiben. |
|
||||
| Whitelist-Validierung | `--upsert-model-price` lehnt unbekannte Provider ab (nur `openai-compatible`/`claude` erlaubt). `--delete-model-price` akzeptiert auch unbekannte Provider, damit verwaiste Einträge entfernbar bleiben. |
|
||||
| Ausgabe-Format | `--list-model-prices`: feste Spaltenbreiten oder einfache Tabular-Ausgabe nach STDOUT, eine Zeile pro Eintrag, Header-Zeile. |
|
||||
|
||||
**Exit-Code-Matrix:**
|
||||
@@ -1277,6 +1288,7 @@ Code-Read).
|
||||
|
||||
- [ ] `V2__token_tracking.sql` erstellt und auf V1-DB getestet
|
||||
- [ ] WAL + `busy_timeout` 5s pro Connection verifiziert
|
||||
- [ ] Zentrale Connection-Factory eingeführt; alle direkten `DriverManager.getConnection`-Aufrufe in Repository-Adaptern auf Factory umgezogen (Voraussetzung für wirksames WAL + `busy_timeout`)
|
||||
- [ ] `AiUsageMetadata`, `AiInvocationSuccess` erweitert
|
||||
- [ ] `ModelPriceEntry` (non-null updatedAt), `ModelPriceView` (nullable)
|
||||
- [ ] `ModelPriceChangeSet`, `ModelPriceKey`
|
||||
@@ -1570,13 +1582,13 @@ Code-Read).
|
||||
|
||||
| # | Code-Read | Erwartetes Ergebnis | Konsequenz bei Abweichung | Status |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `ai_provider`-Werte in `processing_attempt` | `openai-compatible`, `anthropic` | Default-Inserts und Lookup-Logik anpassen | offen |
|
||||
| 2 | Primärschlüsselspalte `processing_attempt.id` | `id` (AUTOINCREMENT, monoton) | Pseudo-SQL und Tie-Breaker anpassen | offen |
|
||||
| 3 | Insert-Pfad für `processing_attempt` | Eine zentrale Stelle | Hook für Tokens und Snapshot dort einbauen | offen |
|
||||
| 4 | Stabilität von `last_known_source_file_name` | Wird nach Initial-Scan nicht überschrieben | Tooltip ggf. anpassen | offen |
|
||||
| 5 | SQLite-JDBC-Window-Function-Unterstützung | `ROW_NUMBER() OVER (...)` ab SQLite 3.25 | Korrelierte Subquery als Fallback | offen |
|
||||
| 6 | Connection-Setup: WAL und `busy_timeout` | Beide aktiv; pro Connection | Im Adapter ergänzen | offen |
|
||||
| 7 | CLI-Adapter-Modul: Befehlsregistrierung | Vorhandenes Pattern nutzen | An bestehendes Pattern anschließen | offen |
|
||||
| 1 | `ai_provider`-Werte in `processing_attempt` | `openai-compatible`, `anthropic` | Default-Inserts und Lookup-Logik anpassen | abweichend – Provider-Wert ist `claude` (nicht `anthropic`); Spec angepasst |
|
||||
| 2 | Primärschlüsselspalte `processing_attempt.id` | `id` (AUTOINCREMENT, monoton) | Pseudo-SQL und Tie-Breaker anpassen | bestätigt – `id INTEGER PRIMARY KEY AUTOINCREMENT`; einzige Migration: V1 |
|
||||
| 3 | Insert-Pfad für `processing_attempt` | Eine zentrale Stelle | Hook für Tokens und Snapshot dort einbauen | bestätigt – `SqliteProcessingAttemptRepositoryAdapter.save()`, 20 Spalten |
|
||||
| 4 | Stabilität von `last_known_source_file_name` | Wird nach Initial-Scan nicht überschrieben | Tooltip ggf. anpassen | abweichend – Feld mutabel, wird bei jedem Lauf überschrieben; Spec klargestellt |
|
||||
| 5 | SQLite-JDBC-Window-Function-Unterstützung | `ROW_NUMBER() OVER (...)` ab SQLite 3.25 | Korrelierte Subquery als Fallback | bestätigt – sqlite-jdbc 3.45.1.0; SQLite 3.45.1; `ROW_NUMBER()` unterstützt |
|
||||
| 6 | Connection-Setup: WAL und `busy_timeout` | Beide aktiv; pro Connection | Im Adapter ergänzen | abweichend – WAL und `busy_timeout` fehlen; `DriverManager`-Stellen ohne Config; Spec um Connection-Factory-Pflicht ergänzt |
|
||||
| 7 | CLI-Adapter-Modul: Befehlsregistrierung | Vorhandenes Pattern nutzen | An bestehendes Pattern anschließen | bestätigt – Eigenbau-Switch-Case in `CliArgumentParser`; Einhänge-Punkt: vor `default` in switch (~Z.95), neuer `StartupMode` in `BootstrapRunner` |
|
||||
|
||||
### Nicht blockierend
|
||||
|
||||
@@ -1586,7 +1598,5 @@ Code-Read).
|
||||
| 9 | Lade-Indikator in der GUI | Wiederverwenden falls zentral | offen |
|
||||
| 10 | JavaFX `Pagination`-Komponente in der Codebasis | Pattern übernehmen falls vorhanden | offen |
|
||||
|
||||
**Hinweis zum Status:** Solange einer der freigabe-blockierenden Reads
|
||||
mit „offen" markiert ist, gilt die Spec als „implementierungsvorbereitend".
|
||||
Erst wenn alle Reads erledigt sind und die Ergebnisse hier dokumentiert
|
||||
sind, ist das Freigabe-Gate offen.
|
||||
**Hinweis zum Status:** Alle freigabe-blockierenden Reads sind erledigt. Das Freigabe-Gate
|
||||
ist offen. Die Implementierung kann mit AP-A beginnen.
|
||||
|
||||
Reference in New Issue
Block a user