Spaces:
Running on Zero
Running on Zero
| title: Animap Inference | |
| emoji: π | |
| colorFrom: green | |
| colorTo: gray | |
| sdk: gradio | |
| sdk_version: 5.49.1 | |
| app_file: space_app.py | |
| python_version: "3.12.12" | |
| pinned: false | |
| license: apache-2.0 | |
| short_description: Livestock models that refuse to invent a result. | |
| # Animap inference | |
| This runs validated livestock models against farm photographs and returns what | |
| the model actually produced. Its most important property is what it **refuses**: | |
| a capability with no checksummed artefact behind it answers `unavailable`, not a | |
| placeholder and not a plausible-looking number. | |
| Twenty-eight capabilities are registered. **Two run.** `cattle_detection` and | |
| `poultry_count` execute a YOLOX-m ONNX artefact whose sha256 is verified against | |
| its model card at start-up. The other twenty-six say so plainly, which is the | |
| honest answer rather than a gap. | |
| The source is `services/inference` in the Animap repository. `app.py` mounts | |
| `app.main:app` β the same FastAPI service the Azure Container App runs β under a | |
| Gradio page, so `/health`, `/capabilities` and `/jobs` behave exactly as they do | |
| in production and the page is a demonstration of them. | |
| **It was a Docker Space until 2026-08-24 and is a Gradio one now**, for one | |
| reason: ZeroGPU is Gradio-SDK only. The Docker image ran the identical Azure | |
| bytes, which was the better provenance story, and it could not be given a GPU at | |
| any price β which left CountGD, the one capability a GPU actually unblocks, | |
| unmeasurable. See **What this Space is not** for what the change cost. | |
| ## What is open and what is not | |
| | Endpoint | Auth | Why | | |
| |---|---|---| | |
| | `GET /animap/health` | none | Carries no farm data, and a platform probe has to reach it | | |
| | `GET /animap/capabilities` | none | The published contract: what may be claimed, and what may not | | |
| | `POST /animap/jobs` | **bearer token** | Runs a model against a farm's photographs | | |
| | `GET /animap/jobs/{id}` | **bearer token** | Returns a farm's result | | |
| `space/publish.py --set-secret` mints `ANIMAP_INFERENCE_TOKEN` and sets it as a | |
| Space secret. **Check it rather than assuming it**: `GET /health` reports | |
| `"authenticated": false` when no token is configured, so a deployment that | |
| reached the internet without one says so to anyone who asks. | |
| curl -s https://bluman1-animap-inference.hf.space/animap/health | |
| ## Running a model | |
| Two public-domain captures are baked in, so a real detection can be obtained | |
| without an Azure account. `space/FIXTURES.md` in the repository lists their ids, | |
| their sources and the human count on record for each. | |
| ```bash | |
| curl -s -X POST https://bluman1-animap-inference.hf.space/animap/jobs \ | |
| -H "authorization: Bearer $ANIMAP_INFERENCE_TOKEN" \ | |
| -H 'content-type: application/json' \ | |
| -d '{"capability_key":"cattle_detection", | |
| "subject_type":"herd", | |
| "subject_id":"00000000-0000-0000-0000-000000000001", | |
| "farm_id":"00000000-0000-0000-0000-000000000002", | |
| "media_ids":["aa5e8481-8be6-509d-b1fa-f1a178c7cda0"], | |
| "captured_at":"2026-08-22T10:00:00Z"}' | |
| ``` | |
| A frame that settles at the first grid answers in well under a second. A dense | |
| one runs all three grids and takes a few seconds; there is no queue, because no | |
| capability yet takes tens of seconds. | |
| Read `warnings` before you read the number. A count is of the animals **visible | |
| in one frame** β never the herd size, never a flock population, and never a | |
| house reconciliation. When the count keeps rising as the frame is read more | |
| finely, or exceeds twenty, the service publishes `count_withheld` and no number | |
| at all. That refusal is a feature and it is measured: see the `known_limits` and | |
| `validation_notes` on each model card. | |
| ## What the SDK change cost, and what it did not | |
| **Lost: the image is no longer byte-identical to Azure's.** A Gradio Space has | |
| no Dockerfile, so the claim *"this Space builds from the same Dockerfile"* is | |
| gone and cannot be got back while ZeroGPU is Gradio-only. What runs is the same | |
| `app/` tree with the same `requirements.txt`, which is close and is not the same | |
| thing, and this file says so rather than letting the old sentence stand. | |
| **Lost: a build-time licence gate.** The Docker build failed if an AGPL runtime | |
| arrived. There is no build to fail now. | |
| **Kept: every gate that actually protects a result.** `app.py` runs | |
| `scripts/install_models.py --check` before it imports the service, so an | |
| artefact that disagrees with its model card stops the Space at start-up rather | |
| than being found by a farm's job. `providers.discover()` still refuses to serve | |
| a capability whose artefact fingerprints as a copyleft runtime, and `/health` | |
| still publishes `artefact_licenses` so a deployment in breach is visible from | |
| outside. | |
| **Gained: the ability to be given a GPU.** Nothing here reaches for CUDA yet β | |
| YOLOX-m and DINOv3 are both ONNX on CPU β so this buys no speed-up today. It is | |
| the prerequisite for CountGD, which gets MAE 14.84 on broiler houses against the | |
| deployed detector's 156.80 and has never been runnable anywhere in this project. | |
| ## What this Space is not | |
| **It is not the production media path, and it must not be read as evidence for | |
| one.** This was true of the Docker Space and the SDK change did nothing to it. Production reads captures out of an Azure Blob container using the | |
| Container App's managed identity β no key, no SAS, nothing stored. A Space is | |
| not inside Azure and has no managed identity, so that credential is unavailable | |
| to it. The alternatives a Space *could* use are a storage account key or a SAS | |
| token in a secret, and neither is the production posture: one hands a public | |
| Space full access to every farm's evidence, and the other expires. | |
| So this Space serves `ANIMAP_MEDIA_PROVIDER=local` against the two baked-in | |
| frames. Everything downstream of the pixels β the quality gate, the detection | |
| pyramid, the counting guard, the observation vocabulary β is the production code | |
| path exactly. Everything upstream of them is not. | |
| **No farm data reaches this Space.** It cannot read `animapmedia`, and the only | |
| captures it holds are two public-domain photographs from Wikimedia Commons. | |
| ## Weights | |
| YOLOX-m, Apache-2.0, from the Megvii `0.1.1rc0` release, **vendored into this | |
| repository under Git LFS rather than fetched at build time**. The service | |
| verifies its sha256 against `models/cattle_detection/model_card.json` at | |
| start-up and refuses to load an artefact that does not match. Vendoring is what | |
| lets the build step stay `install_models.py --check` β verify, never fetch β | |
| which is the posture ADR 0005 asks for and the same command the Azure build | |
| runs. | |
| `models/cattle_identity` is deliberately absent. Its artefact is DINOv3 under a | |
| bespoke Meta licence whose two published texts disagree about an attribution | |
| obligation, and publishing a copy into a public Space is redistribution. That | |
| capability answers `unavailable` here, and correctly. | |
| No AGPL-3.0 software is installed and none may be. `requirements.txt` omits | |
| `ultralytics`, `.dockerignore` excludes `*.pt`, the build fails if one arrives | |
| anyway, and `providers.discover()` refuses to serve a capability whose artefact | |
| fingerprints as a copyleft runtime. `GET /health` publishes | |
| `artefact_licenses`, so a deployment in breach is visible from outside. | |
| ## Attribution | |
| Third-party notices travel with the image in `THIRD_PARTY_NOTICES.md`. The two | |
| demonstration captures are CC0 and public domain; their sources are in | |
| `space/FIXTURES.md`. | |