Spaces:
Running on Zero
Download api/README.md from Alokik089/redaction_tool: direct link, hf CLI and curl.
- Browser
- Download file 4.94 kB
-
https://huggingface.co/spaces/Alokik089/redaction_tool/resolve/main/api/README.md
- Command line
-
hf download hf://spaces/Alokik089/redaction_tool/api/README.md
-
curl -L -o README.md https://huggingface.co/spaces/Alokik089/redaction_tool/resolve/main/api/README.md
A newer version of the Gradio SDK is available: 6.30.0
api/ -- FastAPI backend
Wraps redactor/ (the same engine scripts/redact.py uses) behind an
upload-a-file, poll-for-progress, download-the-result HTTP API, plus a
minimal static page to try it by hand.
Run it
pip install -r requirements.txt # includes fastapi/uvicorn
python -m spacy download en_core_web_trf # if you haven't already
uvicorn api.main:app --reload
Open http://127.0.0.1:8000 for the upload page, or http://127.0.0.1:8000/docs for interactive API docs (auto-generated by FastAPI).
Why this is a job queue, not a synchronous request
Redacting a large document can take real time (see EVALUATION.md's
runtime numbers with en_core_web_trf). Holding an HTTP request open for
minutes is fragile -- browsers, proxies, and most hosts time out
long-idle connections regardless of which model is running underneath.
So the API is async by design: submit a job, get an id back immediately,
poll a status endpoint for progress, fetch the result once it's done.
The upload page just wraps this in a progress bar via setInterval
polling every second (see static/index.html).
Endpoints
| Method | Path | What |
|---|---|---|
| POST | /api/jobs |
Upload a .docx (multipart form field file). Returns {job_id, status} immediately -- the actual redaction runs on a background thread. |
| GET | /api/jobs/{job_id} |
Poll status: {status, units_done, units_total, progress_percent, estimated_seconds_remaining, redactions_made, error}. status is one of queued/running/done/failed. |
| GET | /api/jobs/{job_id}/download |
The redacted .docx, once status == "done" (409 otherwise). |
| GET | /api/jobs/{job_id}/log |
The (category, original_value, fake_value) CSV audit trail for that job. |
| GET/POST/DELETE | /api/config/categories[/{name}] |
List / add a regex-based category / remove a category. See README.md's "Customizing" section. |
| GET/POST/DELETE | /api/config/structural-columns[/{index}] |
List / add / remove a personnel-table column rule. |
| GET/POST/DELETE | /api/config/exclude-list[/{term}] |
List / add / remove an ORGANIZATION deny-list entry. |
Every /api/config/* mutation calls RedactionEngine.reload_categories()
on the shared engine instance afterward, so it takes effect on the next
job immediately -- without restarting the server or reloading the
transformer model.
Module map
api/
main.py FastAPI app + lifespan (loads RedactionEngine ONCE at startup, not per-job)
settings.py env-tunable knobs: JOBS_DIR, MAX_UPLOAD_BYTES, JOB_RETENTION_HOURS
schemas.py Pydantic request/response models (the wire format)
job_store.py in-memory Job record + thread-safe JobStore (see its docstring for scaling notes)
redaction_worker.py runs one job to completion; the only file here that touches redactor/
config_editor.py reads/writes config/pii_categories.yaml + org_exclude_list.txt (ruamel.yaml, comment-preserving)
routers/jobs.py the job endpoints
routers/config.py the settings endpoints
static/index.html upload form + progress bar
static/settings.html Categories / Table layouts / Exclude list editor
static/style.css shared styling for both pages
static/app.js index.html's logic
static/settings.js settings.html's logic
Known limitations (demo-scoped, on purpose)
- In-memory job store. Job state lives in a Python dict in one
process. Restart the server and every job's status is gone (the files
on disk under
api_jobs/survive, but nothing tracks them anymore). Fine for trying this out or a single instance; swapjob_store.pyfor something Redis/Postgres-backed if you need multi-worker or restart-safe job tracking -- routers only talk toJOB_STORE, so that's the one place that would need to change. - No auth. Anyone who can reach the server can submit jobs and, if
they know or guess a
job_id(a UUID4, so not practically guessable, but not access-controlled either), download that job's result. - No cleanup job yet.
JOB_RETENTION_HOURSis defined insettings.pybut nothing currently sweeps oldapi_jobs/*directories -- add a scheduled task if this runs somewhere long-lived. - Config edits aren't file-locked. Two people hitting
/api/config/*at the exact same moment could race on the same YAML write. Fine for one person customizing settings through the UI; add a lock inconfig_editor.pyif this is ever multi-user.
Hosting
This needs real memory -- en_core_web_trf is a transformer model, not
the older en_core_web_lg. A 512MB free-tier box isn't enough (confirmed
by an actual OOM kill on Render's free tier, not just estimated). See
DEPLOYMENT.md at the repo root for concrete, cost-compared steps for
both Hugging Face Spaces and Render's paid tiers -- the Dockerfile here
works unmodified on either.