echoact 0.1.0__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.
- echoact/__init__.py +3 -0
- echoact/__main__.py +117 -0
- echoact/app.py +315 -0
- echoact/audio/__init__.py +0 -0
- echoact/audio/devices.py +192 -0
- echoact/audio/player.py +611 -0
- echoact/audio/wav.py +854 -0
- echoact/config/__init__.py +0 -0
- echoact/config/budget.py +370 -0
- echoact/config/settings.py +1244 -0
- echoact/db/__init__.py +0 -0
- echoact/db/backup.py +2429 -0
- echoact/db/migrations.py +434 -0
- echoact/db/schema.sql +214 -0
- echoact/db/store.py +2062 -0
- echoact/diagnostics.py +902 -0
- echoact/domain.py +487 -0
- echoact/engine/__init__.py +0 -0
- echoact/engine/container.py +843 -0
- echoact/engine/protocol.py +241 -0
- echoact/engine/runtime.py +324 -0
- echoact/engine/supervisor.py +961 -0
- echoact/engine/worker.py +659 -0
- echoact/errors.py +281 -0
- echoact/instance.py +172 -0
- echoact/jobs/__init__.py +0 -0
- echoact/jobs/engine.py +776 -0
- echoact/jobs/request.py +300 -0
- echoact/mcp/__init__.py +0 -0
- echoact/mcp/__main__.py +50 -0
- echoact/mcp/client.py +202 -0
- echoact/mcp/config.py +112 -0
- echoact/mcp/server.py +340 -0
- echoact/models/__init__.py +0 -0
- echoact/models/catalog.py +273 -0
- echoact/models/manifest.py +278 -0
- echoact/models/registry.py +1551 -0
- echoact/paths.py +93 -0
- echoact/policy.py +189 -0
- echoact/security/__init__.py +0 -0
- echoact/security/credentials.py +930 -0
- echoact/security/ratelimit.py +534 -0
- echoact/service/__init__.py +20 -0
- echoact/service/app.py +182 -0
- echoact/service/deps.py +563 -0
- echoact/service/errors.py +241 -0
- echoact/service/routes.py +1125 -0
- echoact/service/schemas.py +509 -0
- echoact/service/server.py +270 -0
- echoact/text/__init__.py +0 -0
- echoact/text/language.py +44 -0
- echoact/text/loader.py +577 -0
- echoact/text/normalize.py +924 -0
- echoact/text/segment.py +499 -0
- echoact/text/sniff.py +1202 -0
- echoact/ui/__init__.py +0 -0
- echoact/ui/bridge.py +50 -0
- echoact/ui/controls.py +360 -0
- echoact/ui/credential_dialog.py +131 -0
- echoact/ui/fonts.py +94 -0
- echoact/ui/i18n.py +260 -0
- echoact/ui/icons.py +440 -0
- echoact/ui/library.py +1642 -0
- echoact/ui/licence.py +162 -0
- echoact/ui/main_window.py +1202 -0
- echoact/ui/mcp_setup.py +494 -0
- echoact/ui/models_view.py +1142 -0
- echoact/ui/notifications.py +202 -0
- echoact/ui/reading.py +494 -0
- echoact/ui/settings_view.py +2258 -0
- echoact/ui/status_view.py +1193 -0
- echoact/ui/theme.py +579 -0
- echoact/util/__init__.py +0 -0
- echoact/util/ids.py +62 -0
- echoact/util/logging.py +127 -0
- echoact-0.1.0.dist-info/METADATA +162 -0
- echoact-0.1.0.dist-info/RECORD +80 -0
- echoact-0.1.0.dist-info/WHEEL +4 -0
- echoact-0.1.0.dist-info/entry_points.txt +3 -0
- echoact-0.1.0.dist-info/licenses/LICENSE +21 -0
|
File without changes
|
echoact/config/budget.py
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"""Turning a resource *setting* into the ceiling a job actually runs under.
|
|
2
|
+
|
|
3
|
+
F-21 says the memory default is derived from the machine and that "the value
|
|
4
|
+
actually applied may be lower depending on currently available memory", so the
|
|
5
|
+
configured number and the enforced number are two different things. F-78 makes
|
|
6
|
+
that distinction visible to the user; this module is where it is computed, and
|
|
7
|
+
``Settings`` never stores the result -- a derived number written back into a
|
|
8
|
+
settings file would follow the user onto a machine it does not describe.
|
|
9
|
+
|
|
10
|
+
F-23 draws the other line: below a 2 GiB budget the model is not loaded at all.
|
|
11
|
+
That is a refusal to start, not a degraded run, so ``resolve_budget`` raises
|
|
12
|
+
rather than returning something unusable.
|
|
13
|
+
|
|
14
|
+
Sampling and the halt decision live here too, because they compare against the
|
|
15
|
+
same numbers. N-04 fixes the headroom and the roughly half-second cadence,
|
|
16
|
+
N-21 requires the generation job to be measured apart from the whole app --
|
|
17
|
+
which is why the sampler is pointed at the worker process's pid rather than at
|
|
18
|
+
this one -- and F-22 displays what it reports.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from collections.abc import Callable, Mapping
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from enum import StrEnum
|
|
26
|
+
from typing import Any, Final
|
|
27
|
+
|
|
28
|
+
import psutil
|
|
29
|
+
|
|
30
|
+
from ..domain import Budget
|
|
31
|
+
from ..errors import Code, EchoActError
|
|
32
|
+
from ..policy import (
|
|
33
|
+
CPU_PERCENT_MAX,
|
|
34
|
+
CPU_PERCENT_MIN,
|
|
35
|
+
HEADROOM_FRACTION,
|
|
36
|
+
HEADROOM_MIN_BYTES,
|
|
37
|
+
MEMORY_CEILING_BYTES,
|
|
38
|
+
MEMORY_CEILING_FRACTION_OF_TOTAL,
|
|
39
|
+
MEMORY_DEFAULT_FRACTION,
|
|
40
|
+
MEMORY_DEFAULT_MAX_BYTES,
|
|
41
|
+
MEMORY_DEFAULT_MIN_BYTES,
|
|
42
|
+
MEMORY_FLOOR_BYTES,
|
|
43
|
+
RESOURCE_SAMPLE_INTERVAL_S,
|
|
44
|
+
)
|
|
45
|
+
from ..util.ids import monotonic
|
|
46
|
+
from .settings import Settings
|
|
47
|
+
|
|
48
|
+
#: Upper bound on worker threads, whatever the CPU budget works out to.
|
|
49
|
+
#:
|
|
50
|
+
#: A.5 measured twenty threads running about twice as slowly as two on this
|
|
51
|
+
#: engine -- classic oversubscription -- and tuned the product at the two the
|
|
52
|
+
#: Section 8.2 baseline produces (8 logical CPUs at F-20's 20% default). The
|
|
53
|
+
#: crossover point between "helps" and "hurts" was not measured, so the cap is
|
|
54
|
+
#: set at double the known-good figure and five times below the known-bad one.
|
|
55
|
+
#: Without a cap, F-20's 70% maximum on a 32-CPU workstation would ask for 22
|
|
56
|
+
#: threads and land squarely in the slow region, so the cap is what keeps
|
|
57
|
+
#: F-87's "threads come from the budget" from meaning "threads get worse as the
|
|
58
|
+
#: budget gets bigger". It is a measured tuning constant, not a policy limit
|
|
59
|
+
#: from Section 4, which is why it lives beside the code that applies it.
|
|
60
|
+
MAX_INTRA_OP_THREADS: Final = 4
|
|
61
|
+
|
|
62
|
+
#: How soon a caller refused for want of memory could get a different answer:
|
|
63
|
+
#: one monitoring interval, since nothing else re-examines the machine. F-57
|
|
64
|
+
#: requires a retryable refusal to carry a hint, and INSUFFICIENT_RESOURCES is
|
|
65
|
+
#: retryable because freeing memory elsewhere genuinely fixes it.
|
|
66
|
+
RESOURCE_RETRY_AFTER_S: Final = RESOURCE_SAMPLE_INTERVAL_S
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def default_memory_bytes(total_ram_bytes: int) -> int:
|
|
70
|
+
"""F-21's out-of-the-box budget: about 25% of total RAM, held to 2-6 GiB."""
|
|
71
|
+
quarter = int(total_ram_bytes * MEMORY_DEFAULT_FRACTION)
|
|
72
|
+
return max(MEMORY_DEFAULT_MIN_BYTES, min(MEMORY_DEFAULT_MAX_BYTES, quarter))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def memory_ceiling_bytes(total_ram_bytes: int) -> int:
|
|
76
|
+
"""F-21's ceiling on what the owner may configure: at most 32 GiB, and
|
|
77
|
+
never more than half of total RAM."""
|
|
78
|
+
return int(min(MEMORY_CEILING_BYTES, total_ram_bytes * MEMORY_CEILING_FRACTION_OF_TOTAL))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def system_headroom_bytes(total_ram_bytes: int) -> int:
|
|
82
|
+
"""N-04: the greater of 10% of total RAM or 1 GiB, kept free for the host."""
|
|
83
|
+
return int(max(total_ram_bytes * HEADROOM_FRACTION, HEADROOM_MIN_BYTES))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def intra_op_threads(logical_cpus: int, cpu_percent: int) -> int:
|
|
87
|
+
"""F-87: the worker's thread count comes from F-20's budget, never from the
|
|
88
|
+
runtime's default.
|
|
89
|
+
|
|
90
|
+
Rounded half-up rather than with Python's banker's rounding, so a machine
|
|
91
|
+
that works out to exactly n.5 threads gets the larger count consistently
|
|
92
|
+
instead of alternating with the parity of n.
|
|
93
|
+
"""
|
|
94
|
+
share = logical_cpus * cpu_percent / 100.0
|
|
95
|
+
threads = int(share + 0.5)
|
|
96
|
+
return max(1, min(MAX_INTRA_OP_THREADS, threads))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def resolve_budget(
|
|
100
|
+
settings: Settings,
|
|
101
|
+
*,
|
|
102
|
+
total_ram_bytes: int,
|
|
103
|
+
available_ram_bytes: int,
|
|
104
|
+
logical_cpus: int,
|
|
105
|
+
) -> Budget:
|
|
106
|
+
"""The ceiling this job will actually run under (F-20, F-21, F-23, N-04).
|
|
107
|
+
|
|
108
|
+
``available_ram_bytes`` is what makes the answer a *current* one: the
|
|
109
|
+
applied memory ceiling is the configured value less whatever the host needs
|
|
110
|
+
to keep, so the same settings legitimately produce a smaller budget on a
|
|
111
|
+
busy machine. F-78 shows both numbers; nothing writes this one back.
|
|
112
|
+
|
|
113
|
+
Raises ``EchoActError(INSUFFICIENT_RESOURCES)`` when the result would fall
|
|
114
|
+
under F-23's 2 GiB floor. Returning a 1 GiB budget instead would let the
|
|
115
|
+
model load and then be killed by the halt check moments later, which F-23
|
|
116
|
+
rules out by saying the model is not loaded at all.
|
|
117
|
+
"""
|
|
118
|
+
if total_ram_bytes <= 0 or logical_cpus <= 0:
|
|
119
|
+
raise AssertionError("resolve_budget needs a real machine description")
|
|
120
|
+
|
|
121
|
+
available = max(0, min(available_ram_bytes, total_ram_bytes))
|
|
122
|
+
configured = (
|
|
123
|
+
default_memory_bytes(total_ram_bytes)
|
|
124
|
+
if settings.memory_bytes is None
|
|
125
|
+
else settings.memory_bytes
|
|
126
|
+
)
|
|
127
|
+
# F-20 and F-21 are safety limits, not just widget ranges: a Settings built
|
|
128
|
+
# in code, or restored from another machine's file, is clamped here too.
|
|
129
|
+
configured = min(configured, memory_ceiling_bytes(total_ram_bytes))
|
|
130
|
+
cpu_percent = max(CPU_PERCENT_MIN, min(CPU_PERCENT_MAX, settings.cpu_percent))
|
|
131
|
+
|
|
132
|
+
headroom = system_headroom_bytes(total_ram_bytes)
|
|
133
|
+
applied = min(configured, available - headroom)
|
|
134
|
+
|
|
135
|
+
if applied < MEMORY_FLOOR_BYTES:
|
|
136
|
+
raise EchoActError(
|
|
137
|
+
Code.INSUFFICIENT_RESOURCES,
|
|
138
|
+
detail={
|
|
139
|
+
"required_bytes": MEMORY_FLOOR_BYTES,
|
|
140
|
+
"configured_bytes": configured,
|
|
141
|
+
"available_bytes": available,
|
|
142
|
+
"headroom_bytes": headroom,
|
|
143
|
+
"grantable_bytes": max(0, applied),
|
|
144
|
+
},
|
|
145
|
+
retry_after_s=RESOURCE_RETRY_AFTER_S,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return Budget(
|
|
149
|
+
cpu_percent=cpu_percent,
|
|
150
|
+
memory_bytes=int(applied),
|
|
151
|
+
intra_op_threads=intra_op_threads(logical_cpus, cpu_percent),
|
|
152
|
+
inter_op_threads=1,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def resolve_budget_from_system(settings: Settings) -> Budget:
|
|
157
|
+
"""``resolve_budget`` against this machine, read once so the three numbers
|
|
158
|
+
describe the same instant."""
|
|
159
|
+
vm = psutil.virtual_memory()
|
|
160
|
+
return resolve_budget(
|
|
161
|
+
settings,
|
|
162
|
+
total_ram_bytes=int(vm.total),
|
|
163
|
+
available_ram_bytes=int(vm.available),
|
|
164
|
+
logical_cpus=psutil.cpu_count(logical=True) or 1,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# ======================================================================
|
|
169
|
+
# Measurement (N-04, N-21, F-22)
|
|
170
|
+
# ======================================================================
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@dataclass(frozen=True, slots=True)
|
|
174
|
+
class ResourceSample:
|
|
175
|
+
"""One reading of the generation job and of the host, taken together.
|
|
176
|
+
|
|
177
|
+
Both halves come from the same moment on purpose: F-23 halts on either the
|
|
178
|
+
job's own usage or the system's free memory, and comparing an RSS from now
|
|
179
|
+
against a free-memory figure from several seconds ago would halt jobs for
|
|
180
|
+
conditions that had already passed.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
#: Worker resident set size. N-03 warns that this and the number the
|
|
184
|
+
#: operating system's limit acts on may differ; this is the displayed one.
|
|
185
|
+
rss_bytes: int
|
|
186
|
+
#: Share of *total logical CPU capacity*, per Section 4.1's display rule --
|
|
187
|
+
#: not psutil's per-core percentage, which exceeds 100 on a busy machine.
|
|
188
|
+
cpu_percent: float
|
|
189
|
+
system_total_bytes: int
|
|
190
|
+
system_available_bytes: int
|
|
191
|
+
at: float
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class ResourceSampler:
|
|
195
|
+
"""Reads the worker process's RSS and CPU at N-04's half-second cadence.
|
|
196
|
+
|
|
197
|
+
Points at the worker's pid, not at this process: N-21 requires the
|
|
198
|
+
generation job to be measured apart from total app usage, and the GUI, the
|
|
199
|
+
database, and the REST server all live in the parent.
|
|
200
|
+
|
|
201
|
+
It owns no thread and never sleeps. The job engine already runs a loop
|
|
202
|
+
that must stay responsive to cancellation within N-22's five seconds, so
|
|
203
|
+
the cadence is expressed as ``due()`` for that loop to consult; a private
|
|
204
|
+
timer thread here would sample a worker the engine had already killed.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
def __init__(
|
|
208
|
+
self,
|
|
209
|
+
pid: int,
|
|
210
|
+
*,
|
|
211
|
+
interval_s: float = RESOURCE_SAMPLE_INTERVAL_S,
|
|
212
|
+
logical_cpus: int | None = None,
|
|
213
|
+
process: Any | None = None,
|
|
214
|
+
memory_probe: Callable[[], tuple[int, int]] | None = None,
|
|
215
|
+
clock: Callable[[], float] = monotonic,
|
|
216
|
+
) -> None:
|
|
217
|
+
self.pid = pid
|
|
218
|
+
self.interval_s = interval_s
|
|
219
|
+
self._clock = clock
|
|
220
|
+
self._logical_cpus = max(1, logical_cpus or psutil.cpu_count(logical=True) or 1)
|
|
221
|
+
self._memory_probe = memory_probe or _system_memory
|
|
222
|
+
self._last_at: float | None = None
|
|
223
|
+
self._alive = True
|
|
224
|
+
if process is not None:
|
|
225
|
+
self._process = process
|
|
226
|
+
else:
|
|
227
|
+
try:
|
|
228
|
+
self._process = psutil.Process(pid)
|
|
229
|
+
except psutil.Error:
|
|
230
|
+
self._process = None
|
|
231
|
+
self._alive = False
|
|
232
|
+
else:
|
|
233
|
+
# Establishes psutil's baseline; the first real reading would
|
|
234
|
+
# otherwise report the process's whole lifetime average.
|
|
235
|
+
try:
|
|
236
|
+
self._process.cpu_percent(None)
|
|
237
|
+
except psutil.Error:
|
|
238
|
+
self._alive = False
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def alive(self) -> bool:
|
|
242
|
+
"""False once the worker has gone. A killed worker is the normal end
|
|
243
|
+
of a cancelled job (N-22), so it is a state, not an error."""
|
|
244
|
+
return self._alive
|
|
245
|
+
|
|
246
|
+
def due(self, at: float | None = None) -> bool:
|
|
247
|
+
now = self._clock() if at is None else at
|
|
248
|
+
return self._last_at is None or (now - self._last_at) >= self.interval_s
|
|
249
|
+
|
|
250
|
+
def sample(self) -> ResourceSample | None:
|
|
251
|
+
"""Read now, whether or not ``due()``. ``None`` means the worker is
|
|
252
|
+
gone and there is nothing left to measure."""
|
|
253
|
+
if self._process is None:
|
|
254
|
+
self._alive = False
|
|
255
|
+
return None
|
|
256
|
+
try:
|
|
257
|
+
rss = int(self._process.memory_info().rss)
|
|
258
|
+
raw_cpu = float(self._process.cpu_percent(None))
|
|
259
|
+
except psutil.Error:
|
|
260
|
+
self._alive = False
|
|
261
|
+
return None
|
|
262
|
+
total, available = self._memory_probe()
|
|
263
|
+
self._last_at = self._clock()
|
|
264
|
+
return ResourceSample(
|
|
265
|
+
rss_bytes=rss,
|
|
266
|
+
cpu_percent=round(raw_cpu / self._logical_cpus, 2),
|
|
267
|
+
system_total_bytes=total,
|
|
268
|
+
system_available_bytes=available,
|
|
269
|
+
at=self._last_at,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def poll(self) -> ResourceSample | None:
|
|
273
|
+
"""Sample only if the interval has elapsed. ``None`` also means "not
|
|
274
|
+
yet", so check ``alive`` to tell the two apart."""
|
|
275
|
+
if not self.due():
|
|
276
|
+
return None
|
|
277
|
+
return self.sample()
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _system_memory() -> tuple[int, int]:
|
|
281
|
+
vm = psutil.virtual_memory()
|
|
282
|
+
return int(vm.total), int(vm.available)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
# ======================================================================
|
|
286
|
+
# F-23's halt decision
|
|
287
|
+
# ======================================================================
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class HaltReason(StrEnum):
|
|
291
|
+
"""Why a running job must stop. F-23 requires the reason to be reported,
|
|
292
|
+
and the two causes call for different advice: one is the app's own budget,
|
|
293
|
+
the other is the rest of the machine."""
|
|
294
|
+
|
|
295
|
+
NONE = "none"
|
|
296
|
+
BUDGET_EXCEEDED = "budget_exceeded"
|
|
297
|
+
SYSTEM_HEADROOM = "system_headroom"
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@dataclass(frozen=True, slots=True)
|
|
301
|
+
class HaltDecision:
|
|
302
|
+
should_halt: bool
|
|
303
|
+
reason: HaltReason = HaltReason.NONE
|
|
304
|
+
message: str = ""
|
|
305
|
+
detail: Mapping[str, Any] = field(default_factory=dict)
|
|
306
|
+
|
|
307
|
+
def to_error(self) -> EchoActError:
|
|
308
|
+
"""The failure to fail the job with. F-23 also releases the model;
|
|
309
|
+
that is the job engine's to do, because only it holds the worker."""
|
|
310
|
+
if not self.should_halt:
|
|
311
|
+
raise AssertionError("no halt was decided")
|
|
312
|
+
return EchoActError(
|
|
313
|
+
Code.OUT_OF_MEMORY,
|
|
314
|
+
self.message,
|
|
315
|
+
detail={"reason": self.reason.value, **dict(self.detail)},
|
|
316
|
+
retry_after_s=RESOURCE_RETRY_AFTER_S,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
_CONTINUE: Final = HaltDecision(should_halt=False)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def halt_decision(sample: ResourceSample, budget: Budget) -> HaltDecision:
|
|
324
|
+
"""F-23: halt when the job is over its limit, or the host is short.
|
|
325
|
+
|
|
326
|
+
Only memory decides this. Halting a job for exceeding its CPU share would
|
|
327
|
+
be wrong twice over: N-03 makes CPU the operating-system container's to
|
|
328
|
+
throttle rather than the app's to police, and N-04 already concedes that
|
|
329
|
+
momentary spikes between half-second samples are not prevented -- so a
|
|
330
|
+
single busy interval would kill a job the requirements say should merely
|
|
331
|
+
run more slowly. Memory is different: an RSS reading that is over budget
|
|
332
|
+
is over budget until something frees it.
|
|
333
|
+
"""
|
|
334
|
+
if sample.rss_bytes > budget.memory_bytes:
|
|
335
|
+
return HaltDecision(
|
|
336
|
+
should_halt=True,
|
|
337
|
+
reason=HaltReason.BUDGET_EXCEEDED,
|
|
338
|
+
message="The job was halted because it exceeded its memory budget.",
|
|
339
|
+
detail={"rss_bytes": sample.rss_bytes, "limit_bytes": budget.memory_bytes},
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
headroom = system_headroom_bytes(sample.system_total_bytes)
|
|
343
|
+
if sample.system_available_bytes < headroom:
|
|
344
|
+
return HaltDecision(
|
|
345
|
+
should_halt=True,
|
|
346
|
+
reason=HaltReason.SYSTEM_HEADROOM,
|
|
347
|
+
message="The job was halted because the computer ran short of free memory.",
|
|
348
|
+
detail={
|
|
349
|
+
"available_bytes": sample.system_available_bytes,
|
|
350
|
+
"headroom_bytes": headroom,
|
|
351
|
+
},
|
|
352
|
+
)
|
|
353
|
+
return _CONTINUE
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
__all__ = [
|
|
357
|
+
"MAX_INTRA_OP_THREADS",
|
|
358
|
+
"RESOURCE_RETRY_AFTER_S",
|
|
359
|
+
"HaltDecision",
|
|
360
|
+
"HaltReason",
|
|
361
|
+
"ResourceSample",
|
|
362
|
+
"ResourceSampler",
|
|
363
|
+
"default_memory_bytes",
|
|
364
|
+
"halt_decision",
|
|
365
|
+
"intra_op_threads",
|
|
366
|
+
"memory_ceiling_bytes",
|
|
367
|
+
"resolve_budget",
|
|
368
|
+
"resolve_budget_from_system",
|
|
369
|
+
"system_headroom_bytes",
|
|
370
|
+
]
|