sequence-base 0.3.26__tar.gz → 0.3.28__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. {sequence_base-0.3.26 → sequence_base-0.3.28}/PKG-INFO +1 -1
  2. sequence_base-0.3.28/pyproject.toml +20 -0
  3. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/__init__.py +2 -25
  4. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/codec/image.py +2 -17
  5. sequence_base-0.3.28/sequence_base/constants/env.py +7 -0
  6. sequence_base-0.3.28/sequence_base/constants/headers.py +4 -0
  7. sequence_base-0.3.28/sequence_base/constants/version.py +3 -0
  8. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/__init__.py +0 -19
  9. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/accounts.py +1 -10
  10. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/act.py +1 -1
  11. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/benchmarks.py +5 -32
  12. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/connect.py +3 -6
  13. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/deployments.py +6 -44
  14. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/errors.py +4 -13
  15. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/eval.py +4 -35
  16. sequence_base-0.3.28/sequence_base/contract/flags.py +65 -0
  17. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/io.py +2 -2
  18. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/policy.py +11 -37
  19. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/templates.py +0 -2
  20. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/volumes.py +0 -5
  21. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/contract/wire.py +1 -3
  22. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/obs/rid.py +0 -3
  23. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/presets/__init__.py +1 -1
  24. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/presets/robot_arm.py +3 -31
  25. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schema_export.py +0 -12
  26. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ActRequest.json +1 -1
  27. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ActResponse.json +1 -1
  28. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ActionChunk.json +2 -2
  29. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ActionContract.json +2 -2
  30. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ActionStep.json +1 -1
  31. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/AdapterTransform.json +1 -1
  32. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/AddLocal.json +2 -2
  33. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Autoscaler.json +1 -1
  34. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/BenchmarkConditionSpec.json +2 -2
  35. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/BenchmarkRegisterRequest.json +18 -5
  36. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/BenchmarkSpec.json +16 -3
  37. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
  38. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/BootstrapResponse.json +1 -1
  39. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Camera.json +1 -1
  40. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ClampInfo.json +1 -1
  41. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/CodeBundle.json +1 -1
  42. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Concurrency.json +18 -2
  43. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Conditions.json +1 -1
  44. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ConnectRequest.json +1 -1
  45. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ConnectResponse.json +4 -4
  46. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/DeploymentAccepted.json +1 -1
  47. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/DeploymentPolicy.json +17 -4
  48. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/DeploymentRequest.json +35 -6
  49. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
  50. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Embodiment.json +3 -3
  51. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ErrorBody.json +1 -1
  52. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ErrorCodes.json +1 -1
  53. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Estimate.json +1 -1
  54. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/EvalDryRunResponse.json +2 -2
  55. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/EvalReport.json +3 -3
  56. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/EvalRequest.json +1 -1
  57. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/EvalResponse.json +1 -1
  58. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageFrame.json +1 -1
  59. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageSpec.json +2 -2
  60. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageStepAddLocal.json +2 -2
  61. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageStepApt.json +1 -1
  62. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageStepEnv.json +1 -1
  63. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageStepRun.json +1 -1
  64. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ImageStepUvPip.json +1 -1
  65. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/InferParam.json +2 -2
  66. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Lease.json +1 -1
  67. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/LossyTransform.json +1 -1
  68. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/MetricAgg.json +1 -1
  69. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Observation.json +1 -1
  70. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/ObservationContract.json +2 -2
  71. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/PolicyRegistration.json +36 -7
  72. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/PolicySpec.json +35 -6
  73. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Proprioception.json +1 -1
  74. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/RenderSpec.json +1 -1
  75. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Resources.json +1 -1
  76. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/RolloutResult.json +1 -1
  77. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/SecretCreateRequest.json +1 -1
  78. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/SecretInfo.json +1 -1
  79. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/SecretRef.json +1 -1
  80. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/SeqFlags.json +1 -1
  81. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Step.json +1 -1
  82. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/SuiteAgg.json +1 -1
  83. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/TaskAgg.json +1 -1
  84. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Timeouts.json +1 -1
  85. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/UploadTarget.json +1 -1
  86. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/UsageReport.json +1 -1
  87. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/Variant.json +1 -1
  88. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeAttach.json +1 -1
  89. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeFile.json +1 -1
  90. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeInfo.json +1 -1
  91. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeMount.json +1 -1
  92. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeRef.json +1 -1
  93. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
  94. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
  95. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/schemas/WarmingError.json +1 -1
  96. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/telemetry/events.py +1 -4
  97. sequence_base-0.3.26/pyproject.toml +0 -27
  98. sequence_base-0.3.26/sequence_base/constants/env.py +0 -7
  99. sequence_base-0.3.26/sequence_base/constants/headers.py +0 -4
  100. sequence_base-0.3.26/sequence_base/constants/version.py +0 -27
  101. sequence_base-0.3.26/sequence_base/contract/flags.py +0 -82
  102. {sequence_base-0.3.26 → sequence_base-0.3.28}/.gitignore +0 -0
  103. {sequence_base-0.3.26 → sequence_base-0.3.28}/README.md +0 -0
  104. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/auth.py +0 -0
  105. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/codec/__init__.py +0 -0
  106. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/constants/__init__.py +0 -0
  107. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/errors/__init__.py +0 -0
  108. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/errors/transport.py +0 -0
  109. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/obs/__init__.py +0 -0
  110. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/obs/log.py +0 -0
  111. {sequence_base-0.3.26 → sequence_base-0.3.28}/sequence_base/telemetry/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sequence-base
3
- Version: 0.3.26
3
+ Version: 0.3.28
4
4
  Summary: Shared foundation for the Sequences platform — wire contract, correlation, error taxonomy, constants. Used by the robot SDK, gateway, and container harness.
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: pydantic>=2.0
@@ -0,0 +1,20 @@
1
+ [project]
2
+ name = "sequence-base"
3
+ version = "0.3.28"
4
+ description = "Shared foundation for the Sequences platform — wire contract, correlation, error taxonomy, constants. Used by the robot SDK, gateway, and container harness."
5
+ requires-python = ">=3.10"
6
+ dependencies = ["pydantic>=2.0"]
7
+
8
+ [project.optional-dependencies]
9
+ codec = ["pillow>=10", "numpy>=1.26"]
10
+ test = ["pytest", "jsonschema>=4"]
11
+
12
+ [build-system]
13
+ requires = ["hatchling"]
14
+ build-backend = "hatchling.build"
15
+
16
+ [tool.hatch.build.targets.wheel]
17
+ packages = ["sequence_base"]
18
+
19
+ [tool.hatch.build.targets.sdist]
20
+ include = ["/sequence_base", "/README.md", "/pyproject.toml"]
@@ -11,9 +11,7 @@ defined once so they cannot drift. Import from here; never redefine.
11
11
  Only shared-by-≥2-and-drifts-if-duplicated things live here. Model-specific, provider-specific, and
12
12
  catalogue values stay in container / gateway / spec.py.
13
13
  """
14
- # contract — the full shared wire contract (obs/action + registry + deployments + connect + eval +
15
- # accounts + error bodies). One definition per shape; see sequence_base/contract for the grouping.
16
- from sequence_base.contract import ( # noqa: F401 (re-export surface)
14
+ from sequence_base.contract import (
17
15
  ActRequest,
18
16
  ActResponse,
19
17
  AddLocal,
@@ -98,14 +96,11 @@ from sequence_base.contract import ( # noqa: F401 (re-export surface)
98
96
  code_for_status,
99
97
  )
100
98
 
101
- # the single schema-level version stamp (embedded in every exported JSON Schema)
102
99
  from sequence_base.constants.version import CONTRACT_VERSION
103
100
 
104
- # obs — correlation id + structured logging (all three sides)
105
101
  from sequence_base.obs.log import Timer, enabled, log
106
102
  from sequence_base.obs.rid import HEADER, clean, new_rid
107
103
 
108
- # errors — shared transport status taxonomy (gateway + container)
109
104
  from sequence_base.errors.transport import (
110
105
  TransportOverloaded,
111
106
  TransportRejected,
@@ -113,55 +108,37 @@ from sequence_base.errors.transport import (
113
108
  TransportWarming,
114
109
  )
115
110
 
116
- # auth — Bearer secret helpers (gateway direct-connect / internal; container guard; worker emit)
117
111
  from sequence_base import auth
118
112
 
119
113
 
120
- # NOTE: codec (image encode/decode) is intentionally NOT re-exported here — it needs PIL + numpy
121
- # (the [codec] extra), and the gateway installs the light core. Import it explicitly where needed:
122
- # from sequence_base.codec import encode, decode, ResizeSpec
123
114
 
124
115
  __all__ = [
125
- # contract 4 — /act data plane
126
116
  "ImageFrame", "Proprioception", "Observation", "ActionSpace", "ActionStep", "ActionChunk",
127
117
  "ActRequest", "ActResponse",
128
- # contract 1 — flags + policy registry
129
118
  "SeqFlags", "ImageStepUvPip", "ImageStepApt", "ImageStepEnv", "ImageStepRun", "AddLocal", "ImageStepAddLocal",
130
119
  "ImageStep", "ImageSpec",
131
120
  "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract", "Embodiment",
132
121
  "PolicySpec", "PolicyRegistration",
133
- # contract 2 — deployments
134
122
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
135
123
  "Resources", "Autoscaler", "Timeouts", "VolumeAttach", "DeploymentPolicy", "InferParam", "DeploymentRequest",
136
124
  "DeploymentAccepted", "DeploymentState", "DeploymentStatusResponse",
137
- # contract 3 — connect / lease
138
125
  "Lease", "ConnectRequest", "ConnectResponse",
139
- # contract 5 — benchmark + eval
140
126
  "Variant", "Conditions", "RenderSpec", "Step", "BenchmarkSpec", "BenchmarkConditionSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
141
127
  "EvalRequest", "ClampInfo", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
142
128
  "EvalStatus", "SuiteAgg", "TaskAgg", "MetricAgg", "LossyTransform", "EvalReport",
143
129
  "AdapterKind", "AdapterTransform",
144
- # contract 6 — accounts / secret / billing
145
130
  "SecretRef", "SecretCreateRequest", "SecretInfo", "BootstrapResponse", "UsageReport",
146
- # contract 7 — volumes (uploaded checkpoints and datasets)
147
131
  "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse", "VolumeInfo", "parse_vol",
148
- # official template weights
149
132
  "TEMPLATE_SCHEME", "parse_template",
150
- # error bodies + version
151
133
  "ErrorCode", "ErrorBody", "WarmingError", "ERRORS", "ErrorSpec", "code_for_status", "CONTRACT_VERSION",
152
- # obs
153
134
  "HEADER", "new_rid", "clean", "log", "Timer", "enabled",
154
- # errors (exception taxonomy)
155
135
  "TransportUnavailable", "TransportWarming", "TransportRejected", "TransportOverloaded",
156
- # auth + telemetry
157
136
  "auth",
158
137
  ]
159
138
 
160
139
  try:
161
- # The installed distribution's own number, so this can never drift from pyproject.toml again (it said 0.3.6
162
- # while the package was 0.3.12). A source tree that was never installed reports "dev".
163
140
  from importlib.metadata import PackageNotFoundError, version as _version
164
141
 
165
142
  __version__ = _version("sequence-base")
166
- except PackageNotFoundError: # pragma: no cover - running from an uninstalled checkout
143
+ except PackageNotFoundError:
167
144
  __version__ = "dev"
@@ -16,19 +16,8 @@ import io
16
16
  from typing import Any
17
17
 
18
18
 
19
- # Quality 95, and the number is measured rather than chosen for looking safe. On a real DROID frame
20
- # with the sampling noise pinned, the encoding moves the resulting action by: q95 0.51% of action
21
- # amplitude, q90 0.20%, q85 1.86%, q75 2.86% — 85 and below is where the error jumps by an order of
22
- # magnitude, that is the line. 95 rather than the empirically-best 90 because the effect is not
23
- # monotonic in quality on a single frame, and tuning to one frame is fitting noise.
24
- # {sequences-models models/pi05_droid/handler.py "Q95 MAX|DELTA| 0.0048 0.51% ... Q85 1.86%"}
25
19
  DEFAULT_JPEG_QUALITY = 95
26
20
 
27
- # The longest edge `encode` uploads. Every policy resizes a frame to its own input size anyway, so pixels above
28
- # that only cost transport — and past a point they do not cost, they fail: a body over ~2 MB was dropped by a
29
- # worker queue with no error and no job id, surfacing as a hang. 640 is above every input size in use (pi0.5's
30
- # 224), so it discards nothing a policy reads. [MEASURED 2026-09-12: capping the long edge at 640 took a
31
- # three-view observation from 1.98 MB to 0.12 MB]
32
21
  MAX_UPLOAD_EDGE = 640
33
22
 
34
23
 
@@ -48,7 +37,7 @@ def encode(frame: Any, *, quality: int = DEFAULT_JPEG_QUALITY) -> str:
48
37
 
49
38
  try:
50
39
  from PIL import Image
51
- except ImportError as e: # pragma: no cover - depends on the install
40
+ except ImportError as e:
52
41
  raise ImportError(
53
42
  "encoding an array or PIL image needs Pillow: pip install 'sequence-base[codec]'. "
54
43
  "Pass an already-encoded base64 str or bytes to avoid it."
@@ -59,8 +48,6 @@ def encode(frame: Any, *, quality: int = DEFAULT_JPEG_QUALITY) -> str:
59
48
  img = img.convert("RGB")
60
49
  if max(img.size) > MAX_UPLOAD_EDGE:
61
50
  scale = MAX_UPLOAD_EDGE / max(img.size)
62
- # round() not int(): int() truncates a 640.9 on one axis and 359.9 on the other, drifting the
63
- # aspect ratio by up to half a percent for no reason.
64
51
  img = img.resize((max(1, round(img.width * scale)),
65
52
  max(1, round(img.height * scale))), Image.LANCZOS)
66
53
 
@@ -85,7 +72,6 @@ def decode(data: str) -> "Any":
85
72
  return np.asarray(Image.open(io.BytesIO(raw)).convert("RGB"), dtype=np.uint8)
86
73
 
87
74
 
88
- # ── depth frames ─────────────────────────────────────────────────────────────────────────────────────────────────────
89
75
  _DEPTH_UNITS = {"m": 1000.0, "mm": 1.0}
90
76
 
91
77
 
@@ -93,8 +79,7 @@ def encode_depth(depth: Any, *, unit: str = "m") -> str:
93
79
  """A depth map -> the base64 16-bit PNG a depth frame (ImageFrame kind="depth") carries: millimetres as uint16, 0
94
80
  where there is no reading (NaN, inf, <= 0), clipped at 65.535 m. `unit` is what the array holds: "m" (float metres,
95
81
  the default) or "mm". Lossless, unlike the JPEG colour frames take — a depth error is a geometry error.
96
- {gs-plans/customization/io-contract.md "深度图"} [CONFIDENCE: CONFIRMED 90% — uint16 millimetres is the layout
97
- depth cameras and ROS depth images use]"""
82
+ """
98
83
  import numpy as np
99
84
  from PIL import Image
100
85
 
@@ -0,0 +1,7 @@
1
+ """SEQ_* environment variable NAMES — a single source. Values live in ~/.config/sequences/, never here."""
2
+
3
+ DIRECT_SECRET = "SEQ_DIRECT_SECRET"
4
+ INTERNAL_SECRET = "SEQ_INTERNAL_SECRET"
5
+ GATEWAY_URL = "SEQ_GATEWAY_URL"
6
+ VOLUME_ROOT = "SEQ_VOLUME_ROOT"
7
+ MANIFEST = "SEQ_MANIFEST"
@@ -0,0 +1,4 @@
1
+ """HTTP header names — one source, so sdk / gateway / container never spell them differently."""
2
+
3
+ REQUEST_ID = "X-Seq-Request-Id"
4
+ AUTH_SCHEME = "Bearer"
@@ -0,0 +1,3 @@
1
+ """The shared wire contract's version."""
2
+
3
+ CONTRACT_VERSION = "0.4.25"
@@ -2,7 +2,6 @@
2
2
  The shared wire contract — one definition per shape, imported (never redeclared) by the SDK, gateway,
3
3
  and container harness. Grouped by the contract number in docs/platform/contracts.md.
4
4
  """
5
- # contract 4 — /act data plane (obs/action DATA types)
6
5
  from sequence_base.contract.act import (
7
6
  ActionChunk,
8
7
  ActionSpace,
@@ -12,10 +11,8 @@ from sequence_base.contract.act import (
12
11
  Proprioception,
13
12
  )
14
13
 
15
- # contract 4, version 2 — the opaque /act envelope (inputs in, outputs out)
16
14
  from sequence_base.contract.wire import ActRequest, ActResponse
17
15
 
18
- # contract 1 — lifecycle flags + policy registry
19
16
  from sequence_base.contract.flags import SeqFlags
20
17
  from sequence_base.contract.policy import (
21
18
  Camera,
@@ -37,7 +34,6 @@ from sequence_base.contract.policy import (
37
34
  VolumeRef,
38
35
  )
39
36
 
40
- # contract 2 — deployments
41
37
  from sequence_base.contract.deployments import (
42
38
  MAX_CODE_BUNDLE_BYTES,
43
39
  Autoscaler,
@@ -53,10 +49,8 @@ from sequence_base.contract.deployments import (
53
49
  VolumeAttach,
54
50
  )
55
51
 
56
- # contract 3 — connect / lease
57
52
  from sequence_base.contract.connect import ConnectRequest, ConnectResponse, Lease
58
53
 
59
- # contract 5 — benchmark registration + eval run / report / adapter
60
54
  from sequence_base.contract.benchmarks import (
61
55
  BenchmarkConditionSpec,
62
56
  BenchmarkRegisterRequest,
@@ -84,7 +78,6 @@ from sequence_base.contract.eval import (
84
78
  TaskAgg,
85
79
  )
86
80
 
87
- # contract 6 — accounts / secret / billing
88
81
  from sequence_base.contract.accounts import (
89
82
  BootstrapResponse,
90
83
  SecretCreateRequest,
@@ -93,10 +86,8 @@ from sequence_base.contract.accounts import (
93
86
  UsageReport,
94
87
  )
95
88
 
96
- # official template weights — template://<name>[@v<n>]
97
89
  from sequence_base.contract.templates import TEMPLATE_SCHEME, parse_template
98
90
 
99
- # contract 7 — volumes (uploaded checkpoints and datasets)
100
91
  from sequence_base.contract.volumes import (
101
92
  MAX_FILES,
102
93
  MAX_UPLOAD_BYTES,
@@ -109,37 +100,27 @@ from sequence_base.contract.volumes import (
109
100
  parse_vol,
110
101
  )
111
102
 
112
- # shared error bodies
113
103
  from sequence_base.contract.errors import ERRORS, ErrorBody, ErrorCode, ErrorSpec, WarmingError, code_for_status
114
104
 
115
105
  __all__ = [
116
- # contract 4
117
106
  "ImageFrame", "Proprioception", "Observation", "ActionSpace", "ActionStep", "ActionChunk",
118
107
  "ActRequest", "ActResponse",
119
- # contract 1
120
108
  "SeqFlags",
121
109
  "ImageStepUvPip", "ImageStepApt", "ImageStepEnv", "ImageStepRun", "AddLocal", "ImageStepAddLocal", "ImageStep",
122
110
  "ImageSpec",
123
111
  "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract", "Embodiment",
124
112
  "PolicySpec", "PolicyRegistration",
125
- # contract 2
126
113
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
127
114
  "Resources", "Autoscaler", "Timeouts", "VolumeAttach", "DeploymentPolicy", "InferParam", "DeploymentRequest",
128
115
  "DeploymentAccepted", "DeploymentState", "DeploymentStatusResponse",
129
- # contract 3
130
116
  "Lease", "ConnectRequest", "ConnectResponse",
131
- # contract 5
132
117
  "Variant", "Conditions", "RenderSpec", "Step", "BenchmarkSpec", "BenchmarkConditionSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
133
118
  "EvalRequest", "ClampInfo", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
134
119
  "EvalStatus", "SuiteAgg", "TaskAgg", "MetricAgg", "LossyTransform", "EvalReport",
135
120
  "AdapterKind", "AdapterTransform",
136
- # contract 6
137
121
  "SecretRef", "SecretCreateRequest", "SecretInfo", "BootstrapResponse", "UsageReport",
138
- # contract 7
139
122
  "MAX_FILES", "MAX_UPLOAD_BYTES", "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse",
140
123
  "VolumeInfo", "check_volume_name", "parse_vol",
141
- # official template weights
142
124
  "TEMPLATE_SCHEME", "parse_template",
143
- # errors
144
125
  "ErrorCode", "ErrorBody", "WarmingError", "ERRORS", "ErrorSpec", "code_for_status",
145
126
  ]
@@ -1,5 +1,5 @@
1
1
  """
2
- 契约 6 — accounts / secret / billing wire types (full picture in docs/platform/accounts.md).
2
+ Contract 6 — accounts / secret / billing wire types (full picture in docs/platform/accounts.md).
3
3
 
4
4
  Secret-leak rule, mechanically checkable (goal 15). Two kinds of secret:
5
5
  (a) platform-minted — `direct_secret`, TLS private key. May appear in the DOWN direction only:
@@ -40,8 +40,6 @@ class SecretCreateRequest(BaseModel):
40
40
  """POST /v1/secrets — one name = one group of env values (e.g. planner = {PLANNER_API_KEY}).
41
41
  `env` carries plaintext values; this is the WRITE-direction allow-list field (goal 15)."""
42
42
 
43
- # F6: write-direction plaintext carrier — hide_input_in_errors so a malformed body's ValidationError
44
- # str() never echoes the raw secret value (symmetric with SecretInfo). RUNTIME config, no schema change.
45
43
  model_config = ConfigDict(extra="forbid", hide_input_in_errors=True)
46
44
 
47
45
  name: str
@@ -51,10 +49,6 @@ class SecretCreateRequest(BaseModel):
51
49
  class SecretInfo(BaseModel):
52
50
  """GET /v1/secrets element — metadata only, NO values. Exposes `env_keys`, not `env`."""
53
51
 
54
- # hide_input_in_errors: if a malformed wire response carries a value-bearing extra field, extra="forbid"
55
- # raises ValidationError; without this, its str() would echo the raw input (the secret value). Defense in
56
- # depth so no caller that forgets to guard model_validate can leak it. RUNTIME config, not a wire shape —
57
- # does NOT change the serialized schema, so CONTRACT_VERSION is unaffected.
58
52
  model_config = ConfigDict(extra="forbid", hide_input_in_errors=True)
59
53
 
60
54
  name: str
@@ -67,15 +61,12 @@ class BootstrapResponse(BaseModel):
67
61
  """What a worker receives when it starts — everything it needs to serve direct TLS `/act`. `direct_secret` + `tls_key` are DOWN-direction platform secrets; `env` is the
68
62
  WRITE-direction injection of the policy's declared user secrets into its subprocess (goal 15)."""
69
63
 
70
- # F6: carries down-direction platform secrets + write-direction user env — hide_input_in_errors so a
71
- # malformed body's ValidationError never echoes plaintext (symmetric with SecretInfo). RUNTIME config.
72
64
  model_config = ConfigDict(extra="forbid", hide_input_in_errors=True)
73
65
 
74
66
  direct_secret: str = Field(..., description="Platform-minted bearer the worker's guard checks")
75
67
  tls_key: str = Field(..., description="Self-signed TLS private key (down-direction platform secret)")
76
68
  tls_cert: str = Field(..., description="Self-signed TLS certificate (public)")
77
69
  env: dict[str, str] = Field(..., description="Policy's user secrets, expanded ENV_KEY → value")
78
- # None: a policy without a checkpoint — nothing was copied or mounted. {gs-plans/storage T13}
79
70
  weights_uri: str | None = None
80
71
  lease: Lease
81
72
 
@@ -5,7 +5,7 @@ They are no longer the platform's wire: since contract 0.4.14 the worker passes
5
5
  (`sequence_base.contract.wire`). This module keeps `from sequence_base.contract.act import Observation` resolving to the
6
6
  same classes for the gateway, harness and SDK code that still uses the preset.
7
7
  """
8
- from sequence_base.presets.robot_arm import ( # noqa: F401 — re-exports
8
+ from sequence_base.presets.robot_arm import (
9
9
  ActionChunk,
10
10
  ActionSpace,
11
11
  ActionStep,
@@ -1,5 +1,5 @@
1
1
  """
2
- 契约 5 (registration half) — benchmark is a second authorizable artifact, its registration symmetric to
2
+ Contract 5 (registration half) — benchmark is a second authorizable artifact, its registration symmetric to
3
3
  a policy's. `BenchmarkSpec` carries the same infra declaration a PolicySpec does, plus a structured
4
4
  `Conditions` (the eval matrix), versioned `assets` volumes, an optional `render` (mp4) declaration, and
5
5
  SeqFlags→method bindings covering SETUP / RESET / CHECK / SCORE. The eval *run* wire types (RolloutResult,
@@ -19,7 +19,7 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator
19
19
  from sequence_base.contract.accounts import SecretRef
20
20
  from sequence_base.contract.deployments import CodeBundle, DeploymentState, InferParam
21
21
  from sequence_base.contract.flags import BENCHMARK_BATCHED, BENCHMARK_BINDABLE, SeqFlags
22
- from sequence_base.contract.policy import ActionContract, ImageSpec, VolumeRef
22
+ from sequence_base.contract.policy import KIND_NAME, ActionContract, ImageSpec, VolumeRef
23
23
 
24
24
  __all__ = [
25
25
  "Variant",
@@ -60,10 +60,6 @@ class RenderSpec(BaseModel):
60
60
 
61
61
  camera: str = Field(..., description="Which camera to pull frames from, e.g. 'wrist' / 'agentview'")
62
62
  fps: int = 30
63
- # How the platform turns each frame before encoding it. Whether a camera's picture is upside down is a property of
64
- # the renderer (OpenGL's glReadPixels returns the bottom row first: LIBERO / robosuite), so its author says it once
65
- # here instead of everyone who runs an eval passing it. {gs-plans inbox "LIBERO 风格的相机录出来是倒的"}
66
- # None (as drawn) is left off the wire, so a recording that is not turned registers on any platform version.
67
63
  flip: Literal["vertical", "horizontal", "both"] | None = Field(
68
64
  None, description="'vertical': rows reversed (upside down); 'horizontal': mirrored; 'both': turned 180°; "
69
65
  "None: as drawn")
@@ -103,18 +99,16 @@ class BenchmarkSpec(BaseModel):
103
99
  handler_ref: str
104
100
  bindings: dict[SeqFlags, str] = Field(..., description="SETUP / RESET / CHECK / SCORE (+ OBSERVE / STEP) → method name")
105
101
  gpu: str | list[str] | None = None
106
- # How many environments one benchmark instance runs at once when an eval does not say (the author's default).
107
102
  envs: int | None = Field(default=None, ge=1, le=65536)
108
103
  assets: list[VolumeRef] = Field(default_factory=list)
109
104
  render: RenderSpec | None = None
110
105
  cpu: int | None = None
111
106
  memory_gb: int | None = None
112
107
  secrets: list[SecretRef] = Field(default_factory=list)
108
+ engine: str | None = Field(default=None, pattern=KIND_NAME)
113
109
 
114
110
  @model_validator(mode="after")
115
111
  def _bindings_only_benchmark_hooks(self) -> "BenchmarkSpec":
116
- # eval-raw: only SETUP / RESET / CHECK / SCORE are bindable. ROLLOUT is deprecated (never bound) and
117
- # a policy-lifecycle flag has no place here — either is a malformed registration, caught at build.
118
112
  bad = set(self.bindings) - set(BENCHMARK_BINDABLE)
119
113
  if bad:
120
114
  names = ", ".join(sorted(f.name or str(f.value) for f in bad))
@@ -130,7 +124,7 @@ class BenchmarkSpec(BaseModel):
130
124
 
131
125
  class BenchmarkConditionSpec(BaseModel):
132
126
  """One episode setting exactly as the author declared it — a sparse list or a cross product, with its own step
133
- cap, wall-clock limit and suite. {gs-plans/customization/benchmark.md "6. 其它在 SDK 里收下、在生产里丢掉的声明"}"""
127
+ cap, wall-clock limit and suite."""
134
128
 
135
129
  model_config = ConfigDict(extra="forbid")
136
130
 
@@ -158,41 +152,20 @@ class BenchmarkRegisterRequest(BaseModel):
158
152
  render: RenderSpec | None = None
159
153
  cpu: int | None = None
160
154
  memory_gb: int | None = None
161
- # The benchmark's own source (reset/check hooks, its adapters), shipped like a policy's — the eval worker
162
- # imports handler_ref and any adapter ref from it. None only when the image installs the module itself.
163
155
  code: CodeBundle | None = None
164
- # Which suite each task belongs to (task -> suite), so reports aggregate per suite without the platform
165
- # guessing from task names. A task not listed here has no suite.
166
156
  suite_of: dict[str, str] = Field(default_factory=dict)
167
- # The action contract the benchmark's sim consumes; eval compares it with the policy's and requires an
168
- # adapter when they differ.
169
157
  action_contract: ActionContract | None = None
170
- # Credentials the sim's own code needs (a dataset token), by name only — injected into the build check and
171
- # every eval shard, never into the policy. {gs-plans/customization/benchmark.md "A.1 SECRETS"}
172
158
  secrets: list[SecretRef] = Field(default_factory=list)
173
- # Whether the benchmark defines @seq.score — the platform then calls it per group (overall, each suite, each
174
- # task) after every episode has run, and its keys override those groups' means. {benchmark.md "A.2"}
175
159
  score: bool = False
176
- # Every condition as declared (a sparse list stays sparse; each keeps its own max_steps / timeout_s / suite),
177
- # where `conditions` can only say tasks x seeds x variants. The eval runs these when present.
178
160
  condition_list: list[BenchmarkConditionSpec] | None = Field(default=None, max_length=5000)
179
- # An episode's wall-clock limit (seconds) when a condition sets none; past it the episode ends truncated.
180
161
  timeout_s: float | None = Field(default=None, gt=0)
181
- # The adapter (module:Class in the benchmark's own code) an eval uses when it names none.
182
162
  default_adapter_ref: str | None = None
183
- # The card each benchmark instance runs on (L4 or RTX-PRO-6000); None takes the platform's default (L4). The
184
- # simulation never runs on a CPU. {gs-plans/benchmark-internals/design.md "一、规则"}
185
163
  gpu: str | None = Field(default=None, max_length=32)
186
- # How many environments one instance runs at once when an eval does not say; None takes 1.
187
164
  envs: int | None = Field(default=None, ge=1, le=65536)
188
- # The parameters its @seq.setup declares — `def setup(self, envs, *, noise_std: float = 0.02)` — each with its type
189
- # and default, which an eval may set (--param benchmark.noise_std=0); None: it declares none, or an SDK from before
190
- # them. {gs-plans/eval-adapters/36}
191
165
  params: dict[str, InferParam] | None = Field(default=None, max_length=32)
192
- # Whether the benchmark is batched (binds OBSERVE and STEP and steps many environments in one call).
193
166
  batched: bool = False
194
- # The Dockerfile's build context, as for a deployment.
195
167
  image_context: CodeBundle | None = None
168
+ engine: str | None = Field(default=None, pattern=KIND_NAME)
196
169
 
197
170
  @model_validator(mode="after")
198
171
  def _param_names(self) -> "BenchmarkRegisterRequest":
@@ -1,9 +1,9 @@
1
1
  """
2
- 契约 3 — connect / lease (client → gateway; control plane, first call + renewal).
2
+ Contract 3 — connect / lease (client → gateway; control plane, first call + renewal).
3
3
 
4
4
  Control plane / data plane split: `connect(model)` goes through the gateway once (admission → acquire →
5
5
  ensure worker up) and hands back the *direct* coordinates the client then uses to hit the worker's
6
- `/act` without the gateway in the hot path (契约 4). The cold-start fallback is a defined wire body,
6
+ `/act` without the gateway in the hot path (contract 4). The cold-start fallback is a defined wire body,
7
7
  not an HTTP header alone — a 503 returns `WarmingError` (contract/errors.py), so `503 + Retry-After`
8
8
  and the warming ETA are typed and checkable.
9
9
  """
@@ -13,7 +13,6 @@ from datetime import datetime
13
13
 
14
14
  from pydantic import BaseModel, ConfigDict, Field
15
15
 
16
- # The 503 cold/warming body is defined once in contract.errors and reused here — no second shape.
17
16
  from sequence_base.contract.errors import WarmingError
18
17
  from sequence_base.contract.io import ActionContract, ObservationContract
19
18
 
@@ -43,7 +42,7 @@ class ConnectResponse(BaseModel):
43
42
  """200 — the full set of coordinates for a direct worker connection.
44
43
 
45
44
  `direct_secret` is a plaintext platform-minted bearer for the worker's `/act`; it is on the
46
- allow-list of fields permitted to carry a secret value in the down direction (契约 6 / goal 15)."""
45
+ allow-list of fields permitted to carry a secret value in the down direction (contract 6 / goal 15)."""
47
46
 
48
47
  model_config = ConfigDict(extra="forbid")
49
48
 
@@ -53,9 +52,7 @@ class ConnectResponse(BaseModel):
53
52
  lease: Lease
54
53
  warming: bool = Field(default=False, description="True → worker still warming; see warming_eta_s")
55
54
  warming_eta_s: int | None = Field(default=None, description="Seconds until warm, when warming")
56
- # The contracts this session is held to, so the client can check and assemble before the first act.
57
55
  action: ActionContract | None = None
58
56
  observation: ObservationContract | None = None
59
57
  embodiment: str | None = None
60
- # The deployment's own description of its inputs and outputs (DeploymentPolicy.io), when the author wrote one.
61
58
  io: str | None = None
@@ -1,5 +1,5 @@
1
1
  """
2
- 契约 2 — `POST /v1/deployments` (SDK `seq deploy` → gateway) and its responses.
2
+ Contract 2 — `POST /v1/deployments` (SDK `seq deploy` → gateway) and its responses.
3
3
 
4
4
  The request is the normalized wire form of a PolicySpec: one big body (new config adds a field, never a
5
5
  new endpoint) whose sub-shapes reuse contract 1's types
@@ -25,6 +25,7 @@ from sequence_base.contract.templates import TEMPLATE_SCHEME, parse_template
25
25
  from sequence_base.contract.volumes import VOL_SCHEME, parse_vol
26
26
  from sequence_base.contract.io import Embodiment, ObservationContract
27
27
  from sequence_base.contract.policy import (
28
+ KIND_NAME,
28
29
  ActionContract,
29
30
  Concurrency,
30
31
  ImageSpec,
@@ -52,15 +53,10 @@ class Resources(BaseModel):
52
53
  model_config = ConfigDict(extra="forbid")
53
54
 
54
55
  gpu: list[str]
55
- # Physical cores and GiB the worker requests, billed per second at the greater of the request and what it uses.
56
- # None requests the minimum (0.125 cores, 0.125 GiB) and the worker may use more. A limit caps what it may use;
57
- # None leaves it free to burst.
58
56
  cpu: float | None = Field(default=None, ge=0.125, le=64)
59
57
  cpu_limit: float | None = Field(default=None, ge=0.125, le=128)
60
58
  memory_gb: float | None = Field(default=None, ge=0.125, le=1024)
61
59
  memory_limit_gb: float | None = Field(default=None, ge=0.125, le=1024)
62
- # Where the workers run (near the robots): one of the regions the platform offers. None: the platform picks.
63
- # {gs-plans/customization/policy.md "区域"}
64
60
  region: str | None = Field(default=None, max_length=32)
65
61
 
66
62
  @model_validator(mode="after")
@@ -77,16 +73,10 @@ class Autoscaler(BaseModel):
77
73
  model_config = ConfigDict(extra="forbid")
78
74
 
79
75
  min_containers: int = 0
80
- # Accepted and ignored: limits count GPUs (`max_gpus`). sequence-ai <= 0.28 still sends it and does not pin
81
- # sequence-base, so removing it would break their deploys; it goes when no supported SDK sends it.
82
76
  max_containers: int | None = None
83
- # The most GPUs this deployment may hold at once. None: no cap of its own — only the account's and the platform's
84
- # GPU limits bound it. Limits count cards, never machines. {gs-plans/parallel/serving.md "1. 限制按卡数,不按台数"}
85
77
  max_gpus: int | None = Field(default=None, ge=1, le=256)
86
78
  buffer_containers: int | None = None
87
79
  scaledown_window: int = 600
88
- # When every slot is taken and no card is left: queue the new robot before its control loop starts, or refuse it at
89
- # once; how long a queued robot waits before it is told why. {gs-plans/parallel/serving-queue.md "connect 的处理顺序"}
90
80
  on_full: Literal["queue", "reject"] = "queue"
91
81
  queue_timeout_s: int = Field(default=120, ge=5, le=3600)
92
82
 
@@ -116,11 +106,11 @@ class VolumeAttach(BaseModel):
116
106
  class InferParam(BaseModel):
117
107
  """One inference parameter the policy's `@seq.infer` declares — `def infer(self, obs, *, num_steps: int = 10)`
118
108
  — with its type and default. An /act or an eval may set it; the worker passes only declared names, cast to
119
- this type, and refuses any other. {gs-plans/customization/policy.md "四、推理行为"}
109
+ this type, and refuses any other.
120
110
 
121
111
  `min` / `max` (a number's bounds, inclusive) and `choices` (the values it may take) — declared as
122
112
  `Annotated[int, seq.Range(1, 10)]` / `seq.OneOf(...)` — refuse any other value before inference, saying which are
123
- allowed; None (left off the wire) holds it to its type alone. {gs-plans/deploy-serving/35 "允许声明范围或可选值"}"""
113
+ allowed; None (left off the wire) holds it to its type alone."""
124
114
 
125
115
  model_config = ConfigDict(extra="forbid")
126
116
 
@@ -182,25 +172,12 @@ class DeploymentPolicy(BaseModel):
182
172
  streaming: bool = False
183
173
  frames_per_chunk: int | None = None
184
174
  secrets: list[SecretRef] = Field(default_factory=list)
185
- # The observation contract the worker checks every /act against (None: undeclared), and per-robot
186
- # contracts for a multi-embodiment policy — the worker picks one by the session's embodiment.
187
175
  observation: ObservationContract | None = None
188
176
  embodiments: dict[str, Embodiment] | None = None
189
- # Did the author declare `action=` (or embodiments)? SDKs before contract 0.4.14 always sent an `action` (a
190
- # placeholder when none was declared); only a declared contract is enforced on the worker and handed to the robot.
191
- # None (an older SDK) is treated as undeclared, so an older policy is never refused for the placeholder it never
192
- # chose. A current SDK sends `action` only when declared. {io-contract.md "旧 SDK 写的 POLICY 照样能 BUILD"}
193
177
  action_declared: bool | None = None
194
- # The author's own description of what infer() takes and returns; stored, handed to the robot at connect, never
195
- # interpreted by the platform.
196
178
  io: str | None = Field(default=None, max_length=4000)
197
- # The inference parameters @seq.infer takes beyond the observation (name -> type, default). None: an older
198
- # SDK that did not look; {}: the policy declares none.
179
+ family: str | None = Field(default=None, pattern=KIND_NAME)
199
180
  params: dict[str, InferParam] | None = Field(default=None, max_length=32)
200
- # The platform models the policy calls while it runs (`@seq.policy(models=[...])`), by full catalogue id
201
- # (accounts/sequences/models/<name>): the act key that drives this deployment may call exactly these on /v1/chat
202
- # and /v1/decide, and nothing else it could spend on. None: the policy calls none (and an older SDK never sent it).
203
- # {gs-plans models-api/11, user 2026-10-04 "act key should have ability to call any model involved in the policy"}
204
181
  models: list[str] | None = Field(default=None, max_length=16)
205
182
 
206
183
  @model_validator(mode="after")
@@ -208,7 +185,6 @@ class DeploymentPolicy(BaseModel):
208
185
  for name in self.params or {}:
209
186
  if not name.isidentifier() or name.startswith("_"):
210
187
  raise ValueError(f"inference parameter {name!r} is not a public Python identifier")
211
- # A declared model is a catalogue id: no blanks, no duplicates (the gateway checks each one exists).
212
188
  for model in self.models or []:
213
189
  if not model or model != model.strip() or "/" not in model:
214
190
  raise ValueError(f"policy model {model!r} is not a catalogue id (accounts/sequences/models/<name>)")
@@ -264,26 +240,15 @@ class DeploymentRequest(BaseModel):
264
240
 
265
241
  name: str
266
242
  image: ImageSpec
267
- # None: a policy without a checkpoint — no weights volume is made, copied or mounted. {gs-plans/storage T13}
268
243
  weights: VolumeMount | None = None
269
244
  resources: Resources
270
245
  policy: DeploymentPolicy
271
- # The handler's source. None only when `handler_ref` is importable from the image itself (a package the
272
- # ImageSpec installs); otherwise the worker has nothing to import and the build fails.
273
246
  code: CodeBundle | None = None
274
- # The Dockerfile's build context (the directory its COPY / ADD read), shipped like the code. Only with
275
- # image.dockerfile. {gs-plans/customization/policy.md "DOCKERFILE 的 BUILD CONTEXT"}
276
247
  image_context: CodeBundle | None = None
277
- # Non-weights volumes the policy declares (goal 7). List, not dict, so a mount is never duplicated as
278
- # both key and value; the gateway only stores the dump, no consumer needs dict lookup.
279
248
  volumes: list[VolumeAttach] = Field(default_factory=list)
280
249
  autoscaler: Autoscaler = Field(default_factory=Autoscaler)
281
- # Every field optional: an omitted @seq.concurrent serves one robot session per model copy, one copy per card.
282
250
  concurrency: Concurrency = Field(default_factory=Concurrency)
283
251
  timeouts: Timeouts | None = None
284
- # Weights in several named parts: `weights` is the first and `weights_name` its name; `extra_weights` the others,
285
- # each provisioned like `weights`. @seq.load then receives {name: directory}. Both None (and absent from the wire):
286
- # one weights path, as before. {gs-plans/customization/options.md "多份权重文件(BASE + LORA …)"}
287
252
  weights_name: str | None = None
288
253
  extra_weights: dict[str, VolumeMount] | None = None
289
254
 
@@ -293,7 +258,7 @@ class DeploymentRequest(BaseModel):
293
258
  """The name is how a robot addresses the model (`connect(model=name)`) and how the console lists it, so
294
259
  it is a slug: letters, digits, `.`, `_`, `-`, starting with a letter or digit, at most 64 characters.
295
260
  A raw POST once registered "hfcheck-missing hf://nobody/x" — a space, a scheme and a slash in a name.
296
- {LIVE 2026-10-02: THREE DEPLOYMENTS NAMED "<SLUG> HF://…" WERE ACCEPTED 202 WITH AN EMPTY WEIGHTS URI}"""
261
+ """
297
262
  if not _NAME.fullmatch(v):
298
263
  raise ValueError("name must be 1-64 letters, digits, '.', '_' or '-', starting with a letter or digit")
299
264
  return v
@@ -325,9 +290,7 @@ class DeploymentRequest(BaseModel):
325
290
  return self
326
291
 
327
292
 
328
- # A deployment name: what `connect(model=...)` and the console address. {DeploymentRequest._name_is_a_slug}
329
293
  _NAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,63}")
330
- # The origins the build knows how to provision weights from.
331
294
  _WEIGHTS_SCHEMES = ("gs://", "hf://", "s3://", "https://")
332
295
 
333
296
 
@@ -357,5 +320,4 @@ class DeploymentStatusResponse(BaseModel):
357
320
  status: DeploymentState
358
321
  model: str
359
322
  error: str | None = Field(default=None, description="Build-failure reason; set only when FAILED")
360
- # Why a deployment that was serving was marked FAILED: its workers kept dying of the same cause.
361
323
  reason: Literal["gpu_out_of_memory", "host_out_of_memory", "crashed"] | None = None