sequence-base 0.4.27__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.
Files changed (121) hide show
  1. {sequence_base-0.4.27 → sequence_base-0.4.28}/PKG-INFO +1 -1
  2. {sequence_base-0.4.27 → sequence_base-0.4.28}/pyproject.toml +1 -1
  3. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/constants/version.py +2 -1
  4. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/deployments.py +0 -9
  5. sequence_base-0.4.28/sequence_base/contract/obs_schedule.py +170 -0
  6. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/observe.py +2 -1
  7. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/own_io.py +66 -7
  8. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/policy.py +0 -9
  9. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ActRequest.json +1 -1
  10. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ActResponse.json +1 -1
  11. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ActionChunk.json +1 -1
  12. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ActionContract.json +1 -1
  13. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ActionStep.json +1 -1
  14. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Actions.json +1 -1
  15. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/AdapterPair.json +1 -1
  16. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/AddLocal.json +1 -1
  17. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Autoscaler.json +1 -1
  18. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/BenchmarkConditionSpec.json +1 -1
  19. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/BenchmarkRegisterRequest.json +1 -1
  20. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/BenchmarkSpec.json +2 -2
  21. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
  22. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/BootstrapResponse.json +1 -1
  23. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Camera.json +1 -1
  24. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/CodeBundle.json +1 -1
  25. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Concurrency.json +1 -1
  26. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Conditions.json +1 -1
  27. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ConnectRequest.json +1 -1
  28. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ConnectResponse.json +1 -1
  29. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/DeploymentAccepted.json +1 -1
  30. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/DeploymentPolicy.json +1 -33
  31. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/DeploymentRequest.json +1 -33
  32. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
  33. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Embodiment.json +1 -1
  34. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ErrorBody.json +1 -1
  35. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ErrorCodes.json +1 -1
  36. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Estimate.json +1 -1
  37. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
  38. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/EvalReport.json +1 -1
  39. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/EvalRequest.json +1 -1
  40. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/EvalResponse.json +1 -1
  41. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ImageFrame.json +1 -1
  42. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ImageSpec.json +1 -1
  43. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepAddLocal.json +1 -1
  44. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepApt.json +1 -1
  45. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepEnv.json +1 -1
  46. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepProject.json +1 -1
  47. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepRun.json +1 -1
  48. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepTemplateFiles.json +1 -1
  49. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ImageStepUvPip.json +1 -1
  50. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/InferParam.json +1 -1
  51. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Lease.json +1 -1
  52. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/MetricAgg.json +1 -1
  53. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Observation.json +1 -1
  54. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ObservationContract.json +1 -1
  55. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Observations.json +1 -1
  56. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ObserveRequest.json +1 -1
  57. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ObserveResponse.json +1 -1
  58. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/PolicyRegistration.json +2 -34
  59. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/PolicySpec.json +1 -33
  60. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Project.json +1 -1
  61. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Proprioception.json +1 -1
  62. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/RenderSpec.json +1 -1
  63. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/ReplaySpec.json +1 -1
  64. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Resources.json +1 -1
  65. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/RolloutResult.json +1 -1
  66. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/SecretCreateRequest.json +1 -1
  67. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/SecretInfo.json +1 -1
  68. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/SecretRef.json +1 -1
  69. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/SeqFlags.json +1 -1
  70. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Step.json +1 -1
  71. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Stream.json +1 -1
  72. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/TaskAgg.json +1 -1
  73. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/TemplateFiles.json +1 -1
  74. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Tensors.json +1 -1
  75. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Timeouts.json +1 -1
  76. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/UploadTarget.json +1 -1
  77. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/Variant.json +1 -1
  78. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/VolumeFile.json +1 -1
  79. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/VolumeInfo.json +1 -1
  80. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/VolumeMount.json +1 -1
  81. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/VolumeRef.json +1 -1
  82. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
  83. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
  84. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schemas/WarmingError.json +1 -1
  85. {sequence_base-0.4.27 → sequence_base-0.4.28}/.gitignore +0 -0
  86. {sequence_base-0.4.27 → sequence_base-0.4.28}/README.md +0 -0
  87. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/__init__.py +0 -0
  88. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/auth.py +0 -0
  89. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/codec/__init__.py +0 -0
  90. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/codec/image.py +0 -0
  91. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/constants/__init__.py +0 -0
  92. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/constants/cards.py +0 -0
  93. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/constants/headers.py +0 -0
  94. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/constants/limits.py +0 -0
  95. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/construct.py +0 -0
  96. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/__init__.py +0 -0
  97. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/accounts.py +0 -0
  98. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/act.py +0 -0
  99. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/adapters.py +0 -0
  100. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/benchmarks.py +0 -0
  101. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/connect.py +0 -0
  102. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/errors.py +0 -0
  103. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/eval.py +0 -0
  104. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/flags.py +0 -0
  105. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/io.py +0 -0
  106. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/templates.py +0 -0
  107. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/volumes.py +0 -0
  108. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/contract/wire.py +0 -0
  109. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/episode.py +0 -0
  110. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/errors/__init__.py +0 -0
  111. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/errors/transport.py +0 -0
  112. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/msgpack_io.py +0 -0
  113. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/obs/__init__.py +0 -0
  114. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/obs/log.py +0 -0
  115. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/obs/rid.py +0 -0
  116. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/presets/__init__.py +0 -0
  117. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/presets/frames.py +0 -0
  118. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/presets/robot_arm.py +0 -0
  119. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/schema_export.py +0 -0
  120. {sequence_base-0.4.27 → sequence_base-0.4.28}/sequence_base/telemetry/__init__.py +0 -0
  121. {sequence_base-0.4.27 → 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.27
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.27"
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"]
@@ -4,7 +4,8 @@
4
4
 
5
5
 
6
6
 
7
- CONTRACT_VERSION = "0.5.24"
7
+ CONTRACT_VERSION = "0.5.25"
8
+
8
9
 
9
10
 
10
11
 
@@ -216,12 +216,6 @@ class DeploymentPolicy(BaseModel):
216
216
 
217
217
 
218
218
 
219
-
220
- own_observations: Literal["latest", "since_last_act"] | None = None
221
- own_actions: Literal["one", "steps"] | None = None
222
-
223
-
224
-
225
219
  observations: Observations | None = None
226
220
  actions: Actions | None = None
227
221
  tensors: Tensors | None = None
@@ -239,9 +233,6 @@ class DeploymentPolicy(BaseModel):
239
233
 
240
234
  @model_validator(mode="after")
241
235
  def _own_io(self) -> "DeploymentPolicy":
242
- if (self.own_observations or self.own_actions) and (self.action or self.observation or self.embodiments):
243
- raise ValueError("own_observations and own_actions are for a policy of its own inputs and outputs; this one "
244
- "declares the robot-arm contract, where observation history and the action chunk say it")
245
236
  check_own_io(self)
246
237
  return self
247
238
 
@@ -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}
@@ -14,7 +14,8 @@ are on the worker before the act is.
14
14
 
15
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
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".
17
+ a policy fed every observation since its last act (Observations(every=1); own_observations "since_last_act" until
18
+ contract 0.5.25).
18
19
 
19
20
  """
20
21
  from __future__ import annotations
@@ -20,13 +20,15 @@ breaks a policy is a frame fitted or encoded other than it was trained on, which
20
20
  from __future__ import annotations
21
21
 
22
22
  import math
23
+ import numbers
24
+ from collections.abc import Mapping
23
25
  from typing import Any, Literal
24
26
 
25
27
  from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
26
28
 
27
29
  from sequence_base.presets.robot_arm import Camera
28
30
 
29
- __all__ = ["Actions", "MAX_STREAMS", "MAX_WINDOW", "Observations", "Stream", "Tensors", "check_own_io"]
31
+ __all__ = ["Actions", "MAX_STREAMS", "MAX_WINDOW", "Observations", "Stream", "Tensors", "check_own_io", "rows_of"]
30
32
 
31
33
 
32
34
 
@@ -103,6 +105,15 @@ class Observations(BaseModel):
103
105
  streams: dict[str, Stream] | None = Field(default=None, max_length=MAX_STREAMS)
104
106
  every: int | float | None = Field(default=None, gt=0)
105
107
  unit: Literal["steps", "seconds"] = "steps"
108
+
109
+ def __init__(self, streams: dict[str, Stream] | None = None, /, **data: Any) -> None:
110
+
111
+
112
+ if streams is not None:
113
+ if "streams" in data:
114
+ raise TypeError("Observations takes the keys it reads once: first, or as streams=")
115
+ data["streams"] = streams
116
+ super().__init__(**data)
106
117
  pad: Literal["repeat_first", "drop"] = "repeat_first"
107
118
  when_behind: Literal["wait", "drop_oldest"] = "wait"
108
119
 
@@ -162,10 +173,62 @@ class Tensors(BaseModel):
162
173
  batch: bool = True
163
174
 
164
175
 
176
+ def rows_of(answer: Any, actions: Any = None) -> list:
177
+ """The rows of a policy's own answer that play, in order - one rule for an eval and a robot. `actions`: its Actions,
178
+ the model or its JSON; declared, each element of the field its `key` names (the answer itself for none) is a row,
179
+ a dict one too, cut at its `open_loop_horizon`. Undeclared, the default: a dict, or a list of numbers, is one action;
180
+ a list of lists of one length, or a 2-D array, plays row by row; a ragged or deeper nesting, or a list of dicts, is
181
+ refused, saying to declare actions= - and a number, a text or any other object is one action, as before.
182
+
183
+ """
184
+ if actions is not None:
185
+ key = actions.get("key") if isinstance(actions, dict) else actions.key
186
+ horizon = actions.get("open_loop_horizon") if isinstance(actions, dict) else actions.open_loop_horizon
187
+ where = "answer" if key is None else f"answer's {key!r} field"
188
+ if key is not None:
189
+ if not isinstance(answer, Mapping) or key not in answer:
190
+ raise ValueError(f"the policy's answer has no {key!r} field, which its actions=seq.Actions(key={key!r}) "
191
+ "names as the rows to play")
192
+ answer = answer[key]
193
+ if isinstance(answer, (Mapping, str, bytes)) or not (isinstance(answer, (list, tuple))
194
+ or (getattr(answer, "ndim", 0) or 0) >= 1):
195
+ raise ValueError(f"the policy's {where} is {type(answer).__name__}, not a list of rows or an array of them")
196
+ rows = list(answer)
197
+ if not rows:
198
+ raise ValueError(f"the policy's {where} holds no rows to play")
199
+ return rows[:horizon] if horizon else rows
200
+ ndim = getattr(answer, "ndim", None)
201
+ if isinstance(ndim, int) and not isinstance(answer, (Mapping, str, bytes)):
202
+ if ndim <= 1:
203
+ return [answer]
204
+ if ndim == 2:
205
+ return list(answer)
206
+ raise ValueError(_unplayable(f"an array of {ndim} dimensions"))
207
+ if isinstance(answer, (list, tuple)):
208
+ if all(isinstance(v, numbers.Number) for v in answer):
209
+ return [answer]
210
+ if all(isinstance(v, (list, tuple)) or getattr(v, "ndim", None) == 1 for v in answer):
211
+ lengths = [len(v) for v in answer]
212
+ if len(set(lengths)) == 1:
213
+ return list(answer)
214
+ raise ValueError(_unplayable(f"a list of lists of lengths {', '.join(map(str, lengths))}"))
215
+ if all(isinstance(v, Mapping) for v in answer):
216
+ raise ValueError(_unplayable(f"a list of {len(answer)} dicts"))
217
+ kinds = dict.fromkeys("numbers" if isinstance(v, numbers.Number) else "lists" if isinstance(v, (list, tuple))
218
+ else type(v).__name__ for v in answer)
219
+ raise ValueError(_unplayable(f"a list of {' and '.join(kinds)}"))
220
+ return [answer]
221
+
222
+
223
+ def _unplayable(what: str) -> str:
224
+ return (f"the policy's answer is {what}, which plays neither as one action (a dict, or a list of numbers) nor row by "
225
+ "row (a list of lists of one length, or a 2-D array): declare actions=seq.Actions() for its rows to play in "
226
+ "order, or turn it into actions yourself (actions_of= on a robot, an adapter in an eval)")
227
+
228
+
165
229
  def check_own_io(spec: Any) -> None:
166
230
  """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."""
231
+ contract, which says what it reads and plays its own way."""
169
232
  new = [name for name in ("observations", "actions", "tensors") if getattr(spec, name) is not None]
170
233
  if not new:
171
234
  return
@@ -173,7 +236,3 @@ def check_own_io(spec: Any) -> None:
173
236
  raise ValueError(f"{', '.join(new)} {'is' if len(new) == 1 else 'are'} for a policy of its own inputs and "
174
237
  "outputs; this one declares the robot-arm contract, where observation history and the action "
175
238
  "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")
@@ -396,12 +396,6 @@ class PolicySpec(BaseModel):
396
396
 
397
397
 
398
398
 
399
- own_observations: Literal["latest", "since_last_act"] | None = None
400
- own_actions: Literal["one", "steps"] | None = None
401
-
402
-
403
-
404
-
405
399
  observations: Observations | None = None
406
400
  actions: Actions | None = None
407
401
  tensors: Tensors | None = None
@@ -433,9 +427,6 @@ class PolicySpec(BaseModel):
433
427
 
434
428
  @model_validator(mode="after")
435
429
  def _own_io(self) -> "PolicySpec":
436
- if (self.own_observations or self.own_actions) and (self.action or self.observation or self.embodiments):
437
- raise ValueError("own_observations and own_actions are for a policy of its own inputs and outputs; this one "
438
- "declares the robot-arm contract, where observation history and the action chunk say it")
439
430
  check_own_io(self)
440
431
  return self
441
432
 
@@ -93,5 +93,5 @@
93
93
  ],
94
94
  "title": "ActRequest",
95
95
  "type": "object",
96
- "x-contract-version": "0.5.24"
96
+ "x-contract-version": "0.5.25"
97
97
  }
@@ -89,5 +89,5 @@
89
89
  ],
90
90
  "title": "ActResponse",
91
91
  "type": "object",
92
- "x-contract-version": "0.5.24"
92
+ "x-contract-version": "0.5.25"
93
93
  }
@@ -99,5 +99,5 @@
99
99
  ],
100
100
  "title": "ActionChunk",
101
101
  "type": "object",
102
- "x-contract-version": "0.5.24"
102
+ "x-contract-version": "0.5.25"
103
103
  }
@@ -84,5 +84,5 @@
84
84
  ],
85
85
  "title": "ActionContract",
86
86
  "type": "object",
87
- "x-contract-version": "0.5.24"
87
+ "x-contract-version": "0.5.25"
88
88
  }
@@ -19,5 +19,5 @@
19
19
  ],
20
20
  "title": "ActionStep",
21
21
  "type": "object",
22
- "x-contract-version": "0.5.24"
22
+ "x-contract-version": "0.5.25"
23
23
  }
@@ -47,5 +47,5 @@
47
47
  },
48
48
  "title": "Actions",
49
49
  "type": "object",
50
- "x-contract-version": "0.5.24"
50
+ "x-contract-version": "0.5.25"
51
51
  }
@@ -17,5 +17,5 @@
17
17
  ],
18
18
  "title": "AdapterPair",
19
19
  "type": "object",
20
- "x-contract-version": "0.5.24"
20
+ "x-contract-version": "0.5.25"
21
21
  }
@@ -23,5 +23,5 @@
23
23
  ],
24
24
  "title": "AddLocal",
25
25
  "type": "object",
26
- "x-contract-version": "0.5.24"
26
+ "x-contract-version": "0.5.25"
27
27
  }
@@ -57,5 +57,5 @@
57
57
  },
58
58
  "title": "Autoscaler",
59
59
  "type": "object",
60
- "x-contract-version": "0.5.24"
60
+ "x-contract-version": "0.5.25"
61
61
  }
@@ -50,5 +50,5 @@
50
50
  ],
51
51
  "title": "BenchmarkConditionSpec",
52
52
  "type": "object",
53
- "x-contract-version": "0.5.24"
53
+ "x-contract-version": "0.5.25"
54
54
  }
@@ -1077,5 +1077,5 @@
1077
1077
  ],
1078
1078
  "title": "BenchmarkRegisterRequest",
1079
1079
  "type": "object",
1080
- "x-contract-version": "0.5.24"
1080
+ "x-contract-version": "0.5.25"
1081
1081
  }
@@ -471,7 +471,7 @@
471
471
  ],
472
472
  "title": "SeqFlags",
473
473
  "type": "integer",
474
- "x-contract-version": "0.5.24",
474
+ "x-contract-version": "0.5.25",
475
475
  "x-flag-names": {
476
476
  "CHECK": 65536,
477
477
  "INFER": 16,
@@ -722,5 +722,5 @@
722
722
  ],
723
723
  "title": "BenchmarkSpec",
724
724
  "type": "object",
725
- "x-contract-version": "0.5.24"
725
+ "x-contract-version": "0.5.25"
726
726
  }
@@ -40,5 +40,5 @@
40
40
  ],
41
41
  "title": "BenchmarkStatusResponse",
42
42
  "type": "object",
43
- "x-contract-version": "0.5.24"
43
+ "x-contract-version": "0.5.25"
44
44
  }
@@ -79,5 +79,5 @@
79
79
  ],
80
80
  "title": "BootstrapResponse",
81
81
  "type": "object",
82
- "x-contract-version": "0.5.24"
82
+ "x-contract-version": "0.5.25"
83
83
  }
@@ -122,5 +122,5 @@
122
122
  ],
123
123
  "title": "Camera",
124
124
  "type": "object",
125
- "x-contract-version": "0.5.24"
125
+ "x-contract-version": "0.5.25"
126
126
  }
@@ -23,5 +23,5 @@
23
23
  ],
24
24
  "title": "CodeBundle",
25
25
  "type": "object",
26
- "x-contract-version": "0.5.24"
26
+ "x-contract-version": "0.5.25"
27
27
  }
@@ -96,5 +96,5 @@
96
96
  },
97
97
  "title": "Concurrency",
98
98
  "type": "object",
99
- "x-contract-version": "0.5.24"
99
+ "x-contract-version": "0.5.25"
100
100
  }
@@ -55,5 +55,5 @@
55
55
  ],
56
56
  "title": "Conditions",
57
57
  "type": "object",
58
- "x-contract-version": "0.5.24"
58
+ "x-contract-version": "0.5.25"
59
59
  }
@@ -25,5 +25,5 @@
25
25
  ],
26
26
  "title": "ConnectRequest",
27
27
  "type": "object",
28
- "x-contract-version": "0.5.24"
28
+ "x-contract-version": "0.5.25"
29
29
  }
@@ -568,5 +568,5 @@
568
568
  ],
569
569
  "title": "ConnectResponse",
570
570
  "type": "object",
571
- "x-contract-version": "0.5.24"
571
+ "x-contract-version": "0.5.25"
572
572
  }
@@ -18,5 +18,5 @@
18
18
  ],
19
19
  "title": "DeploymentAccepted",
20
20
  "type": "object",
21
- "x-contract-version": "0.5.24"
21
+ "x-contract-version": "0.5.25"
22
22
  }
@@ -739,38 +739,6 @@
739
739
  ],
740
740
  "default": null
741
741
  },
742
- "own_actions": {
743
- "anyOf": [
744
- {
745
- "enum": [
746
- "one",
747
- "steps"
748
- ],
749
- "type": "string"
750
- },
751
- {
752
- "type": "null"
753
- }
754
- ],
755
- "default": null,
756
- "title": "Own Actions"
757
- },
758
- "own_observations": {
759
- "anyOf": [
760
- {
761
- "enum": [
762
- "latest",
763
- "since_last_act"
764
- ],
765
- "type": "string"
766
- },
767
- {
768
- "type": "null"
769
- }
770
- ],
771
- "default": null,
772
- "title": "Own Observations"
773
- },
774
742
  "params": {
775
743
  "anyOf": [
776
744
  {
@@ -829,5 +797,5 @@
829
797
  ],
830
798
  "title": "DeploymentPolicy",
831
799
  "type": "object",
832
- "x-contract-version": "0.5.24"
800
+ "x-contract-version": "0.5.25"
833
801
  }
@@ -608,38 +608,6 @@
608
608
  ],
609
609
  "default": null
610
610
  },
611
- "own_actions": {
612
- "anyOf": [
613
- {
614
- "enum": [
615
- "one",
616
- "steps"
617
- ],
618
- "type": "string"
619
- },
620
- {
621
- "type": "null"
622
- }
623
- ],
624
- "default": null,
625
- "title": "Own Actions"
626
- },
627
- "own_observations": {
628
- "anyOf": [
629
- {
630
- "enum": [
631
- "latest",
632
- "since_last_act"
633
- ],
634
- "type": "string"
635
- },
636
- {
637
- "type": "null"
638
- }
639
- ],
640
- "default": null,
641
- "title": "Own Observations"
642
- },
643
611
  "params": {
644
612
  "anyOf": [
645
613
  {
@@ -1610,5 +1578,5 @@
1610
1578
  ],
1611
1579
  "title": "DeploymentRequest",
1612
1580
  "type": "object",
1613
- "x-contract-version": "0.5.24"
1581
+ "x-contract-version": "0.5.25"
1614
1582
  }
@@ -58,5 +58,5 @@
58
58
  ],
59
59
  "title": "DeploymentStatusResponse",
60
60
  "type": "object",
61
- "x-contract-version": "0.5.24"
61
+ "x-contract-version": "0.5.25"
62
62
  }
@@ -285,5 +285,5 @@
285
285
  ],
286
286
  "title": "Embodiment",
287
287
  "type": "object",
288
- "x-contract-version": "0.5.24"
288
+ "x-contract-version": "0.5.25"
289
289
  }