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.
Files changed (100) hide show
  1. lucen/__init__.py +72 -0
  2. lucen/errors.py +141 -0
  3. lucen/hub.py +889 -0
  4. lucen/progress.py +96 -0
  5. lucen/py.typed +0 -0
  6. lucen/results.py +885 -0
  7. lucen_cli/__init__.py +3 -0
  8. lucen_cli/checkpoint_dir.py +226 -0
  9. lucen_cli/checkpoint_publisher.py +269 -0
  10. lucen_cli/client.py +1730 -0
  11. lucen_cli/cmd_auth.py +256 -0
  12. lucen_cli/cmd_datasets.py +190 -0
  13. lucen_cli/cmd_holds.py +76 -0
  14. lucen_cli/cmd_hub.py +245 -0
  15. lucen_cli/cmd_model.py +175 -0
  16. lucen_cli/cmd_org.py +154 -0
  17. lucen_cli/cmd_quickstart.py +320 -0
  18. lucen_cli/cmd_repo.py +87 -0
  19. lucen_cli/cmd_robot.py +784 -0
  20. lucen_cli/cmd_rollout.py +576 -0
  21. lucen_cli/cmd_runs.py +615 -0
  22. lucen_cli/cmd_serve.py +438 -0
  23. lucen_cli/cmd_worker.py +135 -0
  24. lucen_cli/config.py +268 -0
  25. lucen_cli/context.py +43 -0
  26. lucen_cli/contract_derive.py +491 -0
  27. lucen_cli/embodiment.py +522 -0
  28. lucen_cli/errors.py +210 -0
  29. lucen_cli/gate_scorer.py +168 -0
  30. lucen_cli/group.py +104 -0
  31. lucen_cli/hf_dataset.py +696 -0
  32. lucen_cli/live.py +189 -0
  33. lucen_cli/local.py +160 -0
  34. lucen_cli/main.py +277 -0
  35. lucen_cli/model_card.py +523 -0
  36. lucen_cli/offscreen.py +52 -0
  37. lucen_cli/onnx_manifest.py +121 -0
  38. lucen_cli/onnx_writer.py +313 -0
  39. lucen_cli/ort_quiet.py +75 -0
  40. lucen_cli/output.py +868 -0
  41. lucen_cli/progress.py +133 -0
  42. lucen_cli/pull.py +158 -0
  43. lucen_cli/push.py +384 -0
  44. lucen_cli/rl/__init__.py +8 -0
  45. lucen_cli/rl/ppo.py +316 -0
  46. lucen_cli/rl/tasks.py +298 -0
  47. lucen_cli/rl/vecenv.py +246 -0
  48. lucen_cli/robot/__init__.py +66 -0
  49. lucen_cli/robot/checks.py +1416 -0
  50. lucen_cli/robot/confine.py +176 -0
  51. lucen_cli/robot/draft.py +523 -0
  52. lucen_cli/robot/hf.py +218 -0
  53. lucen_cli/robot/hub.py +227 -0
  54. lucen_cli/robot/intake.py +590 -0
  55. lucen_cli/robot/model.py +334 -0
  56. lucen_cli/robot/pose.py +132 -0
  57. lucen_cli/robot/publish.py +197 -0
  58. lucen_cli/robot/refs.py +212 -0
  59. lucen_cli/robot/thumbnail.py +586 -0
  60. lucen_cli/robot/urdf.py +435 -0
  61. lucen_cli/robot/worker.py +599 -0
  62. lucen_cli/robot_assets.py +127 -0
  63. lucen_cli/rollout/__init__.py +12 -0
  64. lucen_cli/rollout/assets.py +135 -0
  65. lucen_cli/rollout/conditions.py +486 -0
  66. lucen_cli/rollout/contract.py +314 -0
  67. lucen_cli/rollout/engine.py +1808 -0
  68. lucen_cli/rollout/harness.py +201 -0
  69. lucen_cli/rollout/obs.py +262 -0
  70. lucen_cli/rollout/plant.py +456 -0
  71. lucen_cli/rollout/posture.py +96 -0
  72. lucen_cli/rollout/video.py +548 -0
  73. lucen_cli/rollout/worker.py +505 -0
  74. lucen_cli/serve/__init__.py +22 -0
  75. lucen_cli/serve/lerobot_backend.py +337 -0
  76. lucen_cli/serve/msgpack_numpy.py +59 -0
  77. lucen_cli/serve/policy.py +276 -0
  78. lucen_cli/serve/server.py +474 -0
  79. lucen_cli/serve/session.py +142 -0
  80. lucen_cli/serve/stub_skill.py +264 -0
  81. lucen_cli/serve/vla.py +788 -0
  82. lucen_cli/serve/worker.py +578 -0
  83. lucen_cli/sim_words.py +55 -0
  84. lucen_cli/state.py +237 -0
  85. lucen_cli/templates/__init__.py +94 -0
  86. lucen_cli/templates/ppo_isaac.py +914 -0
  87. lucen_cli/templates/ppo_mujoco.py +664 -0
  88. lucen_cli/templates/smolvla_lora.py +986 -0
  89. lucen_cli/templates/stub_ppo.py +597 -0
  90. lucen_cli/templates/stub_skill.py +180 -0
  91. lucen_cli/tfevents.py +323 -0
  92. lucen_cli/training_curve.py +70 -0
  93. lucen_cli/transfer.py +209 -0
  94. lucen_cli/vla_manifest.py +322 -0
  95. lucen_cli/worker.py +1433 -0
  96. lucen_cli-0.1.0.dist-info/METADATA +113 -0
  97. lucen_cli-0.1.0.dist-info/RECORD +100 -0
  98. lucen_cli-0.1.0.dist-info/WHEEL +4 -0
  99. lucen_cli-0.1.0.dist-info/entry_points.txt +3 -0
  100. 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
+ ]