sequence-base 0.4.25__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.
Files changed (120) hide show
  1. {sequence_base-0.4.25 → sequence_base-0.4.27}/PKG-INFO +1 -1
  2. {sequence_base-0.4.25 → sequence_base-0.4.27}/pyproject.toml +1 -1
  3. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/__init__.py +17 -1
  4. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/constants/limits.py +9 -0
  5. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/constants/version.py +3 -1
  6. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/__init__.py +10 -1
  7. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/connect.py +5 -0
  8. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/deployments.py +31 -1
  9. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/flags.py +4 -0
  10. sequence_base-0.4.27/sequence_base/contract/observe.py +65 -0
  11. sequence_base-0.4.27/sequence_base/contract/own_io.py +179 -0
  12. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/policy.py +137 -3
  13. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/wire.py +5 -0
  14. sequence_base-0.4.27/sequence_base/episode.py +58 -0
  15. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/presets/robot_arm.py +30 -0
  16. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schema_export.py +13 -0
  17. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ActRequest.json +15 -1
  18. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ActResponse.json +1 -1
  19. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ActionChunk.json +1 -1
  20. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ActionContract.json +1 -1
  21. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ActionStep.json +1 -1
  22. sequence_base-0.4.27/sequence_base/schemas/Actions.json +51 -0
  23. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/AdapterPair.json +1 -1
  24. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/AddLocal.json +1 -1
  25. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Autoscaler.json +1 -1
  26. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/BenchmarkConditionSpec.json +1 -1
  27. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/BenchmarkRegisterRequest.json +92 -1
  28. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/BenchmarkSpec.json +98 -4
  29. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
  30. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/BootstrapResponse.json +1 -1
  31. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Camera.json +51 -1
  32. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/CodeBundle.json +1 -1
  33. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Concurrency.json +1 -1
  34. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Conditions.json +1 -1
  35. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ConnectRequest.json +1 -1
  36. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ConnectResponse.json +259 -1
  37. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/DeploymentAccepted.json +1 -1
  38. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/DeploymentPolicy.json +299 -1
  39. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/DeploymentRequest.json +404 -1
  40. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
  41. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Embodiment.json +51 -1
  42. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ErrorBody.json +1 -1
  43. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ErrorCodes.json +1 -1
  44. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Estimate.json +1 -1
  45. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
  46. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/EvalReport.json +1 -1
  47. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/EvalRequest.json +1 -1
  48. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/EvalResponse.json +1 -1
  49. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ImageFrame.json +1 -1
  50. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ImageSpec.json +92 -1
  51. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepAddLocal.json +1 -1
  52. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepApt.json +1 -1
  53. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepEnv.json +1 -1
  54. sequence_base-0.4.27/sequence_base/schemas/ImageStepProject.json +91 -0
  55. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepRun.json +1 -1
  56. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepTemplateFiles.json +1 -1
  57. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ImageStepUvPip.json +1 -1
  58. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/InferParam.json +1 -1
  59. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Lease.json +1 -1
  60. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/MetricAgg.json +1 -1
  61. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Observation.json +1 -1
  62. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ObservationContract.json +51 -1
  63. sequence_base-0.4.27/sequence_base/schemas/Observations.json +264 -0
  64. sequence_base-0.4.27/sequence_base/schemas/ObserveRequest.json +57 -0
  65. sequence_base-0.4.27/sequence_base/schemas/ObserveResponse.json +17 -0
  66. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/PolicyRegistration.json +397 -5
  67. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/PolicySpec.json +390 -1
  68. sequence_base-0.4.27/sequence_base/schemas/Project.json +76 -0
  69. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Proprioception.json +1 -1
  70. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/RenderSpec.json +1 -1
  71. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/ReplaySpec.json +1 -1
  72. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Resources.json +1 -1
  73. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/RolloutResult.json +1 -1
  74. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/SecretCreateRequest.json +1 -1
  75. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/SecretInfo.json +1 -1
  76. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/SecretRef.json +1 -1
  77. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/SeqFlags.json +4 -2
  78. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Step.json +1 -1
  79. sequence_base-0.4.27/sequence_base/schemas/Stream.json +193 -0
  80. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/TaskAgg.json +1 -1
  81. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/TemplateFiles.json +1 -1
  82. sequence_base-0.4.27/sequence_base/schemas/Tensors.json +14 -0
  83. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Timeouts.json +15 -1
  84. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/UploadTarget.json +1 -1
  85. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/Variant.json +1 -1
  86. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/VolumeFile.json +1 -1
  87. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/VolumeInfo.json +1 -1
  88. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/VolumeMount.json +1 -1
  89. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/VolumeRef.json +1 -1
  90. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
  91. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
  92. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/schemas/WarmingError.json +1 -1
  93. {sequence_base-0.4.25 → sequence_base-0.4.27}/.gitignore +0 -0
  94. {sequence_base-0.4.25 → sequence_base-0.4.27}/README.md +0 -0
  95. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/auth.py +0 -0
  96. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/codec/__init__.py +0 -0
  97. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/codec/image.py +0 -0
  98. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/constants/__init__.py +0 -0
  99. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/constants/cards.py +0 -0
  100. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/constants/headers.py +0 -0
  101. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/construct.py +0 -0
  102. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/accounts.py +0 -0
  103. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/act.py +0 -0
  104. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/adapters.py +0 -0
  105. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/benchmarks.py +0 -0
  106. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/errors.py +0 -0
  107. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/eval.py +0 -0
  108. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/io.py +0 -0
  109. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/templates.py +0 -0
  110. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/contract/volumes.py +0 -0
  111. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/errors/__init__.py +0 -0
  112. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/errors/transport.py +0 -0
  113. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/msgpack_io.py +0 -0
  114. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/obs/__init__.py +0 -0
  115. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/obs/log.py +0 -0
  116. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/obs/rid.py +0 -0
  117. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/presets/__init__.py +0 -0
  118. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/presets/frames.py +0 -0
  119. {sequence_base-0.4.25 → sequence_base-0.4.27}/sequence_base/telemetry/__init__.py +0 -0
  120. {sequence_base-0.4.25 → 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.25
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.25"
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"]
@@ -21,9 +21,14 @@ from sequence_base.contract import (
21
21
  FROZEN_PATH,
22
22
  TemplateFiles,
23
23
  ImageStepTemplateFiles,
24
+ Project,
25
+ ImageStepProject,
24
26
  Camera,
25
27
  Embodiment,
26
28
  ObservationContract,
29
+ ObserveFrame,
30
+ ObserveRequest,
31
+ ObserveResponse,
27
32
  MAX_CODE_BUNDLE_BYTES,
28
33
  UploadTarget,
29
34
  VolumeFile,
@@ -79,6 +84,10 @@ from sequence_base.contract import (
79
84
  PolicyConstructor,
80
85
  PolicyRegistration,
81
86
  PolicySpec,
87
+ Observations,
88
+ Stream,
89
+ Actions,
90
+ Tensors,
82
91
  Proprioception,
83
92
  RenderSpec,
84
93
  ReplaySpec,
@@ -118,6 +127,9 @@ from sequence_base.errors.transport import (
118
127
  )
119
128
 
120
129
 
130
+ from sequence_base.episode import Episode
131
+
132
+
121
133
  from sequence_base import auth
122
134
 
123
135
 
@@ -129,11 +141,13 @@ __all__ = [
129
141
 
130
142
  "ImageFrame", "Proprioception", "Observation", "ActionSpace", "ActionStep", "ActionChunk",
131
143
  "ActRequest", "ActResponse",
144
+ "ObserveFrame", "ObserveRequest", "ObserveResponse",
132
145
 
133
146
  "SeqFlags", "ImageStepUvPip", "ImageStepApt", "ImageStepEnv", "ImageStepRun", "AddLocal", "ImageStepAddLocal",
134
- "FROZEN_PATH", "TemplateFiles", "ImageStepTemplateFiles", "ImageStep", "ImageSpec",
147
+ "FROZEN_PATH", "TemplateFiles", "ImageStepTemplateFiles", "Project", "ImageStepProject", "ImageStep", "ImageSpec",
135
148
  "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract", "Embodiment",
136
149
  "PolicySpec", "PolicyRegistration", "PolicyConstructor",
150
+ "Observations", "Stream", "Actions", "Tensors",
137
151
 
138
152
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
139
153
  "Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
@@ -160,6 +174,8 @@ __all__ = [
160
174
 
161
175
  "TransportUnavailable", "TransportWarming", "TransportRejected", "TransportOverloaded",
162
176
 
177
+ "Episode",
178
+
163
179
  "auth",
164
180
  ]
165
181
 
@@ -28,3 +28,12 @@ TASK_DESCRIPTION_MAX = 300
28
28
  METRIC_UNITS_MAX = 64
29
29
  METRIC_UNIT_MAX = 16
30
30
 
31
+
32
+
33
+
34
+
35
+
36
+ MAX_STREAM_FRAMES = 1024
37
+ MAX_STREAM_BYTES = 64 * 1024 * 1024
38
+ MAX_WORKER_STREAM_BYTES = 1024 * 1024 * 1024
39
+ STREAM_WAIT_S = 10.0
@@ -4,7 +4,9 @@
4
4
 
5
5
 
6
6
 
7
- CONTRACT_VERSION = "0.5.22"
7
+ CONTRACT_VERSION = "0.5.24"
8
+
9
+
8
10
 
9
11
 
10
12
 
@@ -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 (
@@ -34,6 +36,8 @@ from sequence_base.contract.policy import (
34
36
  FROZEN_PATH,
35
37
  TemplateFiles,
36
38
  ImageStepTemplateFiles,
39
+ ImageStepProject,
40
+ Project,
37
41
  PolicyConstructor,
38
42
  PolicyRegistration,
39
43
  PolicySpec,
@@ -42,6 +46,9 @@ from sequence_base.contract.policy import (
42
46
  )
43
47
 
44
48
 
49
+ from sequence_base.contract.own_io import Actions, Observations, Stream, Tensors
50
+
51
+
45
52
  from sequence_base.contract.deployments import (
46
53
  MAX_CODE_BUNDLE_BYTES,
47
54
  Autoscaler,
@@ -117,12 +124,14 @@ __all__ = [
117
124
 
118
125
  "ImageFrame", "Proprioception", "Observation", "ActionSpace", "ActionStep", "ActionChunk",
119
126
  "ActRequest", "ActResponse",
127
+ "ObserveFrame", "ObserveRequest", "ObserveResponse",
120
128
 
121
129
  "SeqFlags",
122
130
  "ImageStepUvPip", "ImageStepApt", "ImageStepEnv", "ImageStepRun", "AddLocal", "ImageStepAddLocal",
123
- "TemplateFiles", "ImageStepTemplateFiles", "FROZEN_PATH", "ImageStep", "ImageSpec",
131
+ "TemplateFiles", "ImageStepTemplateFiles", "Project", "ImageStepProject", "FROZEN_PATH", "ImageStep", "ImageSpec",
124
132
  "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract", "Embodiment",
125
133
  "PolicySpec", "PolicyRegistration", "PolicyConstructor",
134
+ "Observations", "Stream", "Actions", "Tensors",
126
135
 
127
136
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
128
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
- streaming: bool = False
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")
@@ -48,6 +48,10 @@ class SeqFlags(IntFlag):
48
48
 
49
49
 
50
50
 
51
+ SAMPLE = 524288
52
+
53
+
54
+
51
55
 
52
56
 
53
57
 
@@ -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")