Skip to navigation

Cosmos3 Super Reasoner

Compare round-robin routing with per-video session affinity on eight H200 GPUs.

View as MarkdownOpen in Claude

Deploy NVIDIA Cosmos3 Super Reasoner as eight FP8 aggregated workers. Two matched targets isolate routing behavior: pure round robin and round robin with a 300-second per-video session-affinity TTL.

Each worker starts through the Cosmos3 NIM entrypoint. NIM profile selection reads NIM_MODEL_TYPE=reasoner, NIM_MODEL_VARIANT=super, and NIM_PRECISION=fp8, picks the matching model profile for the detected GPU, and then launches dynamo.vllm because NIM_DYNAMO_WORKER=true. The FP8 Super model fits on one H200, so every GPU runs an independent replica. Each replica keeps its own multimodal processor cache, which makes routing the only difference between the two targets: it decides whether a video’s questions reach a worker that has already processed that video.

Choose a routing profile

Routing
Hardware 8x H200, one GPU per workerRuntime vLLM through Cosmos3 NIM 2.1.0Precision FP8Topology AggregatedRouting Round robin, no session-affinity flag
Hardware 8x H200, one GPU per workerRuntime vLLM through Cosmos3 NIM 2.1.0Precision FP8Topology AggregatedRouting Round robin with a 300-second per-video affinity TTL

In a matched eight-H200 benchmark, per-video session affinity improved throughput at every tested concurrency (22% to 41% more requests/s) and kept TTFT p50 at 44 ms. See Measured performance.

Prerequisites

  • A Kubernetes cluster with the Dynamo platform installed.
  • Eight available H200 GPUs and a ReadWriteMany model-cache volume.
  • An NGC API key with access to the Cosmos3 model artifacts.

Run commands from the repository root:

export CONTEXT="<your-kubernetes-context>"
export NAMESPACE="<your-namespace>"
export NGC_API_KEY="<your-ngc-api-key>"
kubectl --context "$CONTEXT" create namespace "$NAMESPACE"
: "${NGC_API_KEY:?Set NGC_API_KEY to your NGC API key}"
kubectl --context "$CONTEXT" -n "$NAMESPACE" create secret generic ngc-api-secret \
--from-literal=NGC_API_KEY="$NGC_API_KEY"

Cluster-specific settings

The manifests contain no credentials or node placement, and most clusters need no changes. Add a private Kustomize overlay only when you see one of these symptoms after deploying:

SymptomMissing setting
Worker pods stuck in ImagePullBackOff with unauthorized from a private registry mirrorImage-pull Secret for the mirror
Worker logs show NGC authentication errors during NIM startupNGC_API_KEY in the Worker
Worker pods Pending with Insufficient nvidia.com/gpu, or scheduled on non-H200 nodesH200 node selector

Both images are public on nvcr.io, so an image-pull Secret is only needed when the cluster pulls through a private registry mirror. In that case, create it for the mirror:

kubectl --context "$CONTEXT" -n "$NAMESPACE" create secret docker-registry \
registry-pull-secret --docker-server=<your-registry-mirror> \
--docker-username=<user> --docker-password=<password>

Copy the profile’s deploy.yaml into a directory outside the repository and add a kustomization.yaml next to it. The patches apply only to the Worker (spec.components[1]); keep only the ones you need:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deploy.yaml
patches:
- target:
group: nvidia.com
kind: DynamoGraphDeployment
patch: |-
- op: add
path: /spec/components/1/podTemplate/spec/imagePullSecrets
value:
- name: registry-pull-secret
- op: add
path: /spec/components/1/podTemplate/spec/containers/0/env/-
value:
name: NGC_API_KEY
valueFrom:
secretKeyRef:
name: ngc-api-secret
key: NGC_API_KEY
- op: add
path: /spec/components/1/podTemplate/spec/nodeSelector
value:
nvidia.com/gpu.product: NVIDIA-H200

Use your cluster’s GPU node label if it differs. In the deploy step, apply the overlay with apply -k <overlay-dir> instead of apply -f .../deploy.yaml.

Populate the model cache

The model-download Job and serving workers use the same pinned image and shared cache. If shared-model-cache already exists, keep it; PVC storage class and access mode fields are immutable.

For a new cache, first replace the placeholder storageClassName in model-cache.yaml with a ReadWriteMany-capable storage class. Then run:

kubectl --context "$CONTEXT" -n "$NAMESPACE" apply \
-f recipes/cosmos3-super-reasoner/model-cache/model-cache.yaml
kubectl --context "$CONTEXT" -n "$NAMESPACE" delete job \
cosmos3-super-reasoner-model-download --ignore-not-found
kubectl --context "$CONTEXT" -n "$NAMESPACE" apply \
-f recipes/cosmos3-super-reasoner/model-cache/model-download.yaml
kubectl --context "$CONTEXT" -n "$NAMESPACE" wait \
--for=condition=Complete job/cosmos3-super-reasoner-model-download \
--timeout=7200s

Deploy

Choose one profile and deploy it. If you created an overlay in Cluster-specific settings, use apply -k instead of apply -f:

export PROFILE=round-robin # or: session-affinity
export DGD="cosmos3-super-reasoner-agg-h200-${PROFILE}"
kubectl --context "$CONTEXT" -n "$NAMESPACE" apply \
-f "recipes/cosmos3-super-reasoner/vllm/agg-h200-${PROFILE}/deploy.yaml"
kubectl --context "$CONTEXT" -n "$NAMESPACE" wait \
--for=condition=Ready "dynamographdeployment/${DGD}" \
--timeout=3600s

The round-robin manifest does not set a session-affinity flag. The session-affinity manifest sets a 300-second TTL.

Verify

Forward the frontend Service. The command keeps running:

kubectl --context "$CONTEXT" -n "$NAMESPACE" port-forward \
"service/${DGD}-frontend" 8000:8000

In a second terminal, check that it serves the model:

curl -fsS http://localhost:8000/v1/models

The response lists nvidia/cosmos3-super-reasoner.

Smoke test

The smoke test generates one synthetic 30-second video with FFmpeg, exposes it to the workers through an in-cluster Service, and validates a non-empty multimodal completion:

kubectl --context "$CONTEXT" -n "$NAMESPACE" apply \
-f "recipes/cosmos3-super-reasoner/perf/profiles/${PROFILE}.yaml"
kubectl --context "$CONTEXT" -n "$NAMESPACE" delete job \
cosmos3-super-reasoner-smoke --ignore-not-found
kubectl --context "$CONTEXT" -n "$NAMESPACE" apply \
-f recipes/cosmos3-super-reasoner/perf/smoke.yaml
kubectl --context "$CONTEXT" -n "$NAMESPACE" logs -f \
job/cosmos3-super-reasoner-smoke

The final log line starts with Smoke test passed and includes the model’s description of the video.

Benchmark

The AIPerf workflow generates 200 synthetic 30-second, 2 FPS videos with FFmpeg before the measured sweep, so no external dataset or credentials are needed. It then sends 20 questions for each video at every concurrency level: 4,000 requests with exactly 128 output tokens each. Every request puts the video before the question, so all questions about one video share a prompt prefix. For session affinity, AIPerf sends one X-Dynamo-Session-ID value, generated per video, on all 20 questions about that video; pure round robin sends no session-affinity header.

kubectl --context "$CONTEXT" -n "$NAMESPACE" apply \
-f "recipes/cosmos3-super-reasoner/perf/profiles/${PROFILE}.yaml"
kubectl --context "$CONTEXT" -n "$NAMESPACE" delete job \
cosmos3-super-reasoner-perf --ignore-not-found
kubectl --context "$CONTEXT" -n "$NAMESPACE" apply \
-f recipes/cosmos3-super-reasoner/perf/perf.yaml
kubectl --context "$CONTEXT" -n "$NAMESPACE" logs -f \
job/cosmos3-super-reasoner-perf

On eight H200 workers, the full sweep takes about 2.5 hours for round robin and about 2 hours for session affinity. Each concurrency level prints its AIPerf summary to the log when it finishes.

Measured performance

These are single-run AIPerf measurements from the eight-H200 deployments in this recipe. Each row represents 4,000 successful requests with no errors. Both profiles used the same 200 synthetic videos, 20 questions per video, and 128 output tokens.

ConcurrencyRound-robin req/sAffinity req/sThroughput gainRound-robin TTFT p50Affinity TTFT p50
10.941.16+23%45 ms44 ms
21.902.36+24%45 ms44 ms
43.834.68+22%45 ms44 ms
87.389.35+27%48 ms44 ms
1612.6517.78+41%124 ms44 ms

At concurrency 16, session affinity produced 2,276 output tokens/s versus 1,619 for round robin. Request-latency p50 fell from 1,140 ms to 919 ms, and TTFT p90 fell from 757 ms to 47 ms. Each worker processes a video the first time it sees it and reuses it for later questions. Round robin spreads a video’s 20 questions across up to eight workers, so the video is processed on each of them, while session affinity processes it once.

The complete TTFT, request-latency, ITL, and throughput sweep is available in the benchmark README.

Source