Spaces:
Running
Alami Vision API — Referenz
Version 2.0.0 · FastAPI · ONNX Runtime (YOLOv8-seg)
Die API liefert Signale, niemals Geldbeträge. TrashCoin-Berechnung ist
server-authoritativ in der Alami-App (siehe AILLMHANDOFF.md §5 im Mobile-Repo).
Authentifizierung
Optional per API-Key. Wird über die Env-Variable ALAMI_API_KEYS
(kommasepariert) aktiviert; ohne diese Variable ist die API offen (Dev-Modus /
aktueller Mobile-Client). Header: x-api-key: <key>. /healthz ist immer offen.
Achtung Rollout-Reihenfolge: Die Mobile-App sendet heute noch keinen Key. Keys erst erzwingen, wenn der App-Client den Header mitschickt — sonst werden ehrliche Nutzer nicht bezahlt (
ai_status='unavailable'⇒ 0 TC).
Endpoints
GET /healthz
Liveness + Modell-Metadaten.
{ "ok": true, "version": "2.0.0", "imgsz": 640,
"names": ["plastic","paper","glass","metal","organic","ewaste","other"],
"conf_thr": 0.35, "supabase": false, "auth": "open", "timestamp": "…" }
POST /predict — Legacy-Contract (Mobile-App, stabil)
Request:
{ "image_url": "https://…", "user_id": "uuid|null", "model_hint": "alami-mobile:v1" }
Response:
{
"model_version": "v20251018-220439",
"inference_ms": 77,
"prediction_id": "uuid",
"predictions": [
{ "xyxy": [x1,y1,x2,y2], "cls": 0, "conf": 0.86,
"label": "plastic", "raw_label": "plastic" }
]
}
label= Material-Bucket (plastic|paper|glass|metal|organic|ewaste|other) — darauf keyed der Economy-Gate der App.raw_label= originale Modellklasse (Nachvollziehbarkeit; bei zukünftigen feingranularen Modellen, z. B. TACO-60 oder Marken-SKUs, weicht sie ab).- Koordinaten in Originalbild-Pixeln.
POST /v1/analyze — Standalone Vision API (neu)
Superset von /predict: Materialzusammenfassung, Stückzahl,
Gewichtsschätzung pro Objekt.
Request:
{ "image_url": "https://…", "user_id": "uuid|null", "domain": "trash" }
Response:
{
"prediction_id": "uuid",
"model_version": "v20251018-220439",
"domain": "trash",
"inference_ms": 77,
"image": { "width": 800, "height": 600 },
"objects": [
{ "label": "metal", "raw_label": "metal", "confidence": 0.91,
"bbox_xyxy": [12.0, 40.5, 180.2, 300.0], "area_fraction": 0.105,
"weight_estimate_g": 15.0, "weight_source": "material_prior_v0" }
],
"summary": {
"item_count": 1, "trash_detected": true,
"materials": { "metal": 1 },
"total_weight_estimate_g": 15.0,
"weight_source": "material_prior_v0"
}
}
log (default true): Auf false setzen für Live-Kamera-Preview-Frames
— das Frame wird analysiert, aber nicht persistiert (kein
trash_predictions/JSONL-Eintrag). So flutet der ~1/s-Preview-Strom die
Trainingsdaten nicht; nur das final gesnappte Foto wird mit log=true geloggt.
source (optional): Kontext-Tag, z. B. "product-scan". Geloggte Zeilen
mit Nicht-Litter-Source hält der Flywheel-Sync in einem separaten Pool
(predictions_other_sources.jsonl) — Regal-/Produktfotos mischen nie ins
Litter-Training, bleiben aber für die spätere Marken-Erkennung erhalten.
Litter-Pool = source fehlt oder "alami-mobile".
domain ist heute nur "trash"; das Feld reserviert die API-Fläche für
weitere visuelle Domänen (siehe Roadmap), ohne dass Clients später migrieren
müssen.
Gewicht: material_prior_v0 ist eine ehrliche Heuristik (Median-Stückgewicht
pro Material, überschreibbar via deploy/<bundle>/weight_priors.json). Sobald
der gelernte Weight-Head live ist, steht in weight_source die Modellversion.
Konsumenten MÜSSEN weight_source auswerten, bevor sie Gewichte anzeigen oder
verrechnen.
Jedes Objekt trägt zusätzlich Felder für Kamera-Overlays (siehe
docs/MOBILE_AGENT_HANDOFF.md §4b):
bbox_norm:[x, y, w, h]normiert in 0..1 — Box ohne Kenntnis der Bildgröße zeichnenlabel_de: deutsches Anzeige-Label (z. B. „Glas")color: stabile Overlay-Farbe (Hex) für das Material
POST /v1/analyze/upload
Wie /v1/analyze, aber Multipart-Upload statt URL (für API-Kunden ohne
öffentliche Bild-URLs). Felder: file (Bild, max. 10 MB), user_id?, domain?.
GET /v1/product/{barcode}
Produkt-Nachhaltigkeits-Lookup (Open Food/Products/Beauty Facts): Eco-Score,
Verpackung → Material-Bucket + deutscher Entsorgungs-Guide, Litter-Gewicht.
Immer HTTP 200 mit status: found | not_found | unavailable (400 nur bei
ungültigem Barcode). 24-h-Cache serverseitig. Getrennt vom Modell-Pfad —
kein Einfluss auf /predict. Client MUSS attribution anzeigen (ODbL).
Details + UI-Anleitung: MOBILE_AGENT_HANDOFF.md §4d.
GET /v1/materials
Statische Anzeige-Metadaten aller 7 Materialien (bucket, label_de, color).
Für konsistente Kamera-Overlays — App holt einmal und cacht.
{ "materials": [
{"bucket":"plastic","label_de":"Plastik","color":"#2563eb"},
{"bucket":"glass","label_de":"Glas","color":"#059669"}, ... ] }
POST /feedback — Human Reinforcement
Fire-and-forget-Korrekturen; Trainingsrohstoff für das Flywheel
(ml/scripts/ingest_feedback.py).
{
"prediction_id": "uuid",
"corrected_type": "plastic",
"corrected_weight_kg": 0.5,
"notes": "war eine PET-Flasche",
"source": "alami-mobile",
"corrected_items": [
{ "index": 0, "action": "reject" },
{ "index": 1, "action": "relabel", "corrected_label": "metal", "corrected_weight_g": 15.0 }
],
"added_items": [
{ "label": "glass", "point": { "x": 0.42, "y": 0.61 } },
{ "label": "plastic", "box": { "x": 0.1, "y": 0.1, "w": 0.2, "h": 0.3 }, "count": 2 }
],
"reasons": ["too_dark", "occluded"]
}
Alle Felder außer prediction_id optional; corrected_weight_kg ∈ [0.001, 20].
corrected_items referenziert per index die Objekte der zugehörigen
Prediction (objektgenaue Labels für zukünftiges Training).
Feedback v2 (Mobile-Kontrakt #141) — additiv, spiegelt den Live-Client:
corrected_items[].action=confirm|relabel|reject— macht Bestätigungen und False Positives zu expliziten Signalen (fehlt → Alt-Verhalten „relabel").added_items— Objekte, die die KI übersah (Recall-Signal).point/boxnormalisiert 0..1 zum hochgeladenen Bild; ein verorteter Treffer ist ein trainierbares Detektor-Label, ein bloßescountnur eine Zählung.reasons— maschinenlesbare Fehler-Chips:too_dark·blurry·occluded·unknown_object·not_trash·unsure_skip(offene Liste, vorwärtskompatibel).
Persistenz nach Supabase trash_predictions (added_items, feedback_reasons;
action reist im corrected_items-JSONB). Fehlt eine Spalte, wird nur diese
weggelassen — Migration siehe docs/FLYWHEEL.md §3.
Environment-Variablen
| Variable | Default | Bedeutung |
|---|---|---|
ALAMI_AI_BUNDLE |
deploy/latest |
Modell-Bundle (model.onnx, names.json, postprocess_config.json) |
ALAMI_API_KEYS |
(leer = offen) | Kommaseparierte API-Keys |
ALAMI_FEEDBACK_DIR |
feedback_logs |
JSONL-Logs (predictions/feedback) |
ALAMI_CORS_ORIGINS |
* |
Kommaseparierte CORS-Origins |
ALAMI_DEBUG |
(aus) | 1 ⇒ Debug-Logs und Debug-Endpoints |
ALAMI_INSECURE_FETCH |
(aus) | 1 ⇒ TLS-Verify beim Bild-Fetch aus (nur lokal!) |
SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY |
(leer) | Optionales Prediction-Logging nach Supabase (trash_predictions) |
Fehlerverhalten
- Nicht erreichbare/zu große Bilder ⇒ 400/408, kein Absturz.
- Der Mobile-Client behandelt jeden Fehler als
unavailableund lässt die Submission mitneeds_reviewdurch — die API darf den Submit-Pfad niemals blockieren (Hard Contract §5.2).
Tests
pip install -r requirements.txt pytest httpx
pytest tests/ -v