Skip to content

Skalierungs-Add-ons: S3-Storage-Treiber + optionale Batch-API - #18

Merged
veniplex merged 2 commits into
mainfrom
claude/ai-module-large-data-processing-oueat5
Jul 16, 2026
Merged

veniplex merged 2 commits into
mainfrom
claude/ai-module-large-data-processing-oueat5

Conversation

@veniplex

@veniplex veniplex commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Überblick

Zwei der zurückgestellten Skalierungs-Bausteine (nach PR #14 Kern und PR #17 Worker + HNSW), beide additiv und opt-in — ohne die neuen Env-Variablen/Flags läuft alles byte-gleich wie bisher:

  1. S3-/Object-Storage-Treiber — Datei-Ablage per Env umschaltbar zwischen local (Default) und s3.
  2. Optionale Batch-API für die „Complete"-Generierung — ~50 % günstiger, per Admin-Flag.

1 — S3-/Object-Storage-Treiber (feat(storage))

Für mehrere GB an Modul-Material braucht Self-Hosting Object Storage statt einer einzelnen Festplatte. Per Env umschaltbar zwischen local (Default, unverändert) und s3 (AWS S3, MinIO, Cloudflare R2, Hetzner). Keine Schema-/DB-Änderung.

  • src/lib/storage/ ersetzt storage.ts: driver.ts (Interface + treiber-agnostische Reinfunktionen + geteilter Upload-Meter für Größe/sha256/maxBytes), local.ts (bisherige node:fs-Implementierung 1:1), s3.ts (AWS-SDK lazy importiert; PutObject/GetObject/HeadObject/DeleteObject + lib-storage Upload fürs Streaming; ranged GetObject für HTTP-Range/206), index.ts (Fassade + neu readFileBuffer).
  • Aufrufer: fileStream ist jetzt async (Datei-Route + Zip-Entpacker awaiten es); extract.ts/media.ts lesen Binärdateien über readFileBuffer statt direkt von der Disk — damit funktionieren OCR/Transkription/PDF-/DOCX-Extraktion auch gegen S3.
  • Env: STORAGE_DRIVER=local|s3; für S3: S3_BUCKET (Pflicht), S3_REGION/AWS_REGION, S3_ENDPOINT, S3_FORCE_PATH_STYLE, S3_KEY_PREFIX. Credentials über die Standard-AWS-Chain.

2 — Optionale Batch-API für vollständige Generierung (feat(generation))

Die „Complete"-Generierung macht pro Thema einen LLM-Call (MAP) — bei 100+ Themen viele Live-Calls. Anthropic Message Batches / OpenAI Batch erledigen dieselben Calls asynchron zu ~50 % der Kosten. Per Admin-Flag ai.useBatchApi (Default aus); ohne Flag oder bei Nicht-Anthropic/OpenAI-Providern bleibt der synchrone Live-Pfad aktiv (auch als Fallback, falls ein Batch-Submit fehlschlägt).

  • batch-adapter.ts (neu): reine, unit-getestete Bausteine (Zod→JSON-Schema, Anthropic-Tool-Use-Requests, OpenAI-json_schema-Tasks, Result-Parsing inkl. Usage) + dünne Netz-Calls; Vendor-SDKs (@anthropic-ai/sdk, openai) lazy importiert. resolveBatchProvider liest Credentials aus Settings + BYOK.
  • generate.ts: MAP-Prompts in geteilte buildCardPrompt/buildQuestionPrompt und Dedup+Insert in persistCards/persistQuestions extrahiert — Live- und Batch-Pfad erzeugen identische Ausgabe. runCoverageGeneration submittet bei aktivem Flag einen Batch für alle offenen Themen und kehrt zurück; Grounding/Retrieval bleibt live.
  • batch-poll.ts (neu): cron-gesteuerter Poller (*/5-Queue poll-batches), holt nach Batch-Abschluss die Ergebnisse, dedupt + inserted, erfasst pro Thema den Token-Verbrauch (Pflicht-Anforderung A) und schließt den Job ab. Fehlgeschlagene Items werden in der Coverage markiert, sodass ein Re-Run nur sie neu erzeugt.
  • Schema: generation_job bekommt nullbare batch_ref + batch_model (Migration 0034); sonst keine Datenmodell-Änderung.
  • Settings/UI: Flag ai.useBatchApi + Admin-Toggle (Switch) mit i18n (de/en).

Async-Hinweis: Der Batch-Pfad ist asynchron — Ergebnisse treffen über Minuten bis (Worst Case) Stunden ein, die Coverage-Anzeige füllt sich nach und nach. Submit/Poll/Fetch gegen die echten Vendor-APIs wird auf dem produktiven Stand verifiziert; CI deckt die reine Builder-/Parser-Logik ab.


Tests / Verifikation

  • Neue Unit-Tests: src/lib/storage/storage.test.ts (S3-Treiber gegen aws-sdk-client-mock + echter LocalStorageDriver-Round-Trip) und src/lib/ai/generation/batch-adapter.test.ts (Schema-Konvertierung, Request-Builder, Result-Parser, Provider-Resolution).
  • Statisch: npm run typecheck (0 Fehler), npm run lint (sauber), npm run test (102 bestanden, 3 skipped), npm run build (erfolgreich).
  • Default-Pfad unverändert: ohne STORAGE_DRIVER bzw. mit useBatchApi aus verhält sich alles exakt wie bisher.

Noch offen (Folge-PR)

E — tus-Resumable-Uploads (tus-s3-store baut auf dem S3-Treiber auf).

🤖 Generated with Claude Code

https://claude.ai/code/session_0191JikNRN8Q2HBtf6fpLXmH

claude added 2 commits July 16, 2026 14:51
Put storage behind a backend-agnostic driver interface selected by the
STORAGE_DRIVER env var: `local` (default, unchanged on-disk behaviour) or
`s3` for S3 / S3-compatible object storage (AWS S3, MinIO, Cloudflare R2,
Hetzner). Additive and opt-in — without STORAGE_DRIVER the local driver is
used and behaviour is byte-for-byte the same as before.

- src/lib/storage/ replaces the single storage.ts:
  - driver.ts: StorageDriver interface, shared pure helpers (safeInlineMime,
    sanitizeName, StorageLimitError), the canonical rel-path key builder, and
    the shared size+sha256+maxBytes upload meter.
  - local.ts: LocalStorageDriver (the previous fs implementation, verbatim).
  - s3.ts: S3StorageDriver — AWS SDK imported lazily so the local path never
    loads it; PutObject/GetObject/HeadObject/DeleteObject + lib-storage
    multipart Upload for streams; ranged GetObject for HTTP range requests;
    credentials via the standard AWS chain.
  - index.ts: facade keeping every existing named export + new readFileBuffer,
    caches the selected driver.
- fileStream is now async (S3 GetObject is async); the file route and the
  zip-unpacker await it.
- extract.ts and media.ts now read stored binaries through readFileBuffer
  instead of reading local disk directly, so OCR/transcription/PDF/DOCX
  extraction work against S3 too.
- No schema change: the stored key is backend-agnostic.
- Tests: mocked S3 driver (put/get/head/delete/range/key-prefix/safe-key) and
  a real LocalStorageDriver round-trip (save/read/size/delete, streaming with
  size+hash+maxBytes, byte-range).
- Docs: STORAGE_DRIVER / S3_* documented in .env.example, docker-compose.yml
  and docs/self-hosting.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0191JikNRN8Q2HBtf6fpLXmH
Run the coverage-generation MAP step through the provider's async Batch API
(Anthropic Message Batches / OpenAI Batch) — ~50% cheaper for large modules.
Additive and opt-in via the `ai.useBatchApi` admin flag (default off); with the
flag off, or for non-Anthropic/OpenAI providers, the synchronous live path is
unchanged and is also the fallback if a batch submit fails.

- src/lib/ai/generation/batch-adapter.ts (new): pure, unit-tested builders and
  parsers (Zod->JSON schema, Anthropic tool-use requests, OpenAI json_schema
  tasks, result parsing with usage) plus thin network calls; the vendor SDKs
  (@anthropic-ai/sdk, openai) are imported lazily so non-batch deploys never
  load them. resolveBatchProvider reads credentials from settings + BYOK.
- generate.ts: extract the two MAP prompts into shared buildCardPrompt/
  buildQuestionPrompt and the dedup+insert into persistCards/persistQuestions,
  so live and batch paths produce identical output. runCoverageGeneration
  submits one batch for all uncovered topics when the flag is on and returns;
  grounding/retrieval still runs live.
- src/lib/ai/generation/batch-poll.ts (new): cron-driven poller that, once a
  vendor batch ends, fetches results, de-dupes + inserts, records token usage
  per topic (requirement A), and completes the job. Failed items are marked in
  coverage so a re-run regenerates only them.
- jobs: new poll-batches queue on a */5 schedule (matches send-reminders).
- schema: generation_job gains nullable batch_ref + batch_model (migration
  0034); no other data-model change.
- settings: ai.useBatchApi flag + admin toggle (Switch) with i18n.

The batch path is asynchronous: results arrive over minutes to hours and the
coverage view fills gradually. Submit/poll/fetch against the real vendor APIs
is verified on the production stand; CI covers the pure builder/parser logic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0191JikNRN8Q2HBtf6fpLXmH
@veniplex veniplex changed the title Optionaler S3-/Object-Storage-Treiber Skalierungs-Add-ons: S3-Storage-Treiber + optionale Batch-API Jul 16, 2026
@veniplex veniplex added the release-candidate Creates a new release and deployment of docker image, once merged. label Jul 16, 2026
@veniplex
veniplex merged commit b4f4050 into main Jul 16, 2026
2 of 3 checks passed
@veniplex
veniplex deleted the claude/ai-module-large-data-processing-oueat5 branch July 16, 2026 17:55
github-actions Bot added a commit that referenced this pull request Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-candidate Creates a new release and deployment of docker image, once merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants