Generated banner for Tao Run Deft Od Aoi
Generated logo for Tao Run Deft Od Aoi

Tao Run Deft Od Aoi

NVIDIAOfficialCreated by NVIDIA Corporation

  • Skill
  • Workflow
  • Software development
  • Apache-2.0
  • Medium risk
  • Has scripts

From the author

Run a binary industrial-inspection DEFT loop with TAO RT-DETR: measure on frozen KPI/test roles, mine gap-similar real and clean images with SigLIP, accumulate admitted COCO data, retrain from one base checkpoint, and select by KPI AP50. Use for iterative AOI defect detection, not generic multiclass OD.
— the description by NVIDIA Corporation

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured, disk-backed loop for training and refining a binary object detection model specifically for industrial inspection (AOI). It automates the process of measuring model performance on frozen KPI datasets, identifying gaps (false positives and false negatives) using SigLIP-based image mining, and iteratively retraining the model with newly admitted data. It supports optional synthetic data generation via AnomalyGenNext to address specific defect types, ensuring a controlled, data-driven improvement cycle for defect detection.

What it does

  • Performs iterative training loops for binary defect detection using TAO RT-DETR.
  • Mines real and clean images to address model gaps identified by loose and strict confidence thresholds.
  • Manages cumulative COCO dataset assembly with deduplication and admission gates.
  • Selects the best model checkpoint based on KPI AP50 metrics.
  • Integrates optional synthetic defect generation for specific, hard-to-detect defect types.
  • Validates data contracts and role-specific requirements for KPI, test, real, and clean datasets.

When to use it

  • When you need to improve a binary defect detection model for industrial inspection.
  • When you have a set of KPI and test images and want to iteratively mine for more training data.
  • When you need to integrate synthetic data generation to supplement real-world defect samples.
  • When you require a rigorous, reproducible workflow for model training and checkpoint selection.

Advantages

  • Automates the complex loop of gap analysis, data mining, and retraining.
  • Ensures model performance is tracked against frozen KPI and test roles.
  • Provides flexible data admission strategies including round-robin and max-similarity.
  • Supports both real-data mining and synthetic data generation for robust training.

Usage examples

“Initialize a new DEFT AOI workflow with my policy file.”
The skill validates the policy and initializes the project structure, ensuring all required roles (KPI, test, real, clean) meet the data contract.
“Run the next iteration of the DEFT loop.”
The agent performs gap analysis, mines new images based on the current model's performance, admits them to the training set, and triggers a new training run.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/nvidia/tao-run-deft-od-aoi/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip tao-run-deft-od-aoi.zip -d .agents/skills

Documentation by NVIDIA Corporation

TAO DEFT OD AOI

Standalone install? If this session was not initialized by the TAO skill bank plugin, run the tao-setup skill first (host preflight, credentials, cross-skill discovery).

This application is a disk-backed RT-DETR loop for one foreground class, defect. Its core is real-data-only; AnomalyGenNext synthesis is an optional route with separate preparation, generation, and admission gates.

References

Read only the references needed for the current stage:

  • intake and launch: references/defaults.md, references/data-contract.md, references/source-manifest.md, and references/preflight.md;

  • orchestration: references/pipeline.md and references/scripts-and-agents.md;

  • gaps and retrieval: references/gap-routing.md and references/tao-analyze-gaps-od-map.md;

  • training and selection: references/training-policy.md;

  • optional synthesis: references/anomalygen-pool.md.

Start

Select an installed platform, read its skill, then invoke tao-launch-workflow. The single launch review must include the four normalized COCO roles, trainable RT-DETR base checkpoint, maximum iterations, image and Data Services containers, GPU shape, and expected runtime. After approval, prepare source COCO files when needed:

scripts/prepare_deft_od_aoi_sources.py \
  --manifest /data/dataset_sources.json --check-only

scripts/prepare_deft_od_aoi_sources.py \
  --manifest /data/dataset_sources.json \
  --output-dir /new/results/normalized

--check-only runs without a container. Materialization must run in the pinned TAO Data Services image because it delegates each role's canonical COCO merge to the existing annotations merge action. The application then validates the merged role contracts before emitting sources.json. Include --gpus when launching the materialization container, as documented in references/source-manifest.md.

The user-facing manifest calls the held-out input benchmark. The second command maps it to the existing internal kpi role and emits sources.json with the canonical kpi, test, real, and clean mapping. Copy assets/default_policy.yaml, use that mapping for sources, fill the other required values, and initialize once:

Source COCOs must already satisfy the canonical KPI metadata contract in references/data-contract.md. The generic source preparer preserves those fields; it does not infer dataset-specific identity or mask paths.

scripts/init_deft_od_aoi.py \
  --config /workspace/deft_policy.yaml \
  --output-dir /new/results/deft_contract

Never reinitialize an existing result. The validator requires disjoint KPI, test, defective-real, and verified-clean roles; every COCO must declare only defect. KPI and test may mix boxed and boxless images because they never enter training. The defective-real role must be nonempty and needs at least one box on every image. Clean images remain explicit zero-annotation COCO entries; an empty clean role is accepted as an unavailable retrieval capability with a startup warning.

Loop boundary

The loop composes existing bank actions:

  1. Baseline gap initialization in explicit cold_start or checkpoint mode; later measurements use tao-train-rtdetr inference on KPI and test.

  2. Two tao-analyze-gaps-od-map actions: loose confidence for FP routing and strict confidence for FN routing.

  3. tao-generate-image-embeddings with one frozen SigLIP encoder, followed by tao-mine-od-images unique-neighbor matching against the real or clean role.

  4. Application-owned admission and cumulative binary COCO assembly.

  5. Direct tao-train-rtdetr training from the same frozen base checkpoint, then KPI-only checkpoint selection. Test remains report-only.

All specs are nested YAML dictionaries. Every GPU/Data Services action uses the selected platform's submit/status/logs/cancel contract and a job record. Stop on missing or invalid artifacts, role overlap, class drift, or failed training. Never infer live state from the JSON record alone.

Retrieval preparation

Build the reusable candidate cache once, then prepare queries after each pair of loose/strict gap jobs:

scripts/prepare_deft_od_aoi_retrieval.py candidates \
  --policy "$RESULTS/deft_od_aoi_policy.yaml" \
  --output-dir "$RESULTS/candidates"

scripts/prepare_deft_od_aoi_retrieval.py queries \
  --policy "$RESULTS/deft_od_aoi_policy.yaml" \
  --strict-gaps "$ITER/strict/box_gaps.parquet" \
  --loose-gaps "$ITER/loose/box_gaps.parquet" \
  --previous-coco "$PREVIOUS/train.json" \
  --iteration 1 --candidate-root "$RESULTS/candidates" \
  --output-dir "$ITER/retrieval"

The candidate manifest records a zero count and emits no candidate parquet or embedding spec for an empty clean source role.

Run every emitted embedding spec through tao-generate-image-embeddings. For max_similarity, run each emitted mining spec through tao-mine-od-images. For round_robin_similarity, materialize the selected artifacts after query embedding instead:

scripts/deft_od_aoi_round_robin_selection.py \
  --policy "$RESULTS/deft_od_aoi_policy.yaml" \
  --candidate-root "$RESULTS/candidates" \
  --retrieval-root "$ITER/retrieval" \
  --previous-coco "$PREVIOUS/train.json"

Omit --previous-coco for iteration 1. Later iterations exclude candidate crops whose source image is already in the cumulative training COCO, while admission remains the authoritative source-level deduplication gate. Both strategies must produce an enabled role's mine_<role>/final_unique_files.parquet before committing iteration_retrieval; round-robin also commits round_robin_selection_report.json as selection_report and round_robin_admission_index.npy as admission_index. Defective candidates and gap queries use the independently selected retrieval.preprocessing.profile: the default square_context mean-pads and resizes to 224×224, while tight_context preserves an aspect-ratio-aware native-size crop. Clean candidates use the same profile over the frozen grid. Strict FNs and loose near-miss FPs route to real data. Background-like loose FPs route only to the verified-clean role. Pass the prior cumulative COCO as --previous-coco; optional role-specific exclusion parquets use --real-exclusions and --clean-exclusions. Empty or fully excluded roles emit audited exhaustion evidence and no mining action. If every producer is exhausted and synthesis is not pending, the stage records convergence. The independent retrieval.selection.strategy defaults to round_robin_similarity, which balances candidates across stable per-query rank rounds and pocket-scoped quotas. max_similarity retains the global maximum-similarity path.

Admission and cumulative COCO

After each enabled role has a committed selection artifact, admit its selected candidate crops back to unique source images and publish the next cumulative dataset:

scripts/admit_deft_od_aoi_coco.py \
  --policy "$RESULTS/deft_od_aoi_policy.yaml" \
  --candidate-root "$RESULTS/candidates" \
  --retrieval-root "$ITER/retrieval" \
  --previous-coco "$PREVIOUS/train.json" \
  --output-dir "$ITER/training_data"

Omit --previous-coco only for iteration 1. For max_similarity, the helper recomputes maximum cosine similarity from the frozen candidate/query embeddings. For round_robin_similarity, it consumes the committed per-query selection, screened boxes, audit report, and admission index without rerunning selection, then publishes admission_index.npy for the next iteration. Both paths apply the minimum similarity upstream, deduplicate crop hits to source images, exclude prior sources, and cap cumulative clean negatives against cumulative real defects. It retains every prior image and box and emits one binary COCO with explicit zero-annotation clean images. Use --link-mode hardlink only when source and output share a filesystem; portable staging should keep the copy default.

Measurement specs

For the baseline, pass --baseline. The default cold_start mode creates an empty prediction file for every KPI image, making all KPI ground-truth boxes initial false negatives without loading an incompatible multiclass checkpoint into the binary inference head. It also emits cardinality-matched empty test prediction evidence so the measurement manifest retains the same KPI/test completion contract without running inference. Explicit checkpoint mode emits normal baseline KPI/test inference specs and therefore requires a binary-compatible checkpoint. Neither mode changes training initialization.

scripts/prepare_deft_od_aoi_measurement.py \
  --policy "$RESULTS/deft_od_aoi_policy.yaml" \
  --checkpoint "$CHECKPOINT" \
  --kpi-predictions "$MEASURE/kpi/inference/labels" \
  --results-root "$MEASURE" --output-dir "$MEASURE/specs" \
  --baseline

Submit only the inference specs listed in measurement_manifest.json; a cold-start baseline lists no inference specs. Then submit gap_loose.yaml and gap_strict.yaml through tao-analyze-gaps-od-map. The helper projects the frozen KPI COCO to KITTI, keeps the two-line inference class map durable, and emits only nested specs. Test inference is report-only and its output cannot influence routing or checkpoint selection.

After training, omit --baseline; every iteration measurement uses the selected binary checkpoint and emits KPI/test inference regardless of the baseline mode.

RT-DETR training specs

Build nested specs from the current cumulative COCO:

scripts/prepare_deft_od_aoi_training.py \
  --policy "$RESULTS/deft_od_aoi_policy.yaml" --iteration 1 \
  --train-coco "$ITER/training_data/train.json" \
  --train-images "$ITER/training_data/images" \
  --results-root "$ITER/training" --output-dir "$ITER/train_specs"

Invoke the RT-DETR leaf as direct training with automl_policy: off; the AOI application already owns its frozen probe policy. Iterations 1–2 use 36 epochs. Later iterations use the clipped size-adaptive budget. When probes are enabled and the iteration reaches the frozen training.probes_start_iteration, the helper emits independent incumbent, data-growth-scaled, and deterministic jitter specs, all initialized from the same frozen base checkpoint. Run all three before selecting and materializing train.yaml; never initialize the next iteration from a previous iteration checkpoint.

Select all three probes before main training:

scripts/select_deft_od_aoi_training.py probes \
  --manifest "$SPECS/training_manifest.json" \
  --main-template "$SPECS/main_template.yaml" \
  --status "$P0/status.json" --status "$P1/status.json" --status "$P2/status.json" \
  --output-dir "$SPECS/selected"

After main training, select the KPI-best checkpoint from structured status rows. Repeat --status for resumed phases. If the best epoch is within the frozen late window, the first call emits one same-iteration extension.yaml from the terminal checkpoint; submit it, then reselect with --extension-applied. The checkpoint carried to measurement is always the maximum KPI val_mAP50, never the latest checkpoint or test result.

After each stage succeeds, commit at least one completion artifact with commit_deft_od_aoi_stage.py. It accepts only the next frozen stage, verifies and hashes every named file, atomically updates deft_state.json, and appends loop_log.jsonl. The final iteration becomes COMPLETE only after its gap artifacts are committed. Poll native backends for live state; this record is durable workflow history, not a scheduler substitute.

Optional synthesis with existing task weights

Enable synthesis only when every KPI sample retains its real, nonempty dataset_id; a missing ID is malformed KPI metadata and is never an opt-out. synthesis.routes is the synthesis allowlist. Dataset IDs absent from it skip synthesis but remain in the separate normal real-data DEFT path. Every FN in a configured route must also carry texture_id, defect_class, and a pixel fn_mask_source, and the route must provide an existing AnomalyGenNext checkpoint and matching recipe. After strict gap analysis, normalize exact FN/annotation matches:

scripts/prepare_deft_od_aoi_synthesis.py \
  --policy "$RESULTS/deft_od_aoi_policy.yaml" \
  --strict-gaps "$MEASURE/gap_strict/box_gaps.parquet" \
  --iteration 1 \
  --real-coco "$ITER/admission/train.json" \
  --output-dir "$ITER/synthesis_request"

The default synthesis.fn_selection.mode: generated_per_type_plan bounds work from the current admitted real COCO. It derives a deterministic per-type plan from cumulative_fraction_of_total_defects, writes and hashes the plan, and records the full allocation calculation. A fraction of 0.25 means synthetic images are 25% of the combined real-plus-synthetic defect pool. Set the mode to all_eligible explicitly to preserve every routed FN without pre-generation budgeting.

The planner derives the finalized yield as 2 * max_neighbors_per_fn: the finalizer emits two fixed mask branches and requests one image from each branch for every retained neighbor. candidate_topn only supplies fallback neighbor candidates and does not enter this calculation. It is synthesis-only and is independent of both max-similarity overfetch and round-robin refill. The derived value is recorded as images_per_fn in the frozen plan evidence; it is not a policy input. candidate_topn is the AMP compute knob: preparation emits two AMP requests per eligible candidate, so one FN costs up to 2 * candidate_topn placement attempts to retain 2 * max_neighbors_per_fn finalized images. For backward compatibility, initialization copies an explicitly supplied legacy retrieval.candidate_overfetch into synthesis when candidate_topn is omitted, and already-frozen policies without the new field retain that fallback. Set both fields explicitly to configure them independently.

Pass the emitted filtering YAML through tao-prepare-anomalygennext-inputs, mounting the complete checkpoint root for its run_amp action, then pass its finalized generation plan through tao-generate-od-defects with the same checkpoint root mounted at the same canonical path. Commit iteration_synthesis before training. Read the selected tao-generate-od-defects action contract and resolve actions.generate.outputs.binary_coco.relative_path against that action's results directory. Re-run admission with the resolved file passed through --synthetic-coco and the generation results directory passed through --synthetic-images; admission never locates a companion skill itself. Synthetic categories are folded to defect, and the frozen cumulative fraction cap is applied against admitted real defects. Commit initial admission before requesting synthesis. The stage controller uses its calculated synthetic capacity: zero room records a no_synthetic_budget skip and bypasses iteration synthesis. Train only when this iteration admitted new images; otherwise converge. Positive capacity can remain from prior real admissions even without new real matches. The post-generation cap and quality checks still apply when synthesis runs; provide the same-iteration real admission as --previous-coco and set --synthetic-only so synthetic inputs do not implicitly rerun mining admission. SYNTHETIC_ADMISSION_CAPPED reports eligible generated images excluded by the cap. See references/pipeline.md for budget-skip evidence. A zero cumulative real count permits zero synthetic admissions.

Boxes never substitute for the required pixel mask. If the request has no routed FNs, or preparation reports no mask-eligible FNs, commit that typed skip contract as iteration_synthesis and continue to training without running AMP or fabricating generation/admission success. Missing or empty AnomalyGen clean reference pools likewise skip only affected FN/types with no_clean_reference_images; eligible types and real/clean retrieval continue. Initialization rejects synthesis when the entire configured reference pool has no supported image under any TEXTURE/clean_image directory. If no FN remains eligible, the preparation-level reason is no_eligible_false_negatives while per-FN evidence retains the specific cause. If every producer is unavailable, the typed skip commits convergence instead of training on an unchanged iteration.

Missing AnomalyGenNext task weights

A synthesis route may replace checkpoint and recipe with a finetune block containing dataset_root, frozen validation_testcase, Cosmos3-Nano base_checkpoint, vae_path, checkpoint_root, future result_handoff, and optional user recipe_template/defect_spec. These are AnomalyGenNext inputs; the application policy is not an upstream training recipe.

Run resolve_deft_od_aoi_synthesis.py before the candidate cache. If a handoff is absent, it emits finetune_requests.json; execute each request through tao-finetune-anomalygennext once. Resolve again into a new directory, verify the hash-bound handoff, commit synthesis_bootstrap, and use the emitted resolved_synthesis_policy.yaml for all later synthesis preparation. Never start or resume AnomalyGenNext training inside a DEFT iteration.

Files

scripts/init_deft_od_aoi.py

#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Validate normalized DEFT OD AOI roles and freeze a real-only run contract."""

from __future__ import annotations

import argparse
import hashlib
import json
import sys
from pathlib import Path
from typing import Any

import yaml

sys.path.insert(0, str(Path(__file__).resolve().parent))
from deft_od_aoi_synthesis_contract import validate_synthesis_contract


DEFAULTS = Path(__file__).resolve().parents[1] / "assets" / "default_policy.yaml"
# Normalized handoff roles: KPI/test are held out, ``real`` is the
# defective-real mining pool, and ``clean`` is the verified-clean mining pool.
ROLES = ("kpi", "test", "real", "clean")
IMAGE_SUFFIXES = {".bmp", ".jpeg", ".jpg", ".png", ".tif", ".tiff"}
ROUTED_SYNTHESIS_FIELDS = ("texture_id", "defect_class", "fn_mask_source")


def _json(path: Path, value: Any) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(value, indent=2, sort_keys=True) + "\n")


def _sha(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as stream:
        for chunk in iter(lambda: stream.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def _merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
    result = dict(base)
    for key, value in override.items():
        result[key] = _merge(result.get(key, {}), value) if isinstance(value, dict) else value
    return result


def _image_path(images: Path, row: dict[str, Any]) -> Path:
    source = str(row.get("source_path") or "").strip()
    path = Path(source) if source else images / str(row.get("file_name") or "")
    return path.expanduser().resolve()


def _validate_bbox(value: Any, image: dict[str, Any], role: str) -> None:
    x, y, width, height = map(float, value)
    image_width = float(image.get("width") or 0)
    image_height = float(image.get("height") or 0)
    if image_width <= 0 or image_height <= 0:
        raise ValueError(f"{role} normalized COCO image dimensions must be positive")
    if width <= 0 or height <= 0:
        raise ValueError(f"{role} normalized COCO bbox dimensions must be positive")
    if x < 0 or y < 0 or x + width > image_width or y + height > image_height:
        raise ValueError(f"{role} normalized COCO bbox exceeds image bounds")


def _has_synthesis_clean_reference(pool: Path) -> bool:
    return any(
        image.is_file() and image.suffix.lower() in IMAGE_SUFFIXES
        for texture in pool.iterdir() if texture.is_dir()
        for clean_dir in (texture / "clean_image",) if clean_dir.is_dir()
        for image in clean_dir.iterdir()
    )


def _synthesis_metadata(image: dict[str, Any], annotation: dict[str, Any]) -> dict[str, Any]:
    """Resolve synthesis metadata with the same image-to-annotation precedence as routing."""
    metadata = {}
    for owner, label in ((image, "image"), (annotation, "annotation")):
        nested = owner.get("deft_od_aoi", {})
        if not isinstance(nested, dict):
            raise ValueError(f"KPI {label} deft_od_aoi metadata must be an object")
        for source in (owner, nested):
            metadata.update({key: source[key] for key in
                             ("dataset_id", *ROUTED_SYNTHESIS_FIELDS) if key in source})
    return metadata


def _role(name: str, value: dict[str, Any], require_dataset_id: bool = False) -> tuple[
        dict[str, Any], dict[str, Any]]:
    images = Path(str(value.get("images") or "")).expanduser().resolve()
    coco_path = Path(str(value.get("coco") or "")).expanduser().resolve()
    if not images.is_dir() or not coco_path.is_file():
        raise ValueError(f"{name} images/COCO are missing")
    coco = json.loads(coco_path.read_text())
    categories = coco.get("categories", [])
    if not categories or {str(row.get("name")) for row in categories} != {"defect"}:
        raise ValueError(f"{name} must declare only the defect category")
    category_ids = {int(row["id"]) for row in categories}
    image_rows = coco.get("images", [])
    images_by_id = {int(row["id"]): row for row in image_rows}
    image_ids = set(images_by_id)
    if not image_rows and name != "clean":
        raise ValueError(f"{name} has no images")
    if len(image_ids) != len(image_rows):
        raise ValueError(f"{name} has duplicate image ids")
    counts = {image_id: 0 for image_id in image_ids}
    missing_dataset_ids = []
    for annotation in coco.get("annotations", []):
        image_id, category = int(annotation["image_id"]), int(annotation["category_id"])
        if image_id not in counts or category not in category_ids:
            raise ValueError(f"{name} annotation references unknown image/category")
        _validate_bbox(annotation["bbox"], images_by_id[image_id], name)
        counts[image_id] += 1
        if require_dataset_id:
            metadata = _synthesis_metadata(images_by_id[image_id], annotation)
            if not str(metadata.get("dataset_id") or "").strip():
                image = images_by_id[image_id]
                missing_dataset_ids.append(
                    f"annotation_id={annotation.get('id')} image_id={image_id} "
                    f"file_name={image.get('file_name')!r}"
                )
    if missing_dataset_ids:
        details = ", ".join(missing_dataset_ids[:5])
        remainder = len(missing_dataset_ids) - 5
        if remainder > 0:
            details += f", and {remainder} more"
        raise ValueError(
            "enabled synthesis requires every KPI annotation to resolve a nonempty "
            f"dataset_id; missing for {details}. synthesis.routes is an allowlist: "
            "a nonempty unconfigured dataset_id is valid and skips synthesis"
        )
    paths = [_image_path(images, row) for row in image_rows]
    missing = [path for path in paths if not path.is_file()]
    if missing:
        raise ValueError(f"{name} references missing images; first={missing[0]}")
    if name == "clean" and any(counts.values()):
        raise ValueError("clean role must have zero annotations")
    if name == "real" and any(count == 0 for count in counts.values()):
        raise ValueError("defective-real role contains a boxless image")
    report = {"images": str(images), "coco": str(coco_path),
              "coco_sha256": _sha(coco_path), "image_count": len(paths),
              "annotation_count": sum(counts.values()),
              "identities": {str(path) for path in paths}}
    return report, coco


def _validate_kpi_retrieval_metadata(document: dict[str, Any]) -> None:
    boxed_image_ids = {
        int(annotation["image_id"]) for annotation in document.get("annotations", [])
    }
    fields = ("dataset_id", "texture_id", "defect_class")
    for row in document.get("images", []):
        if int(row["id"]) not in boxed_image_ids:
            continue
        metadata = row.get("deft_od_aoi") or {}
        missing = [
            key for key in fields
            if not str(metadata.get(key) or row.get(key) or "").strip()
        ]
        if missing:
            raise ValueError(
                f"KPI image {row['id']} lacks canonical retrieval metadata: {missing}"
            )


def _validate_routed_kpi_metadata(
        policy: dict[str, Any], coco: dict[str, Any]) -> None:
    images = {int(row["id"]): row for row in coco["images"]}
    routes = policy["synthesis"]["routes"]
    issues = []
    for annotation in coco.get("annotations", []):
        image_id = int(annotation["image_id"])
        image = images[image_id]
        metadata = _synthesis_metadata(image, annotation)
        dataset = str(metadata.get("dataset_id") or "").strip()
        if not dataset or dataset not in routes:
            continue
        identity = (f"annotation_id={annotation.get('id')} image_id={image_id} "
                    f"file_name={image.get('file_name')!r}")
        missing = [key for key in ROUTED_SYNTHESIS_FIELDS
                   if not str(metadata.get(key) or "").strip()]
        if missing:
            issues.append(f"{identity} missing={missing}")
            continue
        mask = Path(str(metadata["fn_mask_source"])).expanduser().resolve()
        if not mask.is_file():
            issues.append(f"{identity} fn_mask_source is not an existing file: {mask}")
    if issues:
        details = "; ".join(issues[:5])
        remainder = len(issues) - 5
        if remainder > 0:
            details += f"; and {remainder} more"
        raise ValueError(f"routed KPI synthesis metadata is incomplete: {details}")


def initialize(config_path: Path, output: Path) -> dict[str, Any]:
    if output.exists():
        raise FileExistsError(f"refusing to overwrite output: {output}")
    user = yaml.safe_load(config_path.read_text())
    if not isinstance(user, dict):
        raise ValueError("config must be a YAML mapping")
    if "near_miss_real_cap" in (user.get("routing") or {}):
        raise ValueError(
            "routing.near_miss_real_cap is unsupported; use "
            "routing.near_miss_real_cap_per_pocket"
        )
    policy = _merge(yaml.safe_load(DEFAULTS.read_text()), user)
    user_synthesis = user.get("synthesis", {})
    user_retrieval = user.get("retrieval", {})
    inherited_synthesis_candidate_topn = False
    if (isinstance(user_synthesis, dict)
            and "candidate_topn" not in user_synthesis
            and isinstance(user_retrieval, dict)
            and "candidate_overfetch" in user_retrieval):
        # Map an explicit pre-split shared depth to the new synthesis field.
        policy["synthesis"]["candidate_topn"] = user_retrieval["candidate_overfetch"]
        inherited_synthesis_candidate_topn = True
    if (isinstance(user_synthesis, dict)
            and "cumulative_fraction_of_real_defects" in user_synthesis
            and "cumulative_fraction_of_total_defects" not in user_synthesis):
        policy["synthesis"].pop("cumulative_fraction_of_total_defects", None)
        if "fn_selection" not in user_synthesis:
            policy["synthesis"]["fn_selection"] = {"mode": "all_eligible"}
    if not isinstance(policy.get("max_iterations"), int) or policy["max_iterations"] < 1:
        raise ValueError("max_iterations must be a positive integer")
    if not str(policy.get("platform") or "").strip():
        raise ValueError("platform must be selected before initialization")
    checkpoint = Path(str(policy.get("base_checkpoint") or "")).expanduser().resolve()
    if not checkpoint.is_file():
        raise ValueError("base_checkpoint must be a trainable RT-DETR file")
    baseline_mode = str(policy.get("baseline_mode") or "")
    if baseline_mode not in {"cold_start", "checkpoint"}:
        raise ValueError("baseline_mode must be cold_start or checkpoint")
    synthesis = policy.get("synthesis", {})
    synthesis_enabled = bool(synthesis.get("enabled"))
    role_reports = {}
    role_documents = {}
    for name in ROLES:
        report, document = _role(
            name, policy["sources"][name], synthesis_enabled and name == "kpi"
        )
        role_reports[name] = report
        role_documents[name] = document
    _validate_kpi_retrieval_metadata(role_documents["kpi"])
    owners: dict[str, str] = {}
    for name, report in role_reports.items():
        for identity in report.pop("identities"):
            if identity in owners:
                raise ValueError(f"image overlaps {owners[identity]} and {name}: {identity}")
            owners[identity] = name
    gap = policy["gap"]
    if not (0 <= gap["inference_confidence"] <= gap["loose_confidence"]
            <= gap["strict_confidence"] <= 1):
        raise ValueError("inference/loose/strict confidence thresholds are inconsistent")
    if not (0 <= gap["background_iou_upper"] < gap["near_miss_iou_upper"] <= 1):
        raise ValueError("background and near-miss IoU thresholds are inconsistent")
    if policy["class_name"] != "defect":
        raise ValueError("DEFT OD AOI has one foreground class named defect")
    profile = str(((policy.get("retrieval") or {}).get("preprocessing") or {}).get(
        "profile", ""
    ))
    if profile not in {"tight_context", "square_context"}:
        raise ValueError(
            "retrieval.preprocessing.profile must be tight_context or square_context"
        )
    if (profile == "square_context"
            and int(policy["retrieval"].get("output_size", 0)) < 1):
        raise ValueError("retrieval.output_size must be positive")
    strategy = ((policy.get("retrieval") or {}).get("selection") or {}).get(
        "strategy", ""
    )
    if strategy not in {"max_similarity", "round_robin_similarity"}:
        raise ValueError(f"unsupported retrieval selection strategy: {strategy}")
    if int(policy["retrieval"].get("audit_top_k_per_query", 0)) < 1:
        raise ValueError("retrieval.audit_top_k_per_query must be positive")
    if strategy == "round_robin_similarity":
        routing = policy["routing"]
        minimum = int(routing["real_mine_factor_min"])
        maximum = int(routing["real_mine_factor_max"])
        default = int(routing["round_robin_real_factor_default"])
        if not 1 <= minimum <= default <= maximum:
            raise ValueError(
                "round-robin real factor default must be within the frozen bounds"
            )
    if synthesis_enabled:
        try:
            validate_synthesis_contract(synthesis)
        except ValueError as error:
            if (inherited_synthesis_candidate_topn
                    and "candidate_topn" in str(error)):
                raise ValueError(
                    "retrieval.candidate_overfetch was inherited as "
                    f"synthesis.candidate_topn; {error}"
                ) from error
            raise
        pool = Path(str(synthesis.get("pool_dataset_root") or "")).expanduser().resolve()
        if not pool.is_dir():
            raise ValueError("enabled synthesis needs pool_dataset_root")
        if not _has_synthesis_clean_reference(pool):
            raise ValueError("enabled synthesis needs at least one clean reference image")
        if not Path(str(synthesis.get("defect_spec") or "")).is_file():
            raise ValueError("enabled synthesis needs defect_spec")
        if not synthesis.get("routes"):
            raise ValueError("enabled synthesis needs at least one dataset route")
        for name, route in synthesis["routes"].items():
            ready = Path(str(route.get("checkpoint") or "")).is_file() and Path(
                str(route.get("recipe") or "")).is_file()
            finetune = route.get("finetune") or {}
            if not ready and any(not str(finetune.get(key) or "").strip() for key in
                                 ("dataset_root", "validation_testcase", "base_checkpoint",
                                  "vae_path", "checkpoint_root", "result_handoff")):
                raise ValueError(f"synthesis route {name} needs checkpoint/recipe or finetune inputs")
        # Reuse the document parsed from the resolved, existence-checked KPI path.
        _validate_routed_kpi_metadata(policy, role_documents["kpi"])
    output.mkdir(parents=True)
    policy["base_checkpoint"] = str(checkpoint)
    policy["sources"] = {name: {"images": role_reports[name]["images"],
                                 "coco": role_reports[name]["coco"]} for name in ROLES}
    frozen = output / "deft_od_aoi_policy.yaml"
    frozen.write_text(yaml.safe_dump(policy, sort_keys=False))
    classmap = output / "inference_classmap.txt"
    classmap.write_text("background\ndefect\n")
    bootstrap_required = synthesis_enabled and any(
        not (Path(str(route.get("checkpoint") or "")).is_file()
             and Path(str(route.get("recipe") or "")).is_file())
        for route in policy.get("synthesis", {}).get("routes", {}).values()
    )
    retrieval_capabilities = {
        role: {"status": "AVAILABLE" if role_reports[role]["image_count"] else "UNAVAILABLE",
               "reason": None if role_reports[role]["image_count"] else "empty_source_role",
               "source_image_count": role_reports[role]["image_count"]}
        for role in ("real", "clean")
    }
    warnings = [{
        "code": "empty_retrieval_source_role",
        "role": role,
        "message": f"{role} retrieval source role is empty; that producer starts exhausted",
    } for role, evidence in retrieval_capabilities.items()
        if evidence["status"] == "UNAVAILABLE"]
    if inherited_synthesis_candidate_topn and synthesis_enabled:
        warnings.append({
            "code": "inherited_synthesis_candidate_topn",
            "source": "retrieval.candidate_overfetch",
            "target": "synthesis.candidate_topn",
            "value": policy["synthesis"]["candidate_topn"],
            "message": (
                "synthesis.candidate_topn was omitted; inherited the explicit "
                "retrieval.candidate_overfetch value for backward compatibility"
            ),
        })
    state = {"schema_version": 1, "status": "READY",
             "mode": "rtdetr_with_synthesis" if synthesis_enabled else "rtdetr_real_only",
             "baseline_mode": baseline_mode,
             "synthesis_enabled": synthesis_enabled,
             "synthesis_bootstrap_required": bootstrap_required,
             "current_iteration": 0,
             "next_stage": "synthesis_bootstrap" if bootstrap_required else "candidate_cache",
             "max_iterations": policy["max_iterations"], "platform": policy["platform"],
             "base_checkpoint": str(checkpoint), "policy": str(frozen.resolve()),
             "policy_sha256": _sha(frozen), "classmap": str(classmap.resolve()),
             "roles": role_reports,
             "capabilities": {"retrieval": retrieval_capabilities},
             "warnings": warnings, "iterations": {}}
    _json(output / "deft_state.json", state)
    return state


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--config", type=Path, required=True)
    parser.add_argument("--output-dir", type=Path, required=True)
    args = parser.parse_args()
    state = initialize(args.config.resolve(), args.output_dir.resolve())
    for warning in state.get("warnings", []):
        print(f"WARNING: {warning['message']}", file=sys.stderr)
    print(json.dumps(state, sort_keys=True))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Declares allowed tools, Needs Python

The author says: Requires TAO RT-DETR and Data Services images, CUDA GPUs, and normalized COCO roles.

Security

What our scanner noticed· Medium risk

  • Large base64-encoded blobs
  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
NVIDIA Corporation
Publisher
NVIDIA (Official)
Source
NVIDIA/skills · skills/tao-run-deft-od-aoi · commit fce3cca
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE-APACHE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by NVIDIA Corporation or NVIDIA.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download tao-run-deft-od-aoi.zip