sequence-base 0.3.26__tar.gz → 0.3.28__tar.gz
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.
- {sequence_base-0.3.26 → sequence_base-0.3.28}/PKG-INFO +1 -1
- sequence_base-0.3.28/pyproject.toml +20 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/__init__.py +2 -25
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/codec/image.py +2 -17
- sequence_base-0.3.28/sequence_base/constants/env.py +7 -0
- sequence_base-0.3.28/sequence_base/constants/headers.py +4 -0
- sequence_base-0.3.28/sequence_base/constants/version.py +3 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/__init__.py +0 -19
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/accounts.py +1 -10
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/act.py +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/benchmarks.py +5 -32
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/connect.py +3 -6
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/deployments.py +6 -44
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/errors.py +4 -13
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/eval.py +4 -35
- sequence_base-0.3.28/sequence_base/contract/flags.py +65 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/io.py +2 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/policy.py +11 -37
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/templates.py +0 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/volumes.py +0 -5
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/wire.py +1 -3
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/obs/rid.py +0 -3
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/presets/__init__.py +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/presets/robot_arm.py +3 -31
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schema_export.py +0 -12
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ActRequest.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ActResponse.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ActionChunk.json +2 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ActionContract.json +2 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ActionStep.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/AdapterTransform.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/AddLocal.json +2 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Autoscaler.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/BenchmarkConditionSpec.json +2 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/BenchmarkRegisterRequest.json +18 -5
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/BenchmarkSpec.json +16 -3
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/BootstrapResponse.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Camera.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ClampInfo.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/CodeBundle.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Concurrency.json +18 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Conditions.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ConnectRequest.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ConnectResponse.json +4 -4
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/DeploymentAccepted.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/DeploymentPolicy.json +17 -4
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/DeploymentRequest.json +35 -6
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Embodiment.json +3 -3
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ErrorBody.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ErrorCodes.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Estimate.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/EvalDryRunResponse.json +2 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/EvalReport.json +3 -3
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/EvalRequest.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/EvalResponse.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageFrame.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageSpec.json +2 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageStepAddLocal.json +2 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageStepApt.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageStepEnv.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageStepRun.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageStepUvPip.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/InferParam.json +2 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Lease.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/LossyTransform.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/MetricAgg.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Observation.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ObservationContract.json +2 -2
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/PolicyRegistration.json +36 -7
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/PolicySpec.json +35 -6
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Proprioception.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/RenderSpec.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Resources.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/RolloutResult.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/SecretCreateRequest.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/SecretInfo.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/SecretRef.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/SeqFlags.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Step.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/SuiteAgg.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/TaskAgg.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Timeouts.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/UploadTarget.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/UsageReport.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Variant.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeAttach.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeFile.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeInfo.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeMount.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeRef.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/WarmingError.json +1 -1
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/telemetry/events.py +1 -4
- sequence_base-0.3.26/pyproject.toml +0 -27
- sequence_base-0.3.26/sequence_base/constants/env.py +0 -7
- sequence_base-0.3.26/sequence_base/constants/headers.py +0 -4
- sequence_base-0.3.26/sequence_base/constants/version.py +0 -27
- sequence_base-0.3.26/sequence_base/contract/flags.py +0 -82
- {sequence_base-0.3.26 → sequence_base-0.3.28}/.gitignore +0 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/README.md +0 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/auth.py +0 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/codec/__init__.py +0 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/constants/__init__.py +0 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/errors/__init__.py +0 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/errors/transport.py +0 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/obs/__init__.py +0 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/obs/log.py +0 -0
- {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/telemetry/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sequence-base
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.28
|
|
4
4
|
Summary: Shared foundation for the Sequences platform — wire contract, correlation, error taxonomy, constants. Used by the robot SDK, gateway, and container harness.
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Requires-Dist: pydantic>=2.0
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sequence-base"
|
|
3
|
+
version = "0.3.28"
|
|
4
|
+
description = "Shared foundation for the Sequences platform — wire contract, correlation, error taxonomy, constants. Used by the robot SDK, gateway, and container harness."
|
|
5
|
+
requires-python = ">=3.10"
|
|
6
|
+
dependencies = ["pydantic>=2.0"]
|
|
7
|
+
|
|
8
|
+
[project.optional-dependencies]
|
|
9
|
+
codec = ["pillow>=10", "numpy>=1.26"]
|
|
10
|
+
test = ["pytest", "jsonschema>=4"]
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["hatchling"]
|
|
14
|
+
build-backend = "hatchling.build"
|
|
15
|
+
|
|
16
|
+
[tool.hatch.build.targets.wheel]
|
|
17
|
+
packages = ["sequence_base"]
|
|
18
|
+
|
|
19
|
+
[tool.hatch.build.targets.sdist]
|
|
20
|
+
include = ["/sequence_base", "/README.md", "/pyproject.toml"]
|
|
@@ -11,9 +11,7 @@ defined once so they cannot drift. Import from here; never redefine.
|
|
|
11
11
|
Only shared-by-≥2-and-drifts-if-duplicated things live here. Model-specific, provider-specific, and
|
|
12
12
|
catalogue values stay in container / gateway / spec.py.
|
|
13
13
|
"""
|
|
14
|
-
|
|
15
|
-
# accounts + error bodies). One definition per shape; see sequence_base/contract for the grouping.
|
|
16
|
-
from sequence_base.contract import ( # noqa: F401 (re-export surface)
|
|
14
|
+
from sequence_base.contract import (
|
|
17
15
|
ActRequest,
|
|
18
16
|
ActResponse,
|
|
19
17
|
AddLocal,
|
|
@@ -98,14 +96,11 @@ from sequence_base.contract import ( # noqa: F401 (re-export surface)
|
|
|
98
96
|
code_for_status,
|
|
99
97
|
)
|
|
100
98
|
|
|
101
|
-
# the single schema-level version stamp (embedded in every exported JSON Schema)
|
|
102
99
|
from sequence_base.constants.version import CONTRACT_VERSION
|
|
103
100
|
|
|
104
|
-
# obs — correlation id + structured logging (all three sides)
|
|
105
101
|
from sequence_base.obs.log import Timer, enabled, log
|
|
106
102
|
from sequence_base.obs.rid import HEADER, clean, new_rid
|
|
107
103
|
|
|
108
|
-
# errors — shared transport status taxonomy (gateway + container)
|
|
109
104
|
from sequence_base.errors.transport import (
|
|
110
105
|
TransportOverloaded,
|
|
111
106
|
TransportRejected,
|
|
@@ -113,55 +108,37 @@ from sequence_base.errors.transport import (
|
|
|
113
108
|
TransportWarming,
|
|
114
109
|
)
|
|
115
110
|
|
|
116
|
-
# auth — Bearer secret helpers (gateway direct-connect / internal; container guard; worker emit)
|
|
117
111
|
from sequence_base import auth
|
|
118
112
|
|
|
119
113
|
|
|
120
|
-
# NOTE: codec (image encode/decode) is intentionally NOT re-exported here — it needs PIL + numpy
|
|
121
|
-
# (the [codec] extra), and the gateway installs the light core. Import it explicitly where needed:
|
|
122
|
-
# from sequence_base.codec import encode, decode, ResizeSpec
|
|
123
114
|
|
|
124
115
|
__all__ = [
|
|
125
|
-
# contract 4 — /act data plane
|
|
126
116
|
"ImageFrame", "Proprioception", "Observation", "ActionSpace", "ActionStep", "ActionChunk",
|
|
127
117
|
"ActRequest", "ActResponse",
|
|
128
|
-
# contract 1 — flags + policy registry
|
|
129
118
|
"SeqFlags", "ImageStepUvPip", "ImageStepApt", "ImageStepEnv", "ImageStepRun", "AddLocal", "ImageStepAddLocal",
|
|
130
119
|
"ImageStep", "ImageSpec",
|
|
131
120
|
"VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract", "Embodiment",
|
|
132
121
|
"PolicySpec", "PolicyRegistration",
|
|
133
|
-
# contract 2 — deployments
|
|
134
122
|
"MAX_CODE_BUNDLE_BYTES", "CodeBundle",
|
|
135
123
|
"Resources", "Autoscaler", "Timeouts", "VolumeAttach", "DeploymentPolicy", "InferParam", "DeploymentRequest",
|
|
136
124
|
"DeploymentAccepted", "DeploymentState", "DeploymentStatusResponse",
|
|
137
|
-
# contract 3 — connect / lease
|
|
138
125
|
"Lease", "ConnectRequest", "ConnectResponse",
|
|
139
|
-
# contract 5 — benchmark + eval
|
|
140
126
|
"Variant", "Conditions", "RenderSpec", "Step", "BenchmarkSpec", "BenchmarkConditionSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
|
|
141
127
|
"EvalRequest", "ClampInfo", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
|
|
142
128
|
"EvalStatus", "SuiteAgg", "TaskAgg", "MetricAgg", "LossyTransform", "EvalReport",
|
|
143
129
|
"AdapterKind", "AdapterTransform",
|
|
144
|
-
# contract 6 — accounts / secret / billing
|
|
145
130
|
"SecretRef", "SecretCreateRequest", "SecretInfo", "BootstrapResponse", "UsageReport",
|
|
146
|
-
# contract 7 — volumes (uploaded checkpoints and datasets)
|
|
147
131
|
"VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse", "VolumeInfo", "parse_vol",
|
|
148
|
-
# official template weights
|
|
149
132
|
"TEMPLATE_SCHEME", "parse_template",
|
|
150
|
-
# error bodies + version
|
|
151
133
|
"ErrorCode", "ErrorBody", "WarmingError", "ERRORS", "ErrorSpec", "code_for_status", "CONTRACT_VERSION",
|
|
152
|
-
# obs
|
|
153
134
|
"HEADER", "new_rid", "clean", "log", "Timer", "enabled",
|
|
154
|
-
# errors (exception taxonomy)
|
|
155
135
|
"TransportUnavailable", "TransportWarming", "TransportRejected", "TransportOverloaded",
|
|
156
|
-
# auth + telemetry
|
|
157
136
|
"auth",
|
|
158
137
|
]
|
|
159
138
|
|
|
160
139
|
try:
|
|
161
|
-
# The installed distribution's own number, so this can never drift from pyproject.toml again (it said 0.3.6
|
|
162
|
-
# while the package was 0.3.12). A source tree that was never installed reports "dev".
|
|
163
140
|
from importlib.metadata import PackageNotFoundError, version as _version
|
|
164
141
|
|
|
165
142
|
__version__ = _version("sequence-base")
|
|
166
|
-
except PackageNotFoundError:
|
|
143
|
+
except PackageNotFoundError:
|
|
167
144
|
__version__ = "dev"
|
|
@@ -16,19 +16,8 @@ import io
|
|
|
16
16
|
from typing import Any
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
# Quality 95, and the number is measured rather than chosen for looking safe. On a real DROID frame
|
|
20
|
-
# with the sampling noise pinned, the encoding moves the resulting action by: q95 0.51% of action
|
|
21
|
-
# amplitude, q90 0.20%, q85 1.86%, q75 2.86% — 85 and below is where the error jumps by an order of
|
|
22
|
-
# magnitude, that is the line. 95 rather than the empirically-best 90 because the effect is not
|
|
23
|
-
# monotonic in quality on a single frame, and tuning to one frame is fitting noise.
|
|
24
|
-
# {sequences-models models/pi05_droid/handler.py "Q95 MAX|DELTA| 0.0048 0.51% ... Q85 1.86%"}
|
|
25
19
|
DEFAULT_JPEG_QUALITY = 95
|
|
26
20
|
|
|
27
|
-
# The longest edge `encode` uploads. Every policy resizes a frame to its own input size anyway, so pixels above
|
|
28
|
-
# that only cost transport — and past a point they do not cost, they fail: a body over ~2 MB was dropped by a
|
|
29
|
-
# worker queue with no error and no job id, surfacing as a hang. 640 is above every input size in use (pi0.5's
|
|
30
|
-
# 224), so it discards nothing a policy reads. [MEASURED 2026-09-12: capping the long edge at 640 took a
|
|
31
|
-
# three-view observation from 1.98 MB to 0.12 MB]
|
|
32
21
|
MAX_UPLOAD_EDGE = 640
|
|
33
22
|
|
|
34
23
|
|
|
@@ -48,7 +37,7 @@ def encode(frame: Any, *, quality: int = DEFAULT_JPEG_QUALITY) -> str:
|
|
|
48
37
|
|
|
49
38
|
try:
|
|
50
39
|
from PIL import Image
|
|
51
|
-
except ImportError as e:
|
|
40
|
+
except ImportError as e:
|
|
52
41
|
raise ImportError(
|
|
53
42
|
"encoding an array or PIL image needs Pillow: pip install 'sequence-base[codec]'. "
|
|
54
43
|
"Pass an already-encoded base64 str or bytes to avoid it."
|
|
@@ -59,8 +48,6 @@ def encode(frame: Any, *, quality: int = DEFAULT_JPEG_QUALITY) -> str:
|
|
|
59
48
|
img = img.convert("RGB")
|
|
60
49
|
if max(img.size) > MAX_UPLOAD_EDGE:
|
|
61
50
|
scale = MAX_UPLOAD_EDGE / max(img.size)
|
|
62
|
-
# round() not int(): int() truncates a 640.9 on one axis and 359.9 on the other, drifting the
|
|
63
|
-
# aspect ratio by up to half a percent for no reason.
|
|
64
51
|
img = img.resize((max(1, round(img.width * scale)),
|
|
65
52
|
max(1, round(img.height * scale))), Image.LANCZOS)
|
|
66
53
|
|
|
@@ -85,7 +72,6 @@ def decode(data: str) -> "Any":
|
|
|
85
72
|
return np.asarray(Image.open(io.BytesIO(raw)).convert("RGB"), dtype=np.uint8)
|
|
86
73
|
|
|
87
74
|
|
|
88
|
-
# ── depth frames ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
89
75
|
_DEPTH_UNITS = {"m": 1000.0, "mm": 1.0}
|
|
90
76
|
|
|
91
77
|
|
|
@@ -93,8 +79,7 @@ def encode_depth(depth: Any, *, unit: str = "m") -> str:
|
|
|
93
79
|
"""A depth map -> the base64 16-bit PNG a depth frame (ImageFrame kind="depth") carries: millimetres as uint16, 0
|
|
94
80
|
where there is no reading (NaN, inf, <= 0), clipped at 65.535 m. `unit` is what the array holds: "m" (float metres,
|
|
95
81
|
the default) or "mm". Lossless, unlike the JPEG colour frames take — a depth error is a geometry error.
|
|
96
|
-
|
|
97
|
-
depth cameras and ROS depth images use]"""
|
|
82
|
+
"""
|
|
98
83
|
import numpy as np
|
|
99
84
|
from PIL import Image
|
|
100
85
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""SEQ_* environment variable NAMES — a single source. Values live in ~/.config/sequences/, never here."""
|
|
2
|
+
|
|
3
|
+
DIRECT_SECRET = "SEQ_DIRECT_SECRET"
|
|
4
|
+
INTERNAL_SECRET = "SEQ_INTERNAL_SECRET"
|
|
5
|
+
GATEWAY_URL = "SEQ_GATEWAY_URL"
|
|
6
|
+
VOLUME_ROOT = "SEQ_VOLUME_ROOT"
|
|
7
|
+
MANIFEST = "SEQ_MANIFEST"
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
The shared wire contract — one definition per shape, imported (never redeclared) by the SDK, gateway,
|
|
3
3
|
and container harness. Grouped by the contract number in docs/platform/contracts.md.
|
|
4
4
|
"""
|
|
5
|
-
# contract 4 — /act data plane (obs/action DATA types)
|
|
6
5
|
from sequence_base.contract.act import (
|
|
7
6
|
ActionChunk,
|
|
8
7
|
ActionSpace,
|
|
@@ -12,10 +11,8 @@ from sequence_base.contract.act import (
|
|
|
12
11
|
Proprioception,
|
|
13
12
|
)
|
|
14
13
|
|
|
15
|
-
# contract 4, version 2 — the opaque /act envelope (inputs in, outputs out)
|
|
16
14
|
from sequence_base.contract.wire import ActRequest, ActResponse
|
|
17
15
|
|
|
18
|
-
# contract 1 — lifecycle flags + policy registry
|
|
19
16
|
from sequence_base.contract.flags import SeqFlags
|
|
20
17
|
from sequence_base.contract.policy import (
|
|
21
18
|
Camera,
|
|
@@ -37,7 +34,6 @@ from sequence_base.contract.policy import (
|
|
|
37
34
|
VolumeRef,
|
|
38
35
|
)
|
|
39
36
|
|
|
40
|
-
# contract 2 — deployments
|
|
41
37
|
from sequence_base.contract.deployments import (
|
|
42
38
|
MAX_CODE_BUNDLE_BYTES,
|
|
43
39
|
Autoscaler,
|
|
@@ -53,10 +49,8 @@ from sequence_base.contract.deployments import (
|
|
|
53
49
|
VolumeAttach,
|
|
54
50
|
)
|
|
55
51
|
|
|
56
|
-
# contract 3 — connect / lease
|
|
57
52
|
from sequence_base.contract.connect import ConnectRequest, ConnectResponse, Lease
|
|
58
53
|
|
|
59
|
-
# contract 5 — benchmark registration + eval run / report / adapter
|
|
60
54
|
from sequence_base.contract.benchmarks import (
|
|
61
55
|
BenchmarkConditionSpec,
|
|
62
56
|
BenchmarkRegisterRequest,
|
|
@@ -84,7 +78,6 @@ from sequence_base.contract.eval import (
|
|
|
84
78
|
TaskAgg,
|
|
85
79
|
)
|
|
86
80
|
|
|
87
|
-
# contract 6 — accounts / secret / billing
|
|
88
81
|
from sequence_base.contract.accounts import (
|
|
89
82
|
BootstrapResponse,
|
|
90
83
|
SecretCreateRequest,
|
|
@@ -93,10 +86,8 @@ from sequence_base.contract.accounts import (
|
|
|
93
86
|
UsageReport,
|
|
94
87
|
)
|
|
95
88
|
|
|
96
|
-
# official template weights — template://<name>[@v<n>]
|
|
97
89
|
from sequence_base.contract.templates import TEMPLATE_SCHEME, parse_template
|
|
98
90
|
|
|
99
|
-
# contract 7 — volumes (uploaded checkpoints and datasets)
|
|
100
91
|
from sequence_base.contract.volumes import (
|
|
101
92
|
MAX_FILES,
|
|
102
93
|
MAX_UPLOAD_BYTES,
|
|
@@ -109,37 +100,27 @@ from sequence_base.contract.volumes import (
|
|
|
109
100
|
parse_vol,
|
|
110
101
|
)
|
|
111
102
|
|
|
112
|
-
# shared error bodies
|
|
113
103
|
from sequence_base.contract.errors import ERRORS, ErrorBody, ErrorCode, ErrorSpec, WarmingError, code_for_status
|
|
114
104
|
|
|
115
105
|
__all__ = [
|
|
116
|
-
# contract 4
|
|
117
106
|
"ImageFrame", "Proprioception", "Observation", "ActionSpace", "ActionStep", "ActionChunk",
|
|
118
107
|
"ActRequest", "ActResponse",
|
|
119
|
-
# contract 1
|
|
120
108
|
"SeqFlags",
|
|
121
109
|
"ImageStepUvPip", "ImageStepApt", "ImageStepEnv", "ImageStepRun", "AddLocal", "ImageStepAddLocal", "ImageStep",
|
|
122
110
|
"ImageSpec",
|
|
123
111
|
"VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract", "Embodiment",
|
|
124
112
|
"PolicySpec", "PolicyRegistration",
|
|
125
|
-
# contract 2
|
|
126
113
|
"MAX_CODE_BUNDLE_BYTES", "CodeBundle",
|
|
127
114
|
"Resources", "Autoscaler", "Timeouts", "VolumeAttach", "DeploymentPolicy", "InferParam", "DeploymentRequest",
|
|
128
115
|
"DeploymentAccepted", "DeploymentState", "DeploymentStatusResponse",
|
|
129
|
-
# contract 3
|
|
130
116
|
"Lease", "ConnectRequest", "ConnectResponse",
|
|
131
|
-
# contract 5
|
|
132
117
|
"Variant", "Conditions", "RenderSpec", "Step", "BenchmarkSpec", "BenchmarkConditionSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
|
|
133
118
|
"EvalRequest", "ClampInfo", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
|
|
134
119
|
"EvalStatus", "SuiteAgg", "TaskAgg", "MetricAgg", "LossyTransform", "EvalReport",
|
|
135
120
|
"AdapterKind", "AdapterTransform",
|
|
136
|
-
# contract 6
|
|
137
121
|
"SecretRef", "SecretCreateRequest", "SecretInfo", "BootstrapResponse", "UsageReport",
|
|
138
|
-
# contract 7
|
|
139
122
|
"MAX_FILES", "MAX_UPLOAD_BYTES", "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse",
|
|
140
123
|
"VolumeInfo", "check_volume_name", "parse_vol",
|
|
141
|
-
# official template weights
|
|
142
124
|
"TEMPLATE_SCHEME", "parse_template",
|
|
143
|
-
# errors
|
|
144
125
|
"ErrorCode", "ErrorBody", "WarmingError", "ERRORS", "ErrorSpec", "code_for_status",
|
|
145
126
|
]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Contract 6 — accounts / secret / billing wire types (full picture in docs/platform/accounts.md).
|
|
3
3
|
|
|
4
4
|
Secret-leak rule, mechanically checkable (goal 15). Two kinds of secret:
|
|
5
5
|
(a) platform-minted — `direct_secret`, TLS private key. May appear in the DOWN direction only:
|
|
@@ -40,8 +40,6 @@ class SecretCreateRequest(BaseModel):
|
|
|
40
40
|
"""POST /v1/secrets — one name = one group of env values (e.g. planner = {PLANNER_API_KEY}).
|
|
41
41
|
`env` carries plaintext values; this is the WRITE-direction allow-list field (goal 15)."""
|
|
42
42
|
|
|
43
|
-
# F6: write-direction plaintext carrier — hide_input_in_errors so a malformed body's ValidationError
|
|
44
|
-
# str() never echoes the raw secret value (symmetric with SecretInfo). RUNTIME config, no schema change.
|
|
45
43
|
model_config = ConfigDict(extra="forbid", hide_input_in_errors=True)
|
|
46
44
|
|
|
47
45
|
name: str
|
|
@@ -51,10 +49,6 @@ class SecretCreateRequest(BaseModel):
|
|
|
51
49
|
class SecretInfo(BaseModel):
|
|
52
50
|
"""GET /v1/secrets element — metadata only, NO values. Exposes `env_keys`, not `env`."""
|
|
53
51
|
|
|
54
|
-
# hide_input_in_errors: if a malformed wire response carries a value-bearing extra field, extra="forbid"
|
|
55
|
-
# raises ValidationError; without this, its str() would echo the raw input (the secret value). Defense in
|
|
56
|
-
# depth so no caller that forgets to guard model_validate can leak it. RUNTIME config, not a wire shape —
|
|
57
|
-
# does NOT change the serialized schema, so CONTRACT_VERSION is unaffected.
|
|
58
52
|
model_config = ConfigDict(extra="forbid", hide_input_in_errors=True)
|
|
59
53
|
|
|
60
54
|
name: str
|
|
@@ -67,15 +61,12 @@ class BootstrapResponse(BaseModel):
|
|
|
67
61
|
"""What a worker receives when it starts — everything it needs to serve direct TLS `/act`. `direct_secret` + `tls_key` are DOWN-direction platform secrets; `env` is the
|
|
68
62
|
WRITE-direction injection of the policy's declared user secrets into its subprocess (goal 15)."""
|
|
69
63
|
|
|
70
|
-
# F6: carries down-direction platform secrets + write-direction user env — hide_input_in_errors so a
|
|
71
|
-
# malformed body's ValidationError never echoes plaintext (symmetric with SecretInfo). RUNTIME config.
|
|
72
64
|
model_config = ConfigDict(extra="forbid", hide_input_in_errors=True)
|
|
73
65
|
|
|
74
66
|
direct_secret: str = Field(..., description="Platform-minted bearer the worker's guard checks")
|
|
75
67
|
tls_key: str = Field(..., description="Self-signed TLS private key (down-direction platform secret)")
|
|
76
68
|
tls_cert: str = Field(..., description="Self-signed TLS certificate (public)")
|
|
77
69
|
env: dict[str, str] = Field(..., description="Policy's user secrets, expanded ENV_KEY → value")
|
|
78
|
-
# None: a policy without a checkpoint — nothing was copied or mounted. {gs-plans/storage T13}
|
|
79
70
|
weights_uri: str | None = None
|
|
80
71
|
lease: Lease
|
|
81
72
|
|
|
@@ -5,7 +5,7 @@ They are no longer the platform's wire: since contract 0.4.14 the worker passes
|
|
|
5
5
|
(`sequence_base.contract.wire`). This module keeps `from sequence_base.contract.act import Observation` resolving to the
|
|
6
6
|
same classes for the gateway, harness and SDK code that still uses the preset.
|
|
7
7
|
"""
|
|
8
|
-
from sequence_base.presets.robot_arm import (
|
|
8
|
+
from sequence_base.presets.robot_arm import (
|
|
9
9
|
ActionChunk,
|
|
10
10
|
ActionSpace,
|
|
11
11
|
ActionStep,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Contract 5 (registration half) — benchmark is a second authorizable artifact, its registration symmetric to
|
|
3
3
|
a policy's. `BenchmarkSpec` carries the same infra declaration a PolicySpec does, plus a structured
|
|
4
4
|
`Conditions` (the eval matrix), versioned `assets` volumes, an optional `render` (mp4) declaration, and
|
|
5
5
|
SeqFlags→method bindings covering SETUP / RESET / CHECK / SCORE. The eval *run* wire types (RolloutResult,
|
|
@@ -19,7 +19,7 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
|
19
19
|
from sequence_base.contract.accounts import SecretRef
|
|
20
20
|
from sequence_base.contract.deployments import CodeBundle, DeploymentState, InferParam
|
|
21
21
|
from sequence_base.contract.flags import BENCHMARK_BATCHED, BENCHMARK_BINDABLE, SeqFlags
|
|
22
|
-
from sequence_base.contract.policy import ActionContract, ImageSpec, VolumeRef
|
|
22
|
+
from sequence_base.contract.policy import KIND_NAME, ActionContract, ImageSpec, VolumeRef
|
|
23
23
|
|
|
24
24
|
__all__ = [
|
|
25
25
|
"Variant",
|
|
@@ -60,10 +60,6 @@ class RenderSpec(BaseModel):
|
|
|
60
60
|
|
|
61
61
|
camera: str = Field(..., description="Which camera to pull frames from, e.g. 'wrist' / 'agentview'")
|
|
62
62
|
fps: int = 30
|
|
63
|
-
# How the platform turns each frame before encoding it. Whether a camera's picture is upside down is a property of
|
|
64
|
-
# the renderer (OpenGL's glReadPixels returns the bottom row first: LIBERO / robosuite), so its author says it once
|
|
65
|
-
# here instead of everyone who runs an eval passing it. {gs-plans inbox "LIBERO 风格的相机录出来是倒的"}
|
|
66
|
-
# None (as drawn) is left off the wire, so a recording that is not turned registers on any platform version.
|
|
67
63
|
flip: Literal["vertical", "horizontal", "both"] | None = Field(
|
|
68
64
|
None, description="'vertical': rows reversed (upside down); 'horizontal': mirrored; 'both': turned 180°; "
|
|
69
65
|
"None: as drawn")
|
|
@@ -103,18 +99,16 @@ class BenchmarkSpec(BaseModel):
|
|
|
103
99
|
handler_ref: str
|
|
104
100
|
bindings: dict[SeqFlags, str] = Field(..., description="SETUP / RESET / CHECK / SCORE (+ OBSERVE / STEP) → method name")
|
|
105
101
|
gpu: str | list[str] | None = None
|
|
106
|
-
# How many environments one benchmark instance runs at once when an eval does not say (the author's default).
|
|
107
102
|
envs: int | None = Field(default=None, ge=1, le=65536)
|
|
108
103
|
assets: list[VolumeRef] = Field(default_factory=list)
|
|
109
104
|
render: RenderSpec | None = None
|
|
110
105
|
cpu: int | None = None
|
|
111
106
|
memory_gb: int | None = None
|
|
112
107
|
secrets: list[SecretRef] = Field(default_factory=list)
|
|
108
|
+
engine: str | None = Field(default=None, pattern=KIND_NAME)
|
|
113
109
|
|
|
114
110
|
@model_validator(mode="after")
|
|
115
111
|
def _bindings_only_benchmark_hooks(self) -> "BenchmarkSpec":
|
|
116
|
-
# eval-raw: only SETUP / RESET / CHECK / SCORE are bindable. ROLLOUT is deprecated (never bound) and
|
|
117
|
-
# a policy-lifecycle flag has no place here — either is a malformed registration, caught at build.
|
|
118
112
|
bad = set(self.bindings) - set(BENCHMARK_BINDABLE)
|
|
119
113
|
if bad:
|
|
120
114
|
names = ", ".join(sorted(f.name or str(f.value) for f in bad))
|
|
@@ -130,7 +124,7 @@ class BenchmarkSpec(BaseModel):
|
|
|
130
124
|
|
|
131
125
|
class BenchmarkConditionSpec(BaseModel):
|
|
132
126
|
"""One episode setting exactly as the author declared it — a sparse list or a cross product, with its own step
|
|
133
|
-
cap, wall-clock limit and suite.
|
|
127
|
+
cap, wall-clock limit and suite."""
|
|
134
128
|
|
|
135
129
|
model_config = ConfigDict(extra="forbid")
|
|
136
130
|
|
|
@@ -158,41 +152,20 @@ class BenchmarkRegisterRequest(BaseModel):
|
|
|
158
152
|
render: RenderSpec | None = None
|
|
159
153
|
cpu: int | None = None
|
|
160
154
|
memory_gb: int | None = None
|
|
161
|
-
# The benchmark's own source (reset/check hooks, its adapters), shipped like a policy's — the eval worker
|
|
162
|
-
# imports handler_ref and any adapter ref from it. None only when the image installs the module itself.
|
|
163
155
|
code: CodeBundle | None = None
|
|
164
|
-
# Which suite each task belongs to (task -> suite), so reports aggregate per suite without the platform
|
|
165
|
-
# guessing from task names. A task not listed here has no suite.
|
|
166
156
|
suite_of: dict[str, str] = Field(default_factory=dict)
|
|
167
|
-
# The action contract the benchmark's sim consumes; eval compares it with the policy's and requires an
|
|
168
|
-
# adapter when they differ.
|
|
169
157
|
action_contract: ActionContract | None = None
|
|
170
|
-
# Credentials the sim's own code needs (a dataset token), by name only — injected into the build check and
|
|
171
|
-
# every eval shard, never into the policy. {gs-plans/customization/benchmark.md "A.1 SECRETS"}
|
|
172
158
|
secrets: list[SecretRef] = Field(default_factory=list)
|
|
173
|
-
# Whether the benchmark defines @seq.score — the platform then calls it per group (overall, each suite, each
|
|
174
|
-
# task) after every episode has run, and its keys override those groups' means. {benchmark.md "A.2"}
|
|
175
159
|
score: bool = False
|
|
176
|
-
# Every condition as declared (a sparse list stays sparse; each keeps its own max_steps / timeout_s / suite),
|
|
177
|
-
# where `conditions` can only say tasks x seeds x variants. The eval runs these when present.
|
|
178
160
|
condition_list: list[BenchmarkConditionSpec] | None = Field(default=None, max_length=5000)
|
|
179
|
-
# An episode's wall-clock limit (seconds) when a condition sets none; past it the episode ends truncated.
|
|
180
161
|
timeout_s: float | None = Field(default=None, gt=0)
|
|
181
|
-
# The adapter (module:Class in the benchmark's own code) an eval uses when it names none.
|
|
182
162
|
default_adapter_ref: str | None = None
|
|
183
|
-
# The card each benchmark instance runs on (L4 or RTX-PRO-6000); None takes the platform's default (L4). The
|
|
184
|
-
# simulation never runs on a CPU. {gs-plans/benchmark-internals/design.md "一、规则"}
|
|
185
163
|
gpu: str | None = Field(default=None, max_length=32)
|
|
186
|
-
# How many environments one instance runs at once when an eval does not say; None takes 1.
|
|
187
164
|
envs: int | None = Field(default=None, ge=1, le=65536)
|
|
188
|
-
# The parameters its @seq.setup declares — `def setup(self, envs, *, noise_std: float = 0.02)` — each with its type
|
|
189
|
-
# and default, which an eval may set (--param benchmark.noise_std=0); None: it declares none, or an SDK from before
|
|
190
|
-
# them. {gs-plans/eval-adapters/36}
|
|
191
165
|
params: dict[str, InferParam] | None = Field(default=None, max_length=32)
|
|
192
|
-
# Whether the benchmark is batched (binds OBSERVE and STEP and steps many environments in one call).
|
|
193
166
|
batched: bool = False
|
|
194
|
-
# The Dockerfile's build context, as for a deployment.
|
|
195
167
|
image_context: CodeBundle | None = None
|
|
168
|
+
engine: str | None = Field(default=None, pattern=KIND_NAME)
|
|
196
169
|
|
|
197
170
|
@model_validator(mode="after")
|
|
198
171
|
def _param_names(self) -> "BenchmarkRegisterRequest":
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Contract 3 — connect / lease (client → gateway; control plane, first call + renewal).
|
|
3
3
|
|
|
4
4
|
Control plane / data plane split: `connect(model)` goes through the gateway once (admission → acquire →
|
|
5
5
|
ensure worker up) and hands back the *direct* coordinates the client then uses to hit the worker's
|
|
6
|
-
`/act` without the gateway in the hot path (
|
|
6
|
+
`/act` without the gateway in the hot path (contract 4). The cold-start fallback is a defined wire body,
|
|
7
7
|
not an HTTP header alone — a 503 returns `WarmingError` (contract/errors.py), so `503 + Retry-After`
|
|
8
8
|
and the warming ETA are typed and checkable.
|
|
9
9
|
"""
|
|
@@ -13,7 +13,6 @@ from datetime import datetime
|
|
|
13
13
|
|
|
14
14
|
from pydantic import BaseModel, ConfigDict, Field
|
|
15
15
|
|
|
16
|
-
# The 503 cold/warming body is defined once in contract.errors and reused here — no second shape.
|
|
17
16
|
from sequence_base.contract.errors import WarmingError
|
|
18
17
|
from sequence_base.contract.io import ActionContract, ObservationContract
|
|
19
18
|
|
|
@@ -43,7 +42,7 @@ class ConnectResponse(BaseModel):
|
|
|
43
42
|
"""200 — the full set of coordinates for a direct worker connection.
|
|
44
43
|
|
|
45
44
|
`direct_secret` is a plaintext platform-minted bearer for the worker's `/act`; it is on the
|
|
46
|
-
allow-list of fields permitted to carry a secret value in the down direction (
|
|
45
|
+
allow-list of fields permitted to carry a secret value in the down direction (contract 6 / goal 15)."""
|
|
47
46
|
|
|
48
47
|
model_config = ConfigDict(extra="forbid")
|
|
49
48
|
|
|
@@ -53,9 +52,7 @@ class ConnectResponse(BaseModel):
|
|
|
53
52
|
lease: Lease
|
|
54
53
|
warming: bool = Field(default=False, description="True → worker still warming; see warming_eta_s")
|
|
55
54
|
warming_eta_s: int | None = Field(default=None, description="Seconds until warm, when warming")
|
|
56
|
-
# The contracts this session is held to, so the client can check and assemble before the first act.
|
|
57
55
|
action: ActionContract | None = None
|
|
58
56
|
observation: ObservationContract | None = None
|
|
59
57
|
embodiment: str | None = None
|
|
60
|
-
# The deployment's own description of its inputs and outputs (DeploymentPolicy.io), when the author wrote one.
|
|
61
58
|
io: str | None = None
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Contract 2 — `POST /v1/deployments` (SDK `seq deploy` → gateway) and its responses.
|
|
3
3
|
|
|
4
4
|
The request is the normalized wire form of a PolicySpec: one big body (new config adds a field, never a
|
|
5
5
|
new endpoint) whose sub-shapes reuse contract 1's types
|
|
@@ -25,6 +25,7 @@ from sequence_base.contract.templates import TEMPLATE_SCHEME, parse_template
|
|
|
25
25
|
from sequence_base.contract.volumes import VOL_SCHEME, parse_vol
|
|
26
26
|
from sequence_base.contract.io import Embodiment, ObservationContract
|
|
27
27
|
from sequence_base.contract.policy import (
|
|
28
|
+
KIND_NAME,
|
|
28
29
|
ActionContract,
|
|
29
30
|
Concurrency,
|
|
30
31
|
ImageSpec,
|
|
@@ -52,15 +53,10 @@ class Resources(BaseModel):
|
|
|
52
53
|
model_config = ConfigDict(extra="forbid")
|
|
53
54
|
|
|
54
55
|
gpu: list[str]
|
|
55
|
-
# Physical cores and GiB the worker requests, billed per second at the greater of the request and what it uses.
|
|
56
|
-
# None requests the minimum (0.125 cores, 0.125 GiB) and the worker may use more. A limit caps what it may use;
|
|
57
|
-
# None leaves it free to burst.
|
|
58
56
|
cpu: float | None = Field(default=None, ge=0.125, le=64)
|
|
59
57
|
cpu_limit: float | None = Field(default=None, ge=0.125, le=128)
|
|
60
58
|
memory_gb: float | None = Field(default=None, ge=0.125, le=1024)
|
|
61
59
|
memory_limit_gb: float | None = Field(default=None, ge=0.125, le=1024)
|
|
62
|
-
# Where the workers run (near the robots): one of the regions the platform offers. None: the platform picks.
|
|
63
|
-
# {gs-plans/customization/policy.md "区域"}
|
|
64
60
|
region: str | None = Field(default=None, max_length=32)
|
|
65
61
|
|
|
66
62
|
@model_validator(mode="after")
|
|
@@ -77,16 +73,10 @@ class Autoscaler(BaseModel):
|
|
|
77
73
|
model_config = ConfigDict(extra="forbid")
|
|
78
74
|
|
|
79
75
|
min_containers: int = 0
|
|
80
|
-
# Accepted and ignored: limits count GPUs (`max_gpus`). sequence-ai <= 0.28 still sends it and does not pin
|
|
81
|
-
# sequence-base, so removing it would break their deploys; it goes when no supported SDK sends it.
|
|
82
76
|
max_containers: int | None = None
|
|
83
|
-
# The most GPUs this deployment may hold at once. None: no cap of its own — only the account's and the platform's
|
|
84
|
-
# GPU limits bound it. Limits count cards, never machines. {gs-plans/parallel/serving.md "1. 限制按卡数,不按台数"}
|
|
85
77
|
max_gpus: int | None = Field(default=None, ge=1, le=256)
|
|
86
78
|
buffer_containers: int | None = None
|
|
87
79
|
scaledown_window: int = 600
|
|
88
|
-
# When every slot is taken and no card is left: queue the new robot before its control loop starts, or refuse it at
|
|
89
|
-
# once; how long a queued robot waits before it is told why. {gs-plans/parallel/serving-queue.md "connect 的处理顺序"}
|
|
90
80
|
on_full: Literal["queue", "reject"] = "queue"
|
|
91
81
|
queue_timeout_s: int = Field(default=120, ge=5, le=3600)
|
|
92
82
|
|
|
@@ -116,11 +106,11 @@ class VolumeAttach(BaseModel):
|
|
|
116
106
|
class InferParam(BaseModel):
|
|
117
107
|
"""One inference parameter the policy's `@seq.infer` declares — `def infer(self, obs, *, num_steps: int = 10)`
|
|
118
108
|
— with its type and default. An /act or an eval may set it; the worker passes only declared names, cast to
|
|
119
|
-
this type, and refuses any other.
|
|
109
|
+
this type, and refuses any other.
|
|
120
110
|
|
|
121
111
|
`min` / `max` (a number's bounds, inclusive) and `choices` (the values it may take) — declared as
|
|
122
112
|
`Annotated[int, seq.Range(1, 10)]` / `seq.OneOf(...)` — refuse any other value before inference, saying which are
|
|
123
|
-
allowed; None (left off the wire) holds it to its type alone.
|
|
113
|
+
allowed; None (left off the wire) holds it to its type alone."""
|
|
124
114
|
|
|
125
115
|
model_config = ConfigDict(extra="forbid")
|
|
126
116
|
|
|
@@ -182,25 +172,12 @@ class DeploymentPolicy(BaseModel):
|
|
|
182
172
|
streaming: bool = False
|
|
183
173
|
frames_per_chunk: int | None = None
|
|
184
174
|
secrets: list[SecretRef] = Field(default_factory=list)
|
|
185
|
-
# The observation contract the worker checks every /act against (None: undeclared), and per-robot
|
|
186
|
-
# contracts for a multi-embodiment policy — the worker picks one by the session's embodiment.
|
|
187
175
|
observation: ObservationContract | None = None
|
|
188
176
|
embodiments: dict[str, Embodiment] | None = None
|
|
189
|
-
# Did the author declare `action=` (or embodiments)? SDKs before contract 0.4.14 always sent an `action` (a
|
|
190
|
-
# placeholder when none was declared); only a declared contract is enforced on the worker and handed to the robot.
|
|
191
|
-
# None (an older SDK) is treated as undeclared, so an older policy is never refused for the placeholder it never
|
|
192
|
-
# chose. A current SDK sends `action` only when declared. {io-contract.md "旧 SDK 写的 POLICY 照样能 BUILD"}
|
|
193
177
|
action_declared: bool | None = None
|
|
194
|
-
# The author's own description of what infer() takes and returns; stored, handed to the robot at connect, never
|
|
195
|
-
# interpreted by the platform.
|
|
196
178
|
io: str | None = Field(default=None, max_length=4000)
|
|
197
|
-
|
|
198
|
-
# SDK that did not look; {}: the policy declares none.
|
|
179
|
+
family: str | None = Field(default=None, pattern=KIND_NAME)
|
|
199
180
|
params: dict[str, InferParam] | None = Field(default=None, max_length=32)
|
|
200
|
-
# The platform models the policy calls while it runs (`@seq.policy(models=[...])`), by full catalogue id
|
|
201
|
-
# (accounts/sequences/models/<name>): the act key that drives this deployment may call exactly these on /v1/chat
|
|
202
|
-
# and /v1/decide, and nothing else it could spend on. None: the policy calls none (and an older SDK never sent it).
|
|
203
|
-
# {gs-plans models-api/11, user 2026-10-04 "act key should have ability to call any model involved in the policy"}
|
|
204
181
|
models: list[str] | None = Field(default=None, max_length=16)
|
|
205
182
|
|
|
206
183
|
@model_validator(mode="after")
|
|
@@ -208,7 +185,6 @@ class DeploymentPolicy(BaseModel):
|
|
|
208
185
|
for name in self.params or {}:
|
|
209
186
|
if not name.isidentifier() or name.startswith("_"):
|
|
210
187
|
raise ValueError(f"inference parameter {name!r} is not a public Python identifier")
|
|
211
|
-
# A declared model is a catalogue id: no blanks, no duplicates (the gateway checks each one exists).
|
|
212
188
|
for model in self.models or []:
|
|
213
189
|
if not model or model != model.strip() or "/" not in model:
|
|
214
190
|
raise ValueError(f"policy model {model!r} is not a catalogue id (accounts/sequences/models/<name>)")
|
|
@@ -264,26 +240,15 @@ class DeploymentRequest(BaseModel):
|
|
|
264
240
|
|
|
265
241
|
name: str
|
|
266
242
|
image: ImageSpec
|
|
267
|
-
# None: a policy without a checkpoint — no weights volume is made, copied or mounted. {gs-plans/storage T13}
|
|
268
243
|
weights: VolumeMount | None = None
|
|
269
244
|
resources: Resources
|
|
270
245
|
policy: DeploymentPolicy
|
|
271
|
-
# The handler's source. None only when `handler_ref` is importable from the image itself (a package the
|
|
272
|
-
# ImageSpec installs); otherwise the worker has nothing to import and the build fails.
|
|
273
246
|
code: CodeBundle | None = None
|
|
274
|
-
# The Dockerfile's build context (the directory its COPY / ADD read), shipped like the code. Only with
|
|
275
|
-
# image.dockerfile. {gs-plans/customization/policy.md "DOCKERFILE 的 BUILD CONTEXT"}
|
|
276
247
|
image_context: CodeBundle | None = None
|
|
277
|
-
# Non-weights volumes the policy declares (goal 7). List, not dict, so a mount is never duplicated as
|
|
278
|
-
# both key and value; the gateway only stores the dump, no consumer needs dict lookup.
|
|
279
248
|
volumes: list[VolumeAttach] = Field(default_factory=list)
|
|
280
249
|
autoscaler: Autoscaler = Field(default_factory=Autoscaler)
|
|
281
|
-
# Every field optional: an omitted @seq.concurrent serves one robot session per model copy, one copy per card.
|
|
282
250
|
concurrency: Concurrency = Field(default_factory=Concurrency)
|
|
283
251
|
timeouts: Timeouts | None = None
|
|
284
|
-
# Weights in several named parts: `weights` is the first and `weights_name` its name; `extra_weights` the others,
|
|
285
|
-
# each provisioned like `weights`. @seq.load then receives {name: directory}. Both None (and absent from the wire):
|
|
286
|
-
# one weights path, as before. {gs-plans/customization/options.md "多份权重文件(BASE + LORA …)"}
|
|
287
252
|
weights_name: str | None = None
|
|
288
253
|
extra_weights: dict[str, VolumeMount] | None = None
|
|
289
254
|
|
|
@@ -293,7 +258,7 @@ class DeploymentRequest(BaseModel):
|
|
|
293
258
|
"""The name is how a robot addresses the model (`connect(model=name)`) and how the console lists it, so
|
|
294
259
|
it is a slug: letters, digits, `.`, `_`, `-`, starting with a letter or digit, at most 64 characters.
|
|
295
260
|
A raw POST once registered "hfcheck-missing hf://nobody/x" — a space, a scheme and a slash in a name.
|
|
296
|
-
|
|
261
|
+
"""
|
|
297
262
|
if not _NAME.fullmatch(v):
|
|
298
263
|
raise ValueError("name must be 1-64 letters, digits, '.', '_' or '-', starting with a letter or digit")
|
|
299
264
|
return v
|
|
@@ -325,9 +290,7 @@ class DeploymentRequest(BaseModel):
|
|
|
325
290
|
return self
|
|
326
291
|
|
|
327
292
|
|
|
328
|
-
# A deployment name: what `connect(model=...)` and the console address. {DeploymentRequest._name_is_a_slug}
|
|
329
293
|
_NAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,63}")
|
|
330
|
-
# The origins the build knows how to provision weights from.
|
|
331
294
|
_WEIGHTS_SCHEMES = ("gs://", "hf://", "s3://", "https://")
|
|
332
295
|
|
|
333
296
|
|
|
@@ -357,5 +320,4 @@ class DeploymentStatusResponse(BaseModel):
|
|
|
357
320
|
status: DeploymentState
|
|
358
321
|
model: str
|
|
359
322
|
error: str | None = Field(default=None, description="Build-failure reason; set only when FAILED")
|
|
360
|
-
# Why a deployment that was serving was marked FAILED: its workers kept dying of the same cause.
|
|
361
323
|
reason: Literal["gpu_out_of_memory", "host_out_of_memory", "crashed"] | None = None
|