option
MaisonMaison Skill DevOps et CI/CD tao-run-inference-service

tao-run-inference-service

NVIDIA/skills NVIDIA/skills

Démarrer, interroger et arrêter un microservice d'inférence TAO pour une architecture réseau spécifique en déléguant l'exécution des conteneurs à la compétence de plateforme appropriée.

...Développer tout
1
Heure mise à jour 27 septembre 2026

Microservice d'inférence TAO

Instructions

Pour démarrer un service d'inférence :

  1. Rassemblez les données d'entrée requises (section 1) et récupérez l'image du conteneur (section 2).
  2. Créez la charge utile de la tâche et la commande interne (sections 3 à 4.1) ; utilisez references/code-templates.yaml → job_payload_builder.
  3. Read skills/platform//SKILL.md et démarrez le conteneur (section 4.2).
  4. Enregistrez le service dans le registre et vérifiez qu’il est prêt (section 4.3) ; utilisez references/code-templates.yaml → registry_write. et readiness_check.

Pour envoyer une requête d’inférence :

  1. Déterminez quel service reçoit la requête conformément à la section 6.0 (en job_id, par network_arch, ou par choix explicite de l’utilisateur lorsque plusieurs services sont en cours d’exécution — ne jamais utiliser implicitement "latest" par défaut lorsqu’il existe plusieurs services), puis lire le point de terminaison à partir de references/code-templates.yaml → request.registry_read à l’aide du job_id.
  2. Avant de construire le corps de la requête, demandez à l’utilisateur de fournir les paramètres d’échantillonnage de type vLLM (section 6.1). Présentez max_tokens, top_p, temperature (ainsi que les éventuels paramètres supplémentaires propres à l’architecture) avec leurs valeurs par défaut ; laissez l’utilisateur les modifier ou les ignorer pour accepter la valeur par défaut. N’utilisez jamais les valeurs par défaut de manière silencieuse.
  3. Construisez et envoyez le corps de la requête conformément à la section 6.2 ; traitez la réponse conformément à la section 6.3.

Pour arrêter un service : lisez references/code-templates.yaml → stop.registry_read pour résoudre le job_id, lisez skills/platform//SKILL.md, puis suivez les instructions de la section 5.

Données de référence (schémas, mappages, valeurs valides — aucune instruction) :

  • references/service.yaml — mappages d’images, network_arch , schéma de la charge utile des tâches, noms des variables d’environnement, classification des secrets.
  • references/request.yaml — définition des points de terminaison, schéma des champs de requête, formats de réponse, exemples de code.
  • references/code-templates.yaml — Modèles Python pour la création de charges utiles, les écritures dans le registre, les vérifications de disponibilité et les flux d’arrêt/de requête.

Règle relative aux secrets (s'applique à chaque bloc de code généré dans cette compétence)

Ne demandez jamais à l’utilisateur de saisir une valeur secrète dans une invite. Pour chaque valeur secrète :

  1. Indiquez à l’utilisateur quelle variable d’environnement définir (par exemple export HF_TOKEN=...).
  2. Générez du code qui la lit à l’aide de os.environ["VAR_NAME"] — ne jamais coder en dur, interpoler ou demander la valeur via une invite.

Variables d’environnement secrètes (liste complète dans references/service.yaml → secrets_handling): HF_TOKEN, WANDB_API_KEY, CLEARML_API_ACCESS_KEY, CLEARML_API_SECRET_KEY, TAO_API_KEY, TAO_USER_KEY.

Données pouvant être collectées en toute sécurité via une invite : network_arch, model_path, num_gpus, le texte de l'invite, WANDB_* URL de configuration, CLEARML_*_HOST URL.

1. Ce qu'il faut collecter auprès de l'utilisateur

Entrée Rôle
network_arch Choix de l'image du conteneur, la forme de la commande interne par architecture (references/service.yaml → container_commands.) et neural_network_name dans le JSON de la tâche, le cas échéant. Doit correspondre à un nom de base dans valid_network_arch_config_basenames dans references/service.yaml (par exemple cosmos-rl, cosmos-predict2.5).
model_path Le point de contrôle du modèle entraîné. Formats valides : hf_model:/// (HuggingFace Hub — à définir HF_TOKEN pour les modèles « gated ») ou un chemin d’accès au système de fichiers d’un conteneur local. Les URI cloud (s3://, gs://, az://) ne sont PAS pris en charge — le service d’inférence ne dépend pas d’un stockage cloud. Demandez toujours à l’utilisateur ; ne remplacez jamais par un espace réservé. Voir references/service.yaml → model_path_protocols.
platform Plateforme de calcul : local-docker, brev, slurm, ou kubernetes.
num_gpus Valeur par défaut : 1 ; minimum 1 pour l’inférence.

2. Résolution de l’image

Chaque network_arch dispose d’un fichier de configuration sidecar nommé {network_arch}.config.json. Définissez l’image du conteneur comme suit :

  1. Lisez {network_arch}.config.json et prenez api_params.image (par exemple COSMOS_RL). Il s'agit d'une clé dans docker_image_defaults.mapping dans references/service.yaml.
  2. Recherchez cette clé dans la table de correspondance. Si la variable d’environnement hôte IMAGE_ est définie (par ex. IMAGE_COSMOS_RL), elle remplace la valeur par défaut mappée.
  3. La valeur mappée est généralement une clé sous forme de nom pointé dans le manifeste de la racine du dépôt versions.yaml (par exemple tao_toolkit.cosmos_rl). Convertissez-la en une nvcr.io/... en recherchant versions.yaml → images... Les URI absolues sont transmises telles quelles ; ainsi, une IMAGE_ surdéfinition par variable d’environnement contenant une URI complète fonctionne toujours. L’assistant Python dédié se trouve dans references/code-templates.yaml.
  4. Si le fichier de configuration est manquant ou api_params.image est vide, utilisez la COSMOS_RL clé.

Le fichier de configuration contient également spec_params.inference.model_path qui détermine la sémantique entre chemin de dossier et chemin de fichier : si la valeur contient la sous-chaîne folder, le conteneur traite le chemin comme un répertoire.

3. Variables d’environnement (sans callbacks)

Définissez-les dans env_payload avant l’encodage env_json. Ne les définissez pas TAO_LOGGING_SERVER_URL ou TAO_ADMIN_KEY.

TAO_EXECUTION_BACKEND — doit correspondre à la plateforme :

Plateforme TAO_EXECUTION_BACKEND valeur
local-docker local-docker
brev local-docker
slurm slurm
Kubernetes local-k8s

CLOUD_BASED — toujours "False" pour cette compétence (désactive l'envoi de callbacks vers TAO_LOGGING_SERVER_URL).

variables d’environnement GPU — nécessaire uniquement lorsque la compétence de la plateforme ne gère pas automatiquement l’injection GPU :

  • Tegra / Jetson : --runtime=nvidia avec NVIDIA_DRIVER_CAPABILITIES=all et NVIDIA_VISIBLE_DEVICES=.
  • x86 standard + nvidia-container-toolkit : utilisez Docker device_requests. La compétence de plateforme s'en charge.

4. Exécution sur plusieurs plateformes

La charge utile de la tâche et la commande interne (sections 1 à 3) sont indépendantes de la plateforme. Pour chaque plateforme, consultez skills/platform//SKILL.md pour connaître les vérifications préalables et les identifiants avant de générer tout code d'exécution.

4.1 Créer la commande interne (par architecture)

La structure de la commande interne est définie dans network_arch — il n’existe pas de modèle uniforme. Recherchez l’entrée correspondant à l’architecture dans references/service.yaml → container_commands.; si elle n’existe pas, cela signifie que l’architecture n’est pas prise en charge — arrêtez-vous et renseignez-vous. Choisissez le sous-bloc correspondant dans references/code-templates.yaml → job_payload_builder.. Faites précéder la commande de umask 0 && et veillez à ce qu’elle reste identique sur toutes les plateformes (local-docker, brev, slurm, kubernetes).

Commun à toutes les architectures :

  • job_id: fresh uuid.uuid4() — devient le nom du conteneur et la clé de registre.
  • image: à résoudre conformément à la section 2.
  • Les secrets (access_key, secret_key, HF_TOKEN, etc.) sont lus à partir des variables d’environnement lors de l’exécution — ne jamais les coder en dur, ne jamais les consigner dans un journal ni les afficher.

Remarques spécifiques à l’architecture (détails complets dans references/service.yaml → container_commands):

  • cosmos-rl — un seul --job '' --docker_env_vars '' blob ; json.dumps(...) + shlex.quote(...). env_payload contient TAO_EXECUTION_BACKEND (selon le tableau de la section 3), TAO_API_JOB_ID, CLOUD_BASED=False. Le service d’inférence ne dépend d’aucun stockage cloud ; HF_TOKEN est la seule variable d’environnement d’authentification qui s’applique (pour les modèles HuggingFace à accès restreint).
  • cosmos-predict2.5 — style drapeau cosmos_predict inference_microservice start ... --port 8080 (sans setup. préfixe ; les tyro.conf.OmitArgPrefixes). --job/--docker_env_vars ne sont pas acceptés. Traduire model_path en --checkpoint-path (chemin local) ou --model (hf_model://) ; les URI cloud sont rejetées. La seule variable d’environnement d’authentification qui s’applique est HF_TOKEN pour les modèles HuggingFace protégés par un accès contrôlé. Les paramètres par requête (prompt, inference_type, num_output_frames, guidance, seed, num_steps, negative_prompt) doivent figurer dans le corps de la requête, et non lors du démarrage. TAO_EXECUTION_BACKEND/TAO_API_JOB_ID/CLOUD_BASED ne sont pas utilisés et peuvent être omis.

4.2 Déléguer l’exécution à la compétence de la plateforme

Consultez skills/platform//SKILL.md et suivez les instructions pour démarrer le conteneur.

Paramètres de base (toutes les plateformes) :

Paramètre Valeur
image image du conteneur résolue (section 2)
command inner — la chaîne de commande shell générée à la section 4.1
gpu_count num_gpus
env_vars env_payload
nom de la tâche / du conteneur job_id — doit correspondre à l’UUID de la section 4.1 afin que le registre puisse y faire référence
host_port (local-docker, brev) port côté hôte à lier au port 8080 du conteneur. Par défaut 8080, mais doit être unique pour chaque service simultané — voir la règle d’attribution des ports ci-dessous.

Paramètres supplémentaires spécifiques à la plateforme :

Plateforme Entrées supplémentaires
local-docker Aucune au-delà de la configuration de base
brev instance_id (facultatif — réutilisation d’une instance existante) ; pour les comptes à identifiants multiples / espaces de travail multiples, également cloud_cred_id et workspace_group_id pour la première création — voir skills/platform/tao-run-on-brev/SKILL.md
slurm partition et account — vérifier les SLURM_PARTITION/SLURM_ACCOUNT les variables d’environnement ; demander à l’utilisateur si elles ne sont pas définies
kubernetes namespace (par défaut : default); image_pull_secret (obligatoire pour les nvcr.io images)

Liaison de port (local-docker et brev) : utiliser « docker run » directement (et non DockerSDK) afin que -p :8080 puisse être transmis et que le nom du conteneur corresponde job_id exactement.

Règle d’attribution des ports (local-docker et brev, OBLIGATOIRE pour les services simultanés) : avant de démarrer un service, lisez le registre (/tmp/tao-inf-ms-state.json) et collectez l’ensemble des host_port valeurs de chaque entrée existante sur la même plateforme (et, pour brev, la même instance_id). Choisissez le port libre le plus bas à partir de 8080 qui ne figure pas dans cet ensemble — par exemple host_port = next(p for p in range(8080, 8200) if p not in used_ports). La valeur par défaut 8080 ne s’applique que lorsqu’aucun autre service n’est en cours d’exécution. C’est ce qui permet de « démarrer 3 services, chacun accessible à une adresse distincte host_url» ; sans cela, les services 2 et 3 échouent avec bind: address already in use. SLURM et Kubernetes obtiennent des points de terminaison distincts grâce à leurs propres mécanismes de plateforme et n’ont pas besoin de cette étape.

4.3 Après le démarrage : registre de services et point de terminaison

Enregistrez le service dans le registre immédiatement après que la plateforme a confirmé que le conteneur est en cours d’exécution. Le registre (/tmp/tao-inf-ms-state.json) est indexé par job_id; "latest" pointe toujours vers le service le plus récemment démarré.

Voir references/code-templates.yaml → registry_write. pour le modèle Python.

Étape host_url platform_job_id Étape supplémentaire avant l'écriture
local-docker http://localhost:{host_port} — Aucun
brev http://{brev_ip}:{host_port} — brev ls → récupérer l'adresse IP de l'instance (localhost n'est pas valable sur une machine virtuelle distante)
slurm http://localhost:{host_port} ID de tâche du planificateur SLURM Attendre que le processus soit en cours d’exécution ; redirection de port SSH localhost:{host_port}→{node}:8080
kubernetes http://{external_ip}:8080 Nom de la tâche k8s kubectl expose job … --type=LoadBalancer; attendre l'adresse IP externe

Après avoir écrit dans le registre, affichez l'ID de la tâche et l'URL :

print(f"Inference service started.")
print(f"  Job ID : {job_id}")
print(f"  Arch   : {network_arch}")
print(f"  URL    : {state[job_id]['host_url']}/v1/chat/completions")
print(f"Use this Job ID to send requests or stop the service.")

Vérifiez ensuite si le service est prêt — voir references/code-templates.yaml → readiness_check. Le conteneur charge le modèle en arrière-plan ; n’envoyez pas de requêtes avant qu’il ne renvoie un code 200.

5. Arrêt du service d’inférence

Demandez à l’utilisateur le job_id à arrêter. S’il n’en fournit pas, utilisez par défaut state["latest"] et confirmez quel job_id est en cours d’arrêt. Consultez le registre à l’aide de references/code-templates.yaml → stop.registry_read, puis consultez skills/platform//SKILL.md et utilisez son mécanisme d’annulation / d’arrêt.

Identifiant de à transmettre Nettoyage supplémentaire
local-docker job_id_to_stop — nom du conteneur Aucun
brev job_id_to_stop — nom du conteneur Aucun
slurm entry["platform_job_id"] — ID de tâche SLURM pkill -f "ssh.*-L.*{entry['host_port']}"
kubernetes entry["platform_job_id"] — Nom de la tâche k8s kubectl delete svc {entry["platform_job_id"]} -n

où entry = state[job_id_to_stop]. Une fois l'arrêt effectué, nettoyez le registre : references/code-templates.yaml → stop.registry_cleanup.

6. Envoi des requêtes d’inférence

6.0 Déterminer quel service reçoit cette requête (OBLIGATOIRE)

Chaque requête doit être acheminée vers le service spécifique qui exécute le modèle correspondant. L’acheminement s’effectue par job_id — le registre stocke network_arch par entrée ; vous pouvez ainsi déterminer une cible par architecture lorsque l’utilisateur nomme un modèle plutôt qu’un job_id. Appliquez ces règles dans l’ordre suivant :

  1. L’utilisateur a fourni une job_id explicite → utilisez-la. Vérifiez qu’elle existe dans state.
  2. L'utilisateur a spécifié une network_arch (par exemple « envoyer ceci au service cosmos-rl ») → recherchez les entrées correspondantes : candidates = [j for j, e in state.items() if j != "latest" and isinstance(e, dict) and e["network_arch"] == arch].
    • Exactement une correspondance → l’utiliser.
    • Plusieurs correspondances → proposer à l’utilisateur les job_idet de leur started_at; ne pas sélectionner automatiquement.
    • Aucune correspondance → s'arrêter et indiquer à l'utilisateur qu'aucun service n'est en cours d'exécution pour cette architecture.
  3. Pas d’job_id et pas d’network_arch → compter les entrées non-"latest" entrées dans state:
    • Exactement un service en cours d’exécution → l’utiliser.
    • Deux ou plus → ne pas choisir automatiquement « state["latest"] » par défaut. Proposer à l’utilisateur la liste complète (job_id, network_arch, host_url) et exigez un choix explicite. Le "latest" pointeur est une aide pratique pour les flux de travail à service unique, et non une solution de repli de routage lorsque plusieurs services coexistent.
    • Zéro → arrêtez-vous et demandez à l’utilisateur de démarrer d’abord un service.

Une fois la résolution effectuée, lisez le point de terminaison dans le registre (references/code-templates.yaml → request.registry_read), en transmettant le job_id sous la forme user_provided_job_id. Confirmez à l’utilisateur : « Envoi vers job_id=… arch=… url=… ». Si le service est peut-être encore en cours de chargement, vérifiez d’abord s’il est prêt (references/code-templates.yaml → readiness_check).

Vérification croisée avant l’envoi : si le corps de la requête fourni par l’utilisateur contient des champs spécifiques à l’architecture (par ex. guidance / num_steps / seed / negative_prompt → cosmos-predict2.5 ; éléments de contenu requis image_url/video_url éléments de contenu → cosmos-rl), vérifiez qu’ils correspondent à state[job_id]["network_arch"]. En cas de non-correspondance, arrêtez-vous et demandez des précisions — l’envoi d’un corps « cosmos-predict2.5 » à un service « cosmos-rl » échouera au niveau du conteneur avec un code d’erreur 4xx/5xx, ce qui est plus difficile à diagnostiquer que de le détecter ici.

6.1 Paramètres d’échantillonnage — Invite OBLIGATOIRE à l’utilisateur avant chaque requête

Avant de construire le corps de la requête, vous DEVEZ explicitement demander à l’utilisateur les paramètres d’échantillonnage de type vLLM. N’appliquez pas les valeurs par défaut de manière silencieuse. Utilisez une invite structurée, une question par champ, qui :

  1. Énumère tous les champs applicables avec leur type et leur valeur par défaut.
  2. Permette à l’utilisateur d’ignorer ou d’accepter n’importe quel champ pour adopter sa valeur par défaut — la saisie d’une valeur n’est jamais obligatoire.
  3. Récupère tous les champs en une seule fois.

Après l’invite, appliquez mot pour mot chaque valeur saisie par l’utilisateur et remplacez les champs ignorés par leur valeur par défaut. N’inventez pas de valeurs et ne les limitez pas en silence.

Liste des champs, valeurs par défaut et applicabilité par architecture : references/request.yaml → chat_completions_request_body (champs d'échantillonnage de base : max_tokens, top_p, temperature) et network_arch_constraints. (remplacements spécifiques à l'architecture et éléments supplémentaires tels que guidance/num_steps/seed/negative_prompt pour cosmos-predict2.5). Si un champ est marqué comme non pris en charge pour l’architecture active, ne demandez pas de le renseigner et ne l’incluez pas dans le corps de la requête.

6.2 Format de la requête

Envoyez un POST à {BASE_URL}/v1/chat/completions avec Content-Type: application/json et un délai d’expiration d’au moins 300 s. Le corps est compatible avec OpenAI (complétions de chat vLLM) ; voir references/request.yaml → chat_completions_request_body pour le schéma complet des champs et les formats des éléments de contenu (text / image_url / video_url), et code_examples pour des exemples Python et curl prêts à l'emploi.

Contraintes : seul le premier message de l’utilisateur est traité. Aucune valeur secrète n’est autorisée dans le corps des requêtes. Contraintes propres à chaque réseau (par exemple, cosmos-rl exige que chaque requête inclue une image ou une vidéo ; cosmos-rl rejette les data: les URI) sont décrites dans la section references/request.yaml → network_arch_constraints.

6.3 Traitement des réponses

Statut HTTP Signification Action
200 Succès — choices[0].message.content contient le texte généré Lire le résultat
202 Le serveur est toujours en cours d’initialisation ou le modèle est toujours en cours de chargement Réessayer après un certain délai
503 Échec de l'initialisation, échec du chargement du modèle ou modèle pas encore prêt Vérifier error.type: model_not_ready → réessayer ; initialization_error / model_load_error → abandonner et vérifier les journaux
400 Corps JSON manquant ou vide Corriger la requête
500 Exception non gérée lors de l'inférence Vérifier les journaux du conteneur

Pour les codes 202 et 503, le corps contient {"error": {"type": "", "message": ""}}. Voir container_response_shapes dans references/request.yaml pour les chaînes de caractères correspondant aux types d’erreurs.

Voir sur GitHub
---
name: tao-run-inference-service
description: Start, query, and stop a TAO inference microservice for a specific network architecture by delegating container execution to the appropriate platform skill.
license: Apache-2.0
---

# TAO Inference Microservice

## Instructions

**To start an inference service:**
1. Collect required inputs (Section 1) and resolve the container image (Section 2).
2. Build the job payload and inner command (Sections 3–4.1); use `references/code-templates.yaml` → `job_payload_builder`.
3. Read `skills/platform/<platform>/SKILL.md` and start the container (Section 4.2).
4. Write the service registry and poll readiness (Section 4.3); use `references/code-templates.yaml` → `registry_write.<platform>` and `readiness_check`.

**To send an inference request:**
1. Resolve which service receives the request per Section 6.0 (by `job_id`, by `network_arch`, or by explicit user choice when multiple services run — **never silently default to `"latest"` when more than one service exists**), then read the endpoint from `references/code-templates.yaml` → `request.registry_read` with the resolved `job_id`.
2. **Before building the request body, prompt the user for the vLLM-style sampling parameters (Section 6.1).** Present `max_tokens`, `top_p`, `temperature` (and any per-arch extras) with their defaults; let the user override or skip each one to accept the default. Never silently use defaults.
3. Build and send the body per Section 6.2; handle the response per Section 6.3.

**To stop a service:** Read `references/code-templates.yaml` → `stop.registry_read` to resolve the job_id, read `skills/platform/<platform>/SKILL.md`, then follow Section 5.

**Reference data** (schemas, mappings, valid values — no instructions):
- **`references/service.yaml`** — image mappings, valid `network_arch` names, job payload schema, env var names, secrets classification.
- **`references/request.yaml`** — endpoint definition, request field schema, response shapes, code examples.
- **`references/code-templates.yaml`** — Python templates for payload building, registry writes, readiness checks, and stop/request flows.

---

## Secrets rule (applies to every generated code block in this skill)

**Never ask the user to type a secret value into a prompt.** For every secret value:
1. Tell the user which environment variable to set (e.g. `export HF_TOKEN=...`).
2. Generate code that reads it with `os.environ["VAR_NAME"]` — never hard-code, interpolate, or prompt for the value.

**Secret env vars** (full list in `references/service.yaml` → `secrets_handling`):
`HF_TOKEN`, `WANDB_API_KEY`, `CLEARML_API_ACCESS_KEY`, `CLEARML_API_SECRET_KEY`, `TAO_API_KEY`, `TAO_USER_KEY`.

**Safe to collect in the prompt:** `network_arch`, `model_path`, `num_gpus`, prompt text, `WANDB_*` config URLs, `CLEARML_*_HOST` URLs.

---

## 1. What to collect from the user

| Input | Role |
|--------|------|
| **`network_arch`** | Chooses container image, the per-arch inner command shape (`references/service.yaml` → `container_commands.<network_arch>`), and `neural_network_name` in the job JSON when applicable. Must match a basename in `valid_network_arch_config_basenames` in `references/service.yaml` (e.g. `cosmos-rl`, `cosmos-predict2.5`). |
| **`model_path`** | The trained model checkpoint. Valid forms: `hf_model://<org>/<model>` (HuggingFace Hub — set `HF_TOKEN` for gated models) or a local container filesystem path. Cloud URIs (`s3://`, `gs://`, `az://`) are NOT supported — the inference service has no cloud-storage dependency. Always ask the user; never substitute a placeholder. See `references/service.yaml` → `model_path_protocols`. |
| **`platform`** | Compute platform: `local-docker`, `brev`, `slurm`, or `kubernetes`. |
| **`num_gpus`** | Defaults to **1**; minimum **1** for inference. |

---

## 2. Image resolution

Each `network_arch` has a sidecar config file named `{network_arch}.config.json`. Resolve the container image as follows:

1. Read `{network_arch}.config.json` and take `api_params.image` (e.g. `COSMOS_RL`). This is a key into `docker_image_defaults.mapping` in `references/service.yaml`.
2. Look up that key in the mapping. If the host env var `IMAGE_<KEY>` is set (e.g. `IMAGE_COSMOS_RL`), it overrides the mapped default.
3. The mapped value is normally a dotted key into the repo-root `versions.yaml` manifest (e.g. `tao_toolkit.cosmos_rl`). Resolve it to a concrete `nvcr.io/...` image URI by looking up `versions.yaml` → `images.<group>.<name>`. Absolute URIs pass through unchanged, so an `IMAGE_<KEY>` env-var override that contains a full URI still works. The Python helper for this lives in `references/code-templates.yaml`.
4. If the config file is missing or `api_params.image` is empty, fall back to the `COSMOS_RL` key.

The config file also has `spec_params.inference.model_path` which drives **folder vs file** path semantics: if the value contains the substring `folder`, the container treats the path as a directory.

---

## 3. Environment variables (no callbacks)

Set these in `env_payload` before encoding `env_json`. Do **not** set `TAO_LOGGING_SERVER_URL` or `TAO_ADMIN_KEY`.

**`TAO_EXECUTION_BACKEND`** — must match the platform:

| Platform | `TAO_EXECUTION_BACKEND` value |
|----------|-------------------------------|
| local-docker | `local-docker` |
| brev | `local-docker` |
| slurm | `slurm` |
| kubernetes | `local-k8s` |

**`CLOUD_BASED`** — always `"False"` for this skill (disables callback posting to `TAO_LOGGING_SERVER_URL`).

**GPU env vars** — only needed when the platform skill does not handle GPU injection automatically:
- Tegra / Jetson: `--runtime=nvidia` with `NVIDIA_DRIVER_CAPABILITIES=all` and `NVIDIA_VISIBLE_DEVICES=<ids>`.
- Standard x86 + nvidia-container-toolkit: use Docker `device_requests`. The platform skill handles this.

---

## 4. Executing across platforms

The job payload and inner command (Sections 1–3) are **platform-agnostic**. For each platform, read **`skills/platform/<name>/SKILL.md`** for preflight checks and credentials **before** generating any execution code.

### 4.1 Build the inner command (per arch)

The inner-command shape is **per `network_arch`** — there is no uniform template. Look up the per-arch entry in `references/service.yaml` → `container_commands.<network_arch>`; if not present, the arch is unsupported — stop and ask. Pick the matching sub-block in `references/code-templates.yaml` → `job_payload_builder.<network_arch>`. Prefix the command with `umask 0 &&` and keep it **identical across platforms** (local-docker, brev, slurm, kubernetes).

Common across arches:

- `job_id`: fresh `uuid.uuid4()` — becomes the container name and registry key.
- `image`: resolve per Section 2.
- Secrets (`access_key`, `secret_key`, `HF_TOKEN`, etc.) are read from env vars at runtime — never hard-code, never log or print.

Arch-specific notes (full details in `references/service.yaml` → `container_commands`):

- **`cosmos-rl`** — single `--job '<JOB_JSON>' --docker_env_vars '<ENV_JSON>'` blob; `json.dumps(...)` + `shlex.quote(...)`. `env_payload` carries `TAO_EXECUTION_BACKEND` (per Section 3 table), `TAO_API_JOB_ID`, `CLOUD_BASED=False`. The inference service has no cloud-storage dependency; `HF_TOKEN` is the only cred env var that ever applies (for gated HuggingFace models).
- **`cosmos-predict2.5`** — flag-style `cosmos_predict inference_microservice start ... --port 8080` (no `setup.` prefix; uses `tyro.conf.OmitArgPrefixes`). `--job`/`--docker_env_vars` are **not** accepted. Translate `model_path` to `--checkpoint-path` (local path) or `--model <registered_key>` (`hf_model://`); cloud URIs are rejected. The only cred env var that ever applies is `HF_TOKEN` for gated HuggingFace models. Per-request params (prompt, inference_type, num_output_frames, guidance, seed, num_steps, negative_prompt) go in the request body, not at startup. `TAO_EXECUTION_BACKEND`/`TAO_API_JOB_ID`/`CLOUD_BASED` are unused and may be omitted.

### 4.2 Delegate execution to the platform skill

Read **`skills/platform/<platform>/SKILL.md`** and follow it to start the container.

**Base parameters (all platforms):**

| Parameter | Value |
|-----------|-------|
| `image` | resolved container image (Section 2) |
| `command` | `inner` — the shell string built in Section 4.1 |
| `gpu_count` | `num_gpus` |
| `env_vars` | `env_payload` |
| job / container name | `job_id` — must equal the UUID from 4.1 so the registry can reference it |
| `host_port` *(local-docker, brev)* | host-side port to bind to container port 8080. Default `8080`, but **must be unique per concurrent service** — see the port-allocation rule below. |

**Platform-specific additional inputs:**

| Platform | Additional inputs |
|----------|------------------|
| **local-docker** | None beyond base |
| **brev** | `instance_id` (optional — reuse an existing instance); on multi-credential / multi-workspace accounts also `cloud_cred_id` and `workspace_group_id` for first-create — see `skills/platform/tao-run-on-brev/SKILL.md` |
| **slurm** | `partition` and `account` — check `SLURM_PARTITION`/`SLURM_ACCOUNT` env vars; ask user if unset |
| **kubernetes** | `namespace` (default: `default`); `image_pull_secret` (required for `nvcr.io` images) |

**Port binding (local-docker and brev):** use **direct docker run** (not DockerSDK) so that `-p <host_port>:8080` can be passed and the container name equals `job_id` exactly.

**Port allocation rule (local-docker and brev, REQUIRED for concurrent services):** Before starting a service, read the registry (`/tmp/tao-inf-ms-state.json`) and collect the set of `host_port` values from every existing entry on the same platform (and, for brev, the same `instance_id`). Pick the **lowest free port starting from 8080** that is not in that set — e.g. `host_port = next(p for p in range(8080, 8200) if p not in used_ports)`. The default `8080` only applies when no other service is running. This is what makes "start 3 services, each reachable at a distinct `host_url`" work; without it, services 2 and 3 fail with `bind: address already in use`. SLURM and kubernetes get distinct endpoints from their own platform mechanisms and do not need this step.

### 4.3 After start: service registry and endpoint

Write the service registry immediately after the platform confirms the container is running. The registry (`/tmp/tao-inf-ms-state.json`) is keyed by `job_id`; `"latest"` always points to the most recently started service.

See `references/code-templates.yaml` → `registry_write.<platform>` for the Python template.

| Platform | `host_url` | `platform_job_id` | Extra step before writing |
|----------|-----------|-------------------|--------------------------|
| **local-docker** | `http://localhost:{host_port}` | — | None |
| **brev** | `http://{brev_ip}:{host_port}` | — | `brev ls` → get instance IP (`localhost` is invalid on remote VM) |
| **slurm** | `http://localhost:{host_port}` | SLURM scheduler job ID | Wait until Running; SSH port-forward `localhost:{host_port}→{node}:8080` |
| **kubernetes** | `http://{external_ip}:8080` | k8s job name | `kubectl expose job … --type=LoadBalancer`; wait for external IP |

After writing the registry, print the job_id and URL:

```python
print(f"Inference service started.")
print(f"  Job ID : {job_id}")
print(f"  Arch   : {network_arch}")
print(f"  URL    : {state[job_id]['host_url']}/v1/chat/completions")
print(f"Use this Job ID to send requests or stop the service.")
```

Then poll for readiness — see `references/code-templates.yaml` → `readiness_check`. The container loads the model in the background; do not send requests before it returns 200.

---

## 5. Stopping the inference service

Ask the user for the `job_id` to stop. If they don't provide one, default to `state["latest"]` and confirm which job_id is being stopped. Read the registry using `references/code-templates.yaml` → `stop.registry_read`, then read **`skills/platform/<platform>/SKILL.md`** and use its cancellation / stop mechanism.

| Platform | Identifier to pass | Extra cleanup |
|----------|--------------------|---------------|
| **local-docker** | `job_id_to_stop` — container name | None |
| **brev** | `job_id_to_stop` — container name | None |
| **slurm** | `entry["platform_job_id"]` — SLURM job ID | `pkill -f "ssh.*-L.*{entry['host_port']}"` |
| **kubernetes** | `entry["platform_job_id"]` — k8s job name | `kubectl delete svc {entry["platform_job_id"]} -n <namespace>` |

where `entry = state[job_id_to_stop]`. After stopping, clean up the registry: `references/code-templates.yaml` → `stop.registry_cleanup`.

---

## 6. Sending inference requests

### 6.0 Resolve which service receives this request (REQUIRED)

Each request must be routed to the **specific** service that runs the matching model. Routing happens by `job_id` — the registry stores `network_arch` per entry, so you can resolve a target by arch when the user names a model instead of a `job_id`. Apply these rules in order:

1. **User provided an explicit `job_id`** → use it. Verify it exists in `state`.
2. **User named a `network_arch`** (e.g. "send this to the cosmos-rl service") → look up matching entries: `candidates = [j for j, e in state.items() if j != "latest" and isinstance(e, dict) and e["network_arch"] == arch]`.
   - Exactly one match → use it.
   - Multiple matches → **prompt the user** with the candidate `job_id`s and their `started_at`; do not auto-pick.
   - No match → stop and tell the user no service for that arch is running.
3. **No `job_id` and no `network_arch`** → count non-`"latest"` entries in `state`:
   - Exactly one running service → use it.
   - Two or more → **do not silently default to `state["latest"]`**. Prompt the user with the full list (`job_id`, `network_arch`, `host_url`) and require an explicit choice. The `"latest"` pointer is a convenience for single-service workflows, not a routing fallback when multiple services coexist.
   - Zero → stop and tell the user to start a service first.

After resolving, read the endpoint from the registry (`references/code-templates.yaml` → `request.registry_read`), passing the resolved `job_id` as `user_provided_job_id`. Confirm to the user: "Sending to job_id=… arch=… url=…". If the service may still be loading, poll readiness first (`references/code-templates.yaml` → `readiness_check`).

**Cross-check before sending:** if the user-supplied request body contains arch-specific fields (e.g. `guidance` / `num_steps` / `seed` / `negative_prompt` → cosmos-predict2.5; required `image_url`/`video_url` content items → cosmos-rl), verify they are consistent with `state[job_id]["network_arch"]`. On mismatch, stop and ask — sending a cosmos-predict2.5 body to a cosmos-rl service will fail at the container with a 4xx/5xx that is harder to diagnose than catching it here.

### 6.1 Sampling parameters — REQUIRED user prompt before each request

Before constructing the request body, you **MUST** explicitly prompt the user for the vLLM-style sampling parameters. Do **not** silently apply defaults. Use a structured prompt, one question per field, that:

1. Lists every applicable field with its **type** and **default value**.
2. Lets the user skip / accept any field to take that field's default — entering a value is never required.
3. Collects all fields in one round.

After the prompt, apply each user-entered value verbatim and substitute the default for any skipped field. Do not invent values or silently clamp.

**Field list, defaults, and per-arch applicability:** `references/request.yaml` → `chat_completions_request_body` (base sampling fields: `max_tokens`, `top_p`, `temperature`) and `network_arch_constraints.<network_arch>` (per-arch overrides and extras such as `guidance`/`num_steps`/`seed`/`negative_prompt` for `cosmos-predict2.5`). If a field is marked unsupported for the active arch, do **not** prompt for it and do **not** include it in the body.

### 6.2 Request format

Send a `POST` to `{BASE_URL}/v1/chat/completions` with `Content-Type: application/json` and a timeout of **at least 300 s**. The body is OpenAI-compatible (vLLM chat completions); see `references/request.yaml` → `chat_completions_request_body` for the full field schema and content-item shapes (text / image_url / video_url), and `code_examples` for ready-to-run Python and curl samples.

**Constraints:** only the first user message is processed. No secret values in request bodies. **Per-network constraints** (e.g. cosmos-rl requires every request to include an image or video; cosmos-rl rejects `data:` URIs) are in `references/request.yaml` → `network_arch_constraints`.

### 6.3 Response handling

| HTTP status | Meaning | Action |
|-------------|---------|--------|
| **200** | Success — `choices[0].message.content` has the generated text | Read result |
| **202** | Server still initializing or model still loading | Retry after a delay |
| **503** | Initialization failed, model load failed, **or model not yet ready** | Inspect `error.type`: `model_not_ready` → retry; `initialization_error` / `model_load_error` → give up and check logs |
| **400** | Missing or empty JSON body | Fix request |
| **500** | Unhandled exception during inference | Check container logs |

For 202 and 503, the body contains `{"error": {"type": "<error_type>", "message": "<reason>"}}`. See `container_response_shapes` in `references/request.yaml` for error type strings.

Installer tao-run-inference-service

Téléchargez et décompressez les fichiers de compétences dans votre répertoire .claude/skills/.

Télécharger le ZIP

Clonez le dépôt et copiez les fichiers de compétence dans votre projet.

git clone https://github.com/NVIDIA/skills/tree/main/skills/tao-run-inference-service # Copy SKILL.md to your .claude/skills/ directory

Copier Copier
Configuration rapide: Copiez le dossier de la compétence dans .claude/skills/ Claude détectera et utilisera automatiquement cette compétence
Dépôt NVIDIA/skills

Compétences similaires

klingai-upgrade-migration
Heure mise à jour 3 juillet 2026
Verification &amp; Quality Assurance
Heure mise à jour 29 juin 2026
base44-cli
Heure mise à jour 29 juin 2026
Railway CLI Management
Heure mise à jour 2 juillet 2026
OR