sequence-base 0.3.13__tar.gz → 0.3.14__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. {sequence_base-0.3.13 → sequence_base-0.3.14}/PKG-INFO +1 -1
  2. {sequence_base-0.3.13 → sequence_base-0.3.14}/pyproject.toml +1 -1
  3. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/__init__.py +4 -1
  4. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/constants/version.py +2 -1
  5. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/__init__.py +2 -2
  6. sequence_base-0.3.14/sequence_base/contract/errors.py +151 -0
  7. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schema_export.py +3 -2
  8. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ActionChunk.json +1 -1
  9. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ActionContract.json +1 -1
  10. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ActionStep.json +1 -1
  11. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/AdapterTransform.json +1 -1
  12. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Autoscaler.json +1 -1
  13. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/BenchmarkConditionSpec.json +1 -1
  14. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/BenchmarkRegisterRequest.json +1 -1
  15. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/BenchmarkSpec.json +2 -2
  16. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/BenchmarkStatusResponse.json +1 -1
  17. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/BootstrapResponse.json +1 -1
  18. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Camera.json +1 -1
  19. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ClampInfo.json +1 -1
  20. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/CodeBundle.json +1 -1
  21. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Concurrency.json +1 -1
  22. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Conditions.json +1 -1
  23. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ConnectRequest.json +1 -1
  24. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ConnectResponse.json +1 -1
  25. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/DeploymentAccepted.json +1 -1
  26. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/DeploymentPolicy.json +1 -1
  27. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/DeploymentRequest.json +1 -1
  28. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/DeploymentStatusResponse.json +1 -1
  29. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Embodiment.json +1 -1
  30. sequence_base-0.3.14/sequence_base/schemas/ErrorBody.json +98 -0
  31. sequence_base-0.3.14/sequence_base/schemas/ErrorCodes.json +130 -0
  32. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Estimate.json +1 -1
  33. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/EvalDryRunResponse.json +1 -1
  34. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/EvalReport.json +1 -1
  35. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/EvalRequest.json +1 -1
  36. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/EvalResponse.json +1 -1
  37. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ImageFrame.json +1 -1
  38. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ImageSpec.json +1 -1
  39. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ImageStepApt.json +1 -1
  40. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ImageStepEnv.json +1 -1
  41. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ImageStepRun.json +1 -1
  42. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ImageStepUvPip.json +1 -1
  43. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/InferParam.json +1 -1
  44. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Lease.json +1 -1
  45. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/LossyTransform.json +1 -1
  46. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/MetricAgg.json +1 -1
  47. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Observation.json +1 -1
  48. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/ObservationContract.json +1 -1
  49. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/PolicyRegistration.json +2 -2
  50. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/PolicySpec.json +1 -1
  51. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Proprioception.json +1 -1
  52. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/RenderSpec.json +1 -1
  53. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Resources.json +1 -1
  54. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/RolloutResult.json +1 -1
  55. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/SecretCreateRequest.json +1 -1
  56. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/SecretInfo.json +1 -1
  57. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/SecretRef.json +1 -1
  58. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/SeqFlags.json +1 -1
  59. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Step.json +1 -1
  60. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/SuiteAgg.json +1 -1
  61. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/TaskAgg.json +1 -1
  62. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Timeouts.json +1 -1
  63. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/UploadTarget.json +1 -1
  64. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/UsageReport.json +1 -1
  65. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/Variant.json +1 -1
  66. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/VolumeAttach.json +1 -1
  67. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/VolumeFile.json +1 -1
  68. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/VolumeInfo.json +1 -1
  69. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/VolumeMount.json +1 -1
  70. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/VolumeRef.json +1 -1
  71. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/VolumeUploadRequest.json +1 -1
  72. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/schemas/VolumeUploadResponse.json +1 -1
  73. sequence_base-0.3.14/sequence_base/schemas/WarmingError.json +90 -0
  74. sequence_base-0.3.13/sequence_base/contract/errors.py +0 -45
  75. sequence_base-0.3.13/sequence_base/schemas/ErrorBody.json +0 -32
  76. sequence_base-0.3.13/sequence_base/schemas/WarmingError.json +0 -50
  77. {sequence_base-0.3.13 → sequence_base-0.3.14}/.gitignore +0 -0
  78. {sequence_base-0.3.13 → sequence_base-0.3.14}/README.md +0 -0
  79. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/auth.py +0 -0
  80. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/codec/__init__.py +0 -0
  81. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/codec/image.py +0 -0
  82. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/codec/resize.py +0 -0
  83. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/constants/__init__.py +0 -0
  84. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/constants/env.py +0 -0
  85. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/constants/headers.py +0 -0
  86. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/accounts.py +0 -0
  87. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/act.py +0 -0
  88. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/benchmarks.py +0 -0
  89. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/connect.py +0 -0
  90. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/deployments.py +0 -0
  91. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/eval.py +0 -0
  92. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/flags.py +0 -0
  93. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/io.py +0 -0
  94. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/policy.py +0 -0
  95. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/contract/volumes.py +0 -0
  96. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/errors/__init__.py +0 -0
  97. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/errors/transport.py +0 -0
  98. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/obs/__init__.py +0 -0
  99. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/obs/log.py +0 -0
  100. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/obs/rid.py +0 -0
  101. {sequence_base-0.3.13 → sequence_base-0.3.14}/sequence_base/telemetry/__init__.py +0 -0
  102. {sequence_base-0.3.13 → sequence_base-0.3.14}/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.13
3
+ Version: 0.3.14
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.13"
3
+ version = "0.3.14"
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"]
@@ -48,8 +48,10 @@ from sequence_base.contract import ( # noqa: F401 (re-export surface)
48
48
  DeploymentRequest,
49
49
  DeploymentState,
50
50
  DeploymentStatusResponse,
51
+ ERRORS,
51
52
  ErrorBody,
52
53
  ErrorCode,
54
+ ErrorSpec,
53
55
  Estimate,
54
56
  EvalDryRunResponse,
55
57
  EvalReport,
@@ -87,6 +89,7 @@ from sequence_base.contract import ( # noqa: F401 (re-export surface)
87
89
  VolumeMount,
88
90
  VolumeRef,
89
91
  WarmingError,
92
+ code_for_status,
90
93
  )
91
94
 
92
95
  # the single schema-level version stamp (embedded in every exported JSON Schema)
@@ -137,7 +140,7 @@ __all__ = [
137
140
  # contract 7 — volumes (uploaded checkpoints and datasets)
138
141
  "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse", "VolumeInfo", "parse_vol",
139
142
  # error bodies + version
140
- "ErrorCode", "ErrorBody", "WarmingError", "CONTRACT_VERSION",
143
+ "ErrorCode", "ErrorBody", "WarmingError", "ERRORS", "ErrorSpec", "code_for_status", "CONTRACT_VERSION",
141
144
  # obs
142
145
  "HEADER", "new_rid", "clean", "log", "Timer", "enabled",
143
146
  # errors (exception taxonomy)
@@ -4,7 +4,8 @@
4
4
  # across deployments / repos can tell which version of a shape it is validating against. MUST change
5
5
  # whenever any shared wire shape changes (a field added / removed / retyped) — that is what makes
6
6
  # contract evolution mechanically detectable instead of eyeballed. Bump on any shape change.
7
- CONTRACT_VERSION = "0.4.11" # 0.4.10 -> 0.4.11: weights in named parts (weights_name, extra_weights)
7
+ CONTRACT_VERSION = "0.4.12" # 0.4.11 -> 0.4.12: one error definition — ErrorBody is every non-2xx body (detail, code, retryable, retry_after_s, request_id, hint, details), ErrorCode covers every case, ERRORS / ErrorCodes.json give each code its status, retryability, SDK class and CLI exit code
8
+ # previous: 0.4.11 # 0.4.10 -> 0.4.11: weights in named parts (weights_name, extra_weights)
8
9
  # previous: 0.4.8 # 0.4.7 -> 0.4.8: ImageSpec.python, ImageStepRun.secrets, Resources.region, DeploymentRequest/BenchmarkRegisterRequest.image_context
9
10
  # previous: 0.4.7 # 0.4.6 -> 0.4.7: ActionContract.bounds / max_delta / on_violation (action limits)
10
11
  # previous: 0.4.6 # 0.4.5 -> 0.4.6: InferParam + DeploymentPolicy.params; EvalRequest.params / variants / record; BenchmarkRegisterRequest.gpu
@@ -102,7 +102,7 @@ from sequence_base.contract.volumes import (
102
102
  )
103
103
 
104
104
  # shared error bodies
105
- from sequence_base.contract.errors import ErrorBody, ErrorCode, WarmingError
105
+ from sequence_base.contract.errors import ERRORS, ErrorBody, ErrorCode, ErrorSpec, WarmingError, code_for_status
106
106
 
107
107
  __all__ = [
108
108
  # contract 4
@@ -129,5 +129,5 @@ __all__ = [
129
129
  "MAX_FILES", "MAX_UPLOAD_BYTES", "VolumeFile", "VolumeUploadRequest", "UploadTarget", "VolumeUploadResponse",
130
130
  "VolumeInfo", "check_volume_name", "parse_vol",
131
131
  # errors
132
- "ErrorCode", "ErrorBody", "WarmingError",
132
+ "ErrorCode", "ErrorBody", "WarmingError", "ERRORS", "ErrorSpec", "code_for_status",
133
133
  ]
@@ -0,0 +1,151 @@
1
+ """
2
+ Shared error wire types: one definition for every non-2xx response the platform sends.
3
+
4
+ Every error the gateway or a worker returns is an `ErrorBody`. `ERRORS` is the single table that
5
+ gives each `ErrorCode` its HTTP status, whether a retry can help, the SDK exception it becomes and
6
+ the `seq` CLI exit code. The gateway and the worker harness import it; the SDK, which cannot import
7
+ this package on a robot, carries a generated copy that a test compares against `ErrorCodes.json`.
8
+ That is what makes the same situation surface the same way at every entry point.
9
+ {gs-plans/deploy-only/errors.md "同一种情况,不管从哪个入口碰到,结果都一样"}
10
+
11
+ Distinct from `sequence_base.errors.transport`, which is the internal exception taxonomy the gateway
12
+ and harness raise before a response is built.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from enum import Enum
17
+ from typing import Any, NamedTuple
18
+
19
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
20
+ from pydantic_core import PydanticUndefined
21
+
22
+
23
+ class ErrorCode(str, Enum):
24
+ """What a caller switches on. The wording of `detail` may change; a code does not."""
25
+
26
+ INVALID_REQUEST = "invalid_request"
27
+ PAYLOAD_TOO_LARGE = "payload_too_large"
28
+ UNAUTHENTICATED = "unauthenticated"
29
+ FORBIDDEN = "forbidden"
30
+ INSUFFICIENT_CREDIT = "insufficient_credit"
31
+ BUDGET_EXHAUSTED = "budget_exhausted"
32
+ NOT_FOUND = "not_found"
33
+ CONFLICT = "conflict"
34
+ GONE = "gone"
35
+ BUSY = "busy"
36
+ WARMING = "warming"
37
+ UNAVAILABLE = "unavailable"
38
+ UPSTREAM_ERROR = "upstream_error"
39
+ TIMEOUT = "timeout"
40
+ POLICY_ERROR = "policy_error"
41
+ INTERNAL = "internal"
42
+ DEPENDENCY_MISSING = "dependency_missing"
43
+
44
+
45
+ class ErrorSpec(NamedTuple):
46
+ status: int # the HTTP status the code is sent with
47
+ retryable: bool # whether the identical request can succeed later without the caller changing anything
48
+ sdk_class: str # the sequence_ai exception it becomes
49
+ exit_code: int # the `seq` CLI exit code
50
+
51
+
52
+ # Grouped by what the caller does next, not by which module failed. Exit codes: 1 internal, 2 fix the
53
+ # request, 3 credentials or permission, 4 missing or retired, 5 state conflict, 6 money, 7 retry later.
54
+ ERRORS: dict[ErrorCode, ErrorSpec] = {
55
+ ErrorCode.INVALID_REQUEST: ErrorSpec(400, False, "InvalidRequest", 2),
56
+ ErrorCode.PAYLOAD_TOO_LARGE: ErrorSpec(413, False, "InvalidRequest", 2),
57
+ ErrorCode.UNAUTHENTICATED: ErrorSpec(401, False, "AuthError", 3),
58
+ ErrorCode.FORBIDDEN: ErrorSpec(403, False, "PermissionDenied", 3),
59
+ ErrorCode.INSUFFICIENT_CREDIT: ErrorSpec(402, False, "OutOfCredit", 6),
60
+ ErrorCode.BUDGET_EXHAUSTED: ErrorSpec(402, False, "OutOfCredit", 6),
61
+ ErrorCode.NOT_FOUND: ErrorSpec(404, False, "NotFound", 4),
62
+ ErrorCode.CONFLICT: ErrorSpec(409, False, "Conflict", 5),
63
+ ErrorCode.GONE: ErrorSpec(410, False, "Gone", 4),
64
+ ErrorCode.BUSY: ErrorSpec(429, True, "Unavailable", 7),
65
+ ErrorCode.WARMING: ErrorSpec(503, True, "Unavailable", 7),
66
+ ErrorCode.UNAVAILABLE: ErrorSpec(503, True, "Unavailable", 7),
67
+ ErrorCode.UPSTREAM_ERROR: ErrorSpec(502, True, "Unavailable", 7),
68
+ ErrorCode.TIMEOUT: ErrorSpec(504, True, "Unavailable", 7),
69
+ ErrorCode.POLICY_ERROR: ErrorSpec(500, False, "PolicyError", 1),
70
+ ErrorCode.INTERNAL: ErrorSpec(500, False, "InternalError", 1),
71
+ ErrorCode.DEPENDENCY_MISSING: ErrorSpec(424, False, "InternalError", 1),
72
+ }
73
+
74
+ # A response that carries no code (an older server, a proxy in between) is read by its status alone.
75
+ _CODE_FOR_STATUS: dict[int, ErrorCode] = {
76
+ 400: ErrorCode.INVALID_REQUEST, 401: ErrorCode.UNAUTHENTICATED, 402: ErrorCode.INSUFFICIENT_CREDIT,
77
+ 403: ErrorCode.FORBIDDEN, 404: ErrorCode.NOT_FOUND, 405: ErrorCode.INVALID_REQUEST,
78
+ 409: ErrorCode.CONFLICT, 410: ErrorCode.GONE, 413: ErrorCode.PAYLOAD_TOO_LARGE,
79
+ 422: ErrorCode.INVALID_REQUEST, 424: ErrorCode.DEPENDENCY_MISSING, 429: ErrorCode.BUSY,
80
+ 500: ErrorCode.INTERNAL, 502: ErrorCode.UPSTREAM_ERROR, 503: ErrorCode.UNAVAILABLE,
81
+ 504: ErrorCode.TIMEOUT,
82
+ }
83
+
84
+
85
+ def code_for_status(status: int) -> ErrorCode:
86
+ """The code a bare status stands for: the table's own entry, else invalid_request for any other 4xx
87
+ and internal for any other 5xx."""
88
+ if status in _CODE_FOR_STATUS:
89
+ return _CODE_FOR_STATUS[status]
90
+ return ErrorCode.INVALID_REQUEST if 400 <= status < 500 else ErrorCode.INTERNAL
91
+
92
+
93
+ class ErrorBody(BaseModel):
94
+ """The body of every non-2xx response. `detail` stays a plain sentence (the console already reads
95
+ that field); everything a program needs is in `code` and `details`."""
96
+
97
+ model_config = ConfigDict(extra="forbid")
98
+
99
+ detail: str = Field(..., description="One sentence for a person. Its wording may change; switch on `code`.")
100
+ code: ErrorCode
101
+ retryable: bool = Field(..., description="Whether the identical request can succeed later. Always ERRORS[code].retryable")
102
+ retry_after_s: float | None = Field(default=None, description="Seconds to wait before retrying; sent with 429 and 503, mirrored in Retry-After")
103
+ request_id: str | None = Field(default=None, description="Quote it when reporting a problem")
104
+ hint: str | None = Field(default=None, description="The next step, e.g. a command to run")
105
+ details: dict[str, Any] = Field(default_factory=dict, description="Fields specific to the code: resource, name, reason, since, replacement, state, exception, ...")
106
+
107
+ @model_validator(mode="before")
108
+ @classmethod
109
+ def _retryable_from_table(cls, data: Any) -> Any:
110
+ # Filled from the table so no producer can contradict it; a body that states the opposite is refused.
111
+ # A subclass that fixes the code (WarmingError) is read through its default.
112
+ if not isinstance(data, dict):
113
+ return data
114
+ code = data.get("code")
115
+ if code is None:
116
+ default = cls.model_fields["code"].default
117
+ code = None if default is PydanticUndefined else default
118
+ if code is not None:
119
+ want = ERRORS[ErrorCode(code)].retryable
120
+ if data.get("retryable") is None:
121
+ data = {**data, "retryable": want}
122
+ elif data["retryable"] != want:
123
+ raise ValueError(f"retryable for {ErrorCode(code).value} is {want}")
124
+ return data
125
+
126
+ @property
127
+ def status(self) -> int:
128
+ return ERRORS[self.code].status
129
+
130
+
131
+ class WarmingError(ErrorBody):
132
+ """503 while a worker loads: the one error whose remedy is purely to wait `retry_after_s`. A known
133
+ time-to-ready goes in `details["eta_s"]`."""
134
+
135
+ code: ErrorCode = ErrorCode.WARMING
136
+ retry_after_s: float
137
+
138
+
139
+ def error_codes_json_schema(contract_version: str) -> dict:
140
+ """`ERRORS` as data, so a consumer that cannot import this package (the SDK on a robot) can check its
141
+ own copy against it: the code is a string enum, `x-errors` gives each code its status, retryability,
142
+ SDK class and CLI exit code."""
143
+ return {
144
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
145
+ "title": "ErrorCode",
146
+ "description": "Every error code the platform sends, with what each one means for the caller.",
147
+ "type": "string",
148
+ "enum": [c.value for c in ErrorCode],
149
+ "x-errors": {c.value: spec._asdict() for c, spec in ERRORS.items()},
150
+ "x-contract-version": contract_version,
151
+ }
@@ -57,7 +57,7 @@ from sequence_base.contract.deployments import (
57
57
  Timeouts,
58
58
  VolumeAttach,
59
59
  )
60
- from sequence_base.contract.errors import ErrorBody, WarmingError
60
+ from sequence_base.contract.errors import ErrorBody, WarmingError, error_codes_json_schema
61
61
  from sequence_base.contract.volumes import (
62
62
  UploadTarget,
63
63
  VolumeFile,
@@ -237,9 +237,10 @@ def build_schema(model: type[BaseModel]) -> dict:
237
237
 
238
238
 
239
239
  def all_schemas() -> dict[str, dict]:
240
- """Every exported schema, by filename stem — pydantic models plus the SeqFlags IntFlag."""
240
+ """Every exported schema, by filename stem — pydantic models, the SeqFlags IntFlag and the error table."""
241
241
  out: dict[str, dict] = {name: build_schema(m) for name, m in WIRE_MODELS.items()}
242
242
  out["SeqFlags"] = seqflags_json_schema(CONTRACT_VERSION)
243
+ out["ErrorCodes"] = error_codes_json_schema(CONTRACT_VERSION)
243
244
  return out
244
245
 
245
246
 
@@ -98,5 +98,5 @@
98
98
  ],
99
99
  "title": "ActionChunk",
100
100
  "type": "object",
101
- "x-contract-version": "0.4.11"
101
+ "x-contract-version": "0.4.12"
102
102
  }
@@ -99,5 +99,5 @@
99
99
  ],
100
100
  "title": "ActionContract",
101
101
  "type": "object",
102
- "x-contract-version": "0.4.11"
102
+ "x-contract-version": "0.4.12"
103
103
  }
@@ -19,5 +19,5 @@
19
19
  ],
20
20
  "title": "ActionStep",
21
21
  "type": "object",
22
- "x-contract-version": "0.4.11"
22
+ "x-contract-version": "0.4.12"
23
23
  }
@@ -38,5 +38,5 @@
38
38
  ],
39
39
  "title": "AdapterTransform",
40
40
  "type": "object",
41
- "x-contract-version": "0.4.11"
41
+ "x-contract-version": "0.4.12"
42
42
  }
@@ -39,5 +39,5 @@
39
39
  },
40
40
  "title": "Autoscaler",
41
41
  "type": "object",
42
- "x-contract-version": "0.4.11"
42
+ "x-contract-version": "0.4.12"
43
43
  }
@@ -62,5 +62,5 @@
62
62
  ],
63
63
  "title": "BenchmarkConditionSpec",
64
64
  "type": "object",
65
- "x-contract-version": "0.4.11"
65
+ "x-contract-version": "0.4.12"
66
66
  }
@@ -638,5 +638,5 @@
638
638
  ],
639
639
  "title": "BenchmarkRegisterRequest",
640
640
  "type": "object",
641
- "x-contract-version": "0.4.11"
641
+ "x-contract-version": "0.4.12"
642
642
  }
@@ -235,7 +235,7 @@
235
235
  ],
236
236
  "title": "SeqFlags",
237
237
  "type": "integer",
238
- "x-contract-version": "0.4.11",
238
+ "x-contract-version": "0.4.12",
239
239
  "x-flag-names": {
240
240
  "CHECK": 65536,
241
241
  "INFER": 16,
@@ -425,5 +425,5 @@
425
425
  ],
426
426
  "title": "BenchmarkSpec",
427
427
  "type": "object",
428
- "x-contract-version": "0.4.11"
428
+ "x-contract-version": "0.4.12"
429
429
  }
@@ -40,5 +40,5 @@
40
40
  ],
41
41
  "title": "BenchmarkStatusResponse",
42
42
  "type": "object",
43
- "x-contract-version": "0.4.11"
43
+ "x-contract-version": "0.4.12"
44
44
  }
@@ -72,5 +72,5 @@
72
72
  ],
73
73
  "title": "BootstrapResponse",
74
74
  "type": "object",
75
- "x-contract-version": "0.4.11"
75
+ "x-contract-version": "0.4.12"
76
76
  }
@@ -34,5 +34,5 @@
34
34
  ],
35
35
  "title": "Camera",
36
36
  "type": "object",
37
- "x-contract-version": "0.4.11"
37
+ "x-contract-version": "0.4.12"
38
38
  }
@@ -23,5 +23,5 @@
23
23
  ],
24
24
  "title": "ClampInfo",
25
25
  "type": "object",
26
- "x-contract-version": "0.4.11"
26
+ "x-contract-version": "0.4.12"
27
27
  }
@@ -23,5 +23,5 @@
23
23
  ],
24
24
  "title": "CodeBundle",
25
25
  "type": "object",
26
- "x-contract-version": "0.4.11"
26
+ "x-contract-version": "0.4.12"
27
27
  }
@@ -17,5 +17,5 @@
17
17
  ],
18
18
  "title": "Concurrency",
19
19
  "type": "object",
20
- "x-contract-version": "0.4.11"
20
+ "x-contract-version": "0.4.12"
21
21
  }
@@ -55,5 +55,5 @@
55
55
  ],
56
56
  "title": "Conditions",
57
57
  "type": "object",
58
- "x-contract-version": "0.4.11"
58
+ "x-contract-version": "0.4.12"
59
59
  }
@@ -25,5 +25,5 @@
25
25
  ],
26
26
  "title": "ConnectRequest",
27
27
  "type": "object",
28
- "x-contract-version": "0.4.11"
28
+ "x-contract-version": "0.4.12"
29
29
  }
@@ -294,5 +294,5 @@
294
294
  ],
295
295
  "title": "ConnectResponse",
296
296
  "type": "object",
297
- "x-contract-version": "0.4.11"
297
+ "x-contract-version": "0.4.12"
298
298
  }
@@ -18,5 +18,5 @@
18
18
  ],
19
19
  "title": "DeploymentAccepted",
20
20
  "type": "object",
21
- "x-contract-version": "0.4.11"
21
+ "x-contract-version": "0.4.12"
22
22
  }
@@ -366,5 +366,5 @@
366
366
  ],
367
367
  "title": "DeploymentPolicy",
368
368
  "type": "object",
369
- "x-contract-version": "0.4.11"
369
+ "x-contract-version": "0.4.12"
370
370
  }
@@ -811,5 +811,5 @@
811
811
  ],
812
812
  "title": "DeploymentRequest",
813
813
  "type": "object",
814
- "x-contract-version": "0.4.11"
814
+ "x-contract-version": "0.4.12"
815
815
  }
@@ -41,5 +41,5 @@
41
41
  ],
42
42
  "title": "DeploymentStatusResponse",
43
43
  "type": "object",
44
- "x-contract-version": "0.4.11"
44
+ "x-contract-version": "0.4.12"
45
45
  }
@@ -212,5 +212,5 @@
212
212
  ],
213
213
  "title": "Embodiment",
214
214
  "type": "object",
215
- "x-contract-version": "0.4.11"
215
+ "x-contract-version": "0.4.12"
216
216
  }
@@ -0,0 +1,98 @@
1
+ {
2
+ "$defs": {
3
+ "ErrorCode": {
4
+ "description": "What a caller switches on. The wording of `detail` may change; a code does not.",
5
+ "enum": [
6
+ "invalid_request",
7
+ "payload_too_large",
8
+ "unauthenticated",
9
+ "forbidden",
10
+ "insufficient_credit",
11
+ "budget_exhausted",
12
+ "not_found",
13
+ "conflict",
14
+ "gone",
15
+ "busy",
16
+ "warming",
17
+ "unavailable",
18
+ "upstream_error",
19
+ "timeout",
20
+ "policy_error",
21
+ "internal",
22
+ "dependency_missing"
23
+ ],
24
+ "title": "ErrorCode",
25
+ "type": "string"
26
+ }
27
+ },
28
+ "additionalProperties": false,
29
+ "description": "The body of every non-2xx response. `detail` stays a plain sentence (the console already reads\nthat field); everything a program needs is in `code` and `details`.",
30
+ "properties": {
31
+ "code": {
32
+ "$ref": "#/$defs/ErrorCode"
33
+ },
34
+ "detail": {
35
+ "description": "One sentence for a person. Its wording may change; switch on `code`.",
36
+ "title": "Detail",
37
+ "type": "string"
38
+ },
39
+ "details": {
40
+ "additionalProperties": true,
41
+ "description": "Fields specific to the code: resource, name, reason, since, replacement, state, exception, ...",
42
+ "title": "Details",
43
+ "type": "object"
44
+ },
45
+ "hint": {
46
+ "anyOf": [
47
+ {
48
+ "type": "string"
49
+ },
50
+ {
51
+ "type": "null"
52
+ }
53
+ ],
54
+ "default": null,
55
+ "description": "The next step, e.g. a command to run",
56
+ "title": "Hint"
57
+ },
58
+ "request_id": {
59
+ "anyOf": [
60
+ {
61
+ "type": "string"
62
+ },
63
+ {
64
+ "type": "null"
65
+ }
66
+ ],
67
+ "default": null,
68
+ "description": "Quote it when reporting a problem",
69
+ "title": "Request Id"
70
+ },
71
+ "retry_after_s": {
72
+ "anyOf": [
73
+ {
74
+ "type": "number"
75
+ },
76
+ {
77
+ "type": "null"
78
+ }
79
+ ],
80
+ "default": null,
81
+ "description": "Seconds to wait before retrying; sent with 429 and 503, mirrored in Retry-After",
82
+ "title": "Retry After S"
83
+ },
84
+ "retryable": {
85
+ "description": "Whether the identical request can succeed later. Always ERRORS[code].retryable",
86
+ "title": "Retryable",
87
+ "type": "boolean"
88
+ }
89
+ },
90
+ "required": [
91
+ "detail",
92
+ "code",
93
+ "retryable"
94
+ ],
95
+ "title": "ErrorBody",
96
+ "type": "object",
97
+ "x-contract-version": "0.4.12"
98
+ }