humalab 0.0.3__py3-none-any.whl → 0.0.5__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.

Potentially problematic release.


This version of humalab might be problematic. Click here for more details.

@@ -0,0 +1,4 @@
1
+ from .resource_manager import ResourceManager
2
+ from .files import ResourceFile, URDFFile
3
+
4
+ __all__ = ["ResourceManager", "ResourceFile", "URDFFile"]
@@ -0,0 +1,4 @@
1
+ from .resource_file import ResourceFile
2
+ from .urdf_file import URDFFile
3
+
4
+ __all__ = ["ResourceFile", "URDFFile"]
@@ -0,0 +1,41 @@
1
+ from datetime import datetime
2
+
3
+
4
+ class ResourceFile:
5
+ def __init__(self,
6
+ name: str,
7
+ version: int,
8
+ filename: str,
9
+ resource_type: str,
10
+ description: str | None = None,
11
+ created_at: datetime | None = None):
12
+ self._name = name
13
+ self._version = version
14
+ self._filename = filename
15
+ self._resource_type = resource_type
16
+ self._description = description
17
+ self._created_at = created_at
18
+
19
+ @property
20
+ def name(self) -> str:
21
+ return self._name
22
+
23
+ @property
24
+ def version(self) -> int:
25
+ return self._version
26
+
27
+ @property
28
+ def filename(self) -> str:
29
+ return self._filename
30
+
31
+ @property
32
+ def resource_type(self) -> str:
33
+ return self._resource_type
34
+
35
+ @property
36
+ def created_at(self) -> datetime | None:
37
+ return self._created_at
38
+
39
+ @property
40
+ def description(self) -> str | None:
41
+ return self._description
@@ -0,0 +1,65 @@
1
+ from datetime import datetime
2
+ import os
3
+ import glob
4
+ from humalab.assets.files.resource_file import ResourceFile
5
+ from humalab.assets.archive import extract_archive
6
+
7
+
8
+ class URDFFile(ResourceFile):
9
+ def __init__(self,
10
+ name: str,
11
+ version: int,
12
+ filename: str,
13
+ urdf_filename: str | None = None,
14
+ description: str | None = None,
15
+ created_at: datetime | None = None,):
16
+ super().__init__(name=name,
17
+ version=version,
18
+ description=description,
19
+ filename=filename,
20
+ resource_type="URDF",
21
+ created_at=created_at)
22
+ self._urdf_base_filename = urdf_filename
23
+ self._urdf_filename, self._root_path = self._extract()
24
+ self._urdf_filename = os.path.join(self._urdf_filename, self._urdf_filename)
25
+
26
+ def _extract(self):
27
+ working_path = os.path.dirname(self._filename)
28
+ if os.path.exists(self._filename):
29
+ _, ext = os.path.splitext(self._filename)
30
+ ext = ext.lstrip('.') # Remove leading dot
31
+ if ext.lower() != "urdf":
32
+ extract_archive(self._filename, working_path)
33
+ try:
34
+ os.remove(self._filename)
35
+ except Exception as e:
36
+ print(f"Error removing saved file {self._filename}: {e}")
37
+ local_filename = self.search_resource_file(self._urdf_base_filename, working_path)
38
+ if local_filename is None:
39
+ raise ValueError(f"Resource filename {self._urdf_base_filename} not found in {working_path}")
40
+ return local_filename, working_path
41
+
42
+ def search_resource_file(self, resource_filename: str | None, working_path: str) -> str | None:
43
+ found_filename = None
44
+ if resource_filename:
45
+ search_path = os.path.join(working_path, "**")
46
+ search_pattern = os.path.join(search_path, resource_filename)
47
+ files = glob.glob(search_pattern, recursive=True)
48
+ if len(files) > 0:
49
+ found_filename = files[0]
50
+
51
+ if found_filename is None:
52
+ ext = "urdf"
53
+ search_pattern = os.path.join(working_path, "**", f"*.{ext}")
54
+ files = glob.glob(search_pattern, recursive=True)
55
+ if len(files) > 0:
56
+ found_filename = files[0]
57
+ return found_filename
58
+
59
+ @property
60
+ def urdf_filename(self) -> str | None:
61
+ return self._urdf_filename
62
+
63
+ @property
64
+ def root_path(self) -> str:
65
+ return self._root_path
@@ -0,0 +1,58 @@
1
+ from humalab.assets.files.resource_file import ResourceFile
2
+ from humalab.humalab_config import HumalabConfig
3
+ from humalab.humalab_api_client import HumaLabApiClient
4
+ from humalab.assets.files.urdf_file import URDFFile
5
+ import os
6
+ from typing import Any
7
+
8
+
9
+ class ResourceManager:
10
+ def __init__(self,
11
+ api_key: str | None = None,
12
+ host: str | None = None,
13
+ timeout: float | None = None):
14
+ self._humalab_config = HumalabConfig()
15
+ self._base_url = host or self._humalab_config.base_url
16
+ self._api_key = api_key or self._humalab_config.api_key
17
+ self._timeout = timeout or self._humalab_config.timeout
18
+
19
+ self._api_client = HumaLabApiClient(base_url=self._base_url,
20
+ api_key=self._api_key,
21
+ timeout=self._timeout)
22
+
23
+ def _asset_dir(self, name: str, version: int) -> str:
24
+ return os.path.join(self._humalab_config.workspace_path, "assets", name, f"{version}")
25
+
26
+ def _create_asset_dir(self, name: str, version: int) -> bool:
27
+ asset_dir = self._asset_dir(name, version)
28
+ if not os.path.exists(asset_dir):
29
+ os.makedirs(asset_dir, exist_ok=True)
30
+ return True
31
+ return False
32
+
33
+ def download(self,
34
+ project: str,
35
+ name: str,
36
+ version: int | None=None) -> Any:
37
+ resource = self._api_client.get_resource(project_name=project, name=name, version=version)
38
+ filename = os.path.basename(resource['resource_url'])
39
+ filename = os.path.join(self._asset_dir(name, resource["version"]), filename)
40
+ if self._create_asset_dir(name, resource["version"]):
41
+ file_content = self._api_client.download_resource(project_name=project, name="lerobot")
42
+ with open(filename, "wb") as f:
43
+ f.write(file_content)
44
+
45
+ if resource["resource_type"].lower() == "urdf":
46
+ return URDFFile(name=resource["name"],
47
+ version=resource["version"],
48
+ description=resource.get("description"),
49
+ filename=filename,
50
+ urdf_filename=resource.get("filename"),
51
+ created_at=resource.get("created_at"))
52
+
53
+ return ResourceFile(name=name,
54
+ version=resource["version"],
55
+ filename=filename,
56
+ resource_type=resource["resource_type"],
57
+ description=resource.get("description"),
58
+ created_at=resource.get("created_at"))
@@ -20,6 +20,21 @@ class Bernoulli(Distribution):
20
20
  self._p = p
21
21
  self._size = size
22
22
 
23
+ @staticmethod
24
+ def validate(dimensions: int, *args) -> bool:
25
+ arg1 = args[0]
26
+ if dimensions == 0:
27
+ if not isinstance(arg1, (int, float)):
28
+ return False
29
+ return True
30
+ if dimensions == -1:
31
+ return True
32
+ if not isinstance(arg1, (int, float)):
33
+ if isinstance(arg1, (list, np.ndarray)):
34
+ if len(arg1) > dimensions:
35
+ return False
36
+ return True
37
+
23
38
  def _sample(self) -> int | float | np.ndarray:
24
39
  return self._generator.binomial(n=1, p=self._p, size=self._size)
25
40
 
@@ -20,8 +20,15 @@ class Categorical(Distribution):
20
20
  super().__init__(generator=generator)
21
21
  self._choices = choices
22
22
  self._size = size
23
+ if weights is not None and not np.isclose(sum(weights), 1.0):
24
+ weight_sum = sum(weights)
25
+ weights = [w / weight_sum for w in weights]
23
26
  self._weights = weights
24
27
 
28
+ @staticmethod
29
+ def validate(dimensions: int, *args) -> bool:
30
+ return True
31
+
25
32
  def _sample(self) -> int | float | np.ndarray:
26
33
  return self._generator.choice(self._choices, size=self._size, p=self._weights)
27
34
 
humalab/dists/discrete.py CHANGED
@@ -26,6 +26,28 @@ class Discrete(Distribution):
26
26
  self._high = np.array(high)
27
27
  self._size = size
28
28
  self._endpoint = endpoint if endpoint is not None else True
29
+
30
+ @staticmethod
31
+ def validate(dimensions: int, *args) -> bool:
32
+ arg1 = args[0]
33
+ arg2 = args[1]
34
+ if dimensions == 0:
35
+ if not isinstance(arg1, int):
36
+ return False
37
+ if not isinstance(arg2, int):
38
+ return False
39
+ return True
40
+ if dimensions == -1:
41
+ return True
42
+ if not isinstance(arg1, int):
43
+ if isinstance(arg1, (list, np.ndarray)):
44
+ if len(arg1) > dimensions:
45
+ return False
46
+ if not isinstance(arg2, int):
47
+ if isinstance(arg2, (list, np.ndarray)):
48
+ if len(arg2) > dimensions:
49
+ return False
50
+ return True
29
51
 
30
52
  def _sample(self) -> int | float | np.ndarray:
31
53
  return self._generator.integers(self._low, self._high, size=self._size, endpoint=self._endpoint)
humalab/dists/gaussian.py CHANGED
@@ -23,6 +23,28 @@ class Gaussian(Distribution):
23
23
  self._scale = scale
24
24
  self._size = size
25
25
 
26
+ @staticmethod
27
+ def validate(dimensions: int, *args) -> bool:
28
+ arg1 = args[0]
29
+ arg2 = args[1]
30
+ if dimensions == 0:
31
+ if not isinstance(arg1, (int, float)):
32
+ return False
33
+ if not isinstance(arg2, (int, float)):
34
+ return False
35
+ return True
36
+ if dimensions == -1:
37
+ return True
38
+ if not isinstance(arg1, (int, float)):
39
+ if isinstance(arg1, (list, np.ndarray)):
40
+ if len(arg1) > dimensions:
41
+ return False
42
+ if not isinstance(arg2, (int, float)):
43
+ if isinstance(arg2, (list, np.ndarray)):
44
+ if len(arg2) > dimensions:
45
+ return False
46
+ return True
47
+
26
48
  def _sample(self) -> int | float | np.ndarray:
27
49
  return self._generator.normal(loc=self._loc, scale=self._scale, size=self._size)
28
50
 
@@ -22,6 +22,28 @@ class LogUniform(Distribution):
22
22
  self._log_low = np.log(np.array(low))
23
23
  self._log_high = np.log(np.array(high))
24
24
  self._size = size
25
+
26
+ @staticmethod
27
+ def validate(dimensions: int, *args) -> bool:
28
+ arg1 = args[0]
29
+ arg2 = args[1]
30
+ if dimensions == 0:
31
+ if not isinstance(arg1, (int, float)):
32
+ return False
33
+ if not isinstance(arg2, (int, float)):
34
+ return False
35
+ return True
36
+ if dimensions == -1:
37
+ return True
38
+ if not isinstance(arg1, (int, float)):
39
+ if isinstance(arg1, (list, np.ndarray)):
40
+ if len(arg1) > dimensions:
41
+ return False
42
+ if not isinstance(arg2, (int, float)):
43
+ if isinstance(arg2, (list, np.ndarray)):
44
+ if len(arg2) > dimensions:
45
+ return False
46
+ return True
25
47
 
26
48
  def _sample(self) -> int | float | np.ndarray:
27
49
  return np.exp(self._generator.uniform(self._log_low, self._log_high, size=self._size))
@@ -29,6 +29,42 @@ class TruncatedGaussian(Distribution):
29
29
  self._high = high
30
30
  self._size = size
31
31
 
32
+ @staticmethod
33
+ def validate(dimensions: int, *args) -> bool:
34
+ arg1 = args[0]
35
+ arg2 = args[1]
36
+ arg3 = args[2]
37
+ arg4 = args[3]
38
+ if dimensions == 0:
39
+ if not isinstance(arg1, (int, float)):
40
+ return False
41
+ if not isinstance(arg2, (int, float)):
42
+ return False
43
+ if not isinstance(arg3, (int, float)):
44
+ return False
45
+ if not isinstance(arg4, (int, float)):
46
+ return False
47
+ return True
48
+ if dimensions == -1:
49
+ return True
50
+ if not isinstance(arg1, (int, float)):
51
+ if isinstance(arg1, (list, np.ndarray)):
52
+ if len(arg1) > dimensions:
53
+ return False
54
+ if not isinstance(arg2, (int, float)):
55
+ if isinstance(arg2, (list, np.ndarray)):
56
+ if len(arg2) > dimensions:
57
+ return False
58
+ if not isinstance(arg3, (int, float)):
59
+ if isinstance(arg3, (list, np.ndarray)):
60
+ if len(arg3) > dimensions:
61
+ return False
62
+ if not isinstance(arg4, (int, float)):
63
+ if isinstance(arg4, (list, np.ndarray)):
64
+ if len(arg4) > dimensions:
65
+ return False
66
+ return True
67
+
32
68
  def _sample(self) -> int | float | np.ndarray:
33
69
  samples = self._generator.normal(loc=self._loc, scale=self._scale, size=self._size)
34
70
  mask = (samples < self._low) | (samples > self._high)
humalab/dists/uniform.py CHANGED
@@ -23,6 +23,28 @@ class Uniform(Distribution):
23
23
  self._high = np.array(high)
24
24
  self._size = size
25
25
 
26
+ @staticmethod
27
+ def validate(dimensions: int, *args) -> bool:
28
+ arg1 = args[0]
29
+ arg2 = args[1]
30
+ if dimensions == 0:
31
+ if not isinstance(arg1, (int, float)):
32
+ return False
33
+ if not isinstance(arg2, (int, float)):
34
+ return False
35
+ return True
36
+ if dimensions == -1:
37
+ return True
38
+ if not isinstance(arg1, (int, float)):
39
+ if isinstance(arg1, (list, np.ndarray)):
40
+ if len(arg1) > dimensions:
41
+ return False
42
+ if not isinstance(arg2, (int, float)):
43
+ if isinstance(arg2, (list, np.ndarray)):
44
+ if len(arg2) > dimensions:
45
+ return False
46
+ return True
47
+
26
48
  def _sample(self) -> int | float | np.ndarray:
27
49
  return self._generator.uniform(self._low, self._high, size=self._size)
28
50
 
humalab/episode.py ADDED
@@ -0,0 +1,26 @@
1
+
2
+ from humalab.scenario import Scenario
3
+ from omegaconf import DictConfig, OmegaConf
4
+
5
+
6
+ class Episode:
7
+ def __init__(self, run_id: str, episode_id: str, scenario_conf: DictConfig):
8
+ self.run_id = run_id
9
+ self.episode_id = episode_id
10
+ self.scenario_conf = scenario_conf
11
+
12
+ @property
13
+ def scenario(self) -> DictConfig:
14
+ return self.scenario_conf
15
+
16
+ def finish(self):
17
+ print(f"Finishing episode {self.episode_id} for scenario {self.scenario.name}")
18
+
19
+ @property
20
+ def yaml(self) -> str:
21
+ """The current scenario configuration as a YAML string.
22
+
23
+ Returns:
24
+ str: The current scenario as a YAML string.
25
+ """
26
+ return OmegaConf.to_yaml(self.scenario_conf)
@@ -0,0 +1,16 @@
1
+ import random
2
+
3
+ def evaluate(video_path: str | list, task: str | list) -> dict | list[dict]:
4
+ """
5
+ Evaluate the video and return metrics.
6
+ """
7
+ # Placeholder implementation
8
+ if isinstance(video_path, list):
9
+ return [{"video_path": vp, "task": t,
10
+ "score": random.uniform(0, 1),
11
+ "confidence": random.uniform(0, 1)} for vp, t in zip(video_path, task)]
12
+ else:
13
+ return {"video_path": video_path, "task": task, "score": random.uniform(0, 1),
14
+ "confidence": random.uniform(0, 1)}
15
+
16
+ __all__ = ["evaluate"]
humalab/humalab.py CHANGED
@@ -1,11 +1,14 @@
1
1
  from contextlib import contextmanager
2
+
3
+ from omegaconf import OmegaConf
4
+
2
5
  from humalab.run import Run
3
6
  from humalab.humalab_config import HumalabConfig
4
7
  from humalab.humalab_api_client import HumaLabApiClient
5
8
  from humalab.constants import EpisodeStatus
9
+ import requests
6
10
 
7
11
  import uuid
8
- import os
9
12
 
10
13
  from collections.abc import Generator
11
14
 
@@ -14,16 +17,24 @@ from humalab.scenario import Scenario
14
17
  _cur_run: Run | None = None
15
18
 
16
19
  def _pull_scenario(client: HumaLabApiClient,
20
+ project_name: str,
17
21
  scenario: str | list | dict | None = None,
18
22
  scenario_id: str | None = None,) -> str | list | dict | None:
19
23
  if scenario_id is not None:
20
- scenario_response = client.get_scenario(uuid=scenario_id)
24
+ scenario_arr = scenario_id.split(":")
25
+ if len(scenario_arr) < 1:
26
+ raise ValueError("Invalid scenario_id format. Expected 'scenario_id' or 'scenario_name:version'.")
27
+ scenario_real_id = scenario_arr[0]
28
+ scenario_version = int(scenario_arr[1]) if len(scenario_arr) > 1 else None
29
+
30
+ scenario_response = client.get_scenario(
31
+ project_name=project_name,
32
+ uuid=scenario_real_id, version=scenario_version)
21
33
  return scenario_response["yaml_content"]
22
34
  return scenario
23
35
 
24
36
  @contextmanager
25
- def init(entity: str | None = None,
26
- project: str | None = None,
37
+ def init(project: str | None = None,
27
38
  name: str | None = None,
28
39
  description: str | None = None,
29
40
  id: str | None = None,
@@ -34,13 +45,31 @@ def init(entity: str | None = None,
34
45
  api_key: str | None = None,
35
46
  seed: int | None=None,
36
47
  timeout: float | None = None,
37
- num_env: int | None = None
48
+ # num_env: int | None = None,
49
+ auto_create_scenario: bool = False,
38
50
  ) -> Generator[Run, None, None]:
51
+ """
52
+ Initialize a new HumaLab run.
53
+
54
+ Args:
55
+ project: The project name under which to create the run.
56
+ name: The name of the run.
57
+ description: A description of the run.
58
+ id: The unique identifier for the run. If None, a new UUID will be generated.
59
+ tags: A list of tags to associate with the run.
60
+ scenario: The scenario configuration as a string, list, or dict.
61
+ scenario_id: The unique identifier of a pre-defined scenario to use.
62
+ base_url: The base URL of the HumaLab server.
63
+ api_key: The API key for authentication.
64
+ seed: An optional seed for scenario randomization.
65
+ timeout: The timeout for API requests.
66
+ # num_env: The number of parallel environments to run. (Not supported yet.)
67
+ auto_create_scenario: Whether to automatically create the scenario if it does not exist.
68
+ """
39
69
  global _cur_run
40
70
  run = None
41
71
  try:
42
72
  humalab_config = HumalabConfig()
43
- entity = entity or humalab_config.entity
44
73
  project = project or "default"
45
74
  name = name or ""
46
75
  description = description or ""
@@ -54,24 +83,62 @@ def init(entity: str | None = None,
54
83
  api_key=api_key,
55
84
  timeout=timeout)
56
85
  final_scenario = _pull_scenario(client=api_client,
86
+ project_name=project,
57
87
  scenario=scenario,
58
88
  scenario_id=scenario_id)
89
+
90
+ project_resp = api_client.create_project(name=project)
91
+
59
92
  scenario_inst = Scenario()
60
93
  scenario_inst.init(run_id=id,
61
94
  scenario=final_scenario,
62
95
  seed=seed,
63
96
  episode_id=str(uuid.uuid4()),
64
- num_env=num_env)
97
+ #num_env=num_env
98
+ )
99
+ if scenario_id is None and scenario is not None and auto_create_scenario:
100
+ scenario_response = api_client.create_scenario(
101
+ project_name=project_resp['name'],
102
+ name=f"{name} scenario",
103
+ description="Auto-created scenario",
104
+ yaml_content=OmegaConf.to_yaml(scenario_inst.template),
105
+ )
106
+ scenario_id = scenario_response['uuid']
107
+ try:
108
+ run_response = api_client.get_run(run_id=id)
109
+ api_client.update_run(
110
+ run_id=run_response['run_id'],
111
+ )
112
+
113
+ except requests.HTTPError as e:
114
+ if e.response.status_code == 404:
115
+ # If not found then create a new run,
116
+ # so ignore not found error.
117
+ run_response = None
118
+ else:
119
+ # Otherwise re-raise the exception.
120
+ raise
121
+
122
+ if run_response is None:
123
+ run_response = api_client.create_run(name=name,
124
+ project_name=project_resp['name'],
125
+ description=description,
126
+ tags=tags)
127
+ id = run_response['run_id']
128
+ api_client.update_run(
129
+ run_id=id,
130
+ description=description,
131
+ )
65
132
 
66
133
  run = Run(
67
- entity=entity,
68
- project=project,
69
- name=name,
70
- description=description,
71
- id=id,
72
- tags=tags,
134
+ project=project_resp['name'],
135
+ name=run_response["name"],
136
+ description=run_response.get("description"),
137
+ id=run_response['run_id'],
138
+ tags=run_response.get("tags"),
73
139
  scenario=scenario_inst,
74
140
  )
141
+
75
142
  _cur_run = run
76
143
  yield run
77
144
  finally:
@@ -90,60 +157,8 @@ def login(api_key: str | None = None,
90
157
  host: str | None = None,
91
158
  force: bool | None = None,
92
159
  timeout: float | None = None) -> bool:
93
- # TODO: Validate api_key against host given.
94
- # and retrieve entity information.
95
160
  humalab_config = HumalabConfig()
96
161
  humalab_config.api_key = api_key or humalab_config.api_key
97
162
  humalab_config.base_url = host or humalab_config.base_url
98
163
  humalab_config.timeout = timeout or humalab_config.timeout
99
164
  return True
100
-
101
-
102
- if __name__ == "__main__":
103
- login(api_key="GSdIImnRJs1TQRpkN74PyIVHhX8_PISLOI9NVF6uO94",
104
- host="http://localhost:8000")
105
-
106
- with init(entity="default",
107
- project="test",
108
- name="my first run",
109
- description="testing the humalab sdk",
110
- tags=["tag1", "tag2"],
111
- scenario_id="cb9668c6-99fe-490c-a97c-e8c1f06b54a6",
112
- num_env=None) as run:
113
- print(f"Run ID: {run.id}")
114
- print(f"Run Name: {run.name}")
115
- print(f"Run Description: {run.description}")
116
- print(f"Run Tags: {run.tags}")
117
- print(f"Run Scenario YAML:\n{run.scenario.yaml}")
118
-
119
- scenario = run.scenario
120
- # Simulate some operations
121
- print("CUP position: ", scenario.scenario.cup.position)
122
- print("CUP orientation: ", scenario.scenario.cup.orientation)
123
- print("Asset: ", scenario.scenario.cup.asset)
124
- print("Friction: ", scenario.scenario.cup.friction)
125
- print("Num Objects: ", scenario.scenario.num_objects)
126
- scenario.reset()
127
- print("======================SCENARIO RESET==========================")
128
- print("CUP position: ", scenario.scenario.cup.position)
129
- print("CUP orientation: ", scenario.scenario.cup.orientation)
130
- print("Asset: ", scenario.scenario.cup.asset)
131
- print("Friction: ", scenario.scenario.cup.friction)
132
- print("Num Objects: ", scenario.scenario.num_objects)
133
-
134
- humalab_config = HumalabConfig()
135
- base_url = humalab_config.base_url
136
- api_key = humalab_config.api_key
137
- timeout = humalab_config.timeout
138
-
139
- api_client = HumaLabApiClient(base_url=base_url,
140
- api_key=api_key,
141
- timeout=timeout)
142
- resource = api_client.get_resource(name="lerobot", version=1)
143
- print("Resource metadata: ", resource)
144
- file_content = api_client.download_resource(name="lerobot")
145
- filename = os.path.basename(resource['resource_url'])
146
- filename = os.path.join(humalab_config.workspace_path, filename)
147
- with open(filename, "wb") as f:
148
- f.write(file_content)
149
- print(f"Resource file downloaded: {filename}")