Commit 7e58b84
authored
API-contract integration tests, and fold in the measured properties (#275)
* test: API-contract integration layer, and fold in the measured properties (#198, #209)
INTEGRATION TESTS (#198). The pyramid had unit tests that never touch HTTP and Playwright e2e
tests that assert on rendered pixels. Neither would notice a payload silently dropping a field,
renaming a key, or starting to return 200 with an error body — and the workbench, the MCP server
and the Claude skill all consume these payloads BY KEY, so a renamed field is a breaking change
nothing in the suite could see. tests/test_api_contract.py closes that gap with FastAPI's
TestClient: 11 tests over liveness, the head catalog, prediction, the search surfaces and
degradation, with no browser, no port and no fifty-second model load. Suite goes 43 -> 54.
It immediately earned its place by finding a real bug. FLAVORMANCER_NO_MODELS=1 skipped loading
but never set MODELS_READY, so the warming-gate middleware returned 503 to every request FOREVER.
A models-less install was not "degraded", it was dead — the exact opposite of what DATA-PIPELINE.md
promises, and something no existing test could have caught because none of them speak HTTP. It now
marks ready-with-no-models, so the structure-derived answers that need no heads at all (physchem,
the sour/salty rules, applicability) are served.
CI installs fastapi/httpx explicitly. Without them the file skips itself rather than failing,
which would mean CI silently stopping checking every endpoint contract.
MEASURED PROPERTIES (#209). The PUG-View crawl finished: 2,282 molecules gained a measured
boiling or melting point that PubChem's property table never carried.
Folding them in exposed a second NaN trap, and this one had silently discarded the ENTIRE crawl.
The merge guarded on `tgt.get(k) is None`, but a missing value read from parquet is NaN, not None
— so the guard never fired for any row that already existed in properties.parquet with an empty
cell, which is all of them. First rebuild after the crawl moved coverage by exactly zero. Guarding
with pd.isna() instead:
boiling point 27% -> 34% (2,478 -> 3,075 molecules)
melting point 27% -> 31% (2,437 -> 2,792)
Verified end to end rather than assumed: of the crawl's 1,354 boiling points, exactly 6 failed to
reach the table. The rest of the apparent shortfall is real and explainable — the crawler targeted
molecules missing EITHER property, so many returned a boiling point that was already present.
This is the same NaN-is-not-None family as the bug that once left ~500 molecules unnamed. Worth
naming as a pattern: any merge against a pandas-derived dict needs pd.isna(), never `is None`.
Signed-off-by: Austin L. <86896075+rvnminers-A-and-N@users.noreply.github.com>
* docs(perf): record why neither way of parallelising model load can ship yet (#225)
Both options are now measured rather than assumed, and the docstring says so, so nobody
re-attempts either one blind.
Threads were already known to be worse than serial (~34 s serial vs ~86 s across 14 threads):
joblib.load is dominated by GIL-bound Python unpickling, so threads only add contention.
Processes looked like a clean 3.1x win. On 40 aroma models: 11.49 s serial, 1.16 s to deserialise
across a pool while discarding in the worker, 3.74 s once the forests are actually returned to the
parent. So the transfer is real but small next to the unpickling it replaces — roughly 49 s -> 16 s
extrapolated to the full roster.
Then it deadlocked. _load_all_models() runs DURING MODULE IMPORT, and forking while the
interpreter holds the import lock leaves the children inheriting locked import machinery they can
never acquire. The parent and every worker hung indefinitely and had to be SIGKILLed.
So the blocker is not parallelism, it is WHEN loading happens. The pool becomes safe once loading
moves out of import time — a FastAPI startup hook, or an explicit warm() the server calls. That is
a larger change than it sounds: `import predict` having models loaded as a side effect is relied on
by the CLI, the batch scripts and the test suite. Tracked on #225 rather than rushed.
Serial stays, and the warming page already makes the ~50 s visible rather than mysterious.
Signed-off-by: Austin L. <86896075+rvnminers-A-and-N@users.noreply.github.com>
---------
Signed-off-by: Austin L. <86896075+rvnminers-A-and-N@users.noreply.github.com>1 parent 1eacf5a commit 7e58b84
4 files changed
Lines changed: 190 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
27 | 32 | | |
28 | 33 | | |
29 | 34 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 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 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
248 | 248 | | |
249 | 249 | | |
250 | 250 | | |
| 251 | + | |
251 | 252 | | |
252 | 253 | | |
253 | 254 | | |
| |||
257 | 258 | | |
258 | 259 | | |
259 | 260 | | |
260 | | - | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
261 | 266 | | |
262 | 267 | | |
263 | 268 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
266 | 266 | | |
267 | 267 | | |
268 | 268 | | |
269 | | - | |
270 | | - | |
271 | | - | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
272 | 285 | | |
273 | 286 | | |
274 | 287 | | |
| |||
323 | 336 | | |
324 | 337 | | |
325 | 338 | | |
326 | | - | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
327 | 347 | | |
328 | 348 | | |
329 | 349 | | |
| |||
0 commit comments