sequence-base 0.5.0__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 (136) hide show
  1. {sequence_base-0.5.0 → sequence_base-0.5.1}/PKG-INFO +1 -1
  2. {sequence_base-0.5.0 → sequence_base-0.5.1}/pyproject.toml +1 -1
  3. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/constants/version.py +2 -1
  4. sequence_base-0.5.1/sequence_base/construct.py +173 -0
  5. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/benchmarks.py +48 -19
  6. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/connect.py +13 -0
  7. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/flags.py +2 -1
  8. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/policy.py +19 -12
  9. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ActRequest.json +1 -1
  10. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ActResponse.json +1 -1
  11. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ActionChunk.json +1 -1
  12. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ActionContract.json +1 -1
  13. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ActionStep.json +1 -1
  14. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/AdapterPair.json +1 -1
  15. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/AddLocal.json +1 -1
  16. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Autoscaler.json +1 -1
  17. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/BenchmarkRegisterRequest.json +2 -2
  18. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/BenchmarkSpec.json +50 -4
  19. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
  20. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/BootstrapResponse.json +1 -1
  21. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Camera.json +1 -1
  22. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/CodeBundle.json +1 -1
  23. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Concurrency.json +1 -1
  24. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ConnectRequest.json +15 -1
  25. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ConnectResponse.json +16 -1
  26. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentAccepted.json +1 -1
  27. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentPolicy.json +1 -1
  28. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentRequest.json +1 -1
  29. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
  30. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ErrorBody.json +1 -1
  31. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ErrorCodes.json +1 -1
  32. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Estimate.json +1 -1
  33. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
  34. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/EvalReport.json +1 -1
  35. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/EvalRequest.json +1 -1
  36. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/EvalResponse.json +1 -1
  37. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/GroupObservation.json +1 -1
  38. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/GroupObserve.json +1 -1
  39. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/GroupObserved.json +1 -1
  40. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ImageFrame.json +1 -1
  41. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ImageSpec.json +1 -1
  42. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepAddLocal.json +1 -1
  43. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepApt.json +1 -1
  44. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepEnv.json +1 -1
  45. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepProject.json +1 -1
  46. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepRun.json +1 -1
  47. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepTemplateFiles.json +1 -1
  48. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepUvPip.json +1 -1
  49. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/InferParam.json +1 -1
  50. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Lease.json +1 -1
  51. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/MetricAgg.json +1 -1
  52. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Observation.json +1 -1
  53. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ObservationContract.json +1 -1
  54. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Observations.json +1 -1
  55. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ObserveRequest.json +1 -1
  56. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ObserveResponse.json +1 -1
  57. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/PlanAppend.json +1 -1
  58. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/PlanAppended.json +1 -1
  59. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/PolicyRegistration.json +3 -3
  60. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/PolicySpec.json +1 -1
  61. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Project.json +1 -1
  62. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Proprioception.json +1 -1
  63. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/ReplaySpec.json +1 -1
  64. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Resources.json +1 -1
  65. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/RolloutResult.json +1 -1
  66. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/SecretCreateRequest.json +1 -1
  67. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/SecretInfo.json +1 -1
  68. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/SecretRef.json +1 -1
  69. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/SeqFlags.json +1 -1
  70. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/SettingsChange.json +1 -1
  71. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/SettingsChanged.json +1 -1
  72. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/SettingsView.json +1 -1
  73. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Standby.json +1 -1
  74. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Stream.json +1 -1
  75. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/TaskAgg.json +1 -1
  76. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/TeamGroup.json +1 -1
  77. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/TeamGroups.json +1 -1
  78. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/TeamGroupsRequest.json +1 -1
  79. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/TeamMember.json +1 -1
  80. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/TeamPlans.json +1 -1
  81. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/TeamPlansWritten.json +1 -1
  82. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/TeamSpec.json +1 -1
  83. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/TemplateFiles.json +1 -1
  84. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Tensors.json +1 -1
  85. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/Timeouts.json +1 -1
  86. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/UploadTarget.json +1 -1
  87. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/VolumeFile.json +1 -1
  88. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/VolumeInfo.json +1 -1
  89. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/VolumeMount.json +1 -1
  90. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/VolumeRef.json +1 -1
  91. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
  92. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
  93. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schemas/WarmingError.json +1 -1
  94. sequence_base-0.5.0/sequence_base/construct.py +0 -124
  95. {sequence_base-0.5.0 → sequence_base-0.5.1}/.gitignore +0 -0
  96. {sequence_base-0.5.0 → sequence_base-0.5.1}/README.md +0 -0
  97. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/__init__.py +0 -0
  98. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/auth.py +0 -0
  99. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/by_name.py +0 -0
  100. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/codec/__init__.py +0 -0
  101. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/codec/image.py +0 -0
  102. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/codec/marked.py +0 -0
  103. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/constants/__init__.py +0 -0
  104. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/constants/cards.py +0 -0
  105. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/constants/headers.py +0 -0
  106. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/constants/limits.py +0 -0
  107. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/__init__.py +0 -0
  108. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/accounts.py +0 -0
  109. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/act.py +0 -0
  110. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/adapters.py +0 -0
  111. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/deployments.py +0 -0
  112. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/errors.py +0 -0
  113. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/eval.py +0 -0
  114. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/io.py +0 -0
  115. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/obs_schedule.py +0 -0
  116. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/observe.py +0 -0
  117. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/own_io.py +0 -0
  118. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/retired.py +0 -0
  119. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/settings.py +0 -0
  120. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/team.py +0 -0
  121. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/templates.py +0 -0
  122. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/volumes.py +0 -0
  123. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/contract/wire.py +0 -0
  124. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/episode.py +0 -0
  125. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/errors/__init__.py +0 -0
  126. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/errors/transport.py +0 -0
  127. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/msgpack_io.py +0 -0
  128. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/obs/__init__.py +0 -0
  129. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/obs/log.py +0 -0
  130. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/obs/rid.py +0 -0
  131. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/presets/__init__.py +0 -0
  132. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/presets/frames.py +0 -0
  133. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/presets/robot_arm.py +0 -0
  134. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/schema_export.py +0 -0
  135. {sequence_base-0.5.0 → sequence_base-0.5.1}/sequence_base/telemetry/__init__.py +0 -0
  136. {sequence_base-0.5.0 → 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.5.0
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.5.0"
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"]
@@ -4,7 +4,8 @@
4
4
 
5
5
 
6
6
 
7
- CONTRACT_VERSION = "0.5.32"
7
+ CONTRACT_VERSION = "0.5.33"
8
+
8
9
 
9
10
 
10
11
 
@@ -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
@@ -1,9 +1,10 @@
1
1
  """
2
- Contract 5 (registration half) - a benchmark is the env you already have, run as it is: a short class of yours marks
3
- three methods - @seq.setup builds this eval's worlds after the machine's snapshot, @seq.reset starts an episode of a task
4
- at a seed, @seq.step plays one action and says what came of it - and declares its tasks, its seeds and how long an episode
5
- may run. The platform reads none of its observations or actions: they go to the policy, and come from it, as a robot's
6
- do. The eval *run* wire types (RolloutResult, EvalReport, ...) live in contract/eval.py.
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.
7
8
 
8
9
 
9
10
  """
@@ -18,7 +19,8 @@ from sequence_base.constants.limits import MAX_ENVS, TASK_DESCRIPTION_MAX
18
19
  from sequence_base.contract.accounts import SecretRef
19
20
  from sequence_base.contract.deployments import CodeBundle, DeploymentState, InferParam
20
21
  from sequence_base.contract.flags import BENCHMARK_BINDABLE, SeqFlags
21
- from sequence_base.contract.policy import KIND_NAME, ImageSpec, VolumeRef
22
+ from sequence_base.contract.policy import KIND_NAME, ImageSpec, PolicyConstructor, VolumeRef
23
+ from sequence_base.construct import FORMS
22
24
 
23
25
  __all__ = [
24
26
  "ReplaySpec",
@@ -31,6 +33,27 @@ __all__ = [
31
33
  TASK_NAME = r"^[A-Za-z0-9][A-Za-z0-9._/:-]{0,127}$"
32
34
 
33
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
+
34
57
  def _check_tasks(tasks: dict, seeds: list, max_seconds) -> None:
35
58
  import re as _re
36
59
 
@@ -75,11 +98,11 @@ class ReplaySpec(BaseModel):
75
98
 
76
99
 
77
100
  class BenchmarkSpec(BaseModel):
78
- """What `@seq.benchmark(...)` and its marked methods add up to, as an eval runs it. `bindings` names the methods:
79
- RESET and STEP always, SETUP when it builds its worlds after the machine's snapshot - which a vectorized env, one
80
- stepping `worlds` worlds a call (@seq.concurrent(sizes=[N]) on its step), always does. `max_seconds` is an episode's
81
- time on the robot's clock, the same for every task or {task: seconds}; `assets` are versioned scene volumes mounted
82
- at /assets; `secrets` by name."""
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."""
83
106
 
84
107
  model_config = ConfigDict(extra="forbid")
85
108
 
@@ -89,7 +112,7 @@ class BenchmarkSpec(BaseModel):
89
112
  seeds: list[int] = Field(..., description="The seeds an episode starts at; trial k of a task runs seeds[k % len]")
90
113
  max_seconds: float | dict[str, float] = Field(..., description="An episode's time limit, or {task: seconds}")
91
114
  handler_ref: str
92
- bindings: dict[SeqFlags, str] = Field(..., description="SETUP / RESET / STEP -> method name")
115
+ bindings: dict[SeqFlags, str] = Field(..., description="RESET / STEP -> method name")
93
116
 
94
117
 
95
118
  worlds: int | None = Field(default=None, ge=1, le=MAX_ENVS)
@@ -106,6 +129,10 @@ class BenchmarkSpec(BaseModel):
106
129
 
107
130
  engine: str | None = Field(default=None, pattern=KIND_NAME)
108
131
 
132
+
133
+
134
+ constructor: PolicyConstructor | None = None
135
+
109
136
  @model_validator(mode="after")
110
137
  def _tasks(self) -> "BenchmarkSpec":
111
138
  _check_tasks(self.tasks, self.seeds, self.max_seconds)
@@ -113,15 +140,18 @@ class BenchmarkSpec(BaseModel):
113
140
 
114
141
  @model_validator(mode="after")
115
142
  def _bindings(self) -> "BenchmarkSpec":
143
+ if SeqFlags.SETUP in self.bindings:
144
+ raise ValueError(SETUP_GONE)
116
145
  bad = set(self.bindings) - set(BENCHMARK_BINDABLE)
117
146
  if bad:
118
147
  names = ", ".join(sorted(f.name or str(f.value) for f in bad))
119
- raise ValueError(f"a benchmark marks @seq.setup, @seq.reset and @seq.step only; got {names}")
148
+ raise ValueError(f"a benchmark marks @seq.reset and @seq.step only; got {names}")
120
149
  missing = [f.name for f in (SeqFlags.RESET, SeqFlags.STEP) if f not in self.bindings]
121
150
  if missing:
122
151
  raise ValueError(f"a benchmark marks @seq.reset and @seq.step; {', '.join(missing).lower()} is missing")
123
- if self.worlds is not None and SeqFlags.SETUP not in self.bindings:
124
- raise ValueError("a vectorized benchmark (@seq.concurrent on its step) builds its worlds in @seq.setup")
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}"}')
125
155
  return self
126
156
 
127
157
  @model_validator(mode="after")
@@ -132,7 +162,7 @@ class BenchmarkSpec(BaseModel):
132
162
 
133
163
  class BenchmarkRegisterRequest(BaseModel):
134
164
  """POST /v1/benchmarks - what `seq benchmark register` sends: the declaration, the code, and the parameters its
135
- @seq.setup takes beside worlds and policy (each settable by an eval's --param, with its type)."""
165
+ constructor takes with a default beside what init= hands it (each settable by an eval's --param, with its type)."""
136
166
 
137
167
  model_config = ConfigDict(extra="forbid")
138
168
 
@@ -172,9 +202,8 @@ class BenchmarkRegisterRequest(BaseModel):
172
202
  @model_validator(mode="after")
173
203
  def _param_names(self) -> "BenchmarkRegisterRequest":
174
204
  for name in self.params or {}:
175
- if not name.isidentifier() or name.startswith("_") or name in ("worlds", "policy"):
176
- raise ValueError(f"setup parameter {name!r} is not a public Python identifier other than worlds and "
177
- "policy")
205
+ if not name.isidentifier() or name.startswith("_"):
206
+ raise ValueError(f"parameter {name!r} is not a public Python identifier")
178
207
  return self
179
208
 
180
209
  @model_validator(mode="after")
@@ -54,6 +54,11 @@ class ConnectRequest(BaseModel):
54
54
  team: str | None = Field(default=None, description="the team this robot joins, by name")
55
55
  group: str | None = Field(default=None, description="its group in that team; None: the default group")
56
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
+
57
62
  @model_validator(mode="before")
58
63
  @classmethod
59
64
  def _retired(cls, data: Any) -> Any:
@@ -67,6 +72,8 @@ class ConnectRequest(BaseModel):
67
72
  if self.team is None:
68
73
  raise ValueError("a group is a team's: say the team too")
69
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")
70
77
  return self
71
78
 
72
79
 
@@ -116,6 +123,12 @@ class ConnectResponse(BaseModel):
116
123
 
117
124
  standby: Standby | None = None
118
125
 
126
+
127
+
128
+
129
+ standby_retry_s: float | None = Field(default=None, gt=0, le=3600,
130
+ description="no standby could be held yet: ask again in this many seconds")
131
+
119
132
  @model_validator(mode="before")
120
133
  @classmethod
121
134
  def _retired(cls, data: Any) -> Any:
@@ -62,7 +62,8 @@ class SeqFlags(IntFlag):
62
62
 
63
63
 
64
64
 
65
- BENCHMARK_BINDABLE: frozenset[SeqFlags] = frozenset({SeqFlags.SETUP, SeqFlags.RESET, SeqFlags.STEP})
65
+
66
+ BENCHMARK_BINDABLE: frozenset[SeqFlags] = frozenset({SeqFlags.RESET, SeqFlags.STEP})
66
67
 
67
68
 
68
69
  def seqflags_json_schema(contract_version: str) -> dict:
@@ -544,13 +544,15 @@ _MAKE = r"^(?:[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*:)?[A-Za-z_][A-
544
544
 
545
545
 
546
546
  class PolicyConstructor(BaseModel):
547
- """How the worker builds a policy class that names its own methods (`@seq.policy(infer="get_action", ...)`): its
548
- own constructor, or the factory it names (`make`: a classmethod of it, e.g. LeRobot's `from_pretrained`, or a
549
- module's function, "package.module:function", whose return is the instance its methods are called on), called
550
- with `args` and `kwargs` where `@seq.load` would run - the replica's cold start, the weights on disk. The values are
551
- the user's own, as JSON carries them, but for two forms the worker fills in first (sequence_base.construct.expand):
552
- a config file's contents, `{"$config": <its path from the class's file>, "$set": {<keys laid over it>}}`, YAML or
553
- JSON, and in any string `"{weights}"` (the weights directory) or `"{weights.<part>}"` (a named part's).
547
+ """How the worker builds a policy's, a team's or a benchmark's class (`init=`): its own constructor, or the factory
548
+ it names (`make`: a classmethod of it, e.g. LeRobot's `from_pretrained`, or a module's function,
549
+ "package.module:function", whose return is the instance its methods are called on), called with `args` and `kwargs`
550
+ - a policy's at the replica's cold start, the weights on disk, where one deployed before sequence-ai 0.51.1 ran its
551
+ `@seq.load`; a benchmark's after the machine's snapshot (BenchmarkSpec.constructor). The values are the user's own,
552
+ as JSON carries them, but for the forms the worker fills in first (sequence_base.construct.expand): a config file's
553
+ contents, `{"$config": <its path from the class's file>, "$set": {<keys laid over it>}}`, YAML or JSON, and in any
554
+ string a policy's `"{weights}"` (the weights directory) or `"{weights.<part>}"` (a named part's), a benchmark's
555
+ `"{num_worlds}"` (how many worlds this eval builds) or `"{policy.<field>}"` (what the evaluated policy declares).
554
556
 
555
557
  """
556
558
 
@@ -563,6 +565,10 @@ class PolicyConstructor(BaseModel):
563
565
  kwargs: dict[str, Any] = Field(default_factory=dict, description="keyword arguments")
564
566
 
565
567
 
568
+
569
+ _TEAM_BINDS = frozenset({SeqFlags.LOAD, SeqFlags.PLAN, SeqFlags.SHUTDOWN, SeqFlags.SAMPLE})
570
+
571
+
566
572
  class PolicyRegistration(BaseModel):
567
573
  """What the harness loader introspects: the spec, the SeqFlags→method-name bindings, and the
568
574
  `handler_ref` it importlib-loads the policy class from. From one of these the loader recovers which
@@ -596,15 +602,15 @@ class PolicyRegistration(BaseModel):
596
602
  def _a_team_plans(self) -> "PolicyRegistration":
597
603
 
598
604
 
605
+
606
+
599
607
  if self.spec.team is not None:
600
- other = sorted(f.name for f in self.bindings if f not in (SeqFlags.LOAD, SeqFlags.PLAN, SeqFlags.SHUTDOWN))
608
+ other = sorted(f.name for f in self.bindings if f not in _TEAM_BINDS)
601
609
  if other:
602
- raise ValueError(f"a team answers no act: it binds @seq.load, @seq.plan and @seq.shutdown alone, not "
610
+ raise ValueError(f"a team answers no act: it binds @seq.plan, @seq.shutdown and @seq.sample alone, not "
603
611
  f"{', '.join(other)}")
604
612
  if SeqFlags.PLAN not in self.bindings or self.plan_every_s is None:
605
613
  raise ValueError("a team plans: it binds one @seq.plan(every_s=...)")
606
- if self.constructor is not None:
607
- raise ValueError("a team is built as a policy with @seq.load is, not by a constructor")
608
614
  return self
609
615
 
610
616
  @model_validator(mode="after")
@@ -613,7 +619,8 @@ class PolicyRegistration(BaseModel):
613
619
  if SeqFlags.LOAD in self.bindings:
614
620
  raise ValueError("a policy built by its constructor (constructor) has no @seq.load: its constructor is "
615
621
  "its load")
616
- if SeqFlags.INFER not in self.bindings:
622
+ if SeqFlags.INFER not in self.bindings and self.spec.team is None:
623
+
617
624
  raise ValueError("a policy built by its constructor (constructor) names the method that answers an act "
618
625
  "(an INFER binding)")
619
626
  return self
@@ -66,5 +66,5 @@
66
66
  ],
67
67
  "title": "ActRequest",
68
68
  "type": "object",
69
- "x-contract-version": "0.5.32"
69
+ "x-contract-version": "0.5.33"
70
70
  }
@@ -75,5 +75,5 @@
75
75
  ],
76
76
  "title": "ActResponse",
77
77
  "type": "object",
78
- "x-contract-version": "0.5.32"
78
+ "x-contract-version": "0.5.33"
79
79
  }
@@ -99,5 +99,5 @@
99
99
  ],
100
100
  "title": "ActionChunk",
101
101
  "type": "object",
102
- "x-contract-version": "0.5.32"
102
+ "x-contract-version": "0.5.33"
103
103
  }
@@ -96,5 +96,5 @@
96
96
  ],
97
97
  "title": "ActionContract",
98
98
  "type": "object",
99
- "x-contract-version": "0.5.32"
99
+ "x-contract-version": "0.5.33"
100
100
  }
@@ -19,5 +19,5 @@
19
19
  ],
20
20
  "title": "ActionStep",
21
21
  "type": "object",
22
- "x-contract-version": "0.5.32"
22
+ "x-contract-version": "0.5.33"
23
23
  }
@@ -17,5 +17,5 @@
17
17
  ],
18
18
  "title": "AdapterPair",
19
19
  "type": "object",
20
- "x-contract-version": "0.5.32"
20
+ "x-contract-version": "0.5.33"
21
21
  }
@@ -23,5 +23,5 @@
23
23
  ],
24
24
  "title": "AddLocal",
25
25
  "type": "object",
26
- "x-contract-version": "0.5.32"
26
+ "x-contract-version": "0.5.33"
27
27
  }
@@ -36,5 +36,5 @@
36
36
  },
37
37
  "title": "Autoscaler",
38
38
  "type": "object",
39
- "x-contract-version": "0.5.32"
39
+ "x-contract-version": "0.5.33"
40
40
  }
@@ -561,7 +561,7 @@
561
561
  }
562
562
  },
563
563
  "additionalProperties": false,
564
- "description": "POST /v1/benchmarks - what `seq benchmark register` sends: the declaration, the code, and the parameters its\n@seq.setup takes beside worlds and policy (each settable by an eval's --param, with its type).",
564
+ "description": "POST /v1/benchmarks - what `seq benchmark register` sends: the declaration, the code, and the parameters its\nconstructor takes with a default beside what init= hands it (each settable by an eval's --param, with its type).",
565
565
  "properties": {
566
566
  "assets": {
567
567
  "items": {
@@ -766,5 +766,5 @@
766
766
  ],
767
767
  "title": "BenchmarkRegisterRequest",
768
768
  "type": "object",
769
- "x-contract-version": "0.5.32"
769
+ "x-contract-version": "0.5.33"
770
770
  }
@@ -234,6 +234,41 @@
234
234
  "title": "ImageStepUvPip",
235
235
  "type": "object"
236
236
  },
237
+ "PolicyConstructor": {
238
+ "additionalProperties": false,
239
+ "description": "How the worker builds a policy's, a team's or a benchmark's class (`init=`): its own constructor, or the factory\nit names (`make`: a classmethod of it, e.g. LeRobot's `from_pretrained`, or a module's function,\n\"package.module:function\", whose return is the instance its methods are called on), called with `args` and `kwargs`\n- a policy's at the replica's cold start, the weights on disk, where one deployed before sequence-ai 0.51.1 ran its\n`@seq.load`; a benchmark's after the machine's snapshot (BenchmarkSpec.constructor). The values are the user's own,\nas JSON carries them, but for the forms the worker fills in first (sequence_base.construct.expand): a config file's\ncontents, `{\"$config\": <its path from the class's file>, \"$set\": {<keys laid over it>}}`, YAML or JSON, and in any\nstring a policy's `\"{weights}\"` (the weights directory) or `\"{weights.<part>}\"` (a named part's), a benchmark's\n`\"{num_worlds}\"` (how many worlds this eval builds) or `\"{policy.<field>}\"` (what the evaluated policy declares).",
240
+ "properties": {
241
+ "args": {
242
+ "description": "positional arguments, in order",
243
+ "items": {},
244
+ "title": "Args",
245
+ "type": "array"
246
+ },
247
+ "kwargs": {
248
+ "additionalProperties": true,
249
+ "description": "keyword arguments",
250
+ "title": "Kwargs",
251
+ "type": "object"
252
+ },
253
+ "make": {
254
+ "anyOf": [
255
+ {
256
+ "maxLength": 512,
257
+ "pattern": "^(?:[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)*:)?[A-Za-z_][A-Za-z0-9_]*$",
258
+ "type": "string"
259
+ },
260
+ {
261
+ "type": "null"
262
+ }
263
+ ],
264
+ "default": null,
265
+ "description": "a classmethod or staticmethod that returns the instance, or a module's function, 'package.module:function' (contract 0.5.27); None: the class",
266
+ "title": "Make"
267
+ }
268
+ },
269
+ "title": "PolicyConstructor",
270
+ "type": "object"
271
+ },
237
272
  "Project": {
238
273
  "additionalProperties": false,
239
274
  "description": "A project's own dependency files, which the image installs from (`seq.Image.from_project`, or a policy that\ndeclares no image and whose project has them): each read from the deploy's code bundle. The platform turns them into\nthe install when the image builds - for Linux and the worker's card, whatever machine the author wrote them on: the\nlock's exact versions, else the declared ones held to the versions the author's machine has (`pins`), the local\npackages the code imports with their own, less `exclude`, then `extra`.",
@@ -400,7 +435,7 @@
400
435
  ],
401
436
  "title": "SeqFlags",
402
437
  "type": "integer",
403
- "x-contract-version": "0.5.32",
438
+ "x-contract-version": "0.5.33",
404
439
  "x-flag-names": {
405
440
  "ACTION": 1048576,
406
441
  "CHECK": 65536,
@@ -472,7 +507,7 @@
472
507
  }
473
508
  },
474
509
  "additionalProperties": false,
475
- "description": "What `@seq.benchmark(...)` and its marked methods add up to, as an eval runs it. `bindings` names the methods:\nRESET and STEP always, SETUP when it builds its worlds after the machine's snapshot - which a vectorized env, one\nstepping `worlds` worlds a call (@seq.concurrent(sizes=[N]) on its step), always does. `max_seconds` is an episode's\ntime on the robot's clock, the same for every task or {task: seconds}; `assets` are versioned scene volumes mounted\nat /assets; `secrets` by name.",
510
+ "description": "What `@seq.benchmark(...)` and its marked methods add up to, as an eval runs it. `bindings` names the methods,\nRESET and STEP; `constructor` the arguments its class is built with after the machine's snapshot (init=) - which a\nvectorized env, one stepping `worlds` worlds a call (@seq.concurrent(sizes=[N]) on its step), hands how many,\n\"{num_worlds}\". `max_seconds` is an episode's time on the robot's clock, the same for every task or {task: seconds};\n`assets` are versioned scene volumes mounted at /assets; `secrets` by name.",
476
511
  "properties": {
477
512
  "assets": {
478
513
  "items": {
@@ -485,7 +520,7 @@
485
520
  "additionalProperties": {
486
521
  "type": "string"
487
522
  },
488
- "description": "SETUP / RESET / STEP -> method name",
523
+ "description": "RESET / STEP -> method name",
489
524
  "propertyNames": {
490
525
  "enum": [
491
526
  "1",
@@ -508,6 +543,17 @@
508
543
  "title": "Bindings",
509
544
  "type": "object"
510
545
  },
546
+ "constructor": {
547
+ "anyOf": [
548
+ {
549
+ "$ref": "#/$defs/PolicyConstructor"
550
+ },
551
+ {
552
+ "type": "null"
553
+ }
554
+ ],
555
+ "default": null
556
+ },
511
557
  "cpu": {
512
558
  "anyOf": [
513
559
  {
@@ -656,5 +702,5 @@
656
702
  ],
657
703
  "title": "BenchmarkSpec",
658
704
  "type": "object",
659
- "x-contract-version": "0.5.32"
705
+ "x-contract-version": "0.5.33"
660
706
  }
@@ -40,5 +40,5 @@
40
40
  ],
41
41
  "title": "BenchmarkStatusResponse",
42
42
  "type": "object",
43
- "x-contract-version": "0.5.32"
43
+ "x-contract-version": "0.5.33"
44
44
  }
@@ -79,5 +79,5 @@
79
79
  ],
80
80
  "title": "BootstrapResponse",
81
81
  "type": "object",
82
- "x-contract-version": "0.5.32"
82
+ "x-contract-version": "0.5.33"
83
83
  }