sequence-base 0.4.26__tar.gz → 0.4.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.4.26 → sequence_base-0.4.28}/PKG-INFO +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/pyproject.toml +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/__init__.py +14 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/constants/limits.py +9 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/constants/version.py +4 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/__init__.py +7 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/connect.py +5 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/deployments.py +27 -6
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/flags.py +4 -0
- sequence_base-0.4.28/sequence_base/contract/obs_schedule.py +170 -0
- sequence_base-0.4.28/sequence_base/contract/observe.py +66 -0
- sequence_base-0.4.28/sequence_base/contract/own_io.py +238 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/policy.py +36 -7
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/wire.py +5 -0
- sequence_base-0.4.28/sequence_base/episode.py +58 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/presets/robot_arm.py +30 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schema_export.py +9 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ActRequest.json +15 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ActResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ActionChunk.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ActionContract.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ActionStep.json +1 -1
- sequence_base-0.4.28/sequence_base/schemas/Actions.json +51 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/AdapterPair.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/AddLocal.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Autoscaler.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/BenchmarkConditionSpec.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/BenchmarkRegisterRequest.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/BenchmarkSpec.json +7 -4
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/BootstrapResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Camera.json +51 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/CodeBundle.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Concurrency.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Conditions.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ConnectRequest.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ConnectResponse.json +259 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/DeploymentAccepted.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/DeploymentPolicy.json +291 -25
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/DeploymentRequest.json +305 -25
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Embodiment.json +51 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ErrorBody.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ErrorCodes.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Estimate.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/EvalReport.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/EvalRequest.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/EvalResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ImageFrame.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ImageSpec.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepAddLocal.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepApt.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepEnv.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepProject.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepRun.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepTemplateFiles.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepUvPip.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/InferParam.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Lease.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/MetricAgg.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Observation.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ObservationContract.json +51 -1
- sequence_base-0.4.28/sequence_base/schemas/Observations.json +264 -0
- sequence_base-0.4.28/sequence_base/schemas/ObserveRequest.json +57 -0
- sequence_base-0.4.28/sequence_base/schemas/ObserveResponse.json +17 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/PolicyRegistration.json +305 -36
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/PolicySpec.json +298 -32
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Project.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Proprioception.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/RenderSpec.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/ReplaySpec.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Resources.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/RolloutResult.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/SecretCreateRequest.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/SecretInfo.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/SecretRef.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/SeqFlags.json +4 -2
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Step.json +1 -1
- sequence_base-0.4.28/sequence_base/schemas/Stream.json +193 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/TaskAgg.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/TemplateFiles.json +1 -1
- sequence_base-0.4.28/sequence_base/schemas/Tensors.json +14 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Timeouts.json +15 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/UploadTarget.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/Variant.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/VolumeFile.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/VolumeInfo.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/VolumeMount.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/VolumeRef.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/schemas/WarmingError.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.28}/.gitignore +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/README.md +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/auth.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/codec/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/codec/image.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/constants/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/constants/cards.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/constants/headers.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/construct.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/accounts.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/act.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/adapters.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/benchmarks.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/errors.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/eval.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/io.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/templates.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/contract/volumes.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/errors/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/errors/transport.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/msgpack_io.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/obs/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/obs/log.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/obs/rid.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/presets/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/presets/frames.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/telemetry/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.28}/sequence_base/telemetry/events.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sequence-base
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.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
|
Project-URL: Changelog, https://generalsequences.com/docs/changelog/
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sequence-base"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.28"
|
|
4
4
|
description = "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
|
dependencies = ["pydantic>=2.0"]
|
|
@@ -26,6 +26,9 @@ from sequence_base.contract import (
|
|
|
26
26
|
Camera,
|
|
27
27
|
Embodiment,
|
|
28
28
|
ObservationContract,
|
|
29
|
+
ObserveFrame,
|
|
30
|
+
ObserveRequest,
|
|
31
|
+
ObserveResponse,
|
|
29
32
|
MAX_CODE_BUNDLE_BYTES,
|
|
30
33
|
UploadTarget,
|
|
31
34
|
VolumeFile,
|
|
@@ -81,6 +84,10 @@ from sequence_base.contract import (
|
|
|
81
84
|
PolicyConstructor,
|
|
82
85
|
PolicyRegistration,
|
|
83
86
|
PolicySpec,
|
|
87
|
+
Observations,
|
|
88
|
+
Stream,
|
|
89
|
+
Actions,
|
|
90
|
+
Tensors,
|
|
84
91
|
Proprioception,
|
|
85
92
|
RenderSpec,
|
|
86
93
|
ReplaySpec,
|
|
@@ -120,6 +127,9 @@ from sequence_base.errors.transport import (
|
|
|
120
127
|
)
|
|
121
128
|
|
|
122
129
|
|
|
130
|
+
from sequence_base.episode import Episode
|
|
131
|
+
|
|
132
|
+
|
|
123
133
|
from sequence_base import auth
|
|
124
134
|
|
|
125
135
|
|
|
@@ -131,11 +141,13 @@ __all__ = [
|
|
|
131
141
|
|
|
132
142
|
"ImageFrame", "Proprioception", "Observation", "ActionSpace", "ActionStep", "ActionChunk",
|
|
133
143
|
"ActRequest", "ActResponse",
|
|
144
|
+
"ObserveFrame", "ObserveRequest", "ObserveResponse",
|
|
134
145
|
|
|
135
146
|
"SeqFlags", "ImageStepUvPip", "ImageStepApt", "ImageStepEnv", "ImageStepRun", "AddLocal", "ImageStepAddLocal",
|
|
136
147
|
"FROZEN_PATH", "TemplateFiles", "ImageStepTemplateFiles", "Project", "ImageStepProject", "ImageStep", "ImageSpec",
|
|
137
148
|
"VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract", "Embodiment",
|
|
138
149
|
"PolicySpec", "PolicyRegistration", "PolicyConstructor",
|
|
150
|
+
"Observations", "Stream", "Actions", "Tensors",
|
|
139
151
|
|
|
140
152
|
"MAX_CODE_BUNDLE_BYTES", "CodeBundle",
|
|
141
153
|
"Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
|
|
@@ -162,6 +174,8 @@ __all__ = [
|
|
|
162
174
|
|
|
163
175
|
"TransportUnavailable", "TransportWarming", "TransportRejected", "TransportOverloaded",
|
|
164
176
|
|
|
177
|
+
"Episode",
|
|
178
|
+
|
|
165
179
|
"auth",
|
|
166
180
|
]
|
|
167
181
|
|
|
@@ -15,6 +15,8 @@ from sequence_base.contract.act import (
|
|
|
15
15
|
|
|
16
16
|
from sequence_base.contract.wire import ActRequest, ActResponse
|
|
17
17
|
|
|
18
|
+
from sequence_base.contract.observe import ObserveFrame, ObserveRequest, ObserveResponse
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
from sequence_base.contract.flags import SeqFlags
|
|
20
22
|
from sequence_base.contract.policy import (
|
|
@@ -44,6 +46,9 @@ from sequence_base.contract.policy import (
|
|
|
44
46
|
)
|
|
45
47
|
|
|
46
48
|
|
|
49
|
+
from sequence_base.contract.own_io import Actions, Observations, Stream, Tensors
|
|
50
|
+
|
|
51
|
+
|
|
47
52
|
from sequence_base.contract.deployments import (
|
|
48
53
|
MAX_CODE_BUNDLE_BYTES,
|
|
49
54
|
Autoscaler,
|
|
@@ -119,12 +124,14 @@ __all__ = [
|
|
|
119
124
|
|
|
120
125
|
"ImageFrame", "Proprioception", "Observation", "ActionSpace", "ActionStep", "ActionChunk",
|
|
121
126
|
"ActRequest", "ActResponse",
|
|
127
|
+
"ObserveFrame", "ObserveRequest", "ObserveResponse",
|
|
122
128
|
|
|
123
129
|
"SeqFlags",
|
|
124
130
|
"ImageStepUvPip", "ImageStepApt", "ImageStepEnv", "ImageStepRun", "AddLocal", "ImageStepAddLocal",
|
|
125
131
|
"TemplateFiles", "ImageStepTemplateFiles", "Project", "ImageStepProject", "FROZEN_PATH", "ImageStep", "ImageSpec",
|
|
126
132
|
"VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract", "Embodiment",
|
|
127
133
|
"PolicySpec", "PolicyRegistration", "PolicyConstructor",
|
|
134
|
+
"Observations", "Stream", "Actions", "Tensors",
|
|
128
135
|
|
|
129
136
|
"MAX_CODE_BUNDLE_BYTES", "CodeBundle",
|
|
130
137
|
"Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
|
|
@@ -16,6 +16,7 @@ from pydantic import BaseModel, ConfigDict, Field
|
|
|
16
16
|
|
|
17
17
|
from sequence_base.contract.errors import WarmingError
|
|
18
18
|
from sequence_base.contract.io import ActionContract, ObservationContract
|
|
19
|
+
from sequence_base.contract.own_io import Actions, Observations
|
|
19
20
|
|
|
20
21
|
__all__ = ["Lease", "ConnectRequest", "ConnectResponse", "WarmingError"]
|
|
21
22
|
|
|
@@ -57,3 +58,7 @@ class ConnectResponse(BaseModel):
|
|
|
57
58
|
embodiment: str | None = None
|
|
58
59
|
|
|
59
60
|
io: str | None = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
observations: Observations | None = None
|
|
64
|
+
actions: Actions | None = None
|
|
@@ -22,10 +22,12 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida
|
|
|
22
22
|
|
|
23
23
|
from sequence_base.contract.accounts import SecretRef
|
|
24
24
|
from sequence_base.contract.io import Embodiment, ObservationContract
|
|
25
|
+
from sequence_base.contract.own_io import Actions, Observations, Tensors, check_own_io
|
|
25
26
|
from sequence_base.contract.policy import (
|
|
26
27
|
KIND_NAME,
|
|
27
28
|
ActionContract,
|
|
28
29
|
Concurrency,
|
|
30
|
+
Episodes,
|
|
29
31
|
ImageSpec,
|
|
30
32
|
VolumeMount,
|
|
31
33
|
)
|
|
@@ -105,6 +107,11 @@ class Timeouts(BaseModel):
|
|
|
105
107
|
startup: int = Field(..., ge=60, le=18000)
|
|
106
108
|
|
|
107
109
|
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
episode_idle: int | None = Field(default=None, ge=10, le=86400)
|
|
113
|
+
|
|
114
|
+
|
|
108
115
|
class InferParam(BaseModel):
|
|
109
116
|
"""One inference parameter the policy's `@seq.infer` declares — `def infer(self, obs, *, num_steps: int = 10)`
|
|
110
117
|
— with its type and default. An /act or an eval may set it; the worker passes only declared names, cast to
|
|
@@ -171,7 +178,12 @@ class DeploymentPolicy(BaseModel):
|
|
|
171
178
|
|
|
172
179
|
handler_ref: str
|
|
173
180
|
action: ActionContract | None = None
|
|
174
|
-
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
episodes: Episodes | None = None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
streaming: bool = Field(default=False, exclude=True)
|
|
175
187
|
secrets: list[SecretRef] = Field(default_factory=list)
|
|
176
188
|
|
|
177
189
|
|
|
@@ -204,15 +216,24 @@ class DeploymentPolicy(BaseModel):
|
|
|
204
216
|
|
|
205
217
|
|
|
206
218
|
|
|
219
|
+
observations: Observations | None = None
|
|
220
|
+
actions: Actions | None = None
|
|
221
|
+
tensors: Tensors | None = None
|
|
207
222
|
|
|
208
|
-
|
|
209
|
-
|
|
223
|
+
@model_validator(mode="after")
|
|
224
|
+
def _episodes_of_streaming(self) -> "DeploymentPolicy":
|
|
225
|
+
|
|
226
|
+
if self.streaming and self.episodes not in (None, "own"):
|
|
227
|
+
raise ValueError(f"streaming is what a record from before contract 0.5.24 says; beside "
|
|
228
|
+
f"episodes={self.episodes!r}, leave it out")
|
|
229
|
+
if self.streaming:
|
|
230
|
+
self.episodes = "own"
|
|
231
|
+
self.streaming = self.episodes == "own"
|
|
232
|
+
return self
|
|
210
233
|
|
|
211
234
|
@model_validator(mode="after")
|
|
212
235
|
def _own_io(self) -> "DeploymentPolicy":
|
|
213
|
-
|
|
214
|
-
raise ValueError("own_observations and own_actions are for a policy of its own inputs and outputs; this one "
|
|
215
|
-
"declares the robot-arm contract, where observation history and the action chunk say it")
|
|
236
|
+
check_own_io(self)
|
|
216
237
|
return self
|
|
217
238
|
|
|
218
239
|
@model_validator(mode="after")
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Which steps of an episode a policy of its own inputs is handed at an act, and which keys of each: its Observations
|
|
3
|
+
(contract.own_io) read for one act - the one rule a worker hands a robot's streamed frames by
|
|
4
|
+
, an eval hands its benchmark's by (obs_history), and a robot's SDK uploads by.
|
|
5
|
+
|
|
6
|
+
In one sentence: an act at step t, the act before it at t_prev (-1: none yet) → each key's steps - its `at` offsets
|
|
7
|
+
t + off (a window: the worker keeps the frames), every k steps of (t_prev, t] counting back from t (fed: the policy keeps
|
|
8
|
+
them), else t alone; Observations(every=k) the whole observation so → a step before the episode's first reads as its
|
|
9
|
+
first (pad "repeat_first") or goes ("drop") → oldest first, each with the keys read at it, the act's own last:
|
|
10
|
+
{"observations": [...], "steps": [...]}. unit "seconds" counts seconds, taken to steps by the control rate of whoever
|
|
11
|
+
steps the episode - the robot's, the benchmark's - which its caller passes.
|
|
12
|
+
|
|
13
|
+
WHY one rule in one place: the worker, an eval and the SDK each act on the same declaration - which frames to keep,
|
|
14
|
+
which to render, which to upload - and a step one of them reads differently is a frame a policy is handed that it never
|
|
15
|
+
asked for, or never handed one it did. A robot's SDK restates it (it never imports sequence_base); its tests hold it to
|
|
16
|
+
this.
|
|
17
|
+
|
|
18
|
+
sched = obs_schedule.of(observations, rate=10.0) # None: nothing declared - the act's own, as always
|
|
19
|
+
obs_schedule.picks(sched, t=20, t_prev=12) # [(5, ("cam",)), (20, ("cam", "state"))]
|
|
20
|
+
obs_schedule.envelope(picked, frames={5: {...}}, own={...}, t=20)
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
__all__ = ["Schedule", "envelope", "keeps_from", "needed", "of", "picks", "reads_past", "wanted"]
|
|
28
|
+
|
|
29
|
+
Keys = tuple[str, ...] | None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class Schedule:
|
|
34
|
+
"""An Observations declaration in steps. `windows`: key -> its offsets (each 0 or below, oldest first); `fed`:
|
|
35
|
+
key -> every how many steps; `alone`: keys read at the act's step alone; `every`: the whole observation fed every
|
|
36
|
+
so many steps (no keys named); `whole`: no keys named - the whole observation; `pad`: "repeat_first" or "drop"."""
|
|
37
|
+
|
|
38
|
+
windows: dict
|
|
39
|
+
fed: dict
|
|
40
|
+
alone: tuple
|
|
41
|
+
every: int | None
|
|
42
|
+
whole: bool
|
|
43
|
+
pad: str
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _get(d: Any, name: str) -> Any:
|
|
47
|
+
return d.get(name) if isinstance(d, dict) else getattr(d, name, None)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def of(observations: Any, rate: float | None = None) -> Schedule | None:
|
|
51
|
+
"""The schedule an Observations declaration - the model, or its JSON - reads by; None when there is none (the act's
|
|
52
|
+
own observation, as before). `rate`: the control steps a second of whoever steps the episode, for unit "seconds"
|
|
53
|
+
alone. Raises ValueError for unit "seconds" without it."""
|
|
54
|
+
if observations is None:
|
|
55
|
+
return None
|
|
56
|
+
unit = _get(observations, "unit") or "steps"
|
|
57
|
+
|
|
58
|
+
def steps(x: Any) -> int:
|
|
59
|
+
if unit != "seconds":
|
|
60
|
+
return int(x)
|
|
61
|
+
if not rate:
|
|
62
|
+
raise ValueError("this policy counts its observations in seconds (unit='seconds'): the control rate of the "
|
|
63
|
+
"robot or benchmark stepping the episode takes them to steps, and none was given")
|
|
64
|
+
return int(round(float(x) * float(rate)))
|
|
65
|
+
|
|
66
|
+
windows, fed, alone = {}, {}, []
|
|
67
|
+
streams = _get(observations, "streams")
|
|
68
|
+
for key, stream in (streams or {}).items():
|
|
69
|
+
at, every = _get(stream, "at"), _get(stream, "every")
|
|
70
|
+
if at is not None:
|
|
71
|
+
windows[key] = tuple(sorted({min(0, steps(x)) for x in at}))
|
|
72
|
+
elif every is not None:
|
|
73
|
+
fed[key] = max(1, steps(every))
|
|
74
|
+
else:
|
|
75
|
+
alone.append(key)
|
|
76
|
+
every = _get(observations, "every")
|
|
77
|
+
return Schedule(windows=windows, fed=fed, alone=tuple(alone), every=max(1, steps(every)) if every is not None else
|
|
78
|
+
None, whole=streams is None, pad=_get(observations, "pad") or "repeat_first")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def reads_past(declared: Any) -> bool:
|
|
82
|
+
"""Whether an act reads a step before its own - a window reaching back, a fed key, the whole observation fed -
|
|
83
|
+
whose frames must reach the worker before the act does (its observation stream, POST /observe). `declared`: a
|
|
84
|
+
Schedule, or the Observations declaration itself (the model or its JSON) - read with no control rate, which only
|
|
85
|
+
scales a count in seconds."""
|
|
86
|
+
if declared is None:
|
|
87
|
+
return False
|
|
88
|
+
if isinstance(declared, Schedule):
|
|
89
|
+
return (any(off < 0 for offsets in declared.windows.values() for off in offsets) or bool(declared.fed)
|
|
90
|
+
or (declared.whole and declared.every is not None))
|
|
91
|
+
if _get(declared, "every") is not None:
|
|
92
|
+
return True
|
|
93
|
+
for stream in (_get(declared, "streams") or {}).values():
|
|
94
|
+
if _get(stream, "every") is not None or any(x < 0 for x in _get(stream, "at") or ()):
|
|
95
|
+
return True
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def picks(sched: Schedule | None, t: int, t_prev: int = -1) -> list[tuple[int, Keys]]:
|
|
100
|
+
"""(step, keys) of each observation the act at step `t` is handed, oldest first, the act's own last - keys None for
|
|
101
|
+
the whole observation. A step before the episode's first is its first (step 0) under pad "repeat_first", once for
|
|
102
|
+
each such step asked, and is left out under "drop". `t_prev`: the act before it (-1: the episode's first)."""
|
|
103
|
+
if sched is None:
|
|
104
|
+
return [(t, None)]
|
|
105
|
+
asked: dict[int, set] = {}
|
|
106
|
+
whole: set[int] = set()
|
|
107
|
+
for key, offsets in sched.windows.items():
|
|
108
|
+
for off in offsets:
|
|
109
|
+
asked.setdefault(t + off, set()).add(key)
|
|
110
|
+
for key, k in sched.fed.items():
|
|
111
|
+
for s in range(t, t_prev, -k):
|
|
112
|
+
asked.setdefault(s, set()).add(key)
|
|
113
|
+
for key in sched.alone:
|
|
114
|
+
asked.setdefault(t, set()).add(key)
|
|
115
|
+
if sched.whole:
|
|
116
|
+
whole.update(range(t, t_prev, -sched.every) if sched.every else (t,))
|
|
117
|
+
out: list[tuple[int, Keys]] = []
|
|
118
|
+
for s in sorted(set(asked) | whole):
|
|
119
|
+
if s < 0 and sched.pad == "drop":
|
|
120
|
+
continue
|
|
121
|
+
out.append((max(0, s), None if s in whole else tuple(sorted(asked[s]))))
|
|
122
|
+
return out
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def needed(sched: Schedule | None, t: int, t_prev: int = -1) -> set[int]:
|
|
126
|
+
"""The steps the act at `t` reads but does not carry itself: their frames come before it (the stream)."""
|
|
127
|
+
return {s for s, _ in picks(sched, t, t_prev) if s != t}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def keeps_from(sched: Schedule | None, t: int) -> int:
|
|
131
|
+
"""The earliest step an act after the one at `t` may read: a window reaches back its furthest offset from an act
|
|
132
|
+
one step on at the soonest; a fed key, the whole observation and a key read alone, nothing at or before `t`."""
|
|
133
|
+
reach = min((offsets[0] for offsets in (sched.windows.values() if sched else ())), default=0)
|
|
134
|
+
return max(0, t + 1 + min(0, reach))
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _merge(into: dict, step: int, keys: Keys) -> None:
|
|
138
|
+
if keys is None or into.get(step, ()) is None:
|
|
139
|
+
into[step] = None
|
|
140
|
+
else:
|
|
141
|
+
into[step] = tuple(sorted({*into.get(step, ()), *keys}))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def wanted(sched: Schedule | None, t: int, t_next: int) -> dict[int, Keys]:
|
|
145
|
+
"""{step: keys} to read - an eval's benchmark - or upload - a robot's SDK - among the steps after the act at `t` and
|
|
146
|
+
before the next at `t_next` (t_next itself is that act's own, carried by it): those the act at t_next reads, and
|
|
147
|
+
those a later act may - an act after t_next reads a window key at any step from t_next + 1 + its furthest offset,
|
|
148
|
+
which a window longer than one act's turn reaches back into."""
|
|
149
|
+
out: dict[int, Keys] = {}
|
|
150
|
+
for s, keys in picks(sched, t_next, t):
|
|
151
|
+
if t < s < t_next:
|
|
152
|
+
_merge(out, s, keys)
|
|
153
|
+
for key, offsets in (sched.windows.items() if sched else ()):
|
|
154
|
+
for s in range(max(t + 1, t_next + 1 + offsets[0]), t_next):
|
|
155
|
+
_merge(out, s, (key,))
|
|
156
|
+
return dict(sorted(out.items()))
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def envelope(picked: list[tuple[int, Keys]], frames: dict, own: Any, t: int) -> dict:
|
|
160
|
+
"""{"observations": [...], "steps": [...]}: each picked observation - `own`, the act's, at `t`, else `frames[step]`
|
|
161
|
+
- with only the keys read at it (a key it lacks is left out), oldest first. Raises KeyError for a step not in
|
|
162
|
+
`frames`."""
|
|
163
|
+
observations, steps = [], []
|
|
164
|
+
for step, keys in picked:
|
|
165
|
+
obs = own if step == t else frames[step]
|
|
166
|
+
if keys is not None and isinstance(obs, dict):
|
|
167
|
+
obs = {k: obs[k] for k in keys if k in obs}
|
|
168
|
+
observations.append(obs)
|
|
169
|
+
steps.append(step)
|
|
170
|
+
return {"observations": observations, "steps": steps}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The observation stream: a policy of its own inputs that reads every observation since its last act is sent each one as
|
|
3
|
+
the robot makes it, on a connection of its own (POST /observe), and an act carries only the newest.
|
|
4
|
+
|
|
5
|
+
In one sentence: the robot's SDK numbers each observation of an episode from 0 → posts them as they come, each post the
|
|
6
|
+
ones that came while the last was on its way (ObserveRequest) → the worker keeps them by episode and answers how far it
|
|
7
|
+
has every one (ObserveResponse.through) → an act carries the newest observation and its number (ActRequest.seq) → the
|
|
8
|
+
worker waits for the ones before it, STREAM_WAIT_S at most, and hands the policy {"observations": [...]}, oldest first,
|
|
9
|
+
the act's own last - what an eval's since_last_act sends in one act today.
|
|
10
|
+
|
|
11
|
+
WHY: an act was the only upload, so every frame between two acts waited for the next one - 24 frames, 1304 KB and a read
|
|
12
|
+
p50 of 425 ms from a robot at home. Sent as they are made, they
|
|
13
|
+
are on the worker before the act is.
|
|
14
|
+
|
|
15
|
+
What a policy declares of what it reads (its cameras, how many a second, when its upload falls behind) is plan 87's
|
|
16
|
+
first layer, which waits for the comparison of each part (the user's 69): the stream here is the transport alone, for
|
|
17
|
+
a policy fed every observation since its last act (Observations(every=1); own_observations "since_last_act" until
|
|
18
|
+
contract 0.5.25).
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
26
|
+
|
|
27
|
+
__all__ = ["FORMAT", "FRAMES_MISSING", "FRAMES_OVER_LIMIT", "ObserveFrame", "ObserveRequest", "ObserveResponse"]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
FORMAT = "x-seq-observe/1"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
FRAMES_MISSING = "frames_missing"
|
|
37
|
+
FRAMES_OVER_LIMIT = "frames_over_limit"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ObserveFrame(BaseModel):
|
|
41
|
+
"""One observation of an episode: its number in it, from 0, and the inputs, as the policy's infer() takes one."""
|
|
42
|
+
|
|
43
|
+
model_config = ConfigDict(extra="forbid")
|
|
44
|
+
|
|
45
|
+
seq: int = Field(..., ge=0, description="The observation's number in its episode, from 0")
|
|
46
|
+
inputs: Any = Field(..., description="One observation, exactly as the robot's code built it")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ObserveRequest(BaseModel):
|
|
50
|
+
"""POST /observe: the observations of one episode that came since the last post, and the numbers the robot left out
|
|
51
|
+
on purpose, which the worker then does not wait for."""
|
|
52
|
+
|
|
53
|
+
model_config = ConfigDict(extra="forbid")
|
|
54
|
+
|
|
55
|
+
episode_id: str = Field(..., min_length=1, max_length=256)
|
|
56
|
+
frames: list[ObserveFrame] = Field(default_factory=list)
|
|
57
|
+
skipped: list[int] = Field(default_factory=list, description="Numbers left out on purpose, never to come")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ObserveResponse(BaseModel):
|
|
61
|
+
"""200 from POST /observe: the highest number up to which the worker holds every observation of the episode, or has
|
|
62
|
+
been told one is skipped (-1: none yet). A number sent again is answered the same, never kept twice."""
|
|
63
|
+
|
|
64
|
+
model_config = ConfigDict(extra="forbid")
|
|
65
|
+
|
|
66
|
+
through: int = Field(..., ge=-1)
|