servo-client 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.
- servo/__init__.py +126 -0
- servo/client.py +236 -0
- servo/constants.py +79 -0
- servo/data_streaming.py +679 -0
- servo/errors.py +78 -0
- servo/execution/__init__.py +1 -0
- servo/execution/chunked_loop.py +396 -0
- servo/execution/policy.py +102 -0
- servo/execution/session.py +98 -0
- servo/execution/world.py +61 -0
- servo/hardware.py +699 -0
- servo/namespaces/__init__.py +1 -0
- servo/namespaces/adapters.py +26 -0
- servo/namespaces/billing.py +100 -0
- servo/namespaces/datasets.py +250 -0
- servo/namespaces/deployments.py +22 -0
- servo/namespaces/jobs.py +92 -0
- servo/namespaces/licenses.py +23 -0
- servo/namespaces/models.py +466 -0
- servo/namespaces/robots.py +106 -0
- servo/namespaces/secrets.py +43 -0
- servo/resources/__init__.py +1 -0
- servo/resources/adapter.py +79 -0
- servo/resources/base.py +123 -0
- servo/resources/billing.py +54 -0
- servo/resources/dataset.py +561 -0
- servo/resources/deployment.py +130 -0
- servo/resources/embodiment.py +41 -0
- servo/resources/hf_import.py +288 -0
- servo/resources/job.py +221 -0
- servo/resources/model.py +402 -0
- servo/resources/secret.py +38 -0
- servo/resources/upload.py +35 -0
- servo/resources/usage.py +75 -0
- servo/templates.py +47 -0
- servo/types.py +100 -0
- servo_client-0.1.0.dist-info/METADATA +24 -0
- servo_client-0.1.0.dist-info/RECORD +46 -0
- servo_client-0.1.0.dist-info/WHEEL +5 -0
- servo_client-0.1.0.dist-info/top_level.txt +2 -0
- servo_contracts/__init__.py +11 -0
- servo_contracts/errors.py +148 -0
- servo_contracts/inference.py +161 -0
- servo_contracts/observability.py +124 -0
- servo_contracts/resources.py +55 -0
- servo_contracts/templates.py +274 -0
servo/__init__.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Servo SDK — robot AI, made usable.
|
|
2
|
+
|
|
3
|
+
The public surface is one robot policy operations API:
|
|
4
|
+
- **Identity**: ``Model``, ``Checkpoint`` (which weights).
|
|
5
|
+
- **Hosting**: ``HostedDeployment``, ``EdgeExport`` (where it runs; never routes).
|
|
6
|
+
- **Execution**: ``Policy`` (``.act``), ``World`` (``.rollout``), ``Session`` (the control loop).
|
|
7
|
+
|
|
8
|
+
Everything is reached from ``servo.Servo``. ``servo.templates`` carries embodiment helpers;
|
|
9
|
+
``servo.errors`` carries the full typed error surface. Modules can be used independently or
|
|
10
|
+
composed through the same policy and embodiment resources. Upstream frameworks are exposed as
|
|
11
|
+
integration adapters, not as application-facing control surfaces.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from servo import errors, templates # noqa: F401
|
|
15
|
+
from servo.client import Servo
|
|
16
|
+
from servo.constants import (
|
|
17
|
+
ActionSpace,
|
|
18
|
+
Compute,
|
|
19
|
+
PolicyAdapter,
|
|
20
|
+
RobotType,
|
|
21
|
+
)
|
|
22
|
+
from servo.errors import (
|
|
23
|
+
ActionSpaceIncompatible,
|
|
24
|
+
CapabilityNotAvailable,
|
|
25
|
+
DependencyUnavailable,
|
|
26
|
+
EmbodimentUnsupported,
|
|
27
|
+
ExternalDependencyError,
|
|
28
|
+
LicenseBlocked,
|
|
29
|
+
ModelNotServable,
|
|
30
|
+
NotFound,
|
|
31
|
+
NotHydrated,
|
|
32
|
+
NotReady,
|
|
33
|
+
ObservationInvalid,
|
|
34
|
+
PaymentRequired,
|
|
35
|
+
ServoError,
|
|
36
|
+
SurfaceUnsupported,
|
|
37
|
+
Unauthorized,
|
|
38
|
+
ValidationError,
|
|
39
|
+
)
|
|
40
|
+
from servo.execution.chunked_loop import ChunkedRunReport, ChunkRecord
|
|
41
|
+
from servo.execution.policy import Policy
|
|
42
|
+
from servo.execution.session import Session
|
|
43
|
+
from servo.execution.world import World
|
|
44
|
+
from servo.hardware import LocalRobot as ConnectedRobot
|
|
45
|
+
from servo.hardware import RobotRun
|
|
46
|
+
from servo.resources.dataset import Dataset, DatasetInspection
|
|
47
|
+
from servo.resources.deployment import EdgeExport, HostedDeployment
|
|
48
|
+
from servo.resources.embodiment import Embodiment as Robot
|
|
49
|
+
from servo.resources.hf_import import ModelImport, ModelInspection
|
|
50
|
+
from servo.resources.job import EvalResult, FineTuneResult, Job, SynthDataResult
|
|
51
|
+
from servo.resources.model import Checkpoint, CompatibilityReport, Model, RuntimeBinding
|
|
52
|
+
from servo.resources.adapter import Adapter, RuntimeEntry
|
|
53
|
+
from servo.resources.billing import BillingBalance, CheckoutSession
|
|
54
|
+
from servo.resources.secret import Secret
|
|
55
|
+
from servo.resources.upload import ModelUpload
|
|
56
|
+
from servo.resources.usage import Grant, UsageSummary
|
|
57
|
+
from servo.types import ActionPrediction, FrameRef, Observation, RolloutPrediction
|
|
58
|
+
|
|
59
|
+
__all__ = [
|
|
60
|
+
# Client + helpers
|
|
61
|
+
"Servo",
|
|
62
|
+
"templates",
|
|
63
|
+
"errors",
|
|
64
|
+
"Observation",
|
|
65
|
+
"ActionSpace",
|
|
66
|
+
"Compute",
|
|
67
|
+
"PolicyAdapter",
|
|
68
|
+
"RobotType",
|
|
69
|
+
# Identity
|
|
70
|
+
"Model",
|
|
71
|
+
"Checkpoint",
|
|
72
|
+
"Robot",
|
|
73
|
+
"ConnectedRobot",
|
|
74
|
+
"Adapter",
|
|
75
|
+
"RuntimeEntry",
|
|
76
|
+
# Data + import lifecycle
|
|
77
|
+
"Dataset",
|
|
78
|
+
"DatasetInspection",
|
|
79
|
+
"ModelInspection",
|
|
80
|
+
"ModelImport",
|
|
81
|
+
# Hosting
|
|
82
|
+
"HostedDeployment",
|
|
83
|
+
"EdgeExport",
|
|
84
|
+
# Jobs + results
|
|
85
|
+
"Job",
|
|
86
|
+
"FineTuneResult",
|
|
87
|
+
"EvalResult",
|
|
88
|
+
"SynthDataResult",
|
|
89
|
+
# Execution
|
|
90
|
+
"Policy",
|
|
91
|
+
"World",
|
|
92
|
+
"Session",
|
|
93
|
+
"RobotRun",
|
|
94
|
+
"ChunkedRunReport",
|
|
95
|
+
"ChunkRecord",
|
|
96
|
+
# Predictions
|
|
97
|
+
"ActionPrediction",
|
|
98
|
+
"RolloutPrediction",
|
|
99
|
+
"FrameRef",
|
|
100
|
+
# Operational
|
|
101
|
+
"UsageSummary",
|
|
102
|
+
"Grant",
|
|
103
|
+
"CompatibilityReport",
|
|
104
|
+
"RuntimeBinding",
|
|
105
|
+
"BillingBalance",
|
|
106
|
+
"CheckoutSession",
|
|
107
|
+
"Secret",
|
|
108
|
+
"ModelUpload",
|
|
109
|
+
# Errors (also under servo.errors)
|
|
110
|
+
"NotHydrated",
|
|
111
|
+
"NotReady",
|
|
112
|
+
"ServoError",
|
|
113
|
+
"SurfaceUnsupported",
|
|
114
|
+
"CapabilityNotAvailable",
|
|
115
|
+
"LicenseBlocked",
|
|
116
|
+
"EmbodimentUnsupported",
|
|
117
|
+
"ActionSpaceIncompatible",
|
|
118
|
+
"ExternalDependencyError",
|
|
119
|
+
"DependencyUnavailable",
|
|
120
|
+
"ValidationError",
|
|
121
|
+
"NotFound",
|
|
122
|
+
"Unauthorized",
|
|
123
|
+
"ModelNotServable",
|
|
124
|
+
"ObservationInvalid",
|
|
125
|
+
"PaymentRequired",
|
|
126
|
+
]
|
servo/client.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""Servo SDK client (006 redesign).
|
|
2
|
+
|
|
3
|
+
The ``Servo`` client is the single entry point: thin uniform namespaces (``sv.models`` etc.), the
|
|
4
|
+
execution factories (``sv.policy``/``sv.world``/``sv.session``), and ``sv.usage()``. Transport is
|
|
5
|
+
unchanged — HTTP to a running control plane by default, or the in-process ASGI ``app=`` test client.
|
|
6
|
+
|
|
7
|
+
Robot/embodiment normalization (FR-013/D8): every ``robot=`` accepts a registered robot handle,
|
|
8
|
+
id, or name and is resolved to an embodiment id via ``_embodiment_id`` before any request;
|
|
9
|
+
unknown/ambiguous names raise client-side.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import TYPE_CHECKING, Any
|
|
15
|
+
|
|
16
|
+
import httpx
|
|
17
|
+
|
|
18
|
+
from servo_contracts.errors import (
|
|
19
|
+
ActionSpaceIncompatible,
|
|
20
|
+
CapabilityNotAvailable,
|
|
21
|
+
DependencyUnavailable,
|
|
22
|
+
EmbodimentUnsupported,
|
|
23
|
+
ExternalDependencyError,
|
|
24
|
+
LicenseBlocked,
|
|
25
|
+
ModelNotServable,
|
|
26
|
+
NotFound,
|
|
27
|
+
ObservationInvalid,
|
|
28
|
+
PaymentRequired,
|
|
29
|
+
ServoError,
|
|
30
|
+
SurfaceUnsupported,
|
|
31
|
+
Unauthorized,
|
|
32
|
+
)
|
|
33
|
+
from servo.errors import NotReady, ValidationError
|
|
34
|
+
from servo.namespaces.datasets import Datasets
|
|
35
|
+
from servo.namespaces.adapters import Adapters
|
|
36
|
+
from servo.namespaces.billing import Billing
|
|
37
|
+
from servo.namespaces.deployments import Deployments
|
|
38
|
+
from servo.namespaces.robots import Robots
|
|
39
|
+
from servo.namespaces.jobs import Jobs
|
|
40
|
+
from servo.namespaces.licenses import Licenses
|
|
41
|
+
from servo.namespaces.models import Models
|
|
42
|
+
from servo.namespaces.secrets import Secrets
|
|
43
|
+
from servo.resources.usage import UsageSummary
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
46
|
+
from servo.execution.policy import Policy
|
|
47
|
+
from servo.execution.session import Session
|
|
48
|
+
from servo.execution.world import World
|
|
49
|
+
from servo.resources.embodiment import Embodiment
|
|
50
|
+
from servo.resources.model import Checkpoint, Model
|
|
51
|
+
|
|
52
|
+
_ERROR_BY_CODE = {
|
|
53
|
+
"ObservationInvalid": ObservationInvalid,
|
|
54
|
+
"ActionSpaceIncompatible": ActionSpaceIncompatible,
|
|
55
|
+
"ModelNotServable": ModelNotServable,
|
|
56
|
+
"EmbodimentUnsupported": EmbodimentUnsupported,
|
|
57
|
+
"LicenseBlocked": LicenseBlocked,
|
|
58
|
+
"SurfaceUnsupported": SurfaceUnsupported,
|
|
59
|
+
"CapabilityNotAvailable": CapabilityNotAvailable,
|
|
60
|
+
"ExternalDependencyError": ExternalDependencyError,
|
|
61
|
+
"DependencyUnavailable": DependencyUnavailable,
|
|
62
|
+
"ValidationError": ValidationError,
|
|
63
|
+
"NotFound": NotFound,
|
|
64
|
+
"NotReady": NotReady,
|
|
65
|
+
"Unauthorized": Unauthorized,
|
|
66
|
+
"PaymentRequired": PaymentRequired,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _raise_for_error(resp: httpx.Response) -> dict[str, Any]:
|
|
71
|
+
if resp.status_code < 400:
|
|
72
|
+
return resp.json() if resp.content else {}
|
|
73
|
+
try:
|
|
74
|
+
body = resp.json()
|
|
75
|
+
except Exception: # noqa: BLE001
|
|
76
|
+
resp.raise_for_status()
|
|
77
|
+
raise
|
|
78
|
+
code = body.get("code", "ServoError")
|
|
79
|
+
cls = _ERROR_BY_CODE.get(code, ServoError)
|
|
80
|
+
raise cls(body.get("message", "error"), detail=body.get("detail"))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Servo:
|
|
84
|
+
"""Typed Servo client. Pass ``app=`` to drive the ASGI app in-process (hermetic tests)."""
|
|
85
|
+
|
|
86
|
+
def __init__(
|
|
87
|
+
self,
|
|
88
|
+
base_url: str = "http://127.0.0.1:8000",
|
|
89
|
+
api_key: str = "dev",
|
|
90
|
+
app: Any | None = None,
|
|
91
|
+
http: Any | None = None,
|
|
92
|
+
timeout: float | None = 3600.0,
|
|
93
|
+
):
|
|
94
|
+
self.api_key = api_key
|
|
95
|
+
headers = {"Authorization": f"Bearer {api_key}"}
|
|
96
|
+
if http is not None:
|
|
97
|
+
self._http = http
|
|
98
|
+
self._http.headers.update(headers)
|
|
99
|
+
elif app is not None:
|
|
100
|
+
from starlette.testclient import TestClient
|
|
101
|
+
|
|
102
|
+
self._http = TestClient(app)
|
|
103
|
+
self._http.headers.update(headers)
|
|
104
|
+
else:
|
|
105
|
+
# Some control-plane calls block on real GPU work (a synchronous finetune POST runs the
|
|
106
|
+
# Modal job inline and only returns when it finishes — minutes, not seconds). httpx's
|
|
107
|
+
# 5s default read-timeout would abort those mid-flight, so the SDK defaults to a generous
|
|
108
|
+
# 1h timeout. Override per-client with ``timeout=`` (``None`` disables it entirely).
|
|
109
|
+
self._http = httpx.Client(base_url=base_url, headers=headers, timeout=timeout)
|
|
110
|
+
|
|
111
|
+
# Namespaces (thin, uniform — all return typed handles).
|
|
112
|
+
self.robots = Robots(self)
|
|
113
|
+
self.adapters = Adapters(self)
|
|
114
|
+
self.models = Models(self)
|
|
115
|
+
self.datasets = Datasets(self)
|
|
116
|
+
self.data = self.datasets
|
|
117
|
+
self.deployments = Deployments(self)
|
|
118
|
+
self.jobs = Jobs(self)
|
|
119
|
+
self.licenses = Licenses(self)
|
|
120
|
+
self.billing = Billing(self)
|
|
121
|
+
self.secrets = Secrets(self)
|
|
122
|
+
|
|
123
|
+
self._embodiment_cache: dict[str, str] | None = None
|
|
124
|
+
|
|
125
|
+
# ---- execution factories ----
|
|
126
|
+
def policy(
|
|
127
|
+
self,
|
|
128
|
+
model_or_checkpoint: "Any",
|
|
129
|
+
embodiment: "Any",
|
|
130
|
+
*,
|
|
131
|
+
instruction: str | None = None,
|
|
132
|
+
deployment_id: str | None = None,
|
|
133
|
+
advisory: str | None = None,
|
|
134
|
+
) -> "Policy":
|
|
135
|
+
"""Bind a model/checkpoint + embodiment -> a :class:`Policy` (single-shot ``.act``)."""
|
|
136
|
+
from servo.execution.policy import Policy
|
|
137
|
+
from servo.resources.model import Checkpoint, Model
|
|
138
|
+
|
|
139
|
+
ref = (
|
|
140
|
+
model_or_checkpoint.id
|
|
141
|
+
if isinstance(model_or_checkpoint, (Model, Checkpoint))
|
|
142
|
+
else model_or_checkpoint
|
|
143
|
+
)
|
|
144
|
+
local_robot = embodiment if hasattr(embodiment, "run") and hasattr(embodiment, "embodiment") else None
|
|
145
|
+
emb_id = self._embodiment_id(embodiment)
|
|
146
|
+
return Policy(
|
|
147
|
+
self, ref, emb_id,
|
|
148
|
+
instruction=instruction, deployment_id=deployment_id, advisory=advisory,
|
|
149
|
+
robot=local_robot,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def world(self, model: "Any", embodiment: "Any" = None) -> "World":
|
|
153
|
+
"""Bind a world model -> a :class:`World` (``.rollout``)."""
|
|
154
|
+
from servo.execution.world import World
|
|
155
|
+
from servo.resources.model import Checkpoint, Model
|
|
156
|
+
|
|
157
|
+
ref = model.id if isinstance(model, (Model, Checkpoint)) else model
|
|
158
|
+
emb_id = self._embodiment_id(embodiment) if embodiment is not None else None
|
|
159
|
+
return World(self, ref, embodiment_id=emb_id)
|
|
160
|
+
|
|
161
|
+
def session(self, policy: "Policy") -> "Session":
|
|
162
|
+
"""Open the real-robot control loop for a :class:`Policy` -> a :class:`Session` (context mgr)."""
|
|
163
|
+
from servo.execution.session import Session
|
|
164
|
+
|
|
165
|
+
return Session(self, policy)
|
|
166
|
+
|
|
167
|
+
def usage(self) -> UsageSummary:
|
|
168
|
+
"""Org usage telemetry summary -> a typed :class:`UsageSummary`."""
|
|
169
|
+
return UsageSummary(self._get("/v1/usage"))
|
|
170
|
+
|
|
171
|
+
# ---- embodiment normalization (FR-013 / D8) ----
|
|
172
|
+
def _embodiment_id(self, embodiment: "Any") -> str:
|
|
173
|
+
"""Normalize ``Robot handle | id | name`` -> an embodiment id."""
|
|
174
|
+
from servo.resources.embodiment import Embodiment
|
|
175
|
+
|
|
176
|
+
if isinstance(embodiment, Embodiment):
|
|
177
|
+
return embodiment.id
|
|
178
|
+
local_embodiment = getattr(embodiment, "embodiment", None)
|
|
179
|
+
if isinstance(local_embodiment, Embodiment):
|
|
180
|
+
return local_embodiment.id
|
|
181
|
+
if not isinstance(embodiment, str):
|
|
182
|
+
raise ValidationError(
|
|
183
|
+
f"robot must be a Robot | id | name, got {type(embodiment).__name__}",
|
|
184
|
+
detail={"value": repr(embodiment)},
|
|
185
|
+
)
|
|
186
|
+
# An id passed directly (registered embodiment ids are emb_...) — accept as-is.
|
|
187
|
+
if embodiment.startswith("emb_"):
|
|
188
|
+
return embodiment
|
|
189
|
+
cache = self._embodiment_cache_get()
|
|
190
|
+
# id present in the cache (covers non-prefixed ids).
|
|
191
|
+
if embodiment in cache.values():
|
|
192
|
+
return embodiment
|
|
193
|
+
matches = [eid for name, eid in cache.items() if name == embodiment]
|
|
194
|
+
if len(matches) == 1:
|
|
195
|
+
return matches[0]
|
|
196
|
+
if len(matches) > 1:
|
|
197
|
+
raise ValidationError(
|
|
198
|
+
f"embodiment name '{embodiment}' is ambiguous ({len(matches)} matches)",
|
|
199
|
+
detail={"name": embodiment, "matches": matches},
|
|
200
|
+
)
|
|
201
|
+
# Unknown name: refresh once in case the cache is stale, then fail closed.
|
|
202
|
+
self._invalidate_embodiment_cache()
|
|
203
|
+
cache = self._embodiment_cache_get()
|
|
204
|
+
if embodiment in cache.values():
|
|
205
|
+
return embodiment
|
|
206
|
+
matches = [eid for name, eid in cache.items() if name == embodiment]
|
|
207
|
+
if len(matches) == 1:
|
|
208
|
+
return matches[0]
|
|
209
|
+
if len(matches) > 1:
|
|
210
|
+
raise ValidationError(
|
|
211
|
+
f"embodiment name '{embodiment}' is ambiguous ({len(matches)} matches)",
|
|
212
|
+
detail={"name": embodiment, "matches": matches},
|
|
213
|
+
)
|
|
214
|
+
raise ValidationError(
|
|
215
|
+
f"unknown robot '{embodiment}'; register it or pass a Robot handle",
|
|
216
|
+
detail={"name": embodiment},
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
def _embodiment_cache_get(self) -> dict[str, str]:
|
|
220
|
+
if self._embodiment_cache is None:
|
|
221
|
+
data = self._get("/v1/embodiments")["embodiments"]
|
|
222
|
+
self._embodiment_cache = {e["name"]: e["id"] for e in data}
|
|
223
|
+
return self._embodiment_cache
|
|
224
|
+
|
|
225
|
+
def _invalidate_embodiment_cache(self) -> None:
|
|
226
|
+
self._embodiment_cache = None
|
|
227
|
+
|
|
228
|
+
# ---- transport ----
|
|
229
|
+
def _get(self, path: str, params: dict | None = None) -> Any:
|
|
230
|
+
return _raise_for_error(self._http.get(path, params=params))
|
|
231
|
+
|
|
232
|
+
def _post(self, path: str, body: Any) -> Any:
|
|
233
|
+
return _raise_for_error(self._http.post(path, json=body))
|
|
234
|
+
|
|
235
|
+
def _delete(self, path: str) -> Any:
|
|
236
|
+
return _raise_for_error(self._http.delete(path))
|
servo/constants.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Public constants for Servo's typed SDK surface.
|
|
2
|
+
|
|
3
|
+
The constants are ``str`` enums, so callers can pass them anywhere the SDK accepts a string
|
|
4
|
+
while still getting autocomplete and a finite set of Servo-owned values.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RobotType(StrEnum):
|
|
13
|
+
"""Built-in robot types with Servo observation/action schemas."""
|
|
14
|
+
|
|
15
|
+
SO101 = "so101"
|
|
16
|
+
ALOHA = "aloha"
|
|
17
|
+
LIBERO = "libero"
|
|
18
|
+
DROID = "droid"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ActionSpace(StrEnum):
|
|
22
|
+
JOINT_POSITION = "joint_position"
|
|
23
|
+
JOINT_VELOCITY = "joint_velocity"
|
|
24
|
+
EEF_DELTA = "eef_delta"
|
|
25
|
+
EEF_ABSOLUTE = "eef_absolute"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PolicyAdapter(StrEnum):
|
|
29
|
+
"""Servo policy integrations that inspect, validate, train, and serve checkpoints."""
|
|
30
|
+
|
|
31
|
+
OPENPI = "openpi"
|
|
32
|
+
OPENVLA = "openvla"
|
|
33
|
+
SMOLVLA = "smolvla"
|
|
34
|
+
GROOT = "gr00t"
|
|
35
|
+
MOLMOACT2 = "molmoact2"
|
|
36
|
+
STARVLA = "starvla"
|
|
37
|
+
VLA0 = "vla0"
|
|
38
|
+
HY_EMBODIED = "hy-embodied"
|
|
39
|
+
LINGBOT_VLA = "lingbot-vla"
|
|
40
|
+
X_VLA = "x-vla"
|
|
41
|
+
RDT = "rdt"
|
|
42
|
+
OCTO = "octo"
|
|
43
|
+
CUSTOM = "custom"
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def native(cls) -> tuple["PolicyAdapter", ...]:
|
|
47
|
+
return (cls.OPENPI, cls.OPENVLA, cls.SMOLVLA, cls.GROOT)
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def external(cls) -> tuple["PolicyAdapter", ...]:
|
|
51
|
+
return (
|
|
52
|
+
cls.MOLMOACT2,
|
|
53
|
+
cls.STARVLA,
|
|
54
|
+
cls.VLA0,
|
|
55
|
+
cls.HY_EMBODIED,
|
|
56
|
+
cls.LINGBOT_VLA,
|
|
57
|
+
cls.X_VLA,
|
|
58
|
+
cls.RDT,
|
|
59
|
+
cls.OCTO,
|
|
60
|
+
cls.CUSTOM,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def all(cls) -> tuple["PolicyAdapter", ...]:
|
|
65
|
+
return cls.native() + cls.external()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Compute(StrEnum):
|
|
69
|
+
"""Compute substrates Servo can provision for hosted deployment."""
|
|
70
|
+
|
|
71
|
+
MODAL = "modal"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
__all__ = [
|
|
75
|
+
"ActionSpace",
|
|
76
|
+
"Compute",
|
|
77
|
+
"PolicyAdapter",
|
|
78
|
+
"RobotType",
|
|
79
|
+
]
|