sequence-base 0.4.26__tar.gz → 0.4.27__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.27}/PKG-INFO +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/pyproject.toml +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/__init__.py +14 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/constants/limits.py +9 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/constants/version.py +3 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/__init__.py +7 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/connect.py +5 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/deployments.py +31 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/flags.py +4 -0
- sequence_base-0.4.27/sequence_base/contract/observe.py +65 -0
- sequence_base-0.4.27/sequence_base/contract/own_io.py +179 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/policy.py +40 -2
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/wire.py +5 -0
- sequence_base-0.4.27/sequence_base/episode.py +58 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/presets/robot_arm.py +30 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schema_export.py +9 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ActRequest.json +15 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ActResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ActionChunk.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ActionContract.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ActionStep.json +1 -1
- sequence_base-0.4.27/sequence_base/schemas/Actions.json +51 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/AdapterPair.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/AddLocal.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Autoscaler.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/BenchmarkConditionSpec.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/BenchmarkRegisterRequest.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/BenchmarkSpec.json +7 -4
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/BootstrapResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Camera.json +51 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/CodeBundle.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Concurrency.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Conditions.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ConnectRequest.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ConnectResponse.json +259 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/DeploymentAccepted.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/DeploymentPolicy.json +299 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/DeploymentRequest.json +313 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Embodiment.json +51 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ErrorBody.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ErrorCodes.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Estimate.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/EvalReport.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/EvalRequest.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/EvalResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ImageFrame.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ImageSpec.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepAddLocal.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepApt.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepEnv.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepProject.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepRun.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepTemplateFiles.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepUvPip.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/InferParam.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Lease.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/MetricAgg.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Observation.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ObservationContract.json +51 -1
- sequence_base-0.4.27/sequence_base/schemas/Observations.json +264 -0
- sequence_base-0.4.27/sequence_base/schemas/ObserveRequest.json +57 -0
- sequence_base-0.4.27/sequence_base/schemas/ObserveResponse.json +17 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/PolicyRegistration.json +306 -5
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/PolicySpec.json +299 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Project.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Proprioception.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/RenderSpec.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/ReplaySpec.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Resources.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/RolloutResult.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/SecretCreateRequest.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/SecretInfo.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/SecretRef.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/SeqFlags.json +4 -2
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Step.json +1 -1
- sequence_base-0.4.27/sequence_base/schemas/Stream.json +193 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/TaskAgg.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/TemplateFiles.json +1 -1
- sequence_base-0.4.27/sequence_base/schemas/Tensors.json +14 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Timeouts.json +15 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/UploadTarget.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/Variant.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/VolumeFile.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/VolumeInfo.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/VolumeMount.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/VolumeRef.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/schemas/WarmingError.json +1 -1
- {sequence_base-0.4.26 → sequence_base-0.4.27}/.gitignore +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/README.md +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/auth.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/codec/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/codec/image.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/constants/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/constants/cards.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/constants/headers.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/construct.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/accounts.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/act.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/adapters.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/benchmarks.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/errors.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/eval.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/io.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/templates.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/contract/volumes.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/errors/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/errors/transport.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/msgpack_io.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/obs/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/obs/log.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/obs/rid.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/presets/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/presets/frames.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/sequence_base/telemetry/__init__.py +0 -0
- {sequence_base-0.4.26 → sequence_base-0.4.27}/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.27
|
|
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.27"
|
|
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
|
|
|
@@ -208,11 +220,29 @@ class DeploymentPolicy(BaseModel):
|
|
|
208
220
|
own_observations: Literal["latest", "since_last_act"] | None = None
|
|
209
221
|
own_actions: Literal["one", "steps"] | None = None
|
|
210
222
|
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
observations: Observations | None = None
|
|
226
|
+
actions: Actions | None = None
|
|
227
|
+
tensors: Tensors | None = None
|
|
228
|
+
|
|
229
|
+
@model_validator(mode="after")
|
|
230
|
+
def _episodes_of_streaming(self) -> "DeploymentPolicy":
|
|
231
|
+
|
|
232
|
+
if self.streaming and self.episodes not in (None, "own"):
|
|
233
|
+
raise ValueError(f"streaming is what a record from before contract 0.5.24 says; beside "
|
|
234
|
+
f"episodes={self.episodes!r}, leave it out")
|
|
235
|
+
if self.streaming:
|
|
236
|
+
self.episodes = "own"
|
|
237
|
+
self.streaming = self.episodes == "own"
|
|
238
|
+
return self
|
|
239
|
+
|
|
211
240
|
@model_validator(mode="after")
|
|
212
241
|
def _own_io(self) -> "DeploymentPolicy":
|
|
213
242
|
if (self.own_observations or self.own_actions) and (self.action or self.observation or self.embodiments):
|
|
214
243
|
raise ValueError("own_observations and own_actions are for a policy of its own inputs and outputs; this one "
|
|
215
244
|
"declares the robot-arm contract, where observation history and the action chunk say it")
|
|
245
|
+
check_own_io(self)
|
|
216
246
|
return self
|
|
217
247
|
|
|
218
248
|
@model_validator(mode="after")
|
|
@@ -0,0 +1,65 @@
|
|
|
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 that declares own_observations "since_last_act".
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
25
|
+
|
|
26
|
+
__all__ = ["FORMAT", "FRAMES_MISSING", "FRAMES_OVER_LIMIT", "ObserveFrame", "ObserveRequest", "ObserveResponse"]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
FORMAT = "x-seq-observe/1"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
FRAMES_MISSING = "frames_missing"
|
|
36
|
+
FRAMES_OVER_LIMIT = "frames_over_limit"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ObserveFrame(BaseModel):
|
|
40
|
+
"""One observation of an episode: its number in it, from 0, and the inputs, as the policy's infer() takes one."""
|
|
41
|
+
|
|
42
|
+
model_config = ConfigDict(extra="forbid")
|
|
43
|
+
|
|
44
|
+
seq: int = Field(..., ge=0, description="The observation's number in its episode, from 0")
|
|
45
|
+
inputs: Any = Field(..., description="One observation, exactly as the robot's code built it")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ObserveRequest(BaseModel):
|
|
49
|
+
"""POST /observe: the observations of one episode that came since the last post, and the numbers the robot left out
|
|
50
|
+
on purpose, which the worker then does not wait for."""
|
|
51
|
+
|
|
52
|
+
model_config = ConfigDict(extra="forbid")
|
|
53
|
+
|
|
54
|
+
episode_id: str = Field(..., min_length=1, max_length=256)
|
|
55
|
+
frames: list[ObserveFrame] = Field(default_factory=list)
|
|
56
|
+
skipped: list[int] = Field(default_factory=list, description="Numbers left out on purpose, never to come")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ObserveResponse(BaseModel):
|
|
60
|
+
"""200 from POST /observe: the highest number up to which the worker holds every observation of the episode, or has
|
|
61
|
+
been told one is skipped (-1: none yet). A number sent again is answered the same, never kept twice."""
|
|
62
|
+
|
|
63
|
+
model_config = ConfigDict(extra="forbid")
|
|
64
|
+
|
|
65
|
+
through: int = Field(..., ge=-1)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
What a policy of its own inputs and outputs declares (contract 0.5.24): which keys of an observation it reads and at
|
|
3
|
+
which steps, a camera's frames as it was trained to see them, which rows of its answer play and how fast, and whether
|
|
4
|
+
its inputs reach it as torch tensors. Each part is optional; a policy that declares none is sent and handed what it is
|
|
5
|
+
today.
|
|
6
|
+
|
|
7
|
+
In one sentence: `Observations` names the keys a policy reads, each a `Stream` - read at the act's step and the offsets
|
|
8
|
+
in `at` before it (a window the worker keeps), or handed every `every` steps since the last act (fed: the policy keeps
|
|
9
|
+
its own history) - counted in steps or seconds (`unit`) → the robot and the eval send, and a benchmark renders, only
|
|
10
|
+
those steps → infer is handed {"observations": [...], "steps": [...]}, oldest first, the act's own last; `Actions` says
|
|
11
|
+
which field of the answer holds the rows, how many a second they play (`hz`) and how many play before the next act
|
|
12
|
+
(`open_loop_horizon`), so a worker can time a copy's robots by them.
|
|
13
|
+
|
|
14
|
+
WHY by key and not by kind of input: every policy, benchmark and serving framework read for the user's 69 names its
|
|
15
|
+
inputs and gives each its own schedule - GR00T DROID reads its cameras at t and t-15 and its state at t alone - and what
|
|
16
|
+
breaks a policy is a frame fitted or encoded other than it was trained on, which a kind of input does not say and a
|
|
17
|
+
`Camera` does.
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import math
|
|
23
|
+
from typing import Any, Literal
|
|
24
|
+
|
|
25
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
|
26
|
+
|
|
27
|
+
from sequence_base.presets.robot_arm import Camera
|
|
28
|
+
|
|
29
|
+
__all__ = ["Actions", "MAX_STREAMS", "MAX_WINDOW", "Observations", "Stream", "Tensors", "check_own_io"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
MAX_STREAMS = 64
|
|
34
|
+
MAX_WINDOW = 256
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _counts(v: Any, what: str) -> None:
|
|
38
|
+
|
|
39
|
+
if isinstance(v, bool) or not isinstance(v, (int, float)) or not math.isfinite(v):
|
|
40
|
+
raise ValueError(f"{what} is a number of steps or seconds, got {v!r}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Stream(BaseModel):
|
|
44
|
+
"""One key of the observation a policy reads. `at`: the offsets before the act's step it reads at each act, 0 the
|
|
45
|
+
act's own - a window the worker keeps, so the policy keeps none. `every`: handed every that many steps since its last
|
|
46
|
+
act, the act's own always among them - fed, the policy keeping its own history. Neither: the act's step alone.
|
|
47
|
+
`camera`: the key is a camera's frame, read at its width x height - fitted by `resize` and `filter` before it is sent
|
|
48
|
+
when both are declared, sent as `codec` says (unset: as the robot sent it), decoded in the replica to HWC uint8, or
|
|
49
|
+
CHW float in 0..1 for layout "chw_float", before the policy's code."""
|
|
50
|
+
|
|
51
|
+
model_config = ConfigDict(extra="forbid")
|
|
52
|
+
|
|
53
|
+
at: list[int | float] | None = Field(default=None, min_length=1, max_length=MAX_WINDOW,
|
|
54
|
+
description="offsets before the act's step, each 0 or below")
|
|
55
|
+
every: int | float | None = Field(default=None, gt=0, description="handed every this many steps since the last act")
|
|
56
|
+
camera: Camera | None = None
|
|
57
|
+
|
|
58
|
+
@field_validator("at", mode="before")
|
|
59
|
+
@classmethod
|
|
60
|
+
def _offsets_as_given(cls, v: Any) -> Any:
|
|
61
|
+
for x in v if isinstance(v, list) else ():
|
|
62
|
+
_counts(x, "an offset in at=")
|
|
63
|
+
return v
|
|
64
|
+
|
|
65
|
+
@field_validator("at")
|
|
66
|
+
@classmethod
|
|
67
|
+
def _window(cls, v: list[int | float] | None) -> list[int | float] | None:
|
|
68
|
+
if v is None:
|
|
69
|
+
return v
|
|
70
|
+
if any(x > 0 for x in v):
|
|
71
|
+
raise ValueError("at= holds offsets before the act's step: each 0 (the act's own) or below")
|
|
72
|
+
if len(set(v)) != len(v):
|
|
73
|
+
raise ValueError("at= names each offset once")
|
|
74
|
+
return sorted(v)
|
|
75
|
+
|
|
76
|
+
@field_validator("every", mode="before")
|
|
77
|
+
@classmethod
|
|
78
|
+
def _fed(cls, v: Any) -> Any:
|
|
79
|
+
if v is not None:
|
|
80
|
+
_counts(v, "every=")
|
|
81
|
+
return v
|
|
82
|
+
|
|
83
|
+
@model_validator(mode="after")
|
|
84
|
+
def _one_schedule(self) -> "Stream":
|
|
85
|
+
if self.at is not None and self.every is not None:
|
|
86
|
+
raise ValueError("a key is read at= offsets of each act (the worker keeps them) or every= so many steps "
|
|
87
|
+
"(the policy keeps them), not both")
|
|
88
|
+
return self
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Observations(BaseModel):
|
|
92
|
+
"""What a policy of its own inputs reads of each observation, and when. `streams`: the keys it reads, each a Stream;
|
|
93
|
+
only these are sent. None: the whole observation, as the robot built it. `every`: the whole observation handed every
|
|
94
|
+
that many steps since the last act (fed, no keys named). `unit`: what `at` and `every` count - "steps", an episode's
|
|
95
|
+
control steps from 0 (one action played on a robot, one env step in an eval), or "seconds", taken to steps by the
|
|
96
|
+
robot's or the benchmark's control rate. `pad`: a window's step before the episode's first reads as its first
|
|
97
|
+
("repeat_first") or is left out ("drop"). `when_behind`: what the robot does when its upload falls behind - "wait",
|
|
98
|
+
losing none, or "drop_oldest". Declared, infer is handed {"observations": [...], "steps": [...]}: each observation
|
|
99
|
+
the keys read at its step, oldest first, the act's own last."""
|
|
100
|
+
|
|
101
|
+
model_config = ConfigDict(extra="forbid")
|
|
102
|
+
|
|
103
|
+
streams: dict[str, Stream] | None = Field(default=None, max_length=MAX_STREAMS)
|
|
104
|
+
every: int | float | None = Field(default=None, gt=0)
|
|
105
|
+
unit: Literal["steps", "seconds"] = "steps"
|
|
106
|
+
pad: Literal["repeat_first", "drop"] = "repeat_first"
|
|
107
|
+
when_behind: Literal["wait", "drop_oldest"] = "wait"
|
|
108
|
+
|
|
109
|
+
@field_validator("streams")
|
|
110
|
+
@classmethod
|
|
111
|
+
def _keys(cls, v: dict[str, Stream] | None) -> dict[str, Stream] | None:
|
|
112
|
+
for key in v or {}:
|
|
113
|
+
if not key or len(key) > 128 or key != key.strip():
|
|
114
|
+
raise ValueError(f"stream {key!r} is not a key of an observation: 1 to 128 characters, no blanks around")
|
|
115
|
+
return v
|
|
116
|
+
|
|
117
|
+
@field_validator("every", mode="before")
|
|
118
|
+
@classmethod
|
|
119
|
+
def _fed(cls, v: Any) -> Any:
|
|
120
|
+
if v is not None:
|
|
121
|
+
_counts(v, "every=")
|
|
122
|
+
return v
|
|
123
|
+
|
|
124
|
+
@model_validator(mode="after")
|
|
125
|
+
def _counted(self) -> "Observations":
|
|
126
|
+
if self.streams is not None and self.every is not None:
|
|
127
|
+
raise ValueError("Observations(every=...) hands the whole observation; with keys named, give each key its "
|
|
128
|
+
"own Stream(every=...)")
|
|
129
|
+
if self.unit == "steps":
|
|
130
|
+
named = [("every=", self.every)]
|
|
131
|
+
for key, stream in (self.streams or {}).items():
|
|
132
|
+
named += [(f"{key!r} every=", stream.every)] + [(f"{key!r} at=", x) for x in stream.at or []]
|
|
133
|
+
for what, x in named:
|
|
134
|
+
if x is not None and not isinstance(x, int):
|
|
135
|
+
raise ValueError(f"unit 'steps' counts whole steps and {what}{x!r} is not one; for a time, write "
|
|
136
|
+
"unit='seconds'")
|
|
137
|
+
return self
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class Actions(BaseModel):
|
|
141
|
+
"""Which rows of a policy's answer play. `key`: the answer's field that holds them (None: the answer itself) - a
|
|
142
|
+
list of rows or a 2-D array, played in order, `hz` rows a second, `open_loop_horizon` of them before the next act
|
|
143
|
+
(None: all). A shorter answer plays whole and the next act comes sooner; a longer one is cut there. With `hz` a worker
|
|
144
|
+
knows how long one answer plays (open_loop_horizon / hz) and a control period (1 / hz) and times a copy's robots by
|
|
145
|
+
them, as a robot-arm chunk's covers_seconds and control_frequency_hz do. Undeclared, an answer plays as the default
|
|
146
|
+
says: a dict or a 1-D list one action, a 2-D one row by row."""
|
|
147
|
+
|
|
148
|
+
model_config = ConfigDict(extra="forbid")
|
|
149
|
+
|
|
150
|
+
key: str | None = Field(default=None, min_length=1, max_length=128)
|
|
151
|
+
hz: float | None = Field(default=None, gt=0, le=1000)
|
|
152
|
+
open_loop_horizon: int | None = Field(default=None, ge=1, le=100_000)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class Tensors(BaseModel):
|
|
156
|
+
"""A policy's inputs reach its code as torch tensors on its device - a batch dimension first when `batch` - and the
|
|
157
|
+
tensors it answers with go back as arrays; float64 arrays arrive as float32. Converted in the replica, last before
|
|
158
|
+
the policy's code; how a camera's frame is laid out is its Stream's camera.layout."""
|
|
159
|
+
|
|
160
|
+
model_config = ConfigDict(extra="forbid")
|
|
161
|
+
|
|
162
|
+
batch: bool = True
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def check_own_io(spec: Any) -> None:
|
|
166
|
+
"""What PolicySpec and DeploymentPolicy both hold a policy's own-I/O declarations to: none beside the robot-arm
|
|
167
|
+
contract, which says what it reads and plays its own way, and the 0.5.24 ones (observations / actions / tensors)
|
|
168
|
+
not beside own_observations / own_actions, which they take over from."""
|
|
169
|
+
new = [name for name in ("observations", "actions", "tensors") if getattr(spec, name) is not None]
|
|
170
|
+
if not new:
|
|
171
|
+
return
|
|
172
|
+
if spec.action or spec.observation or spec.embodiments:
|
|
173
|
+
raise ValueError(f"{', '.join(new)} {'is' if len(new) == 1 else 'are'} for a policy of its own inputs and "
|
|
174
|
+
"outputs; this one declares the robot-arm contract, where observation history and the action "
|
|
175
|
+
"chunk say it")
|
|
176
|
+
old = [name for name in ("own_observations", "own_actions") if getattr(spec, name)]
|
|
177
|
+
if old:
|
|
178
|
+
raise ValueError(f"{', '.join(new)} and {', '.join(old)} say what a policy reads and plays two ways; declare "
|
|
179
|
+
"observations= and actions= alone")
|
|
@@ -24,8 +24,10 @@ from sequence_base.contract.io import (
|
|
|
24
24
|
ObservationContract,
|
|
25
25
|
)
|
|
26
26
|
from sequence_base.contract.flags import SeqFlags
|
|
27
|
+
from sequence_base.contract.own_io import Actions, Observations, Tensors, check_own_io
|
|
27
28
|
|
|
28
29
|
__all__ = [
|
|
30
|
+
"Episodes",
|
|
29
31
|
"ImageStepUvPip",
|
|
30
32
|
"ImageStepApt",
|
|
31
33
|
"ImageStepEnv",
|
|
@@ -337,6 +339,13 @@ class Concurrency(BaseModel):
|
|
|
337
339
|
KIND_NAME = r"^[a-z0-9][a-z0-9._-]{0,31}$"
|
|
338
340
|
|
|
339
341
|
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
Episodes = Literal["shared", "own"]
|
|
347
|
+
|
|
348
|
+
|
|
340
349
|
class PolicySpec(BaseModel):
|
|
341
350
|
"""What `@seq.policy(...)` accrues — a policy's complete infra declaration, losslessly. `action` /
|
|
342
351
|
`observation` / `embodiments` are optional (contract 0.4.14): a policy that declares none defines its I/O in its own
|
|
@@ -360,7 +369,13 @@ class PolicySpec(BaseModel):
|
|
|
360
369
|
on_full: Literal["queue", "reject"] = "queue"
|
|
361
370
|
queue_timeout_s: int = Field(default=120, ge=5, le=3600)
|
|
362
371
|
concurrency: Concurrency | None = None
|
|
363
|
-
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
episodes: Episodes | None = None
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
streaming: bool = Field(default=False, exclude=True)
|
|
364
379
|
|
|
365
380
|
action: ActionContract | None = None
|
|
366
381
|
|
|
@@ -384,6 +399,13 @@ class PolicySpec(BaseModel):
|
|
|
384
399
|
own_observations: Literal["latest", "since_last_act"] | None = None
|
|
385
400
|
own_actions: Literal["one", "steps"] | None = None
|
|
386
401
|
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
observations: Observations | None = None
|
|
406
|
+
actions: Actions | None = None
|
|
407
|
+
tensors: Tensors | None = None
|
|
408
|
+
|
|
387
409
|
@field_validator("weights")
|
|
388
410
|
@classmethod
|
|
389
411
|
def _weights_scheme(cls, v: str | None) -> str | None:
|
|
@@ -398,11 +420,23 @@ class PolicySpec(BaseModel):
|
|
|
398
420
|
check_weights_uri(uri, f"weights[{name!r}]")
|
|
399
421
|
return self
|
|
400
422
|
|
|
423
|
+
@model_validator(mode="after")
|
|
424
|
+
def _episodes_of_streaming(self) -> "PolicySpec":
|
|
425
|
+
|
|
426
|
+
if self.streaming and self.episodes not in (None, "own"):
|
|
427
|
+
raise ValueError(f"streaming is what a record from before contract 0.5.24 says; beside "
|
|
428
|
+
f"episodes={self.episodes!r}, leave it out")
|
|
429
|
+
if self.streaming:
|
|
430
|
+
self.episodes = "own"
|
|
431
|
+
self.streaming = self.episodes == "own"
|
|
432
|
+
return self
|
|
433
|
+
|
|
401
434
|
@model_validator(mode="after")
|
|
402
435
|
def _own_io(self) -> "PolicySpec":
|
|
403
436
|
if (self.own_observations or self.own_actions) and (self.action or self.observation or self.embodiments):
|
|
404
437
|
raise ValueError("own_observations and own_actions are for a policy of its own inputs and outputs; this one "
|
|
405
438
|
"declares the robot-arm contract, where observation history and the action chunk say it")
|
|
439
|
+
check_own_io(self)
|
|
406
440
|
return self
|
|
407
441
|
|
|
408
442
|
|
|
@@ -477,7 +511,11 @@ class PolicyRegistration(BaseModel):
|
|
|
477
511
|
|
|
478
512
|
A class that names its own methods (`constructor` set) is built by its constructor or factory, so it binds no LOAD,
|
|
479
513
|
and its bindings name its own methods: INFER (what answers an act), and as declared OBSERVE (each observation an
|
|
480
|
-
act carries, before INFER is called with none), RESET (an episode's first act) and ON_EPISODE (its end).
|
|
514
|
+
act carries, before INFER is called with none), RESET (an episode's first act) and ON_EPISODE (its end).
|
|
515
|
+
|
|
516
|
+
Any policy may bind SAMPLE (contract 0.5.24, `@seq.policy(sample="<method>")`): the method returning one episode's
|
|
517
|
+
act inputs, in order, each as an act carries it - what its build check acts with and its worker times its infer on
|
|
518
|
+
when no contract says what it reads."""
|
|
481
519
|
|
|
482
520
|
model_config = ConfigDict(extra="forbid")
|
|
483
521
|
|
|
@@ -41,6 +41,11 @@ class ActRequest(BaseModel):
|
|
|
41
41
|
description="The robot's session, the steps it holds, its rate and network p95")
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
|
|
45
|
+
seq: int | None = Field(default=None, ge=0,
|
|
46
|
+
description="The newest observation's number in its episode's stream (POST /observe)")
|
|
47
|
+
|
|
48
|
+
|
|
44
49
|
class ActResponse(BaseModel):
|
|
45
50
|
"""200 from /act, version 2. `outputs` is infer()'s return value, unchanged; the rest is platform metadata."""
|
|
46
51
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
What a policy that keeps its episodes' state on the platform is handed (PolicySpec.episodes "shared"): its infer
|
|
3
|
+
declares `episode` and gets that episode's Episode - its id, how many of its acts were answered, and `state`, a dict of
|
|
4
|
+
the policy's own.
|
|
5
|
+
|
|
6
|
+
In one sentence: an episode's first act → the copy it is placed on makes its Episode (its id, steps 0, state empty) →
|
|
7
|
+
every act of it hands infer that same object, one more step after each answer → at the episode's end the copy drops it.
|
|
8
|
+
|
|
9
|
+
WHY a class of the platform's rather than a dict: the id and the step count come with it, and what the policy keeps has
|
|
10
|
+
a place of its own - `state` - that no field the platform adds later can collide with.
|
|
11
|
+
|
|
12
|
+
WHY nothing in it is serialized: it lives in the process of the copy serving its episode and is handed by reference,
|
|
13
|
+
so anything goes in `state` - tensors, a model's cache - and an act costs no more for it.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
__all__ = ["Episode", "answered"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Episode:
|
|
23
|
+
"""One episode, as its policy's infer sees it: `id` - the episode's id; `steps` - how many acts of it were answered
|
|
24
|
+
before this one (0 on its first; an act, however many control steps its answer is played as); `state` - a dict for
|
|
25
|
+
the policy's own, empty on the first act and the same object on every act after. `id` and `steps` are the
|
|
26
|
+
platform's: they are read, never set."""
|
|
27
|
+
|
|
28
|
+
__slots__ = ("_id", "_steps", "state")
|
|
29
|
+
|
|
30
|
+
def __init__(self, id: str) -> None:
|
|
31
|
+
object.__setattr__(self, "_id", id)
|
|
32
|
+
object.__setattr__(self, "_steps", 0)
|
|
33
|
+
object.__setattr__(self, "state", {})
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def id(self) -> str:
|
|
37
|
+
return self._id
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def steps(self) -> int:
|
|
41
|
+
return self._steps
|
|
42
|
+
|
|
43
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
44
|
+
if name == "state":
|
|
45
|
+
object.__setattr__(self, name, value)
|
|
46
|
+
elif name in ("id", "steps"):
|
|
47
|
+
raise AttributeError(f"Episode.{name} is the platform's: read it, never set it")
|
|
48
|
+
else:
|
|
49
|
+
raise AttributeError(f"an Episode keeps what a policy puts in it in its state, a dict: "
|
|
50
|
+
f"episode.state[{name!r}] = ...")
|
|
51
|
+
|
|
52
|
+
def __repr__(self) -> str:
|
|
53
|
+
return f"Episode(id={self._id!r}, steps={self._steps})"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def answered(episode: Episode) -> None:
|
|
57
|
+
"""One more act of the episode answered - the platform's, once its infer returned."""
|
|
58
|
+
object.__setattr__(episode, "_steps", episode._steps + 1)
|