sequence-base 0.5.1__tar.gz → 0.6.0__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 (149) hide show
  1. {sequence_base-0.5.1 → sequence_base-0.6.0}/PKG-INFO +1 -1
  2. {sequence_base-0.5.1 → sequence_base-0.6.0}/pyproject.toml +1 -1
  3. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/__init__.py +1 -3
  4. sequence_base-0.6.0/sequence_base/codec/compressed.py +174 -0
  5. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/constants/limits.py +1 -1
  6. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/constants/version.py +2 -1
  7. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/construct.py +1 -1
  8. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/__init__.py +27 -16
  9. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/connect.py +8 -30
  10. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/deployments.py +14 -12
  11. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/observe.py +8 -3
  12. sequence_base-0.6.0/sequence_base/contract/own_io.py +97 -0
  13. sequence_base-0.6.0/sequence_base/contract/planner.py +234 -0
  14. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/policy.py +24 -18
  15. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schema_export.py +24 -24
  16. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ActRequest.json +1 -1
  17. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ActResponse.json +1 -1
  18. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ActionChunk.json +1 -1
  19. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ActionContract.json +1 -1
  20. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ActionStep.json +1 -1
  21. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/AdapterPair.json +1 -1
  22. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/AddLocal.json +1 -1
  23. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Autoscaler.json +1 -1
  24. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/BenchmarkRegisterRequest.json +1 -1
  25. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/BenchmarkSpec.json +3 -3
  26. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
  27. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/BootstrapResponse.json +1 -1
  28. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Camera.json +1 -1
  29. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/CodeBundle.json +1 -1
  30. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Concurrency.json +1 -1
  31. sequence_base-0.6.0/sequence_base/schemas/ConnectRequest.json +28 -0
  32. sequence_base-0.6.0/sequence_base/schemas/ConnectResponse.json +204 -0
  33. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/DeploymentAccepted.json +1 -1
  34. sequence_base-0.6.0/sequence_base/schemas/DeploymentPolicy.json +332 -0
  35. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/DeploymentRequest.json +28 -300
  36. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
  37. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ErrorBody.json +1 -1
  38. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ErrorCodes.json +1 -1
  39. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Estimate.json +1 -1
  40. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
  41. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/EvalReport.json +1 -1
  42. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/EvalRequest.json +1 -1
  43. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/EvalResponse.json +1 -1
  44. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ImageFrame.json +1 -1
  45. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ImageSpec.json +1 -1
  46. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ImageStepAddLocal.json +1 -1
  47. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ImageStepApt.json +1 -1
  48. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ImageStepEnv.json +1 -1
  49. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ImageStepProject.json +1 -1
  50. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ImageStepRun.json +1 -1
  51. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ImageStepTemplateFiles.json +1 -1
  52. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ImageStepUvPip.json +1 -1
  53. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/InferParam.json +1 -1
  54. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Lease.json +1 -1
  55. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/MetricAgg.json +1 -1
  56. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Observation.json +1 -1
  57. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ObservationContract.json +1 -1
  58. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ObserveRequest.json +1 -1
  59. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ObserveResponse.json +1 -1
  60. sequence_base-0.6.0/sequence_base/schemas/PlanAppend.json +13 -0
  61. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/PlanAppended.json +1 -1
  62. sequence_base-0.6.0/sequence_base/schemas/PlannerMember.json +88 -0
  63. sequence_base-0.5.1/sequence_base/schemas/GroupObservation.json → sequence_base-0.6.0/sequence_base/schemas/PlannerObservation.json +4 -4
  64. sequence_base-0.6.0/sequence_base/schemas/PlannerObserve.json +13 -0
  65. sequence_base-0.6.0/sequence_base/schemas/PlannerObserved.json +16 -0
  66. sequence_base-0.6.0/sequence_base/schemas/PlannerPlanIn.json +33 -0
  67. sequence_base-0.6.0/sequence_base/schemas/PlannerPlans.json +66 -0
  68. sequence_base-0.5.1/sequence_base/schemas/TeamPlansWritten.json → sequence_base-0.6.0/sequence_base/schemas/PlannerPlansWritten.json +3 -3
  69. sequence_base-0.5.1/sequence_base/schemas/TeamMember.json → sequence_base-0.6.0/sequence_base/schemas/PlannerRobot.json +4 -9
  70. sequence_base-0.6.0/sequence_base/schemas/PlannerRound.json +200 -0
  71. sequence_base-0.5.1/sequence_base/schemas/TeamGroupsRequest.json → sequence_base-0.6.0/sequence_base/schemas/PlannerRoundRequest.json +3 -3
  72. sequence_base-0.6.0/sequence_base/schemas/PlannerSpec.json +22 -0
  73. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/PolicyRegistration.json +21 -293
  74. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/PolicySpec.json +19 -291
  75. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Project.json +1 -1
  76. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Proprioception.json +1 -1
  77. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/ReplaySpec.json +1 -1
  78. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Resources.json +1 -1
  79. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/RolloutResult.json +1 -1
  80. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/SecretCreateRequest.json +1 -1
  81. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/SecretInfo.json +1 -1
  82. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/SecretRef.json +1 -1
  83. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/SeqFlags.json +1 -1
  84. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/SettingsChange.json +1 -1
  85. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/SettingsChanged.json +1 -1
  86. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/SettingsView.json +1 -1
  87. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Standby.json +1 -1
  88. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/TaskAgg.json +1 -1
  89. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/TemplateFiles.json +1 -1
  90. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Tensors.json +2 -2
  91. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/Timeouts.json +1 -1
  92. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/UploadTarget.json +1 -1
  93. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/VolumeFile.json +1 -1
  94. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/VolumeInfo.json +1 -1
  95. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/VolumeMount.json +1 -1
  96. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/VolumeRef.json +1 -1
  97. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
  98. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
  99. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/schemas/WarmingError.json +1 -1
  100. sequence_base-0.5.1/sequence_base/contract/obs_schedule.py +0 -170
  101. sequence_base-0.5.1/sequence_base/contract/own_io.py +0 -208
  102. sequence_base-0.5.1/sequence_base/contract/team.py +0 -185
  103. sequence_base-0.5.1/sequence_base/schemas/ConnectRequest.json +0 -68
  104. sequence_base-0.5.1/sequence_base/schemas/ConnectResponse.json +0 -476
  105. sequence_base-0.5.1/sequence_base/schemas/DeploymentPolicy.json +0 -604
  106. sequence_base-0.5.1/sequence_base/schemas/GroupObserve.json +0 -13
  107. sequence_base-0.5.1/sequence_base/schemas/GroupObserved.json +0 -16
  108. sequence_base-0.5.1/sequence_base/schemas/Observations.json +0 -264
  109. sequence_base-0.5.1/sequence_base/schemas/PlanAppend.json +0 -13
  110. sequence_base-0.5.1/sequence_base/schemas/Stream.json +0 -193
  111. sequence_base-0.5.1/sequence_base/schemas/TeamGroup.json +0 -120
  112. sequence_base-0.5.1/sequence_base/schemas/TeamGroups.json +0 -135
  113. sequence_base-0.5.1/sequence_base/schemas/TeamPlans.json +0 -51
  114. sequence_base-0.5.1/sequence_base/schemas/TeamSpec.json +0 -22
  115. {sequence_base-0.5.1 → sequence_base-0.6.0}/.gitignore +0 -0
  116. {sequence_base-0.5.1 → sequence_base-0.6.0}/README.md +0 -0
  117. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/auth.py +0 -0
  118. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/by_name.py +0 -0
  119. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/codec/__init__.py +0 -0
  120. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/codec/image.py +0 -0
  121. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/codec/marked.py +0 -0
  122. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/constants/__init__.py +0 -0
  123. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/constants/cards.py +0 -0
  124. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/constants/headers.py +0 -0
  125. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/accounts.py +0 -0
  126. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/act.py +0 -0
  127. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/adapters.py +0 -0
  128. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/benchmarks.py +0 -0
  129. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/errors.py +0 -0
  130. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/eval.py +0 -0
  131. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/flags.py +0 -0
  132. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/io.py +0 -0
  133. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/retired.py +0 -0
  134. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/settings.py +0 -0
  135. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/templates.py +0 -0
  136. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/volumes.py +0 -0
  137. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/contract/wire.py +0 -0
  138. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/episode.py +0 -0
  139. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/errors/__init__.py +0 -0
  140. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/errors/transport.py +0 -0
  141. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/msgpack_io.py +0 -0
  142. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/obs/__init__.py +0 -0
  143. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/obs/log.py +0 -0
  144. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/obs/rid.py +0 -0
  145. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/presets/__init__.py +0 -0
  146. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/presets/frames.py +0 -0
  147. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/presets/robot_arm.py +0 -0
  148. {sequence_base-0.5.1 → sequence_base-0.6.0}/sequence_base/telemetry/__init__.py +0 -0
  149. {sequence_base-0.5.1 → sequence_base-0.6.0}/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.5.1
3
+ Version: 0.6.0
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.5.1"
3
+ version = "0.6.0"
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"]
@@ -86,8 +86,6 @@ from sequence_base.contract import (
86
86
  PolicyConstructor,
87
87
  PolicyRegistration,
88
88
  PolicySpec,
89
- Observations,
90
- Stream,
91
89
  Tensors,
92
90
  Proprioception,
93
91
  ReplaySpec,
@@ -145,7 +143,7 @@ __all__ = [
145
143
  "FROZEN_PATH", "TemplateFiles", "ImageStepTemplateFiles", "Project", "ImageStepProject", "ImageStep", "ImageSpec",
146
144
  "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract",
147
145
  "PolicySpec", "PolicyRegistration", "PolicyConstructor",
148
- "Observations", "Stream", "Tensors",
146
+ "Tensors",
149
147
 
150
148
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
151
149
  "Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
@@ -0,0 +1,174 @@
1
+ """
2
+ Compressed observe items: what a robot's SDK puts in place of a large array or bytes of its observe data when sending it
3
+ compressed saves more upload time than compressing it costs, and what a worker turns back - losslessly, so every reader
4
+ after the worker is handed the bytes the robot built.
5
+
6
+
7
+
8
+ In one sentence: the robot's SDK compresses an unmarked numpy array or bytes of 16 KiB or more with zstd level 1 (zlib
9
+ level 1 where it has no zstd) and puts the map below in its place → the worker names contract.observe.ZSTD_FORMAT, so
10
+ only it is sent such maps → as a post comes, before anything keeps it, expand() turns every map back into the array or
11
+ bytes it was (np.frombuffer: read-only, as msgpack_io reads an array) and says how many bytes that added.
12
+
13
+ The map, version 1 (inside the observe body's msgpack; `data` is bin):
14
+ {"__seq__": "compressed", "v": 1, "codec": "zstd" | "zlib", "n": <bytes before>, "data": <compressed>}
15
+ bytes as they were
16
+ ... and "type": numpy's string for its dtype, "shape": the array's - an array, its data C-ordered
17
+ `n` is checked against the bytes the data turns back into, and against the most one item may hold (the bytes of an
18
+ episode's observations a worker keeps): a map that says less than it holds is refused rather than decompressed past it.
19
+
20
+ WHY the observation format's reserved key: marked items (codec/marked.py) already take a map with "__seq__", so one key
21
+ is reserved in observe data, not two; a compressed item is never inside a marked one - the SDK compresses after encoding
22
+ its markers and leaves their maps as they are - and expand() does not look inside one.
23
+ WHY decompressed as the post comes, at the parent: the stream, Field data and the standby's saves all read a post's
24
+ frames as it comes, and each must keep the robot's data, not the wire's.
25
+
26
+ wire = compress(frame, "zstd") # a reference encoder (the SDK has its own, with its rule)
27
+ value, grew = expand(post_inputs, limit=64 << 20) # the worker, as a post comes
28
+ """
29
+ from __future__ import annotations
30
+
31
+ import zlib
32
+ from typing import Any
33
+
34
+ from sequence_base.codec.marked import MARK, is_marked
35
+
36
+ __all__ = ["CODECS", "KIND", "VERSION", "CompressedError", "compress", "expand", "holds", "is_compressed"]
37
+
38
+ KIND = "compressed"
39
+ VERSION = 1
40
+ CODECS = ("zstd", "zlib")
41
+ _LEVEL = 1
42
+
43
+
44
+ class CompressedError(ValueError):
45
+ """A compressed item that does not read: an unknown codec, data that does not turn back into the `n` bytes it says,
46
+ an `n` past the limit, or an array's type or shape that does not fit them."""
47
+
48
+
49
+ def is_compressed(value: Any) -> bool:
50
+ return isinstance(value, dict) and value.get(MARK) == KIND
51
+
52
+
53
+ def _zstd():
54
+ """zstd's (compress, decompress) - zstandard's, else Python 3.14's compression.zstd. The decompress takes the bytes
55
+ the item says it holds and refuses a frame whose header says other: a header is what sizes the buffer."""
56
+ try:
57
+ import zstandard
58
+
59
+ def unzstd(b: bytes, n: int) -> bytes:
60
+ said = zstandard.frame_content_size(b)
61
+ if said not in (-1, n):
62
+ raise CompressedError(f"a zstd item whose frame says {said} bytes and whose map says {n}")
63
+ return zstandard.ZstdDecompressor().decompress(b, max_output_size=n)
64
+
65
+ return (lambda b: zstandard.ZstdCompressor(level=_LEVEL).compress(b), unzstd)
66
+ except ImportError:
67
+ from compression import zstd
68
+
69
+ def unzstd(b: bytes, n: int) -> bytes:
70
+ said = zstd.get_frame_info(b).decompressed_size
71
+ if said not in (None, n):
72
+ raise CompressedError(f"a zstd item whose frame says {said} bytes and whose map says {n}")
73
+ d = zstd.ZstdDecompressor()
74
+ raw = d.decompress(b, max_length=n)
75
+ if not d.eof:
76
+ raise CompressedError(f"a zstd item that holds more than the {n} bytes it says")
77
+ return raw
78
+
79
+ return (lambda b: zstd.compress(b, level=_LEVEL), unzstd)
80
+
81
+
82
+ def compress(value: Any, codec: str = "zstd") -> dict:
83
+ """`value` - a numpy array or bytes - as its compressed item: a reference encoder, for tests and the platform's own
84
+ senders; the robot's SDK decides by its own rule what to compress."""
85
+ import numpy as np
86
+
87
+ if isinstance(value, np.ndarray):
88
+ raw = np.ascontiguousarray(value).tobytes()
89
+ extra = {"type": value.dtype.str, "shape": list(value.shape)}
90
+ else:
91
+ raw, extra = bytes(value), {}
92
+ if codec == "zstd":
93
+ data = _zstd()[0](raw)
94
+ elif codec == "zlib":
95
+ data = zlib.compress(raw, _LEVEL)
96
+ else:
97
+ raise CompressedError(f"no codec {codec!r}: {', '.join(CODECS)}")
98
+ return dict([(MARK, KIND), ("v", VERSION), ("codec", codec), ("n", len(raw)), *extra.items(), ("data", data)])
99
+
100
+
101
+ def _raw(item: dict, limit: int) -> bytes:
102
+ codec, n, data = item.get("codec"), item.get("n"), item.get("data")
103
+ if codec not in CODECS:
104
+ raise CompressedError(f"a compressed item of codec {codec!r}: {', '.join(CODECS)} are read")
105
+ if isinstance(n, bool) or not isinstance(n, int) or not 0 <= n <= limit:
106
+ raise CompressedError(f"a compressed item of {n!r} bytes: at most {limit} are taken")
107
+ if not isinstance(data, (bytes, bytearray)):
108
+ raise CompressedError("a compressed item whose data is not bytes")
109
+ try:
110
+ if codec == "zstd":
111
+ raw = _zstd()[1](bytes(data), n)
112
+ else:
113
+ d = zlib.decompressobj()
114
+ raw = d.decompress(bytes(data), n)
115
+ if d.unconsumed_tail or not d.eof:
116
+ raise CompressedError(f"a zlib item that holds more than the {n} bytes it says")
117
+ except CompressedError:
118
+ raise
119
+ except Exception as exc:
120
+ raise CompressedError(f"a {codec} item that does not read: {exc}") from None
121
+ if len(raw) != n:
122
+ raise CompressedError(f"a {codec} item of {len(raw)} bytes that says it holds {n}")
123
+ return raw
124
+
125
+
126
+ def _value(item: dict, limit: int) -> Any:
127
+ raw = _raw(item, limit)
128
+ if "type" not in item and "shape" not in item:
129
+ return raw
130
+ import numpy as np
131
+
132
+ from sequence_base.msgpack_io import MsgpackError, _dtype
133
+
134
+ shape = item.get("shape")
135
+ if not isinstance(shape, list) or not all(isinstance(d, int) and not isinstance(d, bool) and d >= 0 for d in shape):
136
+ raise CompressedError("a compressed array whose shape is not a list of non-negative ints")
137
+ try:
138
+ dtype = _dtype(item.get("type"))
139
+ except MsgpackError as exc:
140
+ raise CompressedError(f"a compressed array: {exc}") from None
141
+ if dtype.itemsize * int(np.prod(shape, dtype=np.int64)) != len(raw):
142
+ raise CompressedError(f"a compressed {dtype} array of shape {shape} that holds {len(raw)} bytes")
143
+ return np.frombuffer(raw, dtype=dtype).reshape(shape)
144
+
145
+
146
+ def expand(value: Any, *, limit: int) -> "tuple[Any, int]":
147
+ """`value` with every compressed item in it - in dicts and lists at any depth, never inside a marked item - turned
148
+ back into the array or bytes it was, the same objects where none is; and how many bytes that added. `limit`: the
149
+ most bytes one item may turn back into. CompressedError for an item that does not read."""
150
+ grew = [0]
151
+
152
+ def walk(v: Any) -> Any:
153
+ if isinstance(v, dict):
154
+ if is_compressed(v):
155
+ out = _value(v, limit)
156
+ grew[0] += max(0, int(v["n"]) - len(v["data"]))
157
+ return out
158
+ if is_marked(v) or not holds(v):
159
+ return v
160
+ return {k: walk(x) for k, x in v.items()}
161
+ if isinstance(v, list):
162
+ return [walk(x) for x in v] if holds(v) else v
163
+ return v
164
+
165
+ return walk(value), grew[0]
166
+
167
+
168
+ def holds(value: Any) -> bool:
169
+ """Whether `value` holds a compressed item - in dicts and lists at any depth, not inside a marked item."""
170
+ if isinstance(value, dict):
171
+ return is_compressed(value) or (not is_marked(value) and any(holds(v) for v in value.values()))
172
+ if isinstance(value, list):
173
+ return any(holds(v) for v in value)
174
+ return False
@@ -36,4 +36,4 @@ METRIC_UNIT_MAX = 16
36
36
  MAX_STREAM_FRAMES = 1024
37
37
  MAX_STREAM_BYTES = 64 * 1024 * 1024
38
38
  MAX_WORKER_STREAM_BYTES = 1024 * 1024 * 1024
39
- STREAM_WAIT_S = 10.0
39
+ STREAM_WAIT_S = 3.0
@@ -4,7 +4,8 @@
4
4
 
5
5
 
6
6
 
7
- CONTRACT_VERSION = "0.5.33"
7
+ CONTRACT_VERSION = "0.5.34"
8
+
8
9
 
9
10
 
10
11
 
@@ -1,5 +1,5 @@
1
1
  """
2
- How a class built by its constructor is built - a policy's or a team's (PolicyRegistration.constructor), a benchmark's
2
+ How a class built by its constructor is built - a policy's or a planner's (PolicyRegistration.constructor), a benchmark's
3
3
  (BenchmarkSpec.constructor): its constructor - or the factory it names, a classmethod of it or a module's function -
4
4
  called with the arguments its decorator was given (init=), once the forms in them are filled in.
5
5
 
@@ -45,7 +45,7 @@ from sequence_base.contract.policy import (
45
45
  )
46
46
 
47
47
 
48
- from sequence_base.contract.own_io import Observations, Stream, Tensors
48
+ from sequence_base.contract.own_io import OBSERVATIONS_GONE, Tensors
49
49
 
50
50
 
51
51
  from sequence_base.contract.deployments import (
@@ -124,20 +124,30 @@ from sequence_base.contract.settings import (
124
124
  )
125
125
 
126
126
 
127
- from sequence_base.contract.team import (
128
- TEAM_DEFAULT_GROUP,
129
- GroupObservation,
130
- GroupObserve,
131
- GroupObserved,
127
+
128
+ from sequence_base.contract.planner import (
129
+ KEPT,
130
+ MOST_LAYERS,
131
+ PLANS_MOST,
132
+ STATE_MOST,
133
+ PLAN_ON_A_POLICY,
134
+ PLANNER_ID,
135
+ POLICY_ID,
136
+ ROBOT_ID,
137
+ TEAM_GONE,
132
138
  PlanAppend,
133
139
  PlanAppended,
134
- TeamGroup,
135
- TeamGroups,
136
- TeamGroupsRequest,
137
- TeamMember,
138
- TeamPlans,
139
- TeamPlansWritten,
140
- TeamSpec,
140
+ PlannerMember,
141
+ PlannerObservation,
142
+ PlannerObserve,
143
+ PlannerObserved,
144
+ PlannerPlanIn,
145
+ PlannerPlans,
146
+ PlannerPlansWritten,
147
+ PlannerRobot,
148
+ PlannerRound,
149
+ PlannerRoundRequest,
150
+ PlannerSpec,
141
151
  )
142
152
 
143
153
 
@@ -154,7 +164,7 @@ __all__ = [
154
164
  "TemplateFiles", "ImageStepTemplateFiles", "Project", "ImageStepProject", "FROZEN_PATH", "ImageStep", "ImageSpec",
155
165
  "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract",
156
166
  "PolicySpec", "PolicyRegistration", "PolicyConstructor",
157
- "Observations", "Stream", "Tensors",
167
+ "OBSERVATIONS_GONE", "Tensors",
158
168
 
159
169
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
160
170
  "Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
@@ -174,8 +184,9 @@ __all__ = [
174
184
  "SETTINGS", "Setting", "SettingsChange", "Applied", "SettingState", "SettingsView", "SettingChanged",
175
185
  "SettingsChanged",
176
186
 
177
- "TEAM_DEFAULT_GROUP", "TeamSpec", "TeamGroupsRequest", "GroupObservation", "TeamMember", "TeamGroup", "TeamGroups",
178
- "TeamPlans", "TeamPlansWritten", "PlanAppend", "PlanAppended", "GroupObserve", "GroupObserved",
187
+ "KEPT", "MOST_LAYERS", "PLANS_MOST", "STATE_MOST", "PLAN_ON_A_POLICY", "PLANNER_ID", "POLICY_ID", "ROBOT_ID", "TEAM_GONE", "PlannerSpec",
188
+ "PlannerRoundRequest", "PlannerRobot", "PlannerMember", "PlannerObservation", "PlannerPlanIn", "PlannerRound",
189
+ "PlannerPlans", "PlannerPlansWritten", "PlanAppend", "PlanAppended", "PlannerObserve", "PlannerObserved",
179
190
 
180
191
  "TEMPLATE_SCHEME", "parse_template",
181
192
 
@@ -16,9 +16,8 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator
16
16
 
17
17
 
18
18
  from sequence_base.contract.errors import WarmingError
19
- from sequence_base.contract.own_io import Observations
19
+ from sequence_base.contract.planner import TEAM_GONE
20
20
  from sequence_base.contract.retired import dropped
21
- from sequence_base.contract.team import check_name
22
21
 
23
22
  __all__ = ["Lease", "ConnectRequest", "ConnectResponse", "Standby", "WarmingError"]
24
23
 
@@ -47,34 +46,15 @@ class ConnectRequest(BaseModel):
47
46
 
48
47
 
49
48
  switched: bool = Field(default=False, description="this robot just switched to the standby copy it was handed")
50
-
51
-
52
-
53
-
54
- team: str | None = Field(default=None, description="the team this robot joins, by name")
55
- group: str | None = Field(default=None, description="its group in that team; None: the default group")
56
-
57
-
58
-
59
- team_key: str | None = Field(default=None, max_length=256,
60
- description="a team key of the group it joins (seq_team_…); its group is the key's")
61
-
62
49
  @model_validator(mode="before")
63
50
  @classmethod
64
51
  def _retired(cls, data: Any) -> Any:
65
- return dropped(data, "embodiment")
66
52
 
67
- @model_validator(mode="after")
68
- def _a_group_is_a_teams(self) -> "ConnectRequest":
69
- if self.team is not None:
70
- check_name(self.team, "team")
71
- if self.group is not None:
72
- if self.team is None:
73
- raise ValueError("a group is a team's: say the team too")
74
- check_name(self.group, "group")
75
- if self.team_key is not None and self.team is None:
76
- raise ValueError("a team key is a team's: say the team too")
77
- return self
53
+
54
+
55
+ if isinstance(data, dict) and any(data.get(k) is not None for k in ("team", "group", "team_key")):
56
+ raise ValueError(TEAM_GONE)
57
+ return dropped(data, "embodiment", "team", "group", "team_key")
78
58
 
79
59
 
80
60
  class Standby(BaseModel):
@@ -118,9 +98,6 @@ class ConnectResponse(BaseModel):
118
98
  io: str | None = None
119
99
 
120
100
 
121
- observations: Observations | None = None
122
-
123
-
124
101
  standby: Standby | None = None
125
102
 
126
103
 
@@ -133,4 +110,5 @@ class ConnectResponse(BaseModel):
133
110
  @classmethod
134
111
  def _retired(cls, data: Any) -> Any:
135
112
 
136
- return dropped(data, "embodiment", "feedback", "action", "observation", "actions")
113
+
114
+ return dropped(data, "embodiment", "feedback", "action", "observation", "actions", "observations")
@@ -21,9 +21,9 @@ from typing import Any, Literal
21
21
  from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
22
22
 
23
23
  from sequence_base.contract.accounts import SecretRef
24
- from sequence_base.contract.own_io import Observations, Tensors
24
+ from sequence_base.contract.own_io import Tensors, observations_gone
25
+ from sequence_base.contract.planner import PLAN_ON_A_POLICY, PlannerSpec, team_gone
25
26
  from sequence_base.contract.retired import dropped
26
- from sequence_base.contract.team import TeamSpec
27
27
  from sequence_base.contract.policy import (
28
28
  KIND_NAME,
29
29
  SCALING_RETIRED,
@@ -185,7 +185,8 @@ class DeploymentPolicy(BaseModel):
185
185
  @model_validator(mode="before")
186
186
  @classmethod
187
187
  def _retired(cls, data: Any) -> Any:
188
- return dropped(data, "embodiments", "feedback", "action", "observation", "action_declared", "actions")
188
+ return dropped(team_gone(observations_gone(data)), "embodiments", "feedback", "action", "observation",
189
+ "action_declared", "actions")
189
190
 
190
191
  handler_ref: str
191
192
 
@@ -219,26 +220,27 @@ class DeploymentPolicy(BaseModel):
219
220
  models: list[str] | None = Field(default=None, max_length=16)
220
221
 
221
222
 
222
-
223
-
224
223
  plan_every_s: float | None = Field(default=None, gt=0)
225
224
 
226
225
 
227
- observations: Observations | None = None
228
226
  tensors: Tensors | None = None
229
227
 
230
228
 
231
- team: TeamSpec | None = None
229
+
230
+
231
+ planner: PlannerSpec | None = None
232
232
 
233
233
  @model_validator(mode="after")
234
- def _team_answers_no_act(self) -> "DeploymentPolicy":
235
- if self.team is not None:
236
- said = [name for name in ("hz", "action_chunk_len", "action_chunk_exec_len", "observations", "tensors")
234
+ def _planner_answers_no_act(self) -> "DeploymentPolicy":
235
+ if self.planner is not None:
236
+ said = [name for name in ("hz", "action_chunk_len", "action_chunk_exec_len", "tensors")
237
237
  if getattr(self, name) is not None]
238
238
  if said:
239
- raise ValueError(f"a team answers no act, so it declares no {', '.join(said)}")
239
+ raise ValueError(f"a planner answers no act, so it declares no {', '.join(said)}")
240
240
  if self.plan_every_s is None:
241
- raise ValueError("a team plans: its policy block says plan_every_s")
241
+ raise ValueError("a planner plans: its policy block says plan_every_s")
242
+ elif self.plan_every_s is not None:
243
+ raise ValueError(PLAN_ON_A_POLICY)
242
244
  return self
243
245
 
244
246
  @model_validator(mode="after")
@@ -14,8 +14,7 @@ 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 fed every observation since its last act (Observations(every=1); own_observations "since_last_act" until
18
- contract 0.5.25).
17
+ a policy handed every observation as its robot sends it (an @seq.observe method's, or the newest kept for its act).
19
18
 
20
19
  """
21
20
  from __future__ import annotations
@@ -24,7 +23,8 @@ from typing import Any
24
23
 
25
24
  from pydantic import BaseModel, ConfigDict, Field
26
25
 
27
- __all__ = ["FORMAT", "FRAMES_MISSING", "FRAMES_OVER_LIMIT", "ObserveFrame", "ObserveRequest", "ObserveResponse"]
26
+ __all__ = ["FORMAT", "FRAMES_MISSING", "FRAMES_OVER_LIMIT", "ObserveFrame", "ObserveRequest", "ObserveResponse",
27
+ "ZSTD_FORMAT"]
28
28
 
29
29
 
30
30
 
@@ -33,6 +33,11 @@ FORMAT = "x-seq-observe/1"
33
33
 
34
34
 
35
35
 
36
+ ZSTD_FORMAT = "x-seq-zstd/1"
37
+
38
+
39
+
40
+
36
41
  FRAMES_MISSING = "frames_missing"
37
42
  FRAMES_OVER_LIMIT = "frames_over_limit"
38
43
 
@@ -0,0 +1,97 @@
1
+ """
2
+ What a policy of its own inputs declares (contract 0.5.24): whether its inputs reach it as torch tensors - and what an
3
+ eval plays of its answers (rows_of). A policy that declares nothing is sent and handed what it is today.
4
+
5
+ `observations=seq.Observations(...)` and `seq.Stream(...)`, which said which keys of an observation a policy read at
6
+ which steps, are gone: a policy is handed every observation as its robot sends it, and one that reads more than the
7
+ newest keeps them in an @seq.observe method (on self, or in episode.state). A spec or a deploy that still declares them
8
+ is refused (OBSERVATIONS_GONE); one stored with `observations: null` reads as it did.
9
+
10
+
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import numbers
15
+ from collections.abc import Mapping
16
+ from typing import Any
17
+
18
+ from pydantic import BaseModel, ConfigDict
19
+
20
+ from sequence_base.contract.retired import dropped
21
+
22
+ __all__ = ["OBSERVATIONS_GONE", "Tensors", "observations_gone", "rows_of"]
23
+
24
+
25
+ OBSERVATIONS_GONE = ("observations= is gone: a policy is handed every observation as its robot sends it - keep the ones "
26
+ "an act reads in an @seq.observe method (on self, or in episode.state), and deploy again with the "
27
+ "latest sequence-ai")
28
+
29
+
30
+ def observations_gone(data: Any) -> Any:
31
+ """A model's before-validator for a body that may name `observations`: refused (OBSERVATIONS_GONE) when it declares
32
+ some, read without it when it is null - as a spec stored before it went holds it."""
33
+ if isinstance(data, dict) and data.get("observations") is not None:
34
+ raise ValueError(OBSERVATIONS_GONE)
35
+ return dropped(data, "observations")
36
+
37
+
38
+ class Tensors(BaseModel):
39
+ """A policy's inputs reach its code as torch tensors on its device - a batch dimension first when `batch` - and the
40
+ tensors it answers with go back as arrays; float64 arrays arrive as float32. Converted in the replica, last before
41
+ the policy's code."""
42
+
43
+ model_config = ConfigDict(extra="forbid")
44
+
45
+ batch: bool = True
46
+
47
+
48
+ def rows_of(answer: Any, actions: Any = None) -> list:
49
+ """The rows of a policy's own answer that play, in order, as an eval's adapter reads them. `actions`: what its
50
+ caller says of the rows, a dict or an object - each element of the field its `key` names (the answer itself for
51
+ none) is a row, a dict one too, cut at its `open_loop_horizon`. Without it, the default: a dict, or a list of
52
+ numbers, is one action; a list of lists of one length, or a 2-D array, plays row by row; a ragged or deeper
53
+ nesting, or a list of dicts, is refused, saying to turn it into actions in an adapter - and a number, a text or any
54
+ other object is one action, as before.
55
+
56
+ """
57
+ if actions is not None:
58
+ key = actions.get("key") if isinstance(actions, dict) else actions.key
59
+ horizon = actions.get("open_loop_horizon") if isinstance(actions, dict) else actions.open_loop_horizon
60
+ where = "answer" if key is None else f"answer's {key!r} field"
61
+ if key is not None:
62
+ if not isinstance(answer, Mapping) or key not in answer:
63
+ raise ValueError(f"the policy's answer has no {key!r} field, which holds the rows to play")
64
+ answer = answer[key]
65
+ if isinstance(answer, (Mapping, str, bytes)) or not (isinstance(answer, (list, tuple))
66
+ or (getattr(answer, "ndim", 0) or 0) >= 1):
67
+ raise ValueError(f"the policy's {where} is {type(answer).__name__}, not a list of rows or an array of them")
68
+ rows = list(answer)
69
+ if not rows:
70
+ raise ValueError(f"the policy's {where} holds no rows to play")
71
+ return rows[:horizon] if horizon else rows
72
+ ndim = getattr(answer, "ndim", None)
73
+ if isinstance(ndim, int) and not isinstance(answer, (Mapping, str, bytes)):
74
+ if ndim <= 1:
75
+ return [answer]
76
+ if ndim == 2:
77
+ return list(answer)
78
+ raise ValueError(_unplayable(f"an array of {ndim} dimensions"))
79
+ if isinstance(answer, (list, tuple)):
80
+ if all(isinstance(v, numbers.Number) for v in answer):
81
+ return [answer]
82
+ if all(isinstance(v, (list, tuple)) or getattr(v, "ndim", None) == 1 for v in answer):
83
+ lengths = [len(v) for v in answer]
84
+ if len(set(lengths)) == 1:
85
+ return list(answer)
86
+ raise ValueError(_unplayable(f"a list of lists of lengths {', '.join(map(str, lengths))}"))
87
+ if all(isinstance(v, Mapping) for v in answer):
88
+ raise ValueError(_unplayable(f"a list of {len(answer)} dicts"))
89
+ kinds = dict.fromkeys("numbers" if isinstance(v, numbers.Number) else "lists" if isinstance(v, (list, tuple))
90
+ else type(v).__name__ for v in answer)
91
+ raise ValueError(_unplayable(f"a list of {' and '.join(kinds)}"))
92
+ return [answer]
93
+
94
+
95
+ def _unplayable(what: str) -> str:
96
+ return (f"the policy's answer is {what}, which plays neither as one action (a dict, or a list of numbers) nor row by "
97
+ "row (a list of lists of one length, or a 2-D array): turn it into actions in an adapter")