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.
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/CLAUDE.md +2 -2
- grid_cortex_client-0.6.1/MIGRATION.md +19 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/PKG-INFO +1 -1
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/pyproject.toml +1 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/client.py +3 -4
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/cortex_hub_client.py +3 -3
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/model_type.py +43 -21
- grid_cortex_client-0.6.1/src/grid_cortex_client/models/c_radio.py +129 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/pi05.py +26 -0
- grid_cortex_client-0.6.1/tests/test_c_radio.py +124 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_contact_graspnet.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_cortex_hub_client.py +14 -12
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_da3metric.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_fast_foundation_stereo.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_foundation_stereo.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_graspgen.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_graspgenx.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_gsam2.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_locate_anything.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_metric3d.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_moondream.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_oneformer.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_owlv2.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_pi05.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_qwen_vl.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_sam2.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_sam3.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_ur5e_pyroki_collision_batch.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_ur5e_pyroki_fk.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_zoedepth.py +2 -2
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/.gitignore +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/README.md +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/__init__.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/cortex_client.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/__init__.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/base_model.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/contact_graspnet.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/da3metric.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/fast_foundation_stereo.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/finetune_pi05.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/foundation_stereo.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/graspgen.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/graspgenx.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/gsam2.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/locate_anything.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/metric3d.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/moondream.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/oneformer.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/owlv2.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/qwen_vl.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/sam2.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/sam3.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/ur5e_pyroki_collision_batch.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/ur5e_pyroki_fk.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/zoedepth.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/preprocessing.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/tools/__init__.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/tools/generate_enum.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/tools/registry.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/utils.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/ws.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/README.md +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/__init__.py +0 -0
- {grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/conftest.py +0 -0
- {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
|
|
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.
|
|
@@ -4,7 +4,7 @@ import httpx
|
|
|
4
4
|
import logging
|
|
5
5
|
from . import utils as _u
|
|
6
6
|
|
|
7
|
-
|
|
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",
|
|
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",
|
|
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)
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/cortex_hub_client.py
RENAMED
|
@@ -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")
|
|
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
|
|
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
|
-
|
|
16
|
+
C_RADIO = "c-radio"
|
|
17
17
|
"""
|
|
18
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
36
|
-
-
|
|
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
|
|
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
|
|
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")
|
|
271
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
272
272
|
if not api_key:
|
|
273
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
24
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
25
25
|
if not api_key:
|
|
26
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
36
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
37
37
|
if not api_key:
|
|
38
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
52
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
53
53
|
if not api_key:
|
|
54
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
293
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
294
294
|
if not api_key:
|
|
295
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
310
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
311
311
|
if not api_key:
|
|
312
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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
|
|
420
|
-
"""Test default URL points to
|
|
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")
|
|
111
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
112
112
|
if not api_key:
|
|
113
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
278
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
279
279
|
if not api_key:
|
|
280
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
271
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
272
272
|
if not api_key:
|
|
273
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
366
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
367
367
|
if not api_key:
|
|
368
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
138
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
139
139
|
if not api_key:
|
|
140
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
194
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
195
195
|
if not api_key:
|
|
196
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
192
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
193
193
|
if not api_key:
|
|
194
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
137
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
138
138
|
if not api_key:
|
|
139
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
231
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
232
232
|
if not api_key:
|
|
233
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
196
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
197
197
|
if not api_key:
|
|
198
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
227
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
228
228
|
if not api_key:
|
|
229
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
197
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
198
198
|
if not api_key:
|
|
199
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
173
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
174
174
|
if not api_key:
|
|
175
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
218
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
219
219
|
if not api_key:
|
|
220
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
306
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
307
307
|
if not api_key:
|
|
308
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/tests/test_ur5e_pyroki_collision_batch.py
RENAMED
|
@@ -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")
|
|
188
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
189
189
|
if not api_key:
|
|
190
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
247
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
248
248
|
if not api_key:
|
|
249
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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")
|
|
116
|
+
api_key = os.getenv("GRID_CORTEX_API_KEY")
|
|
117
117
|
if not api_key:
|
|
118
|
-
pytest.skip("GRID_CORTEX_API_KEY
|
|
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)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/cortex_client.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/__init__.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/base_model.py
RENAMED
|
File without changes
|
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/da3metric.py
RENAMED
|
File without changes
|
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/finetune_pi05.py
RENAMED
|
File without changes
|
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/graspgen.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/graspgenx.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/gsam2.py
RENAMED
|
File without changes
|
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/metric3d.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/moondream.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/oneformer.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/owlv2.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/qwen_vl.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/models/zoedepth.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/preprocessing.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/tools/__init__.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/tools/generate_enum.py
RENAMED
|
File without changes
|
{grid_cortex_client-0.5.1 → grid_cortex_client-0.6.1}/src/grid_cortex_client/tools/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|