sequence-base 0.4.15__tar.gz → 0.4.17__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 (108) hide show
  1. {sequence_base-0.4.15 → sequence_base-0.4.17}/PKG-INFO +1 -1
  2. {sequence_base-0.4.15 → sequence_base-0.4.17}/pyproject.toml +1 -1
  3. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/__init__.py +6 -4
  4. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/constants/version.py +3 -1
  5. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/__init__.py +7 -4
  6. sequence_base-0.4.17/sequence_base/contract/adapters.py +181 -0
  7. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/benchmarks.py +3 -4
  8. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/deployments.py +2 -26
  9. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/eval.py +5 -27
  10. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/flags.py +3 -6
  11. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/policy.py +4 -9
  12. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schema_export.py +2 -4
  13. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ActRequest.json +1 -1
  14. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ActResponse.json +1 -1
  15. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ActionChunk.json +1 -1
  16. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ActionContract.json +1 -1
  17. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ActionStep.json +1 -1
  18. sequence_base-0.4.17/sequence_base/schemas/AdapterPair.json +21 -0
  19. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/AddLocal.json +1 -1
  20. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Autoscaler.json +1 -13
  21. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/BenchmarkConditionSpec.json +1 -1
  22. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/BenchmarkRegisterRequest.json +2 -2
  23. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/BenchmarkSpec.json +4 -13
  24. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
  25. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/BootstrapResponse.json +1 -1
  26. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Camera.json +1 -1
  27. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/CodeBundle.json +1 -1
  28. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Concurrency.json +1 -1
  29. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Conditions.json +1 -1
  30. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ConnectRequest.json +1 -1
  31. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ConnectResponse.json +1 -1
  32. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/DeploymentAccepted.json +1 -1
  33. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/DeploymentPolicy.json +1 -13
  34. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/DeploymentRequest.json +2 -58
  35. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
  36. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Embodiment.json +1 -1
  37. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ErrorBody.json +1 -1
  38. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ErrorCodes.json +1 -1
  39. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Estimate.json +1 -1
  40. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
  41. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/EvalReport.json +1 -1
  42. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/EvalRequest.json +2 -32
  43. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/EvalResponse.json +1 -38
  44. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ImageFrame.json +1 -1
  45. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ImageSpec.json +1 -1
  46. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ImageStepAddLocal.json +1 -1
  47. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ImageStepApt.json +1 -1
  48. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ImageStepEnv.json +1 -1
  49. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ImageStepRun.json +1 -1
  50. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ImageStepTemplateFiles.json +1 -1
  51. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ImageStepUvPip.json +1 -1
  52. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/InferParam.json +1 -1
  53. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Lease.json +1 -1
  54. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/MetricAgg.json +1 -1
  55. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Observation.json +1 -1
  56. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ObservationContract.json +1 -1
  57. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/PolicyRegistration.json +5 -76
  58. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/PolicySpec.json +4 -72
  59. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Proprioception.json +1 -1
  60. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/RenderSpec.json +1 -1
  61. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/ReplaySpec.json +1 -1
  62. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Resources.json +2 -2
  63. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/RolloutResult.json +1 -1
  64. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/SecretCreateRequest.json +1 -1
  65. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/SecretInfo.json +1 -1
  66. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/SecretRef.json +1 -1
  67. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/SeqFlags.json +1 -3
  68. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Step.json +1 -1
  69. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/TaskAgg.json +1 -1
  70. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/TemplateFiles.json +1 -1
  71. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Timeouts.json +1 -1
  72. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/UploadTarget.json +1 -1
  73. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/Variant.json +1 -1
  74. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/VolumeFile.json +1 -1
  75. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/VolumeInfo.json +1 -1
  76. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/VolumeMount.json +1 -1
  77. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/VolumeRef.json +2 -2
  78. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
  79. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
  80. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/schemas/WarmingError.json +1 -1
  81. sequence_base-0.4.15/sequence_base/schemas/ClampInfo.json +0 -27
  82. sequence_base-0.4.15/sequence_base/schemas/VolumeAttach.json +0 -26
  83. {sequence_base-0.4.15 → sequence_base-0.4.17}/.gitignore +0 -0
  84. {sequence_base-0.4.15 → sequence_base-0.4.17}/README.md +0 -0
  85. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/auth.py +0 -0
  86. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/codec/__init__.py +0 -0
  87. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/codec/image.py +0 -0
  88. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/constants/__init__.py +0 -0
  89. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/constants/headers.py +0 -0
  90. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/constants/limits.py +0 -0
  91. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/accounts.py +0 -0
  92. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/act.py +0 -0
  93. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/connect.py +0 -0
  94. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/errors.py +0 -0
  95. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/io.py +0 -0
  96. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/templates.py +0 -0
  97. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/volumes.py +0 -0
  98. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/contract/wire.py +0 -0
  99. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/errors/__init__.py +0 -0
  100. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/errors/transport.py +0 -0
  101. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/obs/__init__.py +0 -0
  102. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/obs/log.py +0 -0
  103. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/obs/rid.py +0 -0
  104. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/presets/__init__.py +0 -0
  105. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/presets/frames.py +0 -0
  106. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/presets/robot_arm.py +0 -0
  107. {sequence_base-0.4.15 → sequence_base-0.4.17}/sequence_base/telemetry/__init__.py +0 -0
  108. {sequence_base-0.4.15 → sequence_base-0.4.17}/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.15
3
+ Version: 0.4.17
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.15"
3
+ version = "0.4.17"
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"]
@@ -33,6 +33,8 @@ from sequence_base.contract import (
33
33
  parse_vol,
34
34
  TEMPLATE_SCHEME,
35
35
  parse_template,
36
+ AdapterPair,
37
+ parse_pairs,
36
38
  ActionChunk,
37
39
  ActionContract,
38
40
  ActionSpace,
@@ -43,7 +45,6 @@ from sequence_base.contract import (
43
45
  BenchmarkStatusResponse,
44
46
  BenchmarkSpec,
45
47
  BootstrapResponse,
46
- ClampInfo,
47
48
  CodeBundle,
48
49
  Concurrency,
49
50
  Conditions,
@@ -90,7 +91,6 @@ from sequence_base.contract import (
90
91
  TaskAgg,
91
92
  Timeouts,
92
93
  Variant,
93
- VolumeAttach,
94
94
  VolumeMount,
95
95
  VolumeRef,
96
96
  WarmingError,
@@ -135,13 +135,13 @@ __all__ = [
135
135
  "PolicySpec", "PolicyRegistration",
136
136
 
137
137
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
138
- "Resources", "Autoscaler", "Timeouts", "VolumeAttach", "DeploymentPolicy", "InferParam", "DeploymentRequest",
138
+ "Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
139
139
  "DeploymentAccepted", "DeploymentState", "DeploymentStatusResponse",
140
140
 
141
141
  "Lease", "ConnectRequest", "ConnectResponse",
142
142
 
143
143
  "Variant", "Conditions", "RenderSpec", "ReplaySpec", "Step", "BenchmarkSpec", "BenchmarkConditionSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
144
- "EvalRequest", "ClampInfo", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
144
+ "EvalRequest", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
145
145
  "EvalStatus", "TaskAgg", "MetricAgg", "EvalReport",
146
146
 
147
147
  "SecretRef", "SecretCreateRequest", "SecretInfo", "BootstrapResponse",
@@ -150,6 +150,8 @@ __all__ = [
150
150
 
151
151
  "TEMPLATE_SCHEME", "parse_template",
152
152
 
153
+ "AdapterPair", "parse_pairs",
154
+
153
155
  "ErrorCode", "ErrorBody", "WarmingError", "ERRORS", "ErrorSpec", "code_for_status", "CONTRACT_VERSION",
154
156
  "MAX_ENVS", "MAX_USD", "TASK_DESCRIPTION_MAX",
155
157
 
@@ -4,7 +4,9 @@
4
4
 
5
5
 
6
6
 
7
- CONTRACT_VERSION = "0.5.14"
7
+ CONTRACT_VERSION = "0.5.16"
8
+
9
+
8
10
 
9
11
 
10
12
 
@@ -53,7 +53,6 @@ from sequence_base.contract.deployments import (
53
53
  DeploymentStatusResponse,
54
54
  Resources,
55
55
  Timeouts,
56
- VolumeAttach,
57
56
  )
58
57
 
59
58
 
@@ -72,7 +71,6 @@ from sequence_base.contract.benchmarks import (
72
71
  Variant,
73
72
  )
74
73
  from sequence_base.contract.eval import (
75
- ClampInfo,
76
74
  Estimate,
77
75
  EvalDryRunResponse,
78
76
  EvalReport,
@@ -96,6 +94,9 @@ from sequence_base.contract.accounts import (
96
94
  from sequence_base.contract.templates import TEMPLATE_SCHEME, parse_template
97
95
 
98
96
 
97
+ from sequence_base.contract.adapters import AdapterPair, parse_pairs
98
+
99
+
99
100
  from sequence_base.contract.volumes import (
100
101
  MAX_FILES,
101
102
  MAX_UPLOAD_BYTES,
@@ -123,13 +124,13 @@ __all__ = [
123
124
  "PolicySpec", "PolicyRegistration",
124
125
 
125
126
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
126
- "Resources", "Autoscaler", "Timeouts", "VolumeAttach", "DeploymentPolicy", "InferParam", "DeploymentRequest",
127
+ "Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
127
128
  "DeploymentAccepted", "DeploymentState", "DeploymentStatusResponse",
128
129
 
129
130
  "Lease", "ConnectRequest", "ConnectResponse",
130
131
 
131
132
  "Variant", "Conditions", "RenderSpec", "ReplaySpec", "Step", "BenchmarkSpec", "BenchmarkConditionSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
132
- "EvalRequest", "ClampInfo", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
133
+ "EvalRequest", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
133
134
  "EvalStatus", "TaskAgg", "MetricAgg", "EvalReport",
134
135
 
135
136
  "SecretRef", "SecretCreateRequest", "SecretInfo", "BootstrapResponse",
@@ -139,5 +140,7 @@ __all__ = [
139
140
 
140
141
  "TEMPLATE_SCHEME", "parse_template",
141
142
 
143
+ "AdapterPair", "parse_pairs",
144
+
142
145
  "ErrorCode", "ErrorBody", "WarmingError", "ERRORS", "ErrorSpec", "code_for_status",
143
146
  ]
@@ -0,0 +1,181 @@
1
+ """
2
+ An adapter's pairs - the benchmark → policy pairs a registered adapter converts between.
3
+
4
+ In one sentence: an adapter's author writes in its code which pairs its conversion is for - `@seq.adapter(pairs=[
5
+ (benchmark, policy), ...])`, each side an account's name, `template:<name>` (anything made from that template, at any
6
+ version) or `*` (any one), or `pairs="any"` for every pair → `seq deploy` registers them with the version → as an eval
7
+ starts, the gateway matches the eval's benchmark and policy against them (match) and refuses the rest before anything
8
+ runs. The rules are defined once, here: the SDK reads them (the decorator, `seq adapter check`) and so does the
9
+ gateway (registration, the eval's check, which adapters fit a pair), so what can be chosen is what runs.
10
+ WHY in the code, with the version: which pairs a conversion is for is part of the conversion - changing them is a new
11
+ version, and an eval keeps the version it ran. WHY by name and never by version: a benchmark's or a policy's I/O
12
+ seldom changes with its version, and pinned pairs would make every upgrade a new adapter. WHY a pair need not exist
13
+ yet: an adapter may come before the policy it feeds; names are matched as an eval starts.
14
+ {the user 10-19, through TEST USER: "you need to specify which benchmark and which policy you want the conversion to
15
+ be and then you can specify multiple … you can only apply those that are stated here as the pairs"; "We should have
16
+ the option to open to all"}
17
+
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import re
22
+ from typing import Any, Iterable, Literal, Union
23
+
24
+ from pydantic import BaseModel, ConfigDict
25
+
26
+ ANY = "any"
27
+ WILDCARD = "*"
28
+ TEMPLATE = "template:"
29
+ MAX_PAIRS = 64
30
+
31
+
32
+
33
+ _NAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,63}")
34
+ _TEMPLATE_NAME = re.compile(r"[a-z0-9][a-z0-9-]{0,63}")
35
+
36
+
37
+ class AdapterPair(BaseModel):
38
+ """One pair an adapter converts between: its benchmark side and its policy side, each an account's name,
39
+ `template:<name>` or `*`."""
40
+
41
+ model_config = ConfigDict(extra="forbid", frozen=True)
42
+
43
+ benchmark: str
44
+ policy: str
45
+
46
+
47
+ Pairs = Union[Literal["any"], list[AdapterPair]]
48
+
49
+ _EXAMPLE = 'pairs=[("libero-10", "my-pi05"), ("template:libero-spatial", "*")], or pairs="any"'
50
+
51
+
52
+ def _side(value: Any, which: str, i: int) -> str:
53
+ if not isinstance(value, str) or not value.strip():
54
+ raise ValueError(f"pair {i}: its {which} is {value!r}, not a name")
55
+ side = value.strip()
56
+ if side == WILDCARD:
57
+ return side
58
+ if side.startswith(TEMPLATE):
59
+ if not _TEMPLATE_NAME.fullmatch(side[len(TEMPLATE):]):
60
+ raise ValueError(f"pair {i}: {which} {value!r}: a template is template:<name>, the name `seq init` takes "
61
+ "(lowercase letters, digits or '-'), e.g. template:libero-10")
62
+ return side
63
+ if not _NAME.fullmatch(side):
64
+ raise ValueError(f"pair {i}: {which} {value!r} is no name: 1-64 letters, digits, '.', '_' or '-', starting "
65
+ "with a letter or digit - or template:<name>, or *")
66
+ return side
67
+
68
+
69
+ def parse_pairs(value: Any) -> Pairs:
70
+ """`"any"`, or the pairs as [(benchmark, policy), ...] or [{"benchmark", "policy"}, ...] → "any" or [AdapterPair,
71
+ ...] in their order, each once. Raises ValueError saying which pair and which side is wrong; None and [] are wrong
72
+ too - an adapter says which pairs it is for, or "any"."""
73
+ if isinstance(value, str):
74
+ if value == ANY:
75
+ return ANY
76
+ raise ValueError(f"pairs is {value!r}: write the (benchmark, policy) pairs the adapter converts between - "
77
+ f"{_EXAMPLE}")
78
+ if value is None:
79
+ raise ValueError(f"the adapter declares no pairs: say which benchmark → policy pairs it converts between - "
80
+ f"{_EXAMPLE}")
81
+ try:
82
+ items = list(value)
83
+ except TypeError:
84
+ raise ValueError(f"pairs is {value!r}, not a list of (benchmark, policy) pairs - {_EXAMPLE}") from None
85
+ if not items:
86
+ raise ValueError(f"pairs is empty: name at least one (benchmark, policy) pair - {_EXAMPLE}")
87
+ out: list[AdapterPair] = []
88
+ for i, item in enumerate(items, 1):
89
+ if isinstance(item, AdapterPair):
90
+ bench, policy = item.benchmark, item.policy
91
+ elif isinstance(item, dict) and set(item) == {"benchmark", "policy"}:
92
+ bench, policy = item["benchmark"], item["policy"]
93
+ elif isinstance(item, (list, tuple)) and len(item) == 2:
94
+ bench, policy = item
95
+ else:
96
+ raise ValueError(f"pair {i} is {item!r}: a pair is (benchmark, policy)")
97
+ pair = AdapterPair(benchmark=_side(bench, "benchmark", i), policy=_side(policy, "policy", i))
98
+ if pair not in out:
99
+ out.append(pair)
100
+ if len(out) > MAX_PAIRS:
101
+ raise ValueError(f"{len(out)} pairs; at most {MAX_PAIRS} - template:<name> or * names many at once")
102
+ return out
103
+
104
+
105
+ def as_json(pairs: Pairs | None) -> str | list[dict] | None:
106
+ """The pairs as the wire, the API and the table carry them: "any", [{"benchmark", "policy"}, ...], or None (a
107
+ version registered before pairs)."""
108
+ if pairs is None or pairs == ANY:
109
+ return pairs
110
+ return [p.model_dump() for p in pairs]
111
+
112
+
113
+ def _stored(pairs: Any) -> Pairs | None:
114
+ """Pairs as kept (as_json's form) or as parsed; None for none - never raises on what parse_pairs once accepted."""
115
+ if pairs is None or pairs == ANY:
116
+ return pairs
117
+ return [p if isinstance(p, AdapterPair) else AdapterPair(**p) for p in pairs]
118
+
119
+
120
+ def image_templates(image: Any) -> set[str]:
121
+ """The templates an image installs the files of - each `template_files` step's template, the line `seq init` writes
122
+ into a template's entry file and a user who edits the code keeps: what makes a benchmark or a policy one of the
123
+ template's for `template:<name>`. `image`: an ImageSpec, or its dict as a request or a stored spec carries it."""
124
+ if image is None:
125
+ return set()
126
+ steps = image.get("steps") if isinstance(image, dict) else getattr(image, "steps", None)
127
+ out: set[str] = set()
128
+ for step in steps or ():
129
+ files = step.get("template_files") if isinstance(step, dict) else getattr(step, "template_files", None)
130
+ name = files.get("template") if isinstance(files, dict) else getattr(files, "template", None)
131
+ if isinstance(name, str) and name:
132
+ out.add(name)
133
+ return out
134
+
135
+
136
+ def side_matches(side: str, name: str, templates: Iterable[str] = ()) -> bool:
137
+ """Whether one side of a pair is this benchmark or policy: `*`; `template:<t>` for one made from t; or its name."""
138
+ if side == WILDCARD:
139
+ return True
140
+ if side.startswith(TEMPLATE):
141
+ return side[len(TEMPLATE):] in set(templates or ())
142
+ return side == name
143
+
144
+
145
+ def _rank_side(side: str) -> int:
146
+ return 3 if side == WILDCARD else 1 if side.startswith(TEMPLATE) else 0
147
+
148
+
149
+ def rank(hit: AdapterPair | str) -> int:
150
+ """How specific a matching pair is, the most specific lowest: name and name 0, name and template 1, template and
151
+ template 2, a side `*` 3 and over, "any" 7 - the order an eval's adapters are offered in."""
152
+ if hit == ANY:
153
+ return 7
154
+ return _rank_side(hit.benchmark) + _rank_side(hit.policy)
155
+
156
+
157
+ def match(pairs: Any, benchmark: tuple[str, Iterable[str]],
158
+ policy: tuple[str, Iterable[str]]) -> AdapterPair | str | None:
159
+ """The pair of `pairs` that lets `benchmark` and `policy` - each (its name in the account, the templates it is
160
+ made from) - run with the adapter: the most specific one (rank); "any" for an adapter open to every pair; None
161
+ when none does, and for no pairs (a version registered before pairs)."""
162
+ pairs = _stored(pairs)
163
+ if pairs is None:
164
+ return None
165
+ if pairs == ANY:
166
+ return ANY
167
+ (bench_name, bench_templates), (policy_name, policy_templates) = benchmark, policy
168
+ hits = [p for p in pairs if side_matches(p.benchmark, bench_name, bench_templates)
169
+ and side_matches(p.policy, policy_name, policy_templates)]
170
+ return min(hits, key=rank) if hits else None
171
+
172
+
173
+ def describe(pairs: Any) -> list[str]:
174
+ """The pairs as lines a person reads: ["any"], or ["template:libero-10 → template:pi05-libero", ...]; [] for
175
+ none."""
176
+ pairs = _stored(pairs)
177
+ if pairs is None:
178
+ return []
179
+ if pairs == ANY:
180
+ return [ANY]
181
+ return [f"{p.benchmark} → {p.policy}" for p in pairs]
@@ -113,8 +113,7 @@ class BenchmarkSpec(BaseModel):
113
113
  ⑦: a benchmark also references secrets by name). `assets` are versioned scene/mesh/URDF/MJCF/USD volumes
114
114
  the worker mounts at /assets (reusing S2's Volume mechanism). `render` optionally declares the mp4.
115
115
  `max_steps` is the platform SAFETY cap (cost backstop, records `truncated` — NOT semantic termination;
116
- `done` is decided by @seq.check). `bindings` covers SETUP / RESET / CHECK / SCORE (ROLLOUT is deprecated
117
- and must not appear)."""
116
+ `done` is decided by @seq.check). `bindings` covers SETUP / RESET / CHECK / SCORE (+ OBSERVE / STEP)."""
118
117
 
119
118
  model_config = ConfigDict(extra="forbid")
120
119
 
@@ -124,7 +123,8 @@ class BenchmarkSpec(BaseModel):
124
123
  max_steps: int = Field(..., description="Platform safety cap; truncates and records truncated")
125
124
  handler_ref: str
126
125
  bindings: dict[SeqFlags, str] = Field(..., description="SETUP / RESET / CHECK / SCORE (+ OBSERVE / STEP) → method name")
127
- gpu: str | list[str] | None = None
126
+
127
+ gpu: str | None = None
128
128
 
129
129
  envs: int | None = Field(default=None, ge=1, le=MAX_ENVS)
130
130
  assets: list[VolumeRef] = Field(default_factory=list)
@@ -146,7 +146,6 @@ class BenchmarkSpec(BaseModel):
146
146
  names = ", ".join(sorted(f.name or str(f.value) for f in bad))
147
147
  raise ValueError(
148
148
  f"benchmark bindings may only cover SETUP / RESET / CHECK / SCORE / OBSERVE / STEP; got disallowed {names}"
149
- " (ROLLOUT is deprecated and must not be bound)"
150
149
  )
151
150
  batched = set(self.bindings) & set(BENCHMARK_BATCHED)
152
151
  if batched and batched != set(BENCHMARK_BATCHED):
@@ -36,7 +36,6 @@ __all__ = [
36
36
  "Resources",
37
37
  "Autoscaler",
38
38
  "Timeouts",
39
- "VolumeAttach",
40
39
  "DeploymentPolicy",
41
40
  "DeploymentRequest",
42
41
  "DeploymentAccepted",
@@ -46,7 +45,8 @@ __all__ = [
46
45
 
47
46
 
48
47
  class Resources(BaseModel):
49
- """The hardware a worker runs on. `gpu` is list[str] (the PolicySpec `str | list[str]` normalized)."""
48
+ """The hardware a worker runs on. `gpu` is list[str], the PolicySpec's one card as a list of one - the shape a
49
+ deployment's spec is kept in, so one stored from before a list was refused (0.4.15) still reads."""
50
50
 
51
51
  model_config = ConfigDict(extra="forbid")
52
52
 
@@ -83,9 +83,6 @@ class Autoscaler(BaseModel):
83
83
  min_containers: int = 0
84
84
 
85
85
 
86
- max_containers: int | None = None
87
-
88
-
89
86
  max_gpus: int | None = Field(default=None, ge=1, le=256)
90
87
  buffer_containers: int | None = None
91
88
  scaledown_window: int = 600
@@ -108,21 +105,6 @@ class Timeouts(BaseModel):
108
105
  startup: int = Field(..., ge=60, le=18000)
109
106
 
110
107
 
111
- class VolumeAttach(BaseModel):
112
- """A non-weights volume mount in the deploy body (goal 7). Distinct from `weights` (a `VolumeMount`,
113
- a provisioned pull at the reserved `/data`): an explicit `volumes=` attachment names a persistent
114
- volume by `vol://<name>` ref, pins its container `mount`, and carries the `create_if_missing`
115
- provisioning intent so the gateway either binds an existing volume or creates it — the semantic the
116
- wire `VolumeMount` deliberately does not carry. Not reused/extended from `VolumeMount` because
117
- `create_if_missing` is meaningless for the weights pull."""
118
-
119
- model_config = ConfigDict(extra="forbid")
120
-
121
- uri: str
122
- mount: str
123
- create_if_missing: bool
124
-
125
-
126
108
  class InferParam(BaseModel):
127
109
  """One inference parameter the policy's `@seq.infer` declares — `def infer(self, obs, *, num_steps: int = 10)`
128
110
  — with its type and default. An /act or an eval may set it; the worker passes only declared names, cast to
@@ -190,9 +172,6 @@ class DeploymentPolicy(BaseModel):
190
172
  handler_ref: str
191
173
  action: ActionContract | None = None
192
174
  streaming: bool = False
193
-
194
-
195
- frames_per_chunk: int | None = None
196
175
  secrets: list[SecretRef] = Field(default_factory=list)
197
176
 
198
177
 
@@ -289,9 +268,6 @@ class DeploymentRequest(BaseModel):
289
268
 
290
269
 
291
270
  image_context: CodeBundle | None = None
292
-
293
-
294
- volumes: list[VolumeAttach] = Field(default_factory=list)
295
271
  autoscaler: Autoscaler = Field(default_factory=Autoscaler)
296
272
 
297
273
  concurrency: Concurrency = Field(default_factory=Concurrency)
@@ -18,7 +18,6 @@ from sequence_base.constants.limits import MAX_ENVS, MAX_USD
18
18
 
19
19
  __all__ = [
20
20
  "EvalRequest",
21
- "ClampInfo",
22
21
  "ArmReport",
23
22
  "EvalTiming",
24
23
  "Estimate",
@@ -45,10 +44,11 @@ class EvalRequest(BaseModel):
45
44
  """`POST /v1/eval`. A real eval names `model` + `benchmark`; a rerun (`rerun_of` / `rerun_unsuccessful_of`) may
46
45
  leave them out and take the earlier eval's. `dry_run` selects the estimate-only response shape below.
47
46
 
48
- The one body the platform accepts: the current knobs, plus the two an older SDK still sends that are refused with
49
- the way on (`benchmark_gpu`, `region`) - never silently dropped. sequence-ai <= 0.28's `gpus` / `envs_per_gpu` are
50
- refused as any field this body does not define, never mapped: no alias for an old name. The gateway inherits this
51
- model rather than keeping its own."""
47
+ The one body the platform accepts, the newest SDK's: the names an older SDK sent (`gpus`, `envs_per_gpu`,
48
+ `benchmark_gpu`, `region`) are gone, refused as any field this body does not define, since every client the
49
+ platform serves is the newest (the user 10-19: "This is the latest version that we want everything to be based on,
50
+ so delete all the old stuff"). The gateway inherits this model rather than keeping its own.
51
+ """
52
52
 
53
53
 
54
54
 
@@ -70,12 +70,6 @@ class EvalRequest(BaseModel):
70
70
  policy_machines: int | None = Field(default=None, ge=1, le=64)
71
71
  policy_gpu: str | None = Field(default=None, max_length=32)
72
72
  benchmark_machines: int | None = Field(default=None, ge=1, le=64)
73
-
74
-
75
-
76
- benchmark_gpu: str | None = Field(default=None, max_length=32, json_schema_extra={"deprecated": True},
77
- description="Not accepted: an eval runs on the card its benchmark was registered "
78
- "on, so a card is refused. Omit it (an older SDK still sends it).")
79
73
  envs: int | None = Field(default=None, ge=1)
80
74
  adapter: str | None = Field(default=None, max_length=200)
81
75
 
@@ -107,11 +101,6 @@ class EvalRequest(BaseModel):
107
101
 
108
102
  replay: Literal["3d", "none"] | None = None
109
103
 
110
-
111
- region: str | None = Field(default=None, max_length=32, json_schema_extra={"deprecated": True},
112
- description="Not accepted: an eval's machines run wherever there are cards, so a region "
113
- "is refused. Omit it (an older SDK still sends it).")
114
-
115
104
  @field_validator("envs")
116
105
  @classmethod
117
106
  def _envs_cap(cls, v: int | None) -> int | None:
@@ -141,16 +130,6 @@ class EvalRequest(BaseModel):
141
130
  return self
142
131
 
143
132
 
144
- class ClampInfo(BaseModel):
145
- """Why the granted GPU count was clamped below the request — structured, not a bare value."""
146
-
147
- model_config = ConfigDict(extra="forbid")
148
-
149
- limit: str = Field(..., description="Which limit bound it, e.g. 'account_quota'")
150
- requested: int
151
- granted: int
152
-
153
-
154
133
  class Estimate(BaseModel):
155
134
  model_config = ConfigDict(extra="forbid")
156
135
 
@@ -175,7 +154,6 @@ class EvalResponse(BaseModel):
175
154
  eval_id: str
176
155
  gpus_granted: int
177
156
  estimate: Estimate
178
- clamped_by: ClampInfo | None = None
179
157
 
180
158
  queue: EvalQueue | None = None
181
159
 
@@ -9,12 +9,12 @@ plan step; and the benchmark SETUP / RESET / CHECK / SCORE quartet.
9
9
  eval-raw revision: the platform provides the reset→step→check loop as a tool; the user defines the goal
10
10
  through four hooks — SETUP (load sim + assets), RESET (build one episode's env), CHECK (per-step: read raw
11
11
  env state → done + user-named metrics), SCORE (cross-episode aggregate). RESET=32768 and CHECK=65536 are
12
- the new hook bits. The old single-hook `ROLLOUT` is DEPRECATED: bit 8192 stays reserved (never reused for
13
- another member) but appears in NO bindings — the setup/reset/check/score model replaces it.
12
+ the new hook bits. Bit 8192 was the old single-hook ROLLOUT, gone with every SDK that knew it (sequence-base 0.4.15):
13
+ it stays unused, never given to another member, so nothing serialized before reads as a hook it never was.
14
14
 
15
15
  The exact bit values are the contract: a registration serialized by one version must decode the same in
16
16
  another. A member taking the wrong value, WARMUP collapsing back into LOAD, RESET/CHECK overlapping an
17
- existing bit, or ROLLOUT being re-bound is caught bit-by-bit (see tests), not waved away with "an IntFlag
17
+ existing bit, or bit 8192 being given out again is caught bit-by-bit (see tests), not waved away with "an IntFlag
18
18
  is naturally bit-composable".
19
19
  """
20
20
  from __future__ import annotations
@@ -48,9 +48,6 @@ class SeqFlags(IntFlag):
48
48
 
49
49
 
50
50
 
51
- ROLLOUT = 8192
52
-
53
-
54
51
 
55
52
 
56
53
  BENCHMARK_BINDABLE: frozenset[SeqFlags] = frozenset(
@@ -172,7 +172,7 @@ class VolumeMount(BaseModel):
172
172
 
173
173
 
174
174
  class VolumeRef(BaseModel):
175
- """PolicySpec.volumes value — at least a `uri`; a `mount` may pin the container path."""
175
+ """A volume a benchmark mounts (BenchmarkSpec.assets): at least a `uri`; a `mount` may pin the container path."""
176
176
 
177
177
  model_config = ConfigDict(extra="forbid")
178
178
 
@@ -243,17 +243,15 @@ class PolicySpec(BaseModel):
243
243
  model_config = ConfigDict(extra="forbid")
244
244
 
245
245
  name: str
246
- gpu: str | list[str] = Field(..., description="A GPU type, or a list = cross-provider fallback")
246
+
247
+
248
+ gpu: str = Field(..., description='The one card a worker runs on: "L4", "H100:2" for two of one type, or "cpu"')
247
249
  image: ImageSpec
248
250
 
249
251
  weights: str | None = Field(None, description="Weights URI: template:// (an official template), hf:// or vol:// "
250
252
  "(an uploaded volume); none for a policy without a checkpoint")
251
- volumes: dict[str, VolumeRef] = Field(default_factory=dict)
252
253
  secrets: list[SecretRef] = Field(default_factory=list)
253
254
  min_containers: int = 0
254
-
255
-
256
- max_containers: int | None = None
257
255
  max_gpus: int | None = Field(default=None, ge=1, le=256)
258
256
  buffer_containers: int | None = None
259
257
  scaledown_window: int = 600
@@ -262,9 +260,6 @@ class PolicySpec(BaseModel):
262
260
  concurrency: Concurrency | None = None
263
261
  streaming: bool = False
264
262
 
265
-
266
- frames_per_chunk: int | None = None
267
-
268
263
  action: ActionContract | None = None
269
264
 
270
265
  observation: ObservationContract | None = None
@@ -56,8 +56,8 @@ from sequence_base.contract.deployments import (
56
56
  DeploymentStatusResponse,
57
57
  Resources,
58
58
  Timeouts,
59
- VolumeAttach,
60
59
  )
60
+ from sequence_base.contract.adapters import AdapterPair
61
61
  from sequence_base.contract.errors import ErrorBody, WarmingError, error_codes_json_schema
62
62
  from sequence_base.contract.volumes import (
63
63
  UploadTarget,
@@ -67,7 +67,6 @@ from sequence_base.contract.volumes import (
67
67
  VolumeUploadResponse,
68
68
  )
69
69
  from sequence_base.contract.eval import (
70
- ClampInfo,
71
70
  Estimate,
72
71
  EvalDryRunResponse,
73
72
  EvalReport,
@@ -136,7 +135,6 @@ WIRE_MODELS: dict[str, type[BaseModel]] = {
136
135
  "Resources": Resources,
137
136
  "Autoscaler": Autoscaler,
138
137
  "Timeouts": Timeouts,
139
- "VolumeAttach": VolumeAttach,
140
138
  "CodeBundle": CodeBundle,
141
139
  "DeploymentPolicy": DeploymentPolicy,
142
140
  "InferParam": InferParam,
@@ -159,7 +157,6 @@ WIRE_MODELS: dict[str, type[BaseModel]] = {
159
157
  "BenchmarkStatusResponse": BenchmarkStatusResponse,
160
158
 
161
159
  "EvalRequest": EvalRequest,
162
- "ClampInfo": ClampInfo,
163
160
  "Estimate": Estimate,
164
161
  "EvalResponse": EvalResponse,
165
162
  "EvalDryRunResponse": EvalDryRunResponse,
@@ -167,6 +164,7 @@ WIRE_MODELS: dict[str, type[BaseModel]] = {
167
164
  "TaskAgg": TaskAgg,
168
165
  "MetricAgg": MetricAgg,
169
166
  "EvalReport": EvalReport,
167
+ "AdapterPair": AdapterPair,
170
168
 
171
169
  "SecretRef": SecretRef,
172
170
  "SecretCreateRequest": SecretCreateRequest,
@@ -79,5 +79,5 @@
79
79
  ],
80
80
  "title": "ActRequest",
81
81
  "type": "object",
82
- "x-contract-version": "0.5.14"
82
+ "x-contract-version": "0.5.16"
83
83
  }
@@ -89,5 +89,5 @@
89
89
  ],
90
90
  "title": "ActResponse",
91
91
  "type": "object",
92
- "x-contract-version": "0.5.14"
92
+ "x-contract-version": "0.5.16"
93
93
  }
@@ -99,5 +99,5 @@
99
99
  ],
100
100
  "title": "ActionChunk",
101
101
  "type": "object",
102
- "x-contract-version": "0.5.14"
102
+ "x-contract-version": "0.5.16"
103
103
  }
@@ -84,5 +84,5 @@
84
84
  ],
85
85
  "title": "ActionContract",
86
86
  "type": "object",
87
- "x-contract-version": "0.5.14"
87
+ "x-contract-version": "0.5.16"
88
88
  }
@@ -19,5 +19,5 @@
19
19
  ],
20
20
  "title": "ActionStep",
21
21
  "type": "object",
22
- "x-contract-version": "0.5.14"
22
+ "x-contract-version": "0.5.16"
23
23
  }
@@ -0,0 +1,21 @@
1
+ {
2
+ "additionalProperties": false,
3
+ "description": "One pair an adapter converts between: its benchmark side and its policy side, each an account's name,\n`template:<name>` or `*`.",
4
+ "properties": {
5
+ "benchmark": {
6
+ "title": "Benchmark",
7
+ "type": "string"
8
+ },
9
+ "policy": {
10
+ "title": "Policy",
11
+ "type": "string"
12
+ }
13
+ },
14
+ "required": [
15
+ "benchmark",
16
+ "policy"
17
+ ],
18
+ "title": "AdapterPair",
19
+ "type": "object",
20
+ "x-contract-version": "0.5.16"
21
+ }
@@ -23,5 +23,5 @@
23
23
  ],
24
24
  "title": "AddLocal",
25
25
  "type": "object",
26
- "x-contract-version": "0.5.14"
26
+ "x-contract-version": "0.5.16"
27
27
  }