UncountablePythonSDK 0.0.164__py3-none-any.whl → 0.0.166__py3-none-any.whl
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.
- pkgs/serialization_util/__init__.py +2 -0
- pkgs/serialization_util/serialization_helpers.py +25 -0
- pkgs/type_spec/type_info/emit_type_info.py +4 -5
- uncountable/integration/queue_runner/worker.py +38 -2
- uncountable/integration/telemetry.py +32 -1
- uncountable/integration/webhook_server/entrypoint.py +27 -3
- uncountable/types/__init__.py +2 -0
- uncountable/types/api/output_parameters/__init__.py +1 -0
- uncountable/types/api/output_parameters/swap_output_condition_parameters.py +150 -0
- uncountable/types/async_batch_processor.py +59 -16
- uncountable/types/async_batch_t.py +1 -0
- uncountable/types/client_base.py +105 -74
- uncountable/types/entity_t.py +14 -2
- {uncountablepythonsdk-0.0.164.dist-info → uncountablepythonsdk-0.0.166.dist-info}/METADATA +1 -1
- {uncountablepythonsdk-0.0.164.dist-info → uncountablepythonsdk-0.0.166.dist-info}/RECORD +17 -15
- {uncountablepythonsdk-0.0.164.dist-info → uncountablepythonsdk-0.0.166.dist-info}/WHEEL +0 -0
- {uncountablepythonsdk-0.0.164.dist-info → uncountablepythonsdk-0.0.166.dist-info}/top_level.txt +0 -0
|
@@ -3,12 +3,14 @@ from .dataclasses import dict_fields as dict_fields
|
|
|
3
3
|
from .dataclasses import iterate_fields as iterate_fields
|
|
4
4
|
from .serialization_helpers import (
|
|
5
5
|
JsonValue,
|
|
6
|
+
convert_decimals_to_strings,
|
|
6
7
|
serialize_for_api,
|
|
7
8
|
serialize_for_storage,
|
|
8
9
|
serialize_for_storage_dict,
|
|
9
10
|
)
|
|
10
11
|
|
|
11
12
|
__all__: list[str] = [
|
|
13
|
+
"convert_decimals_to_strings",
|
|
12
14
|
"convert_dict_to_snake_case",
|
|
13
15
|
"serialize_for_api",
|
|
14
16
|
"serialize_for_storage",
|
|
@@ -14,6 +14,7 @@ from typing import (
|
|
|
14
14
|
Protocol,
|
|
15
15
|
TypeVar,
|
|
16
16
|
Union,
|
|
17
|
+
assert_never,
|
|
17
18
|
overload,
|
|
18
19
|
)
|
|
19
20
|
|
|
@@ -37,6 +38,30 @@ else:
|
|
|
37
38
|
T = TypeVar("T")
|
|
38
39
|
|
|
39
40
|
|
|
41
|
+
# IMPROVE: this match should be exhaustive over JsonValue, but mypy can't verify
|
|
42
|
+
# that Mapping()/Sequence() fully narrow the TYPE_CHECKING definition of JsonValue.
|
|
43
|
+
def convert_decimals_to_strings(obj: JsonValue) -> JsonValue:
|
|
44
|
+
match obj:
|
|
45
|
+
case Decimal():
|
|
46
|
+
return str(obj)
|
|
47
|
+
case (
|
|
48
|
+
str()
|
|
49
|
+
| int()
|
|
50
|
+
| float()
|
|
51
|
+
| bool()
|
|
52
|
+
| None
|
|
53
|
+
| datetime.datetime()
|
|
54
|
+
| datetime.date()
|
|
55
|
+
):
|
|
56
|
+
return obj
|
|
57
|
+
case Mapping():
|
|
58
|
+
return {k: convert_decimals_to_strings(v) for k, v in obj.items()}
|
|
59
|
+
case Sequence():
|
|
60
|
+
return [convert_decimals_to_strings(v) for v in obj]
|
|
61
|
+
case _ as unreachable:
|
|
62
|
+
assert_never(unreachable)
|
|
63
|
+
|
|
64
|
+
|
|
40
65
|
class Dataclass(Protocol):
|
|
41
66
|
__dataclass_fields__: ClassVar[dict] # type: ignore[type-arg,unused-ignore]
|
|
42
67
|
|
|
@@ -144,11 +144,10 @@ def asdict_for_yaml_dump(dataclass_instance: Any) -> Any:
|
|
|
144
144
|
def emit_type_info_python(build: builder.SpecBuilder, output: str) -> None:
|
|
145
145
|
type_map = _build_map_all(build, python=True)
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
util.rewrite_file(f"{output}/type_map.yaml", yaml_content)
|
|
147
|
+
for namespace, data in type_map.namespaces.items():
|
|
148
|
+
serial = serialize_for_storage(asdict_for_yaml_dump(data))
|
|
149
|
+
yaml_content = yaml.dump(serial, default_flow_style=False, sort_keys=True)
|
|
150
|
+
util.rewrite_file(f"{output}/namespace_{namespace}.yaml", yaml_content)
|
|
152
151
|
|
|
153
152
|
|
|
154
153
|
@dataclasses.dataclass
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import pathlib
|
|
2
4
|
import sys
|
|
3
5
|
import typing
|
|
4
6
|
from concurrent.futures import ProcessPoolExecutor
|
|
@@ -105,14 +107,48 @@ def _resolve_queued_job_request_context(
|
|
|
105
107
|
return None
|
|
106
108
|
|
|
107
109
|
|
|
110
|
+
_CGROUP_V2_MEMORY_MAX = pathlib.Path("/sys/fs/cgroup/memory.max")
|
|
111
|
+
_CGROUP_V1_MEMORY_LIMIT = pathlib.Path("/sys/fs/cgroup/memory/memory.limit_in_bytes")
|
|
112
|
+
|
|
113
|
+
_MEMORY_LIMIT_FRACTION_CGROUP = 0.75
|
|
114
|
+
_MEMORY_LIMIT_FRACTION_HOST = 0.9
|
|
115
|
+
|
|
116
|
+
_logger = logging.getLogger(__name__)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _get_cgroup_memory_limit_bytes() -> int | None:
|
|
120
|
+
for cgroup_path in (_CGROUP_V2_MEMORY_MAX, _CGROUP_V1_MEMORY_LIMIT):
|
|
121
|
+
if not cgroup_path.exists():
|
|
122
|
+
continue
|
|
123
|
+
raw = cgroup_path.read_text().strip()
|
|
124
|
+
if raw == "max":
|
|
125
|
+
# cgroups v2 "max" means no limit is set
|
|
126
|
+
return None
|
|
127
|
+
value = int(raw)
|
|
128
|
+
# cgroups v1 uses a very large sentinel for "no limit"
|
|
129
|
+
if value >= 2**63 - 1:
|
|
130
|
+
return None
|
|
131
|
+
return value
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
|
|
108
135
|
def run_queued_job(
|
|
109
136
|
queued_job: queued_job_t.QueuedJob,
|
|
110
137
|
) -> job_definition_t.JobResult:
|
|
111
138
|
with get_otel_tracer().start_as_current_span(name="run_queued_job") as span:
|
|
112
139
|
if resource is not None:
|
|
113
|
-
|
|
114
|
-
|
|
140
|
+
cgroup_limit = _get_cgroup_memory_limit_bytes()
|
|
141
|
+
if cgroup_limit is not None:
|
|
142
|
+
limit_bytes = int(cgroup_limit * _MEMORY_LIMIT_FRACTION_CGROUP)
|
|
143
|
+
else:
|
|
144
|
+
total_mem = psutil.virtual_memory().total
|
|
145
|
+
limit_bytes = int(total_mem * _MEMORY_LIMIT_FRACTION_HOST)
|
|
115
146
|
resource.setrlimit(resource.RLIMIT_AS, (limit_bytes, limit_bytes))
|
|
147
|
+
_logger.info(
|
|
148
|
+
"RLIMIT_AS set to %.2f GiB (source: %s)",
|
|
149
|
+
limit_bytes / (1024**3),
|
|
150
|
+
"cgroup" if cgroup_limit is not None else "host",
|
|
151
|
+
)
|
|
116
152
|
|
|
117
153
|
job_details = get_registered_job_details(queued_job.job_ref_name)
|
|
118
154
|
job_logger = JobLogger(
|
|
@@ -220,9 +220,15 @@ class Logger:
|
|
|
220
220
|
|
|
221
221
|
|
|
222
222
|
class PerJobResourceTracker:
|
|
223
|
-
def __init__(
|
|
223
|
+
def __init__(
|
|
224
|
+
self,
|
|
225
|
+
logger: "JobLogger",
|
|
226
|
+
sample_interval: float = 0.5,
|
|
227
|
+
log_interval: float = 30.0,
|
|
228
|
+
) -> None:
|
|
224
229
|
self.logger = logger
|
|
225
230
|
self.sample_interval = sample_interval
|
|
231
|
+
self.log_interval = log_interval
|
|
226
232
|
self._process = psutil.Process(os.getpid())
|
|
227
233
|
self._stop_event = threading.Event()
|
|
228
234
|
self._thread: threading.Thread | None = None
|
|
@@ -233,15 +239,40 @@ class PerJobResourceTracker:
|
|
|
233
239
|
self.start_wall_time: float | None = None
|
|
234
240
|
self.end_wall_time: float | None = None
|
|
235
241
|
|
|
242
|
+
def _current_stats(self) -> Attributes:
|
|
243
|
+
assert self.start_cpu_times is not None
|
|
244
|
+
assert self.start_wall_time is not None
|
|
245
|
+
current_cpu_times = self._process.cpu_times()
|
|
246
|
+
cpu_user = current_cpu_times.user - self.start_cpu_times.user
|
|
247
|
+
cpu_sys = current_cpu_times.system - self.start_cpu_times.system
|
|
248
|
+
cpu_total = cpu_user + cpu_sys
|
|
249
|
+
elapsed = time.monotonic() - self.start_wall_time
|
|
250
|
+
return {
|
|
251
|
+
"cpu_user_s": round(cpu_user, 3),
|
|
252
|
+
"cpu_system_s": round(cpu_sys, 3),
|
|
253
|
+
"cpu_total_s": round(cpu_total, 3),
|
|
254
|
+
"wall_time_s": round(elapsed, 3),
|
|
255
|
+
"current_rss_mb": round(self._process.memory_info().rss / (1024 * 1024), 2),
|
|
256
|
+
"peak_rss_mb": round(self.max_rss / (1024 * 1024), 2),
|
|
257
|
+
}
|
|
258
|
+
|
|
236
259
|
def start(self) -> None:
|
|
237
260
|
self.start_cpu_times = self._process.cpu_times()
|
|
238
261
|
self.start_wall_time = time.monotonic()
|
|
239
262
|
|
|
240
263
|
def _monitor() -> None:
|
|
264
|
+
last_log_time = time.monotonic()
|
|
241
265
|
try:
|
|
242
266
|
while not self._stop_event.is_set():
|
|
243
267
|
rss = self._process.memory_info().rss
|
|
244
268
|
self.max_rss = max(self.max_rss, rss)
|
|
269
|
+
now = time.monotonic()
|
|
270
|
+
if now - last_log_time >= self.log_interval:
|
|
271
|
+
self.logger.log_info(
|
|
272
|
+
"Job resource usage (periodic)",
|
|
273
|
+
attributes=self._current_stats(),
|
|
274
|
+
)
|
|
275
|
+
last_log_time = now
|
|
245
276
|
time.sleep(self.sample_interval)
|
|
246
277
|
except Exception:
|
|
247
278
|
self._stop_event.set()
|
|
@@ -24,8 +24,22 @@ def register_route(
|
|
|
24
24
|
job: job_definition_t.HttpJobDefinitionBase,
|
|
25
25
|
) -> None:
|
|
26
26
|
route = f"/{profile_meta.name}/{job.id}"
|
|
27
|
+
log_attributes = {
|
|
28
|
+
"profile.name": profile_meta.name,
|
|
29
|
+
"profile.base_url": profile_meta.base_url,
|
|
30
|
+
"job.name": job.name,
|
|
31
|
+
"job.id": job.id,
|
|
32
|
+
"http.route": route,
|
|
33
|
+
}
|
|
27
34
|
|
|
28
35
|
def handle_request() -> ResponseReturnValue:
|
|
36
|
+
method = flask.request.method
|
|
37
|
+
url = flask.request.url
|
|
38
|
+
request_attributes = {
|
|
39
|
+
**log_attributes,
|
|
40
|
+
"http.method": method,
|
|
41
|
+
"http.url": url,
|
|
42
|
+
}
|
|
29
43
|
with server_logger.push_scope(route):
|
|
30
44
|
try:
|
|
31
45
|
if not isinstance(job.executor, job_definition_t.JobExecutorScript):
|
|
@@ -56,10 +70,18 @@ def register_route(
|
|
|
56
70
|
http_response.headers,
|
|
57
71
|
)
|
|
58
72
|
except HttpException as e:
|
|
59
|
-
server_logger.log_exception(
|
|
73
|
+
server_logger.log_exception(
|
|
74
|
+
e,
|
|
75
|
+
message=f"HTTP error on {method} {url}",
|
|
76
|
+
attributes=request_attributes,
|
|
77
|
+
)
|
|
60
78
|
return e.make_error_response()
|
|
61
79
|
except Exception as e:
|
|
62
|
-
server_logger.log_exception(
|
|
80
|
+
server_logger.log_exception(
|
|
81
|
+
e,
|
|
82
|
+
message=f"Unexpected error on {method} {url}",
|
|
83
|
+
attributes=request_attributes,
|
|
84
|
+
)
|
|
63
85
|
return HttpException.unknown_error().make_error_response()
|
|
64
86
|
|
|
65
87
|
app.add_url_rule(
|
|
@@ -69,7 +91,9 @@ def register_route(
|
|
|
69
91
|
methods=["POST"],
|
|
70
92
|
)
|
|
71
93
|
|
|
72
|
-
server_logger.log_info(
|
|
94
|
+
server_logger.log_info(
|
|
95
|
+
f"job {job.id} webhook registered at: {route}", attributes=log_attributes
|
|
96
|
+
)
|
|
73
97
|
|
|
74
98
|
|
|
75
99
|
def main() -> None:
|
uncountable/types/__init__.py
CHANGED
|
@@ -135,6 +135,7 @@ from .api.entity import set_values as set_values_t
|
|
|
135
135
|
from . import sockets_t as sockets_t
|
|
136
136
|
from . import specs_t as specs_t
|
|
137
137
|
from . import step_relationships_t as step_relationships_t
|
|
138
|
+
from .api.output_parameters import swap_output_condition_parameters as swap_output_condition_parameters_t
|
|
138
139
|
from .api.entity import transition_entity_phase as transition_entity_phase_t
|
|
139
140
|
from .api.recipes import unarchive_recipes as unarchive_recipes_t
|
|
140
141
|
from . import units_t as units_t
|
|
@@ -286,6 +287,7 @@ __all__: list[str] = [
|
|
|
286
287
|
"sockets_t",
|
|
287
288
|
"specs_t",
|
|
288
289
|
"step_relationships_t",
|
|
290
|
+
"swap_output_condition_parameters_t",
|
|
289
291
|
"transition_entity_phase_t",
|
|
290
292
|
"unarchive_recipes_t",
|
|
291
293
|
"units_t",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
2
|
+
# ruff: noqa: E402 Q003
|
|
3
|
+
# fmt: off
|
|
4
|
+
# isort: skip_file
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
import typing # noqa: F401
|
|
7
|
+
import datetime # noqa: F401
|
|
8
|
+
from decimal import Decimal # noqa: F401
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
import dataclasses
|
|
11
|
+
from pkgs.serialization import serial_class
|
|
12
|
+
from pkgs.serialization import serial_union_annotation
|
|
13
|
+
from ... import async_batch_t
|
|
14
|
+
from ... import base_t
|
|
15
|
+
from ... import identifier_t
|
|
16
|
+
|
|
17
|
+
__all__: list[str] = [
|
|
18
|
+
"AllInProjectSwapScope",
|
|
19
|
+
"Arguments",
|
|
20
|
+
"ConditionParameterDefinition",
|
|
21
|
+
"Data",
|
|
22
|
+
"ENDPOINT_METHOD",
|
|
23
|
+
"ENDPOINT_PATH",
|
|
24
|
+
"NewOutputCondition",
|
|
25
|
+
"NewOutputConditionExisting",
|
|
26
|
+
"NewOutputConditionFromDefinition",
|
|
27
|
+
"NewOutputConditionType",
|
|
28
|
+
"SwapScope",
|
|
29
|
+
"SwapScopeType",
|
|
30
|
+
"TargetedSwapScope",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
ENDPOINT_METHOD = "POST"
|
|
34
|
+
ENDPOINT_PATH = "api/external/output_parameters/swap_output_condition_parameters"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
38
|
+
@serial_class(
|
|
39
|
+
named_type_path="sdk.api.output_parameters.swap_output_condition_parameters.ConditionParameterDefinition",
|
|
40
|
+
)
|
|
41
|
+
@dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
|
|
42
|
+
class ConditionParameterDefinition:
|
|
43
|
+
condition_parameter_key: identifier_t.IdentifierKey
|
|
44
|
+
value_numeric: Decimal | None = None
|
|
45
|
+
value_text: str | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
49
|
+
class NewOutputConditionType(StrEnum):
|
|
50
|
+
EXISTING = "existing"
|
|
51
|
+
DEFINITION = "definition"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
55
|
+
@serial_class(
|
|
56
|
+
named_type_path="sdk.api.output_parameters.swap_output_condition_parameters.NewOutputConditionExisting",
|
|
57
|
+
parse_require={"type"},
|
|
58
|
+
)
|
|
59
|
+
@dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
|
|
60
|
+
class NewOutputConditionExisting:
|
|
61
|
+
type: typing.Literal[NewOutputConditionType.EXISTING] = NewOutputConditionType.EXISTING
|
|
62
|
+
condition_key: identifier_t.IdentifierKey
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
66
|
+
@serial_class(
|
|
67
|
+
named_type_path="sdk.api.output_parameters.swap_output_condition_parameters.NewOutputConditionFromDefinition",
|
|
68
|
+
parse_require={"type"},
|
|
69
|
+
)
|
|
70
|
+
@dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
|
|
71
|
+
class NewOutputConditionFromDefinition:
|
|
72
|
+
type: typing.Literal[NewOutputConditionType.DEFINITION] = NewOutputConditionType.DEFINITION
|
|
73
|
+
parameters: list[ConditionParameterDefinition]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
77
|
+
NewOutputCondition = typing.Annotated[
|
|
78
|
+
NewOutputConditionExisting | NewOutputConditionFromDefinition,
|
|
79
|
+
serial_union_annotation(
|
|
80
|
+
named_type_path="sdk.api.output_parameters.swap_output_condition_parameters.NewOutputCondition",
|
|
81
|
+
discriminator="type",
|
|
82
|
+
discriminator_map={
|
|
83
|
+
"existing": NewOutputConditionExisting,
|
|
84
|
+
"definition": NewOutputConditionFromDefinition,
|
|
85
|
+
},
|
|
86
|
+
),
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
91
|
+
class SwapScopeType(StrEnum):
|
|
92
|
+
TARGETED = "targeted"
|
|
93
|
+
ALL_IN_PROJECT = "all_in_project"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
97
|
+
@serial_class(
|
|
98
|
+
named_type_path="sdk.api.output_parameters.swap_output_condition_parameters.TargetedSwapScope",
|
|
99
|
+
parse_require={"type"},
|
|
100
|
+
)
|
|
101
|
+
@dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
|
|
102
|
+
class TargetedSwapScope:
|
|
103
|
+
type: typing.Literal[SwapScopeType.TARGETED] = SwapScopeType.TARGETED
|
|
104
|
+
recipe_keys: list[identifier_t.IdentifierKey]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
108
|
+
@serial_class(
|
|
109
|
+
named_type_path="sdk.api.output_parameters.swap_output_condition_parameters.AllInProjectSwapScope",
|
|
110
|
+
parse_require={"type"},
|
|
111
|
+
)
|
|
112
|
+
@dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
|
|
113
|
+
class AllInProjectSwapScope:
|
|
114
|
+
type: typing.Literal[SwapScopeType.ALL_IN_PROJECT] = SwapScopeType.ALL_IN_PROJECT
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
118
|
+
SwapScope = typing.Annotated[
|
|
119
|
+
TargetedSwapScope | AllInProjectSwapScope,
|
|
120
|
+
serial_union_annotation(
|
|
121
|
+
named_type_path="sdk.api.output_parameters.swap_output_condition_parameters.SwapScope",
|
|
122
|
+
discriminator="type",
|
|
123
|
+
discriminator_map={
|
|
124
|
+
"targeted": TargetedSwapScope,
|
|
125
|
+
"all_in_project": AllInProjectSwapScope,
|
|
126
|
+
},
|
|
127
|
+
),
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
132
|
+
@serial_class(
|
|
133
|
+
named_type_path="sdk.api.output_parameters.swap_output_condition_parameters.Arguments",
|
|
134
|
+
)
|
|
135
|
+
@dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
|
|
136
|
+
class Arguments:
|
|
137
|
+
scope: SwapScope
|
|
138
|
+
output_key: identifier_t.IdentifierKey
|
|
139
|
+
new_condition: NewOutputCondition
|
|
140
|
+
old_condition_key: identifier_t.IdentifierKey | None = None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
144
|
+
@serial_class(
|
|
145
|
+
named_type_path="sdk.api.output_parameters.swap_output_condition_parameters.Data",
|
|
146
|
+
)
|
|
147
|
+
@dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
|
|
148
|
+
class Data(async_batch_t.AsyncBatchActionReturn):
|
|
149
|
+
pass
|
|
150
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
@@ -40,6 +40,7 @@ import uncountable.types.api.triggers.run_trigger as run_trigger_t
|
|
|
40
40
|
import uncountable.types.api.entity.set_barcode as set_barcode_t
|
|
41
41
|
import uncountable.types.api.entity.set_entity_field_values as set_entity_field_values_t
|
|
42
42
|
import uncountable.types.api.recipes.set_recipe_metadata as set_recipe_metadata_t
|
|
43
|
+
import uncountable.types.api.output_parameters.swap_output_condition_parameters as swap_output_condition_parameters_t
|
|
43
44
|
import uncountable.types.api.entity.transition_entity_phase as transition_entity_phase_t
|
|
44
45
|
import uncountable.types.api.entity.unlock_entity as unlock_entity_t
|
|
45
46
|
import uncountable.types.api.recipes.unlock_recipes as unlock_recipes_t
|
|
@@ -69,7 +70,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
69
70
|
depends_on: list[str] | None = None,
|
|
70
71
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
71
72
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
72
|
-
"""Create or return
|
|
73
|
+
"""Create or return an input association for a piece of equipment within specified material families. The equipment is resolved via identifier key. Supports both direct invocation and async batch execution. Returns modification_made and result_id of the association.
|
|
73
74
|
|
|
74
75
|
:param equipment_key: Identifier of the equipment to associate
|
|
75
76
|
:param material_family_ids: The list of material families to add the input to. This must be non-empty
|
|
@@ -106,7 +107,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
106
107
|
depends_on: list[str] | None = None,
|
|
107
108
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
108
109
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
109
|
-
"""
|
|
110
|
+
"""Associates a recipe as an input (ingredient). When no existing input is specified, creates a new input or returns the existing association if one already exists. When an existing input is specified, associates it with the recipe; fails if that input is already associated with a different recipe. Use `disassociate_recipe_as_input` to remove an existing association first. Returns the input ID and whether a modification was made.
|
|
110
111
|
|
|
111
112
|
:param recipe_key: Identifier for the recipe
|
|
112
113
|
:param input_key: Identifier for an input to use for the association. Optionally supplied. If not supplied, one is created
|
|
@@ -144,7 +145,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
144
145
|
depends_on: list[str] | None = None,
|
|
145
146
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
146
147
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
147
|
-
"""
|
|
148
|
+
"""Creates a lot association between a recipe and an ingredient. The recipe becomes a lot for the specified ingredient. Returns whether a modification was made.
|
|
148
149
|
|
|
149
150
|
:param recipe_key: Identifier for the recipe
|
|
150
151
|
:param ingredient_key: Identifier for the ingredient
|
|
@@ -179,7 +180,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
179
180
|
depends_on: list[str] | None = None,
|
|
180
181
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
181
182
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
182
|
-
"""Clears all output values
|
|
183
|
+
"""Clears all output values and output metadata for a given recipe identified by key. Returns whether a modification was made.
|
|
183
184
|
|
|
184
185
|
:param recipe_key: The identifier of the recipe
|
|
185
186
|
:param depends_on: A list of batch reference keys to process before processing this request
|
|
@@ -215,7 +216,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
215
216
|
depends_on: list[str] | None = None,
|
|
216
217
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
217
218
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
218
|
-
"""
|
|
219
|
+
"""Completes the asynchronous parsing step of a file upload workflow. Accepts either inline parsed_file_data or a file_id referencing a JSON file containing the parsed data (exactly one must be provided). Resolves the async job by its identifier key and applies the parsed data to the upload destination. Updates the async job status to COMPLETED upon success. Supports async batch execution.
|
|
219
220
|
|
|
220
221
|
:param parsed_file_data: Inline parsed data. Provide either parsed_file_data or file_id, not both.
|
|
221
222
|
:param file_id: File ID containing parsed data. Provide either file_id or parsed_file_data, not both.
|
|
@@ -253,7 +254,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
253
254
|
depends_on: list[str] | None = None,
|
|
254
255
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
255
256
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
256
|
-
"""
|
|
257
|
+
"""Completes an asynchronous runsheet export by processing an uploaded file and placing it in the job owner's export downloads folder. Sends an export-complete notification to the job owner on completion. Supports async batch execution.
|
|
257
258
|
|
|
258
259
|
:param depends_on: A list of batch reference keys to process before processing this request
|
|
259
260
|
"""
|
|
@@ -287,7 +288,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
287
288
|
depends_on: list[str] | None = None,
|
|
288
289
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
289
290
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
290
|
-
"""Creates mix order on a recipe workflow step
|
|
291
|
+
"""Creates a mix order on a recipe workflow step. The recipe and workflow step are identified by their respective keys. Returns whether a modification was made.
|
|
291
292
|
|
|
292
293
|
:param depends_on: A list of batch reference keys to process before processing this request
|
|
293
294
|
"""
|
|
@@ -369,7 +370,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
369
370
|
depends_on: list[str] | None = None,
|
|
370
371
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
371
372
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
372
|
-
"""
|
|
373
|
+
"""Creates a single recipe in the specified material family and workflow. If a matching recipe already exists, no new recipe is created and the existing recipe ID is returned. Returns the recipe ID.
|
|
373
374
|
|
|
374
375
|
:param name: The name for the recipe
|
|
375
376
|
:param material_family_id: The material family for the recipe
|
|
@@ -420,7 +421,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
420
421
|
depends_on: list[str] | None = None,
|
|
421
422
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
422
423
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
423
|
-
"""
|
|
424
|
+
"""Applies a list of edits to inputs on a specific recipe workflow step. Supported edit types include clearing all inputs, upserting or adding inputs, updating annotations, setting lots, setting propagated percentages, changing basis, adding instructions, setting roles, and managing placeholders.
|
|
424
425
|
|
|
425
426
|
:param recipe_key: Identifier for the recipe
|
|
426
427
|
:param depends_on: A list of batch reference keys to process before processing this request
|
|
@@ -500,7 +501,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
500
501
|
depends_on: list[str] | None = None,
|
|
501
502
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
502
503
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
503
|
-
"""
|
|
504
|
+
"""Processes one or more uploaded files through a configured uploader, parsing and writing the extracted data to a recipe destination. Resolves the uploader by its identifier key and requires READ_ALL permission on the uploader entity. The file_ids parameter is preferred; file_id is deprecated. Non-SDK users should use file_upload/external_create_file_record to upload files first. SDK users can use the client.upload_files() helper method. Supports async batch execution.
|
|
504
505
|
|
|
505
506
|
:param file_id: DEPRECATED: use file_ids
|
|
506
507
|
:param depends_on: A list of batch reference keys to process before processing this request
|
|
@@ -578,7 +579,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
578
579
|
depends_on: list[str] | None = None,
|
|
579
580
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
580
581
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
581
|
-
"""
|
|
582
|
+
"""Locks experiments to prevent editing. Supports locking all data (inputs and measurements) or inputs only. Returns whether a modification was made.
|
|
582
583
|
|
|
583
584
|
:param type: The type of lock to set.
|
|
584
585
|
All = both inputs and measurements are locked.
|
|
@@ -661,7 +662,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
661
662
|
depends_on: list[str] | None = None,
|
|
662
663
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
663
664
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
664
|
-
"""
|
|
665
|
+
"""Send a notification to one or more users or user groups. Supports specifying a subject, message body, and optional entity link. Can optionally display as an in-app notice with custom configuration. Supports both direct invocation and async batch execution.
|
|
665
666
|
|
|
666
667
|
:param depends_on: A list of batch reference keys to process before processing this request
|
|
667
668
|
"""
|
|
@@ -700,7 +701,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
700
701
|
depends_on: list[str] | None = None,
|
|
701
702
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
702
703
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
703
|
-
"""
|
|
704
|
+
"""Executes a trigger script identified by ref name, optionally on a specific entity, and returns only after the trigger has fully completed. Requires admin user access; non-admin users receive a 403 error. Accepts either entity_identifier (preferred) or entity (deprecated) to specify the target entity, but not both. Returns whether any entities were modified by the trigger execution.
|
|
704
705
|
|
|
705
706
|
:param entity_identifier: Identifier of the entity to run the trigger on.
|
|
706
707
|
:param entity: [Deprecated: use entity_identifier] Entity to run the trigger on.
|
|
@@ -807,7 +808,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
807
808
|
depends_on: list[str] | None = None,
|
|
808
809
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
809
810
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
810
|
-
"""
|
|
811
|
+
"""Sets metadata values on a recipe identified by key. Accepts a list of metadata field-value pairs to populate on the recipe. Fails if any of the metadata fields are read-only.
|
|
811
812
|
|
|
812
813
|
:param recipe_key: Identifier for the recipe
|
|
813
814
|
:param recipe_metadata: Metadata values to populate the recipe with
|
|
@@ -835,6 +836,48 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
835
836
|
batch_reference=req.batch_reference,
|
|
836
837
|
)
|
|
837
838
|
|
|
839
|
+
def swap_output_condition_parameters(
|
|
840
|
+
self,
|
|
841
|
+
*,
|
|
842
|
+
scope: swap_output_condition_parameters_t.SwapScope,
|
|
843
|
+
output_key: identifier_t.IdentifierKey,
|
|
844
|
+
new_condition: swap_output_condition_parameters_t.NewOutputCondition,
|
|
845
|
+
old_condition_key: identifier_t.IdentifierKey | None = None,
|
|
846
|
+
depends_on: list[str] | None = None,
|
|
847
|
+
_request_options: client_config_t.RequestOptions | None = None,
|
|
848
|
+
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
849
|
+
"""Swap output condition parameters on recipes. Either specify recipe keys for targeted swaps or set swap_all to swap across all recipes in the project.
|
|
850
|
+
|
|
851
|
+
:param scope: Which recipes to apply the swap to. See SwapScope variants for options.
|
|
852
|
+
:param output_key: Identifier for the output on which to swap conditions.
|
|
853
|
+
:param old_condition_key: Identifier for the old condition to replace. If omitted, recipes without a condition on the output are targeted.
|
|
854
|
+
:param new_condition: The new output condition to swap to. See NewOutputCondition variants for options.
|
|
855
|
+
:param depends_on: A list of batch reference keys to process before processing this request
|
|
856
|
+
"""
|
|
857
|
+
args = swap_output_condition_parameters_t.Arguments(
|
|
858
|
+
scope=scope,
|
|
859
|
+
output_key=output_key,
|
|
860
|
+
old_condition_key=old_condition_key,
|
|
861
|
+
new_condition=new_condition,
|
|
862
|
+
)
|
|
863
|
+
json_data = serialize_for_api(args)
|
|
864
|
+
|
|
865
|
+
batch_reference = str(uuid.uuid4())
|
|
866
|
+
|
|
867
|
+
req = async_batch_t.AsyncBatchRequest(
|
|
868
|
+
path=async_batch_t.AsyncBatchRequestPath.SWAP_OUTPUT_CONDITION_PARAMETERS,
|
|
869
|
+
data=json_data,
|
|
870
|
+
depends_on=depends_on,
|
|
871
|
+
batch_reference=batch_reference,
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
self._enqueue(req)
|
|
875
|
+
|
|
876
|
+
return async_batch_t.QueuedAsyncBatchRequest(
|
|
877
|
+
path=req.path,
|
|
878
|
+
batch_reference=req.batch_reference,
|
|
879
|
+
)
|
|
880
|
+
|
|
838
881
|
def transition_entity_phase(
|
|
839
882
|
self,
|
|
840
883
|
*,
|
|
@@ -918,7 +961,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
918
961
|
depends_on: list[str] | None = None,
|
|
919
962
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
920
963
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
921
|
-
"""
|
|
964
|
+
"""Unlocks previously locked experiments, making them editable again. Returns whether a modification was made.
|
|
922
965
|
|
|
923
966
|
:param type: The method to unlock recipes. Default is standard.
|
|
924
967
|
:param recipes: The recipes to unlock, a maximum of 100 can be sent
|
|
@@ -959,7 +1002,7 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
959
1002
|
depends_on: list[str] | None = None,
|
|
960
1003
|
_request_options: client_config_t.RequestOptions | None = None,
|
|
961
1004
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
962
|
-
"""Creates or updates condition match
|
|
1005
|
+
"""Creates or updates a condition match record. Condition matches are used to set goals on outputs. Each call creates a new condition match — there is no idempotent behavior, so the same inputs will produce a new condition match with each invocation. Returns modification_made and result_id of the created or updated condition match.
|
|
963
1006
|
|
|
964
1007
|
:param depends_on: A list of batch reference keys to process before processing this request
|
|
965
1008
|
"""
|
|
@@ -55,6 +55,7 @@ class AsyncBatchRequestPath(StrEnum):
|
|
|
55
55
|
RUN_TRIGGER = "triggers/run_trigger"
|
|
56
56
|
UPSERT_STEP_RELATIONSHIPS = "recipes/upsert_step_relationships"
|
|
57
57
|
UPSERT_RECIPE_WORKFLOW_STEP = "recipes/upsert_recipe_workflow_step"
|
|
58
|
+
SWAP_OUTPUT_CONDITION_PARAMETERS = "output_parameters/swap_output_condition_parameters"
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
# DO NOT MODIFY -- This file is generated by type_spec
|