grid-cortex-client 0.5.1__tar.gz → 0.6.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/CLAUDE.md +2 -2
  2. grid_cortex_client-0.6.1/MIGRATION.md +19 -0
  3. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/PKG-INFO +1 -1
  4. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/pyproject.toml +1 -0
  5. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/client.py +3 -4
  6. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/cortex_hub_client.py +3 -3
  7. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/model_type.py +43 -21
  8. grid_cortex_client-0.6.1/src/grid_cortex_client/models/c_radio.py +129 -0
  9. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/pi05.py +26 -0
  10. grid_cortex_client-0.6.1/tests/test_c_radio.py +124 -0
  11. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_contact_graspnet.py +2 -2
  12. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_cortex_hub_client.py +14 -12
  13. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_da3metric.py +2 -2
  14. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_fast_foundation_stereo.py +2 -2
  15. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_foundation_stereo.py +2 -2
  16. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_graspgen.py +2 -2
  17. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_graspgenx.py +2 -2
  18. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_gsam2.py +2 -2
  19. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_locate_anything.py +2 -2
  20. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_metric3d.py +2 -2
  21. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_moondream.py +2 -2
  22. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_oneformer.py +2 -2
  23. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_owlv2.py +2 -2
  24. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_pi05.py +2 -2
  25. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_qwen_vl.py +2 -2
  26. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_sam2.py +2 -2
  27. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_sam3.py +2 -2
  28. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_ur5e_pyroki_collision_batch.py +2 -2
  29. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_ur5e_pyroki_fk.py +2 -2
  30. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_zoedepth.py +2 -2
  31. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/.gitignore +0 -0
  32. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/README.md +0 -0
  33. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/__init__.py +0 -0
  34. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/cortex_client.py +0 -0
  35. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/__init__.py +0 -0
  36. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/base_model.py +0 -0
  37. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/contact_graspnet.py +0 -0
  38. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/da3metric.py +0 -0
  39. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/fast_foundation_stereo.py +0 -0
  40. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/finetune_pi05.py +0 -0
  41. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/foundation_stereo.py +0 -0
  42. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/graspgen.py +0 -0
  43. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/graspgenx.py +0 -0
  44. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/gsam2.py +0 -0
  45. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/locate_anything.py +0 -0
  46. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/metric3d.py +0 -0
  47. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/moondream.py +0 -0
  48. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/oneformer.py +0 -0
  49. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/owlv2.py +0 -0
  50. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/qwen_vl.py +0 -0
  51. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/sam2.py +0 -0
  52. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/sam3.py +0 -0
  53. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/ur5e_pyroki_collision_batch.py +0 -0
  54. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/ur5e_pyroki_fk.py +0 -0
  55. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/zoedepth.py +0 -0
  56. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/preprocessing.py +0 -0
  57. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/tools/__init__.py +0 -0
  58. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/tools/generate_enum.py +0 -0
  59. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/tools/registry.py +0 -0
  60. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/utils.py +0 -0
  61. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/ws.py +0 -0
  62. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/README.md +0 -0
  63. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/__init__.py +0 -0
  64. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/conftest.py +0 -0
  65. {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_finetune_pi05.py +0 -0
@@ -5,7 +5,7 @@ Python client for the Cortex ML inference API. Published to **public PyPI**.
5
5
  ## Build & Test
6
6
 
7
7
  ```bash
8
- uv run --package grid-cortex-client pytest
8
+ BUILD_VERSION=0.0.0.dev0 uv run --project cortex --package grid-cortex-client pytest
9
9
  ```
10
10
 
11
11
  - Build tool: hatchling + hatch-vcs
@@ -27,7 +27,7 @@ uv run --package grid-cortex-client pytest
27
27
  | Stage | `https://cortex-stage.generalrobotics.dev/cortex` | `wss://cortex-stage.generalrobotics.dev/cortex/ws/ws` |
28
28
  | Prod | `https://cortex-prod.generalrobotics.dev/cortex` | `wss://cortex-prod.generalrobotics.dev/cortex/ws/ws` |
29
29
 
30
- Override via `GRID_CORTEX_BASE_URL` (REST) and `GRID_CORTEX_WS_URL` (WebSocket). Default is prod.
30
+ Override the REST/WebSocket base via `GRID_CORTEX_BASE_URL`. Default is prod.
31
31
 
32
32
  ## Publishing
33
33
 
@@ -0,0 +1,19 @@
1
+ # grid-cortex-client migration notes
2
+
3
+ ## `CORTEX_API_KEY` → `GRID_CORTEX_API_KEY` (breaking)
4
+
5
+ The client now resolves the API key from **`GRID_CORTEX_API_KEY`** only. The
6
+ legacy `CORTEX_API_KEY` environment-variable fallback in `CortexHubClient` has
7
+ been removed.
8
+
9
+ **Action required:** set `GRID_CORTEX_API_KEY` instead of `CORTEX_API_KEY`, or
10
+ pass `api_key=` explicitly to the client. Code that still relies on
11
+ `CORTEX_API_KEY` will construct the client with no key and get a `401` on the
12
+ first call.
13
+
14
+ This is a breaking change to the published package and must ship under a MAJOR
15
+ bump (`cortex-client/vX.0.0`) — see `client-versioning.md` (the squash subject
16
+ needs `feat(cortex-client)!:` or a `BREAKING CHANGE:` footer, or cut the MAJOR
17
+ manually). `GRID_CORTEX_API_KEY` is the single canonical name repo-wide: the
18
+ legacy `CORTEX_API_KEY` is no longer injected into the session pod either, so
19
+ customer skills that read `CORTEX_API_KEY` directly must migrate too.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grid-cortex-client
3
- Version: 0.5.1
3
+ Version: 0.6.1
4
4
  Summary: Python client for Grid Cortex
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3.8
@@ -53,4 +53,5 @@ markers = [
53
53
  [dependency-groups]
54
54
  dev = [
55
55
  "pytest>=8.3.5",
56
+ "pytest-asyncio>=0.24",
56
57
  ]
@@ -4,7 +4,7 @@ import httpx
4
4
  import logging
5
5
  from . import utils as _u
6
6
 
7
- BASE_URL = os.getenv("GRID_CORTEX_BASE_URL", "https://cortex-prod.generalrobotics.dev/cortex")
7
+ DEFAULT_BASE_URL = "https://cortex-prod.generalrobotics.dev/cortex"
8
8
 
9
9
  # Remove basicConfig, as library logging should not configure root logger
10
10
  # logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
@@ -51,7 +51,7 @@ class HTTPClient:
51
51
  headers["x-api-key"] = api_key
52
52
 
53
53
  # Determine the final base URL
54
- final_base_url = base_url or os.getenv("GRID_CORTEX_BASE_URL", "https://cortex-prod.generalrobotics.dev/cortex")
54
+ final_base_url = base_url or os.getenv("GRID_CORTEX_BASE_URL", DEFAULT_BASE_URL)
55
55
  logger.info(f"HTTPClient initialized for base URL: {final_base_url}")
56
56
 
57
57
  self._client = httpx.Client(base_url=final_base_url, timeout=timeout, headers=headers)
@@ -75,7 +75,6 @@ class HTTPClient:
75
75
  headers.setdefault("Content-Type", "application/msgpack")
76
76
  resp = self._client.post(path, content=data, headers=headers, timeout=timeout)
77
77
  else:
78
- # JSON fall-back (legacy)
79
78
  headers = headers or {"Content-Type": "application/json"}
80
79
  resp = self._client.post(path, json=json, data=data, headers=headers, timeout=timeout)
81
80
  resp.raise_for_status()
@@ -170,7 +169,7 @@ class AsyncHTTPClient:
170
169
  else:
171
170
  headers["x-api-key"] = api_key
172
171
 
173
- final_base_url = base_url or os.getenv("GRID_CORTEX_BASE_URL", "https://cortex-prod.generalrobotics.dev/cortex")
172
+ final_base_url = base_url or os.getenv("GRID_CORTEX_BASE_URL", DEFAULT_BASE_URL)
174
173
  logger.info(f"AsyncHTTPClient initialized for base URL: {final_base_url}")
175
174
 
176
175
  self._client = httpx.AsyncClient(base_url=final_base_url, timeout=timeout, headers=headers)
@@ -84,7 +84,7 @@ class CortexHubClient:
84
84
 
85
85
  Args:
86
86
  api_key: API key (defaults to GRID_CORTEX_API_KEY env var)
87
- base_url: Base URL (defaults to GRID_CORTEX_BASE_URL env var or production).
87
+ base_url: Base URL (defaults to the GRID_CORTEX_BASE_URL env var, or production if unset).
88
88
  The WebSocket endpoint is derived from the `/ws` route prefix (ends up as `/ws/ws`).
89
89
  timeout: Socket timeout in seconds
90
90
 
@@ -108,9 +108,9 @@ class CortexHubClient:
108
108
  base_url: Optional[str] = None,
109
109
  timeout: float = 60.0,
110
110
  ):
111
- self._api_key = api_key or os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
111
+ self._api_key = api_key or os.getenv("GRID_CORTEX_API_KEY")
112
112
  if not self._api_key:
113
- logger.warning("API key not set. Set GRID_CORTEX_API_KEY or CORTEX_API_KEY.")
113
+ logger.warning("API key not set. Set GRID_CORTEX_API_KEY.")
114
114
  self._base_url = base_url
115
115
  self._url = self._resolve_ws_url(base_url)
116
116
  self._timeout = timeout
@@ -13,36 +13,54 @@ from enum import Enum
13
13
 
14
14
 
15
15
  class ModelType(Enum):
16
- CONTACT_GRASPNET = "contact-graspnet"
16
+ C_RADIO = "c-radio"
17
17
  """
18
- Generate 6-DoF grasp poses from a depth image + camera intrinsics
19
- (+ optional segmentation mask) using Contact-GraspNet (PyTorch port).
18
+ Compute an L2-normalized embedding for a single RGB image.
20
19
 
21
20
  Args:
22
- depth_image (Union[str, Image.Image, np.ndarray]): Depth image (path/URL/PIL/ndarray; float32 H×W).
23
- camera_intrinsics (Union[str, np.ndarray]): 3×3 intrinsics matrix.
24
- seg_image (Union[str, Image.Image, np.ndarray, None]): Optional segmentation mask (uint8 H×W).
25
- When supplied + aux_args["filter_grasps"] = True, grasps are restricted to the masked region.
26
- aux_args (Dict[str, Any]): Optional dict. Keys:
27
- - "top_k" (int, default 64): cap on returned grasps after score-sort.
28
- - "z_range" ([float, float], default [0.2, 1.8]): z-axis crop in metres.
29
- - "local_regions" (bool, default False): crop 3D regions around segments.
30
- - "filter_grasps" (bool, default False): restrict grasps to segmap; requires seg_image.
31
- - "forward_passes" (int, default 1): how many forward passes to ensemble.
32
- timeout (float | None): Optional HTTP timeout.
21
+ image_input (Union[str, Image.Image, np.ndarray]): RGB image as file path, URL, PIL Image, or numpy array.
22
+ timeout (float | None): Optional timeout in seconds for the HTTP request.
33
23
 
34
24
  Returns:
35
- Dict with:
36
- - grasps: Array of 4×4 grasp poses in camera frame, shape (N, 4, 4), N <= top_k.
37
- - confidence: Array of confidence scores in [0, 1], shape (N,).
38
- - latency_ms: Optional server-reported latency in milliseconds.
25
+ np.ndarray: Embedding as numpy array of shape ``(D,)``, dtype ``float32``,
26
+ L2-normalized so dot products give cosine similarity directly.
39
27
 
40
28
  Raises:
41
- ValueError: If depth or intrinsics cannot be loaded.
29
+ ValueError: If image cannot be loaded.
42
30
  RuntimeError: If no HTTP transport is configured.
43
31
  Exception: If the HTTP request fails.
44
32
 
45
33
  Example:
34
+ >>> from grid_cortex_client import CortexClient, ModelType
35
+ >>> import numpy as np
36
+ >>> from PIL import Image
37
+ >>> client = CortexClient()
38
+ >>> image = np.array(Image.open("cat.jpg"))
39
+ >>> emb = client.run(ModelType.C_RADIO, image_input=image)
40
+ >>> print(emb.shape, float(np.linalg.norm(emb))) # (D,) 1.0
41
+ """
42
+ CONTACT_GRASPNET = "contact-graspnet"
43
+ """
44
+ Generate grasps using ContactGraspNet.
45
+
46
+ Args:
47
+ depth_image (Union[str, Image.Image, np.ndarray, None]): Depth image (required).
48
+ camera_intrinsics (Union[str, np.ndarray, None]): 3×3 intrinsics matrix (required).
49
+ seg_image (Union[str, Image.Image, np.ndarray, None]): Optional segmentation mask (uint8 H×W).
50
+ aux_args (Dict[str, Any] | None): Optional dict. Keys: ``top_k`` (int, default 64),
51
+ ``z_range`` ([float, float], default [0.2, 1.8]),
52
+ ``local_regions`` (bool, default False),
53
+ ``filter_grasps`` (bool, default False; requires ``seg_image``),
54
+ ``forward_passes`` (int, default 1).
55
+ timeout (float | None): Optional HTTP timeout.
56
+
57
+ Returns:
58
+ Dict with:
59
+ - ``grasps``: (N, 4, 4) array of grasp poses in camera frame
60
+ - ``confidence``: (N,) array of confidence scores in [0, 1]
61
+ - ``latency_ms``: server-reported latency
62
+
63
+ Examples:
46
64
  >>> from grid_cortex_client import CortexClient, ModelType
47
65
  >>> import numpy as np
48
66
  >>> client = CortexClient()
@@ -302,14 +320,18 @@ class ModelType(Enum):
302
320
  """
303
321
  METRIC3D = "metric3d"
304
322
  """
305
- Estimate metric depth and surface normals from a single RGB image.
323
+ Estimate metric depth and/or surface normals from a single RGB image.
306
324
 
307
325
  Args:
308
326
  image_input (Union[str, Image.Image, np.ndarray]): RGB image as file path, URL, PIL Image, or numpy array.
309
327
  timeout (float | None): Optional timeout in seconds for the HTTP request.
328
+ outputs (Optional[Union[str, Sequence[str]]]): Keyword-only. Optional selection of maps to return —
329
+ ``"depth"``, ``"normals"``, or a non-empty subset of both.
330
+ ``None`` (default) returns both. Requesting only what you need
331
+ shrinks the response payload (normals are 3x the size of depth).
310
332
 
311
333
  Returns:
312
- Dict with keys:
334
+ Dict with the requested subset of:
313
335
  - ``"depth"``: (H, W) float32 array, metric depth in metres.
314
336
  - ``"normals"``: (H, W, 3) float32 array, unit surface normals.
315
337
 
@@ -0,0 +1,129 @@
1
+ """C-RADIO wrapper.
2
+
3
+ Client-side helper for the dense visual embedding endpoint powered by
4
+ NVIDIA C-RADIOv4-SO400M.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from io import BytesIO
10
+ from typing import Any, Dict, Union
11
+
12
+ import numpy as np
13
+ from PIL import Image
14
+
15
+ from ..preprocessing import load_image, image_to_bytes
16
+ from .base_model import BaseModel
17
+
18
+
19
+ class CRadio(BaseModel):
20
+ """Dense visual embedding (C-RADIOv4-SO400M).
21
+
22
+ Preferred usage (through :class:`CortexClient`)
23
+ ---------------------------------------------
24
+ ```pycon
25
+ >>> emb = CortexClient().run(ModelType.C_RADIO, image_input=img)
26
+ >>> emb.shape
27
+ (2304,)
28
+ >>> float(np.linalg.norm(emb))
29
+ 1.0
30
+ ```
31
+
32
+ Args (for :py:meth:`run`)
33
+ ------------------------
34
+ image_input: RGB image (path, URL, ``PIL.Image`` or ``np.ndarray``).
35
+
36
+ Returns
37
+ -------
38
+ np.ndarray
39
+ L2-normalized embedding vector (``float32``, 1-D). Cosine similarity
40
+ between two outputs is a plain ``a @ b``.
41
+ """
42
+
43
+ name: str = "c-radio"
44
+ model_id: str = "c-radio"
45
+
46
+ def __init__(self, *, transport=None) -> None:
47
+ """Create a C-RADIO wrapper.
48
+
49
+ Args:
50
+ transport: Optional shared HTTP transport injected by
51
+ :class:`~grid_cortex_client.CortexClient`.
52
+ """
53
+ super().__init__(transport=transport)
54
+
55
+ # ------------------------------------------------------------------
56
+ # BaseModel interface
57
+ # ------------------------------------------------------------------
58
+
59
+ def preprocess(
60
+ self,
61
+ *,
62
+ image_input: Union[str, Image.Image, np.ndarray],
63
+ ) -> Dict[str, Any]:
64
+ """Return msgpack-ready payload with raw JPEG image bytes."""
65
+ pil_img = load_image(image_input)
66
+ return {"image_input": image_to_bytes(pil_img, encoding_format="JPEG")}
67
+
68
+ def postprocess(
69
+ self,
70
+ response_data: Dict[str, Any],
71
+ **_: Any,
72
+ ) -> np.ndarray:
73
+ """Decode backend response into an L2-normalized embedding array.
74
+
75
+ Args:
76
+ response_data: Backend dict (or list-wrapped dict) containing an
77
+ ``"output"`` key holding ``.npy`` bytes for a 1-D float vector.
78
+
79
+ Returns:
80
+ np.ndarray of shape ``(D,)``, dtype ``float32``, L2-normalized.
81
+
82
+ Raises:
83
+ ValueError: If the payload is missing or cannot be decoded.
84
+ """
85
+ if isinstance(response_data, list):
86
+ response_dict = response_data[0]
87
+ else:
88
+ response_dict = response_data
89
+
90
+ emb_bytes = response_dict.get("output")
91
+ if not isinstance(emb_bytes, (bytes, bytearray, memoryview)):
92
+ raise ValueError("'output' field missing or not bytes")
93
+
94
+ arr = np.load(BytesIO(emb_bytes), allow_pickle=False)
95
+ return arr.astype(np.float32)
96
+
97
+ def run(
98
+ self,
99
+ image_input: Union[str, Image.Image, np.ndarray],
100
+ timeout: float | None = None,
101
+ ) -> np.ndarray:
102
+ """Compute an L2-normalized embedding for a single RGB image.
103
+
104
+ Args:
105
+ image_input: RGB image as file path, URL, PIL Image, or numpy array.
106
+ timeout: Optional timeout in seconds for the HTTP request.
107
+
108
+ Returns:
109
+ Embedding as numpy array of shape ``(D,)``, dtype ``float32``,
110
+ L2-normalized so dot products give cosine similarity directly.
111
+
112
+ Raises:
113
+ ValueError: If image cannot be loaded.
114
+ RuntimeError: If no HTTP transport is configured.
115
+ Exception: If the HTTP request fails.
116
+
117
+ Example:
118
+ >>> from grid_cortex_client import CortexClient, ModelType
119
+ >>> import numpy as np
120
+ >>> from PIL import Image
121
+ >>> client = CortexClient()
122
+ >>> image = np.array(Image.open("cat.jpg"))
123
+ >>> emb = client.run(ModelType.C_RADIO, image_input=image)
124
+ >>> print(emb.shape, float(np.linalg.norm(emb))) # (D,) 1.0
125
+ """
126
+ return super().run(
127
+ image_input=image_input,
128
+ timeout=timeout,
129
+ )
@@ -135,6 +135,8 @@ class PI05(BaseModel):
135
135
  gripper: Union[np.ndarray, list],
136
136
  state: Union[np.ndarray, list],
137
137
  prompt: Optional[str] = None,
138
+ rtc_kwargs: Optional[Dict[str, Any]] = None,
139
+ prev_chunk: Optional[Union[np.ndarray, list]] = None,
138
140
  ) -> Dict[str, Any]:
139
141
  """Convert inputs to the payload expected by the server.
140
142
 
@@ -145,6 +147,13 @@ class PI05(BaseModel):
145
147
  gripper: Gripper position.
146
148
  state: Robot state (concatenation of joints and gripper).
147
149
  prompt: Optional task prompt.
150
+ rtc_kwargs: Optional real-time-chunking params. When set, the server
151
+ routes through the RTC path (model.realtime_action). Expected
152
+ keys: ``rtc_current_index`` (steps of the previous chunk already
153
+ executed), ``rtc_frozen_until``, ``rtc_thaw_until``.
154
+ prev_chunk: Previous predicted action chunk, fed back as RTC guidance.
155
+ None on the first call; on later calls pass the ``prev_chunk``
156
+ returned by the previous inference.
148
157
 
149
158
  Returns:
150
159
  Dictionary that will be msgpack-encoded and sent to the server.
@@ -180,6 +189,13 @@ class PI05(BaseModel):
180
189
  "gripper": gripper_arr,
181
190
  "state": state_arr,
182
191
  }
192
+ # Real-time chunking (optional). Presence of rtc_kwargs is what makes the
193
+ # server take the RTC path; prev_chunk is the guidance chunk (None → the
194
+ # server bootstraps one on the first call).
195
+ if rtc_kwargs is not None:
196
+ obs["rtc_kwargs"] = rtc_kwargs
197
+ if prev_chunk is not None:
198
+ obs["prev_chunk"] = np.asarray(prev_chunk, dtype=np.float32)
183
199
 
184
200
  payload = {"obs": obs}
185
201
  if prompt is not None:
@@ -235,6 +251,8 @@ class PI05(BaseModel):
235
251
  gripper: Union[np.ndarray, list],
236
252
  state: Union[np.ndarray, list],
237
253
  prompt: Optional[str] = None,
254
+ rtc_kwargs: Optional[Dict[str, Any]] = None,
255
+ prev_chunk: Optional[Union[np.ndarray, list]] = None,
238
256
  timeout: float | None = None,
239
257
  ) -> Dict[str, Any]:
240
258
  """Run PI05 policy inference on robot observations.
@@ -246,11 +264,17 @@ class PI05(BaseModel):
246
264
  gripper: Gripper position as numpy array or list.
247
265
  state: Robot state as numpy array or list (concatenation of joints and gripper).
248
266
  prompt: Optional task prompt string.
267
+ rtc_kwargs: Optional real-time-chunking params (see :py:meth:`preprocess`).
268
+ When set, the server runs the RTC path and returns a ``prev_chunk``
269
+ field to feed back on the next call.
270
+ prev_chunk: Previous predicted chunk fed back as RTC guidance (None on
271
+ the first call).
249
272
  timeout: Optional timeout in seconds for the HTTP request.
250
273
 
251
274
  Returns:
252
275
  Action dictionary containing:
253
276
  - "actions": numpy array of shape (horizon, action_dim) with predicted actions
277
+ - "prev_chunk": (RTC only) chunk to feed back on the next call
254
278
  - Other fields from policy.infer() (e.g., "state", "policy_timing", etc.)
255
279
 
256
280
  Raises:
@@ -284,5 +308,7 @@ class PI05(BaseModel):
284
308
  gripper=gripper,
285
309
  state=state,
286
310
  prompt=prompt,
311
+ rtc_kwargs=rtc_kwargs,
312
+ prev_chunk=prev_chunk,
287
313
  timeout=timeout,
288
314
  )
@@ -0,0 +1,124 @@
1
+ """Test C-RADIO model."""
2
+
3
+ import os
4
+ import time
5
+
6
+ import numpy as np
7
+ import pytest
8
+
9
+ from grid_cortex_client import AsyncCortexClient, CortexClient, CortexHubClient, ModelType
10
+ from grid_cortex_client.client import CortexAPIError
11
+
12
+ from .conftest import BaseModelTestHTTP, run_concurrent_requests, is_gpu_mem_close
13
+
14
+
15
+ class TestCRadioHTTP(BaseModelTestHTTP):
16
+ """HTTP-based tests for C-RADIO dense visual embedding model."""
17
+
18
+ @property
19
+ def batch_size(self) -> int:
20
+ """C-RADIO model batch size (matches `max_ongoing_requests` in config.yaml)."""
21
+ return 8
22
+
23
+ @property
24
+ def gpu_mem(self) -> float:
25
+ """C-RADIO model GPU memory budget (MB, matches config.yaml `gpu_mem`)."""
26
+ return 2000.0
27
+
28
+ @property
29
+ def max_input_size(self) -> tuple:
30
+ """C-RADIO model maximum input size (width, height)."""
31
+ return (720, 1280)
32
+
33
+ def test_basic_inference(self, client: CortexClient, rgb_image_np: np.ndarray):
34
+ """Test basic embedding extraction with valid input."""
35
+ emb = client.run(ModelType.C_RADIO, image_input=rgb_image_np)
36
+
37
+ assert emb is not None
38
+ assert isinstance(emb, np.ndarray)
39
+ assert emb.ndim == 1, f"emb.ndim {emb.ndim} != 1"
40
+ assert emb.shape[0] == 2304, f"emb dim {emb.shape[0]} != 2304 (C-RADIOv4-SO400M summary)"
41
+ assert emb.dtype == np.float32, f"emb.dtype {emb.dtype} != float32"
42
+ assert np.isfinite(emb).all(), "embedding contains NaN/Inf"
43
+ # L2-normalized → magnitude should be 1.0 within float32 tolerance.
44
+ norm = float(np.linalg.norm(emb))
45
+ assert abs(norm - 1.0) < 1e-3, f"embedding not L2-normalized (||emb|| = {norm})"
46
+
47
+ def test_image_input_list_rejected(self, client: CortexClient, rgb_image_np: np.ndarray):
48
+ """List image_input is rejected — at the client (TypeError) or server (422)."""
49
+ with pytest.raises((TypeError, CortexAPIError)):
50
+ client.run(ModelType.C_RADIO, image_input=[rgb_image_np, rgb_image_np])
51
+
52
+ def test_maximum_payload_size(self, client: CortexClient):
53
+ """Test that inference works with maximum allowed payload size."""
54
+ large_image = np.random.randint(0, 255, (*self.max_input_size, 3), dtype=np.uint8)
55
+ emb = client.run(ModelType.C_RADIO, image_input=large_image)
56
+
57
+ assert emb is not None
58
+ assert isinstance(emb, np.ndarray)
59
+ assert emb.ndim == 1
60
+
61
+ oversized_image = np.random.randint(0, 255, (4096, 4096, 3), dtype=np.uint8)
62
+ with pytest.raises(CortexAPIError) as exc_info:
63
+ client.run(ModelType.C_RADIO, image_input=oversized_image)
64
+ assert exc_info.value.status_code == 413
65
+
66
+ large_image = np.random.randint(0, 255, (*self.max_input_size, 3), dtype=np.uint8)
67
+ emb = client.run(ModelType.C_RADIO, image_input=large_image)
68
+
69
+ assert emb is not None
70
+ assert isinstance(emb, np.ndarray)
71
+ assert emb.ndim == 1
72
+
73
+ def test_inference_latency(self, client: CortexClient, rgb_image_np: np.ndarray):
74
+ """Test that inference latency is within acceptable range."""
75
+ start_time = time.time()
76
+ emb = client.run(ModelType.C_RADIO, image_input=rgb_image_np)
77
+ elapsed_time = time.time() - start_time
78
+
79
+ assert emb is not None
80
+ assert elapsed_time < 10.0, f"Inference took {elapsed_time:.2f}s, expected < 10s"
81
+
82
+ @pytest.mark.asyncio
83
+ async def test_concurrent_requests(self, async_client: AsyncCortexClient, rgb_image_np: np.ndarray):
84
+ """Test that multiple concurrent requests are handled correctly."""
85
+
86
+ async def make_request():
87
+ return await async_client.run(ModelType.C_RADIO, image_input=rgb_image_np)
88
+
89
+ results = await run_concurrent_requests(make_request, num_requests=self.batch_size)
90
+
91
+ assert len(results) == self.batch_size
92
+ assert all(not isinstance(r, Exception) for r in results)
93
+ assert all(isinstance(r, np.ndarray) for r in results)
94
+ assert all(r.ndim == 1 for r in results)
95
+
96
+ @pytest.mark.asyncio
97
+ async def test_gpu_memory_usage(self, async_client: AsyncCortexClient, rgb_image_np: np.ndarray):
98
+ """Test that GPU memory usage is within expected limits during inference."""
99
+ large_image = np.random.randint(0, 255, (*self.max_input_size, 3), dtype=np.uint8)
100
+
101
+ async def make_request():
102
+ return await async_client.run(ModelType.C_RADIO, image_input=large_image, raw_response=True)
103
+
104
+ results = await run_concurrent_requests(make_request, num_requests=self.batch_size)
105
+
106
+ assert len(results) == self.batch_size
107
+ assert all(not isinstance(r, Exception) for r in results)
108
+ assert all(is_gpu_mem_close(self.gpu_mem, result["gpu_stats"]["mem_usage_mb"]) for result in results), (
109
+ f"GPU memory peak too high: {[result['gpu_stats']['mem_usage_mb'] for result in results]} MB"
110
+ )
111
+
112
+ @pytest.mark.asyncio
113
+ async def test_cortex_hub(self, rgb_image_np: np.ndarray):
114
+ """Test C-RADIO inference via CortexHubClient (WebSocket)."""
115
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
116
+ if not api_key:
117
+ pytest.skip("GRID_CORTEX_API_KEY not set")
118
+
119
+ async with CortexHubClient(api_key=api_key) as hub:
120
+ emb = await hub.run(ModelType.C_RADIO, image_input=rgb_image_np)
121
+
122
+ assert emb is not None
123
+ assert isinstance(emb, np.ndarray)
124
+ assert emb.ndim == 1
@@ -268,9 +268,9 @@ class TestContactGraspNetHTTP(BaseModelTestHTTP):
268
268
  camera_intrinsics: np.ndarray,
269
269
  aux_args: dict,
270
270
  ):
271
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
271
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
272
272
  if not api_key:
273
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
273
+ pytest.skip("GRID_CORTEX_API_KEY not set")
274
274
 
275
275
  async with CortexHubClient(api_key=api_key) as hub:
276
276
  result = await hub.run(
@@ -21,9 +21,9 @@ class TestCortexHubClient:
21
21
  @pytest_asyncio.fixture
22
22
  async def hub(self):
23
23
  """Create connected CortexHubClient instance."""
24
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
24
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
25
25
  if not api_key:
26
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
26
+ pytest.skip("GRID_CORTEX_API_KEY not set")
27
27
 
28
28
  hub = CortexHubClient(api_key=api_key)
29
29
  await hub.connect()
@@ -33,9 +33,9 @@ class TestCortexHubClient:
33
33
  @pytest.mark.asyncio
34
34
  async def test_connect_disconnect(self):
35
35
  """Test connection lifecycle."""
36
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
36
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
37
37
  if not api_key:
38
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
38
+ pytest.skip("GRID_CORTEX_API_KEY not set")
39
39
 
40
40
  hub = CortexHubClient(api_key=api_key)
41
41
  assert not hub.is_connected
@@ -49,9 +49,9 @@ class TestCortexHubClient:
49
49
  @pytest.mark.asyncio
50
50
  async def test_context_manager(self, rgb_image_np: np.ndarray):
51
51
  """Test async context manager usage."""
52
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
52
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
53
53
  if not api_key:
54
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
54
+ pytest.skip("GRID_CORTEX_API_KEY not set")
55
55
 
56
56
  async with CortexHubClient(api_key=api_key) as hub:
57
57
  assert hub.is_connected
@@ -290,9 +290,9 @@ class TestCortexHubClient:
290
290
  @pytest.mark.asyncio
291
291
  async def test_not_connected_error(self, rgb_image_np: np.ndarray):
292
292
  """Test error when calling run without connecting."""
293
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
293
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
294
294
  if not api_key:
295
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
295
+ pytest.skip("GRID_CORTEX_API_KEY not set")
296
296
 
297
297
  hub = CortexHubClient(api_key=api_key)
298
298
  # Don't connect
@@ -307,9 +307,9 @@ class TestCortexHubClientPubSub:
307
307
  @pytest_asyncio.fixture
308
308
  async def hub(self):
309
309
  """Create connected CortexHubClient instance."""
310
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
310
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
311
311
  if not api_key:
312
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
312
+ pytest.skip("GRID_CORTEX_API_KEY not set")
313
313
 
314
314
  hub = CortexHubClient(api_key=api_key)
315
315
  await hub.connect()
@@ -416,8 +416,10 @@ class TestCortexHubClientPubSub:
416
416
  class TestCortexHubClientURLResolution:
417
417
  """Tests for URL resolution (no network needed)."""
418
418
 
419
- def test_default_url_is_production(self):
420
- """Test default URL points to production with /ws/ws suffix."""
419
+ def test_default_url_is_prod(self, monkeypatch):
420
+ """Test default URL points to prod with /ws/ws suffix."""
421
+ monkeypatch.delenv("GRID_TARGET_ENV", raising=False)
422
+ monkeypatch.delenv("GRID_CORTEX_BASE_URL", raising=False)
421
423
  hub = CortexHubClient(api_key="test")
422
424
  assert "cortex-prod" in hub.url
423
425
  assert hub.url.endswith("/ws/ws")
@@ -108,9 +108,9 @@ class TestDA3MetricHTTP(BaseModelTestHTTP):
108
108
  @pytest.mark.asyncio
109
109
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
110
110
  """Test DA3Metric inference via CortexHubClient (WebSocket)."""
111
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
111
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
112
112
  if not api_key:
113
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
113
+ pytest.skip("GRID_CORTEX_API_KEY not set")
114
114
 
115
115
  async with CortexHubClient(api_key=api_key) as hub:
116
116
  depth = await hub.run(ModelType.DA3METRIC, image_input=rgb_image_np)
@@ -275,9 +275,9 @@ class TestFastFoundationStereoHTTP(BaseModelTestHTTP):
275
275
  @pytest.mark.asyncio
276
276
  async def test_cortex_hub(self, stereo_pair: tuple, aux_args: dict):
277
277
  """Test Fast-FoundationStereo inference via CortexHubClient (WebSocket)."""
278
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
278
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
279
279
  if not api_key:
280
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
280
+ pytest.skip("GRID_CORTEX_API_KEY not set")
281
281
 
282
282
  left, right = stereo_pair
283
283
  async with CortexHubClient(api_key=api_key) as hub:
@@ -268,9 +268,9 @@ class TestFoundationStereoHTTP(BaseModelTestHTTP):
268
268
  @pytest.mark.asyncio
269
269
  async def test_cortex_hub(self, stereo_pair: tuple, aux_args: dict):
270
270
  """Test FoundationStereo inference via CortexHubClient (WebSocket)."""
271
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
271
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
272
272
  if not api_key:
273
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
273
+ pytest.skip("GRID_CORTEX_API_KEY not set")
274
274
 
275
275
  left, right = stereo_pair
276
276
  async with CortexHubClient(api_key=api_key) as hub:
@@ -363,9 +363,9 @@ class TestGraspGenHTTP(BaseModelTestHTTP):
363
363
  aux_args: dict,
364
364
  ):
365
365
  """Test GraspGen inference via CortexHubClient (WebSocket)."""
366
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
366
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
367
367
  if not api_key:
368
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
368
+ pytest.skip("GRID_CORTEX_API_KEY not set")
369
369
 
370
370
  async with CortexHubClient(api_key=api_key) as hub:
371
371
  result = await hub.run(
@@ -135,9 +135,9 @@ class TestGraspGenXHTTP(BaseModelTestHTTP):
135
135
  @pytest.mark.asyncio
136
136
  async def test_cortex_hub(self, point_cloud: np.ndarray, aux_args: dict):
137
137
  """Inference via CortexHubClient (WebSocket)."""
138
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
138
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
139
139
  if not api_key:
140
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
140
+ pytest.skip("GRID_CORTEX_API_KEY not set")
141
141
  async with CortexHubClient(api_key=api_key) as hub:
142
142
  result = await hub.run(ModelType.GRASPGENX, point_cloud=point_cloud, aux_args=aux_args)
143
143
  assert result is not None
@@ -191,9 +191,9 @@ class TestGSAM2HTTP(BaseModelTestHTTP):
191
191
  @pytest.mark.asyncio
192
192
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
193
193
  """Test GSAM2 inference via CortexHubClient (WebSocket)."""
194
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
194
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
195
195
  if not api_key:
196
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
196
+ pytest.skip("GRID_CORTEX_API_KEY not set")
197
197
 
198
198
  async with CortexHubClient(api_key=api_key) as hub:
199
199
  result = await hub.run(ModelType.GSAM2, image_input=rgb_image_np, prompt="object")
@@ -189,9 +189,9 @@ class TestLocateAnythingHTTP(BaseModelTestHTTP):
189
189
  @pytest.mark.asyncio
190
190
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
191
191
  """Test LocateAnything inference via CortexHubClient (WebSocket)."""
192
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
192
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
193
193
  if not api_key:
194
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
194
+ pytest.skip("GRID_CORTEX_API_KEY not set")
195
195
 
196
196
  async with CortexHubClient(api_key=api_key) as hub:
197
197
  result = await hub.run(ModelType.LOCATE_ANYTHING, image_input=rgb_image_np, task="detect", prompt="cat")
@@ -134,9 +134,9 @@ class TestMetric3DHTTP(BaseModelTestHTTP):
134
134
  @pytest.mark.asyncio
135
135
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
136
136
  """Test Metric3D inference via CortexHubClient (WebSocket)."""
137
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
137
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
138
138
  if not api_key:
139
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
139
+ pytest.skip("GRID_CORTEX_API_KEY not set")
140
140
 
141
141
  async with CortexHubClient(api_key=api_key) as hub:
142
142
  result = await hub.run(ModelType.METRIC3D, image_input=rgb_image_np)
@@ -228,9 +228,9 @@ class TestMoondreamHTTP(BaseModelTestHTTP):
228
228
  @pytest.mark.asyncio
229
229
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
230
230
  """Test Moondream inference via CortexHubClient (WebSocket)."""
231
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
231
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
232
232
  if not api_key:
233
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
233
+ pytest.skip("GRID_CORTEX_API_KEY not set")
234
234
 
235
235
  async with CortexHubClient(api_key=api_key) as hub:
236
236
  result = await hub.run(ModelType.MOONDREAM, image_input=rgb_image_np, task="caption", length="short")
@@ -193,9 +193,9 @@ class TestOneFormerHTTP(BaseModelTestHTTP):
193
193
  @pytest.mark.asyncio
194
194
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
195
195
  """Test OneFormer inference via CortexHubClient (WebSocket)."""
196
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
196
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
197
197
  if not api_key:
198
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
198
+ pytest.skip("GRID_CORTEX_API_KEY not set")
199
199
 
200
200
  async with CortexHubClient(api_key=api_key) as hub:
201
201
  result = await hub.run(ModelType.ONEFORMER, image_input=rgb_image_np, mode="panoptic")
@@ -224,9 +224,9 @@ class TestOWLv2HTTP(BaseModelTestHTTP):
224
224
  @pytest.mark.asyncio
225
225
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
226
226
  """Test OWLv2 inference via CortexHubClient (WebSocket)."""
227
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
227
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
228
228
  if not api_key:
229
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
229
+ pytest.skip("GRID_CORTEX_API_KEY not set")
230
230
 
231
231
  async with CortexHubClient(api_key=api_key) as hub:
232
232
  result = await hub.run(ModelType.OWLV2, image_input=rgb_image_np, prompt="cat . dog")
@@ -194,9 +194,9 @@ class TestPI05HTTP(BaseModelTestHTTP):
194
194
  @pytest.mark.asyncio
195
195
  async def test_cortex_hub(self):
196
196
  """Test PI05 inference via CortexHubClient (WebSocket)."""
197
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
197
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
198
198
  if not api_key:
199
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
199
+ pytest.skip("GRID_CORTEX_API_KEY not set")
200
200
 
201
201
  inputs = self._create_test_inputs()
202
202
  async with CortexHubClient(api_key=api_key) as hub:
@@ -170,9 +170,9 @@ class TestQwenVLHTTP(BaseModelTestHTTP):
170
170
  @pytest.mark.asyncio
171
171
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
172
172
  """Test Qwen VL inference via CortexHubClient (WebSocket)."""
173
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
173
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
174
174
  if not api_key:
175
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
175
+ pytest.skip("GRID_CORTEX_API_KEY not set")
176
176
 
177
177
  async with CortexHubClient(api_key=api_key) as hub:
178
178
  result = await hub.run(ModelType.QWEN_VL, image_input=rgb_image_np, prompt="Describe this image.")
@@ -215,9 +215,9 @@ class TestSAM2HTTP(BaseModelTestHTTP):
215
215
  @pytest.mark.asyncio
216
216
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
217
217
  """Test SAM2 inference via CortexHubClient (WebSocket)."""
218
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
218
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
219
219
  if not api_key:
220
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
220
+ pytest.skip("GRID_CORTEX_API_KEY not set")
221
221
 
222
222
  async with CortexHubClient(api_key=api_key) as hub:
223
223
  result = await hub.run(ModelType.SAM2, image_input=rgb_image_np, prompts=[[320, 240]], labels=[1])
@@ -303,9 +303,9 @@ class TestSAM3HTTP(BaseModelTestHTTP):
303
303
  @pytest.mark.asyncio
304
304
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
305
305
  """Test SAM3 inference via CortexHubClient (WebSocket)."""
306
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
306
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
307
307
  if not api_key:
308
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
308
+ pytest.skip("GRID_CORTEX_API_KEY not set")
309
309
 
310
310
  async with CortexHubClient(api_key=api_key) as hub:
311
311
  result = await hub.run(ModelType.SAM3, image_input=rgb_image_np, text="object")
@@ -185,9 +185,9 @@ class TestUR5ePyrokiCollisionBatchHTTP(BaseModelTestHTTP):
185
185
  @pytest.mark.asyncio
186
186
  async def test_cortex_hub(self, collision_batch_payload: dict):
187
187
  """Test UR5e PyRoKi Collision Batch inference via CortexHubClient (WebSocket)."""
188
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
188
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
189
189
  if not api_key:
190
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
190
+ pytest.skip("GRID_CORTEX_API_KEY not set")
191
191
 
192
192
  async with CortexHubClient(api_key=api_key) as hub:
193
193
  result = await hub.run(ModelType.UR5E_PYROKI_COLLISION_BATCH, **collision_batch_payload)
@@ -244,9 +244,9 @@ class TestUR5ePyrokiFkHTTP(BaseModelTestHTTP):
244
244
  @pytest.mark.asyncio
245
245
  async def test_cortex_hub(self, batch_joint_states_np: np.ndarray):
246
246
  """Test UR5e PyRoKi FK inference via CortexHubClient (WebSocket)."""
247
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
247
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
248
248
  if not api_key:
249
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
249
+ pytest.skip("GRID_CORTEX_API_KEY not set")
250
250
 
251
251
  async with CortexHubClient(api_key=api_key) as hub:
252
252
  result = await hub.run(ModelType.UR5E_PYROKI_FK, batch_joint_states=batch_joint_states_np)
@@ -113,9 +113,9 @@ class TestZoeDepthHTTP(BaseModelTestHTTP):
113
113
  @pytest.mark.asyncio
114
114
  async def test_cortex_hub(self, rgb_image_np: np.ndarray):
115
115
  """Test ZoeDepth inference via CortexHubClient (WebSocket)."""
116
- api_key = os.getenv("GRID_CORTEX_API_KEY") or os.getenv("CORTEX_API_KEY")
116
+ api_key = os.getenv("GRID_CORTEX_API_KEY")
117
117
  if not api_key:
118
- pytest.skip("GRID_CORTEX_API_KEY or CORTEX_API_KEY not set")
118
+ pytest.skip("GRID_CORTEX_API_KEY not set")
119
119
 
120
120
  async with CortexHubClient(api_key=api_key) as hub:
121
121
  depth = await hub.run(ModelType.ZOEDEPTH, image_input=rgb_image_np)