Skip to content

Add Cosmos3 Policy#894

Open
ptrdlr14 wants to merge 3 commits into
isaac-sim:mainfrom
ptrdlr14:ptrdlr14/feature/cosmos3-policy
Open

Add Cosmos3 Policy#894
ptrdlr14 wants to merge 3 commits into
isaac-sim:mainfrom
ptrdlr14:ptrdlr14/feature/cosmos3-policy

Conversation

@ptrdlr14

Copy link
Copy Markdown

Summary

Add Cosmos3 Policy support via openpi-server.

Detailed description

  • Introduced isaaclab_arena_cosmos3 Extension: Created a new extension package dedicated to Cosmos3 policy integration.

  • Added OpenPI Client & Policy: Implemented Cosmos3RemotePolicy which establishes a WebSocket connection to a remote inference server using openpi_client.

@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a Cosmos3 remote policy extension. The main changes are:

  • A new isaaclab_arena_cosmos3 package.
  • A WebSocket-backed Cosmos3RemotePolicy.
  • A DROID adapter for Cosmos3 request payloads.
  • A Docker helper for running the Cosmos3 server.
  • Unit tests and the openpi-client dependency.

Confidence Score: 4/5

The Cosmos3 policy path is not safe to merge until the setup, construction, and cleanup contracts are fixed.

  • The default server setup fails because the pinned commit file contains placeholder text.
  • The registered policy constructor does not match the typed runner construction path.
  • Remote cleanup can call a method this policy does not implement.
  • Response validation depends on assertions that optimized Python removes.

isaaclab_arena_cosmos3/docker/COSMOS3_COMMIT; isaaclab_arena_cosmos3/policy/cosmos3_remote_policy.py

Important Files Changed

Filename Overview
isaaclab_arena_cosmos3/policy/cosmos3_remote_policy.py Adds the remote policy, action cache, retry handling, and adapter resolution; construction, cleanup, and validation paths need fixes.
isaaclab_arena_cosmos3/policy/droid_adapter.py Adds the DROID observation adapter and composed Cosmos3 request payload.
isaaclab_arena_cosmos3/docker/run_cosmos3_server.sh Adds a Docker helper that builds and runs the Cosmos3 inference server.
isaaclab_arena_cosmos3/docker/COSMOS3_COMMIT Adds the commit pin consumed by the Docker helper, but it currently contains placeholder text.
setup.py Adds openpi-client as a runtime dependency for the new remote client code.

Reviews (1): Last reviewed commit: "Add openpi-client runtime dependency and..." | Re-trigger Greptile

@@ -0,0 +1 @@
TODO: pin a known-working cosmos-framework commit

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Placeholder Commit Breaks Server Setup

The default server script reads this file and runs git checkout "$PINNED_COMMIT". With the placeholder text here, the documented ./run_cosmos3_server.sh path clones cosmos-framework and then fails before building the inference server.

name = "cosmos3_remote"
config_class = Cosmos3RemotePolicyArgs

def __init__(self, config: Cosmos3RemotePolicyArgs, cosmos3_embodiment_adapter: Cosmos3EmbodimentAdapter) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Registered Constructor Needs Adapter

Registered policies are constructed from their config as policy_type(policy_cfg). This constructor requires a second cosmos3_embodiment_adapter argument, so selecting cosmos3_remote through the typed runner path raises TypeError before rollout starts.

Comment on lines +244 to +246
@property
def is_remote(self) -> bool:
return True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Remote Cleanup Method Missing

Returning True marks this policy for remote-policy teardown, where the runner calls shutdown_remote() on remote policies. This class only implements close(), so a rollout can finish and then fail during cleanup with an AttributeError.

Comment on lines +188 to +193
assert (
chunk.ndim == 2 and chunk.shape[1] == self._cosmos3_embodiment_adapter.action_dim
), f"Expected action of shape (H, {self._cosmos3_embodiment_adapter.action_dim}); got {chunk.shape}"
assert (
chunk.shape[0] >= self._open_loop_horizon
), f"Server returned horizon {chunk.shape[0]} < configured open_loop_horizon {self._open_loop_horizon}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Optimized Mode Skips Response Checks

These checks validate data from the WebSocket server, but assert statements are removed under python -O. In that mode, a wrong-width or too-short action response can enter the cache and later produce malformed actions or indexing failures instead of a clear policy error.

Comment on lines +130 to +133
assert self.task_description, (
"Cosmos3RemotePolicy requires a non-empty language instruction"
" (set via --language_instruction or on the task definition)."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Optimized Mode Allows Empty Prompt

This language-instruction guard also disappears under python -O. If the environment returns an empty or missing task description, the policy sends that value in prompt to the remote server instead of stopping locally with a clear error.

right = right_t.squeeze(0).permute(1, 2, 0).numpy().astype(wrist.dtype)

# Compose: vstack(wrist, hstack(left, right)) → (720, 640, 3)
image = np.concatenate((wrist, np.concatenate((left, right), axis=1)), axis=0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Composed Camera Layout Unverified

This sends the wrist view on the top half and the two exterior views below it. If the Cosmos3 DROID server was trained on a different packed layout, every rollout still produces a valid (720, 640, 3) image but the model reads each camera from the wrong spatial position and returns bad actions without an obvious error.

wrist = image_tools.resize_with_pad(extracted.wrist_image, self.image_h, self.image_w)

# Resize left/right to full size first, then bilinear downsample.
half_size = (self.image_h // 2, self.image_w // 2) # (180, 320)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 The composed image is 540×640, not the 720×640 this method promises. resize_with_pad makes wrist (360, 640); half_size=(180, 320) makes left/right (180, 320) each, so hstack(left, right) is (180, 640) and the final vstack is (540, 640, 3). That contradicts the class docstring ("single 720×640 image"), this method's docstring, the assert message on line 97, and test_droid_adapter_uses_cosmos3_wire_keys, which asserts (720, 640, 3) — so that test fails as written. The assert expression on line 96 (image_h + image_h//2) was written to match the 540 result, so it passes and hides the mismatch. If cosmos3's wire format really is 720×640 (wrist on top, left|right filling the bottom half), the bottom row needs full height:

Suggested change
half_size = (self.image_h // 2, self.image_w // 2) # (180, 320)
half_size = (self.image_h, self.image_w // 2) # (360, 320)

and line 96's assert should become (2 * self.image_h, self.image_w, 3). Could you confirm the server's expected layout and reconcile all four spots? A wrong image shape silently corrupts every cosmos3 eval.



@register_policy
class Cosmos3RemotePolicy(PolicyBase):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 This file is a near-verbatim clone of isaaclab_arena_openpi/policy/pi0_remote_policy.py — the __init__ cache setup, get_action, reset, _maybe_init_per_env_state, _call_server_with_retry, _close_websocket_best_effort, and the _resolve_*_adapter pattern are all mechanically renamed copies. The # TODO(cvolk...): add a RemotePolicy base class to unify this and other remote policies in from_dict is exactly the abstraction this duplication is asking for — and the same TODO already sits in pi0. Could we pull the chunk-replay cache + retry/reconnect machinery into a RemotePolicy base in isaaclab_arena/policy/ now, so cosmos3 and pi0 only supply the response key (action vs actions), the optional post-process, and adapter resolution? Otherwise the two paths drift silently. (Same theme, minor: Cosmos3RemotePolicyArgs breaks the ...Cfg / PolicyCfg-subclass convention that Pi0RemotePolicyCfg follows, and PolicyBase is left unparameterized here where pi0 uses PolicyBase[Pi0RemotePolicyCfg].)

remote_port: int = 8000
"""Port the cosmos3 inference server listens on."""

num_envs: int = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 num_envs is never read — the per-env caches are lazy-allocated from env.unwrapped.num_envs in _maybe_init_per_env_state, not from this field, so the docstring's "Used to pre-allocate per-environment action caches" is inaccurate too. Suggest dropping the field (it isn't wired through add_args_to_parser/from_args either) unless a later change needs it.

@@ -0,0 +1 @@
TODO: pin a known-working cosmos-framework commit

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 This placeholder is read literally by run_cosmos3_server.sh (PINNED_COMMIT=$(tr -d ... < COSMOS3_COMMIT)git checkout "$PINNED_COMMIT"), so the default build path runs git checkout 'TODO: pin a known-working cosmos-framework commit' and fails. The server launcher can't work until a real commit is pinned — is this meant to land before merge, or is the PR waiting on it?

@arena-review-bot

Copy link
Copy Markdown
Contributor

🤖 Isaac Lab-Arena Review Bot

Summary

This PR adds a new isaaclab_arena_cosmos3 extension: a Cosmos3RemotePolicy that talks to a cosmos3/OpenPI WebSocket inference server, a DROID embodiment adapter, a Docker server launcher, and a solid unit-test suite. The client-side plumbing (per-env chunk cache, reconnect/retry, gripper binarization) is well-tested, but there is one real correctness bug in the image composition, and the policy file is a near-verbatim clone of the existing openpi remote policy that should be unified rather than copied.

Design, Boundaries & Scope

cosmos3_remote_policy.py duplicates isaaclab_arena_openpi/policy/pi0_remote_policy.py almost line-for-line (cache setup, get_action, reset, retry/reconnect, best-effort close, adapter resolution). The author's own TODO(cvolk): add a RemotePolicy base class to unify this and other remote policies — which already exists in pi0 — is exactly the abstraction the duplication is calling for. Suggest extracting the shared chunk-replay + retry machinery into a RemotePolicy base in isaaclab_arena/policy/ before this second copy lands, so the two paths can't drift.

Findings

🔴 Critical: droid_adapter.py:82 — Composed image is (540, 640, 3), not the 720×640 the class/method docstrings, the assert message, and the unit test all claim; the assert expression was written to match 540, so it silently passes. The wire-format image is malformed → corrupts every cosmos3 eval. test_droid_adapter_uses_cosmos3_wire_keys fails as written.
🟡 Warning: cosmos3_remote_policy.py:52 — Near-verbatim duplication of pi0_remote_policy.py; extract a shared RemotePolicy base. Also: Cosmos3RemotePolicyArgs breaks the ...Cfg/PolicyCfg convention and PolicyBase is left unparameterized.
🟡 Warning: docker/COSMOS3_COMMIT:1 — File contains a literal TODO: string that run_cosmos3_server.sh feeds straight into git checkout, so the default server build fails until a real commit is pinned.
🔵 Improvement: cosmos3_remote_config.py:27num_envs config field is never read (caches derive from env.unwrapped.num_envs); the field and its docstring are dead. Drop it.

Test Coverage

Good client-side coverage (caching/advancement, parallel envs, reset-by-env-ids, gripper binarization, retry/reconnect, shape asserts). These are non-sim CPU tests, so the inner/outer run_simulation_app_function pattern isn't required here. One gap: test_droid_adapter_uses_cosmos3_wire_keys asserts (720, 640, 3) while pack_request produces (540, 640, 3) — that test currently fails and points straight at the bug above; please confirm the intended shape and make production + test agree.

Verdict

Minor fixes needed — resolve the image-shape bug (blocking) and ideally the duplication before merge.

ptrdlr14 added 2 commits July 13, 2026 16:58
Correct composed image shape from (720, 640, 3) to (540, 640, 3) in class
and method docstrings, assert message, and unit test to match the actual
wire format used by cosmos3 DROID adapter.

Pin COSMOS3_COMMIT to a known-working Release (2026-07-11) so the server
Docker build succeeds without user intervention.

Delete unused num_envs field from Cosmos3RemotePolicyArgs.

Signed-off-by: ptrdlr14 <ptrdlr14@gmail.com>
Consistent with pi0 and dreamzero: use config.policy_device instead of
env.unwrapped.device, and expose --policy_device CLI argument.

Signed-off-by: ptrdlr14 <ptrdlr14@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant