sequence-base 0.3.2__tar.gz → 0.3.4__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 (165) hide show
  1. {sequence_base-0.3.2 → sequence_base-0.3.4}/PKG-INFO +1 -1
  2. {sequence_base-0.3.2 → sequence_base-0.3.4}/pyproject.toml +1 -1
  3. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/__init__.py +14 -2
  4. sequence_base-0.3.4/sequence_base/constants/version.py +11 -0
  5. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/contract/__init__.py +20 -1
  6. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/contract/act.py +3 -0
  7. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/contract/connect.py +6 -0
  8. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/contract/deployments.py +18 -3
  9. sequence_base-0.3.4/sequence_base/contract/io.py +83 -0
  10. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/contract/policy.py +20 -15
  11. sequence_base-0.3.4/sequence_base/contract/volumes.py +141 -0
  12. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schema_export.py +19 -0
  13. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ActionChunk.json +1 -1
  14. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ActionContract.json +1 -1
  15. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ActionStep.json +1 -1
  16. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/AdapterTransform.json +1 -1
  17. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Autoscaler.json +1 -1
  18. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/BenchmarkRegisterRequest.json +1 -1
  19. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/BenchmarkSpec.json +2 -2
  20. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
  21. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/BootstrapResponse.json +1 -1
  22. sequence_base-0.3.4/sequence_base/schemas/Camera.json +25 -0
  23. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ClampInfo.json +1 -1
  24. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/CodeBundle.json +1 -1
  25. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Concurrency.json +1 -1
  26. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Conditions.json +1 -1
  27. sequence_base-0.3.4/sequence_base/schemas/ConnectRequest.json +29 -0
  28. sequence_base-0.3.4/sequence_base/schemas/ConnectResponse.json +225 -0
  29. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/DeploymentAccepted.json +1 -1
  30. sequence_base-0.3.4/sequence_base/schemas/DeploymentPolicy.json +239 -0
  31. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/DeploymentRequest.json +139 -1
  32. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
  33. sequence_base-0.3.4/sequence_base/schemas/Embodiment.json +143 -0
  34. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ErrorBody.json +1 -1
  35. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Estimate.json +1 -1
  36. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
  37. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/EvalReport.json +1 -1
  38. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/EvalRequest.json +1 -1
  39. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/EvalResponse.json +1 -1
  40. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ImageFrame.json +1 -1
  41. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ImageSpec.json +1 -1
  42. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ImageStepApt.json +1 -1
  43. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ImageStepEnv.json +1 -1
  44. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ImageStepRun.json +1 -1
  45. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/ImageStepUvPip.json +1 -1
  46. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Lease.json +1 -1
  47. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/LossyTransform.json +1 -1
  48. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/MetricAgg.json +1 -1
  49. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Observation.json +19 -1
  50. sequence_base-0.3.4/sequence_base/schemas/ObservationContract.json +73 -0
  51. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/PolicyRegistration.json +129 -3
  52. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/PolicySpec.json +128 -2
  53. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Proprioception.json +1 -1
  54. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/RenderSpec.json +1 -1
  55. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Resources.json +1 -1
  56. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/RolloutResult.json +1 -1
  57. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/SecretCreateRequest.json +1 -1
  58. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/SecretInfo.json +1 -1
  59. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/SecretRef.json +1 -1
  60. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/SeqFlags.json +1 -1
  61. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Step.json +1 -1
  62. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/SuiteAgg.json +1 -1
  63. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/TaskAgg.json +1 -1
  64. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Timeouts.json +1 -1
  65. sequence_base-0.3.4/sequence_base/schemas/UploadTarget.json +28 -0
  66. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/UsageReport.json +1 -1
  67. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/Variant.json +1 -1
  68. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/VolumeAttach.json +1 -1
  69. sequence_base-0.3.4/sequence_base/schemas/VolumeFile.json +29 -0
  70. sequence_base-0.3.4/sequence_base/schemas/VolumeInfo.json +72 -0
  71. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/VolumeMount.json +1 -1
  72. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/VolumeRef.json +1 -1
  73. sequence_base-0.3.4/sequence_base/schemas/VolumeUploadRequest.json +60 -0
  74. sequence_base-0.3.4/sequence_base/schemas/VolumeUploadResponse.json +69 -0
  75. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/schemas/WarmingError.json +1 -1
  76. {sequence_base-0.3.2 → sequence_base-0.3.4}/tests/test_contracts.py +82 -0
  77. sequence_base-0.3.2/sequence_base/constants/version.py +0 -9
  78. sequence_base-0.3.2/sequence_base/schemas/ConnectRequest.json +0 -16
  79. sequence_base-0.3.2/sequence_base/schemas/ConnectResponse.json +0 -81
  80. sequence_base-0.3.2/sequence_base/schemas/DeploymentPolicy.json +0 -101
  81. {sequence_base-0.3.2 → sequence_base-0.3.4}/.github/workflows/ci.yml +0 -0
  82. {sequence_base-0.3.2 → sequence_base-0.3.4}/.gitignore +0 -0
  83. {sequence_base-0.3.2 → sequence_base-0.3.4}/README.md +0 -0
  84. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/README.md +0 -0
  85. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/REVIEW.md +0 -0
  86. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/START.md +0 -0
  87. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/accounts.md +0 -0
  88. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/billing.md +0 -0
  89. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/build/BUILD_LOG.md +0 -0
  90. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/build/goal_sha256.json +0 -0
  91. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/build/gs-build-wave.js +0 -0
  92. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/build/gs-spec-goals.js +0 -0
  93. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/container.md +0 -0
  94. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/contracts.md +0 -0
  95. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/deploy/PLAN.md +0 -0
  96. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/deploy/PROD_REORG.md +0 -0
  97. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/deploy/SCHEMA_COMPAT.md +0 -0
  98. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/deploy/archive/sequences-gw.genseq-506918.20261001.yaml +0 -0
  99. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/eval.md +0 -0
  100. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/gateway.md +0 -0
  101. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/A1.yaml +0 -0
  102. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/A2.yaml +0 -0
  103. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/A3.yaml +0 -0
  104. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/C1.yaml +0 -0
  105. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/C2.yaml +0 -0
  106. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/C3.yaml +0 -0
  107. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/C4.yaml +0 -0
  108. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/C5.yaml +0 -0
  109. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/G1.yaml +0 -0
  110. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/G2.yaml +0 -0
  111. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/G3.yaml +0 -0
  112. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/G4.yaml +0 -0
  113. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/G5.yaml +0 -0
  114. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/O1.yaml +0 -0
  115. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/O2.yaml +0 -0
  116. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/O3.yaml +0 -0
  117. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/O4.yaml +0 -0
  118. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/O5.yaml +0 -0
  119. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/R1.yaml +0 -0
  120. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/R2.yaml +0 -0
  121. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/R3.yaml +0 -0
  122. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/S1.yaml +0 -0
  123. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/S2.yaml +0 -0
  124. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/S3.yaml +0 -0
  125. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/S4.yaml +0 -0
  126. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/S5.yaml +0 -0
  127. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/S6.yaml +0 -0
  128. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/V1-storage.md +0 -0
  129. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/V2-user-quotas.md +0 -0
  130. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/W0.1.yaml +0 -0
  131. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/goals/W0.2.yaml +0 -0
  132. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/README.md +0 -0
  133. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/architecture.md +0 -0
  134. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/connect.md +0 -0
  135. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/cost.md +0 -0
  136. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/custom-domain.md +0 -0
  137. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/debugging.md +0 -0
  138. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/goals.md +0 -0
  139. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/limits.md +0 -0
  140. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/plan.md +0 -0
  141. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/white-label.md +0 -0
  142. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/modal/wiring.md +0 -0
  143. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/observability.md +0 -0
  144. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/references.md +0 -0
  145. {sequence_base-0.3.2 → sequence_base-0.3.4}/docs/platform/sdk.md +0 -0
  146. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/auth.py +0 -0
  147. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/codec/__init__.py +0 -0
  148. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/codec/image.py +0 -0
  149. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/codec/resize.py +0 -0
  150. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/constants/__init__.py +0 -0
  151. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/constants/env.py +0 -0
  152. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/constants/headers.py +0 -0
  153. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/contract/accounts.py +0 -0
  154. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/contract/benchmarks.py +0 -0
  155. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/contract/errors.py +0 -0
  156. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/contract/eval.py +0 -0
  157. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/contract/flags.py +0 -0
  158. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/errors/__init__.py +0 -0
  159. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/errors/transport.py +0 -0
  160. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/obs/__init__.py +0 -0
  161. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/obs/log.py +0 -0
  162. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/obs/rid.py +0 -0
  163. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/telemetry/__init__.py +0 -0
  164. {sequence_base-0.3.2 → sequence_base-0.3.4}/sequence_base/telemetry/events.py +0 -0
  165. {sequence_base-0.3.2 → sequence_base-0.3.4}/tests/test_codec_roundtrip.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sequence-base
3
- Version: 0.3.2
3
+ Version: 0.3.4
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.11
6
6
  Requires-Dist: pydantic>=2.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sequence-base"
3
- version = "0.3.2"
3
+ version = "0.3.4"
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.11"
6
6
  dependencies = ["pydantic>=2.0"]
@@ -14,7 +14,16 @@ catalogue values stay in container / gateway / spec.py.
14
14
  # contract — the full shared wire contract (obs/action + registry + deployments + connect + eval +
15
15
  # accounts + error bodies). One definition per shape; see sequence_base/contract for the grouping.
16
16
  from sequence_base.contract import ( # noqa: F401 (re-export surface)
17
+ Camera,
18
+ Embodiment,
19
+ ObservationContract,
17
20
  MAX_CODE_BUNDLE_BYTES,
21
+ UploadTarget,
22
+ VolumeFile,
23
+ VolumeInfo,
24
+ VolumeUploadRequest,
25
+ VolumeUploadResponse,
26
+ parse_vol,
18
27
  ActionChunk,
19
28
  ActionContract,
20
29
  ActionSpace,
@@ -108,7 +117,8 @@ __all__ = [
108
117
  "ImageFrame", "Proprioception", "Observation", "ActionSpace", "ActionStep", "ActionChunk",
109
118
  # contract 1 — flags + policy registry
110
119
  "SeqFlags", "ImageStepUvPip", "ImageStepApt", "ImageStepEnv", "ImageStepRun", "ImageStep", "ImageSpec",
111
- "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "PolicySpec", "PolicyRegistration",
120
+ "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract", "Embodiment",
121
+ "PolicySpec", "PolicyRegistration",
112
122
  # contract 2 — deployments
113
123
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
114
124
  "Resources", "Autoscaler", "Timeouts", "VolumeAttach", "DeploymentPolicy", "DeploymentRequest",
@@ -122,6 +132,8 @@ __all__ = [
122
132
  "AdapterKind", "AdapterTransform",
123
133
  # contract 6 — accounts / secret / billing
124
134
  "SecretRef", "SecretCreateRequest", "SecretInfo", "BootstrapResponse", "UsageReport",
135
+ # contract 7 — volumes (uploaded checkpoints and datasets)
136
+ "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse", "VolumeInfo", "parse_vol",
125
137
  # error bodies + version
126
138
  "ErrorCode", "ErrorBody", "WarmingError", "CONTRACT_VERSION",
127
139
  # obs
@@ -132,4 +144,4 @@ __all__ = [
132
144
  "auth", "WorkerLogEvent",
133
145
  ]
134
146
 
135
- __version__ = "0.3.2"
147
+ __version__ = "0.3.4"
@@ -0,0 +1,11 @@
1
+ """Wire protocol version. Bump on a breaking change to contract / codec / telemetry shapes."""
2
+
3
+ PROTOCOL_VERSION = "1"
4
+
5
+ # The single version stamped into every exported JSON Schema (as `x-contract-version`), so a consumer
6
+ # across deployments / repos can tell which version of a shape it is validating against. MUST change
7
+ # whenever any shared wire shape changes (a field added / removed / retyped) — that is what makes
8
+ # contract evolution mechanically detectable instead of eyeballed. Bump on any shape change.
9
+ CONTRACT_VERSION = "0.4.3" # 0.4.2 -> 0.4.3: I/O contract — ObservationContract/Camera/Embodiment on PolicySpec + DeploymentPolicy, Observation.state, ConnectRequest.embodiment, ConnectResponse.action/observation/embodiment
10
+ # previous: 0.4.2 # 0.4.1 -> 0.4.2: 契约7 volumes (uploads: VolumeFile/VolumeUploadRequest/UploadTarget/VolumeUploadResponse/VolumeInfo), weights accept vol://<name>[@v<n>]; DeploymentRequest name is a slug and weights need an origin
11
+ # previous: 0.4.1 # 0.4.0 -> 0.4.1: 契约2 deploy body completed during S3/G4 — DeploymentPolicy.secrets (name-only passthrough), DeploymentRequest.volumes: list[VolumeAttach{uri,mount,create_if_missing}], concurrency default materialized; telemetry events add load_start stage + emitted_at/rid/dedup_key (O4)
@@ -15,6 +15,9 @@ from sequence_base.contract.act import (
15
15
  # contract 1 — lifecycle flags + policy registry
16
16
  from sequence_base.contract.flags import SeqFlags
17
17
  from sequence_base.contract.policy import (
18
+ Camera,
19
+ Embodiment,
20
+ ObservationContract,
18
21
  ActionContract,
19
22
  Concurrency,
20
23
  ImageSpec,
@@ -83,6 +86,19 @@ from sequence_base.contract.accounts import (
83
86
  UsageReport,
84
87
  )
85
88
 
89
+ # contract 7 — volumes (uploaded checkpoints and datasets)
90
+ from sequence_base.contract.volumes import (
91
+ MAX_FILES,
92
+ MAX_UPLOAD_BYTES,
93
+ UploadTarget,
94
+ VolumeFile,
95
+ VolumeInfo,
96
+ VolumeUploadRequest,
97
+ VolumeUploadResponse,
98
+ check_volume_name,
99
+ parse_vol,
100
+ )
101
+
86
102
  # shared error bodies
87
103
  from sequence_base.contract.errors import ErrorBody, ErrorCode, WarmingError
88
104
 
@@ -92,7 +108,7 @@ __all__ = [
92
108
  # contract 1
93
109
  "SeqFlags",
94
110
  "ImageStepUvPip", "ImageStepApt", "ImageStepEnv", "ImageStepRun", "ImageStep", "ImageSpec",
95
- "VolumeMount", "VolumeRef", "Concurrency", "ActionContract",
111
+ "VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract", "Embodiment",
96
112
  "PolicySpec", "PolicyRegistration",
97
113
  # contract 2
98
114
  "MAX_CODE_BUNDLE_BYTES", "CodeBundle",
@@ -107,6 +123,9 @@ __all__ = [
107
123
  "AdapterKind", "AdapterTransform",
108
124
  # contract 6
109
125
  "SecretRef", "SecretCreateRequest", "SecretInfo", "BootstrapResponse", "UsageReport",
126
+ # contract 7
127
+ "MAX_FILES", "MAX_UPLOAD_BYTES", "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse",
128
+ "VolumeInfo", "check_volume_name", "parse_vol",
110
129
  # errors
111
130
  "ErrorCode", "ErrorBody", "WarmingError",
112
131
  ]
@@ -50,6 +50,9 @@ class Observation(BaseModel):
50
50
  images: list[ImageFrame] = Field(..., min_length=1, max_length=256)
51
51
  proprioception: Proprioception
52
52
  instruction: str
53
+ # Named state vectors that are not joints — an end-effector axis-angle, an object pose — so they need not
54
+ # be smuggled into joint_positions. Absent when the policy declares none. {io-contract.md "Observation.state"}
55
+ state: dict[str, list[float]] | None = None
53
56
 
54
57
 
55
58
  class ActionSpace(str, Enum):
@@ -15,6 +15,7 @@ from pydantic import BaseModel, ConfigDict, Field
15
15
 
16
16
  # The 503 cold/warming body is defined once in contract.errors and reused here — no second shape.
17
17
  from sequence_base.contract.errors import WarmingError
18
+ from sequence_base.contract.io import ActionContract, ObservationContract
18
19
 
19
20
  __all__ = ["Lease", "ConnectRequest", "ConnectResponse", "WarmingError"]
20
21
 
@@ -35,6 +36,7 @@ class ConnectRequest(BaseModel):
35
36
  model_config = ConfigDict(extra="forbid")
36
37
 
37
38
  model: str = Field(..., description="Deployment name to connect to")
39
+ embodiment: str | None = Field(default=None, description="Which declared embodiment this session drives")
38
40
 
39
41
 
40
42
  class ConnectResponse(BaseModel):
@@ -51,3 +53,7 @@ class ConnectResponse(BaseModel):
51
53
  lease: Lease
52
54
  warming: bool = Field(default=False, description="True → worker still warming; see warming_eta_s")
53
55
  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
+ action: ActionContract | None = None
58
+ observation: ObservationContract | None = None
59
+ embodiment: str | None = None
@@ -21,6 +21,8 @@ from typing import Literal
21
21
  from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
22
22
 
23
23
  from sequence_base.contract.accounts import SecretRef
24
+ from sequence_base.contract.volumes import VOL_SCHEME, parse_vol
25
+ from sequence_base.contract.io import Embodiment, ObservationContract
24
26
  from sequence_base.contract.policy import (
25
27
  ActionContract,
26
28
  Concurrency,
@@ -103,6 +105,15 @@ class DeploymentPolicy(BaseModel):
103
105
  streaming: bool = False
104
106
  frames_per_chunk: int | None = None
105
107
  secrets: list[SecretRef] = Field(default_factory=list)
108
+ # The observation contract the worker checks every /act against (None: undeclared), and per-robot
109
+ # contracts for a multi-embodiment policy — the worker picks one by the session's embodiment.
110
+ observation: ObservationContract | None = None
111
+ embodiments: dict[str, Embodiment] | None = None
112
+ # Did the author declare `action=` (or embodiments)? `action` is required on the wire, so an SDK that had
113
+ # nothing declared sends a placeholder; only a declared contract is enforced on the worker and handed to the
114
+ # robot. None (an older SDK) is treated as undeclared, so an older policy is never refused for the
115
+ # placeholder it never chose. {io-contract.md "旧 SDK 写的 POLICY 照样能 BUILD"}
116
+ action_declared: bool | None = None
106
117
 
107
118
 
108
119
  MAX_CODE_BUNDLE_BYTES = 8 * 1024 * 1024
@@ -182,11 +193,15 @@ class DeploymentRequest(BaseModel):
182
193
 
183
194
  @model_validator(mode="after")
184
195
  def _weights_have_an_origin(self) -> "DeploymentRequest":
185
- """The checkpoint the build provisions: a gs:// prefix or an hf:// repo, never empty. PolicySpec already
186
- enforces this for SDK-authored deploys; this holds it for a body POSTed by anything else."""
196
+ """The checkpoint the build provisions: a gs:// prefix, an hf:// repo or an uploaded vol:// volume, never
197
+ empty. PolicySpec already enforces this for SDK-authored deploys; this holds it for any POSTed body."""
187
198
  uri = self.weights.uri.strip()
199
+ if uri.startswith(VOL_SCHEME):
200
+ parse_vol(uri)
201
+ return self
188
202
  if not any(uri.startswith(s) and len(uri) > len(s) for s in _WEIGHTS_SCHEMES):
189
- raise ValueError("weights.uri must be a gs://bucket/prefix or an hf://org/model checkpoint")
203
+ raise ValueError("weights.uri must be a gs://bucket/prefix, an hf://org/model repo or a "
204
+ "vol://<name> volume")
190
205
  return self
191
206
 
192
207
 
@@ -0,0 +1,83 @@
1
+ """
2
+ The I/O contract — what a policy reads and what it returns, declared once and checked everywhere.
3
+
4
+ In one sentence: the author declares `action=` (and `observation=`, or per-robot `embodiments=`) on `@seq.policy`
5
+ → it rides the deploy body into the worker's manifest → the worker refuses an /act whose observation does not
6
+ match before inference, and a chunk that does not match before it leaves → connect hands the same contracts to
7
+ the robot, which checks and assembles against them. {gs-plans/customization/io-contract.md}
8
+
9
+ Kept in its own module (not policy.py) because both the policy declaration and the connect response use it, and
10
+ policy → accounts → connect would otherwise import back into policy.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ from typing import Any
15
+
16
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
17
+
18
+ from sequence_base.contract.act import ActionSpace
19
+
20
+
21
+ class ActionContract(BaseModel):
22
+ """The fixed obs→ActionChunk contract a policy promises. Every field required; `action_space` reuses
23
+ contract 4's enum (not a free string). Each ActionChunk echoes exactly these invariants back."""
24
+
25
+ model_config = ConfigDict(extra="forbid")
26
+
27
+ robot: str
28
+ action_space: ActionSpace
29
+ action_dim: int
30
+ control_frequency_hz: float
31
+
32
+
33
+ # The proprioception fields an observation contract may require, with the wire field each one names.
34
+ PROPRIO_FIELDS = ("joint_positions", "joint_velocities", "end_effector_pose", "gripper")
35
+
36
+
37
+ class Camera(BaseModel):
38
+ """One camera view a policy reads: the size it expects. Informational at the worker (the policy resizes);
39
+ the client resizes and letterboxes to it before sending, so the bytes on the wire are what is used."""
40
+
41
+ model_config = ConfigDict(extra="forbid")
42
+
43
+ width: int = Field(..., gt=0, le=8192)
44
+ height: int = Field(..., gt=0, le=8192)
45
+
46
+
47
+ class ObservationContract(BaseModel):
48
+ """What a policy reads, declared once: the worker checks every /act against it before inference, the
49
+ client assembles to it. An observation that does not match is refused with what is missing — it is never
50
+ inferred on. {gs-plans/customization/io-contract.md "二、观测契约"}"""
51
+
52
+ model_config = ConfigDict(extra="forbid")
53
+
54
+ cameras: dict[str, Camera] = Field(default_factory=dict, description="view name → expected size")
55
+ history: int = Field(default=1, ge=1, le=64, description="frames per view the policy reads")
56
+ proprio: dict[str, int] = Field(default_factory=dict,
57
+ description="proprioception field → its length, e.g. joint_positions: 7")
58
+ state: dict[str, int] = Field(default_factory=dict,
59
+ description="named extra state vector → its length (Observation.state)")
60
+ instruction: bool = Field(default=True, description="whether a non-empty instruction is required")
61
+
62
+ @field_validator("proprio")
63
+ @classmethod
64
+ def _known_proprio(cls, v: dict[str, int]) -> dict[str, int]:
65
+ bad = sorted(set(v) - set(PROPRIO_FIELDS))
66
+ if bad:
67
+ raise ValueError(f"proprio keys must be among {PROPRIO_FIELDS}; got {bad} — put other vectors in state=")
68
+ if any(n < 1 for n in v.values()):
69
+ raise ValueError("each proprio length must be at least 1")
70
+ return v
71
+
72
+
73
+ class Embodiment(BaseModel):
74
+ """One robot body a multi-embodiment policy serves: its action contract, its observation contract, and
75
+ free-form metadata the policy reads (e.g. which normalisation statistics). Chosen per session at connect."""
76
+
77
+ model_config = ConfigDict(extra="forbid")
78
+
79
+ action: ActionContract
80
+ observation: ObservationContract | None = None
81
+ meta: dict[str, Any] = Field(default_factory=dict)
82
+
83
+
@@ -8,10 +8,19 @@ Every shape here forbids unknown fields and pins exact field types — no `Optio
8
8
  """
9
9
  from __future__ import annotations
10
10
 
11
+ from typing import Any
12
+
11
13
  from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
12
14
 
13
15
  from sequence_base.contract.accounts import SecretRef
14
- from sequence_base.contract.act import ActionSpace
16
+ from sequence_base.contract.act import ActionSpace # noqa: F401 — re-exported for older imports
17
+ from sequence_base.contract.io import ( # noqa: F401 — the I/O contract lives in io.py, re-exported here
18
+ PROPRIO_FIELDS,
19
+ ActionContract,
20
+ Camera,
21
+ Embodiment,
22
+ ObservationContract,
23
+ )
15
24
  from sequence_base.contract.flags import SeqFlags
16
25
 
17
26
  __all__ = [
@@ -112,18 +121,6 @@ class Concurrency(BaseModel):
112
121
  target_inputs: int
113
122
 
114
123
 
115
- class ActionContract(BaseModel):
116
- """The fixed obs→ActionChunk contract a policy promises. Every field required; `action_space` reuses
117
- contract 4's enum (not a free string). Each ActionChunk echoes exactly these invariants back."""
118
-
119
- model_config = ConfigDict(extra="forbid")
120
-
121
- robot: str
122
- action_space: ActionSpace
123
- action_dim: int
124
- control_frequency_hz: float
125
-
126
-
127
124
  class PolicySpec(BaseModel):
128
125
  """What `@seq.policy(...)` accrues — a policy's complete infra declaration, losslessly. `action` is
129
126
  required (no default): a policy without an obs→ActionChunk contract is not deployable."""
@@ -133,7 +130,7 @@ class PolicySpec(BaseModel):
133
130
  name: str
134
131
  gpu: str | list[str] = Field(..., description="A GPU type, or a list = cross-provider fallback")
135
132
  image: ImageSpec
136
- weights: str = Field(..., description="Weights URI; scheme restricted to gs:// or hf://")
133
+ weights: str = Field(..., description="Weights URI: gs://, hf:// or vol:// (an uploaded volume)")
137
134
  volumes: dict[str, VolumeRef] = Field(default_factory=dict)
138
135
  secrets: list[SecretRef] = Field(default_factory=list)
139
136
  min_containers: int = 0
@@ -144,12 +141,20 @@ class PolicySpec(BaseModel):
144
141
  streaming: bool = False
145
142
  frames_per_chunk: int | None = None
146
143
  action: ActionContract
144
+ # What the policy reads (None: undeclared — an older SDK); per-robot contracts when one model drives several.
145
+ observation: ObservationContract | None = None
146
+ embodiments: dict[str, Embodiment] | None = None
147
147
 
148
148
  @field_validator("weights")
149
149
  @classmethod
150
150
  def _weights_scheme(cls, v: str) -> str:
151
+ # vol:// is a volume the owner uploaded (contract 7); the gateway resolves it per owner at deploy.
152
+ if v.startswith("vol://"):
153
+ from sequence_base.contract.volumes import parse_vol
154
+ parse_vol(v)
155
+ return v
151
156
  if not (v.startswith("gs://") or v.startswith("hf://")):
152
- raise ValueError("weights must be a gs:// or hf:// URI")
157
+ raise ValueError("weights must be a gs://, hf:// or vol:// URI")
153
158
  return v
154
159
 
155
160
 
@@ -0,0 +1,141 @@
1
+ """
2
+ contract 7 — volumes: a user's named, versioned files on the platform (an uploaded checkpoint, a dataset).
3
+
4
+ In one sentence: `seq volume put <name> <dir>` lists the local files with their sizes and md5s → POST
5
+ /v1/volumes/{name}/uploads answers one signed, resumable upload target per file → the client sends each file
6
+ straight to its target → POST /v1/volumes/{name}/uploads/{upload_id}/commit checks every object's size and md5
7
+ and makes that version ready (and billed) → a deploy names it `weights="vol://<name>"` (or `@v<n>` to pin).
8
+
9
+ WHY direct-to-storage targets rather than streaming through the API: a checkpoint is gigabytes, and the API
10
+ host caps a request body at 32 MiB and would carry every byte twice. WHY the md5 list up front: the commit can
11
+ then prove the platform holds exactly the bytes the client had — a truncated or corrupted upload is refused
12
+ instead of becoming the weights a robot runs on.
13
+ {gs-plans/customization/volumes.md "上传流程" + "为什么不直接上传进 MODAL VOLUME"}
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import base64
18
+ import binascii
19
+ import re
20
+ from typing import Literal
21
+
22
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
23
+
24
+ VOL_SCHEME = "vol://"
25
+ # A volume name addresses it everywhere (CLI, `vol://`, console) — the same slug rule as a deployment name.
26
+ _NAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,63}")
27
+ _VERSION = re.compile(r"v([1-9][0-9]{0,8})")
28
+ # Bounds on one upload: enough for any single policy checkpoint or a modest dataset, small enough that a
29
+ # runaway client cannot ask for millions of signed targets or reserve unbounded storage in one call.
30
+ MAX_FILES = 10_000
31
+ MAX_UPLOAD_BYTES = 500 * 1024 ** 3
32
+
33
+
34
+ def parse_vol(uri: str) -> tuple[str, int | None]:
35
+ """`vol://<name>[@v<n>]` → (name, n or None). Raises ValueError naming the problem."""
36
+ if not uri.startswith(VOL_SCHEME):
37
+ raise ValueError(f"{uri!r} is not a vol:// reference")
38
+ name, _, ver = uri[len(VOL_SCHEME):].strip().rstrip("/").partition("@")
39
+ if not _NAME.fullmatch(name):
40
+ raise ValueError(f"{uri!r}: a volume name is 1-64 letters, digits, '.', '_' or '-', starting with a "
41
+ "letter or digit")
42
+ if not ver:
43
+ return name, None
44
+ m = _VERSION.fullmatch(ver)
45
+ if not m:
46
+ raise ValueError(f"{uri!r}: pin a version as vol://{name}@v<n>, e.g. @v2")
47
+ return name, int(m.group(1))
48
+
49
+
50
+ def check_volume_name(name: str) -> str:
51
+ """The slug rule for a volume name, raised as ValueError — for path parameters and model fields alike."""
52
+ if not _NAME.fullmatch(name or ""):
53
+ raise ValueError("a volume name is 1-64 letters, digits, '.', '_' or '-', starting with a letter or digit")
54
+ return name
55
+
56
+
57
+ class VolumeFile(BaseModel):
58
+ """One file of an upload: its path inside the volume, its size, and the base64 md5 of its bytes (the form
59
+ the storage layer reports back, so the commit compares like with like)."""
60
+
61
+ model_config = ConfigDict(extra="forbid")
62
+
63
+ path: str = Field(..., min_length=1, max_length=1024)
64
+ size: int = Field(..., ge=0)
65
+ md5: str
66
+
67
+ @field_validator("path")
68
+ @classmethod
69
+ def _relative_and_clean(cls, v: str) -> str:
70
+ # A path is a key under the volume's own prefix: relative, forward slashes, no empty / . / .. segment —
71
+ # so no upload can name an object outside its version's prefix.
72
+ parts = v.split("/")
73
+ if v.startswith("/") or "\\" in v or any(p in ("", ".", "..") for p in parts):
74
+ raise ValueError(f"file path {v!r} must be relative, use '/', and have no empty, '.' or '..' part")
75
+ return v
76
+
77
+ @field_validator("md5")
78
+ @classmethod
79
+ def _md5_is_16_bytes(cls, v: str) -> str:
80
+ try:
81
+ raw = base64.b64decode(v, validate=True)
82
+ except (binascii.Error, ValueError):
83
+ raise ValueError("md5 must be the base64 of the 16-byte digest") from None
84
+ if len(raw) != 16:
85
+ raise ValueError("md5 must be the base64 of the 16-byte digest")
86
+ return v
87
+
88
+
89
+ class VolumeUploadRequest(BaseModel):
90
+ """POST /v1/volumes/{name}/uploads — the files of one new version."""
91
+
92
+ model_config = ConfigDict(extra="forbid")
93
+
94
+ kind: Literal["checkpoint", "dataset"] = "checkpoint"
95
+ files: list[VolumeFile] = Field(..., min_length=1, max_length=MAX_FILES)
96
+
97
+ @model_validator(mode="after")
98
+ def _unique_and_bounded(self) -> "VolumeUploadRequest":
99
+ paths = [f.path for f in self.files]
100
+ if len(set(paths)) != len(paths):
101
+ raise ValueError("each file path may appear only once")
102
+ if sum(f.size for f in self.files) > MAX_UPLOAD_BYTES:
103
+ raise ValueError(f"one upload may hold at most {MAX_UPLOAD_BYTES // 1024 ** 3} GB")
104
+ return self
105
+
106
+
107
+ class UploadTarget(BaseModel):
108
+ """Where one file goes: POST `url` with `headers` to open a resumable session, then send the bytes to the
109
+ session address that answer returns. Valid only for this file's object and only until the upload expires."""
110
+
111
+ model_config = ConfigDict(extra="forbid")
112
+
113
+ path: str
114
+ url: str
115
+ headers: dict[str, str] = Field(default_factory=dict)
116
+
117
+
118
+ class VolumeUploadResponse(BaseModel):
119
+ """201 — one target per file; the version becomes ready only at commit."""
120
+
121
+ model_config = ConfigDict(extra="forbid")
122
+
123
+ upload_id: str
124
+ name: str
125
+ version: int = Field(..., ge=1)
126
+ targets: list[UploadTarget]
127
+ expires_at: str
128
+
129
+
130
+ class VolumeInfo(BaseModel):
131
+ """One volume as its owner sees it — never where it is stored."""
132
+
133
+ model_config = ConfigDict(extra="forbid")
134
+
135
+ name: str
136
+ kind: str
137
+ state: Literal["uploading", "active", "deleted"]
138
+ version: int | None = None
139
+ size_bytes: int = 0
140
+ created_at: str | None = None
141
+ updated_at: str | None = None
@@ -56,6 +56,13 @@ from sequence_base.contract.deployments import (
56
56
  VolumeAttach,
57
57
  )
58
58
  from sequence_base.contract.errors import ErrorBody, WarmingError
59
+ from sequence_base.contract.volumes import (
60
+ UploadTarget,
61
+ VolumeFile,
62
+ VolumeInfo,
63
+ VolumeUploadRequest,
64
+ VolumeUploadResponse,
65
+ )
59
66
  from sequence_base.contract.eval import (
60
67
  AdapterTransform,
61
68
  ClampInfo,
@@ -72,6 +79,9 @@ from sequence_base.contract.eval import (
72
79
  )
73
80
  from sequence_base.contract.flags import seqflags_json_schema
74
81
  from sequence_base.contract.policy import (
82
+ Camera,
83
+ Embodiment,
84
+ ObservationContract,
75
85
  ActionContract,
76
86
  Concurrency,
77
87
  ImageSpec,
@@ -106,6 +116,9 @@ WIRE_MODELS: dict[str, type[BaseModel]] = {
106
116
  "VolumeRef": VolumeRef,
107
117
  "Concurrency": Concurrency,
108
118
  "ActionContract": ActionContract,
119
+ "Camera": Camera,
120
+ "ObservationContract": ObservationContract,
121
+ "Embodiment": Embodiment,
109
122
  "PolicySpec": PolicySpec,
110
123
  "PolicyRegistration": PolicyRegistration,
111
124
  # contract 2 — deployments
@@ -149,6 +162,12 @@ WIRE_MODELS: dict[str, type[BaseModel]] = {
149
162
  "SecretInfo": SecretInfo,
150
163
  "BootstrapResponse": BootstrapResponse,
151
164
  "UsageReport": UsageReport,
165
+ # contract 7 — volumes
166
+ "VolumeFile": VolumeFile,
167
+ "VolumeUploadRequest": VolumeUploadRequest,
168
+ "UploadTarget": UploadTarget,
169
+ "VolumeUploadResponse": VolumeUploadResponse,
170
+ "VolumeInfo": VolumeInfo,
152
171
  # shared error bodies
153
172
  "ErrorBody": ErrorBody,
154
173
  "WarmingError": WarmingError,
@@ -98,5 +98,5 @@
98
98
  ],
99
99
  "title": "ActionChunk",
100
100
  "type": "object",
101
- "x-contract-version": "0.4.1"
101
+ "x-contract-version": "0.4.3"
102
102
  }
@@ -39,5 +39,5 @@
39
39
  ],
40
40
  "title": "ActionContract",
41
41
  "type": "object",
42
- "x-contract-version": "0.4.1"
42
+ "x-contract-version": "0.4.3"
43
43
  }
@@ -19,5 +19,5 @@
19
19
  ],
20
20
  "title": "ActionStep",
21
21
  "type": "object",
22
- "x-contract-version": "0.4.1"
22
+ "x-contract-version": "0.4.3"
23
23
  }
@@ -38,5 +38,5 @@
38
38
  ],
39
39
  "title": "AdapterTransform",
40
40
  "type": "object",
41
- "x-contract-version": "0.4.1"
41
+ "x-contract-version": "0.4.3"
42
42
  }
@@ -39,5 +39,5 @@
39
39
  },
40
40
  "title": "Autoscaler",
41
41
  "type": "object",
42
- "x-contract-version": "0.4.1"
42
+ "x-contract-version": "0.4.3"
43
43
  }
@@ -393,5 +393,5 @@
393
393
  ],
394
394
  "title": "BenchmarkRegisterRequest",
395
395
  "type": "object",
396
- "x-contract-version": "0.4.1"
396
+ "x-contract-version": "0.4.3"
397
397
  }
@@ -207,7 +207,7 @@
207
207
  ],
208
208
  "title": "SeqFlags",
209
209
  "type": "integer",
210
- "x-contract-version": "0.4.1",
210
+ "x-contract-version": "0.4.3",
211
211
  "x-flag-names": {
212
212
  "CHECK": 65536,
213
213
  "INFER": 16,
@@ -397,5 +397,5 @@
397
397
  ],
398
398
  "title": "BenchmarkSpec",
399
399
  "type": "object",
400
- "x-contract-version": "0.4.1"
400
+ "x-contract-version": "0.4.3"
401
401
  }
@@ -40,5 +40,5 @@
40
40
  ],
41
41
  "title": "BenchmarkStatusResponse",
42
42
  "type": "object",
43
- "x-contract-version": "0.4.1"
43
+ "x-contract-version": "0.4.3"
44
44
  }
@@ -72,5 +72,5 @@
72
72
  ],
73
73
  "title": "BootstrapResponse",
74
74
  "type": "object",
75
- "x-contract-version": "0.4.1"
75
+ "x-contract-version": "0.4.3"
76
76
  }
@@ -0,0 +1,25 @@
1
+ {
2
+ "additionalProperties": false,
3
+ "description": "One camera view a policy reads: the size it expects. Informational at the worker (the policy resizes);\nthe client resizes and letterboxes to it before sending, so the bytes on the wire are what is used.",
4
+ "properties": {
5
+ "height": {
6
+ "exclusiveMinimum": 0,
7
+ "maximum": 8192,
8
+ "title": "Height",
9
+ "type": "integer"
10
+ },
11
+ "width": {
12
+ "exclusiveMinimum": 0,
13
+ "maximum": 8192,
14
+ "title": "Width",
15
+ "type": "integer"
16
+ }
17
+ },
18
+ "required": [
19
+ "width",
20
+ "height"
21
+ ],
22
+ "title": "Camera",
23
+ "type": "object",
24
+ "x-contract-version": "0.4.3"
25
+ }
@@ -23,5 +23,5 @@
23
23
  ],
24
24
  "title": "ClampInfo",
25
25
  "type": "object",
26
- "x-contract-version": "0.4.1"
26
+ "x-contract-version": "0.4.3"
27
27
  }