kscale 0.3.10__py3-none-any.whl → 0.3.12__py3-none-any.whl

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.
kscale/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Defines the common interface for the K-Scale Python API."""
2
2
 
3
- __version__ = "0.3.10"
3
+ __version__ = "0.3.12"
4
4
 
5
5
  from pathlib import Path
6
6
 
@@ -41,10 +41,11 @@ async def list() -> None:
41
41
  click.style(rc.id, fg="blue"),
42
42
  click.style(rc.class_name, fg="green"),
43
43
  rc.description or "N/A",
44
+ "N/A" if rc.num_downloads is None else f"{rc.num_downloads:,}",
44
45
  ]
45
46
  for rc in robot_classes
46
47
  ]
47
- click.echo(tabulate(table_data, headers=["ID", "Name", "Description"], tablefmt="simple"))
48
+ click.echo(tabulate(table_data, headers=["ID", "Name", "Description", "Downloads"], tablefmt="simple"))
48
49
  else:
49
50
  click.echo(click.style("No robot classes found", fg="red"))
50
51
 
@@ -9,7 +9,7 @@ import sys
9
9
  import time
10
10
  import webbrowser
11
11
  from types import TracebackType
12
- from typing import Any, Self, Type
12
+ from typing import Any, Mapping, Self, Type
13
13
  from urllib.parse import urljoin
14
14
 
15
15
  import aiohttp
@@ -363,6 +363,7 @@ class BaseClient:
363
363
  params: dict[str, Any] | None = None,
364
364
  data: BaseModel | dict[str, Any] | None = None,
365
365
  files: dict[str, Any] | None = None,
366
+ error_code_suggestions: dict[int, str] | None = None,
366
367
  ) -> dict[str, Any]:
367
368
  url = urljoin(self.base_url, endpoint)
368
369
  kwargs: dict[str, Any] = {}
@@ -380,12 +381,27 @@ class BaseClient:
380
381
  response = await client.request(method, url, **kwargs)
381
382
 
382
383
  if response.is_error:
383
- logger.error("Got %d error K-Scale: %s", response.status_code, response.text)
384
- if verbose_error():
384
+ error_code = response.status_code
385
+ error_json = response.json()
386
+ use_verbose_error = verbose_error()
387
+
388
+ if not use_verbose_error:
389
+ logger.info("Use KSCALE_VERBOSE_ERROR=1 to see the full error message")
390
+ logger.info("If this persists, please create an issue here: https://github.com/kscalelabs/kscale")
391
+
392
+ logger.error("Got error %d from the K-Scale API", error_code)
393
+ if isinstance(error_json, Mapping):
394
+ for key, value in error_json.items():
395
+ logger.error(" [%s] %s", key, value)
396
+ else:
397
+ logger.error(" %s", error_json)
398
+
399
+ if error_code_suggestions is not None and error_code in error_code_suggestions:
400
+ logger.error("Hint: %s", error_code_suggestions[error_code])
401
+
402
+ if use_verbose_error:
385
403
  response.raise_for_status()
386
404
  else:
387
- logger.error("Use KSCALE_VERBOSE_ERROR=1 to see the full error message")
388
- logger.error("If this persists, please create an issue here: https://github.com/kscalelabs/kscale")
389
405
  sys.exit(1)
390
406
 
391
407
  return response.json()
@@ -31,7 +31,7 @@ class RobotClassClient(BaseClient):
31
31
  data = await self._request(
32
32
  "GET",
33
33
  "/robots/",
34
- auth=True,
34
+ auth=False,
35
35
  )
36
36
  return [RobotClass.model_validate(item) for item in data]
37
37
 
@@ -39,7 +39,7 @@ class RobotClassClient(BaseClient):
39
39
  data = await self._request(
40
40
  "GET",
41
41
  f"/robots/name/{class_name}",
42
- auth=True,
42
+ auth=False,
43
43
  )
44
44
  return RobotClass.model_validate(data)
45
45
 
@@ -117,7 +117,12 @@ class RobotClassClient(BaseClient):
117
117
  cache_path = get_robots_dir() / class_name / "robot.tgz"
118
118
  if cache and cache_path.exists() and not should_refresh_file(cache_path):
119
119
  return cache_path
120
- data = await self._request("GET", f"/robots/urdf/{class_name}", auth=True)
120
+ data = await self._request(
121
+ "GET",
122
+ f"/robots/urdf/{class_name}",
123
+ auth=False,
124
+ error_code_suggestions={404: "Use `kscale robot list` to view classes."},
125
+ )
121
126
  response = RobotDownloadURDFResponse.model_validate(data)
122
127
  expected_hash = response.md5_hash
123
128
  cache_path.parent.mkdir(parents=True, exist_ok=True)
kscale/web/gen/api.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # generated by datamodel-codegen:
4
4
  # filename: openapi.json
5
- # timestamp: 2025-01-22T23:12:27+00:00
5
+ # timestamp: 2025-05-03T21:02:12+00:00
6
6
 
7
7
  from __future__ import annotations
8
8
 
@@ -37,8 +37,8 @@ class JointMetadataInput(BaseModel):
37
37
  offset: Optional[Union[float, str]] = Field(None, title="Offset")
38
38
  flipped: Optional[bool] = Field(None, title="Flipped")
39
39
  actuator_type: Optional[str] = Field(None, title="Actuator Type")
40
- nn_id: Optional[int] = Field(None, title="Neural Network Id")
41
- soft_torque_limit: Optional[str] = Field(None, title="Soft Torque Limit")
40
+ nn_id: Optional[int] = Field(None, title="Nn Id")
41
+ soft_torque_limit: Optional[Union[float, str]] = Field(None, title="Soft Torque Limit")
42
42
 
43
43
 
44
44
  class JointMetadataOutput(BaseModel):
@@ -50,6 +50,8 @@ class JointMetadataOutput(BaseModel):
50
50
  offset: Optional[str] = Field(None, title="Offset")
51
51
  flipped: Optional[bool] = Field(None, title="Flipped")
52
52
  actuator_type: Optional[str] = Field(None, title="Actuator Type")
53
+ nn_id: Optional[int] = Field(None, title="Nn Id")
54
+ soft_torque_limit: Optional[str] = Field(None, title="Soft Torque Limit")
53
55
 
54
56
 
55
57
  class OICDInfo(BaseModel):
@@ -139,3 +141,4 @@ class RobotClass(BaseModel):
139
141
  description: str = Field(..., title="Description")
140
142
  user_id: str = Field(..., title="User Id")
141
143
  metadata: Optional[RobotURDFMetadataOutput] = None
144
+ num_downloads: Optional[int] = Field(0, title="Num Downloads")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kscale
3
- Version: 0.3.10
3
+ Version: 0.3.12
4
4
  Summary: The kscale project
5
5
  Home-page: https://github.com/kscalelabs/kscale
6
6
  Author: Benjamin Bolte
@@ -1,4 +1,4 @@
1
- kscale/__init__.py,sha256=OrJ0sOz8gJCA-Z3BRE8pwaZr9SYTmtm1K4gKbvdtpI8,201
1
+ kscale/__init__.py,sha256=dvs5DUTbLSWgAycehACzl60WVsJdFN7yCMHoTvxFI38,201
2
2
  kscale/cli.py,sha256=JvaPtmWvF7s0D4I3K98eZAItf3oOi2ULsn5aPGxDcu4,795
3
3
  kscale/conf.py,sha256=dm35XSnzJp93St-ixVtYN4Nvqvb5upPGBrWkSI6Yb-4,1743
4
4
  kscale/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -15,19 +15,19 @@ kscale/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  kscale/web/utils.py,sha256=Mme-FAQ0_zbjjOQeX8wyq8F4kL4i9fH7ytri16U6qOA,1046
16
16
  kscale/web/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  kscale/web/cli/robot.py,sha256=rI-A4_0uvJPeA71Apl4Z3mV5fIfWkgmzT9JRmJYxz3A,3307
18
- kscale/web/cli/robot_class.py,sha256=DLB5ok69JAeUUeA7Yy80VmyZGFt9dKf8kD0vSBMmqP8,18987
18
+ kscale/web/cli/robot_class.py,sha256=Gzg1NTD1iDf04jPVRz_fCKVAjnQ3pmA7dB3KmUjxXwk,19080
19
19
  kscale/web/cli/user.py,sha256=9IGsJBPyhjsmT04mZ2RGOs35ePfqB2RltYP0Ty5zF5o,1290
20
20
  kscale/web/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- kscale/web/clients/base.py,sha256=ThQakuvGh7CquiGQ4wiCyCWAgqmKInsJcANo910X9Uw,15585
21
+ kscale/web/clients/base.py,sha256=P5AZdoawNpocgx_8j5v0eauowG5suUf8SnnD7q1_gx0,16213
22
22
  kscale/web/clients/client.py,sha256=rzW2s8T7bKVuybOSQ65-ghl02rcXBoOxnx_nUDwgEPw,362
23
23
  kscale/web/clients/robot.py,sha256=PI8HHkU-4Re9I5rLpp6dGbekRE-rBNVfXZxR_mO2MqE,1485
24
- kscale/web/clients/robot_class.py,sha256=G8Nk6V7LGJE9Wpg9tyyCkIfz1fRTsxXQRgHtleiUVqo,6834
24
+ kscale/web/clients/robot_class.py,sha256=mgA-95P8lR0eCIF7-9f7oPE5tQ8tYLOIU7a83dFiqCs,6970
25
25
  kscale/web/clients/user.py,sha256=jsa1_s6qXRM-AGBbHlPhd1NierUtynjY9tVAPNr6_Os,568
26
26
  kscale/web/gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- kscale/web/gen/api.py,sha256=ZzOtY6zC8mgLfzmCcb7tN2rj8Bt9XUC_JBMCfJPt5ww,4946
28
- kscale-0.3.10.dist-info/licenses/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
29
- kscale-0.3.10.dist-info/METADATA,sha256=adNko0KwPbaTni8Bk3yi67kB3bUmgROXp0sgtWInVIs,2343
30
- kscale-0.3.10.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
31
- kscale-0.3.10.dist-info/entry_points.txt,sha256=N_0pCpPnwGDYVzOeuaSOrbJkS5L3lS9d8CxpJF1f8UI,62
32
- kscale-0.3.10.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
33
- kscale-0.3.10.dist-info/RECORD,,
27
+ kscale/web/gen/api.py,sha256=VSTHOLFtsjH_9mGYChW0iDbtX5FlLU8Itz5MzDb4maE,5147
28
+ kscale-0.3.12.dist-info/licenses/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
29
+ kscale-0.3.12.dist-info/METADATA,sha256=Pqgb5F9FCKrg6aoVgzHzmqKTT229TOMWOBqOa1e9SfM,2343
30
+ kscale-0.3.12.dist-info/WHEEL,sha256=GHB6lJx2juba1wDgXDNlMTyM13ckjBMKf-OnwgKOCtA,91
31
+ kscale-0.3.12.dist-info/entry_points.txt,sha256=N_0pCpPnwGDYVzOeuaSOrbJkS5L3lS9d8CxpJF1f8UI,62
32
+ kscale-0.3.12.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
33
+ kscale-0.3.12.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.1.0)
2
+ Generator: setuptools (80.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5