simulo-interfaces 0.14.0__tar.gz → 0.15.2__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.
- {simulo_interfaces-0.14.0/src/simulo_interfaces.egg-info → simulo_interfaces-0.15.2}/PKG-INFO +1 -1
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/pyproject.toml +1 -1
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/__init__.py +16 -0
- simulo_interfaces-0.15.2/src/simulo/interfaces/ids.py +134 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/__init__.py +55 -0
- simulo_interfaces-0.15.2/src/simulo/interfaces/platform/_payload.py +15 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/artifacts.py +40 -4
- simulo_interfaces-0.15.2/src/simulo/interfaces/platform/job_events.py +244 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/runs.py +61 -3
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/submit.py +87 -8
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2/src/simulo_interfaces.egg-info}/PKG-INFO +1 -1
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo_interfaces.egg-info/SOURCES.txt +2 -0
- simulo_interfaces-0.14.0/src/simulo/interfaces/ids.py +0 -24
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/MANIFEST.in +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/PYPI.md +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/setup.cfg +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/__init__.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/actuator.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/asset.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/entity.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/light.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/materials.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/physics.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/pose.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/primitives.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/prop.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/record.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/robot.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/scene.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/sensor.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/terrain.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/visual.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/world.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/exceptions.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/app.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/asset.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/asset_catalog.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/callbacks.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/debug.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/domain.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/enums.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/manifest.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/policy_bundle.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/runtime.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/volume.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/py.typed +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/__init__.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/anomaly.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/components.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/env.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/player.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/policy.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/scenario.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/task.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/tensors.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/trainer.py +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo_interfaces.egg-info/dependency_links.txt +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo_interfaces.egg-info/requires.txt +0 -0
- {simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo_interfaces.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simulo-interfaces"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.15.2"
|
|
8
8
|
description = "Type contracts for the Simulo SDK. Installed automatically as a dependency of the simulo package."
|
|
9
9
|
readme = "PYPI.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -31,13 +31,21 @@ from simulo.interfaces.ids import (
|
|
|
31
31
|
CheckpointId,
|
|
32
32
|
Digest,
|
|
33
33
|
JobId,
|
|
34
|
+
JobPublicId,
|
|
35
|
+
ModelPublicId,
|
|
34
36
|
OrganizationId,
|
|
37
|
+
OutputPublicId,
|
|
35
38
|
PackageId,
|
|
36
39
|
ProjectId,
|
|
40
|
+
RecordingPublicId,
|
|
37
41
|
ResourceId,
|
|
38
42
|
ResourceUri,
|
|
39
43
|
TagId,
|
|
40
44
|
TrainedModelId,
|
|
45
|
+
parse_job_public_id,
|
|
46
|
+
parse_model_public_id,
|
|
47
|
+
parse_output_public_id,
|
|
48
|
+
parse_recording_public_id,
|
|
41
49
|
)
|
|
42
50
|
|
|
43
51
|
__all__ = [
|
|
@@ -56,6 +64,14 @@ __all__ = [
|
|
|
56
64
|
"ProjectId",
|
|
57
65
|
"PackageId",
|
|
58
66
|
"JobId",
|
|
67
|
+
"JobPublicId",
|
|
68
|
+
"ModelPublicId",
|
|
69
|
+
"RecordingPublicId",
|
|
70
|
+
"OutputPublicId",
|
|
71
|
+
"parse_job_public_id",
|
|
72
|
+
"parse_model_public_id",
|
|
73
|
+
"parse_recording_public_id",
|
|
74
|
+
"parse_output_public_id",
|
|
59
75
|
"ResourceId",
|
|
60
76
|
"CheckpointId",
|
|
61
77
|
"TrainedModelId",
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Strongly-typed string identifiers for the platform domain model (Part B).
|
|
2
|
+
|
|
3
|
+
``NewType`` aliases — zero runtime cost (each is the underlying ``str`` at
|
|
4
|
+
runtime) but distinct to the type checker, so a ``ProjectId`` can never be
|
|
5
|
+
passed where a ``JobId`` is expected.
|
|
6
|
+
|
|
7
|
+
The ``*PublicId`` aliases identify grammar-validated human-facing references.
|
|
8
|
+
Their parsers normalize only outer ASCII whitespace and ASCII letter case;
|
|
9
|
+
Unicode whitespace, Unicode case folding, wildcards, and partial IDs are
|
|
10
|
+
deliberately rejected.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from typing import NewType
|
|
15
|
+
|
|
16
|
+
OrganizationId = NewType("OrganizationId", str)
|
|
17
|
+
ProjectId = NewType("ProjectId", str)
|
|
18
|
+
PackageId = NewType("PackageId", str)
|
|
19
|
+
JobId = NewType("JobId", str)
|
|
20
|
+
ResourceId = NewType("ResourceId", str)
|
|
21
|
+
CheckpointId = NewType("CheckpointId", str)
|
|
22
|
+
TrainedModelId = NewType("TrainedModelId", str)
|
|
23
|
+
TagId = NewType("TagId", str)
|
|
24
|
+
ResourceUri = NewType("ResourceUri", str) # e.g. "robot/so-arm-100:v3"
|
|
25
|
+
Digest = NewType("Digest", str) # content-addressed package source digest
|
|
26
|
+
RecordingId = NewType("RecordingId", str) # a job-produced MCAP recording (PR-0 wire contract)
|
|
27
|
+
WorkerId = NewType("WorkerId", str) # a claiming worker process (PR-0 wire contract)
|
|
28
|
+
AssetId = NewType("AssetId", str) # a catalog asset (USD Asset Catalogs wire contract, PR-1)
|
|
29
|
+
AssetVersionId = NewType("AssetVersionId", str) # one immutable asset version (PR-1)
|
|
30
|
+
ArtifactId = NewType("ArtifactId", str) # a job-produced output in the /artifacts projection (Plan B)
|
|
31
|
+
|
|
32
|
+
JobPublicId = NewType("JobPublicId", str)
|
|
33
|
+
ModelPublicId = NewType("ModelPublicId", str)
|
|
34
|
+
RecordingPublicId = NewType("RecordingPublicId", str)
|
|
35
|
+
OutputPublicId = NewType("OutputPublicId", str)
|
|
36
|
+
|
|
37
|
+
_ASCII_WHITESPACE = " \t\n\r\v\f"
|
|
38
|
+
_ASCII_LOWERCASE_TRANSLATION = str.maketrans(
|
|
39
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
40
|
+
"abcdefghijklmnopqrstuvwxyz",
|
|
41
|
+
)
|
|
42
|
+
_JOB_STEM_PATTERN = r"[a-z]{3,10}-[a-z]{3,10}-[0-9a-hjkmnp-tv-z]{6}"
|
|
43
|
+
_CANONICAL_CHILD_COUNTER_PATTERN = r"(?:(?!0000)[0-9]{4}|[1-9][0-9]{4,})"
|
|
44
|
+
_JOB_PUBLIC_ID_RE = re.compile(rf"job_{_JOB_STEM_PATTERN}\Z")
|
|
45
|
+
_MODEL_PUBLIC_ID_RE = re.compile(rf"mdl_{_JOB_STEM_PATTERN}-{_CANONICAL_CHILD_COUNTER_PATTERN}\Z")
|
|
46
|
+
_RECORDING_PUBLIC_ID_RE = re.compile(rf"rec_{_JOB_STEM_PATTERN}-{_CANONICAL_CHILD_COUNTER_PATTERN}\Z")
|
|
47
|
+
_OUTPUT_PUBLIC_ID_RE = re.compile(rf"out_{_JOB_STEM_PATTERN}-{_CANONICAL_CHILD_COUNTER_PATTERN}\Z")
|
|
48
|
+
_MAX_JOB_PUBLIC_ID_LENGTH = 32
|
|
49
|
+
_MAX_CHILD_PUBLIC_ID_LENGTH = 64
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _normalize_public_id(value: str, *, parser: str) -> str:
|
|
53
|
+
if not isinstance(value, str):
|
|
54
|
+
raise TypeError(f"{parser}() requires a str, got {type(value).__name__}.")
|
|
55
|
+
return value.strip(_ASCII_WHITESPACE).translate(_ASCII_LOWERCASE_TRANSLATION)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _parse_public_id(value: str, *, parser: str, pattern: re.Pattern[str], max_length: int) -> str:
|
|
59
|
+
normalized = _normalize_public_id(value, parser=parser)
|
|
60
|
+
if len(normalized) > max_length or pattern.fullmatch(normalized) is None:
|
|
61
|
+
raise ValueError(f"{parser}() received a non-canonical public ID.")
|
|
62
|
+
return normalized
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def parse_job_public_id(value: str) -> JobPublicId:
|
|
66
|
+
"""Return a canonical Job ID such as ``job_quiet-cedar-7k3m4q``.
|
|
67
|
+
|
|
68
|
+
Only outer ASCII whitespace is trimmed and only ASCII ``A-Z`` is
|
|
69
|
+
lowercased. Matching is exact: prefixes, partial values, wildcards, and
|
|
70
|
+
non-ASCII input are rejected.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
return JobPublicId(
|
|
74
|
+
_parse_public_id(
|
|
75
|
+
value,
|
|
76
|
+
parser="parse_job_public_id",
|
|
77
|
+
pattern=_JOB_PUBLIC_ID_RE,
|
|
78
|
+
max_length=_MAX_JOB_PUBLIC_ID_LENGTH,
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def parse_model_public_id(value: str) -> ModelPublicId:
|
|
84
|
+
"""Return a canonical Model ID such as ``mdl_quiet-cedar-7k3m4q-0001``.
|
|
85
|
+
|
|
86
|
+
Only outer ASCII whitespace is trimmed and only ASCII ``A-Z`` is
|
|
87
|
+
lowercased. Matching is exact and non-ASCII input is rejected. Counters
|
|
88
|
+
use exactly four digits through ``9999`` and expand unpadded at ``10000``.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
return ModelPublicId(
|
|
92
|
+
_parse_public_id(
|
|
93
|
+
value,
|
|
94
|
+
parser="parse_model_public_id",
|
|
95
|
+
pattern=_MODEL_PUBLIC_ID_RE,
|
|
96
|
+
max_length=_MAX_CHILD_PUBLIC_ID_LENGTH,
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def parse_recording_public_id(value: str) -> RecordingPublicId:
|
|
102
|
+
"""Return a Recording ID such as ``rec_quiet-cedar-7k3m4q-0001``.
|
|
103
|
+
|
|
104
|
+
Only outer ASCII whitespace is trimmed and only ASCII ``A-Z`` is
|
|
105
|
+
lowercased. Matching is exact and non-ASCII input is rejected. Counters
|
|
106
|
+
use exactly four digits through ``9999`` and expand unpadded at ``10000``.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
return RecordingPublicId(
|
|
110
|
+
_parse_public_id(
|
|
111
|
+
value,
|
|
112
|
+
parser="parse_recording_public_id",
|
|
113
|
+
pattern=_RECORDING_PUBLIC_ID_RE,
|
|
114
|
+
max_length=_MAX_CHILD_PUBLIC_ID_LENGTH,
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def parse_output_public_id(value: str) -> OutputPublicId:
|
|
120
|
+
"""Return an Output ID such as ``out_quiet-cedar-7k3m4q-0001``.
|
|
121
|
+
|
|
122
|
+
Only outer ASCII whitespace is trimmed and only ASCII ``A-Z`` is
|
|
123
|
+
lowercased. Matching is exact and non-ASCII input is rejected. Counters
|
|
124
|
+
use exactly four digits through ``9999`` and expand unpadded at ``10000``.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
return OutputPublicId(
|
|
128
|
+
_parse_public_id(
|
|
129
|
+
value,
|
|
130
|
+
parser="parse_output_public_id",
|
|
131
|
+
pattern=_OUTPUT_PUBLIC_ID_RE,
|
|
132
|
+
max_length=_MAX_CHILD_PUBLIC_ID_LENGTH,
|
|
133
|
+
)
|
|
134
|
+
)
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/__init__.py
RENAMED
|
@@ -100,6 +100,34 @@ from simulo.interfaces.platform.callbacks import JobCallbackProtocol
|
|
|
100
100
|
from simulo.interfaces.platform.debug import DebugSessionProtocol, ReplaySessionProtocol, VisualizationSessionProtocol
|
|
101
101
|
from simulo.interfaces.platform.domain import Checkpoint, Job, Package, Project, Resource, Tag, TrainedModel
|
|
102
102
|
from simulo.interfaces.platform.enums import ArtifactKind
|
|
103
|
+
from simulo.interfaces.platform.job_events import (
|
|
104
|
+
MAX_PUBLIC_ASSET_FINDING_COUNT,
|
|
105
|
+
MAX_PUBLIC_JOB_EVENT_ATTEMPT,
|
|
106
|
+
MAX_PUBLIC_JOB_EVENT_BYTES,
|
|
107
|
+
MAX_PUBLIC_JOB_EVENT_ENTRIES,
|
|
108
|
+
MAX_PUBLIC_JOB_EVENT_QUIET_SECONDS,
|
|
109
|
+
MAX_PUBLIC_JOB_EVENT_WIRE_BYTES,
|
|
110
|
+
PUBLIC_JOB_EVENT_ATTEMPT_MISMATCH_CODE,
|
|
111
|
+
PUBLIC_JOB_EVENT_OFFSET_CONFLICT_CODE,
|
|
112
|
+
PUBLIC_JOB_EVENT_OFFSET_GAP_CODE,
|
|
113
|
+
PUBLIC_JOB_EVENT_SCHEMA_VERSION,
|
|
114
|
+
PUBLIC_JOB_SUPPORT_ID_HEX_CHARS,
|
|
115
|
+
PUBLIC_JOB_SUPPORT_ID_PREFIX,
|
|
116
|
+
WORKER_JOB_EVENTS_ROUTE_TEMPLATE,
|
|
117
|
+
JobAssetFindingData,
|
|
118
|
+
JobAssetFindingEvent,
|
|
119
|
+
JobPreparingData,
|
|
120
|
+
JobPreparingEvent,
|
|
121
|
+
JobRunningData,
|
|
122
|
+
JobRunningEvent,
|
|
123
|
+
JobStillWorkingData,
|
|
124
|
+
JobStillWorkingEvent,
|
|
125
|
+
PublicAssetFindingReason,
|
|
126
|
+
PublicJobEvent,
|
|
127
|
+
PublicJobEventCode,
|
|
128
|
+
PublicJobEventSeverity,
|
|
129
|
+
PublicJobPreparationPhase,
|
|
130
|
+
)
|
|
103
131
|
from simulo.interfaces.platform.manifest import (
|
|
104
132
|
FINDING_CLASSIFICATIONS,
|
|
105
133
|
MAX_PREFLIGHT_BODY_BYTES,
|
|
@@ -221,6 +249,33 @@ from simulo.interfaces.platform.volume import VolumeProtocol
|
|
|
221
249
|
__all__ = [
|
|
222
250
|
# enums
|
|
223
251
|
"ArtifactKind",
|
|
252
|
+
# closed public job-event contract (#656)
|
|
253
|
+
"PUBLIC_JOB_EVENT_SCHEMA_VERSION",
|
|
254
|
+
"WORKER_JOB_EVENTS_ROUTE_TEMPLATE",
|
|
255
|
+
"MAX_PUBLIC_JOB_EVENT_BYTES",
|
|
256
|
+
"MAX_PUBLIC_JOB_EVENT_WIRE_BYTES",
|
|
257
|
+
"MAX_PUBLIC_JOB_EVENT_ENTRIES",
|
|
258
|
+
"MAX_PUBLIC_JOB_EVENT_ATTEMPT",
|
|
259
|
+
"MAX_PUBLIC_JOB_EVENT_QUIET_SECONDS",
|
|
260
|
+
"MAX_PUBLIC_ASSET_FINDING_COUNT",
|
|
261
|
+
"PUBLIC_JOB_SUPPORT_ID_PREFIX",
|
|
262
|
+
"PUBLIC_JOB_SUPPORT_ID_HEX_CHARS",
|
|
263
|
+
"PUBLIC_JOB_EVENT_OFFSET_GAP_CODE",
|
|
264
|
+
"PUBLIC_JOB_EVENT_OFFSET_CONFLICT_CODE",
|
|
265
|
+
"PUBLIC_JOB_EVENT_ATTEMPT_MISMATCH_CODE",
|
|
266
|
+
"PublicJobEventSeverity",
|
|
267
|
+
"PublicJobEventCode",
|
|
268
|
+
"PublicJobPreparationPhase",
|
|
269
|
+
"PublicAssetFindingReason",
|
|
270
|
+
"JobPreparingData",
|
|
271
|
+
"JobRunningData",
|
|
272
|
+
"JobStillWorkingData",
|
|
273
|
+
"JobAssetFindingData",
|
|
274
|
+
"JobPreparingEvent",
|
|
275
|
+
"JobRunningEvent",
|
|
276
|
+
"JobStillWorkingEvent",
|
|
277
|
+
"JobAssetFindingEvent",
|
|
278
|
+
"PublicJobEvent",
|
|
224
279
|
# domain dataclasses
|
|
225
280
|
"Tag",
|
|
226
281
|
"Project",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Internal helpers for tolerant response-record reconstruction."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from dataclasses import fields
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def known_payload_fields(record_type: Any, payload: Mapping[str, Any], *, owner: str) -> dict[str, Any]:
|
|
9
|
+
"""Copy only fields declared by ``record_type`` from one response mapping."""
|
|
10
|
+
|
|
11
|
+
if not isinstance(payload, Mapping):
|
|
12
|
+
raise TypeError(f"{owner} requires a mapping, got {type(payload).__name__}.")
|
|
13
|
+
return {
|
|
14
|
+
definition.name: payload[definition.name] for definition in fields(record_type) if definition.name in payload
|
|
15
|
+
}
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/artifacts.py
RENAMED
|
@@ -165,7 +165,17 @@ import re
|
|
|
165
165
|
from collections.abc import Mapping
|
|
166
166
|
from dataclasses import dataclass, field
|
|
167
167
|
|
|
168
|
-
from simulo.interfaces.ids import
|
|
168
|
+
from simulo.interfaces.ids import (
|
|
169
|
+
ArtifactId,
|
|
170
|
+
JobId,
|
|
171
|
+
ModelPublicId,
|
|
172
|
+
OutputPublicId,
|
|
173
|
+
RecordingPublicId,
|
|
174
|
+
parse_model_public_id,
|
|
175
|
+
parse_output_public_id,
|
|
176
|
+
parse_recording_public_id,
|
|
177
|
+
)
|
|
178
|
+
from simulo.interfaces.platform._payload import known_payload_fields
|
|
169
179
|
from simulo.interfaces.platform.enums import ArtifactKind
|
|
170
180
|
|
|
171
181
|
# --------------------------------------------------------------------------
|
|
@@ -740,9 +750,9 @@ class ArtifactRecord:
|
|
|
740
750
|
|
|
741
751
|
Wire-shaped by construction (``dataclasses.asdict`` serializes directly;
|
|
742
752
|
``kind`` is a ``StrEnum`` and JSON-encodes as its string value).
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
753
|
+
Use :meth:`from_payload` for tolerant response reconstruction: it ignores
|
|
754
|
+
additive unknown fields and coerces the known enum and public-ID fields.
|
|
755
|
+
Direct typed construction keeps the dataclass's strict keyword surface.
|
|
746
756
|
"""
|
|
747
757
|
|
|
748
758
|
artifact_id: ArtifactId
|
|
@@ -805,6 +815,32 @@ class ArtifactRecord:
|
|
|
805
815
|
not record one (recordings rows — the ``ModelRecord.kind`` unknown-value
|
|
806
816
|
precedent)."""
|
|
807
817
|
|
|
818
|
+
public_id: ModelPublicId | RecordingPublicId | OutputPublicId | None = None
|
|
819
|
+
"""The row's home Model, Recording, or Output ID.
|
|
820
|
+
|
|
821
|
+
``None`` means an older producer omitted the additive field. The union
|
|
822
|
+
projection never mints a second ID for model or recording bytes.
|
|
823
|
+
"""
|
|
824
|
+
|
|
825
|
+
@classmethod
|
|
826
|
+
def from_payload(cls, payload: Mapping[str, object]) -> "ArtifactRecord":
|
|
827
|
+
"""Build from a response mapping while ignoring additive unknown fields."""
|
|
828
|
+
|
|
829
|
+
values = known_payload_fields(cls, payload, owner="ArtifactRecord.from_payload()")
|
|
830
|
+
if "kind" in values:
|
|
831
|
+
values["kind"] = ArtifactKind(values["kind"])
|
|
832
|
+
if values.get("public_id") is not None:
|
|
833
|
+
value = values["public_id"]
|
|
834
|
+
for parser in (parse_model_public_id, parse_recording_public_id, parse_output_public_id):
|
|
835
|
+
try:
|
|
836
|
+
values["public_id"] = parser(value)
|
|
837
|
+
break
|
|
838
|
+
except ValueError:
|
|
839
|
+
continue
|
|
840
|
+
else:
|
|
841
|
+
raise ValueError("ArtifactRecord.from_payload() received a non-canonical public ID.")
|
|
842
|
+
return cls(**values)
|
|
843
|
+
|
|
808
844
|
|
|
809
845
|
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
810
846
|
class OutputFlag:
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""Closed, versioned public job-event contract.
|
|
2
|
+
|
|
3
|
+
Workers may publish only these Simulo-owned events. The contract deliberately
|
|
4
|
+
contains no free-form message, path, URL, exception, or metadata field; public
|
|
5
|
+
copy is rendered by the trusted consumer from ``code`` and its typed ``data``.
|
|
6
|
+
|
|
7
|
+
The write-side byte offset is over canonical projected text, not request JSON
|
|
8
|
+
and not the legacy raw-log store. ``offset == total`` appends. An earlier
|
|
9
|
+
offset is an idempotent retry only when a stored event starts exactly there
|
|
10
|
+
with identical canonical projection bytes; different bytes (or an offset
|
|
11
|
+
inside an event) are ``event_offset_conflict``. A future offset is
|
|
12
|
+
``event_offset_gap`` with the current public total. Every event carries the
|
|
13
|
+
current claim attempt and is fenced when it differs from the locked job row.
|
|
14
|
+
Every event POST must also carry the current claim lease in
|
|
15
|
+
:data:`simulo.interfaces.platform.submit.WORKER_LEASE_HEADER`; a missing or
|
|
16
|
+
stale lease is ``409 lease_lost`` and cannot append.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from enum import StrEnum
|
|
23
|
+
from typing import Literal, TypeAlias
|
|
24
|
+
|
|
25
|
+
PUBLIC_JOB_EVENT_SCHEMA_VERSION: Literal[1] = 1
|
|
26
|
+
WORKER_JOB_EVENTS_ROUTE_TEMPLATE = "/internal/worker/jobs/{job_id}/events"
|
|
27
|
+
|
|
28
|
+
# A public event is intentionally much smaller than the legacy 1 MiB raw-log
|
|
29
|
+
# chunk. The control plane applies this bound to the canonical projection.
|
|
30
|
+
MAX_PUBLIC_JOB_EVENT_BYTES = 4 * 1024
|
|
31
|
+
# Strict request-wire cap. Valid closed events are far smaller; this separate
|
|
32
|
+
# bound protects authentication-passing callers before JSON parsing.
|
|
33
|
+
MAX_PUBLIC_JOB_EVENT_WIRE_BYTES = 8 * 1024
|
|
34
|
+
# Bounds the server-owned provenance ledger kept outside the worker claim.
|
|
35
|
+
MAX_PUBLIC_JOB_EVENT_ENTRIES = 4_096
|
|
36
|
+
MAX_PUBLIC_JOB_EVENT_ATTEMPT = 1_000
|
|
37
|
+
MAX_PUBLIC_JOB_EVENT_QUIET_SECONDS = 24 * 60 * 60
|
|
38
|
+
MAX_PUBLIC_ASSET_FINDING_COUNT = 1_000
|
|
39
|
+
|
|
40
|
+
# Generated by trusted server code when an event-only job is enqueued. It is
|
|
41
|
+
# a correlation handle, not a credential or secret.
|
|
42
|
+
PUBLIC_JOB_SUPPORT_ID_PREFIX = "sup_"
|
|
43
|
+
PUBLIC_JOB_SUPPORT_ID_HEX_CHARS = 32
|
|
44
|
+
|
|
45
|
+
PUBLIC_JOB_EVENT_OFFSET_GAP_CODE = "event_offset_gap"
|
|
46
|
+
PUBLIC_JOB_EVENT_OFFSET_CONFLICT_CODE = "event_offset_conflict"
|
|
47
|
+
PUBLIC_JOB_EVENT_ATTEMPT_MISMATCH_CODE = "event_attempt_mismatch"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class PublicJobEventSeverity(StrEnum):
|
|
51
|
+
"""Closed public severities used by the initial event set."""
|
|
52
|
+
|
|
53
|
+
INFO = "info"
|
|
54
|
+
WARNING = "warning"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class PublicJobEventCode(StrEnum):
|
|
58
|
+
"""Closed event codes required by the asset-validation worker."""
|
|
59
|
+
|
|
60
|
+
JOB_PREPARING = "job.preparing"
|
|
61
|
+
JOB_RUNNING = "job.running"
|
|
62
|
+
JOB_STILL_WORKING = "job.still_working"
|
|
63
|
+
JOB_ASSET_FINDING = "job.asset_finding"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class PublicJobPreparationPhase(StrEnum):
|
|
67
|
+
"""Safe preparation phases for the initial event schema."""
|
|
68
|
+
|
|
69
|
+
ASSET_VALIDATION = "asset_validation"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class PublicAssetFindingReason(StrEnum):
|
|
73
|
+
"""Stable, product-owned asset finding reasons.
|
|
74
|
+
|
|
75
|
+
These values classify a trusted adapter outcome. They never carry the
|
|
76
|
+
native diagnostic or the asset's path/name.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
ASSET_OPEN_FAILED = "asset_open_failed"
|
|
80
|
+
ASSET_REFERENCE_UNRESOLVED = "asset_reference_unresolved"
|
|
81
|
+
ASSET_MATERIAL_UNRESOLVED = "asset_material_unresolved"
|
|
82
|
+
ASSET_PRIM_INVALID = "asset_prim_invalid"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _validate_attempt(attempt: int) -> None:
|
|
86
|
+
if isinstance(attempt, bool) or not isinstance(attempt, int):
|
|
87
|
+
raise TypeError("attempt must be an integer")
|
|
88
|
+
if not 1 <= attempt <= MAX_PUBLIC_JOB_EVENT_ATTEMPT:
|
|
89
|
+
raise ValueError(f"attempt must be between 1 and {MAX_PUBLIC_JOB_EVENT_ATTEMPT}")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _validate_schema_version(schema_version: object) -> None:
|
|
93
|
+
# ``True == 1`` in Python; identity of the JSON scalar type is part of the
|
|
94
|
+
# version boundary, not merely numeric equality.
|
|
95
|
+
if type(schema_version) is not int or schema_version != PUBLIC_JOB_EVENT_SCHEMA_VERSION:
|
|
96
|
+
raise ValueError("unsupported public job-event schema version")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
100
|
+
class JobPreparingData:
|
|
101
|
+
phase: PublicJobPreparationPhase
|
|
102
|
+
|
|
103
|
+
def __post_init__(self) -> None:
|
|
104
|
+
if not isinstance(self.phase, PublicJobPreparationPhase):
|
|
105
|
+
raise TypeError("phase must be a PublicJobPreparationPhase")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
109
|
+
class JobRunningData:
|
|
110
|
+
"""The running transition needs no worker-controlled detail."""
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
114
|
+
class JobStillWorkingData:
|
|
115
|
+
quiet_seconds: int
|
|
116
|
+
|
|
117
|
+
def __post_init__(self) -> None:
|
|
118
|
+
if isinstance(self.quiet_seconds, bool) or not isinstance(self.quiet_seconds, int):
|
|
119
|
+
raise TypeError("quiet_seconds must be an integer")
|
|
120
|
+
if not 1 <= self.quiet_seconds <= MAX_PUBLIC_JOB_EVENT_QUIET_SECONDS:
|
|
121
|
+
raise ValueError("quiet_seconds must be between 1 and " f"{MAX_PUBLIC_JOB_EVENT_QUIET_SECONDS}")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
125
|
+
class JobAssetFindingData:
|
|
126
|
+
reason: PublicAssetFindingReason
|
|
127
|
+
count: int = 1
|
|
128
|
+
|
|
129
|
+
def __post_init__(self) -> None:
|
|
130
|
+
if not isinstance(self.reason, PublicAssetFindingReason):
|
|
131
|
+
raise TypeError("reason must be a PublicAssetFindingReason")
|
|
132
|
+
if isinstance(self.count, bool) or not isinstance(self.count, int):
|
|
133
|
+
raise TypeError("count must be an integer")
|
|
134
|
+
if not 1 <= self.count <= MAX_PUBLIC_ASSET_FINDING_COUNT:
|
|
135
|
+
raise ValueError(f"count must be between 1 and {MAX_PUBLIC_ASSET_FINDING_COUNT}")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
139
|
+
class JobPreparingEvent:
|
|
140
|
+
attempt: int
|
|
141
|
+
data: JobPreparingData
|
|
142
|
+
schema_version: Literal[1] = PUBLIC_JOB_EVENT_SCHEMA_VERSION
|
|
143
|
+
code: Literal[PublicJobEventCode.JOB_PREPARING] = PublicJobEventCode.JOB_PREPARING
|
|
144
|
+
severity: Literal[PublicJobEventSeverity.INFO] = PublicJobEventSeverity.INFO
|
|
145
|
+
|
|
146
|
+
def __post_init__(self) -> None:
|
|
147
|
+
_validate_attempt(self.attempt)
|
|
148
|
+
_validate_schema_version(self.schema_version)
|
|
149
|
+
if self.code is not PublicJobEventCode.JOB_PREPARING:
|
|
150
|
+
raise ValueError("code does not match JobPreparingEvent")
|
|
151
|
+
if self.severity is not PublicJobEventSeverity.INFO:
|
|
152
|
+
raise ValueError("severity does not match JobPreparingEvent")
|
|
153
|
+
if not isinstance(self.data, JobPreparingData):
|
|
154
|
+
raise TypeError("data must be JobPreparingData")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
158
|
+
class JobRunningEvent:
|
|
159
|
+
attempt: int
|
|
160
|
+
data: JobRunningData
|
|
161
|
+
schema_version: Literal[1] = PUBLIC_JOB_EVENT_SCHEMA_VERSION
|
|
162
|
+
code: Literal[PublicJobEventCode.JOB_RUNNING] = PublicJobEventCode.JOB_RUNNING
|
|
163
|
+
severity: Literal[PublicJobEventSeverity.INFO] = PublicJobEventSeverity.INFO
|
|
164
|
+
|
|
165
|
+
def __post_init__(self) -> None:
|
|
166
|
+
_validate_attempt(self.attempt)
|
|
167
|
+
_validate_schema_version(self.schema_version)
|
|
168
|
+
if self.code is not PublicJobEventCode.JOB_RUNNING:
|
|
169
|
+
raise ValueError("code does not match JobRunningEvent")
|
|
170
|
+
if self.severity is not PublicJobEventSeverity.INFO:
|
|
171
|
+
raise ValueError("severity does not match JobRunningEvent")
|
|
172
|
+
if not isinstance(self.data, JobRunningData):
|
|
173
|
+
raise TypeError("data must be JobRunningData")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
177
|
+
class JobStillWorkingEvent:
|
|
178
|
+
attempt: int
|
|
179
|
+
data: JobStillWorkingData
|
|
180
|
+
schema_version: Literal[1] = PUBLIC_JOB_EVENT_SCHEMA_VERSION
|
|
181
|
+
code: Literal[PublicJobEventCode.JOB_STILL_WORKING] = PublicJobEventCode.JOB_STILL_WORKING
|
|
182
|
+
severity: Literal[PublicJobEventSeverity.INFO] = PublicJobEventSeverity.INFO
|
|
183
|
+
|
|
184
|
+
def __post_init__(self) -> None:
|
|
185
|
+
_validate_attempt(self.attempt)
|
|
186
|
+
_validate_schema_version(self.schema_version)
|
|
187
|
+
if self.code is not PublicJobEventCode.JOB_STILL_WORKING:
|
|
188
|
+
raise ValueError("code does not match JobStillWorkingEvent")
|
|
189
|
+
if self.severity is not PublicJobEventSeverity.INFO:
|
|
190
|
+
raise ValueError("severity does not match JobStillWorkingEvent")
|
|
191
|
+
if not isinstance(self.data, JobStillWorkingData):
|
|
192
|
+
raise TypeError("data must be JobStillWorkingData")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
196
|
+
class JobAssetFindingEvent:
|
|
197
|
+
attempt: int
|
|
198
|
+
data: JobAssetFindingData
|
|
199
|
+
schema_version: Literal[1] = PUBLIC_JOB_EVENT_SCHEMA_VERSION
|
|
200
|
+
code: Literal[PublicJobEventCode.JOB_ASSET_FINDING] = PublicJobEventCode.JOB_ASSET_FINDING
|
|
201
|
+
severity: Literal[PublicJobEventSeverity.WARNING] = PublicJobEventSeverity.WARNING
|
|
202
|
+
|
|
203
|
+
def __post_init__(self) -> None:
|
|
204
|
+
_validate_attempt(self.attempt)
|
|
205
|
+
_validate_schema_version(self.schema_version)
|
|
206
|
+
if self.code is not PublicJobEventCode.JOB_ASSET_FINDING:
|
|
207
|
+
raise ValueError("code does not match JobAssetFindingEvent")
|
|
208
|
+
if self.severity is not PublicJobEventSeverity.WARNING:
|
|
209
|
+
raise ValueError("severity does not match JobAssetFindingEvent")
|
|
210
|
+
if not isinstance(self.data, JobAssetFindingData):
|
|
211
|
+
raise TypeError("data must be JobAssetFindingData")
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
PublicJobEvent: TypeAlias = JobPreparingEvent | JobRunningEvent | JobStillWorkingEvent | JobAssetFindingEvent
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
__all__ = [
|
|
218
|
+
"PUBLIC_JOB_EVENT_SCHEMA_VERSION",
|
|
219
|
+
"WORKER_JOB_EVENTS_ROUTE_TEMPLATE",
|
|
220
|
+
"MAX_PUBLIC_JOB_EVENT_BYTES",
|
|
221
|
+
"MAX_PUBLIC_JOB_EVENT_WIRE_BYTES",
|
|
222
|
+
"MAX_PUBLIC_JOB_EVENT_ENTRIES",
|
|
223
|
+
"MAX_PUBLIC_JOB_EVENT_ATTEMPT",
|
|
224
|
+
"MAX_PUBLIC_JOB_EVENT_QUIET_SECONDS",
|
|
225
|
+
"MAX_PUBLIC_ASSET_FINDING_COUNT",
|
|
226
|
+
"PUBLIC_JOB_SUPPORT_ID_PREFIX",
|
|
227
|
+
"PUBLIC_JOB_SUPPORT_ID_HEX_CHARS",
|
|
228
|
+
"PUBLIC_JOB_EVENT_OFFSET_GAP_CODE",
|
|
229
|
+
"PUBLIC_JOB_EVENT_OFFSET_CONFLICT_CODE",
|
|
230
|
+
"PUBLIC_JOB_EVENT_ATTEMPT_MISMATCH_CODE",
|
|
231
|
+
"PublicJobEventSeverity",
|
|
232
|
+
"PublicJobEventCode",
|
|
233
|
+
"PublicJobPreparationPhase",
|
|
234
|
+
"PublicAssetFindingReason",
|
|
235
|
+
"JobPreparingData",
|
|
236
|
+
"JobRunningData",
|
|
237
|
+
"JobStillWorkingData",
|
|
238
|
+
"JobAssetFindingData",
|
|
239
|
+
"JobPreparingEvent",
|
|
240
|
+
"JobRunningEvent",
|
|
241
|
+
"JobStillWorkingEvent",
|
|
242
|
+
"JobAssetFindingEvent",
|
|
243
|
+
"PublicJobEvent",
|
|
244
|
+
]
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/runs.py
RENAMED
|
@@ -151,8 +151,9 @@ from collections.abc import Mapping
|
|
|
151
151
|
from dataclasses import dataclass, field
|
|
152
152
|
from typing import Any, Optional
|
|
153
153
|
|
|
154
|
-
from simulo.interfaces.ids import JobId, PackageId
|
|
155
|
-
from simulo.interfaces.platform.
|
|
154
|
+
from simulo.interfaces.ids import JobId, JobPublicId, PackageId, parse_job_public_id
|
|
155
|
+
from simulo.interfaces.platform._payload import known_payload_fields
|
|
156
|
+
from simulo.interfaces.platform.artifacts import JobOutputsSummary, OutputFlag
|
|
156
157
|
from simulo.interfaces.platform.enums import JobStatus
|
|
157
158
|
|
|
158
159
|
#: Default TCP port for the jobs API. Locally this is where ``simulo-backend
|
|
@@ -234,7 +235,9 @@ class JobRecord:
|
|
|
234
235
|
``GET /v1/jobs/{job_id}``. Timestamps are ISO 8601 UTC strings (wire-shaped
|
|
235
236
|
by construction so ``dataclasses.asdict`` serializes directly; ``status`` is
|
|
236
237
|
a :class:`~simulo.interfaces.platform.enums.JobStatus` ``StrEnum`` and
|
|
237
|
-
JSON-encodes as its string value).
|
|
238
|
+
JSON-encodes as its string value). Use :meth:`from_payload` for responses:
|
|
239
|
+
it ignores additive unknown fields and coerces known nested and enum values.
|
|
240
|
+
Direct typed construction keeps the dataclass's strict keyword surface.
|
|
238
241
|
"""
|
|
239
242
|
|
|
240
243
|
job_id: JobId
|
|
@@ -358,3 +361,58 @@ class JobRecord:
|
|
|
358
361
|
docstring: ``flags`` are what the worker announced at ``complete`` time;
|
|
359
362
|
``counts`` are reconciled at read time from actually-persisted artifact
|
|
360
363
|
rows, so a count never asserts bytes that did not land."""
|
|
364
|
+
|
|
365
|
+
public_id: Optional[JobPublicId] = None
|
|
366
|
+
"""Human-facing Job ID, or ``None`` when an older producer omitted it."""
|
|
367
|
+
|
|
368
|
+
@classmethod
|
|
369
|
+
def from_payload(cls, payload: Mapping[str, Any]) -> "JobRecord":
|
|
370
|
+
"""Build from a response mapping while ignoring additive unknown fields."""
|
|
371
|
+
|
|
372
|
+
values = known_payload_fields(cls, payload, owner="JobRecord.from_payload()")
|
|
373
|
+
if "status" in values:
|
|
374
|
+
values["status"] = JobStatus(values["status"])
|
|
375
|
+
if "assets" in values:
|
|
376
|
+
assets = values["assets"]
|
|
377
|
+
if not isinstance(assets, (list, tuple)):
|
|
378
|
+
raise TypeError("JobRecord.from_payload().assets requires an array.")
|
|
379
|
+
if not all(isinstance(asset, str) for asset in assets):
|
|
380
|
+
raise TypeError("JobRecord.from_payload().assets[] requires a str.")
|
|
381
|
+
values["assets"] = tuple(assets)
|
|
382
|
+
if values.get("public_id") is not None:
|
|
383
|
+
values["public_id"] = parse_job_public_id(values["public_id"])
|
|
384
|
+
|
|
385
|
+
outputs = values.get("outputs")
|
|
386
|
+
if outputs is not None and not isinstance(outputs, JobOutputsSummary):
|
|
387
|
+
if not isinstance(outputs, Mapping):
|
|
388
|
+
raise TypeError("JobRecord.from_payload().outputs requires a mapping or JobOutputsSummary.")
|
|
389
|
+
output_values = known_payload_fields(
|
|
390
|
+
JobOutputsSummary,
|
|
391
|
+
outputs,
|
|
392
|
+
owner="JobRecord.from_payload().outputs",
|
|
393
|
+
)
|
|
394
|
+
if "counts" in output_values and not isinstance(output_values["counts"], Mapping):
|
|
395
|
+
raise TypeError("JobRecord.from_payload().outputs.counts requires a mapping.")
|
|
396
|
+
if "flags" in output_values:
|
|
397
|
+
flags = output_values["flags"]
|
|
398
|
+
if not isinstance(flags, (list, tuple)):
|
|
399
|
+
raise TypeError("JobRecord.from_payload().outputs.flags requires an array.")
|
|
400
|
+
parsed_flags = []
|
|
401
|
+
for flag in flags:
|
|
402
|
+
if isinstance(flag, OutputFlag):
|
|
403
|
+
parsed_flags.append(flag)
|
|
404
|
+
elif isinstance(flag, Mapping):
|
|
405
|
+
parsed_flags.append(
|
|
406
|
+
OutputFlag(
|
|
407
|
+
**known_payload_fields(
|
|
408
|
+
OutputFlag,
|
|
409
|
+
flag,
|
|
410
|
+
owner="JobRecord.from_payload().outputs.flags[]",
|
|
411
|
+
)
|
|
412
|
+
)
|
|
413
|
+
)
|
|
414
|
+
else:
|
|
415
|
+
raise TypeError("JobRecord.from_payload().outputs.flags[] requires a mapping or OutputFlag.")
|
|
416
|
+
output_values["flags"] = tuple(parsed_flags)
|
|
417
|
+
values["outputs"] = JobOutputsSummary(**output_values)
|
|
418
|
+
return cls(**values)
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/submit.py
RENAMED
|
@@ -167,6 +167,16 @@ platform-standard shape: ``{"error": {"code": ..., "message": ..., "request_id":
|
|
|
167
167
|
"lease_seconds": 300,
|
|
168
168
|
"lease_id": "5e0c4a4e-..."}
|
|
169
169
|
|
|
170
|
+
A server-marked event-only asset-validation job additionally carries
|
|
171
|
+
``public_event_schema_version``, ``public_log_offset``, and ``support_id``.
|
|
172
|
+
These fields are absent for ordinary and legacy jobs, so workers published
|
|
173
|
+
before the event contract continue to ignore the additive seam. The support
|
|
174
|
+
id is a server-generated, non-secret correlation handle; a worker reports
|
|
175
|
+
events without supplying or overriding it. See
|
|
176
|
+
:mod:`simulo.interfaces.platform.job_events` for the closed event schema.
|
|
177
|
+
The private provenance ledger that derives ``public_log_offset`` is never
|
|
178
|
+
included in the claim.
|
|
179
|
+
|
|
170
180
|
or ``204`` with an empty body when there is no queued work. A claim is one
|
|
171
181
|
atomic ``SELECT ... FOR UPDATE SKIP LOCKED`` transition of exactly one
|
|
172
182
|
``queued`` job to ``running`` — plus a status event, ``attempts += 1``, and
|
|
@@ -175,7 +185,7 @@ platform-standard shape: ``{"error": {"code": ..., "message": ..., "request_id":
|
|
|
175
185
|
``lease_id`` (lease-reaper wave, additive) is the **per-claim ownership
|
|
176
186
|
token**: a fresh server-generated opaque id stamped on the job row by this
|
|
177
187
|
claim. The worker must present it back on every subsequent job-scoped call
|
|
178
|
-
(heartbeat / logs / complete) in the :data:`WORKER_LEASE_HEADER` request
|
|
188
|
+
(heartbeat / logs / events / complete) in the :data:`WORKER_LEASE_HEADER` request
|
|
179
189
|
header — it is what distinguishes "the worker holding the CURRENT claim"
|
|
180
190
|
from "a worker that once held a claim on this job_id". A call whose
|
|
181
191
|
presented lease does not match the job's current one (the job was reaped
|
|
@@ -197,9 +207,15 @@ platform-standard shape: ``{"error": {"code": ..., "message": ..., "request_id":
|
|
|
197
207
|
a live run), because the atomic ``lease_id`` swap at reclaim is what
|
|
198
208
|
fences out a stale worker, not the wall clock.
|
|
199
209
|
|
|
200
|
-
* :data:`WORKER_JOB_LOGS_ROUTE_TEMPLATE` (``POST``, ``?offset=N``) —
|
|
201
|
-
body is raw log bytes (not JSON), capped at
|
|
202
|
-
call.
|
|
210
|
+
* :data:`WORKER_JOB_LOGS_ROUTE_TEMPLATE` (``POST``, ``?offset=N``) — for an
|
|
211
|
+
ordinary job, the request body is raw log bytes (not JSON), capped at
|
|
212
|
+
:data:`MAX_LOG_CHUNK_BYTES` per call. Asset-validation jobs are mutually
|
|
213
|
+
exclusive with this legacy surface: marked and unmarked rows both reject a
|
|
214
|
+
raw append with ``409 raw_logs_disabled_for_asset_validation``. A marked
|
|
215
|
+
asset-validation worker instead posts the closed event body to
|
|
216
|
+
:data:`simulo.interfaces.platform.job_events.WORKER_JOB_EVENTS_ROUTE_TEMPLATE`;
|
|
217
|
+
the control plane renders the canonical public text and stores only those
|
|
218
|
+
bytes. ``offset`` is the byte offset the worker believes the server is at
|
|
203
219
|
(mirrors the client-side ``GET .../logs?offset=N`` semantics in
|
|
204
220
|
``runs.py``, from the writer's side):
|
|
205
221
|
|
|
@@ -221,6 +237,29 @@ platform-standard shape: ``{"error": {"code": ..., "message": ..., "request_id":
|
|
|
221
237
|
only channel). A stale lease is ``409 lease_lost`` — a reaped worker can
|
|
222
238
|
never append into the stream a later attempt now owns.
|
|
223
239
|
|
|
240
|
+
* :data:`simulo.interfaces.platform.job_events.WORKER_JOB_EVENTS_ROUTE_TEMPLATE`
|
|
241
|
+
(``POST``, ``?offset=N``) — the marked asset-validation replacement for raw
|
|
242
|
+
logs. Authentication and the machine rate limit run before the bounded body
|
|
243
|
+
is read; the JSON wire body is capped at
|
|
244
|
+
:data:`simulo.interfaces.platform.job_events.MAX_PUBLIC_JOB_EVENT_WIRE_BYTES`
|
|
245
|
+
and its canonical projection at
|
|
246
|
+
:data:`simulo.interfaces.platform.job_events.MAX_PUBLIC_JOB_EVENT_BYTES`.
|
|
247
|
+
Offsets are over canonical projected UTF-8 bytes only:
|
|
248
|
+
|
|
249
|
+
- ``offset == <public total>`` appends the new projection.
|
|
250
|
+
- ``offset < <public total>`` succeeds only when a ledger entry starts
|
|
251
|
+
exactly there with identical canonical projection bytes. Different bytes
|
|
252
|
+
or an offset inside an event answer ``409 event_offset_conflict``.
|
|
253
|
+
- ``offset > <public total>`` answers ``409 event_offset_gap`` with the
|
|
254
|
+
current public total.
|
|
255
|
+
|
|
256
|
+
The request must carry the current claim lease in
|
|
257
|
+
:data:`WORKER_LEASE_HEADER`; a missing or stale value answers ``409
|
|
258
|
+
lease_lost`` and cannot append. The body ``attempt`` must equal the locked
|
|
259
|
+
job row's current attempt; otherwise it is fenced with ``409
|
|
260
|
+
event_attempt_mismatch``. Physical legacy raw rows are outside this logical
|
|
261
|
+
byte space and cannot affect claim, retry, GET/tail, or flattening behavior.
|
|
262
|
+
|
|
224
263
|
* :data:`WORKER_JOB_HEARTBEAT_ROUTE_TEMPLATE` (``POST``) — no request body
|
|
225
264
|
required; extends the claim lease. The request carries the claim's
|
|
226
265
|
``lease_id`` in :data:`WORKER_LEASE_HEADER`; a stale lease is ``409
|
|
@@ -349,10 +388,20 @@ models; the list is empty, never an error.
|
|
|
349
388
|
"""
|
|
350
389
|
|
|
351
390
|
import re
|
|
391
|
+
from collections.abc import Mapping
|
|
352
392
|
from dataclasses import dataclass
|
|
353
393
|
from typing import Optional
|
|
354
394
|
|
|
355
|
-
from simulo.interfaces.ids import
|
|
395
|
+
from simulo.interfaces.ids import (
|
|
396
|
+
JobId,
|
|
397
|
+
ModelPublicId,
|
|
398
|
+
RecordingId,
|
|
399
|
+
RecordingPublicId,
|
|
400
|
+
TrainedModelId,
|
|
401
|
+
parse_model_public_id,
|
|
402
|
+
parse_recording_public_id,
|
|
403
|
+
)
|
|
404
|
+
from simulo.interfaces.platform._payload import known_payload_fields
|
|
356
405
|
|
|
357
406
|
# --------------------------------------------------------------------------
|
|
358
407
|
# Client-facing routes (bearer = user JWT; require_active_org server-side)
|
|
@@ -443,7 +492,9 @@ SEED_SOURCE_CONFLICT_CODE = "seed_source_conflict"
|
|
|
443
492
|
|
|
444
493
|
#: ``POST`` — atomically claim the next queued job (``FOR UPDATE SKIP LOCKED``).
|
|
445
494
|
WORKER_CLAIM_ROUTE = "/internal/worker/claim"
|
|
446
|
-
#: ``POST`` — append raw log bytes at a byte offset
|
|
495
|
+
#: ``POST`` — append ordinary-job raw log bytes at a byte offset
|
|
496
|
+
#: (``?offset=N``). Asset-validation jobs must use the closed event route.
|
|
497
|
+
#: ``.format(job_id=...)``.
|
|
447
498
|
WORKER_JOB_LOGS_ROUTE_TEMPLATE = "/internal/worker/jobs/{job_id}/logs"
|
|
448
499
|
#: ``POST`` — extend the claim lease; response carries the ``cancelled`` seam.
|
|
449
500
|
#: ``.format(job_id=...)``.
|
|
@@ -468,9 +519,9 @@ WORKER_JOB_MODELS_PRESIGN_ROUTE_TEMPLATE = "/internal/worker/jobs/{job_id}/model
|
|
|
468
519
|
WORKER_JOB_MODELS_ROUTE_TEMPLATE = "/internal/worker/jobs/{job_id}/models"
|
|
469
520
|
|
|
470
521
|
#: Request header carrying the claim's ``lease_id`` ownership token on every
|
|
471
|
-
#: job-scoped worker call (heartbeat / logs / complete). A header — not a body
|
|
522
|
+
#: job-scoped worker call (heartbeat / logs / events / complete). A header — not a body
|
|
472
523
|
#: field — because the logs route's body is raw bytes and heartbeat has no
|
|
473
|
-
#: body; one uniform channel for all
|
|
524
|
+
#: body; one uniform channel for all four. A missing or mismatching value is
|
|
474
525
|
#: ``409 lease_lost`` (see the module docstring's claim section for the full
|
|
475
526
|
#: lease/reaper semantics). Recording presign/finalize are deliberately NOT
|
|
476
527
|
#: lease-gated: the worker uploads recordings AFTER ``complete`` — i.e. after
|
|
@@ -525,6 +576,8 @@ class RecordingRecord:
|
|
|
525
576
|
them. The three episode-summary fields are optional because not every
|
|
526
577
|
recording is episodic (e.g. a raw rollout capture) and because a worker
|
|
527
578
|
may finalize a recording before its summary stats are computed.
|
|
579
|
+
Use :meth:`from_payload` for tolerant response reconstruction; direct typed
|
|
580
|
+
construction keeps the dataclass's strict keyword surface.
|
|
528
581
|
"""
|
|
529
582
|
|
|
530
583
|
recording_id: RecordingId
|
|
@@ -560,6 +613,18 @@ class RecordingRecord:
|
|
|
560
613
|
success_rate: Optional[float] = None
|
|
561
614
|
"""Fraction of episodes that reached a success termination, if computed."""
|
|
562
615
|
|
|
616
|
+
public_id: Optional[RecordingPublicId] = None
|
|
617
|
+
"""Human-facing Recording ID, or ``None`` when an older producer omitted it."""
|
|
618
|
+
|
|
619
|
+
@classmethod
|
|
620
|
+
def from_payload(cls, payload: Mapping[str, object]) -> "RecordingRecord":
|
|
621
|
+
"""Build from a response mapping while ignoring additive unknown fields."""
|
|
622
|
+
|
|
623
|
+
values = known_payload_fields(cls, payload, owner="RecordingRecord.from_payload()")
|
|
624
|
+
if values.get("public_id") is not None:
|
|
625
|
+
values["public_id"] = parse_recording_public_id(values["public_id"])
|
|
626
|
+
return cls(**values)
|
|
627
|
+
|
|
563
628
|
|
|
564
629
|
#: The checkpoint kinds a training job produces (``simulo.checkpoint.v1``:
|
|
565
630
|
#: ``best.pt`` = highest-mean-episode-reward snapshot, ``latest.pt`` = most
|
|
@@ -583,6 +648,8 @@ class ModelRecord:
|
|
|
583
648
|
``digest_sha256`` is always present (bare 64-hex): the worker hashes the
|
|
584
649
|
checkpoint before finalizing, and clients MUST verify the downloaded bytes
|
|
585
650
|
against it.
|
|
651
|
+
Use :meth:`from_payload` for tolerant response reconstruction; direct typed
|
|
652
|
+
construction keeps the dataclass's strict keyword surface.
|
|
586
653
|
"""
|
|
587
654
|
|
|
588
655
|
model_id: TrainedModelId
|
|
@@ -609,6 +676,18 @@ class ModelRecord:
|
|
|
609
676
|
created_at: str
|
|
610
677
|
"""When the model was finalized (ISO 8601 UTC)."""
|
|
611
678
|
|
|
679
|
+
public_id: Optional[ModelPublicId] = None
|
|
680
|
+
"""Human-facing Model ID, or ``None`` when an older producer omitted it."""
|
|
681
|
+
|
|
682
|
+
@classmethod
|
|
683
|
+
def from_payload(cls, payload: Mapping[str, object]) -> "ModelRecord":
|
|
684
|
+
"""Build from a response mapping while ignoring additive unknown fields."""
|
|
685
|
+
|
|
686
|
+
values = known_payload_fields(cls, payload, owner="ModelRecord.from_payload()")
|
|
687
|
+
if values.get("public_id") is not None:
|
|
688
|
+
values["public_id"] = parse_model_public_id(values["public_id"])
|
|
689
|
+
return cls(**values)
|
|
690
|
+
|
|
612
691
|
|
|
613
692
|
# --------------------------------------------------------------------------
|
|
614
693
|
# Reserved runtime environment keys
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo_interfaces.egg-info/SOURCES.txt
RENAMED
|
@@ -23,6 +23,7 @@ src/simulo/interfaces/authoring/terrain.py
|
|
|
23
23
|
src/simulo/interfaces/authoring/visual.py
|
|
24
24
|
src/simulo/interfaces/authoring/world.py
|
|
25
25
|
src/simulo/interfaces/platform/__init__.py
|
|
26
|
+
src/simulo/interfaces/platform/_payload.py
|
|
26
27
|
src/simulo/interfaces/platform/app.py
|
|
27
28
|
src/simulo/interfaces/platform/artifacts.py
|
|
28
29
|
src/simulo/interfaces/platform/asset.py
|
|
@@ -31,6 +32,7 @@ src/simulo/interfaces/platform/callbacks.py
|
|
|
31
32
|
src/simulo/interfaces/platform/debug.py
|
|
32
33
|
src/simulo/interfaces/platform/domain.py
|
|
33
34
|
src/simulo/interfaces/platform/enums.py
|
|
35
|
+
src/simulo/interfaces/platform/job_events.py
|
|
34
36
|
src/simulo/interfaces/platform/manifest.py
|
|
35
37
|
src/simulo/interfaces/platform/policy_bundle.py
|
|
36
38
|
src/simulo/interfaces/platform/runs.py
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"""Strongly-typed string identifiers for the platform domain model (Part B).
|
|
2
|
-
|
|
3
|
-
``NewType`` aliases — zero runtime cost (each is the underlying ``str`` at
|
|
4
|
-
runtime) but distinct to the type checker, so a ``ProjectId`` can never be
|
|
5
|
-
passed where a ``JobId`` is expected.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from typing import NewType
|
|
9
|
-
|
|
10
|
-
OrganizationId = NewType("OrganizationId", str)
|
|
11
|
-
ProjectId = NewType("ProjectId", str)
|
|
12
|
-
PackageId = NewType("PackageId", str)
|
|
13
|
-
JobId = NewType("JobId", str)
|
|
14
|
-
ResourceId = NewType("ResourceId", str)
|
|
15
|
-
CheckpointId = NewType("CheckpointId", str)
|
|
16
|
-
TrainedModelId = NewType("TrainedModelId", str)
|
|
17
|
-
TagId = NewType("TagId", str)
|
|
18
|
-
ResourceUri = NewType("ResourceUri", str) # e.g. "robot/so-arm-100:v3"
|
|
19
|
-
Digest = NewType("Digest", str) # content-addressed package source digest
|
|
20
|
-
RecordingId = NewType("RecordingId", str) # a job-produced MCAP recording (PR-0 wire contract)
|
|
21
|
-
WorkerId = NewType("WorkerId", str) # a claiming worker process (PR-0 wire contract)
|
|
22
|
-
AssetId = NewType("AssetId", str) # a catalog asset (USD Asset Catalogs wire contract, PR-1)
|
|
23
|
-
AssetVersionId = NewType("AssetVersionId", str) # one immutable asset version (PR-1)
|
|
24
|
-
ArtifactId = NewType("ArtifactId", str) # a job-produced output in the /artifacts projection (Plan B)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/__init__.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/actuator.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/asset.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/entity.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/light.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/materials.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/physics.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/pose.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/primitives.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/prop.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/record.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/robot.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/scene.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/sensor.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/terrain.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/visual.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/authoring/world.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/asset.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/callbacks.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/debug.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/domain.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/enums.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/manifest.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/runtime.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/platform/volume.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/__init__.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/anomaly.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/components.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/player.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/policy.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/scenario.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/tensors.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo/interfaces/runtime/trainer.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo_interfaces.egg-info/requires.txt
RENAMED
|
File without changes
|
{simulo_interfaces-0.14.0 → simulo_interfaces-0.15.2}/src/simulo_interfaces.egg-info/top_level.txt
RENAMED
|
File without changes
|