PineForge HPO 1.11.0
Native hyperparameter optimization for PineForge strategies
Loading...
Searching...
No Matches
request_inventory.py
Go to the documentation of this file.
1"""Pure selected-study admission of producer-bound request-feed dependencies.
2
3Only ``read_request_feed_inventory`` reads artifact files. Neither entry point
4loads a plugin, reads feeds, creates a strategy, nor enumerates trial products.
5The optional codegen inventory module supplies the wire-token canonicalizer.
6"""
7
8from __future__ import annotations
9
10import json
11import os
12from pathlib import Path
13import re
14from typing import Any, Callable, Literal, Mapping
15
16from .error import HpoError
17from .study_spec import ParameterSpec
18from .transpile import REQUEST_FEED_INVENTORY_SCHEMA
19
20# report-v2.md section 2 [SOURCE]: E/docker/selected_window_report.py:78-90.
21# These candidate implementation constants are checked again by the engine,
22# which remains authoritative. Lane 5 re-proves them against its support_policy().
23SUPPORT_POLICY = {
24 "primary_chart_timeframes": ("1", "3", "5", "15", "30", "60", "120", "240"),
25 "preroll": "supported",
26 "request_feed_timeframes": ("1", "D"),
27}
28
29RequestInventoryReason = Literal[
30 "missing",
31 "invalid",
32 "unbound",
33 "artifact_mismatch",
34 "chart_mismatch",
35 "unknown",
36 "input_unresolved",
37 "producer_unavailable",
38]
39RequestTimeframeCapability = Literal[
40 "selected_window_chart_timeframe",
41 "selected_window_request_feed_timeframe",
42]
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61class RequestInventoryError(HpoError, RuntimeError):
62 """An inventory refusal with closed arguments and a bounded diagnostic."""
63
64 def __init__(self, reason: RequestInventoryReason, message: str) -> None:
65 super().__init__(_diagnostic(message))
66 self.with_failure(
67 "hpo_walkforward_inventory_invalid",
68 {
69 "capability": "selected_window_request_feed_inventory",
70 "reason": reason,
71 },
72 )
73
74
75class RequestTimeframeError(HpoError, RuntimeError):
76 """A policy refusal; offending tokens belong in text, not catalog args."""
77
78 def __init__(self, capability: RequestTimeframeCapability, message: str) -> None:
79 super().__init__(_diagnostic(message))
80 self.with_failure(
81 "hpo_walkforward_timeframe_unsupported", {"capability": capability}
82 )
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
152 artifact_dir_or_manifest: str | os.PathLike[str] | Mapping[str, Any],
153) -> Mapping[str, Any]:
154 """Read the manifest block and compare an optional adjacent JSON sidecar.
155
156 Pass an artifact directory, a manifest path, or an already parsed manifest
157 (the last form performs no I/O). The block is required; a sidecar alone never
158 supplies it. When present, the sidecar must have identical parsed JSON bytes.
159 Duplicate keys at every JSON depth are invalid, as required by
160 E/docker/request_feed_inventory.py:160-221 (report-v2 section 2).
161
162 A missing block with ``request_identity.request_feed_inventory: null`` is
163 ``producer_unavailable``: the build explicitly lacked the producer. An older
164 identity without the key is ``missing``. A present null block is ``invalid``.
165 Ordinary artifact loading never calls this selected-only helper.
166 """
167
168 if isinstance(artifact_dir_or_manifest, Mapping):
169 return _manifest_inventory(artifact_dir_or_manifest, "manifest")
170 path = Path(artifact_dir_or_manifest)
171 manifest_path = path / "manifest.json" if path.is_dir() else path
172 inventory = _manifest_inventory(_read_json(manifest_path), str(manifest_path))
173 sidecar = manifest_path.parent / "request_feed_inventory.json"
174 if sidecar.exists():
175 companion = _read_json(sidecar)
176 if _json_bytes(companion) != _json_bytes(inventory):
178 "invalid",
179 f"{sidecar} disagrees with {manifest_path}.request_feed_inventory",
180 )
181 return inventory
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
281 inventory: Mapping[str, Any] | None,
282 *,
283 plugin_sha256: str,
284 generated_cpp_sha256: str,
285 chart_timeframe: str,
286 fixed_inputs: Mapping[str, Any],
287 input_domains: Mapping[str, ParameterSpec],
288) -> tuple[str, ...]:
289 """Return every reachable request token, sorted, unique and canonical.
290
291 Rules from report-v2 section 2 [SOURCE], E/docker/request_feed_inventory.py:
292 :190-221 requires exactly five schema keys and exact token/input/unknown
293 entry shapes, with no extras, missing or duplicate JSON keys (duplicates
294 must be rejected while parsing; ``read_request_feed_inventory`` does so).
295 :258-264 requires lowercase 64-hex digests and an artifact binding. HPO also
296 compares the recorded source digest against supplied generated C++ identity.
297 Null artifact binding is ``unbound``; either differing digest is
298 ``artifact_mismatch``. :239-280 accepts a null primary (chart-unbound),
299 otherwise requires its canonical token to match the supplied canonical chart.
300 An ``unknown`` entry always refuses; empty entries is complete and admitted.
301 Token entries and non-null ``primary_chart_timeframe`` must already equal
302 codegen's ``canonical_wire_timeframe`` result (:99-157). This is stricter than
303 the engine resolver, which accepts any spelling that canonicalizes, and
304 matches codegen's binder ``_is_wire_token`` (canonical == value). Codegen
305 never emits aliases here, so this rule only refuses hand-edited inventories.
306 Input values may use aliases, resolve from effective fixed values or declared
307 defaults, and invalid present values never fall back (:239-280).
308
309 Study admission additionally checks every choice of each referenced finite
310 ParameterSpec domain, independently, before trials. A swept input takes
311 precedence over any fixed value; no non-enumerable or non-string domain can
312 prove admission. No Cartesian products are constructed. :283-321 checks the
313 primary chart policy first, then resolved feed policy; the engine's own
314 admission remains authoritative. No artifact/feed I/O or library load occurs.
315
316 Callers must pass the verified SHA-256 of the plugin file that will be loaded
317 as ``plugin_sha256`` and of its generated C++ as ``generated_cpp_sha256``
318 (manifest values verified against the files by the artifact loader).
319 Admission performs no I/O and compares inventory hashes only with these
320 supplied values, so its identity check is only as good as those arguments.
321 """
322
323 try:
324 from pineforge_codegen.request_feed_inventory import canonical_wire_timeframe
325 except ImportError as error:
327 "producer_unavailable",
328 "cannot import pineforge_codegen.request_feed_inventory; "
329 "admission needs pineforge-codegen with request_feed_inventory",
330 ) from error
331
332 chart = canonical_wire_timeframe(chart_timeframe)
333 if chart not in SUPPORT_POLICY["primary_chart_timeframes"]:
335 "selected_window_chart_timeframe",
336 f"selected-window chart timeframe {chart_timeframe!r} "
337 f"(canonical {chart!r}) is unsupported",
338 )
339 if inventory is None:
341 "missing", "selected execution requires request_feed_inventory"
342 )
343 checked = _validate_shape(inventory, canonical_wire_timeframe)
344 if checked["artifact_sha256"] is None:
346 "unbound", "request_feed_inventory.artifact_sha256 is null"
347 )
348 for name, expected in (
349 ("source_sha256", generated_cpp_sha256),
350 ("artifact_sha256", plugin_sha256),
351 ):
352 if checked[name] != expected:
354 "artifact_mismatch",
355 f"request_feed_inventory.{name} {checked[name]!r} differs "
356 f"from the supplied artifact identity {expected!r}",
357 )
358 primary = checked["primary_chart_timeframe"]
359 if primary is not None and primary != chart:
361 "chart_mismatch",
362 f"request_feed_inventory.primary_chart_timeframe {primary!r} "
363 f"differs from chart timeframe {chart!r}",
364 )
365 tokens: set[str] = set()
366 for position, entry in enumerate(checked["entries"]):
367 if entry["kind"] == "unknown":
369 "unknown", f"request_feed_inventory.entries[{position}] is unknown"
370 )
371 if entry["kind"] == "token":
372 tokens.add(entry["timeframe"])
373 continue
374 for value in _input_values(entry, fixed_inputs, input_domains):
375 token = canonical_wire_timeframe(value)
376 if token is None:
378 "input_unresolved",
379 f"request_feed_inventory.entries[{position}] input "
380 f"{entry['key']!r} cannot resolve timeframe value {value!r}",
381 )
382 tokens.add(token)
383 unsupported = sorted(tokens - set(SUPPORT_POLICY["request_feed_timeframes"]))
384 if unsupported:
386 "selected_window_request_feed_timeframe",
387 f"selected-window request-feed timeframes {unsupported!r} are unsupported",
388 )
389 return tuple(sorted(tokens))
390
391
392
393
394
395
396
397
398
399
400
with_failure(self, str|None code, dict[str, Any]|None args, str origin="hpo")
Definition error.py:25
None __init__(self, RequestInventoryReason reason, str message)
None __init__(self, RequestTimeframeCapability capability, str message)
Mapping[str, Any] read_request_feed_inventory(str|os.PathLike[str]|Mapping[str, Any] artifact_dir_or_manifest)
tuple[str,...] admit_request_feed_inventory(Mapping[str, Any]|None inventory, *str plugin_sha256, str generated_cpp_sha256, str chart_timeframe, Mapping[str, Any] fixed_inputs, Mapping[str, ParameterSpec] input_domains)