Skip to content

Commit cda03cc

Browse files
cursor[bot]cursoragentblainekasten
authored
fix: handle optional endpoint deployment hardware in CLI (#468)
* fix: handle missing endpoint deployment hardware in CLI Co-authored-by: Blaine Kasten <blainekasten@gmail.com> * test: cover endpoint list without deployment hardware Co-authored-by: Blaine Kasten <blainekasten@gmail.com> * test: assert nullable deployment hardware renders Co-authored-by: Blaine Kasten <blainekasten@gmail.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Blaine Kasten <blainekasten@gmail.com>
1 parent c6b9a0b commit cda03cc

2 files changed

Lines changed: 40 additions & 1 deletion

File tree

src/together/lib/cli/api/beta/endpoints/list.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,12 @@ def _format_replicas(deployment: EndpointDeploymentSummary) -> str:
188188
return f"{ready}/{desired} ready"
189189

190190

191-
def _prettify_hardware(hardware: str) -> str:
191+
def _prettify_hardware(hardware: str | None) -> str:
192192
import re
193193

194+
if hardware is None:
195+
return "[dim]—[/dim]"
196+
194197
match = re.search(r"(\d+)x(.*)", hardware)
195198
if match:
196199
count, hw_type = match.groups()

tests/cli/test_beta_endpoints.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ def test_deploy_creates_endpoint_deployment_and_traffic_split(
160160
"model": "projects/proj/models/ml_1/revisions/latest",
161161
"modelId": "ml_1",
162162
"hardware": "1x-h100",
163+
"estimatedEffectiveTrafficShare": 1,
163164
"state": "DEPLOYMENT_STATE_READY",
164165
"readyReplicas": 1,
165166
"desiredReplicas": 1,
@@ -253,6 +254,41 @@ def test_list_sends_cursor_pagination(self, respx_mock: MockRouter, cli_runner:
253254
assert "after=tok" in url
254255
assert json.loads(result.output)["next_cursor"] == "next"
255256

257+
@pytest.mark.respx(base_url=base_url)
258+
def test_list_handles_deployment_without_hardware(self, respx_mock: MockRouter, cli_runner: CliRunner) -> None:
259+
_mock_model_and_config(respx_mock)
260+
respx_mock.get("/projects/proj/endpoints").mock(
261+
return_value=httpx.Response(
262+
200,
263+
json={
264+
"object": "list",
265+
"data": [
266+
_endpoint_body(
267+
deployments=[
268+
{
269+
"id": "dep_1",
270+
"name": "my-project/my-endpoint/my-dep",
271+
"model": "projects/proj/models/ml_1/revisions/latest",
272+
"modelId": "ml_1",
273+
"estimatedEffectiveTrafficShare": 1,
274+
"state": "DEPLOYMENT_STATE_READY",
275+
"readyReplicas": 1,
276+
"desiredReplicas": 1,
277+
"createdAt": "2026-01-01T00:00:00Z",
278+
"autoscaling": {"minReplicas": 1, "maxReplicas": 1},
279+
}
280+
],
281+
)
282+
],
283+
"next_cursor": None,
284+
},
285+
)
286+
)
287+
288+
result = cli_runner.invoke(["beta", "endpoints", "ls", "--project", "proj"])
289+
290+
assert result.exit_code == 0, result.output
291+
256292
@pytest.mark.respx(base_url=base_url)
257293
def test_list_org_scoped(self, respx_mock: MockRouter, cli_runner: CliRunner) -> None:
258294
respx_mock.get("/whoami").mock(return_value=httpx.Response(200, json=_whoami_body()))

0 commit comments

Comments
 (0)