6.7 KiB
API Reference
Toate endpoint-urile, parametrii, status codes, exemple curl.
Base URL
http://localhost:8080
Endpoints
POST /api/forensic-evidence
Singurul endpoint care contează în practică. Upload un video/imagine, primește înapoi evidence formatat pentru LLM.
Request
Content-Type: multipart/form-data
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
video |
file | DA | — | Fișier video sau imagine (mp4, mov, avi, mkv, webm, jpg, jpeg, png). Max 2 GB. |
modules |
string | NU | toate | CSV de module IDs ("m25,m27,m28"). Default rulează toate cele 5. |
encode_images |
string | NU | "1" | "1" → atașează data URLs base64; "0" → doar paths absolute pe disk |
every_n_frames |
int | NU | adaptiv | Pas extracție cadre. None → orchestrator alege bazat pe durata video |
async_mode |
string | NU | "0" | "1" → returnează 202 cu job_id; "0" → blochează până la rezultat |
Response
Sync mode (default) — 200 OK — JSON cu schema completă (vezi CONTRACT.md):
{
"video_path": "video.mp4",
"n_frames_extracted": 64,
"every_n_frames_used": 3,
"auto_skipped": ["m26 (no audio track)"],
"modules_run": ["m25", "m27", "m28", "m29"],
"execution_time_ms": 48432.0,
"fusion": {
"score": 0.67,
"label": "FAKE",
"confidence": 0.78,
"n_contributing": 4,
"n_no_signal": 0,
"disagreement": 0.005,
"contributions": {"m25": 0.09, "m27": 0.23, "m28": 0.24, "m29": 0.11}
},
"explanation": [
"Verdict forensic: FAKE (score=0.67, confidence=0.78, din 4 detectoare active)",
"Top contributors: m28 INCERT, m27 FAKE, m25 INCERT"
],
"modules": {
"m25": { ... schema completă CONTRACT.md ... },
"m27": { ... },
"m28": { ... },
"m29": { ... }
},
"evidence_text": "FORENSIC EVIDENCE (objective measurements...)\n\n[m25 ...] score=...\n...",
"images": [
{
"name": "m25_pulse_signal.png",
"tool_id": "m25",
"abs_path": "/app/data/results/{job_id}/images/m25_pulse_signal.png",
"size_bytes": 69384,
"data_url": "data:image/png;base64,iVBORw0KG..." // dacă encode_images=1
},
// ... 8-15 imagini total
],
"summary": {
"overall_score": 0.67,
"overall_label": "FAKE",
"overall_confidence": 0.78,
"n_modules_run": 4,
"n_modules_signal": 4,
"disagreement": 0.005
},
"instruction_for_llm": "HOW TO USE FORENSIC EVIDENCE ABOVE:\n...",
"errors": []
}
Async mode (async_mode=1) — 202 Accepted:
{
"job_id": "abc123def456789a",
"status": "queued",
"modules": ["m25", "m26", "m27", "m28", "m29"]
}
Apoi polling pe /api/status/{job_id} și preluare cu /api/result/{job_id}.
Error codes
| Code | Cauza |
|---|---|
| 400 | Câmp video lipsește, tip fișier nesuportat, sau modul necunoscut |
| 500 | Eroare în pipeline (vezi mesaj eroare în body) |
Examples
Sync, toate modulele:
curl -X POST http://localhost:8080/api/forensic-evidence \
-F "video=@suspicious_video.mp4"
Sync, doar 2 module + fără base64 (mai rapid):
curl -X POST http://localhost:8080/api/forensic-evidence \
-F "video=@image.jpg" \
-F "modules=m27,m28" \
-F "encode_images=0"
Async, polling:
# Submit
JOB=$(curl -s -X POST http://localhost:8080/api/forensic-evidence \
-F "video=@long_video.mp4" -F "async_mode=1" \
| python -c "import sys,json; print(json.load(sys.stdin)['job_id'])")
# Wait
while [ "$(curl -s http://localhost:8080/api/status/$JOB \
| python -c "import sys,json; print(json.load(sys.stdin)['status'])")" != "done" ]; do
sleep 5
done
# Get result
curl http://localhost:8080/api/result/$JOB | jq .summary
GET /api/forensic-modules
Listează modulele disponibile, pentru introspection.
curl http://localhost:8080/api/forensic-modules
{
"available_modules": [
{"id": "m25", "input_type": "overview_frames",
"import_path": "tools.m25_physiology.physiology", "function": "run"},
{"id": "m26", "input_type": "video_path",
"import_path": "tools.m26_audio.audio", "function": "run"},
{"id": "m27", "input_type": "overview_frames",
"import_path": "tools.m27_ai_detector.ai_detector", "function": "run"},
{"id": "m28", "input_type": "overview_frames",
"import_path": "tools.m28_forgery_heatmap.forgery_heatmap", "function": "run"},
{"id": "m29", "input_type": "overview_frames",
"import_path": "tools.m29_lighting.lighting", "function": "run"}
],
"default": ["m25", "m26", "m27", "m28", "m29"]
}
GET /api/status/{job_id}
Polling pentru cereri async. Returns 200 cu status string.
curl http://localhost:8080/api/status/abc123def456789a
{
"job_id": "abc123def456789a",
"status": "running",
"progress": "Running forensic modules m25-m29..."
}
Status values: queued, running, done, error.
Error code 404 dacă job_id nu există.
GET /api/result/{job_id}
Preluare rezultat job async. Comportament:
| Job status | Răspuns |
|---|---|
queued sau running |
202 cu status + progress |
done |
200 cu rezultatul complet (același ca sync) |
error |
500 cu mesajul de eroare |
curl http://localhost:8080/api/result/abc123def456789a
Error code 404 dacă job_id nu există.
GET /health
Healthcheck pentru Docker / load balancer / monitoring.
curl http://localhost:8080/health
{
"status": "ok",
"service": "forensic-features",
"modules": ["m25", "m26", "m27", "m28", "m29"]
}
Notes
Limita upload
client_max_size=2 * 1024**3 = 2 GB per request. Pe video >2GB,
preprocesezi cu ffmpeg înainte de upload.
Concurența
Job store este in-memory (Python dict). Asta înseamnă:
- Joburile se pierd la restart container
- Multi-replica fără sticky sessions = nu funcționează
Pentru producție serioasă, înlocuiește _jobs din api.py:30 cu un store
persistent (Redis recomandat).
CORS
Activat default cu * pentru orice origine. Pentru producție public,
restrictionează în api.py:build_app().
Rate limiting
NU există. Pentru producție public, pune un reverse proxy (nginx, Traefik) în față cu rate limiting.
Auth
NU există. Endpoint-urile sunt deschise. Pentru producție, integrează cu sistemul tău de auth via reverse proxy sau adaugă middleware.