Skip to content

Latest commit

 

History

History
165 lines (138 loc) · 7.69 KB

File metadata and controls

165 lines (138 loc) · 7.69 KB

ManouAPI – Minimaler Übersetzer-API-Plan

Ziel: Schlanke, performante, zustandslose API für ein einzelnes Modell (Default: Yamemaru/gemma-3-4b-Darija-GRPO-16bit) zur Übersetzung und optionalem Chat. Fokus auf schnelle Startbarkeit, Stabilität und wenig Abhängigkeiten.

1) Scope

  • Nicht enthalten: Auth, DB, Multi-Model-Registry, komplexe CI/CD, Docker (nur optionaler Hinweis in README).
  • Enthalten: Health, Info, Translate, minimalistisches Chat, Logging, CORS, Timeouts, Semaphore-Rate-Limit, optional INT4.

2) Tech-Stack & Constraints

  • Python 3.11+
  • FastAPI + Uvicorn
  • PyTorch (CUDA), Transformers
  • Optional: bitsandbytes (4-bit), Accelerate
  • Single-GPU (RTX 5080, 16 GB), FP16 als Standard, optional INT4 via Flag

3) Projektstruktur

manouapi/
  app/
    __init__.py
    main.py          # FastAPI App, Startup, Middlewares
    config.py        # ENV (.env) + Defaults
    model.py         # Model/Tokenizer Load, Worker, Generate/Translate
    router.py        # Route-Definitionen
    schemas.py       # Pydantic-Modelle
    utils.py         # Prompting, Token-Counting, Timing, Logging-Helfer
  requirements.txt
  README.md

4) Konfiguration (ENV / config.py)

  • MODEL_NAME (Default: Yamemaru/gemma-3-4b-Darija-GRPO-16bit)
  • USE_INT4 (Default: false) → wenn true, lade bnb-int4
  • MAX_NEW_TOKENS (Default: 128)
  • MAX_INPUT_CHARS (Default: 4000) → 413 bei Überschreitung
  • TEMPERATURE_TRANSLATE (Default: 0.2)
  • TEMPERATURE_CHAT (Default: 0.7)
  • TIMEOUT_SECONDS (Default: 60)
  • BATCH_SIZE (Default: 4) und BATCH_TIMEOUT_MS (Default: 50) – optional Micro-Batching
  • HOST (Default: 0.0.0.0), PORT (Default: 8000)
  • CORS_ORIGINS (Default: http://localhost:3000,http://localhost:5173) – Flutter-Origin eintragen

5) Model-Loading (Startup)

  • Setze torch.backends.cuda.matmul.allow_tf32 = True.
  • Tokenizer: AutoTokenizer.from_pretrained(MODEL_NAME, use_fast=True).
  • Modell:
    • FP16: AutoModelForCausalLM.from_pretrained(..., torch_dtype=torch.float16, device_map="auto")
    • INT4: AutoModelForCausalLM.from_pretrained(..., load_in_4bit=True, bnb_4bit_compute_dtype=torch.float16, device_map="auto")
  • Gerät: cuda:0 (Fallback prüfen; wenn kein CUDA → 500 beim Start).
  • Prüfe apply_chat_template des Tokenizers: falls vorhanden, für Prompts nutzen; sonst Fallback-Template.

6) Prompting-Richtlinien

  • Translate-Systemprompt: „You are a precise translator. Preserve meaning, tone, and idioms. Output only the translation. Source={src}, Target={tgt}.“
  • Chat-Systemprompt: „You are a helpful assistant specializing in Darija and Maghrebi Arabic.“
  • Für Übersetzen deterministisch: temperature=0.2, do_sample=false (bevorzugt deterministisch für Übersetzung). Für Chat: temperature=0.7, do_sample=true.

7) Inferenz-Wrapper (Worker)

  • Singleton-Worker mit asyncio.Queue und asyncio.Semaphore(1) pro Prozess.
  • Serielle Abarbeitung standardmäßig; optionales Micro-Batching: sammle bis N=4 Requests oder 50ms Timeout und rufe generate in einem Batch auf (nur wenn gleiche Parameter; sonst seriell).
  • Tokenzählung:
    • prompt_tokens = len(input_ids[0])
    • output_tokens = len(generated_ids[0]) - len(input_ids[0])
  • Rückgabe pro Request: Ergebnistext, Token-Usage, Latenz in ms.

8) Endpunkte (finale Spez)

  1. POST /translate

    • Body: { "text": "<string>", "source_lang": "auto|en|fr|de|ary|aeb|arq", "target_lang": "ary|en|fr|de" }
    • Validierung: text nicht leer → 400, Länge ≤ MAX_INPUT_CHARS → sonst 413.
    • Prompt-Bau: Chat-Template wenn verfügbar, sonst Fallback. Bei auto Source wird Prompt klar als „Translate from auto-detected ...“ formuliert.
    • Antwort: { "translation": "<string>", "prompt_tokens": <int>, "output_tokens": <int>, "latency_ms": <int> }.
  2. POST /chat (optional, minimal)

    • Body: { "messages": [{"role":"user|system|assistant", "content":"..." }], "max_new_tokens": 256 }
    • System-Prompt wird vorn angestellt (siehe oben) falls nicht vorhanden.
    • Antwort: { "response": "<string>", "usage": { "prompt_tokens": <int>, "output_tokens": <int> } }.
  3. GET /healthz{ "ok": true }

  4. GET /info{ "model": "<name>", "dtype": "fp16|int4", "device": "cuda:0" }

9) Fehlerbehandlung & Limits

  • 400: leerer Text, ungültige Sprache, ungültige Messages-Struktur
  • 413: Eingabe zu lang (MAX_INPUT_CHARS)
  • 500: CUDA-/Ladefehler, Generate-Fehler (gekürzte Message, keine Stacks im Body)
  • Timeout je Request: 60s (konfigurierbar)
  • Rate-Limit: asyncio.Semaphore(1) pro Prozess (Single-Worker)

10) CORS & Security

  • CORS mit konfigurierten Origins (CORS_ORIGINS).
  • Keine Auth; keine Secrets im Code. Option .env für Konfig.

11) Logging (INFO)

  • Pro Request: Methode, Pfad, Latenz ms, prompt/output Tokens, ggf. torch.cuda.memory_allocated() / memory_reserved().
  • Strukturierte, kurze Logs; keine Inhalte sensible Daten.

12) Requirements (Minimal)

  • fastapi>=0.110
  • uvicorn[standard]>=0.29
  • transformers>=4.42
  • torch>=2.3 (CUDA Build)
  • accelerate>=0.30
  • bitsandbytes>=0.43 (optional, nur wenn USE_INT4=true)
  • sentencepiece>=0.2 (für einige Tokenizer)
  • pydantic>=2.6
  • python-dotenv>=1.0
  • numpy>=1.26

13) Implementierungsskizze pro Datei

  • app/main.py

    • Erstellen der FastAPI-App, CORS, Startup-Event → load_model() aus model.py.
    • Include router.py.
    • Health/Info-Routen.
  • app/config.py

    • ENV-Lesen (os + dotenv), Defaults setzen, Validieren.
  • app/model.py

    • Globale Referenzen auf tokenizer, model, device, dtype.
    • load_model() basierend auf Konfig (FP16/INT4) + device_map="auto".
    • Worker mit asyncio.Queue; enqueue_request(); worker_loop().
    • generate(inputs, **gen_kwargs); translate(text, src, tgt); chat(messages, max_new_tokens).
  • app/router.py

    • POST /translate und /chat Handlers (Validierung via schemas.py), Timeout-Wrapping, Fehler-Mapping, Logging.
  • app/schemas.py

    • Pydantic-Modelle: TranslateRequest, TranslateResponse, ChatRequest, ChatResponse.
  • app/utils.py

    • Prompt-Helper: build_translate_messages(src, tgt, text); Fallback-Template.
    • Tokenzählung, Timing (monotonic_ns), CUDA-Memory-Stats.

14) README (Kurz)

  • Setup: Python, venv, pip install -r requirements.txt.
  • Start: uvicorn app.main:app --host 0.0.0.0 --port 8000.
  • ENV-Beispiele (.env).
  • Smoke-Tests:
    • curl -s http://localhost:8000/healthz
    • Translate EN→ARY Beispiel (20 Wörter).
    • Chat-Beispiel.
  • Optional: Hinweis zu Docker, aber kein Image bereitgestellt.

15) Performance-Ziele & Akzeptanzkriterien

  • Startup < 60s (Cold Load, Netz abhängig vom HF-Cache). Erster Warmstart nach Download.
  • /translate < 2s bei 20 Wörtern (Warm, FP16) auf RTX 5080 16GB.
  • Kein VRAM-Overflow bei Einzelrequest; Nutzung überwachen.
  • /info zeigt korrektes Modell & dtype.
  • Smoke-Tests laufen wie im README angegeben.

16) Risiken & Gegenmaßnahmen

  • Bitsandbytes unter Windows problematisch → Flag standardmäßig false; README verweist auf Linux-Empfehlung für INT4.
  • HF-Download langsam → README Hinweis, Modell vorab zu cachen (HF_HOME).
  • Chat-Template fehlt → robuster Fallback implementiert.

17) Umsetzungsschritte (ToDo)

  1. Grundgerüst manouapi/ anlegen (Struktur wie oben).
  2. config.py + .env-Parsing implementieren.
  3. model.py: Load (FP16/optional INT4), Worker, generate/translate/chat + Tokenzählung.
  4. schemas.py: Pydantic-Modelle.
  5. utils.py: Prompt-Bau, Timing, CUDA-Memory-Stats.
  6. router.py: Endpunkte und Fehler-/Timeout-Handling, Logging.
  7. main.py: App, CORS, Startup/Shutdown, Routen.
  8. requirements.txt und README.md (Smoke-Beispiele einfügen).
  9. Manuelle Smoke-Tests lokal.