Conventions
- All commands are
uv run <script> …from the repo root. --helpworks on every command and subcommand.- Importing a CLI never pulls heavy deps eagerly —
tensor-factory --helpworks without the inference extra; subcommands lazy-import what they need.
| Script | Package | Purpose |
|---|---|---|
tensor-factory | tensor-factory | inference: detect, bench |
tensor-factory-synth | tensor-factory-synth | generate, label, triage |
tensor-factory-train | tensor-factory-train | train + export int8 ONNX |
tensor-factory-label | tensor-factory-label | Label Studio push/pull |
tensor-factory-mcp | tensor-factory-mcp | MCP server (stdio) |
tensor-factory-http | tensor-factory-mcp | HTTP detection endpoint |
tensor-factory
The inference harness. Two subcommands.
detect
Detect the target object in one image; print the box in normalized and 8-bit form.
| Flag | Default | Meaning |
|---|---|---|
--model | — (required) | path to the ONNX model |
--image | — (required) | path to the input image |
--size | 480 | model input size (px) |
bench
Measure CPU throughput (fps).
| Flag | Default | Meaning |
|---|---|---|
--model | — (required) | path to the ONNX model |
--image | gray frame | image to loop on (default: synthetic gray) |
--n | 100 | iterations |
--size | 480 | model input size (px) |
tensor-factory-synth
Generation, auto-labeling, and triage. Global flags precede the subcommand.
| Global flag | Default | Meaning |
|---|---|---|
--backend | mock | generation backend: mock (anywhere) or gemini (Nano Banana) |
--size | 480 | image size (px) |
--seed | 0 | starting seed |
sample
Render a contact sheet for one prompt — eyeball aesthetics before a full run.
| Flag | Default | Meaning |
|---|---|---|
--prompt | — (required) | generation prompt |
--n | 9 | number of samples |
--cols | 3 | grid columns |
--out | samples.png | output PNG |
dataset
Generate n images, optionally auto-label (gemini backend), and write a COCO dataset + Label Studio pre-annotations.
| Flag | Default | Meaning |
|---|---|---|
--prompt | — (required) | generation prompt |
--features | — (required) | one or more feature phrases to detect |
--n | 64 | number of images |
--out | dataset | output directory |
--no-label | off | skip auto-labeling (gemini only) |
--skip-errors | off | log and skip per-image failures instead of aborting |
Output: images/img_SEED.png, annotations.coco.json, label_studio.json.
triage
Summarize a dataset's review state (approved / pending / rejected, plus trainable count).
Batch scripts
Helicoils-example scripts under packages/tensor-factory-synth/scripts/, run with uv run --with google-genai python … (all read GEMINI_API_KEY / GOOGLE_API_KEY):
| Script | Key args (defaults) | Produces |
|---|---|---|
gen_samples.py | --n 25 --out images --seed 20260620 --reference … --workers 5 | QC samples + manifest.json |
build_ds.py | positional [out] [n=170] [reference] | generate → GroundingDINO label → COCO |
gen_negatives.py | --n 110 --out …/negatives_pool --seed 200000 --workers 6 | negatives + manifest.json |
tensor-factory-train
One subcommand: fit — train on a COCO dataset and export an int8 ONNX model.
| Flag | Default | Meaning |
|---|---|---|
--data | — (required) | dataset dir (annotations.coco.json + images) |
--out | model.onnx | output ONNX path |
--epochs | 10 | training epochs |
--batch | 16 | batch size |
--lr | 1e-3 | learning rate |
--size | 480 | model input size (px) |
--width | 16 | model channel width |
--device | auto | cuda/mps/cpu (default resolves cuda → mps → cpu) |
--presence | off | add a YOLO-style objectness head (auto-on when --negatives is given) |
--val-frac | 0.0 | held-out fraction for metrics + best-checkpoint export |
--box-weight | 1.0 | weight on the box loss |
--presence-weight | 1.0 | weight on the objectness loss |
--augment | off | random flip augmentation |
--negatives | none | one or more dirs of no-object images that train the objectness head toward absent (turns the presence head on; box loss masked) |
--allow-unreviewed | off | train on un-validated labels too (default: only human-approved) |
--freeze-gain | off | freeze the soft-argmax gain at 1 (ablation; default lets it learn) |
Needs the train extra (torch). See Internals › the model for what --width and --freeze-gain change.
tensor-factory-label
Label Studio integration. Global flags select the server.
| Global flag | Default | Meaning |
|---|---|---|
--url | $LABEL_STUDIO_URL → http://localhost:8080 | Label Studio base URL |
--token | $LABEL_STUDIO_API_KEY | API token (required) |
config
Print the RectangleLabels config XML for a project.
push
Create a project and import the COCO dataset, with the candidate boxes as predictions.
| Flag | Default | Meaning |
|---|---|---|
--data | — (required) | dataset dir (annotations.coco.json + images/) |
--title | tensor_factory | project title |
--image-base | local storage | HTTP base serving the images; omit to use Label Studio local storage |
Prints the project id and labeling URL.
pull
Export corrected annotations back to COCO, stamped review=approved, source=human (now trainable).
Tasks with no rectangle (or cancelled) annotations are skipped.
tensor-factory-mcp
FastMCP server over stdio. Defaults to the bundled helicoil-presence-cam-v1.onnx.
Three read-only tools, each taking an optional model_path and input_size (16–4096, default 480):
| Tool | Returns |
|---|---|
tensor_factory_detect | box (norm/pixel/uint8), image size, model — plus present/score for a presence-head model (box fields are null when absent) |
tensor_factory_model_info | resolved model, input size, input name, ORT providers |
tensor_factory_benchmark | fps over n iterations (1–10000, default 100) |
On failure each tool returns Error: <type>: <message> rather than raising.
tensor-factory-http
Stdlib http.server endpoint over the same core inference. Binds loopback by default.
| Flag | Default | Meaning |
|---|---|---|
--host | 127.0.0.1 | bind host (use 0.0.0.0 to expose deliberately) |
--port | 8088 | bind port (0 = ephemeral) |
--model | bundled | ONNX model path |
--input-size | 480 | square model input (px) |
--max-mb | 25 | reject request bodies larger than this |
| Route | Response |
|---|---|
GET /health | {"status":"ok"} |
GET /model_info | model path, input size, IO name, ORT providers |
POST /detect | body = raw image bytes → the same JSON as the MCP detect tool |
Bad image bytes → 400; missing Content-Length → 411; empty body → 400; over --max-mb → 413; unknown route → 404.
./Quickstart
The repo's top-level entry point.
| Invocation | Does |
|---|---|
./Quickstart | bootstrap (uv sync --locked --all-packages) then run the MCP server (stdio, bundled model) |
./Quickstart -c | full local gate: sync → ruff check --fix → ruff format → ty check → pytest -m unit |
./Quickstart -u | unit tests only |
./Quickstart -p | full gate, then uv build + uv publish (needs UV_PUBLISH_TOKEN) |
Install extras
The default uv sync stays light. Heavy steps live behind extras you pull only when needed:
| Extra | Package | Pulls | For |
|---|---|---|---|
infer | tensor-factory | onnxruntime, numpy, pillow | CPU inference |
gemini | tensor-factory-synth | google-genai | Nano Banana generation (needs GEMINI_API_KEY) |
gpu | tensor-factory-synth | torch, transformers, accelerate | GroundingDINO auto-labeling |
train | tensor-factory-train | torch | training |
Pull an extra with e.g. uv sync --extra gemini, or run a one-off with uv run --with google-genai ….
Environment variables
| Variable | Used by |
|---|---|
GEMINI_API_KEY / GOOGLE_API_KEY | Nano Banana generation |
LABEL_STUDIO_URL / LABEL_STUDIO_API_KEY | Label Studio push/pull |
UV_PUBLISH_TOKEN | ./Quickstart -p publish |
PYTORCH_ENABLE_MPS_FALLBACK | set automatically by the auto-labeler for MPS |