lucen-cli 0.1.0__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.
- lucen/__init__.py +72 -0
- lucen/errors.py +141 -0
- lucen/hub.py +889 -0
- lucen/progress.py +96 -0
- lucen/py.typed +0 -0
- lucen/results.py +885 -0
- lucen_cli/__init__.py +3 -0
- lucen_cli/checkpoint_dir.py +226 -0
- lucen_cli/checkpoint_publisher.py +269 -0
- lucen_cli/client.py +1730 -0
- lucen_cli/cmd_auth.py +256 -0
- lucen_cli/cmd_datasets.py +190 -0
- lucen_cli/cmd_holds.py +76 -0
- lucen_cli/cmd_hub.py +245 -0
- lucen_cli/cmd_model.py +175 -0
- lucen_cli/cmd_org.py +154 -0
- lucen_cli/cmd_quickstart.py +320 -0
- lucen_cli/cmd_repo.py +87 -0
- lucen_cli/cmd_robot.py +784 -0
- lucen_cli/cmd_rollout.py +576 -0
- lucen_cli/cmd_runs.py +615 -0
- lucen_cli/cmd_serve.py +438 -0
- lucen_cli/cmd_worker.py +135 -0
- lucen_cli/config.py +268 -0
- lucen_cli/context.py +43 -0
- lucen_cli/contract_derive.py +491 -0
- lucen_cli/embodiment.py +522 -0
- lucen_cli/errors.py +210 -0
- lucen_cli/gate_scorer.py +168 -0
- lucen_cli/group.py +104 -0
- lucen_cli/hf_dataset.py +696 -0
- lucen_cli/live.py +189 -0
- lucen_cli/local.py +160 -0
- lucen_cli/main.py +277 -0
- lucen_cli/model_card.py +523 -0
- lucen_cli/offscreen.py +52 -0
- lucen_cli/onnx_manifest.py +121 -0
- lucen_cli/onnx_writer.py +313 -0
- lucen_cli/ort_quiet.py +75 -0
- lucen_cli/output.py +868 -0
- lucen_cli/progress.py +133 -0
- lucen_cli/pull.py +158 -0
- lucen_cli/push.py +384 -0
- lucen_cli/rl/__init__.py +8 -0
- lucen_cli/rl/ppo.py +316 -0
- lucen_cli/rl/tasks.py +298 -0
- lucen_cli/rl/vecenv.py +246 -0
- lucen_cli/robot/__init__.py +66 -0
- lucen_cli/robot/checks.py +1416 -0
- lucen_cli/robot/confine.py +176 -0
- lucen_cli/robot/draft.py +523 -0
- lucen_cli/robot/hf.py +218 -0
- lucen_cli/robot/hub.py +227 -0
- lucen_cli/robot/intake.py +590 -0
- lucen_cli/robot/model.py +334 -0
- lucen_cli/robot/pose.py +132 -0
- lucen_cli/robot/publish.py +197 -0
- lucen_cli/robot/refs.py +212 -0
- lucen_cli/robot/thumbnail.py +586 -0
- lucen_cli/robot/urdf.py +435 -0
- lucen_cli/robot/worker.py +599 -0
- lucen_cli/robot_assets.py +127 -0
- lucen_cli/rollout/__init__.py +12 -0
- lucen_cli/rollout/assets.py +135 -0
- lucen_cli/rollout/conditions.py +486 -0
- lucen_cli/rollout/contract.py +314 -0
- lucen_cli/rollout/engine.py +1808 -0
- lucen_cli/rollout/harness.py +201 -0
- lucen_cli/rollout/obs.py +262 -0
- lucen_cli/rollout/plant.py +456 -0
- lucen_cli/rollout/posture.py +96 -0
- lucen_cli/rollout/video.py +548 -0
- lucen_cli/rollout/worker.py +505 -0
- lucen_cli/serve/__init__.py +22 -0
- lucen_cli/serve/lerobot_backend.py +337 -0
- lucen_cli/serve/msgpack_numpy.py +59 -0
- lucen_cli/serve/policy.py +276 -0
- lucen_cli/serve/server.py +474 -0
- lucen_cli/serve/session.py +142 -0
- lucen_cli/serve/stub_skill.py +264 -0
- lucen_cli/serve/vla.py +788 -0
- lucen_cli/serve/worker.py +578 -0
- lucen_cli/sim_words.py +55 -0
- lucen_cli/state.py +237 -0
- lucen_cli/templates/__init__.py +94 -0
- lucen_cli/templates/ppo_isaac.py +914 -0
- lucen_cli/templates/ppo_mujoco.py +664 -0
- lucen_cli/templates/smolvla_lora.py +986 -0
- lucen_cli/templates/stub_ppo.py +597 -0
- lucen_cli/templates/stub_skill.py +180 -0
- lucen_cli/tfevents.py +323 -0
- lucen_cli/training_curve.py +70 -0
- lucen_cli/transfer.py +209 -0
- lucen_cli/vla_manifest.py +322 -0
- lucen_cli/worker.py +1433 -0
- lucen_cli-0.1.0.dist-info/METADATA +113 -0
- lucen_cli-0.1.0.dist-info/RECORD +100 -0
- lucen_cli-0.1.0.dist-info/WHEEL +4 -0
- lucen_cli-0.1.0.dist-info/entry_points.txt +3 -0
- lucen_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
lucen/__init__.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""`from lucen import Hub` — MouseMouse from a Python script.
|
|
2
|
+
|
|
3
|
+
from lucen import Hub
|
|
4
|
+
|
|
5
|
+
hub = Hub() # LUCEN_API_URL / LUCEN_API_KEY, else ~/.config/lucen/credentials
|
|
6
|
+
hub.pull("lucen/laika", "./laika") # every sha256 verified
|
|
7
|
+
hub.push("./my-dataset", "you/my-dataset", kind="dataset", public=True)
|
|
8
|
+
run = hub.runs.create({"template": "ppo_mujoco", "robot": "lucen/laika"}, follow=True)
|
|
9
|
+
gate = hub.rollouts.create(run.output_model_repo, "lucen/laika", battery="smoke", local=True)
|
|
10
|
+
print(gate.scorecard)
|
|
11
|
+
|
|
12
|
+
A thin, typed surface over the `lucen` CLI's own engine (P5): the same
|
|
13
|
+
credentials rules, the same push/pull/follow code, the same sentence for
|
|
14
|
+
every refusal — as records and exceptions instead of lines and exit codes.
|
|
15
|
+
It ships inside the `lucen-cli` distribution: installing `lucen-cli` (from
|
|
16
|
+
the hub's own index or a checkout) is all it takes.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from lucen.errors import Conflict, Forbidden, HubError, HubUnreachable, NotFound, NotSignedIn
|
|
22
|
+
from lucen.hub import Hub
|
|
23
|
+
from lucen.progress import ProgressEvent
|
|
24
|
+
from lucen.results import (
|
|
25
|
+
Cell,
|
|
26
|
+
Episode,
|
|
27
|
+
Gate,
|
|
28
|
+
LogEvent,
|
|
29
|
+
Me,
|
|
30
|
+
Member,
|
|
31
|
+
Org,
|
|
32
|
+
PullResult,
|
|
33
|
+
PushResult,
|
|
34
|
+
Repo,
|
|
35
|
+
RobotDraft,
|
|
36
|
+
RobotPublishResult,
|
|
37
|
+
RobotReport,
|
|
38
|
+
Rollout,
|
|
39
|
+
Run,
|
|
40
|
+
RunEstimate,
|
|
41
|
+
RunRecommendation,
|
|
42
|
+
)
|
|
43
|
+
from lucen_cli import __version__ as __version__
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"Cell",
|
|
47
|
+
"Conflict",
|
|
48
|
+
"Episode",
|
|
49
|
+
"Forbidden",
|
|
50
|
+
"Gate",
|
|
51
|
+
"Hub",
|
|
52
|
+
"HubError",
|
|
53
|
+
"HubUnreachable",
|
|
54
|
+
"LogEvent",
|
|
55
|
+
"Me",
|
|
56
|
+
"Member",
|
|
57
|
+
"NotFound",
|
|
58
|
+
"NotSignedIn",
|
|
59
|
+
"Org",
|
|
60
|
+
"ProgressEvent",
|
|
61
|
+
"PullResult",
|
|
62
|
+
"PushResult",
|
|
63
|
+
"Repo",
|
|
64
|
+
"RobotDraft",
|
|
65
|
+
"RobotPublishResult",
|
|
66
|
+
"RobotReport",
|
|
67
|
+
"Rollout",
|
|
68
|
+
"Run",
|
|
69
|
+
"RunEstimate",
|
|
70
|
+
"RunRecommendation",
|
|
71
|
+
"__version__",
|
|
72
|
+
]
|
lucen/errors.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""One exception family, carrying the three lines a terminal user reads (C7).
|
|
2
|
+
|
|
3
|
+
Every refusal the CLI prints is `Error: <what happened>` / `The hub said:
|
|
4
|
+
<its sentence>` / `Fix: <one line>`. A script gets the same three things as
|
|
5
|
+
attributes — :attr:`HubError.message`, :attr:`HubError.detail`,
|
|
6
|
+
:attr:`HubError.fix` — and ``str(error)`` renders them the same way, so a
|
|
7
|
+
traceback and a terminal say the same sentence about the same refusal.
|
|
8
|
+
|
|
9
|
+
The subclasses follow the status the hub answered, which is also how the
|
|
10
|
+
CLI picks its exit code:
|
|
11
|
+
|
|
12
|
+
- :class:`NotSignedIn` — 401, or no key was configured at all,
|
|
13
|
+
- :class:`Forbidden` — 403: the key lacks the scope, or the resource is not
|
|
14
|
+
yours to write,
|
|
15
|
+
- :class:`NotFound` — 404: it does not exist, or it is private and you are not
|
|
16
|
+
a member (the hub never tells those two apart),
|
|
17
|
+
- :class:`Conflict` — 409: already exists, blocked by history, already ended,
|
|
18
|
+
- :class:`HubUnreachable` — the hub did not answer (a refused connection, a
|
|
19
|
+
timeout, or a 502/504/52x from the network in front of it).
|
|
20
|
+
|
|
21
|
+
Everything else — a 422, a 5xx, a local refusal such as a directory with no
|
|
22
|
+
files — is a plain :class:`HubError`. The translation from the CLI's own
|
|
23
|
+
exceptions is :func:`from_cli_error`; nothing here imports typer.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
from lucen_cli.errors import UNREACHABLE_STATUSES, ApiError, CLIError
|
|
31
|
+
from lucen_cli.errors import HubUnreachable as _CLIHubUnreachable
|
|
32
|
+
|
|
33
|
+
#: The one line every "not signed in" refusal ends with, phrased for a script.
|
|
34
|
+
LOGIN_FIX_PYTHON = (
|
|
35
|
+
"Pass Hub(key='lucen_sk_…') with a key from the API keys page of the hub, "
|
|
36
|
+
"set LUCEN_API_KEY, or run `lucen auth login` once on this machine."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class HubError(Exception):
|
|
41
|
+
"""What happened, what the hub said (if anything), and the fix."""
|
|
42
|
+
|
|
43
|
+
#: The HTTP status the hub answered, or None for a refusal made locally.
|
|
44
|
+
status: int | None
|
|
45
|
+
#: The hub's own sentence (an RFC 9457 `detail`), when it gave one.
|
|
46
|
+
detail: str | None
|
|
47
|
+
#: One line that makes the next attempt succeed, when there is one.
|
|
48
|
+
fix: str | None
|
|
49
|
+
#: A machine-readable code (`lucen robot` refusals: `interface.base`, …).
|
|
50
|
+
code: str | None
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
message: str,
|
|
55
|
+
*,
|
|
56
|
+
status: int | None = None,
|
|
57
|
+
detail: str | None = None,
|
|
58
|
+
fix: str | None = None,
|
|
59
|
+
code: str | None = None,
|
|
60
|
+
) -> None:
|
|
61
|
+
super().__init__(message)
|
|
62
|
+
self.message = message
|
|
63
|
+
self.status = status
|
|
64
|
+
self.detail = detail
|
|
65
|
+
self.fix = fix
|
|
66
|
+
self.code = code
|
|
67
|
+
|
|
68
|
+
def __str__(self) -> str:
|
|
69
|
+
lines = [self.message]
|
|
70
|
+
if self.detail:
|
|
71
|
+
lines.append(f" The hub said: {self.detail}")
|
|
72
|
+
if self.fix:
|
|
73
|
+
lines.append(f"Fix: {self.fix}")
|
|
74
|
+
return "\n".join(lines)
|
|
75
|
+
|
|
76
|
+
def to_dict(self) -> dict[str, Any]:
|
|
77
|
+
"""The same shape as `lucen --json` prints for an error."""
|
|
78
|
+
return {
|
|
79
|
+
"error": self.message,
|
|
80
|
+
"detail": self.detail,
|
|
81
|
+
"fix": self.fix,
|
|
82
|
+
"status": self.status,
|
|
83
|
+
"code": self.code,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class NotSignedIn(HubError):
|
|
88
|
+
"""401 — the hub rejected the credential — or no key is configured at all."""
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Forbidden(HubError):
|
|
92
|
+
"""403 — the key lacks the scope this call needs, or the resource is not yours."""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class NotFound(HubError):
|
|
96
|
+
"""404 — no such thing, or a private one you are not a member of (indistinguishable)."""
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class Conflict(HubError):
|
|
100
|
+
"""409 — already exists, already ended, or blocked by history the hub keeps."""
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class HubUnreachable(HubError):
|
|
104
|
+
"""The hub did not answer: no connection, a timeout, or a 502/504/52x in front of it."""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def from_cli_error(exc: CLIError) -> HubError:
|
|
108
|
+
"""The CLI's exception as this module's: same three lines, the subclass by status."""
|
|
109
|
+
status = exc.status if isinstance(exc, ApiError) else None
|
|
110
|
+
code = getattr(exc, "code", None)
|
|
111
|
+
kwargs: dict[str, Any] = {
|
|
112
|
+
"status": status,
|
|
113
|
+
"detail": exc.detail,
|
|
114
|
+
"fix": exc.hint,
|
|
115
|
+
"code": code if isinstance(code, str) else None,
|
|
116
|
+
}
|
|
117
|
+
if isinstance(exc, _CLIHubUnreachable):
|
|
118
|
+
return HubUnreachable(exc.message, **kwargs)
|
|
119
|
+
if status == 401:
|
|
120
|
+
return NotSignedIn(exc.message, **kwargs)
|
|
121
|
+
if status == 403:
|
|
122
|
+
return Forbidden(exc.message, **kwargs)
|
|
123
|
+
if status == 404:
|
|
124
|
+
return NotFound(exc.message, **kwargs)
|
|
125
|
+
if status == 409:
|
|
126
|
+
return Conflict(exc.message, **kwargs)
|
|
127
|
+
if status is not None and status in UNREACHABLE_STATUSES:
|
|
128
|
+
return HubUnreachable(exc.message, **kwargs)
|
|
129
|
+
return HubError(exc.message, **kwargs)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
__all__ = [
|
|
133
|
+
"LOGIN_FIX_PYTHON",
|
|
134
|
+
"Conflict",
|
|
135
|
+
"Forbidden",
|
|
136
|
+
"HubError",
|
|
137
|
+
"HubUnreachable",
|
|
138
|
+
"NotFound",
|
|
139
|
+
"NotSignedIn",
|
|
140
|
+
"from_cli_error",
|
|
141
|
+
]
|