diff --git a/docs/specs/V3_3_-_Spezifikation.md b/docs/specs/V3_3_-_Spezifikation.md index 3aede7b..04742b9 100644 --- a/docs/specs/V3_3_-_Spezifikation.md +++ b/docs/specs/V3_3_-_Spezifikation.md @@ -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.