Dynamo With Streaming#
Use reasoner_with_streaming.yaml for Chat
Completions plus persistent video sessions over REST on NVIDIA Dynamo. The frontend and workers
use the same Cosmos3 NIM image, with different launch commands. The frontend
runs the bundled streaming adapter. Workers run the NIM server with streaming
enabled.
WebSocket is not supported in Dynamo mode. The
WS /v1/streaming/wsendpoint is unavailable on both the shared frontend and worker NIM endpoints. Use the REST session API below.
First complete the prerequisites and
namespace and cache setup.
Complete the shared model-download step
before starting workers. If reusing a cache, set spec.pvcs[0].name and
ReasonerWorker.volumeMounts[0].name in the streaming YAML, plus the download
Job’s PVC claimName, to <existing-cache-pvc>. Keep create: false.
Replace all <angle-bracket> values before applying.
This manifest adapts the regular deployment’s v1alpha1 services schema,
resources, shared cache, and operator-managed worker health probes. Validate
the adapter and session requests below on your deployment.
Set the NIM Image#
reasoner_with_streaming.yaml uses
nvcr.io/nim/nvidia/cosmos3:2.1.0 for both the frontend and the workers. If you change it,
change both occurrences and the download Job together. The image must include
/opt/nim/streaming/dynamo/.
The frontend runs /opt/nim/.venv/bin/python -m streaming.dynamo.frontend
from /opt/nim. It serves streaming configuration and REST sessions. It
forwards ordinary Chat Completions to Dynamo without loading a model or
requesting a GPU. The stock python3 -m dynamo.frontend command does not
expose the session routes.
Workers start /opt/nim/start_server.sh: NIM_DYNAMO_WORKER=true and
NIM_ENABLE_STREAMING=1 select the streaming worker and its private frontend.
The worker’s DYN_NAMESPACE_PREFIX comes from the operator’s pod label,
allowing the private frontend to discover models in suffixed worker
namespaces. The
streaming pool still uses the separate NIM_DYNAMO_STREAMING_ENDPOINT setting.
Refer to Dynamo Worker Configuration
and Dynamo Streaming Configuration
for environment variables, defaults, precedence, and timeouts.
The tested image bundled Dynamo 1.5.0.dev20260902. The graph’s operator
runtime override remains 1.5.0. Verify compatibility for your
image/operator pairing.
Both manifests use the same Bash launch style (set -euo pipefail followed
by exec), explicit Kubernetes discovery, scheduling tolerations, and shared
worker environment values. The streaming manifest differs from the regular
one in the following ways:
Uses the NIM image and its Python environment for the frontend in place of the regular pinned Dynamo image
Changes the frontend module
Enables
NIM_ENABLE_STREAMINGDisables asynchronous scheduling
Adds the shared streaming endpoint to the frontend and workers
The YAML sets synchronous scheduling explicitly and uses the same
NIM_DYNAMO_STREAMING_ENDPOINT on frontend and workers. Keep that value
identical. Use a distinct endpoint for each separate model pool.
Use Edge Reasoner#
Edge supports the same REST session API in BF16 and FP8. In both
model-download.yaml and the streaming manifest’s ReasonerWorker, set:
- name: NIM_MODEL_VARIANT
value: edge
- name: NIM_PRECISION
value: fp8 # or bf16
- name: NIM_USE_DFLASH
value: '0'
- name: NIM_STREAMING_SPEC_METHOD
value: none
Replace the existing variant and precision entries rather than adding
duplicates. Keep the image, cache, and GPU selectors aligned between the Job and
workers. Edge does not support DFlash in this version. Set the streaming method
explicitly to none, because streaming defaults to DFlash and Edge has no
draft. The shared frontend needs no model-specific change. The client discovers
the Edge model from /v1/streaming/config. Use a separate streaming endpoint if
running Edge alongside a Nano or Super pool.
Deploy and Check#
Run from .agents/skills/cookbooks/cosmos3/nim/dynamo. If your image uses another registry,
configure its credentials and replace the imagePullSecrets entries in both
services and the download Job with <your-image-pull-secret>.
kubectl -n "$NAMESPACE" apply -f reasoner_with_streaming.yaml
kubectl -n "$NAMESPACE" get pods \
-l nvidia.com/dynamo-graph-deployment-name=cosmos3-reasoner -w
Both YAML files define cosmos3-reasoner: apply only the chosen variant.
Applying the other variant updates that deployment and replaces its pods.
Existing streaming sessions are lost.
Follow the shared
readiness and port-forward checks,
including setting NIM_URL. Check frontend and worker logs for
[Dynamo streaming] messages confirming that the HTTP adapter and worker REST
handlers attached. Chat Completions use the same
request example. For the Python Reasoner,
Responses, and profile-inspection scripts, use the
worker NIM URL commands.
Stream Video Frames Over REST#
Use the forwarded frontend at NIM_URL. Create a session, send one JPEG or
PNG frame at a time, then delete the session. This Dynamo path supports raw
image bodies and JSON image_b64. GET /v1/streaming/config returns the
selected streaming worker’s model and default retention/sampling settings:
curl -fsS "$NIM_URL/v1/streaming/config" | python3 -m json.tool
Use this config’s model value when specifying a model for session creation.
It can be a local model path rather than the public alias in /v1/models.
The shared frontend exposes Dynamo’s /health and /v1/models. NIM readiness,
metadata, and manifest routes belong to each worker’s NIM endpoint. A 404 from
/v1/health/ready or /v1/metadata on the frontend does not establish that
streaming is unavailable.
From .agents/skills/cookbooks/cosmos3/nim, run the client in the
pinned client environment:
uv run python examples/streaming.py --backend dynamo --transport rest \
--nim-url "$NIM_URL" --fps 1 --max-frames 3
The client uses only the shared frontend. It checks for a registered generate
worker at /health, a served model at /v1/models, and streaming configuration
at /v1/streaming/config. It does not call worker metadata or manifest
endpoints and needs no worker URLs or port forwards. Missing runtime metadata
fields in older worker images do not block this mode. The client sends
JSON/base64 frames and requires REST in Dynamo mode.
These checks establish frontend discovery and streaming configuration, not the readiness or selected profile of every worker. Operators can inspect individual workers using the deployment checks.
After the readiness checks above, try one local JPEG frame:
(
set -euo pipefail
created=$(curl -fsS "$NIM_URL/v1/streaming/sessions" \
-H 'Content-Type: application/json' \
-d '{"system_prompt":"Describe each frame in one short sentence.","question":"What is happening?","fps":1,"sampling":{"max_tokens":48}}')
session_id=$(printf '%s' "$created" | python3 -c \
'import json,sys; print(json.load(sys.stdin)["session_id"])')
trap 'curl -fsS -X DELETE "$NIM_URL/v1/streaming/sessions/$session_id"' EXIT
curl -fsS "$NIM_URL/v1/streaming/sessions/$session_id/frame" \
-H 'Content-Type: image/jpeg' --data-binary '@<path-to-frame.jpg>'
)
For video, decode frames on the client and wait for each reply before sending
the next. Keep the returned session_id unchanged: frame uploads and deletion
route to its owner even through a different frontend replica. This ownership
does not depend on the Chat Completions affinity header or its 300-second TTL.
Refer to Streaming for session fields
and streaming settings for
limits. The REST idle timeout defaults to 120 seconds.
A session-route 404 can indicate an old frontend/worker image or streaming disabled on the worker. A 404 can also mean that an existing session expired. A 503 can mean workers are loading, capacity is full, or the owning worker is unavailable. Sessions do not migrate after worker loss, so create a new one. Do not automatically retry a frame after a timeout.
Other Routes#
The adapter forwards ordinary HTTP requests to the bundled Dynamo frontend,
including POST /v1/responses. Use store=false for Responses creation, as in
the Dynamo request example. Creation does
not establish support for stored-response retrieval, cancellation, background
responses, or previous_response_id. Those require separate backend storage
support. NIM’s NIM_DISABLE_RESPONSES_ROUTE controls the worker proxy’s route,
not the shared frontend’s routes.
Scale or Stop#
Set spec.services.ReasonerWorker.replicas to <worker-count> in the streaming
YAML and apply that file again. Each replica needs the shared
resources and cache. Scaling down or
replacing workers loses their active sessions because session state does not
migrate. Set the count to 0 to release the GPUs while retaining the frontend
and cache. Restore a positive count and wait for readiness before sending more
requests.
To stop serving while keeping the cache PVC and secrets:
kubectl -n "$NAMESPACE" delete -f reasoner_with_streaming.yaml
Deleting the graph interrupts active requests and loses active sessions. Run the delete command only when you intend to stop serving. Stop the port-forward with Ctrl+C.