Skip to content

Models and predictions

This page covers the model registry and how to write predictions back as annotations.

When creating a registry row, model_type must be one of:

  • CUSTOM
  • OBJECT_DETECTION
  • CLASSIFICATION
  • INSTANCE_SEGMENTATION
  • ACTIVITY_DETECTION

New models are typically stored with is_active = false.

  • You can upload weights and inference packages while inactive.
  • predictions.create_batch requires an active model (otherwise the API returns 400).

Activate with handle.activate() or pass is_active=True on create when appropriate.

row = project.models.add_pretrained_yolo(
weights_filename="yolov8n.pt",
name="yolov8n_coco",
version="1.0.0",
metadata={
"label_ids": [
"11111111-1111-1111-1111-111111111111",
"22222222-2222-2222-2222-222222222222",
],
"yolo_conf": 0.25,
"yolo_iou": 0.45,
},
activate=True,
)
print(row["id"])

What this does: asks the server to attach Ultralytics weights and prepare deployment metadata for YOLO-style detection.

Why label_ids matter: they map model class outputs to your project’s schema UUIDs.

Replace the example UUIDs with real IDs from project.schema.get()["schemas"].

Option B — Custom model + inference package

Section titled “Option B — Custom model + inference package”
from pathlib import Path
row = project.models.create(
name="my_ensemble",
version="1.0.0",
model_type="CUSTOM",
framework="pytorch",
metadata={"label_ids": ["…uuid…"]},
is_active=False,
)
model_id = row["id"]
handle = project.models.model(model_id)
handle.upload(
model_path=Path("./weights/main.pt"),
config_path=Path("./config.json"), # optional
)
handle.inference_package.upload_package_dir(Path("./my_inference_package"))
handle.activate()

What this does:

  1. Creates a registry row.
  2. Uploads weight artifacts via presigned upload + confirm.
  3. Uploads an inference package directory (must include your handler code, typically inference.py).
  4. Activates the model so predictions are allowed.

Diagram: Custom model path — create → upload weights → upload inference package → activate

Custom handlers implement LabeltyInferenceHandler (load / predict). See the SDK’s ML_CUSTOM_MODEL guide in the repository for package layout details.

rows = project.models.list(limit=50, offset=0)
detail = project.models.get(model_id)
project.models.download(model_id, Path("./downloaded.pt"), artifact="model")
publish_row = project.models.publish(
model_id=model_id,
title="Dental Ensemble v1",
summary="Reusable model for dental detection",
tags=["dental", "xray", "object-detection"],
)
# Attach an org-store model into a project
added = client.org_models.add_to_project(
org_model_id=publish_row["public_model_id"], # field names may vary — inspect the response
project_id="target-project-uuid",
)

Coordinates are normalized between 0 and 1. Each item creates one annotation. Asset status / assignee are not modified.

result = project.predictions.create_batch(
model_id=model_id,
predictions=[
{
"asset_id": "uuid-of-asset",
"label_id": "uuid-of-label-schema",
"points": [[0.1, 0.1], [0.5, 0.1], [0.5, 0.4], [0.1, 0.4]],
"metadata": {"pipeline": "v1"},
},
],
)
# response typically includes model_id, annotation_ids, asset_ids

Why normalized points: they stay correct if images are displayed at different pixel sizes in the labeling UI.

  • Activate models only when ready for consumers.
  • Keep label_ids in metadata aligned with the current schema.
  • Prefer small, idempotent scripts for CI prediction ingest.
  • For production HTTP inference to external systems, prefer inference workflows rather than ad-hoc prediction scripts.