- `lib/url-fetch.ts`: fetchUrlContent aus decks-from-image herausgezogen
— gemeinsam genutzte Logik für mana-search + direktes HTTP-Fetch-Fallback
- `decks-generate.ts`: optionales `url`-Feld im Input-Schema;
URL-Inhalt wird an den Prompt angehängt wenn vorhanden
- `decks.ts` (web): `generateDeck()` akzeptiert jetzt `url?: string`
- UI: imageUrl wird für Text-KI + Bild-KI als Kontext genutzt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- API: fetchUrlContent() via mana-search /api/v1/extract (Fallback: direktes Fetch)
- URL-Inhalt wird als Kontext an die LLM-Karten-Generierung übergeben
- Client: url-only Flow sendet JSON statt FormData (Bun-Kompatibilität)
- Deck-Neu-Seite: URL-Eingabefeld neben dem Datei-Upload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neuer Endpoint POST /api/v1/decks/from-image akzeptiert bis zu 5 Bilder
(PNG/JPG/WebP, max 10 MiB je) oder PDFs (max 30 MiB je) als multipart/form-data.
Alle Dateien werden in einem einzigen mana-llm Vision-Call verarbeitet
(mana/vision → llava → Gemini 2.5-flash → GPT-4o Fallback-Chain).
PDFs werden von Gemini nativ verstanden (Layout, Tabellen, Bilder im Dokument)
ohne Zwischenschritt über Text-Extraktion oder Rendering. Der google.py-Provider
reicht den MIME-Type aus dem data:-URI direkt an types.Part.from_bytes() weiter.
- llm-client: chatVisionJson() mit images[]-Array (mehrere Bilder/Dokumente)
- decks-generate: GeneratedDeckSchema + insertGeneratedDeck() exportiert
- decks-from-image: neuer Route-Handler, MIME-Filter für image/* + application/pdf
- index: neue Route gemountet
- client.ts: apiForm() für multipart-Uploads ohne JSON.stringify
- decks.ts: generateDeckFromImage(files, opts)
- NewDeckCard + /decks/new: Dropzone mit Multi-File, Thumbnail-Strip, PDF-Icon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Server-side AI-Pfad mit atomischer Deck+Cards-Erzeugung:
POST /api/v1/decks/generate
body { prompt, language?: 'de'|'en', count?: 3..40 }
→ ruft mana-llm /v1/chat/completions mit `mana/structured`-Alias
(JSON-Output, hartes zod-Schema)
→ SystemPrompt fixiert das Output-Format (deck_name + cards mit
front/back), verbietet HTML/Code-Fences, akzeptiert Markdown
→ Validation: zod-strict, halluzinations-resilient
→ Insert: Deck + alle Karten + Reviews in einer DB-Transaction,
contentHash beim Insert geschrieben (Phase-9j-konform)
→ 502 wenn LLM Schema bricht oder Endpoint timeoutet (90s cap)
Frontend:
- Neue Route /decks/new-ai mit Prompt-Form, Anzahl-Karten-Slider
(3-40), Sprach-Wähler (DE/EN, default = aktuelle UI-Sprache).
- 5 klickbare Beispiel-Prompts als Inspiration.
- busy-State zeigt "10-60s typisch" (Disclaimer für die LLM-Latenz).
- "✨ KI-Deck"-Button neben "Neues Deck" auf /decks.
- error-Display mit role=alert.
apps/api/src/services/llm-client.ts kapselt den Aufruf:
- mana/structured als Alias (Routing-Layer wählt Provider)
- response_format json_object
- 90s-Timeout per AbortController
- LlmError mit status + body für saubere 502-Mapping
- Optional CARDS_LLM_API_KEY-Env (für später, wenn mana-llm
GPU_API_KEY enforce'd)
Auth: aktuell User-JWT via authMiddleware. Tier-Gating bewusst
nicht aktiv — Cards-MVP ist tier-frei. Wenn AI-Generation Credits
kosten soll, kommt requireTier('beta') + creditsClient.reserve()
davor (Phase-6-Plumbing ist da, ein-Liner-Aktivierung).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>