API reference¶
Auto-generated from docstrings. The pipeline is the primary public API; the orchestration stages and evaluation metrics are documented for programmatic use.
Pipeline¶
trialmatchai.pipeline ¶
The single TrialMatchAI pipeline: an ordered registry of idempotent stages.
Every command is a slice of this one pipeline. Each stage wraps an
already-idempotent orchestration function (it internally skips finished work), so the
driver only decides which stages to run from the user's selection
(--only / --skip / --from / --to) and which to force (--force).
Because each stage is idempotent, a run from any starting state "just works": finished stages are cheap no-ops, unfinished ones run — the "one e2e workflow, maximally modular, never redo finished work" contract, where the e2e run is simply "run every stage".
STAGES
module-attribute
¶
STAGES = (
Stage(
"prepare",
_run_prepare,
"embed + entity-annotate the trial corpus",
),
Stage(
"concepts",
_run_concepts,
"build the entity-linking concept store",
),
Stage(
"link",
_run_link,
"link extracted entities to concept IDs (idempotent)",
),
Stage(
"index",
_run_index,
"build the LanceDB search tables",
),
Stage(
"ingest",
_run_ingest,
"import patient inputs into canonical profiles",
),
Stage(
"expand",
_run_expand,
"CoT query expansion of patient summaries",
),
Stage(
"match",
_run_match,
"retrieval + reranking + CoT eligibility + ranking",
),
Stage(
"eval",
_run_eval,
"score results against qrels (benchmark runs)",
),
)
StageContext
dataclass
¶
Everything the stages need, resolved once and threaded through the run.
Source code in src/trialmatchai/pipeline.py
Stage
dataclass
¶
select_stages ¶
Resolve the user's selection into an ordered list of stages to run.
Source code in src/trialmatchai/pipeline.py
run_pipeline ¶
Run the selected pipeline slice, freeing GPU models once at the end.
Source code in src/trialmatchai/pipeline.py
Orchestration stages¶
trialmatchai.orchestration ¶
Idempotent end-to-end orchestration for TrialMatchAI.
Chains the three pipeline stages (ingest patient inputs, build the search index,
run matching) and skips work already done: a patient is skipped once its profile
exists / once it has a non-empty ranked_trials.json, and the index is skipped once
the search tables exist. The e2e command and the TREC preset are thin wrappers
over these stages, so idempotency behaves identically everywhere.
ingest_inputs ¶
Import patient inputs (any supported format) into canonical profiles.
Skips a patient whose profile already exists unless force. Returns the
number of profiles available afterwards.
Source code in src/trialmatchai/orchestration.py
expand_queries ¶
Enrich each patient's matching summary via the CoT query expander.
No-op unless query_expansion.enabled. Loads the model once, enriches
every summary, then frees it before the match stage loads its own model.
Idempotent: a summary already marked query_expanded is skipped.
Source code in src/trialmatchai/orchestration.py
build_index ¶
build_index(
config,
*,
processed_trials_folder="data/processed_trials",
processed_criteria_folder="data/processed_criteria",
nct_filter=None,
force=False,
)
Build the LanceDB search tables, optionally restricted to nct_filter.
Skips when both tables already exist AND the recorded embedder still matches the configured
one (unless force). If the embedder changed, the index is rebuilt and re-embedded
automatically so retrieval matches the new embedder instead of silently falling back to BM25.
Source code in src/trialmatchai/orchestration.py
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 | |
run_matching ¶
Run the matching pipeline with per-patient resume.
Resume skips loading the model stack when every patient is done, and is invalidated when the search index changed since the matches were produced, so stale results aren't served.
Source code in src/trialmatchai/orchestration.py
prepare_corpus ¶
prepare_corpus(
config,
*,
trials_json_folder,
processed_trials_folder,
processed_criteria_folder,
force=False,
log_every=500,
)
Embed + annotate normalized trial JSONs into processed_*; resumable.
Streams one trial at a time (bounded memory), skips trials already prepared so an interrupted build picks up where it left off, and isolates per-trial failures so one bad document cannot abort the whole corpus.
Source code in src/trialmatchai/orchestration.py
build_system ¶
build_system(
config,
*,
trials_json_folder=None,
processed_trials_folder="data/processed_trials",
processed_criteria_folder="data/processed_criteria",
force_prepare=False,
force_reindex=False,
link_concepts=False,
)
Run the setup half (prepare -> link -> index), idempotent, with a manifest.
Each stage is resumable and recorded in .trialmatchai_build.json next to
the processed data, so a disrupted build can be re-run and continues from the
last completed work.
Source code in src/trialmatchai/orchestration.py
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 | |
build_state ¶
build_state(
config,
*,
processed_trials_folder="data/processed_trials",
processed_criteria_folder="data/processed_criteria",
)
Report what the build half has produced — used by build --status.
Source code in src/trialmatchai/orchestration.py
Registry updater¶
trialmatchai.registry.updater ¶
RegistryUpdater ¶
Source code in src/trialmatchai/registry/updater.py
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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 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 | |
RegistryUpdateConfig
dataclass
¶
Source code in src/trialmatchai/registry/updater.py
RegistryUpdateReport
dataclass
¶
Source code in src/trialmatchai/registry/updater.py
Evaluation metrics¶
trialmatchai.trec.metrics ¶
Ranking-quality metrics for TREC evaluation (complementing recall@k in qrels).
nDCG here is tie-aware (McSherry & Najork, 2008): tied scores each get the mean positional discount over the tie group's ranks — the expected nDCG over all tie orderings, invariant to arbitrary tie-breaking. It is also condensed (over labeled-and-retrieved trials only, decoupling ranking quality from recall). Gain is linear (gain = relevance grade), matching trec_eval's default.
ndcg_at_k ¶
Tie-aware nDCG@k. ordered_ids should be the condensed (labeled) list.
ideal_gains chooses the IDCG basis: None (default) uses the gains of
ordered_ids (judged-AND-ranked), making nDCG recall-independent; pass the FULL
judged pool's gains for recall-aware trec_eval-style nDCG, where an unranked
relevant trial stays in the ideal and lowers the score.
Source code in src/trialmatchai/trec/metrics.py
condensed_ndcg ¶
Tie-aware nDCG@k per cutoff. The DCG numerator is always condensed to judged trials
(those in grade_of), so unjudged trials never count. full_ideal selects the IDCG
basis: False (default) normalizes over judged-AND-ranked trials (recall-independent);
True normalizes over the FULL judged pool (recall-aware, trec_eval-style) so
unretrieved relevant trials lower the score.
Source code in src/trialmatchai/trec/metrics.py
precision_at_k ¶
Standard binary P@k over the final ranked list (hard cutoff k).
Source code in src/trialmatchai/trec/metrics.py
trialmatchai.trec.qrels ¶
Official TREC relevance judgments (qrels): download, parse, corpus, metrics.
The per-track NCT corpus pool is derived directly from the judged trials, and
recall@k is scored against the same qrels. Relevance grades: 0 = not relevant,
1 = excluded (condition matches but patient excluded), 2 = eligible. Default
threshold counts grade >= 1 as relevant; pass threshold=2 for eligible-only.
parse_qrels ¶
Parse a TREC qrels file (<topic> <iter> <nct_id> <rel>) into {query_id: {nct_id: rel}}.
The query id is f"{id_prefix}{topic}" to match the imported topic ids.
Source code in src/trialmatchai/trec/qrels.py
corpus_ncts ¶
The judged-trial pool across all queries (used to restrict the index).
evaluate ¶
Per-query and mean metrics over the patients in results_dir.
Reports recall@k (retrieval, first-level list) and tie-aware nDCG@{5,10,20} + P@10 (ranking, condensed to judged trials). P@10 is split into "relevant" (grade>=1) and "eligible" (grade==2).
Source code in src/trialmatchai/trec/qrels.py
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 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 | |
HTML report¶
trialmatchai.interop.exporters.html_report ¶
Self-contained HTML results report for a matched patient.
Joins ranked_trials.json, per-trial CoT eligibility evaluations, trial metadata, and the patient summary into one offline report.html; content is embedded as a JSON island and rendered client-side, so there is no server-side templating.
build_report_model ¶
build_report_model(
*,
patient_summary,
ranked,
eligibility_by_id,
meta_by_id,
cot_by_id=None,
generated_at,
run_info=None,
)
Pure join of a patient's result artifacts into a render-ready model (no I/O).
Trials keep ranked_trials.json order; rank is the 1-based position.
Source code in src/trialmatchai/interop/exporters/html_report.py
profile_to_model ¶
profile_to_model(
patient_dir,
*,
summary_dir=None,
trial_meta_folders=None,
generated_at=None,
run_info=None,
)
Read a patient's result dir into a render-ready model (no HTML).
Metadata folders are tried in order; ids with no metadata degrade to id + score + verdict only.
Source code in src/trialmatchai/interop/exporters/html_report.py
profile_to_html_report ¶
profile_to_html_report(
patient_dir,
*,
summary_dir=None,
trial_meta_folders=None,
generated_at=None,
run_info=None,
)
Read a patient's result dir and return a self-contained single-patient report.
Source code in src/trialmatchai/interop/exporters/html_report.py
render_unified_html ¶
One self-contained report over many patients, with a client-side per-patient drill-down.
Source code in src/trialmatchai/interop/exporters/html_report.py
render_html_report ¶
Embed the model as a tag-safe JSON island in the static template.
A single-patient model is wrapped as {"patients": [...]} so the template
always reads DATA.patients.