Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
152 commits
Select commit Hold shift + click to select a range
358f988
components: resolve the relu2 (squared ReLU) activation
merceod Sep 17, 2026
c96772a
components: dense tensor-parallel MLP (ParallelMLP) for Nemotron-styl…
merceod Sep 17, 2026
09ad824
components: export ParallelMLP and ParallelGatedMLPUnfused
merceod Sep 17, 2026
ba901f9
test: CPU checks for ParallelMLP and relu2
merceod Sep 17, 2026
c0d4260
cosmos3: parse the Edge backbone family, reasoner and serving default…
merceod Sep 17, 2026
55cd274
cosmos3: name the reasoner walks
merceod Sep 17, 2026
d78df58
cosmos3: Edge backbone family in the DiT (relu2 MLP, Nemotron norms, …
merceod Sep 17, 2026
0b86cd7
cosmos3: keep lm_head for reasoner backbones and load the Edge vision…
merceod Sep 17, 2026
67111ea
cosmos3: Edge reasoner vision tower and patch-merger projector
merceod Sep 17, 2026
e086376
cosmos3: reasoner media preprocessing, placeholder expansion and mRoP…
merceod Sep 17, 2026
32bceaa
cosmos3: reasoner and vision-encoder nodes, native flow schedule
merceod Sep 17, 2026
e2482fe
cosmos3: serve the Edge reasoner (chat walks) next to the generator
merceod Sep 17, 2026
39d9816
registry: cosmos3_edge, cosmos3_edge_droid and the 4-step Super variants
merceod Sep 17, 2026
6676acc
cli: default configs for the Cosmos3-Edge and 4-step Super keys
merceod Sep 17, 2026
5948fcd
api: Cosmos3-Edge adapter with chat (reasoner) support
merceod Sep 17, 2026
b49c90c
configs: Cosmos3-Edge single-GPU deployment
merceod Sep 17, 2026
b1b12a3
configs: Cosmos3-Edge-Policy-DROID deployment
merceod Sep 17, 2026
621b775
configs: Cosmos3-Super-Text2Image-4Step TP2 deployment
merceod Sep 17, 2026
ed79c74
configs: Cosmos3-Super-Image2Video-4Step TP2 deployment
merceod Sep 17, 2026
bbc58c3
docs: Cosmos3-Edge and 4-step Super rows in the model table
merceod Sep 17, 2026
357fb82
test: Cosmos3-Edge backbone, cached-prefill parity, reasoner prompt p…
merceod Sep 17, 2026
6237d4d
cosmos3: reasoner serving knobs (enable_reasoner, reasoner_temperature)
merceod Sep 17, 2026
d88ac2d
cosmos3 tests: fused pipeline follows the native flow schedule
merceod Sep 17, 2026
c155e38
test: Cosmos3-Edge serving surface in dummy mode
merceod Sep 17, 2026
9ad6193
cosmos3: resize reasoner media through the torchvision v2 functional …
merceod Sep 17, 2026
efe9a88
test: Cosmos3-Edge reasoner CPU parity against the HF reference dump
merceod Sep 17, 2026
76b1e36
test: Cosmos3-Edge DiT step CPU parity against the diffusers referenc…
merceod Sep 17, 2026
0645950
test: Cosmos3-Edge t2i/t2v/i2v GPU parity harness against diffusers d…
merceod Sep 17, 2026
1431714
cosmos3: PyAV fallback for video decoding when torchcodec has no FFmpeg
merceod Sep 17, 2026
e52d71a
cosmos3: conditioning-frame recipes (stretch vs aspect-crop) for imag…
merceod Sep 17, 2026
ac3a891
cosmos3: conditioning_resize knob
merceod Sep 17, 2026
51685c8
cosmos3: vae_encoder node applies the deployment's i2v conditioning r…
merceod Sep 17, 2026
6e4eda7
cosmos3 tests: fused pipeline applies the i2v conditioning recipe
merceod Sep 17, 2026
86f23f3
configs: Edge i2v conditioning follows the diffusers 0.40 recipe
merceod Sep 17, 2026
84732f5
configs: Edge-DROID i2v conditioning follows the diffusers 0.40 recipe
merceod Sep 17, 2026
d042e1b
test: conditioning-frame recipes
merceod Sep 17, 2026
6583dae
benchmark: Cosmos3-Edge reasoner TTFT / decode tok/s chat client
merceod Sep 17, 2026
ef10ca4
test: served reasoner submodule path (packed requests) against the HF…
merceod Sep 17, 2026
415c5eb
test: cache-once Edge DiT path against the diffusers reference
merceod Sep 17, 2026
72d2801
benchmark: Cosmos3-Edge reproduction script (M* vs vLLM-Omni vs vLLM)
merceod Sep 17, 2026
474b4d7
test: Edge reasoner video prompt spans, grid and positions
merceod Sep 17, 2026
0189e0d
engine: windowed generation schedule and KV lifecycle session (from #…
merceod Sep 17, 2026
5ee7257
test: window schedule and KV session (from #198)
merceod Sep 17, 2026
2f7712e
kv: protect_prefix / release_oldest on cache streams (partial release…
merceod Sep 17, 2026
a804dd1
test: partial KV release on the pool
merceod Sep 17, 2026
e8513e2
kv: apply a per-stream RetentionPolicy at commit
merceod Sep 17, 2026
fb4d55a
windowing: bind the schedule as the stream's retention policy
merceod Sep 17, 2026
3e51b8e
Test commit-time retention on the KV pool
merceod Sep 17, 2026
1926976
Test the windowed session's retention binding
merceod Sep 17, 2026
c452854
Add websockets dependency for the WebSocket route
merceod Sep 17, 2026
65e76c0
Add /generate/ws: pipelined /generate over one WebSocket
merceod Sep 17, 2026
0a10417
Test /generate/ws framing, pipelining, errors and cancel
merceod Sep 17, 2026
fd1d3e9
Add openpi-style action loop client for /generate/ws
merceod Sep 17, 2026
6bd147e
Document the /generate/ws WebSocket route
merceod Sep 17, 2026
d82878f
cosmos3: windowed AR video walk and partition names
merceod Sep 17, 2026
e52681c
cosmos3: windowed video serving knobs
merceod Sep 17, 2026
b993130
cosmos3: absolute frame offset for window statics
merceod Sep 17, 2026
cd72224
cosmos3: commit_window appends a finished window's clean K/V
merceod Sep 17, 2026
71dd8ca
cosmos3: windowed denoise, kv commits with pool retention, streaming …
merceod Sep 17, 2026
7f0e4d6
cosmos3: video_gen_ar walk, window_decoder partition, window knobs
merceod Sep 17, 2026
44aa26f
videos: NDJSON stream for stream_video requests
merceod Sep 17, 2026
1a0ba15
videos: stream_video responses as x-ndjson
merceod Sep 17, 2026
61f4251
configs: Edge serves the windowed streaming rollout
merceod Sep 17, 2026
b15515e
configs: Nano windowed-AR deployment
merceod Sep 17, 2026
15945a3
test: Cosmos3 windowed rollout on CPU
merceod Sep 17, 2026
691af00
test: block-causal windowed kv oracle
merceod Sep 17, 2026
0645f80
test: windowed kv parity on the engine resources
merceod Sep 17, 2026
3999517
test: stream_video NDJSON on the videos route
merceod Sep 17, 2026
015e139
docs: Cosmos3 streaming rollout knobs and routes
merceod Sep 17, 2026
e398b10
benchmark: streaming rollout TTFF, cadence and frames/s
merceod Sep 17, 2026
2567bce
benchmark: bench-stream step for Edge
merceod Sep 17, 2026
a4eeac4
examples: windowed video streaming over /generate/ws
merceod Sep 17, 2026
ccc7041
cosmos3: session_store_size for windowed sessions
merceod Sep 17, 2026
e6ee1f3
cosmos3: session_id / resume_session request knobs
merceod Sep 17, 2026
3252670
cosmos3: keep the last window per session and resume from it
merceod Sep 17, 2026
6a5a268
test: windowed session store, resume head and decoder context
merceod Sep 17, 2026
22fe6ca
docs: Cosmos3 windowed sessions
merceod Sep 17, 2026
e09b128
cosmos3: read the distilled sampler (sigmas, scheduler class) from th…
merceod Sep 17, 2026
eba5ae2
cosmos3: distilled checkpoints fix steps and guidance, build FlowMatc…
merceod Sep 17, 2026
23e50b5
cosmos3: 4-step distilled SDE sampler with seeded re-noising and anch…
merceod Sep 17, 2026
d69fc0c
test: distilled Super sampler config, request rules and SDE loop
merceod Sep 17, 2026
ba3c321
configs: distilled i2v uses the modular pipeline's conditioning crop
merceod Sep 17, 2026
3a037d6
cosmos3: byte-faithful streamed reasoner text
merceod Sep 17, 2026
8e9d588
test: streamed Edge text reassembles multi-byte characters
merceod Sep 17, 2026
4f8768d
cosmos3: gen_capture_video tiers for video denoise CUDA graphs
merceod Sep 17, 2026
54cf2e3
configs: Edge captures the 480p clip and window denoise steps
merceod Sep 17, 2026
cf8a0f5
cosmos3: noisy-token mask on the batched denoise steps
merceod Sep 17, 2026
80aa379
cosmos3: capture video and chained-window denoise steps as CUDA graphs
merceod Sep 17, 2026
39d131d
test: captured-step tail carries the frame layout
merceod Sep 17, 2026
122eb63
test: video capture buckets, keys, windowed inputs and mask equivalence
merceod Sep 17, 2026
043abfa
docs: video denoise CUDA graphs for the Cosmos3 rollout
merceod Sep 17, 2026
6526ef6
cosmos3: build kv-window statics on demand, keep a long rollout's sta…
merceod Sep 17, 2026
0e39ff6
test: long rollout keeps a flat per-request state
merceod Sep 17, 2026
3822d53
readme: Cosmos3-Edge row
merceod Sep 17, 2026
d9f1073
docs: Cosmos3-Edge reasoner chat knobs and the action loop
merceod Sep 17, 2026
48d90cd
cosmos3: native-flow sigmas as an array (UniPC rejects a list)
merceod Sep 17, 2026
5b356a1
test: the Edge native-flow scheduler builds and shifts its sigmas
merceod Sep 17, 2026
186b06d
test: engine-cache harness on the current forward info and KV spec ov…
merceod Sep 17, 2026
0ec5bd5
test: action harness on the current forward info
merceod Sep 17, 2026
9a247db
test: sound harness on the current forward info
merceod Sep 17, 2026
88bd985
cosmos3: Edge checkpoints load the model-card serving recipe defaults
merceod Sep 17, 2026
c89d230
test: fused pipeline builds its scheduler like the served node
merceod Sep 17, 2026
56082ab
test: video parity runs at the reference flow shift
merceod Sep 17, 2026
5ddb7f5
test: Edge recipe defaults from the checkpoint
merceod Sep 17, 2026
b35ad4f
test: Edge recipe override case reuses the fake checkpoint dir
merceod Sep 17, 2026
9547ade
cosmos3: idle the window-decoder partition for text requests; mp4 fal…
merceod Sep 17, 2026
bfe368e
test: text requests idle the window-decoder partition
merceod Sep 17, 2026
f8d3195
test: fused pipeline tokenizes with the served prompt layout
merceod Sep 17, 2026
564b140
test: engine-cache harness tokenizes with the served prompt layout
merceod Sep 17, 2026
0174d97
cosmos3: PyAV libx264 mp4 encoder when torchcodec cannot load
merceod Sep 18, 2026
a12725a
test: mp4 falls back to PyAV without torchcodec
merceod Sep 18, 2026
804515d
test: probe the mp4 frame rate with PyAV
merceod Sep 18, 2026
2fbfe32
test: engine-cache harness resolves resource dependencies like the en…
merceod Sep 18, 2026
3ddf33b
configs: Edge video steps stay on the eager dense path (graphs measur…
merceod Sep 18, 2026
a939057
docs: video capture tiers are opt-in after the 480p measurement
merceod Sep 18, 2026
efb4312
cosmos3: note when video capture pays
merceod Sep 18, 2026
e59e0a0
test: engine-cache harness binds the node resources into the DiT
merceod Sep 18, 2026
eb12b44
examples: action loop client excludes the warm-up request from its rates
merceod Sep 18, 2026
5c176c3
kv: promote a staged pre-plan only into the step it was planned for
merceod Sep 18, 2026
e09730f
sampler: promote a staged pre-plan only into its own leased step
merceod Sep 18, 2026
035fcdb
Test pre-plan promotion is bound to its step
merceod Sep 18, 2026
1b56327
kv: a staged pre-plan for another step does not skip this step's admit
merceod Sep 18, 2026
5552950
test: fused pipeline decodes in the VAE's current dtype
merceod Sep 18, 2026
e5b68b7
cosmos3: keep a padded reasoner decode batch on one device
merceod Sep 18, 2026
371acf5
Test the reasoner's padded decode batch shares one device
merceod Sep 18, 2026
d3b2b93
chat: end a failed stream with an in-band error event
merceod Sep 18, 2026
53a4079
Test a failed chat stream reports the error in-band
merceod Sep 18, 2026
ba5f604
runner: drop a staged pre-plan on every resource before a foreign step
merceod Sep 18, 2026
0f60824
engine: reset a pre-plan through the runner
merceod Sep 18, 2026
7d8b57c
Test the runner drops a stale pre-plan on every resource
merceod Sep 18, 2026
edf7b2d
configs: Super i2v 4-step TP2 decodes on rank 1
merceod Sep 18, 2026
e83775f
cosmos3: stretch conditioning keeps the VideoProcessor resize
merceod Sep 18, 2026
55ef19c
cosmos3: the captured denoise forward runs native bf16
merceod Sep 18, 2026
1f33011
test: engine-cache harness mirrors the engine's autocast and copies c…
merceod Sep 18, 2026
ad687ef
test: engine-cache anchor image on the 8-bit grid; batched drift bar …
merceod Sep 18, 2026
594b5fa
docs: Cosmos3 concurrency and batched-generation numerics
merceod Sep 18, 2026
7463414
cosmos3: compile the reasoner's captured decode step by default
merceod Sep 18, 2026
6a7e190
cosmos3: reasoner decode capture honours compile_reasoner_decode
merceod Sep 18, 2026
e8b9408
docs: the compiled reasoner decode step and its switch
merceod Sep 18, 2026
b20bb5e
test: windowed dense-vs-paged window-0 bar from the measured drift
merceod Sep 18, 2026
52d198d
cosmos3: keep the timestep embedder fp32 under any cast of the shared…
merceod Sep 24, 2026
21eea4e
test: timestep embedder stays fp32 whichever submodule casts last
merceod Sep 24, 2026
0fd6211
docs: batched reasoner decode can flip bf16 near-ties
merceod Sep 24, 2026
e021614
cosmos3: compile_reasoner_prefill switch
merceod Sep 24, 2026
346bd2d
cosmos3: run the reasoner prefill through a compiled text tower
merceod Sep 24, 2026
ebd3ce9
cosmos3: compiled reasoner prefill off by default (inductor dynamic-s…
merceod Sep 24, 2026
7c61f12
test: Edge policy-step parity against the diffusers action dump
merceod Sep 24, 2026
5d3f282
cosmos3: encode one conditioning frame for policy and forward-dynamic…
merceod Sep 24, 2026
9a5dafb
test: vae_encoder conditioning clip per request kind
merceod Sep 24, 2026
faf8da5
benchmark: SGLang-Diffusion video and image client for the Cosmos3 table
merceod Sep 24, 2026
cbd1d41
benchmark: vLLM-Omni and SGLang action-policy clients
merceod Sep 24, 2026
60e42cf
benchmark: reproduce_edge.sh serves SGLang and the DROID loop
merceod Sep 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ _Note_: The **first request(s) on a fresh environment can be slow** — often te
| [Orpheus](https://huggingface.co/canopylabs/orpheus-3b-0.1-ft) | Speech LM | text → speech | `/v1/audio/speech` |
| [Cosmos3 Nano / Super](https://huggingface.co/nvidia/Cosmos3-Nano) | World model | text, image, video → image, video (+ sound), robot actions | `/v1/images/generations`, `/v1/videos/generations` |
| [Cosmos3 Policy DROID](https://huggingface.co/nvidia/Cosmos3-Nano-Policy-DROID) | Robot policy | text, image, video → robot actions, video | `/generate`, `/v1/images/generations`, `/v1/videos/generations` |
| [Cosmos3-Edge](https://huggingface.co/nvidia/Cosmos3-Edge) | 480p world model + VLM | text, image, video → image, video (streamed windows), robot actions, text | `/v1/chat/completions`, `/v1/images/generations`, `/v1/videos/generations`, `/generate`, `/generate/ws` |
| [Pi0.5](https://huggingface.co/lerobot/pi05_base) | Vision-language-action | text, image, state → robot actions | `/generate` |
| [V-JEPA 2 / 2-AC](https://huggingface.co/facebook/vjepa2-vitl-fpc64-256) | World model | video (+ actions) → latents, rollouts | `/generate` |
| [Wan2.2-TI2V-5B](https://huggingface.co/Wan-AI/Wan2.2-TI2V-5B-Diffusers) | Video diffusion | text, image → video | `/v1/videos/generations`, `/generate` |
Expand Down
204 changes: 204 additions & 0 deletions benchmark/cosmos3/bench_action_baselines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
"""Action-policy baseline clients. vLLM-Omni: POST /v1/videos (multipart, extra_params.action_mode=policy) -> poll
GET /v1/videos/{id} -> top-level `action` {data, shape, dtype, raw_action_dim}. Saves the actions as .npy (for
notes/served_action_parity.py --ref) and reports per-call latency and actions/s over N rounds.
SGLang: GET /v1/actions/metadata then one best-effort POST /v1/actions/generations (Cosmos3 may not enable it).

python benchmark/cosmos3/bench_action_baselines.py vllm-omni --port 8200 --image <jpg> --rounds 5 \
--out results/<date>/action_vllm_omni.npy
python benchmark/cosmos3/bench_action_baselines.py sglang --port 8400 --image <jpg>
"""

import argparse
import base64
import json
import statistics
import sys
import time
import urllib.request

import numpy as np

ap = argparse.ArgumentParser()
ap.add_argument("engine", choices=["vllm-omni", "sglang"])
ap.add_argument("--port", type=int, required=True)
ap.add_argument("--model", default="nvidia/Cosmos3-Edge")
ap.add_argument("--image", required=True)
ap.add_argument("--prompt", default="Pick up the red cup and place it in the sink.")
ap.add_argument("--domain", default="droid_lerobot")
ap.add_argument("--action-dim", type=int, default=10)
ap.add_argument("--chunk", type=int, default=32)
ap.add_argument("--size", default="832x480")
ap.add_argument("--steps", type=int, default=4)
ap.add_argument("--gs", type=float, default=3.0)
ap.add_argument(
"--flow-shift",
type=float,
default=5.0,
help="the DROID policy recipe (vLLM-Omni ROBOLAB defaults: 4 steps, gs 3.0, shift 5.0)",
)
ap.add_argument("--seed", type=int, default=0)
ap.add_argument("--rounds", type=int, default=5)
ap.add_argument("--warmup", type=int, default=1)
ap.add_argument("--poll", type=float, default=0.05)
ap.add_argument("--out", default="")
a = ap.parse_args()
BASE = f"http://127.0.0.1:{a.port}"


def data_url(path):
ext = path.rsplit(".", 1)[-1].lower().replace("jpg", "jpeg")
return f"data:image/{ext};base64," + base64.b64encode(open(path, "rb").read()).decode()


def _multipart(fields, files=()):
"""files: (field, filename, bytes, content_type) tuples."""
boundary = "----act" + str(int(time.time() * 1e6))
body = b""
for k, v in fields.items():
body += f'--{boundary}\r\nContent-Disposition: form-data; name="{k}"\r\n\r\n{v}\r\n'.encode()
for k, fn, data, ctype in files:
body += (
f'--{boundary}\r\nContent-Disposition: form-data; name="{k}"; filename="{fn}"\r\n'
f"Content-Type: {ctype}\r\n\r\n".encode()
+ data
+ b"\r\n"
)
return body + f"--{boundary}--\r\n".encode(), f"multipart/form-data; boundary={boundary}"


def _body_of(e):
try:
return e.read().decode(errors="replace")[:600]
except Exception:
return ""


def _get(path, timeout=600):
with urllib.request.urlopen(BASE + path, timeout=timeout) as r:
return json.load(r)


def vllm_omni_once():
extra = {
"action_mode": "policy",
"domain_name": a.domain,
"raw_action_dim": a.action_dim,
"action_chunk_size": a.chunk,
}
fields = {
"model": a.model,
"prompt": a.prompt,
"size": a.size,
"num_frames": str(a.chunk + 1),
"fps": "15",
"num_inference_steps": str(a.steps),
"guidance_scale": str(a.gs),
"flow_shift": str(a.flow_shift),
"seed": str(a.seed),
"extra_params": json.dumps(extra),
}
# The recipe ships the conditioning frame as the input_reference upload (video_bench.py does the same for i2v);
# a data-URL image_reference is rejected with 400 "did not decode to an image".
body, ctype = _multipart(
fields, files=[("input_reference", a.image.rsplit("/", 1)[-1], open(a.image, "rb").read(), "image/jpeg")]
)
t0 = time.perf_counter()
req = urllib.request.Request(BASE + "/v1/videos", data=body, headers={"Content-Type": ctype})
with urllib.request.urlopen(req, timeout=1800) as r:
job = json.load(r)
while job.get("status") not in ("completed", "failed"):
time.sleep(a.poll)
job = _get(f"/v1/videos/{job['id']}")
wall = time.perf_counter() - t0
if job.get("status") != "completed":
raise RuntimeError(f"vllm-omni job failed: {job.get('error')}")
act = job.get("action")
if not act:
raise RuntimeError(f"no action in job response; keys={list(job.keys())}")
arr = np.array(act["data"], dtype=np.float32).reshape(act["shape"])
while arr.ndim > 2:
arr = arr[0]
return arr, wall, act


def sglang_once():
meta = _get("/v1/actions/metadata")
print(
json.dumps({"sglang_action_metadata": {k: meta.get(k) for k in ("policy_family", "input", "output")}}),
flush=True,
)
keys = (meta.get("input") or {}).get("image_keys") or ["image"]
# sglang.multimodal_gen.runtime.entrypoints.action.protocol: the JSON body is
# {"input": {"task", "observation"}, "parameters"};
# image values are {"b64_json": ...} dicts (data URLs are not decoded).
# Cosmos3 policy parameters ride in "parameters".
b64 = base64.b64encode(open(a.image, "rb").read()).decode()
payload = {
"input": {"task": a.prompt, "observation": {"images": {keys[0]: {"b64_json": b64}}}},
"parameters": {
"seed": a.seed,
"num_inference_steps": a.steps,
"guidance_scale": a.gs,
"flow_shift": a.flow_shift,
"action_mode": "policy",
"domain_name": a.domain,
"raw_action_dim": a.action_dim,
"action_chunk_size": a.chunk,
"num_frames": a.chunk + 1,
"width": int(a.size.split("x")[0]),
"height": int(a.size.split("x")[1]),
},
}
t0 = time.perf_counter()
req = urllib.request.Request(
BASE + "/v1/actions/generations",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=1800) as r:
out = json.load(r)
except urllib.error.HTTPError as e:
raise RuntimeError(f"sglang {e.code}: {_body_of(e)}") from None
wall = time.perf_counter() - t0
# action.protocol.action_generation_response:
# {"data": [{"action": {"shape": [H, D], "values": [[...]], "raw_action_dim"}}], "usage"}
act = out["data"][0]["action"]
arr = np.array(act["values"], dtype=np.float32).reshape(act["shape"])
raw = int(act.get("raw_action_dim") or a.action_dim)
return arr[:, :raw], wall, {"shape": act["shape"], "raw_action_dim": raw, "usage": out.get("usage")}


once = vllm_omni_once if a.engine == "vllm-omni" else sglang_once
print(
f"=== {a.engine} action policy port={a.port} domain={a.domain} chunk={a.chunk} "
f"steps={a.steps} gs={a.gs} seed={a.seed} ===",
flush=True,
)
for i in range(a.warmup):
try:
_, w, _ = once()
print(f" warmup {i}: {w:.3f}s", flush=True)
except Exception as exc: # noqa: BLE001
print(f" warmup {i} failed: {exc!r}", flush=True)
sys.exit(2)
walls, arrs = [], []
for i in range(a.rounds):
arr, w, act = once()
walls.append(w)
arrs.append(arr)
print(
f" round {i}: {w:.3f}s actions {arr.shape} first={np.round(arr[0, : min(3, arr.shape[1])], 4).tolist()} "
f"dtype={act.get('dtype')} raw_dim={act.get('raw_action_dim')}",
flush=True,
)
med = statistics.median(walls)
rep = float(max(np.abs(x - arrs[0]).max() for x in arrs[1:])) if len(arrs) > 1 else 0.0
print(
f" chunk latency median {med:.3f}s p95 {sorted(walls)[int(0.95 * (len(walls) - 1))]:.3f}s"
f" -> {a.chunk / med:.1f} actions/s (sequential calls); repeat max-abs-diff {rep:.3e}",
flush=True,
)
if a.out:
np.save(a.out, arrs[0])
print(" saved", a.out)
116 changes: 116 additions & 0 deletions benchmark/cosmos3/bench_chat_oai.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""Reasoner (understanding tower) latency client for the OpenAI chat endpoint
both M* (``mstar serve cosmos3_edge``) and vLLM (``vllm serve nvidia/Cosmos3-Edge``)
expose: time-to-first-token and decode tokens/s, streamed, at a chosen
concurrency, on the model card's reasoning prompt (image + text) or text only.

Same payload on both engines (greedy, fixed max_tokens, thinking off unless
asked), client-side timing, warmup excluded, median and p95 reported.

python bench_chat_oai.py --port 8000 --model nvidia/Cosmos3-Edge --tag vllm --image assets/example_reasoning_input.png
python bench_chat_oai.py --port 8100 --model cosmos3_edge --tag ours --image assets/example_reasoning_input.png
"""
import argparse
import base64
import concurrent.futures as cf
import json
import mimetypes
import statistics
import time
import urllib.request

ap = argparse.ArgumentParser()
ap.add_argument("--port", type=int, required=True)
ap.add_argument("--model", default="nvidia/Cosmos3-Edge")
ap.add_argument("--image", default="") # optional conditioning image path (else text-only)
ap.add_argument("--prompt", default="The task is to put flower into the red bottle. Generate a plan consisting of "
"subtasks for accomplish the task.")
ap.add_argument("--max-tokens", type=int, default=128)
ap.add_argument("--concurrency", default="1,8,32")
ap.add_argument("--requests", type=int, default=16) # per concurrency level (>= concurrency)
ap.add_argument("--warmup", type=int, default=2)
ap.add_argument("--thinking", action="store_true")
ap.add_argument("--tag", default="run")
ap.add_argument("--out", default="") # optional JSON results path
args = ap.parse_args()

URL = f"http://localhost:{args.port}/v1/chat/completions"
content = []
if args.image:
mime = mimetypes.guess_type(args.image)[0] or "image/png"
with open(args.image, "rb") as f:
data_url = f"data:{mime};base64," + base64.b64encode(f.read()).decode()
content.append({"type": "image_url", "image_url": {"url": data_url}})
content.append({"type": "text", "text": args.prompt})
BODY = {
"model": args.model,
"messages": [{"role": "user", "content": content}],
"max_tokens": args.max_tokens,
"temperature": 0.0,
"stream": True,
"chat_template_kwargs": {"enable_thinking": bool(args.thinking)},
}


def one() -> dict:
req = urllib.request.Request(URL, data=json.dumps(BODY).encode(), headers={"Content-Type": "application/json"})
t0 = time.perf_counter()
first = None
n_chunks = 0
text = []
with urllib.request.urlopen(req, timeout=600) as r:
for line in r:
line = line.decode("utf-8", "replace").strip()
if not line.startswith("data:"):
continue
payload = line[5:].strip()
if payload == "[DONE]":
break
delta = json.loads(payload)["choices"][0].get("delta", {})
piece = delta.get("content")
if piece:
if first is None:
first = time.perf_counter()
n_chunks += 1
text.append(piece)
end = time.perf_counter()
ttft = (first or end) - t0
total = end - t0
decode = max(end - (first or end), 1e-9)
return {"ttft": ttft, "total": total, "chunks": n_chunks, "tok_s": (n_chunks - 1) / decode if n_chunks > 1 else 0.0,
"text": "".join(text)}


def pct(xs, p):
xs = sorted(xs)
return xs[min(len(xs) - 1, int(round(p * (len(xs) - 1))))]


results = {}
print(f"=== {args.tag} port={args.port} model={args.model} max_tokens={args.max_tokens} "
f"image={'yes' if args.image else 'no'} thinking={args.thinking} ===", flush=True)
for _ in range(args.warmup):
one()
for conc in [int(c) for c in args.concurrency.split(",")]:
n = max(args.requests, conc)
t0 = time.perf_counter()
with cf.ThreadPoolExecutor(max_workers=conc) as ex:
outs = list(ex.map(lambda _: one(), range(n)))
wall = time.perf_counter() - t0
ttfts = [o["ttft"] for o in outs]
toks = [o["tok_s"] for o in outs]
total_tokens = sum(o["chunks"] for o in outs)
rec = {
"concurrency": conc, "requests": n, "ttft_p50": statistics.median(ttfts), "ttft_p95": pct(ttfts, 0.95),
"decode_tok_s_per_req_p50": statistics.median(toks), "aggregate_tok_s": total_tokens / wall,
"wall_s": wall, "sample": outs[0]["text"][:120],
}
results[conc] = rec
print(f" conc={conc:3d} TTFT p50 {rec['ttft_p50'] * 1000:7.1f} ms p95 {rec['ttft_p95'] * 1000:7.1f} ms "
f"decode {rec['decode_tok_s_per_req_p50']:6.1f} tok/s/req aggregate {rec['aggregate_tok_s']:7.1f} tok/s "
f"(n={n}, wall {wall:.1f}s)", flush=True)
print(" sample:", repr(results[min(results)]["sample"]))
if args.out:
with open(args.out, "w") as f:
json.dump({"tag": args.tag, "model": args.model, "max_tokens": args.max_tokens, "image": bool(args.image),
"results": results}, f, indent=2)
print("DONE", flush=True)
Loading
Loading