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.
- {sequence_base-0.3.2 → sequence_base-0.3.3}/PKG-INFO +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/pyproject.toml +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/__init__.py +9 -1
- sequence_base-0.3.3/sequence_base/constants/version.py +10 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/__init__.py +16 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/deployments.py +8 -3
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/policy.py +7 -2
- sequence_base-0.3.3/sequence_base/contract/volumes.py +141 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schema_export.py +13 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ActionChunk.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ActionContract.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ActionStep.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/AdapterTransform.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Autoscaler.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/BenchmarkRegisterRequest.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/BenchmarkSpec.json +2 -2
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/BootstrapResponse.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ClampInfo.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/CodeBundle.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Concurrency.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Conditions.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ConnectRequest.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ConnectResponse.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/DeploymentAccepted.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/DeploymentPolicy.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/DeploymentRequest.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ErrorBody.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Estimate.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/EvalReport.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/EvalRequest.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/EvalResponse.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageFrame.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageSpec.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageStepApt.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageStepEnv.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageStepRun.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/ImageStepUvPip.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Lease.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/LossyTransform.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/MetricAgg.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Observation.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/PolicyRegistration.json +3 -3
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/PolicySpec.json +2 -2
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Proprioception.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/RenderSpec.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Resources.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/RolloutResult.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/SecretCreateRequest.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/SecretInfo.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/SecretRef.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/SeqFlags.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Step.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/SuiteAgg.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/TaskAgg.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Timeouts.json +1 -1
- sequence_base-0.3.3/sequence_base/schemas/UploadTarget.json +28 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/UsageReport.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/Variant.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/VolumeAttach.json +1 -1
- sequence_base-0.3.3/sequence_base/schemas/VolumeFile.json +29 -0
- sequence_base-0.3.3/sequence_base/schemas/VolumeInfo.json +72 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/VolumeMount.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/VolumeRef.json +1 -1
- sequence_base-0.3.3/sequence_base/schemas/VolumeUploadRequest.json +60 -0
- sequence_base-0.3.3/sequence_base/schemas/VolumeUploadResponse.json +69 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/schemas/WarmingError.json +1 -1
- {sequence_base-0.3.2 → sequence_base-0.3.3}/tests/test_contracts.py +55 -0
- sequence_base-0.3.2/sequence_base/constants/version.py +0 -9
- {sequence_base-0.3.2 → sequence_base-0.3.3}/.github/workflows/ci.yml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/.gitignore +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/README.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/README.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/REVIEW.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/START.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/accounts.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/billing.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/build/BUILD_LOG.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/build/goal_sha256.json +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/build/gs-build-wave.js +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/build/gs-spec-goals.js +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/container.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/contracts.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/deploy/PLAN.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/deploy/PROD_REORG.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/deploy/SCHEMA_COMPAT.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/deploy/archive/sequences-gw.genseq-506918.20261001.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/eval.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/gateway.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/A1.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/A2.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/A3.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/C1.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/C2.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/C3.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/C4.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/C5.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/G1.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/G2.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/G3.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/G4.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/G5.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/O1.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/O2.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/O3.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/O4.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/O5.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/R1.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/R2.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/R3.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S1.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S2.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S3.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S4.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S5.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/S6.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/V1-storage.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/V2-user-quotas.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/W0.1.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/goals/W0.2.yaml +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/README.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/architecture.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/connect.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/cost.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/custom-domain.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/debugging.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/goals.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/limits.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/plan.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/white-label.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/modal/wiring.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/observability.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/references.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/docs/platform/sdk.md +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/auth.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/codec/__init__.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/codec/image.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/codec/resize.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/constants/__init__.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/constants/env.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/constants/headers.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/accounts.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/act.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/benchmarks.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/connect.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/errors.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/eval.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/contract/flags.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/errors/__init__.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/errors/transport.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/obs/__init__.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/obs/log.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/obs/rid.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/telemetry/__init__.py +0 -0
- {sequence_base-0.3.2 → sequence_base-0.3.3}/sequence_base/telemetry/events.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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
|
|
186
|
-
enforces this for SDK-authored deploys; this holds it for
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
],
|
|
208
208
|
"title": "SeqFlags",
|
|
209
209
|
"type": "integer",
|
|
210
|
-
"x-contract-version": "0.4.
|
|
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.
|
|
400
|
+
"x-contract-version": "0.4.2"
|
|
401
401
|
}
|
|
@@ -286,7 +286,7 @@
|
|
|
286
286
|
"type": "object"
|
|
287
287
|
},
|
|
288
288
|
"weights": {
|
|
289
|
-
"description": "Weights URI
|
|
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.
|
|
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.
|
|
437
|
+
"x-contract-version": "0.4.2"
|
|
438
438
|
}
|