Releases: cbib/TrialMatchAI
Release list
v0.6.0 - config-driven model swapping
v0.6.0 — config-driven model swapping
Swap the embedder, search backend, reranker, or CoT model with a config edit — no code changes.
- Non-lossy config: undeclared knobs now reach their consumers (a new runtime knob needs no pydantic field); revived
rag/query_expansion.no_think. - Plugin registry:
build_search_backend/build_embedderfactories replace by-name construction; a new backend is one@registerline. - Self-describing embedders:
dim/native_metric/is_asymmetric/fingerprint(); the vector metric now follows the embedder; fixed the asymmetricembed_queriesinstruction-drop bug. - Index provenance:
_embedder.jsonsidecar + auto-reembed on an embedder swap (legacy indexes trusted, no rebuild). - Unified engine: the reranker builds through the shared vLLM loader (quantization / kv-cache / max-len parity, shared cache); scoring unchanged.
- Model catalog:
embedder: {model: "medcpt"}one-line swap.
+27 tests, full suite green, ruff clean. Existing configs and persisted indexes resolve identically (A/B validated: byte-identical retrieval and metrics). Full detail in commit 9ffb279.
v0.5.0
Added
- Asymmetric dual-encoder embedders (
embedder.query_model_name) — e.g. MedCPT article/query encoders; symmetric bge-m3 unchanged; same-dimension checked at load. - Configurable first-level vector similarity:
search_backend.vector_metric= cosine (default) | dot (ANN index + re-ranker). - Configurable hybrid blend:
search_backend.vector_weight(default 0.5) — score = (1-w)·text + w·vector. - First-level embedder benchmark harness (
scripts/benchmark_embedder.py+benchmarks/embedders/registry.json): isolated per-embedder index, recall@k for grade-2 and grade-1+2, non-destructive.
Fixed
- Cosine re-ranker keeps its single-pass path; dot buffers only for min-max normalization (all-equal → neutral 0.0).
vector_metric/vector_weightvalidated at backend construction;upsert_trialsrebuilds the index with the configured metric.
v0.4.1
New-system release: the eligibility scoring and ranking work validated across TREC CT 2021/2022 (now above the legacy system on its own metrics). Includes all of 0.4.0.
Added
trialmatchai trec --rerankre-ranks a finished run from cached chain-of-thought outputs and re-evaluates against qrels — no build, match, or model inference — so a ranking-logic change can be re-applied to existing results in seconds instead of re-running the GPU pipeline.
Changed
- Eligibility scoring credits "Unclear" criteria (neutral-Unclear). The band scored only Met/Not-Met and dropped Unclear (the dominant label), collapsing eligible and merely-relevant trials into one band; Unclear now counts at 0.5 (Met=1, Not Met=0). Irrelevant is still excluded; a Violated exclusion still hard-disqualifies.
From 0.4.0 (never released to PyPI)
- Per-condition first-level channels for comorbidities; RRF shortlist fusion (
search.shortlist_fusion); condensed precision aligned with nDCG; recall-awarendcg_full@kreported alongsidendcg@k.
v0.4.0
Retrieval and evaluation release.
Added
- Per-condition first-level query channels for comorbidities. Each distinct
other_conditionsterm now gets its own focused first-level retrieval channel (weight
0.25, below the primary condition) instead of only diffusing into the shared narrative
channel. A single blended channel over many distinct conditions dilutes to noise, which
starved retrieval on multi-morbid patients; per-condition channels restore their coverage
without displacing single-condition patients. The channels are de-duplicated against the
primary terms and bounded by a fixed cap. - Recall-aware nDCG reported alongside the condensed variant. TREC evaluation now emits
bothndcg@k— normalized by the ideal over judged-and-ranked trials (pure ordering
quality, independent of recall) — andndcg_full@k— normalized by the ideal over the
full judged pool (trec_eval-style), where a relevant trial that was never ranked
lowers the score. Reporting both keeps ordering quality and retrieval coverage legible
instead of conflating them in a single number. The DCG numerator is condensed (ignores
unjudged trials) in both.
Changed
- Second-level shortlist selection fuses rankings instead of summing raw scores.
The pre-CoT shortlist previously ranked trials byfirst_level_score + second_level_score.
Those two scores live on different scales (the first-level value is a reciprocal-rank
fusion contribution; the second-level value is an aggregated reranker score), so the
larger-scaled signal dominated the sum and a trial the retriever ranked highly could be
dropped whenever the reranker scored its criteria low — or omitted entirely when no
criterion cleared the aggregation threshold. Shortlist selection now combines the
first-level and second-level rankings with reciprocal-rank fusion over the union of
both, so a strong retrieval hit keeps a floor and rank fusion is scale-free. Behaviour is
governed bysearch.shortlist_fusion(rrf, the new default, orscore_sumfor the
previous behaviour),search.shortlist_rrf_k, and per-source weights
search.shortlist_first_level_weight/search.shortlist_second_level_weight. The
shortlist size is unchanged — it is still keyed to the number of reranked trials — so only
which trials are selected changes, not how many.
Fixed
- TREC precision was computed on the raw ranked list while its docstring — and nDCG —
used the condensed (judged-only) list.evaluatenow condenses the ranking to the
judged pool before the P@k cutoff, so an unjudged trial the assessors never saw is no
longer scored as a miss. This aligns precision withcondensed_ndcgand with the
function's own documented behaviour; the prior inconsistency understated P@k for a
non-pooled system and made it incomparable to nDCG. Judged-but-non-relevant (grade 0)
trials still count as negatives — only genuinely unjudged pairs are ignored. A regression
test pins the condensing.
v0.3.3 — settings config-strip fix + vLLM fp8 KV cache
Fixed
- Config knobs silently dropped by settings validation.
VllmSettings/LLMRerankerSettingsonly keep declared fields, soLLM_reranker.tensor_parallel_size(added in 0.3.2) never survivedload_configand the reranker stayed single-GPU. Now declared and reaches the engine; a regression test pins the round-trip.
Added
- fp8 KV cache + concurrency cap for vLLM.
vllm.kv_cache_dtype(fp8/fp8_e4m3/fp8_e5m2) halves KV-cache memory so a large window (e.g. 8192) fits on a single 48 GB card, andvllm.max_num_seqscaps concurrency. Validated: phi-4 loads at 8192 on one 48 GB L40 with ~44k tokens of KV.
See CHANGELOG.md.
v0.3.2 — healthcheck cold-start fix + multi-GPU reranker
Fixed
- Healthcheck no longer aborts a cold-start build. The 0.3.1 hardening made
health()flag a never-built (table-less) search index as unhealthy by default, so a pre-build preflight (trialmatchai healthcheck, run underset -ebefore the index exists) aborted a from-scratch run. The never-built check is now gated onrequire_tables=True: the lenient default preflight passes pre-build, while match-readiness (which already passesrequire_tables=True) still catches an unbuilt index.
Added
- Multi-GPU reranker.
LLM_reranker.tensor_parallel_sizelets the vLLM reranker shard across GPUs, matching the CoT engine, so both engines can run tensor-parallel on a multi-GPU node (e.g. 2× L40) instead of contending for a single device.
See CHANGELOG.md.
v0.3.1 — deep robustness audit
A deep, line-by-line robustness audit. 82 verified defects were fixed across retrieval, constraint evaluation, entity linking, FHIR/OMOP ingest, TREC metrics, and the build/resume pipeline — each pinned by a regression test (the suite grew to 377). No public API changes; behaviour is more correct, not different in shape.
Highlights
- Constraints — thousands-separator lab thresholds (
10,000) no longer truncate to a wrong magnitude; a relative's disease infamily_historyno longer satisfies a patient-disease inclusion; bare gene names no longer emit spurious "present" biomarker constraints; cross-unit numeric comparisons abstain instead of mis-deciding; inclusion aggregation uses worst-case (mirroring exclusion); whole-word matching replaces raw substring containment. - Retrieval & search — criteria de-duplicated before trial aggregation (no inflation by query overlap); short query tokens no longer match inside unrelated words; scan fallback pushes its limit into LanceDB instead of loading the whole table; a never-built index is reported unhealthy.
- Entity linking — length-ratio substring scoring (no more "carcinoma" → "hepatocellular carcinoma"); the annotation path wires the lexical reranker; OBO files keep their last term; empty mentions degrade to FTS-only.
- Query expansion — CoT output is stripped of
<think>blocks before JSON extraction, so a reasoning preamble can't poison the primary retrieval query. - Patient ingest — OMOP
NOTE_NLPfacts no longer silently dropped; NaN dates / float negations /concept_id 0handled; ISO-8601 day/week ages preserved; FHIRvalueRangekeeps negative signs,valueRationo longer emits"None", genomic resources with non-CodeableConcepttypes no longer dropped. - Ranking & TREC — the pure second-level reranker score (not first+second combined) reaches
rank_trials; a score blend breaks coarse eligibility ties within-band; month/year-precision trial dates parsed deterministically. - Build/resume integrity — atomic temp-then-rename writes and completion sentinels; the index fingerprint no longer chains off a stale link fingerprint; per-trial resume re-embeds changed trials; the concept-store fingerprint includes the embedder identity; atomic-write temp files no longer match
*.jsonglobs. - Config & CLI — falsy-but-valid config values respected;
trialmatchai rungoes through corpus-fingerprint invalidation; failure accounting/exit codes no longer mask an all-failed resume run as success. - Verbose comments/docstrings tightened to intent-only;
__version__realigned with the packaged version.
See CHANGELOG.md for the full list.
v0.3.0
What's Changed
- fix(P0): four shipping bugs from the test/codebase audit by @majdabd in #20
- test(P1): prove crash-safe resume + atomic-write invariants by @majdabd in #21
- test(P2): cover production paths + fix RRF dedup key by @majdabd in #22
- fix: four medium-severity audit bugs (nDCG, OMOP zero, topic sex, age range) by @majdabd in #23
- harden(P3): variant precision guard, embedder validators, metrics/interop tests by @majdabd in #24
- trec: deepen analysis funnel to 1000/500/250 (configurable second->CoT thinning) by @majdabd in #25
- feat(report): self-contained HTML match report (MVP) by @majdabd in #26
- release: 0.3.0 by @majdabd in #27
- docs: document the HTML match report (report subcommand + API) by @majdabd in #28
Full Changelog: v0.2.0...v0.3.0
TrialMatchAI v0.2.0
The deployment-readiness release: TrialMatchAI becomes one installable, idempotent, end-to-end pipeline behind a single CLI — with crash-safe resume, faithful TREC evaluation, a docs site, and PyPI packaging.
Highlights
- Unified pipeline
trialmatchai pipeline(prepare → concepts → index → ingest → expand → match → eval) — idempotent + resumable, with--only/--skip/--from/--to/--force(stage flags double as ablation knobs). - Single
trialmatchaiconsole script (12 subcommands). - LanceDB hybrid search + vLLM CoT eligibility reasoning + GLiNER2 NER + an entity-linking concept store from open vocabularies.
- Tie-aware nDCG (McSherry–Najork) +
P@10, with a TREC reproduction regression guard. - PyPI packaging + Trusted-Publishing release workflow; MkDocs docs site at https://cbib.github.io/TrialMatchAI/.
pip install trialmatchaiFull notes in CHANGELOG.md.