sequence-base 0.4.34__tar.gz → 0.5.1__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.4.34 → sequence_base-0.5.1}/PKG-INFO +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/pyproject.toml +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/__init__.py +9 -8
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/constants/version.py +4 -1
- sequence_base-0.5.1/sequence_base/construct.py +173 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/__init__.py +40 -10
- sequence_base-0.5.1/sequence_base/contract/benchmarks.py +223 -0
- sequence_base-0.5.1/sequence_base/contract/connect.py +136 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/deployments.py +41 -25
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/eval.py +29 -32
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/flags.py +4 -4
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/own_io.py +14 -47
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/policy.py +120 -42
- sequence_base-0.5.1/sequence_base/contract/settings.py +357 -0
- sequence_base-0.5.1/sequence_base/contract/team.py +185 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/wire.py +9 -7
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/presets/robot_arm.py +9 -7
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schema_export.py +35 -13
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ActRequest.json +1 -15
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ActResponse.json +1 -15
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ActionChunk.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ActionContract.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ActionStep.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/AdapterPair.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/AddLocal.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Autoscaler.json +2 -23
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/BenchmarkRegisterRequest.json +31 -368
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/BenchmarkSpec.json +101 -134
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/BootstrapResponse.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Camera.json +3 -3
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/CodeBundle.json +1 -1
- sequence_base-0.5.1/sequence_base/schemas/Concurrency.json +113 -0
- sequence_base-0.5.1/sequence_base/schemas/ConnectRequest.json +68 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ConnectResponse.json +110 -218
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentAccepted.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentPolicy.json +61 -237
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentRequest.json +81 -265
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ErrorBody.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ErrorCodes.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Estimate.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/EvalReport.json +14 -29
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/EvalRequest.json +5 -41
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/EvalResponse.json +1 -1
- sequence_base-0.5.1/sequence_base/schemas/GroupObservation.json +41 -0
- sequence_base-0.5.1/sequence_base/schemas/GroupObserve.json +13 -0
- sequence_base-0.5.1/sequence_base/schemas/GroupObserved.json +16 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageFrame.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageSpec.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepAddLocal.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepApt.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepEnv.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepProject.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepRun.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepTemplateFiles.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ImageStepUvPip.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/InferParam.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Lease.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/MetricAgg.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Observation.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ObservationContract.json +3 -3
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Observations.json +3 -3
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ObserveRequest.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ObserveResponse.json +1 -1
- sequence_base-0.5.1/sequence_base/schemas/PlanAppend.json +13 -0
- sequence_base-0.5.1/sequence_base/schemas/PlanAppended.json +28 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/PolicyRegistration.json +88 -257
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/PolicySpec.json +81 -253
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Project.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Proprioception.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/ReplaySpec.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Resources.json +1 -1
- sequence_base-0.5.1/sequence_base/schemas/RolloutResult.json +90 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/SecretCreateRequest.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/SecretInfo.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/SecretRef.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/SeqFlags.json +4 -2
- sequence_base-0.5.1/sequence_base/schemas/SettingsChange.json +56 -0
- sequence_base-0.5.1/sequence_base/schemas/SettingsChanged.json +137 -0
- sequence_base-0.5.1/sequence_base/schemas/SettingsView.json +402 -0
- sequence_base-0.5.1/sequence_base/schemas/Standby.json +92 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Stream.json +3 -3
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/TaskAgg.json +1 -1
- sequence_base-0.5.1/sequence_base/schemas/TeamGroup.json +120 -0
- sequence_base-0.5.1/sequence_base/schemas/TeamGroups.json +135 -0
- sequence_base-0.5.1/sequence_base/schemas/TeamGroupsRequest.json +21 -0
- sequence_base-0.5.1/sequence_base/schemas/TeamMember.json +39 -0
- sequence_base-0.5.1/sequence_base/schemas/TeamPlans.json +51 -0
- sequence_base-0.5.1/sequence_base/schemas/TeamPlansWritten.json +30 -0
- sequence_base-0.5.1/sequence_base/schemas/TeamSpec.json +22 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/TemplateFiles.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Tensors.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/Timeouts.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/UploadTarget.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeFile.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeInfo.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeMount.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeRef.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/schemas/WarmingError.json +1 -1
- sequence_base-0.4.34/sequence_base/construct.py +0 -124
- sequence_base-0.4.34/sequence_base/contract/benchmarks.py +0 -305
- sequence_base-0.4.34/sequence_base/contract/connect.py +0 -74
- sequence_base-0.4.34/sequence_base/schemas/Actions.json +0 -63
- sequence_base-0.4.34/sequence_base/schemas/BenchmarkConditionSpec.json +0 -54
- sequence_base-0.4.34/sequence_base/schemas/Concurrency.json +0 -100
- sequence_base-0.4.34/sequence_base/schemas/Conditions.json +0 -59
- sequence_base-0.4.34/sequence_base/schemas/ConnectRequest.json +0 -16
- sequence_base-0.4.34/sequence_base/schemas/RenderSpec.json +0 -40
- sequence_base-0.4.34/sequence_base/schemas/RolloutResult.json +0 -59
- sequence_base-0.4.34/sequence_base/schemas/Step.json +0 -86
- sequence_base-0.4.34/sequence_base/schemas/Variant.json +0 -24
- {sequence_base-0.4.34 → sequence_base-0.5.1}/.gitignore +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/README.md +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/auth.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/by_name.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/codec/__init__.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/codec/image.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/codec/marked.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/constants/__init__.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/constants/cards.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/constants/headers.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/constants/limits.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/accounts.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/act.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/adapters.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/errors.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/io.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/obs_schedule.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/observe.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/retired.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/templates.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/contract/volumes.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/episode.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/errors/__init__.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/errors/transport.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/msgpack_io.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/obs/__init__.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/obs/log.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/obs/rid.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/presets/__init__.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/presets/frames.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/telemetry/__init__.py +0 -0
- {sequence_base-0.4.34 → sequence_base-0.5.1}/sequence_base/telemetry/events.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sequence-base
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
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
|
Project-URL: Changelog, https://generalsequences.com/docs/changelog/
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sequence-base"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.1"
|
|
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.10"
|
|
6
6
|
dependencies = ["pydantic>=2.0"]
|
|
@@ -37,6 +37,11 @@ from sequence_base.contract import (
|
|
|
37
37
|
parse_vol,
|
|
38
38
|
TEMPLATE_SCHEME,
|
|
39
39
|
parse_template,
|
|
40
|
+
SETTINGS,
|
|
41
|
+
Setting,
|
|
42
|
+
SettingsChange,
|
|
43
|
+
SettingsView,
|
|
44
|
+
SettingsChanged,
|
|
40
45
|
AdapterPair,
|
|
41
46
|
parse_pairs,
|
|
42
47
|
ActionChunk,
|
|
@@ -44,14 +49,12 @@ from sequence_base.contract import (
|
|
|
44
49
|
ActionSpace,
|
|
45
50
|
ActionStep,
|
|
46
51
|
Autoscaler,
|
|
47
|
-
BenchmarkConditionSpec,
|
|
48
52
|
BenchmarkRegisterRequest,
|
|
49
53
|
BenchmarkStatusResponse,
|
|
50
54
|
BenchmarkSpec,
|
|
51
55
|
BootstrapResponse,
|
|
52
56
|
CodeBundle,
|
|
53
57
|
Concurrency,
|
|
54
|
-
Conditions,
|
|
55
58
|
ConnectRequest,
|
|
56
59
|
ConnectResponse,
|
|
57
60
|
DeploymentAccepted,
|
|
@@ -85,10 +88,8 @@ from sequence_base.contract import (
|
|
|
85
88
|
PolicySpec,
|
|
86
89
|
Observations,
|
|
87
90
|
Stream,
|
|
88
|
-
Actions,
|
|
89
91
|
Tensors,
|
|
90
92
|
Proprioception,
|
|
91
|
-
RenderSpec,
|
|
92
93
|
ReplaySpec,
|
|
93
94
|
Resources,
|
|
94
95
|
RolloutResult,
|
|
@@ -96,10 +97,8 @@ from sequence_base.contract import (
|
|
|
96
97
|
SecretInfo,
|
|
97
98
|
SecretRef,
|
|
98
99
|
SeqFlags,
|
|
99
|
-
Step,
|
|
100
100
|
TaskAgg,
|
|
101
101
|
Timeouts,
|
|
102
|
-
Variant,
|
|
103
102
|
VolumeMount,
|
|
104
103
|
VolumeRef,
|
|
105
104
|
WarmingError,
|
|
@@ -146,7 +145,7 @@ __all__ = [
|
|
|
146
145
|
"FROZEN_PATH", "TemplateFiles", "ImageStepTemplateFiles", "Project", "ImageStepProject", "ImageStep", "ImageSpec",
|
|
147
146
|
"VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract",
|
|
148
147
|
"PolicySpec", "PolicyRegistration", "PolicyConstructor",
|
|
149
|
-
"Observations", "Stream", "
|
|
148
|
+
"Observations", "Stream", "Tensors",
|
|
150
149
|
|
|
151
150
|
"MAX_CODE_BUNDLE_BYTES", "CodeBundle",
|
|
152
151
|
"Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
|
|
@@ -154,7 +153,7 @@ __all__ = [
|
|
|
154
153
|
|
|
155
154
|
"Lease", "ConnectRequest", "ConnectResponse",
|
|
156
155
|
|
|
157
|
-
"
|
|
156
|
+
"ReplaySpec", "BenchmarkSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
|
|
158
157
|
"EvalRequest", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
|
|
159
158
|
"EvalStatus", "TaskAgg", "MetricAgg", "EvalReport",
|
|
160
159
|
|
|
@@ -162,6 +161,8 @@ __all__ = [
|
|
|
162
161
|
|
|
163
162
|
"VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse", "VolumeInfo", "parse_vol",
|
|
164
163
|
|
|
164
|
+
"SETTINGS", "Setting", "SettingsChange", "SettingsView", "SettingsChanged",
|
|
165
|
+
|
|
165
166
|
"TEMPLATE_SCHEME", "parse_template",
|
|
166
167
|
|
|
167
168
|
"AdapterPair", "parse_pairs",
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""
|
|
2
|
+
How a class built by its constructor is built - a policy's or a team's (PolicyRegistration.constructor), a benchmark's
|
|
3
|
+
(BenchmarkSpec.constructor): its constructor - or the factory it names, a classmethod of it or a module's function -
|
|
4
|
+
called with the arguments its decorator was given (init=), once the forms in them are filled in.
|
|
5
|
+
|
|
6
|
+
In one sentence: a value {"$config": path, "$set": {...}} becomes the YAML or JSON file at `path` - from the folder of
|
|
7
|
+
the class's file - read where the class runs, with `$set`'s keys laid over its top level → in every string, a policy's
|
|
8
|
+
"{weights}" becomes the weights directory and "{weights.<part>}" a named part's, a benchmark's "{num_worlds}" how many
|
|
9
|
+
worlds this eval builds and "{policy.<field>}" what the evaluated policy declares (POLICY_FIELDS) - a string that is one
|
|
10
|
+
form alone becomes its value as it is, an int or None, one among other text its value as text → `cls.<make>(*args,
|
|
11
|
+
**kwargs)`, or `function(*args, **kwargs)` for make="package.module:function" - the module imported where the class
|
|
12
|
+
runs - or `cls(*args, **kwargs)` without a factory.
|
|
13
|
+
|
|
14
|
+
WHY a config file is read where the policy runs: it is part of the project (XPolicyLab's deploy.yml beside its
|
|
15
|
+
model.py) and goes up with it, so one copy - the one the policy runs with - is read, and "{weights}" in it means the
|
|
16
|
+
worker's directory.
|
|
17
|
+
|
|
18
|
+
WHY a benchmark's too: its worlds are built after the machine's snapshot by the class's own constructor - a repository's
|
|
19
|
+
env as it is, handed how many worlds and the policy's rate where its own code takes them - where a method of the
|
|
20
|
+
platform's (@seq.setup) built them before.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import importlib
|
|
26
|
+
import json
|
|
27
|
+
import os
|
|
28
|
+
import re
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
__all__ = ["CONFIG_KEY", "FORMS", "POLICY_FIELDS", "SET_KEY", "ConstructError", "build", "config", "expand",
|
|
32
|
+
"is_config", "maker", "read_config"]
|
|
33
|
+
|
|
34
|
+
CONFIG_KEY, SET_KEY = "$config", "$set"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
POLICY_FIELDS = ("hz", "action_chunk_len", "action_chunk_exec_len")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
FORMS = re.compile(r"\{(?:weights(?:\.(?P<part>[A-Za-z_][A-Za-z0-9_-]*))?|(?P<worlds>num_worlds)"
|
|
42
|
+
r"|policy\.(?P<field>[A-Za-z_][A-Za-z0-9_]*))\}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ConstructError(ValueError):
|
|
46
|
+
"""Arguments that cannot be filled in: a config file missing, unreadable or of another kind, a weights placeholder
|
|
47
|
+
with no weights - or no such part - to fill it, "{num_worlds}" where no worlds are built, or a policy field no policy
|
|
48
|
+
declares."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def config(path: str, **overrides: Any) -> dict:
|
|
52
|
+
"""A config file's contents with `overrides` laid over its top level, as the worker reads it (`seq.config`)."""
|
|
53
|
+
if not isinstance(path, str) or not path:
|
|
54
|
+
raise ConstructError("a config file is a path, from the folder of the class's file")
|
|
55
|
+
if os.path.isabs(path):
|
|
56
|
+
raise ConstructError(f"{path}: give a config file's path from the folder of the class's file - the policy runs "
|
|
57
|
+
"it where the upload puts it")
|
|
58
|
+
return {CONFIG_KEY: path, SET_KEY: dict(overrides)}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def is_config(value: Any) -> bool:
|
|
62
|
+
return isinstance(value, dict) and set(value) == {CONFIG_KEY, SET_KEY} and isinstance(value[CONFIG_KEY], str)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def read_config(path: str) -> Any:
|
|
66
|
+
"""The YAML (.yml, .yaml) or JSON (.json) file at `path`."""
|
|
67
|
+
kind = os.path.splitext(path)[1].lower()
|
|
68
|
+
if kind not in (".json", ".yml", ".yaml"):
|
|
69
|
+
raise ConstructError(f"{path}: a config file is .json, .yml or .yaml")
|
|
70
|
+
if not os.path.isfile(path):
|
|
71
|
+
raise ConstructError(f"{path}: no such config file")
|
|
72
|
+
with open(path, encoding="utf-8") as f:
|
|
73
|
+
if kind == ".json":
|
|
74
|
+
return json.load(f)
|
|
75
|
+
try:
|
|
76
|
+
import yaml
|
|
77
|
+
except ImportError:
|
|
78
|
+
raise ConstructError(f"{path}: reading YAML needs pyyaml") from None
|
|
79
|
+
return yaml.safe_load(f)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def expand(value: Any, *, base_dir: str, weights: Any = None, worlds: int | None = None, policy: Any = None) -> Any:
|
|
83
|
+
"""`value` with its config files read (from `base_dir`) and its forms filled: a policy's from `weights` - the
|
|
84
|
+
weights directory, or {part: directory} for named parts, or None for a policy without weights - a benchmark's from
|
|
85
|
+
`worlds`, how many worlds this eval builds, and `policy`, what the evaluated policy declares ({field: value}, or an
|
|
86
|
+
object with them; None where none is evaluated, each field None). A string that is one form alone becomes its value
|
|
87
|
+
as it is; one form among other text, its value as text."""
|
|
88
|
+
forms = {"base_dir": base_dir, "weights": weights, "worlds": worlds, "policy": policy}
|
|
89
|
+
if is_config(value):
|
|
90
|
+
loaded = read_config(os.path.join(base_dir, value[CONFIG_KEY]))
|
|
91
|
+
if value[SET_KEY] and not isinstance(loaded, dict):
|
|
92
|
+
raise ConstructError(f"{value[CONFIG_KEY]} holds a {type(loaded).__name__}, which has no keys to set")
|
|
93
|
+
merged = {**loaded, **value[SET_KEY]} if isinstance(loaded, dict) else loaded
|
|
94
|
+
return expand(merged, **forms)
|
|
95
|
+
if isinstance(value, dict):
|
|
96
|
+
return {k: expand(v, **forms) for k, v in value.items()}
|
|
97
|
+
if isinstance(value, (list, tuple)):
|
|
98
|
+
return [expand(v, **forms) for v in value]
|
|
99
|
+
if isinstance(value, str):
|
|
100
|
+
alone = FORMS.fullmatch(value)
|
|
101
|
+
if alone:
|
|
102
|
+
return _filled(alone, weights, worlds, policy)
|
|
103
|
+
return FORMS.sub(lambda m: str(_filled(m, weights, worlds, policy)), value)
|
|
104
|
+
return value
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _filled(form: re.Match, weights: Any, worlds: int | None, policy: Any) -> Any:
|
|
108
|
+
if form.group("worlds"):
|
|
109
|
+
if worlds is None:
|
|
110
|
+
raise ConstructError("{num_worlds} is how many worlds this eval builds, and none is handed: a benchmark of one "
|
|
111
|
+
"world an instance takes none")
|
|
112
|
+
return worlds
|
|
113
|
+
field = form.group("field")
|
|
114
|
+
if field is not None:
|
|
115
|
+
if field not in POLICY_FIELDS:
|
|
116
|
+
raise ConstructError(f"{{policy.{field}}}: a policy declares {', '.join(POLICY_FIELDS)}")
|
|
117
|
+
if policy is None:
|
|
118
|
+
return None
|
|
119
|
+
return policy.get(field) if isinstance(policy, dict) else getattr(policy, field, None)
|
|
120
|
+
return _weights_of(form.group("part"), weights)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _weights_of(part: str | None, weights: Any) -> str:
|
|
124
|
+
if weights is None:
|
|
125
|
+
raise ConstructError("{weights} names the weights directory, and this policy declares no weights=")
|
|
126
|
+
if isinstance(weights, dict):
|
|
127
|
+
if part is None:
|
|
128
|
+
raise ConstructError(f"its weights come in named parts ({', '.join(sorted(weights))}): name one, "
|
|
129
|
+
f"{{weights.<part>}}")
|
|
130
|
+
if part not in weights:
|
|
131
|
+
raise ConstructError(f"{{weights.{part}}}: its weights have no part {part!r} "
|
|
132
|
+
f"({', '.join(sorted(weights))})")
|
|
133
|
+
return str(weights[part])
|
|
134
|
+
if part is not None:
|
|
135
|
+
raise ConstructError(f"{{weights.{part}}} names a part, and its weights come whole: {{weights}}")
|
|
136
|
+
return str(weights)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def build(cls: type, constructor: Any, *, base_dir: str, weights: Any = None, worlds: int | None = None,
|
|
140
|
+
policy: Any = None, params: dict | None = None) -> Any:
|
|
141
|
+
"""The instance `constructor` (a PolicyConstructor) makes of `cls`: `cls.<make>(*args, **kwargs)`, the function
|
|
142
|
+
make="package.module:function" names called so, or `cls(...)` - its arguments filled (expand), and `params`, what
|
|
143
|
+
an eval set of the constructor's other arguments (--param benchmark.NAME=VALUE), laid over them. A keyword argument
|
|
144
|
+
that is "{policy.<field>}" alone is left out while that field is None - no policy evaluated, at a registration
|
|
145
|
+
check, or one that declares none - so the constructor's own default holds, as a benchmark's own code did with what
|
|
146
|
+
@seq.setup was handed before."""
|
|
147
|
+
forms = {"base_dir": base_dir, "weights": weights, "worlds": worlds, "policy": policy}
|
|
148
|
+
args = expand(list(constructor.args), **forms)
|
|
149
|
+
kwargs = {}
|
|
150
|
+
for name, value in dict(constructor.kwargs).items():
|
|
151
|
+
filled = expand(value, **forms)
|
|
152
|
+
alone = FORMS.fullmatch(value) if isinstance(value, str) else None
|
|
153
|
+
if filled is None and alone is not None and alone.group("field") is not None:
|
|
154
|
+
continue
|
|
155
|
+
kwargs[name] = filled
|
|
156
|
+
return maker(cls, constructor.make)(*args, **{**kwargs, **(params or {})})
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def maker(cls: type, make: str | None) -> Any:
|
|
160
|
+
"""What builds the instance: `cls` for no make=, its classmethod or staticmethod `make`, or - make written
|
|
161
|
+
"package.module:function" - that module's function, the module imported (as the policy's code imports it).
|
|
162
|
+
"""
|
|
163
|
+
if not make:
|
|
164
|
+
return cls
|
|
165
|
+
module, colon, name = make.rpartition(":")
|
|
166
|
+
if not colon:
|
|
167
|
+
return getattr(cls, make)
|
|
168
|
+
found = getattr(importlib.import_module(module), name, None)
|
|
169
|
+
if found is None:
|
|
170
|
+
raise ConstructError(f"make={make!r}: {module} has no function {name!r}")
|
|
171
|
+
if not callable(found):
|
|
172
|
+
raise ConstructError(f"make={make!r}: {module}.{name} is not callable ({type(found).__name__})")
|
|
173
|
+
return found
|
|
@@ -45,7 +45,7 @@ from sequence_base.contract.policy import (
|
|
|
45
45
|
)
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
from sequence_base.contract.own_io import
|
|
48
|
+
from sequence_base.contract.own_io import Observations, Stream, Tensors
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
from sequence_base.contract.deployments import (
|
|
@@ -63,19 +63,14 @@ from sequence_base.contract.deployments import (
|
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
from sequence_base.contract.connect import ConnectRequest, ConnectResponse, Lease
|
|
66
|
+
from sequence_base.contract.connect import ConnectRequest, ConnectResponse, Lease, Standby
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
from sequence_base.contract.benchmarks import (
|
|
70
|
-
BenchmarkConditionSpec,
|
|
71
70
|
BenchmarkRegisterRequest,
|
|
72
71
|
BenchmarkStatusResponse,
|
|
73
72
|
BenchmarkSpec,
|
|
74
|
-
Conditions,
|
|
75
|
-
RenderSpec,
|
|
76
73
|
ReplaySpec,
|
|
77
|
-
Step,
|
|
78
|
-
Variant,
|
|
79
74
|
)
|
|
80
75
|
from sequence_base.contract.eval import (
|
|
81
76
|
Estimate,
|
|
@@ -117,6 +112,35 @@ from sequence_base.contract.volumes import (
|
|
|
117
112
|
)
|
|
118
113
|
|
|
119
114
|
|
|
115
|
+
from sequence_base.contract.settings import (
|
|
116
|
+
SETTINGS,
|
|
117
|
+
Applied,
|
|
118
|
+
Setting,
|
|
119
|
+
SettingChanged,
|
|
120
|
+
SettingState,
|
|
121
|
+
SettingsChange,
|
|
122
|
+
SettingsChanged,
|
|
123
|
+
SettingsView,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
from sequence_base.contract.team import (
|
|
128
|
+
TEAM_DEFAULT_GROUP,
|
|
129
|
+
GroupObservation,
|
|
130
|
+
GroupObserve,
|
|
131
|
+
GroupObserved,
|
|
132
|
+
PlanAppend,
|
|
133
|
+
PlanAppended,
|
|
134
|
+
TeamGroup,
|
|
135
|
+
TeamGroups,
|
|
136
|
+
TeamGroupsRequest,
|
|
137
|
+
TeamMember,
|
|
138
|
+
TeamPlans,
|
|
139
|
+
TeamPlansWritten,
|
|
140
|
+
TeamSpec,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
120
144
|
from sequence_base.contract.errors import ERRORS, ErrorBody, ErrorCode, ErrorSpec, WarmingError, code_for_status
|
|
121
145
|
|
|
122
146
|
__all__ = [
|
|
@@ -130,15 +154,15 @@ __all__ = [
|
|
|
130
154
|
"TemplateFiles", "ImageStepTemplateFiles", "Project", "ImageStepProject", "FROZEN_PATH", "ImageStep", "ImageSpec",
|
|
131
155
|
"VolumeMount", "VolumeRef", "Concurrency", "ActionContract", "Camera", "ObservationContract",
|
|
132
156
|
"PolicySpec", "PolicyRegistration", "PolicyConstructor",
|
|
133
|
-
"Observations", "Stream", "
|
|
157
|
+
"Observations", "Stream", "Tensors",
|
|
134
158
|
|
|
135
159
|
"MAX_CODE_BUNDLE_BYTES", "CodeBundle",
|
|
136
160
|
"Resources", "Autoscaler", "Timeouts", "DeploymentPolicy", "InferParam", "DeploymentRequest",
|
|
137
161
|
"DeploymentAccepted", "DeploymentState", "DeploymentStatusResponse",
|
|
138
162
|
|
|
139
|
-
"Lease", "ConnectRequest", "ConnectResponse",
|
|
163
|
+
"Lease", "ConnectRequest", "ConnectResponse", "Standby",
|
|
140
164
|
|
|
141
|
-
"
|
|
165
|
+
"ReplaySpec", "BenchmarkSpec", "BenchmarkRegisterRequest", "BenchmarkStatusResponse",
|
|
142
166
|
"EvalRequest", "Estimate", "EvalResponse", "EvalDryRunResponse", "RolloutResult",
|
|
143
167
|
"EvalStatus", "TaskAgg", "MetricAgg", "EvalReport",
|
|
144
168
|
|
|
@@ -147,6 +171,12 @@ __all__ = [
|
|
|
147
171
|
"MAX_FILES", "MAX_UPLOAD_BYTES", "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse",
|
|
148
172
|
"VolumeInfo", "check_volume_name", "parse_vol",
|
|
149
173
|
|
|
174
|
+
"SETTINGS", "Setting", "SettingsChange", "Applied", "SettingState", "SettingsView", "SettingChanged",
|
|
175
|
+
"SettingsChanged",
|
|
176
|
+
|
|
177
|
+
"TEAM_DEFAULT_GROUP", "TeamSpec", "TeamGroupsRequest", "GroupObservation", "TeamMember", "TeamGroup", "TeamGroups",
|
|
178
|
+
"TeamPlans", "TeamPlansWritten", "PlanAppend", "PlanAppended", "GroupObserve", "GroupObserved",
|
|
179
|
+
|
|
150
180
|
"TEMPLATE_SCHEME", "parse_template",
|
|
151
181
|
|
|
152
182
|
"AdapterPair", "parse_pairs",
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contract 5 (registration half) - a benchmark is the env you already have, run as it is: its class is built by its own
|
|
3
|
+
constructor after the machine's snapshot (init= its arguments, "{num_worlds}" and "{policy.<field>}" filled), a short class
|
|
4
|
+
of yours marks two methods - @seq.reset starts an episode of a task at a seed, @seq.step plays one action and says what
|
|
5
|
+
came of it - and declares its tasks, its seeds and how long an episode may run. The platform reads none of its
|
|
6
|
+
observations or actions: they go to the policy, and come from it, as a robot's do. The eval *run* wire types
|
|
7
|
+
(RolloutResult, EvalReport, ...) live in contract/eval.py.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Literal
|
|
14
|
+
|
|
15
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
16
|
+
|
|
17
|
+
from sequence_base.constants.cards import DRIVER, check_driver
|
|
18
|
+
from sequence_base.constants.limits import MAX_ENVS, TASK_DESCRIPTION_MAX
|
|
19
|
+
from sequence_base.contract.accounts import SecretRef
|
|
20
|
+
from sequence_base.contract.deployments import CodeBundle, DeploymentState, InferParam
|
|
21
|
+
from sequence_base.contract.flags import BENCHMARK_BINDABLE, SeqFlags
|
|
22
|
+
from sequence_base.contract.policy import KIND_NAME, ImageSpec, PolicyConstructor, VolumeRef
|
|
23
|
+
from sequence_base.construct import FORMS
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"ReplaySpec",
|
|
27
|
+
"BenchmarkSpec",
|
|
28
|
+
"BenchmarkRegisterRequest",
|
|
29
|
+
"BenchmarkStatusResponse",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
TASK_NAME = r"^[A-Za-z0-9][A-Za-z0-9._/:-]{0,127}$"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
SETUP_GONE = ("@seq.setup is gone: a benchmark builds its worlds in its constructor, after the machine's snapshot - "
|
|
38
|
+
'init= its arguments, "{num_worlds}" how many worlds this eval builds and "{policy.<field>}" what the '
|
|
39
|
+
"evaluated policy declares; a benchmark registered with @seq.setup is registered again with sequence-ai "
|
|
40
|
+
"0.51.1 or later (seq deploy)")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _names_worlds(constructor: PolicyConstructor | None) -> bool:
|
|
44
|
+
"""Whether init= hands the constructor "{num_worlds}" anywhere - in a string, at any depth."""
|
|
45
|
+
def walk(value) -> bool:
|
|
46
|
+
if isinstance(value, str):
|
|
47
|
+
return any(m.group("worlds") for m in FORMS.finditer(value))
|
|
48
|
+
if isinstance(value, dict):
|
|
49
|
+
return any(walk(v) for v in value.values())
|
|
50
|
+
if isinstance(value, (list, tuple)):
|
|
51
|
+
return any(walk(v) for v in value)
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
return constructor is not None and walk([constructor.args, constructor.kwargs])
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _check_tasks(tasks: dict, seeds: list, max_seconds) -> None:
|
|
58
|
+
import re as _re
|
|
59
|
+
|
|
60
|
+
if not tasks:
|
|
61
|
+
raise ValueError("tasks names at least one task: {name: description}")
|
|
62
|
+
for name, text in tasks.items():
|
|
63
|
+
if not _re.match(TASK_NAME, name):
|
|
64
|
+
raise ValueError(f"task {name!r}: 1-128 letters, digits, '.', '_', '/', ':' or '-', starting with a letter "
|
|
65
|
+
"or digit")
|
|
66
|
+
if len(text or "") > TASK_DESCRIPTION_MAX:
|
|
67
|
+
raise ValueError(f"tasks[{name!r}] is {len(text)} characters: at most {TASK_DESCRIPTION_MAX}")
|
|
68
|
+
if not seeds:
|
|
69
|
+
raise ValueError("seeds names at least one seed")
|
|
70
|
+
if len(set(seeds)) != len(seeds):
|
|
71
|
+
raise ValueError("seeds names each seed once")
|
|
72
|
+
limits = max_seconds if isinstance(max_seconds, dict) else {"": max_seconds}
|
|
73
|
+
for task, limit in limits.items():
|
|
74
|
+
if task and task not in tasks:
|
|
75
|
+
raise ValueError(f"max_seconds names {task!r}, which is not one of its tasks")
|
|
76
|
+
if not limit > 0:
|
|
77
|
+
raise ValueError(f"max_seconds{'[' + repr(task) + ']' if task else ''} must be above 0 s, not {limit!r}")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class ReplaySpec(BaseModel):
|
|
81
|
+
"""a.k.a. `seq.Replay` — declares that the platform records the eval's 3D replay: each step every node's world
|
|
82
|
+
transform, drawn in the owner's browser after the eval. `engine` picks the platform's
|
|
83
|
+
adapter that reads the simulation's state ("mujoco", "sapien", "isaac"); "own" means the benchmark hands the frames itself
|
|
84
|
+
(`seq.replay.scene` / `seq.replay.frame`); None takes the benchmark's own `engine`. `state` names the benchmark
|
|
85
|
+
method an adapter calls for the engine's state of an env (or of a batched benchmark's slots), `looks` the one that
|
|
86
|
+
says what the adapter cannot read - a colour or a visibility each episode sets. Whether an eval records it is the
|
|
87
|
+
eval's `replay` ("3d" by default, "none")."""
|
|
88
|
+
|
|
89
|
+
model_config = ConfigDict(extra="forbid")
|
|
90
|
+
|
|
91
|
+
engine: Literal["mujoco", "sapien", "isaac", "own"] | None = Field(
|
|
92
|
+
None, description="The adapter that reads the simulation: 'mujoco' (incl. MuJoCo Warp), 'sapien', 'isaac', or "
|
|
93
|
+
"'own' (the benchmark hands frames itself); None: the benchmark's engine")
|
|
94
|
+
state: str | None = Field(None, pattern=r"^[A-Za-z_][A-Za-z0-9_]*$",
|
|
95
|
+
description="The benchmark method an adapter calls for an env's (or slots') engine state")
|
|
96
|
+
looks: str | None = Field(None, pattern=r"^[A-Za-z_][A-Za-z0-9_]*$",
|
|
97
|
+
description="The benchmark method that says each episode's colours or visibility")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class BenchmarkSpec(BaseModel):
|
|
101
|
+
"""What `@seq.benchmark(...)` and its marked methods add up to, as an eval runs it. `bindings` names the methods,
|
|
102
|
+
RESET and STEP; `constructor` the arguments its class is built with after the machine's snapshot (init=) - which a
|
|
103
|
+
vectorized env, one stepping `worlds` worlds a call (@seq.concurrent(sizes=[N]) on its step), hands how many,
|
|
104
|
+
"{num_worlds}". `max_seconds` is an episode's time on the robot's clock, the same for every task or {task: seconds};
|
|
105
|
+
`assets` are versioned scene volumes mounted at /assets; `secrets` by name."""
|
|
106
|
+
|
|
107
|
+
model_config = ConfigDict(extra="forbid")
|
|
108
|
+
|
|
109
|
+
name: str
|
|
110
|
+
image: ImageSpec
|
|
111
|
+
tasks: dict[str, str] = Field(..., description="Each task's name and what it asks of the robot, shown beside it")
|
|
112
|
+
seeds: list[int] = Field(..., description="The seeds an episode starts at; trial k of a task runs seeds[k % len]")
|
|
113
|
+
max_seconds: float | dict[str, float] = Field(..., description="An episode's time limit, or {task: seconds}")
|
|
114
|
+
handler_ref: str
|
|
115
|
+
bindings: dict[SeqFlags, str] = Field(..., description="RESET / STEP -> method name")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
worlds: int | None = Field(default=None, ge=1, le=MAX_ENVS)
|
|
119
|
+
|
|
120
|
+
gpu: str | None = None
|
|
121
|
+
|
|
122
|
+
driver: str | None = Field(default=None, pattern=DRIVER)
|
|
123
|
+
assets: list[VolumeRef] = Field(default_factory=list)
|
|
124
|
+
|
|
125
|
+
replay: ReplaySpec | None = None
|
|
126
|
+
cpu: int | None = None
|
|
127
|
+
memory_gb: int | None = None
|
|
128
|
+
secrets: list[SecretRef] = Field(default_factory=list)
|
|
129
|
+
|
|
130
|
+
engine: str | None = Field(default=None, pattern=KIND_NAME)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
constructor: PolicyConstructor | None = None
|
|
135
|
+
|
|
136
|
+
@model_validator(mode="after")
|
|
137
|
+
def _tasks(self) -> "BenchmarkSpec":
|
|
138
|
+
_check_tasks(self.tasks, self.seeds, self.max_seconds)
|
|
139
|
+
return self
|
|
140
|
+
|
|
141
|
+
@model_validator(mode="after")
|
|
142
|
+
def _bindings(self) -> "BenchmarkSpec":
|
|
143
|
+
if SeqFlags.SETUP in self.bindings:
|
|
144
|
+
raise ValueError(SETUP_GONE)
|
|
145
|
+
bad = set(self.bindings) - set(BENCHMARK_BINDABLE)
|
|
146
|
+
if bad:
|
|
147
|
+
names = ", ".join(sorted(f.name or str(f.value) for f in bad))
|
|
148
|
+
raise ValueError(f"a benchmark marks @seq.reset and @seq.step only; got {names}")
|
|
149
|
+
missing = [f.name for f in (SeqFlags.RESET, SeqFlags.STEP) if f not in self.bindings]
|
|
150
|
+
if missing:
|
|
151
|
+
raise ValueError(f"a benchmark marks @seq.reset and @seq.step; {', '.join(missing).lower()} is missing")
|
|
152
|
+
if self.worlds is not None and not _names_worlds(self.constructor):
|
|
153
|
+
raise ValueError("a vectorized benchmark (@seq.concurrent on its step) builds its worlds in its "
|
|
154
|
+
'constructor, handed how many by init= - init={"worlds": "{num_worlds}"}')
|
|
155
|
+
return self
|
|
156
|
+
|
|
157
|
+
@model_validator(mode="after")
|
|
158
|
+
def _driver_of_a_card(self) -> "BenchmarkSpec":
|
|
159
|
+
check_driver(self.driver, self.gpu)
|
|
160
|
+
return self
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class BenchmarkRegisterRequest(BaseModel):
|
|
164
|
+
"""POST /v1/benchmarks - what `seq benchmark register` sends: the declaration, the code, and the parameters its
|
|
165
|
+
constructor takes with a default beside what init= hands it (each settable by an eval's --param, with its type)."""
|
|
166
|
+
|
|
167
|
+
model_config = ConfigDict(extra="forbid")
|
|
168
|
+
|
|
169
|
+
name: str
|
|
170
|
+
image: ImageSpec
|
|
171
|
+
tasks: dict[str, str]
|
|
172
|
+
seeds: list[int]
|
|
173
|
+
max_seconds: float | dict[str, float]
|
|
174
|
+
handler_ref: str
|
|
175
|
+
worlds: int | None = Field(default=None, ge=1, le=MAX_ENVS)
|
|
176
|
+
assets: list[VolumeRef] = Field(default_factory=list)
|
|
177
|
+
replay: ReplaySpec | None = None
|
|
178
|
+
cpu: int | None = None
|
|
179
|
+
memory_gb: int | None = None
|
|
180
|
+
|
|
181
|
+
code: CodeBundle | None = None
|
|
182
|
+
|
|
183
|
+
freeze: bool | None = None
|
|
184
|
+
secrets: list[SecretRef] = Field(default_factory=list)
|
|
185
|
+
gpu: str | None = Field(default=None, max_length=32)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
driver: str | None = Field(default=None, pattern=DRIVER,
|
|
189
|
+
description='The NVIDIA driver its card must have, by major version: "580"')
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
params: dict[str, InferParam] | None = Field(default=None, max_length=32)
|
|
193
|
+
|
|
194
|
+
image_context: CodeBundle | None = None
|
|
195
|
+
engine: str | None = Field(default=None, pattern=KIND_NAME)
|
|
196
|
+
|
|
197
|
+
@model_validator(mode="after")
|
|
198
|
+
def _tasks(self) -> "BenchmarkRegisterRequest":
|
|
199
|
+
_check_tasks(self.tasks, self.seeds, self.max_seconds)
|
|
200
|
+
return self
|
|
201
|
+
|
|
202
|
+
@model_validator(mode="after")
|
|
203
|
+
def _param_names(self) -> "BenchmarkRegisterRequest":
|
|
204
|
+
for name in self.params or {}:
|
|
205
|
+
if not name.isidentifier() or name.startswith("_"):
|
|
206
|
+
raise ValueError(f"parameter {name!r} is not a public Python identifier")
|
|
207
|
+
return self
|
|
208
|
+
|
|
209
|
+
@model_validator(mode="after")
|
|
210
|
+
def _driver_of_a_card(self) -> "BenchmarkRegisterRequest":
|
|
211
|
+
check_driver(self.driver, self.gpu)
|
|
212
|
+
return self
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class BenchmarkStatusResponse(BaseModel):
|
|
216
|
+
"""GET /v1/benchmarks/{name} — the build state of a registered benchmark (same lifecycle as a deployment:
|
|
217
|
+
building → ready | failed), with the failure reason when failed."""
|
|
218
|
+
|
|
219
|
+
model_config = ConfigDict(extra="forbid")
|
|
220
|
+
|
|
221
|
+
name: str
|
|
222
|
+
status: DeploymentState
|
|
223
|
+
error: str | None = None
|