sequence-base 0.4.34__tar.gz → 0.5.1__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 (147) hide show
  1. {sequence_base-0.4.34 → sequence_base-0.5.1}/PKG-INFO +1 -1
  2. {sequence_base-0.4.34 → sequence_base-0.5.1}/pyproject.toml +1 -1
  3. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/__init__.py +9 -8
  4. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/constants/version.py +4 -1
  5. sequence_base-0.5.1/sequence_base/construct.py +173 -0
  6. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/__init__.py +40 -10
  7. sequence_base-0.5.1/sequence_base/contract/benchmarks.py +223 -0
  8. sequence_base-0.5.1/sequence_base/contract/connect.py +136 -0
  9. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/deployments.py +41 -25
  10. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/eval.py +29 -32
  11. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/flags.py +4 -4
  12. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/own_io.py +14 -47
  13. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/policy.py +120 -42
  14. sequence_base-0.5.1/sequence_base/contract/settings.py +357 -0
  15. sequence_base-0.5.1/sequence_base/contract/team.py +185 -0
  16. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/wire.py +9 -7
  17. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/presets/robot_arm.py +9 -7
  18. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schema_export.py +35 -13
  19. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ActRequest.json +1 -15
  20. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ActResponse.json +1 -15
  21. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ActionChunk.json +1 -1
  22. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ActionContract.json +1 -1
  23. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ActionStep.json +1 -1
  24. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/AdapterPair.json +1 -1
  25. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/AddLocal.json +1 -1
  26. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Autoscaler.json +2 -23
  27. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/BenchmarkRegisterRequest.json +31 -368
  28. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/BenchmarkSpec.json +101 -134
  29. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
  30. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/BootstrapResponse.json +1 -1
  31. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Camera.json +3 -3
  32. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/CodeBundle.json +1 -1
  33. sequence_base-0.5.1/sequence_base/schemas/Concurrency.json +113 -0
  34. sequence_base-0.5.1/sequence_base/schemas/ConnectRequest.json +68 -0
  35. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ConnectResponse.json +110 -218
  36. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentAccepted.json +1 -1
  37. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentPolicy.json +61 -237
  38. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentRequest.json +81 -265
  39. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
  40. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ErrorBody.json +1 -1
  41. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ErrorCodes.json +1 -1
  42. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Estimate.json +1 -1
  43. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
  44. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/EvalReport.json +14 -29
  45. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/EvalRequest.json +5 -41
  46. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/EvalResponse.json +1 -1
  47. sequence_base-0.5.1/sequence_base/schemas/GroupObservation.json +41 -0
  48. sequence_base-0.5.1/sequence_base/schemas/GroupObserve.json +13 -0
  49. sequence_base-0.5.1/sequence_base/schemas/GroupObserved.json +16 -0
  50. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageFrame.json +1 -1
  51. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageSpec.json +1 -1
  52. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepAddLocal.json +1 -1
  53. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepApt.json +1 -1
  54. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepEnv.json +1 -1
  55. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepProject.json +1 -1
  56. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepRun.json +1 -1
  57. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepTemplateFiles.json +1 -1
  58. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepUvPip.json +1 -1
  59. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/InferParam.json +1 -1
  60. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Lease.json +1 -1
  61. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/MetricAgg.json +1 -1
  62. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Observation.json +1 -1
  63. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ObservationContract.json +3 -3
  64. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Observations.json +3 -3
  65. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ObserveRequest.json +1 -1
  66. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ObserveResponse.json +1 -1
  67. sequence_base-0.5.1/sequence_base/schemas/PlanAppend.json +13 -0
  68. sequence_base-0.5.1/sequence_base/schemas/PlanAppended.json +28 -0
  69. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/PolicyRegistration.json +88 -257
  70. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/PolicySpec.json +81 -253
  71. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Project.json +1 -1
  72. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Proprioception.json +1 -1
  73. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ReplaySpec.json +1 -1
  74. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Resources.json +1 -1
  75. sequence_base-0.5.1/sequence_base/schemas/RolloutResult.json +90 -0
  76. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/SecretCreateRequest.json +1 -1
  77. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/SecretInfo.json +1 -1
  78. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/SecretRef.json +1 -1
  79. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/SeqFlags.json +4 -2
  80. sequence_base-0.5.1/sequence_base/schemas/SettingsChange.json +56 -0
  81. sequence_base-0.5.1/sequence_base/schemas/SettingsChanged.json +137 -0
  82. sequence_base-0.5.1/sequence_base/schemas/SettingsView.json +402 -0
  83. sequence_base-0.5.1/sequence_base/schemas/Standby.json +92 -0
  84. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Stream.json +3 -3
  85. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/TaskAgg.json +1 -1
  86. sequence_base-0.5.1/sequence_base/schemas/TeamGroup.json +120 -0
  87. sequence_base-0.5.1/sequence_base/schemas/TeamGroups.json +135 -0
  88. sequence_base-0.5.1/sequence_base/schemas/TeamGroupsRequest.json +21 -0
  89. sequence_base-0.5.1/sequence_base/schemas/TeamMember.json +39 -0
  90. sequence_base-0.5.1/sequence_base/schemas/TeamPlans.json +51 -0
  91. sequence_base-0.5.1/sequence_base/schemas/TeamPlansWritten.json +30 -0
  92. sequence_base-0.5.1/sequence_base/schemas/TeamSpec.json +22 -0
  93. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/TemplateFiles.json +1 -1
  94. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Tensors.json +1 -1
  95. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Timeouts.json +1 -1
  96. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/UploadTarget.json +1 -1
  97. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeFile.json +1 -1
  98. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeInfo.json +1 -1
  99. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeMount.json +1 -1
  100. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeRef.json +1 -1
  101. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
  102. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
  103. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/WarmingError.json +1 -1
  104. sequence_base-0.4.34/sequence_base/construct.py +0 -124
  105. sequence_base-0.4.34/sequence_base/contract/benchmarks.py +0 -305
  106. sequence_base-0.4.34/sequence_base/contract/connect.py +0 -74
  107. sequence_base-0.4.34/sequence_base/schemas/Actions.json +0 -63
  108. sequence_base-0.4.34/sequence_base/schemas/BenchmarkConditionSpec.json +0 -54
  109. sequence_base-0.4.34/sequence_base/schemas/Concurrency.json +0 -100
  110. sequence_base-0.4.34/sequence_base/schemas/Conditions.json +0 -59
  111. sequence_base-0.4.34/sequence_base/schemas/ConnectRequest.json +0 -16
  112. sequence_base-0.4.34/sequence_base/schemas/RenderSpec.json +0 -40
  113. sequence_base-0.4.34/sequence_base/schemas/RolloutResult.json +0 -59
  114. sequence_base-0.4.34/sequence_base/schemas/Step.json +0 -86
  115. sequence_base-0.4.34/sequence_base/schemas/Variant.json +0 -24
  116. {sequence_base-0.4.34 → sequence_base-0.5.1}/.gitignore +0 -0
  117. {sequence_base-0.4.34 → sequence_base-0.5.1}/README.md +0 -0
  118. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/auth.py +0 -0
  119. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/by_name.py +0 -0
  120. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/codec/__init__.py +0 -0
  121. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/codec/image.py +0 -0
  122. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/codec/marked.py +0 -0
  123. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/constants/__init__.py +0 -0
  124. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/constants/cards.py +0 -0
  125. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/constants/headers.py +0 -0
  126. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/constants/limits.py +0 -0
  127. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/accounts.py +0 -0
  128. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/act.py +0 -0
  129. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/adapters.py +0 -0
  130. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/errors.py +0 -0
  131. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/io.py +0 -0
  132. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/obs_schedule.py +0 -0
  133. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/observe.py +0 -0
  134. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/retired.py +0 -0
  135. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/templates.py +0 -0
  136. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/volumes.py +0 -0
  137. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/episode.py +0 -0
  138. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/errors/__init__.py +0 -0
  139. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/errors/transport.py +0 -0
  140. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/msgpack_io.py +0 -0
  141. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/obs/__init__.py +0 -0
  142. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/obs/log.py +0 -0
  143. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/obs/rid.py +0 -0
  144. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/presets/__init__.py +0 -0
  145. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/presets/frames.py +0 -0
  146. {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/telemetry/__init__.py +0 -0
  147. {sequence_base-0.4.34 → sequence_base-0.5.1}/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.34
3
+ Version: 0.5.1
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.34"
3
+ version = "0.5.1"
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"]
@@ -37,6 +37,11 @@ from sequence_base.contract import (
37
37
  parse_vol,
38
38
  TEMPLATE_SCHEME,
39
39
  parse_template,
40
+ SETTINGS,
41
+ Setting,
42
+ SettingsChange,
43
+ SettingsView,
44
+ SettingsChanged,
40
45
  AdapterPair,
41
46
  parse_pairs,
42
47
  ActionChunk,
@@ -44,14 +49,12 @@ from sequence_base.contract import (
44
49
  ActionSpace,
45
50
  ActionStep,
46
51
  Autoscaler,
47
- BenchmarkConditionSpec,
48
52
  BenchmarkRegisterRequest,
49
53
  BenchmarkStatusResponse,
50
54
  BenchmarkSpec,
51
55
  BootstrapResponse,
52
56
  CodeBundle,
53
57
  Concurrency,
54
- Conditions,
55
58
  ConnectRequest,
56
59
  ConnectResponse,
57
60
  DeploymentAccepted,
@@ -85,10 +88,8 @@ from sequence_base.contract import (
85
88
  PolicySpec,
86
89
  Observations,
87
90
  Stream,
88
- Actions,
89
91
  Tensors,
90
92
  Proprioception,
91
- RenderSpec,
92
93
  ReplaySpec,
93
94
  Resources,
94
95
  RolloutResult,
@@ -96,10 +97,8 @@ from sequence_base.contract import (
96
97
  SecretInfo,
97
98
  SecretRef,
98
99
  SeqFlags,
99
- Step,
100
100
  TaskAgg,
101
101
  Timeouts,
102
- Variant,
103
102
  VolumeMount,
104
103
  VolumeRef,
105
104
  WarmingError,
@@ -146,7 +145,7 @@ __all__ = [
146
145
  "FROZEN_PATH", "TemplateFiles", "ImageStepTemplateFiles", "Project", "ImageStepProject", "ImageStep", "ImageSpec",
147
146
  "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract",
148
147
  "PolicySpec", "PolicyRegistration", "PolicyConstructor",
149
- "Observations", "Stream", "Actions", "Tensors",
148
+ "Observations", "Stream", "Tensors",
150
149
 
151
150
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
152
151
  "Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
@@ -154,7 +153,7 @@ __all__ = [
154
153
 
155
154
  "Lease", "ConnectRequest", "ConnectResponse",
156
155
 
157
- "Variant", "Conditions", "RenderSpec", "ReplaySpec", "Step", "BenchmarkSpec", "BenchmarkConditionSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
156
+ "ReplaySpec", "BenchmarkSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
158
157
  "EvalRequest", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
159
158
  "EvalStatus", "TaskAgg", "MetricAgg", "EvalReport",
160
159
 
@@ -162,6 +161,8 @@ __all__ = [
162
161
 
163
162
  "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse", "VolumeInfo", "parse_vol",
164
163
 
164
+ "SETTINGS", "Setting", "SettingsChange", "SettingsView", "SettingsChanged",
165
+
165
166
  "TEMPLATE_SCHEME", "parse_template",
166
167
 
167
168
  "AdapterPair", "parse_pairs",
@@ -4,7 +4,10 @@
4
4
 
5
5
 
6
6
 
7
- CONTRACT_VERSION = "0.5.30"
7
+ CONTRACT_VERSION = "0.5.33"
8
+
9
+
10
+
8
11
 
9
12
 
10
13
 
@@ -0,0 +1,173 @@
1
+ """
2
+ How a class built by its constructor is built - a policy's or a team's (PolicyRegistration.constructor), a benchmark's
3
+ (BenchmarkSpec.constructor): its constructor - or the factory it names, a classmethod of it or a module's function -
4
+ called with the arguments its decorator was given (init=), once the forms in them are filled in.
5
+
6
+ In one sentence: a value {"$config": path, "$set": {...}} becomes the YAML or JSON file at `path` - from the folder of
7
+ the class's file - read where the class runs, with `$set`'s keys laid over its top level → in every string, a policy's
8
+ "{weights}" becomes the weights directory and "{weights.<part>}" a named part's, a benchmark's "{num_worlds}" how many
9
+ worlds this eval builds and "{policy.<field>}" what the evaluated policy declares (POLICY_FIELDS) - a string that is one
10
+ form alone becomes its value as it is, an int or None, one among other text its value as text → `cls.<make>(*args,
11
+ **kwargs)`, or `function(*args, **kwargs)` for make="package.module:function" - the module imported where the class
12
+ runs - or `cls(*args, **kwargs)` without a factory.
13
+
14
+ WHY a config file is read where the policy runs: it is part of the project (XPolicyLab's deploy.yml beside its
15
+ model.py) and goes up with it, so one copy - the one the policy runs with - is read, and "{weights}" in it means the
16
+ worker's directory.
17
+
18
+ WHY a benchmark's too: its worlds are built after the machine's snapshot by the class's own constructor - a repository's
19
+ env as it is, handed how many worlds and the policy's rate where its own code takes them - where a method of the
20
+ platform's (@seq.setup) built them before.
21
+
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import importlib
26
+ import json
27
+ import os
28
+ import re
29
+ from typing import Any
30
+
31
+ __all__ = ["CONFIG_KEY", "FORMS", "POLICY_FIELDS", "SET_KEY", "ConstructError", "build", "config", "expand",
32
+ "is_config", "maker", "read_config"]
33
+
34
+ CONFIG_KEY, SET_KEY = "$config", "$set"
35
+
36
+
37
+
38
+ POLICY_FIELDS = ("hz", "action_chunk_len", "action_chunk_exec_len")
39
+
40
+
41
+ FORMS = re.compile(r"\{(?:weights(?:\.(?P<part>[A-Za-z_][A-Za-z0-9_-]*))?|(?P<worlds>num_worlds)"
42
+ r"|policy\.(?P<field>[A-Za-z_][A-Za-z0-9_]*))\}")
43
+
44
+
45
+ class ConstructError(ValueError):
46
+ """Arguments that cannot be filled in: a config file missing, unreadable or of another kind, a weights placeholder
47
+ with no weights - or no such part - to fill it, "{num_worlds}" where no worlds are built, or a policy field no policy
48
+ declares."""
49
+
50
+
51
+ def config(path: str, **overrides: Any) -> dict:
52
+ """A config file's contents with `overrides` laid over its top level, as the worker reads it (`seq.config`)."""
53
+ if not isinstance(path, str) or not path:
54
+ raise ConstructError("a config file is a path, from the folder of the class's file")
55
+ if os.path.isabs(path):
56
+ raise ConstructError(f"{path}: give a config file's path from the folder of the class's file - the policy runs "
57
+ "it where the upload puts it")
58
+ return {CONFIG_KEY: path, SET_KEY: dict(overrides)}
59
+
60
+
61
+ def is_config(value: Any) -> bool:
62
+ return isinstance(value, dict) and set(value) == {CONFIG_KEY, SET_KEY} and isinstance(value[CONFIG_KEY], str)
63
+
64
+
65
+ def read_config(path: str) -> Any:
66
+ """The YAML (.yml, .yaml) or JSON (.json) file at `path`."""
67
+ kind = os.path.splitext(path)[1].lower()
68
+ if kind not in (".json", ".yml", ".yaml"):
69
+ raise ConstructError(f"{path}: a config file is .json, .yml or .yaml")
70
+ if not os.path.isfile(path):
71
+ raise ConstructError(f"{path}: no such config file")
72
+ with open(path, encoding="utf-8") as f:
73
+ if kind == ".json":
74
+ return json.load(f)
75
+ try:
76
+ import yaml
77
+ except ImportError:
78
+ raise ConstructError(f"{path}: reading YAML needs pyyaml") from None
79
+ return yaml.safe_load(f)
80
+
81
+
82
+ def expand(value: Any, *, base_dir: str, weights: Any = None, worlds: int | None = None, policy: Any = None) -> Any:
83
+ """`value` with its config files read (from `base_dir`) and its forms filled: a policy's from `weights` - the
84
+ weights directory, or {part: directory} for named parts, or None for a policy without weights - a benchmark's from
85
+ `worlds`, how many worlds this eval builds, and `policy`, what the evaluated policy declares ({field: value}, or an
86
+ object with them; None where none is evaluated, each field None). A string that is one form alone becomes its value
87
+ as it is; one form among other text, its value as text."""
88
+ forms = {"base_dir": base_dir, "weights": weights, "worlds": worlds, "policy": policy}
89
+ if is_config(value):
90
+ loaded = read_config(os.path.join(base_dir, value[CONFIG_KEY]))
91
+ if value[SET_KEY] and not isinstance(loaded, dict):
92
+ raise ConstructError(f"{value[CONFIG_KEY]} holds a {type(loaded).__name__}, which has no keys to set")
93
+ merged = {**loaded, **value[SET_KEY]} if isinstance(loaded, dict) else loaded
94
+ return expand(merged, **forms)
95
+ if isinstance(value, dict):
96
+ return {k: expand(v, **forms) for k, v in value.items()}
97
+ if isinstance(value, (list, tuple)):
98
+ return [expand(v, **forms) for v in value]
99
+ if isinstance(value, str):
100
+ alone = FORMS.fullmatch(value)
101
+ if alone:
102
+ return _filled(alone, weights, worlds, policy)
103
+ return FORMS.sub(lambda m: str(_filled(m, weights, worlds, policy)), value)
104
+ return value
105
+
106
+
107
+ def _filled(form: re.Match, weights: Any, worlds: int | None, policy: Any) -> Any:
108
+ if form.group("worlds"):
109
+ if worlds is None:
110
+ raise ConstructError("{num_worlds} is how many worlds this eval builds, and none is handed: a benchmark of one "
111
+ "world an instance takes none")
112
+ return worlds
113
+ field = form.group("field")
114
+ if field is not None:
115
+ if field not in POLICY_FIELDS:
116
+ raise ConstructError(f"{{policy.{field}}}: a policy declares {', '.join(POLICY_FIELDS)}")
117
+ if policy is None:
118
+ return None
119
+ return policy.get(field) if isinstance(policy, dict) else getattr(policy, field, None)
120
+ return _weights_of(form.group("part"), weights)
121
+
122
+
123
+ def _weights_of(part: str | None, weights: Any) -> str:
124
+ if weights is None:
125
+ raise ConstructError("{weights} names the weights directory, and this policy declares no weights=")
126
+ if isinstance(weights, dict):
127
+ if part is None:
128
+ raise ConstructError(f"its weights come in named parts ({', '.join(sorted(weights))}): name one, "
129
+ f"{{weights.<part>}}")
130
+ if part not in weights:
131
+ raise ConstructError(f"{{weights.{part}}}: its weights have no part {part!r} "
132
+ f"({', '.join(sorted(weights))})")
133
+ return str(weights[part])
134
+ if part is not None:
135
+ raise ConstructError(f"{{weights.{part}}} names a part, and its weights come whole: {{weights}}")
136
+ return str(weights)
137
+
138
+
139
+ def build(cls: type, constructor: Any, *, base_dir: str, weights: Any = None, worlds: int | None = None,
140
+ policy: Any = None, params: dict | None = None) -> Any:
141
+ """The instance `constructor` (a PolicyConstructor) makes of `cls`: `cls.<make>(*args, **kwargs)`, the function
142
+ make="package.module:function" names called so, or `cls(...)` - its arguments filled (expand), and `params`, what
143
+ an eval set of the constructor's other arguments (--param benchmark.NAME=VALUE), laid over them. A keyword argument
144
+ that is "{policy.<field>}" alone is left out while that field is None - no policy evaluated, at a registration
145
+ check, or one that declares none - so the constructor's own default holds, as a benchmark's own code did with what
146
+ @seq.setup was handed before."""
147
+ forms = {"base_dir": base_dir, "weights": weights, "worlds": worlds, "policy": policy}
148
+ args = expand(list(constructor.args), **forms)
149
+ kwargs = {}
150
+ for name, value in dict(constructor.kwargs).items():
151
+ filled = expand(value, **forms)
152
+ alone = FORMS.fullmatch(value) if isinstance(value, str) else None
153
+ if filled is None and alone is not None and alone.group("field") is not None:
154
+ continue
155
+ kwargs[name] = filled
156
+ return maker(cls, constructor.make)(*args, **{**kwargs, **(params or {})})
157
+
158
+
159
+ def maker(cls: type, make: str | None) -> Any:
160
+ """What builds the instance: `cls` for no make=, its classmethod or staticmethod `make`, or - make written
161
+ "package.module:function" - that module's function, the module imported (as the policy's code imports it).
162
+ """
163
+ if not make:
164
+ return cls
165
+ module, colon, name = make.rpartition(":")
166
+ if not colon:
167
+ return getattr(cls, make)
168
+ found = getattr(importlib.import_module(module), name, None)
169
+ if found is None:
170
+ raise ConstructError(f"make={make!r}: {module} has no function {name!r}")
171
+ if not callable(found):
172
+ raise ConstructError(f"make={make!r}: {module}.{name} is not callable ({type(found).__name__})")
173
+ return found
@@ -45,7 +45,7 @@ from sequence_base.contract.policy import (
45
45
  )
46
46
 
47
47
 
48
- from sequence_base.contract.own_io import Actions, Observations, Stream, Tensors
48
+ from sequence_base.contract.own_io import Observations, Stream, Tensors
49
49
 
50
50
 
51
51
  from sequence_base.contract.deployments import (
@@ -63,19 +63,14 @@ from sequence_base.contract.deployments import (
63
63
  )
64
64
 
65
65
 
66
- from sequence_base.contract.connect import ConnectRequest, ConnectResponse, Lease
66
+ from sequence_base.contract.connect import ConnectRequest, ConnectResponse, Lease, Standby
67
67
 
68
68
 
69
69
  from sequence_base.contract.benchmarks import (
70
- BenchmarkConditionSpec,
71
70
  BenchmarkRegisterRequest,
72
71
  BenchmarkStatusResponse,
73
72
  BenchmarkSpec,
74
- Conditions,
75
- RenderSpec,
76
73
  ReplaySpec,
77
- Step,
78
- Variant,
79
74
  )
80
75
  from sequence_base.contract.eval import (
81
76
  Estimate,
@@ -117,6 +112,35 @@ from sequence_base.contract.volumes import (
117
112
  )
118
113
 
119
114
 
115
+ from sequence_base.contract.settings import (
116
+ SETTINGS,
117
+ Applied,
118
+ Setting,
119
+ SettingChanged,
120
+ SettingState,
121
+ SettingsChange,
122
+ SettingsChanged,
123
+ SettingsView,
124
+ )
125
+
126
+
127
+ from sequence_base.contract.team import (
128
+ TEAM_DEFAULT_GROUP,
129
+ GroupObservation,
130
+ GroupObserve,
131
+ GroupObserved,
132
+ PlanAppend,
133
+ PlanAppended,
134
+ TeamGroup,
135
+ TeamGroups,
136
+ TeamGroupsRequest,
137
+ TeamMember,
138
+ TeamPlans,
139
+ TeamPlansWritten,
140
+ TeamSpec,
141
+ )
142
+
143
+
120
144
  from sequence_base.contract.errors import ERRORS, ErrorBody, ErrorCode, ErrorSpec, WarmingError, code_for_status
121
145
 
122
146
  __all__ = [
@@ -130,15 +154,15 @@ __all__ = [
130
154
  "TemplateFiles", "ImageStepTemplateFiles", "Project", "ImageStepProject", "FROZEN_PATH", "ImageStep", "ImageSpec",
131
155
  "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract",
132
156
  "PolicySpec", "PolicyRegistration", "PolicyConstructor",
133
- "Observations", "Stream", "Actions", "Tensors",
157
+ "Observations", "Stream", "Tensors",
134
158
 
135
159
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
136
160
  "Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
137
161
  "DeploymentAccepted", "DeploymentState", "DeploymentStatusResponse",
138
162
 
139
- "Lease", "ConnectRequest", "ConnectResponse",
163
+ "Lease", "ConnectRequest", "ConnectResponse", "Standby",
140
164
 
141
- "Variant", "Conditions", "RenderSpec", "ReplaySpec", "Step", "BenchmarkSpec", "BenchmarkConditionSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
165
+ "ReplaySpec", "BenchmarkSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
142
166
  "EvalRequest", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
143
167
  "EvalStatus", "TaskAgg", "MetricAgg", "EvalReport",
144
168
 
@@ -147,6 +171,12 @@ __all__ = [
147
171
  "MAX_FILES", "MAX_UPLOAD_BYTES", "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse",
148
172
  "VolumeInfo", "check_volume_name", "parse_vol",
149
173
 
174
+ "SETTINGS", "Setting", "SettingsChange", "Applied", "SettingState", "SettingsView", "SettingChanged",
175
+ "SettingsChanged",
176
+
177
+ "TEAM_DEFAULT_GROUP", "TeamSpec", "TeamGroupsRequest", "GroupObservation", "TeamMember", "TeamGroup", "TeamGroups",
178
+ "TeamPlans", "TeamPlansWritten", "PlanAppend", "PlanAppended", "GroupObserve", "GroupObserved",
179
+
150
180
  "TEMPLATE_SCHEME", "parse_template",
151
181
 
152
182
  "AdapterPair", "parse_pairs",
@@ -0,0 +1,223 @@
1
+ """
2
+ Contract 5 (registration half) - a benchmark is the env you already have, run as it is: its class is built by its own
3
+ constructor after the machine's snapshot (init= its arguments, "{num_worlds}" and "{policy.<field>}" filled), a short class
4
+ of yours marks two methods - @seq.reset starts an episode of a task at a seed, @seq.step plays one action and says what
5
+ came of it - and declares its tasks, its seeds and how long an episode may run. The platform reads none of its
6
+ observations or actions: they go to the policy, and come from it, as a robot's do. The eval *run* wire types
7
+ (RolloutResult, EvalReport, ...) live in contract/eval.py.
8
+
9
+
10
+ """
11
+ from __future__ import annotations
12
+
13
+ from typing import Literal
14
+
15
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
16
+
17
+ from sequence_base.constants.cards import DRIVER, check_driver
18
+ from sequence_base.constants.limits import MAX_ENVS, TASK_DESCRIPTION_MAX
19
+ from sequence_base.contract.accounts import SecretRef
20
+ from sequence_base.contract.deployments import CodeBundle, DeploymentState, InferParam
21
+ from sequence_base.contract.flags import BENCHMARK_BINDABLE, SeqFlags
22
+ from sequence_base.contract.policy import KIND_NAME, ImageSpec, PolicyConstructor, VolumeRef
23
+ from sequence_base.construct import FORMS
24
+
25
+ __all__ = [
26
+ "ReplaySpec",
27
+ "BenchmarkSpec",
28
+ "BenchmarkRegisterRequest",
29
+ "BenchmarkStatusResponse",
30
+ ]
31
+
32
+
33
+ TASK_NAME = r"^[A-Za-z0-9][A-Za-z0-9._/:-]{0,127}$"
34
+
35
+
36
+
37
+ SETUP_GONE = ("@seq.setup is gone: a benchmark builds its worlds in its constructor, after the machine's snapshot - "
38
+ 'init= its arguments, "{num_worlds}" how many worlds this eval builds and "{policy.<field>}" what the '
39
+ "evaluated policy declares; a benchmark registered with @seq.setup is registered again with sequence-ai "
40
+ "0.51.1 or later (seq deploy)")
41
+
42
+
43
+ def _names_worlds(constructor: PolicyConstructor | None) -> bool:
44
+ """Whether init= hands the constructor "{num_worlds}" anywhere - in a string, at any depth."""
45
+ def walk(value) -> bool:
46
+ if isinstance(value, str):
47
+ return any(m.group("worlds") for m in FORMS.finditer(value))
48
+ if isinstance(value, dict):
49
+ return any(walk(v) for v in value.values())
50
+ if isinstance(value, (list, tuple)):
51
+ return any(walk(v) for v in value)
52
+ return False
53
+
54
+ return constructor is not None and walk([constructor.args, constructor.kwargs])
55
+
56
+
57
+ def _check_tasks(tasks: dict, seeds: list, max_seconds) -> None:
58
+ import re as _re
59
+
60
+ if not tasks:
61
+ raise ValueError("tasks names at least one task: {name: description}")
62
+ for name, text in tasks.items():
63
+ if not _re.match(TASK_NAME, name):
64
+ raise ValueError(f"task {name!r}: 1-128 letters, digits, '.', '_', '/', ':' or '-', starting with a letter "
65
+ "or digit")
66
+ if len(text or "") > TASK_DESCRIPTION_MAX:
67
+ raise ValueError(f"tasks[{name!r}] is {len(text)} characters: at most {TASK_DESCRIPTION_MAX}")
68
+ if not seeds:
69
+ raise ValueError("seeds names at least one seed")
70
+ if len(set(seeds)) != len(seeds):
71
+ raise ValueError("seeds names each seed once")
72
+ limits = max_seconds if isinstance(max_seconds, dict) else {"": max_seconds}
73
+ for task, limit in limits.items():
74
+ if task and task not in tasks:
75
+ raise ValueError(f"max_seconds names {task!r}, which is not one of its tasks")
76
+ if not limit > 0:
77
+ raise ValueError(f"max_seconds{'[' + repr(task) + ']' if task else ''} must be above 0 s, not {limit!r}")
78
+
79
+
80
+ class ReplaySpec(BaseModel):
81
+ """a.k.a. `seq.Replay` — declares that the platform records the eval's 3D replay: each step every node's world
82
+ transform, drawn in the owner's browser after the eval. `engine` picks the platform's
83
+ adapter that reads the simulation's state ("mujoco", "sapien", "isaac"); "own" means the benchmark hands the frames itself
84
+ (`seq.replay.scene` / `seq.replay.frame`); None takes the benchmark's own `engine`. `state` names the benchmark
85
+ method an adapter calls for the engine's state of an env (or of a batched benchmark's slots), `looks` the one that
86
+ says what the adapter cannot read - a colour or a visibility each episode sets. Whether an eval records it is the
87
+ eval's `replay` ("3d" by default, "none")."""
88
+
89
+ model_config = ConfigDict(extra="forbid")
90
+
91
+ engine: Literal["mujoco", "sapien", "isaac", "own"] | None = Field(
92
+ None, description="The adapter that reads the simulation: 'mujoco' (incl. MuJoCo Warp), 'sapien', 'isaac', or "
93
+ "'own' (the benchmark hands frames itself); None: the benchmark's engine")
94
+ state: str | None = Field(None, pattern=r"^[A-Za-z_][A-Za-z0-9_]*$",
95
+ description="The benchmark method an adapter calls for an env's (or slots') engine state")
96
+ looks: str | None = Field(None, pattern=r"^[A-Za-z_][A-Za-z0-9_]*$",
97
+ description="The benchmark method that says each episode's colours or visibility")
98
+
99
+
100
+ class BenchmarkSpec(BaseModel):
101
+ """What `@seq.benchmark(...)` and its marked methods add up to, as an eval runs it. `bindings` names the methods,
102
+ RESET and STEP; `constructor` the arguments its class is built with after the machine's snapshot (init=) - which a
103
+ vectorized env, one stepping `worlds` worlds a call (@seq.concurrent(sizes=[N]) on its step), hands how many,
104
+ "{num_worlds}". `max_seconds` is an episode's time on the robot's clock, the same for every task or {task: seconds};
105
+ `assets` are versioned scene volumes mounted at /assets; `secrets` by name."""
106
+
107
+ model_config = ConfigDict(extra="forbid")
108
+
109
+ name: str
110
+ image: ImageSpec
111
+ tasks: dict[str, str] = Field(..., description="Each task's name and what it asks of the robot, shown beside it")
112
+ seeds: list[int] = Field(..., description="The seeds an episode starts at; trial k of a task runs seeds[k % len]")
113
+ max_seconds: float | dict[str, float] = Field(..., description="An episode's time limit, or {task: seconds}")
114
+ handler_ref: str
115
+ bindings: dict[SeqFlags, str] = Field(..., description="RESET / STEP -> method name")
116
+
117
+
118
+ worlds: int | None = Field(default=None, ge=1, le=MAX_ENVS)
119
+
120
+ gpu: str | None = None
121
+
122
+ driver: str | None = Field(default=None, pattern=DRIVER)
123
+ assets: list[VolumeRef] = Field(default_factory=list)
124
+
125
+ replay: ReplaySpec | None = None
126
+ cpu: int | None = None
127
+ memory_gb: int | None = None
128
+ secrets: list[SecretRef] = Field(default_factory=list)
129
+
130
+ engine: str | None = Field(default=None, pattern=KIND_NAME)
131
+
132
+
133
+
134
+ constructor: PolicyConstructor | None = None
135
+
136
+ @model_validator(mode="after")
137
+ def _tasks(self) -> "BenchmarkSpec":
138
+ _check_tasks(self.tasks, self.seeds, self.max_seconds)
139
+ return self
140
+
141
+ @model_validator(mode="after")
142
+ def _bindings(self) -> "BenchmarkSpec":
143
+ if SeqFlags.SETUP in self.bindings:
144
+ raise ValueError(SETUP_GONE)
145
+ bad = set(self.bindings) - set(BENCHMARK_BINDABLE)
146
+ if bad:
147
+ names = ", ".join(sorted(f.name or str(f.value) for f in bad))
148
+ raise ValueError(f"a benchmark marks @seq.reset and @seq.step only; got {names}")
149
+ missing = [f.name for f in (SeqFlags.RESET, SeqFlags.STEP) if f not in self.bindings]
150
+ if missing:
151
+ raise ValueError(f"a benchmark marks @seq.reset and @seq.step; {', '.join(missing).lower()} is missing")
152
+ if self.worlds is not None and not _names_worlds(self.constructor):
153
+ raise ValueError("a vectorized benchmark (@seq.concurrent on its step) builds its worlds in its "
154
+ 'constructor, handed how many by init= - init={"worlds": "{num_worlds}"}')
155
+ return self
156
+
157
+ @model_validator(mode="after")
158
+ def _driver_of_a_card(self) -> "BenchmarkSpec":
159
+ check_driver(self.driver, self.gpu)
160
+ return self
161
+
162
+
163
+ class BenchmarkRegisterRequest(BaseModel):
164
+ """POST /v1/benchmarks - what `seq benchmark register` sends: the declaration, the code, and the parameters its
165
+ constructor takes with a default beside what init= hands it (each settable by an eval's --param, with its type)."""
166
+
167
+ model_config = ConfigDict(extra="forbid")
168
+
169
+ name: str
170
+ image: ImageSpec
171
+ tasks: dict[str, str]
172
+ seeds: list[int]
173
+ max_seconds: float | dict[str, float]
174
+ handler_ref: str
175
+ worlds: int | None = Field(default=None, ge=1, le=MAX_ENVS)
176
+ assets: list[VolumeRef] = Field(default_factory=list)
177
+ replay: ReplaySpec | None = None
178
+ cpu: int | None = None
179
+ memory_gb: int | None = None
180
+
181
+ code: CodeBundle | None = None
182
+
183
+ freeze: bool | None = None
184
+ secrets: list[SecretRef] = Field(default_factory=list)
185
+ gpu: str | None = Field(default=None, max_length=32)
186
+
187
+
188
+ driver: str | None = Field(default=None, pattern=DRIVER,
189
+ description='The NVIDIA driver its card must have, by major version: "580"')
190
+
191
+
192
+ params: dict[str, InferParam] | None = Field(default=None, max_length=32)
193
+
194
+ image_context: CodeBundle | None = None
195
+ engine: str | None = Field(default=None, pattern=KIND_NAME)
196
+
197
+ @model_validator(mode="after")
198
+ def _tasks(self) -> "BenchmarkRegisterRequest":
199
+ _check_tasks(self.tasks, self.seeds, self.max_seconds)
200
+ return self
201
+
202
+ @model_validator(mode="after")
203
+ def _param_names(self) -> "BenchmarkRegisterRequest":
204
+ for name in self.params or {}:
205
+ if not name.isidentifier() or name.startswith("_"):
206
+ raise ValueError(f"parameter {name!r} is not a public Python identifier")
207
+ return self
208
+
209
+ @model_validator(mode="after")
210
+ def _driver_of_a_card(self) -> "BenchmarkRegisterRequest":
211
+ check_driver(self.driver, self.gpu)
212
+ return self
213
+
214
+
215
+ class BenchmarkStatusResponse(BaseModel):
216
+ """GET /v1/benchmarks/{name} — the build state of a registered benchmark (same lifecycle as a deployment:
217
+ building → ready | failed), with the failure reason when failed."""
218
+
219
+ model_config = ConfigDict(extra="forbid")
220
+
221
+ name: str
222
+ status: DeploymentState
223
+ error: str | None = None