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