animap-gpu / models /README.md
bluman1's picture
Publish services/inference
4b98524 verified
|
Raw
History Blame Contribute Delete
8.5 kB

A newer version of the Gradio SDK is available: 6.26.0

Upgrade

Model artefacts

Three capabilities have a model behind them. cattle_detection and poultry_count run a checksummed YOLOX-m ONNX graph (ADR 0017, ADR 0018), and cattle_identity has a frozen DINOv2-small embedding backbone (ADR 0020) β€” a backbone, not an identity model, and the capability stays coming_soon because nothing has tested it on the same animal twice. The rest have nothing, and the service says so rather than inventing a result.

models/
  _artefacts/
    yolox_m-0.1.1rc0.onnx        the shipped detector, shared by two capabilities
    dinov3_vits16-onnx-1.onnx    frozen embeddings, locally exported
  cattle_detection/
    model_card.json
  poultry_count/
    model_card.json
  cattle_identity/
    model_card.json
  alternates/
    yolox_onnx/                  Apache-2.0, kept working, never registered
      model_card.json
      yolox_s-0.1.1rc0.onnx
    dinov2_embedding/            Apache-2.0 fallback; measurably worse, kept working
      model_card.json
      dinov2_small-onnx-1.onnx
    megadescriptor/              CC-BY-NC-4.0. Installed to be measured, never served
      model_card.json
      megadescriptor_l_384-onnx-1.onnx
    miewid/                      no licence at all. Same: measured, never served
      model_card.json
      miewid_msv3-onnx-1.onnx

Two of those alternates are weights Animap may not serve. They are on disk because a licence stopped being a reason to leave a model unmeasured β€” directive Β§40.2 asked for MegaDescriptor against DINOv3 and the honest answer needed the benchmark, not an argument about why it could not be run. Nothing about the terms changed: app/adapters/licences.py still reports both as non-servable, registry.refused() still names them, and they load only under ANIMAP_LICENCE_POLICY=record, which logs every load at ERROR and keeps it. Under the default enforce policy they are refused and their adapters report unavailable.

An alternate keeps its artefact inside its own directory. load_card refuses a path that climbs out of the card's grandparent, which is what stops a card being a way to load whatever is on the box.

Weights are not in git. Cards are β€” a governance record nobody can see in a diff is not one.

Installing them

.venv/bin/python scripts/install_models.py          # fetch what the cards name
.venv/bin/python scripts/install_models.py --check  # verify, never fetch

Some artefacts have no URL to fetch. An ONNX export of a published checkpoint is not a file the publisher hosts, so its card carries produced_by naming the script that makes it, and install_models.py refuses to download rather than writing a model-card web page into a .onnx file:

.venv/bin/pip install -r requirements-export.txt    # build-time only, never shipped
.venv/bin/python scripts/export_embedding.py

The export is checked against torch before it is written and is byte-reproducible, so the sha256 on a card pins the exact bytes that were validated. It is reproducible from the same exporter code, not from the weights alone β€” renaming a wrapper attribute changes the graph's node names and therefore the checksum, without changing a single weight.

The script reads the committed cards, downloads what each one names, and refuses anything whose sha256 does not match. It has no list of its own, so there is nothing to drift out of date. The service never calls it. A model that is not on disk leaves its capability unavailable.

Permissive licences only

A card naming AGPL-3.0 is refused at load, not warned about. discover() skips it and the capability stays unavailable.

And a card is not trusted about its own licence. app/adapters/licences.py holds what each runtime really loads under, read from a primary source and dated, and discover() refuses a card whose declared licence disagrees with it. That closes the exploit ADR 0017 records β€” "license": "Apache-2.0" written over a path to copyleft weights β€” without depending on somebody having hard-coded the right runtime name. It also catches the version of the trap that arrives from upstream: BVRA/MegaDescriptor-L-384/config.json declares "license": "mit" inside the checkpoint config while the repository is CC-BY-NC-4.0. Ultralytics YOLO was the shipped detector until ADR 0017 and is now not installed at all; read docs/adr/0017-ultralytics-licence.md before reaching for it again.

Four mechanisms hold that position, because it is the failure that is cheap now and expensive later: requirements.txt omits the package, .dockerignore excludes *.pt, the Dockerfile fails the build if one arrives anyway, and providers.discover() refuses the card. GET /health reports artefact_licenses, so a deployment that picked one up says so from outside.

Adding one

Create a directory named for the capability, containing a model_card.json. Every field below is required; a card missing any of them is refused at load, and the capability stays unavailable:

{
  "model_id": "animap-cattle-weight",
  "version": "0.1.0",
  "capability_key": "cattle_weight",
  "runtime": "ultralytics",
  "artefact": "../_artefacts/model.pt",
  "source": "https://…  where it came from, exactly",
  "license": "the licence, and it must permit this use",
  "sha256": "checksum of the artefact file",
  "validation_notes": "What was tested, on what data, with what result.",
  "geography_validated": ["NG"]
}

Why each field is enforced

source and license β€” a model of unknown origin cannot be shipped, and a licence that forbids commercial use is a legal problem discovered too late.

sha256 β€” verified on every load. The artefact on disk must be the one that was validated, not one swapped in afterwards.

validation_notes β€” must be non-empty, and whitespace does not count. This is the field that says somebody checked the model works. A card can otherwise be filled in purely to get past the loader.

geography_validated β€” a body-condition model trained on European dairy cattle is not validated for White Fulani in Kaduna. Recording where it was tested is what stops that being assumed. The sentinel global-coco means "validated nowhere, only pretrained", and Capability.may_be_enabled refuses to promote a capability past beta while it is present.

runtime β€” not required by governance, because governance is about provenance. But a card without it can be inspected and not run: detectors.build refuses rather than defaulting, since a default would let a typo change which licensed model produced a farmer's result.

Where the artefact may live

The artefact path may point sideways β€” several capabilities share one detector, and storing the file twice would mean two things to keep in step. It may not point out of the models tree; load_card refuses that. A card under alternates/ is two levels down and outside the discovery glob, so an alternate can never silently become the model that answered a request.

What is not allowed

  • No runtime downloads. The service never fetches a model. An artefact arrives through a reviewed change, or it does not arrive.
  • No binaries in git. .gitignore excludes *.tflite, *.onnx, *.pt and *.pth, and everything in this directory except the READMEs and the cards.
  • No placeholder adapters. A capability with a validated artefact but no implemented adapter returns 501, not a plausible-looking result. An adapter that returns something believable is precisely the failure ADR 0005 exists to prevent.

Promoting a capability

The path from nothing to trusted, per ADR 0006:

  1. Register the capability β€” already done for all 21.
  2. Install a validated artefact here.
  3. Implement its adapter and check the output against this contract.
  4. Move coming_soon β†’ beta. The app shows a Beta badge; results are stored with their confidence, and nothing acts on them automatically.
  5. Collect corrections. corrections.selected_interpretation_id captures when the model's second-ranked candidate was the right one, which is the highest value training signal the schema produces.
  6. Promote to enabled when the corrections say it earns it.

Step 6 is a decision about evidence, not a configuration change β€” and today it is also blocked mechanically, because both beta capabilities carry the global-coco sentinel.