sequence-base 0.3.2__tar.gz → 0.3.3__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 (158) hide show
  1. {sequence_base-0.3.2 → sequence_base-0.3.3}/PKG-INFO +1 -1
  2. {sequence_base-0.3.2 → sequence_base-0.3.3}/pyproject.toml +1 -1
  3. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/__init__.py +9 -1
  4. sequence_base-0.3.3/sequence_base/constants/version.py +10 -0
  5. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/__init__.py +16 -0
  6. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/deployments.py +8 -3
  7. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/policy.py +7 -2
  8. sequence_base-0.3.3/sequence_base/contract/volumes.py +141 -0
  9. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schema_export.py +13 -0
  10. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ActionChunk.json +1 -1
  11. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ActionContract.json +1 -1
  12. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ActionStep.json +1 -1
  13. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/AdapterTransform.json +1 -1
  14. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Autoscaler.json +1 -1
  15. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/BenchmarkRegisterRequest.json +1 -1
  16. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/BenchmarkSpec.json +2 -2
  17. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
  18. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/BootstrapResponse.json +1 -1
  19. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ClampInfo.json +1 -1
  20. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/CodeBundle.json +1 -1
  21. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Concurrency.json +1 -1
  22. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Conditions.json +1 -1
  23. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ConnectRequest.json +1 -1
  24. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ConnectResponse.json +1 -1
  25. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/DeploymentAccepted.json +1 -1
  26. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/DeploymentPolicy.json +1 -1
  27. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/DeploymentRequest.json +1 -1
  28. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
  29. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ErrorBody.json +1 -1
  30. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Estimate.json +1 -1
  31. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
  32. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/EvalReport.json +1 -1
  33. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/EvalRequest.json +1 -1
  34. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/EvalResponse.json +1 -1
  35. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageFrame.json +1 -1
  36. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageSpec.json +1 -1
  37. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageStepApt.json +1 -1
  38. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageStepEnv.json +1 -1
  39. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageStepRun.json +1 -1
  40. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageStepUvPip.json +1 -1
  41. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Lease.json +1 -1
  42. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/LossyTransform.json +1 -1
  43. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/MetricAgg.json +1 -1
  44. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Observation.json +1 -1
  45. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/PolicyRegistration.json +3 -3
  46. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/PolicySpec.json +2 -2
  47. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Proprioception.json +1 -1
  48. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/RenderSpec.json +1 -1
  49. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Resources.json +1 -1
  50. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/RolloutResult.json +1 -1
  51. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/SecretCreateRequest.json +1 -1
  52. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/SecretInfo.json +1 -1
  53. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/SecretRef.json +1 -1
  54. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/SeqFlags.json +1 -1
  55. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Step.json +1 -1
  56. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/SuiteAgg.json +1 -1
  57. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/TaskAgg.json +1 -1
  58. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Timeouts.json +1 -1
  59. sequence_base-0.3.3/sequence_base/schemas/UploadTarget.json +28 -0
  60. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/UsageReport.json +1 -1
  61. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Variant.json +1 -1
  62. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/VolumeAttach.json +1 -1
  63. sequence_base-0.3.3/sequence_base/schemas/VolumeFile.json +29 -0
  64. sequence_base-0.3.3/sequence_base/schemas/VolumeInfo.json +72 -0
  65. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/VolumeMount.json +1 -1
  66. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/VolumeRef.json +1 -1
  67. sequence_base-0.3.3/sequence_base/schemas/VolumeUploadRequest.json +60 -0
  68. sequence_base-0.3.3/sequence_base/schemas/VolumeUploadResponse.json +69 -0
  69. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/WarmingError.json +1 -1
  70. {sequence_base-0.3.2 → sequence_base-0.3.3}/tests/test_contracts.py +55 -0
  71. sequence_base-0.3.2/sequence_base/constants/version.py +0 -9
  72. {sequence_base-0.3.2 → sequence_base-0.3.3}/.github/workflows/ci.yml +0 -0
  73. {sequence_base-0.3.2 → sequence_base-0.3.3}/.gitignore +0 -0
  74. {sequence_base-0.3.2 → sequence_base-0.3.3}/README.md +0 -0
  75. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/README.md +0 -0
  76. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/REVIEW.md +0 -0
  77. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/START.md +0 -0
  78. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/accounts.md +0 -0
  79. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/billing.md +0 -0
  80. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/build/BUILD_LOG.md +0 -0
  81. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/build/goal_sha256.json +0 -0
  82. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/build/gs-build-wave.js +0 -0
  83. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/build/gs-spec-goals.js +0 -0
  84. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/container.md +0 -0
  85. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/contracts.md +0 -0
  86. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/deploy/PLAN.md +0 -0
  87. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/deploy/PROD_REORG.md +0 -0
  88. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/deploy/SCHEMA_COMPAT.md +0 -0
  89. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/deploy/archive/sequences-gw.genseq-506918.20261001.yaml +0 -0
  90. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/eval.md +0 -0
  91. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/gateway.md +0 -0
  92. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/A1.yaml +0 -0
  93. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/A2.yaml +0 -0
  94. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/A3.yaml +0 -0
  95. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/C1.yaml +0 -0
  96. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/C2.yaml +0 -0
  97. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/C3.yaml +0 -0
  98. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/C4.yaml +0 -0
  99. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/C5.yaml +0 -0
  100. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/G1.yaml +0 -0
  101. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/G2.yaml +0 -0
  102. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/G3.yaml +0 -0
  103. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/G4.yaml +0 -0
  104. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/G5.yaml +0 -0
  105. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/O1.yaml +0 -0
  106. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/O2.yaml +0 -0
  107. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/O3.yaml +0 -0
  108. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/O4.yaml +0 -0
  109. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/O5.yaml +0 -0
  110. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/R1.yaml +0 -0
  111. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/R2.yaml +0 -0
  112. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/R3.yaml +0 -0
  113. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S1.yaml +0 -0
  114. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S2.yaml +0 -0
  115. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S3.yaml +0 -0
  116. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S4.yaml +0 -0
  117. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S5.yaml +0 -0
  118. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S6.yaml +0 -0
  119. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/V1-storage.md +0 -0
  120. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/V2-user-quotas.md +0 -0
  121. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/W0.1.yaml +0 -0
  122. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/W0.2.yaml +0 -0
  123. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/README.md +0 -0
  124. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/architecture.md +0 -0
  125. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/connect.md +0 -0
  126. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/cost.md +0 -0
  127. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/custom-domain.md +0 -0
  128. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/debugging.md +0 -0
  129. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/goals.md +0 -0
  130. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/limits.md +0 -0
  131. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/plan.md +0 -0
  132. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/white-label.md +0 -0
  133. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/wiring.md +0 -0
  134. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/observability.md +0 -0
  135. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/references.md +0 -0
  136. {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/sdk.md +0 -0
  137. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/auth.py +0 -0
  138. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/codec/__init__.py +0 -0
  139. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/codec/image.py +0 -0
  140. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/codec/resize.py +0 -0
  141. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/constants/__init__.py +0 -0
  142. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/constants/env.py +0 -0
  143. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/constants/headers.py +0 -0
  144. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/accounts.py +0 -0
  145. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/act.py +0 -0
  146. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/benchmarks.py +0 -0
  147. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/connect.py +0 -0
  148. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/errors.py +0 -0
  149. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/eval.py +0 -0
  150. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/flags.py +0 -0
  151. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/errors/__init__.py +0 -0
  152. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/errors/transport.py +0 -0
  153. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/obs/__init__.py +0 -0
  154. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/obs/log.py +0 -0
  155. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/obs/rid.py +0 -0
  156. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/telemetry/__init__.py +0 -0
  157. {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/telemetry/events.py +0 -0
  158. {sequence_base-0.3.2 → sequence_base-0.3.3}/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.3
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.3"
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"]
@@ -15,6 +15,12 @@ catalogue values stay in container / gateway / spec.py.
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
17
  MAX_CODE_BUNDLE_BYTES,
18
+ UploadTarget,
19
+ VolumeFile,
20
+ VolumeInfo,
21
+ VolumeUploadRequest,
22
+ VolumeUploadResponse,
23
+ parse_vol,
18
24
  ActionChunk,
19
25
  ActionContract,
20
26
  ActionSpace,
@@ -122,6 +128,8 @@ __all__ = [
122
128
  "AdapterKind", "AdapterTransform",
123
129
  # contract 6 — accounts / secret / billing
124
130
  "SecretRef", "SecretCreateRequest", "SecretInfo", "BootstrapResponse", "UsageReport",
131
+ # contract 7 — volumes (uploaded checkpoints and datasets)
132
+ "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse", "VolumeInfo", "parse_vol",
125
133
  # error bodies + version
126
134
  "ErrorCode", "ErrorBody", "WarmingError", "CONTRACT_VERSION",
127
135
  # obs
@@ -132,4 +140,4 @@ __all__ = [
132
140
  "auth", "WorkerLogEvent",
133
141
  ]
134
142
 
135
- __version__ = "0.3.2"
143
+ __version__ = "0.3.3"
@@ -0,0 +1,10 @@
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.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
10
+ # 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)
@@ -83,6 +83,19 @@ from sequence_base.contract.accounts import (
83
83
  UsageReport,
84
84
  )
85
85
 
86
+ # contract 7 — volumes (uploaded checkpoints and datasets)
87
+ from sequence_base.contract.volumes import (
88
+ MAX_FILES,
89
+ MAX_UPLOAD_BYTES,
90
+ UploadTarget,
91
+ VolumeFile,
92
+ VolumeInfo,
93
+ VolumeUploadRequest,
94
+ VolumeUploadResponse,
95
+ check_volume_name,
96
+ parse_vol,
97
+ )
98
+
86
99
  # shared error bodies
87
100
  from sequence_base.contract.errors import ErrorBody, ErrorCode, WarmingError
88
101
 
@@ -107,6 +120,9 @@ __all__ = [
107
120
  "AdapterKind", "AdapterTransform",
108
121
  # contract 6
109
122
  "SecretRef", "SecretCreateRequest", "SecretInfo", "BootstrapResponse", "UsageReport",
123
+ # contract 7
124
+ "MAX_FILES", "MAX_UPLOAD_BYTES", "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse",
125
+ "VolumeInfo", "check_volume_name", "parse_vol",
110
126
  # errors
111
127
  "ErrorCode", "ErrorBody", "WarmingError",
112
128
  ]
@@ -21,6 +21,7 @@ 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
24
25
  from sequence_base.contract.policy import (
25
26
  ActionContract,
26
27
  Concurrency,
@@ -182,11 +183,15 @@ class DeploymentRequest(BaseModel):
182
183
 
183
184
  @model_validator(mode="after")
184
185
  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."""
186
+ """The checkpoint the build provisions: a gs:// prefix, an hf:// repo or an uploaded vol:// volume, never
187
+ empty. PolicySpec already enforces this for SDK-authored deploys; this holds it for any POSTed body."""
187
188
  uri = self.weights.uri.strip()
189
+ if uri.startswith(VOL_SCHEME):
190
+ parse_vol(uri)
191
+ return self
188
192
  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")
193
+ raise ValueError("weights.uri must be a gs://bucket/prefix, an hf://org/model repo or a "
194
+ "vol://<name> volume")
190
195
  return self
191
196
 
192
197
 
@@ -133,7 +133,7 @@ class PolicySpec(BaseModel):
133
133
  name: str
134
134
  gpu: str | list[str] = Field(..., description="A GPU type, or a list = cross-provider fallback")
135
135
  image: ImageSpec
136
- weights: str = Field(..., description="Weights URI; scheme restricted to gs:// or hf://")
136
+ weights: str = Field(..., description="Weights URI: gs://, hf:// or vol:// (an uploaded volume)")
137
137
  volumes: dict[str, VolumeRef] = Field(default_factory=dict)
138
138
  secrets: list[SecretRef] = Field(default_factory=list)
139
139
  min_containers: int = 0
@@ -148,8 +148,13 @@ class PolicySpec(BaseModel):
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,
@@ -149,6 +156,12 @@ WIRE_MODELS: dict[str, type[BaseModel]] = {
149
156
  "SecretInfo": SecretInfo,
150
157
  "BootstrapResponse": BootstrapResponse,
151
158
  "UsageReport": UsageReport,
159
+ # contract 7 — volumes
160
+ "VolumeFile": VolumeFile,
161
+ "VolumeUploadRequest": VolumeUploadRequest,
162
+ "UploadTarget": UploadTarget,
163
+ "VolumeUploadResponse": VolumeUploadResponse,
164
+ "VolumeInfo": VolumeInfo,
152
165
  # shared error bodies
153
166
  "ErrorBody": ErrorBody,
154
167
  "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.2"
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.2"
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.2"
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.2"
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.2"
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.2"
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.2",
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.2"
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.2"
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.2"
76
76
  }
@@ -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.2"
27
27
  }
@@ -23,5 +23,5 @@
23
23
  ],
24
24
  "title": "CodeBundle",
25
25
  "type": "object",
26
- "x-contract-version": "0.4.1"
26
+ "x-contract-version": "0.4.2"
27
27
  }
@@ -17,5 +17,5 @@
17
17
  ],
18
18
  "title": "Concurrency",
19
19
  "type": "object",
20
- "x-contract-version": "0.4.1"
20
+ "x-contract-version": "0.4.2"
21
21
  }
@@ -55,5 +55,5 @@
55
55
  ],
56
56
  "title": "Conditions",
57
57
  "type": "object",
58
- "x-contract-version": "0.4.1"
58
+ "x-contract-version": "0.4.2"
59
59
  }
@@ -12,5 +12,5 @@
12
12
  ],
13
13
  "title": "ConnectRequest",
14
14
  "type": "object",
15
- "x-contract-version": "0.4.1"
15
+ "x-contract-version": "0.4.2"
16
16
  }
@@ -77,5 +77,5 @@
77
77
  ],
78
78
  "title": "ConnectResponse",
79
79
  "type": "object",
80
- "x-contract-version": "0.4.1"
80
+ "x-contract-version": "0.4.2"
81
81
  }
@@ -18,5 +18,5 @@
18
18
  ],
19
19
  "title": "DeploymentAccepted",
20
20
  "type": "object",
21
- "x-contract-version": "0.4.1"
21
+ "x-contract-version": "0.4.2"
22
22
  }
@@ -97,5 +97,5 @@
97
97
  ],
98
98
  "title": "DeploymentPolicy",
99
99
  "type": "object",
100
- "x-contract-version": "0.4.1"
100
+ "x-contract-version": "0.4.2"
101
101
  }
@@ -463,5 +463,5 @@
463
463
  ],
464
464
  "title": "DeploymentRequest",
465
465
  "type": "object",
466
- "x-contract-version": "0.4.1"
466
+ "x-contract-version": "0.4.2"
467
467
  }
@@ -41,5 +41,5 @@
41
41
  ],
42
42
  "title": "DeploymentStatusResponse",
43
43
  "type": "object",
44
- "x-contract-version": "0.4.1"
44
+ "x-contract-version": "0.4.2"
45
45
  }
@@ -28,5 +28,5 @@
28
28
  ],
29
29
  "title": "ErrorBody",
30
30
  "type": "object",
31
- "x-contract-version": "0.4.1"
31
+ "x-contract-version": "0.4.2"
32
32
  }
@@ -21,5 +21,5 @@
21
21
  ],
22
22
  "title": "Estimate",
23
23
  "type": "object",
24
- "x-contract-version": "0.4.1"
24
+ "x-contract-version": "0.4.2"
25
25
  }
@@ -37,5 +37,5 @@
37
37
  ],
38
38
  "title": "EvalDryRunResponse",
39
39
  "type": "object",
40
- "x-contract-version": "0.4.1"
40
+ "x-contract-version": "0.4.2"
41
41
  }
@@ -183,5 +183,5 @@
183
183
  ],
184
184
  "title": "EvalReport",
185
185
  "type": "object",
186
- "x-contract-version": "0.4.1"
186
+ "x-contract-version": "0.4.2"
187
187
  }
@@ -107,5 +107,5 @@
107
107
  ],
108
108
  "title": "EvalRequest",
109
109
  "type": "object",
110
- "x-contract-version": "0.4.1"
110
+ "x-contract-version": "0.4.2"
111
111
  }
@@ -84,5 +84,5 @@
84
84
  ],
85
85
  "title": "EvalResponse",
86
86
  "type": "object",
87
- "x-contract-version": "0.4.1"
87
+ "x-contract-version": "0.4.2"
88
88
  }
@@ -32,5 +32,5 @@
32
32
  ],
33
33
  "title": "ImageFrame",
34
34
  "type": "object",
35
- "x-contract-version": "0.4.1"
35
+ "x-contract-version": "0.4.2"
36
36
  }
@@ -119,5 +119,5 @@
119
119
  },
120
120
  "title": "ImageSpec",
121
121
  "type": "object",
122
- "x-contract-version": "0.4.1"
122
+ "x-contract-version": "0.4.2"
123
123
  }
@@ -14,5 +14,5 @@
14
14
  ],
15
15
  "title": "ImageStepApt",
16
16
  "type": "object",
17
- "x-contract-version": "0.4.1"
17
+ "x-contract-version": "0.4.2"
18
18
  }
@@ -14,5 +14,5 @@
14
14
  ],
15
15
  "title": "ImageStepEnv",
16
16
  "type": "object",
17
- "x-contract-version": "0.4.1"
17
+ "x-contract-version": "0.4.2"
18
18
  }
@@ -14,5 +14,5 @@
14
14
  ],
15
15
  "title": "ImageStepRun",
16
16
  "type": "object",
17
- "x-contract-version": "0.4.1"
17
+ "x-contract-version": "0.4.2"
18
18
  }
@@ -14,5 +14,5 @@
14
14
  ],
15
15
  "title": "ImageStepUvPip",
16
16
  "type": "object",
17
- "x-contract-version": "0.4.1"
17
+ "x-contract-version": "0.4.2"
18
18
  }
@@ -24,5 +24,5 @@
24
24
  ],
25
25
  "title": "Lease",
26
26
  "type": "object",
27
- "x-contract-version": "0.4.1"
27
+ "x-contract-version": "0.4.2"
28
28
  }
@@ -17,5 +17,5 @@
17
17
  ],
18
18
  "title": "LossyTransform",
19
19
  "type": "object",
20
- "x-contract-version": "0.4.1"
20
+ "x-contract-version": "0.4.2"
21
21
  }
@@ -83,5 +83,5 @@
83
83
  ],
84
84
  "title": "MetricAgg",
85
85
  "type": "object",
86
- "x-contract-version": "0.4.1"
86
+ "x-contract-version": "0.4.2"
87
87
  }
@@ -129,5 +129,5 @@
129
129
  ],
130
130
  "title": "Observation",
131
131
  "type": "object",
132
- "x-contract-version": "0.4.1"
132
+ "x-contract-version": "0.4.2"
133
133
  }
@@ -286,7 +286,7 @@
286
286
  "type": "object"
287
287
  },
288
288
  "weights": {
289
- "description": "Weights URI; scheme restricted to gs:// or hf://",
289
+ "description": "Weights URI: gs://, hf:// or vol:// (an uploaded volume)",
290
290
  "title": "Weights",
291
291
  "type": "string"
292
292
  }
@@ -334,7 +334,7 @@
334
334
  ],
335
335
  "title": "SeqFlags",
336
336
  "type": "integer",
337
- "x-contract-version": "0.4.1",
337
+ "x-contract-version": "0.4.2",
338
338
  "x-flag-names": {
339
339
  "CHECK": 65536,
340
340
  "INFER": 16,
@@ -434,5 +434,5 @@
434
434
  ],
435
435
  "title": "PolicyRegistration",
436
436
  "type": "object",
437
- "x-contract-version": "0.4.1"
437
+ "x-contract-version": "0.4.2"
438
438
  }