""" Search tab — entity-based discovery across all 13+ entity types. Dynamic forms show/hide filters based on selected entity. """ import gradio as gr from core import config, runner from core.formatter import format_result ENTITY_CHOICES = [ "all", "gene", "disease", "variant", "article", "trial", "drug", "pathway", "protein", "adverse-event", "pgx", "gwas", "phenotype", ] TRIAL_STATUS = [ "", "recruiting", "not yet recruiting", "active, not recruiting", "completed", "terminated", "suspended", "withdrawn", ] TRIAL_PHASES = ["", "1", "2", "3", "4"] VARIANT_SIGNIFICANCE = [ "", "pathogenic", "likely_pathogenic", "uncertain_significance", "likely_benign", "benign", "conflicting_interpretations", "risk_factor", ] VARIANT_CONSEQUENCE = [ "", "missense_variant", "nonsense_variant", "synonymous_variant", "frameshift_variant", "splice_acceptor_variant", "splice_donor_variant", "inframe_deletion", "inframe_insertion", "stop_lost", "start_lost", ] ARTICLE_SOURCE = ["", "all", "pubtator", "europepmc", "pubmed"] TRIAL_SOURCE = ["", "ctgov", "nci"] DISEASE_SOURCE = ["", "mondo"] SEARCH_EXAMPLES = { "all": ("--gene BRAF --disease melanoma", "Cross-entity overview for BRAF + melanoma"), "gene": ("-q BRAF", "Search genes matching BRAF"), "disease": ("-q melanoma", "Search diseases matching melanoma"), "variant": ("-g BRAF --hgvsp V600E", "Search BRAF V600E variants"), "article": ("-g BRAF -d melanoma --since 2024-01-01", "BRAF melanoma articles since 2024"), "trial": ("-c melanoma --status recruiting", "Recruiting melanoma trials"), "drug": ("-q pembrolizumab", "Search drug pembrolizumab"), "pathway": ('-q "MAPK signaling"', "Search MAPK signaling pathways"), "protein": ("-q kinase", "Search kinase proteins"), "adverse-event": ("--drug pembrolizumab --serious", "Serious adverse events for pembrolizumab"), "pgx": ("-g CYP2D6", "PGx data for CYP2D6"), "gwas": ('--trait "type 2 diabetes"', "GWAS for type 2 diabetes"), "phenotype": ('"HP:0001250 HP:0001263"', "Phenotype similarity search"), } def create_search_tab(session_keys): """Build the Search tab with dynamic entity forms.""" with gr.Tab("🔎 Search"): gr.Markdown( "## Entity Search\n" "Discovery across 13 biomedical entity types. Select an entity and fill in the relevant filters." ) with gr.Row(): entity = gr.Dropdown( choices=ENTITY_CHOICES, value="gene", label="Entity Type", scale=1, ) limit = gr.Number(label="Limit", value=10, minimum=1, maximum=100, scale=1) offset = gr.Number(label="Offset", value=0, minimum=0, scale=1) with gr.Row(): no_cache = gr.Checkbox(label="Bypass cache", value=False) # === All (cross-entity) filters === with gr.Group(visible=False) as all_group: gr.Markdown("### Cross-Entity Search Filters") with gr.Row(): all_gene = gr.Textbox(label="Gene", placeholder="e.g., BRAF") all_disease = gr.Textbox(label="Disease", placeholder="e.g., melanoma") all_keyword = gr.Textbox(label="Keyword", placeholder="e.g., immunotherapy resistance") with gr.Row(): all_since = gr.Textbox(label="Since (date)", placeholder="e.g., 2024-01-01") all_counts_only = gr.Checkbox(label="Counts only") all_debug_plan = gr.Checkbox(label="Debug plan") # === Gene filters === with gr.Group(visible=True) as gene_group: gr.Markdown("### Gene Search") gene_query = gr.Textbox(label="Query", placeholder="e.g., BRAF, TP53, EGFR") # === Disease filters === with gr.Group(visible=False) as disease_group: gr.Markdown("### Disease Search") with gr.Row(): disease_query = gr.Textbox(label="Query", placeholder="e.g., melanoma, Lynch syndrome") disease_source = gr.Dropdown(choices=DISEASE_SOURCE, value="", label="Source") # === Variant filters === with gr.Group(visible=False) as variant_group: gr.Markdown("### Variant Search") with gr.Row(): variant_gene = gr.Textbox(label="Gene (-g)", placeholder="e.g., BRAF") variant_hgvsp = gr.Textbox(label="HGVSp", placeholder="e.g., V600E") with gr.Row(): variant_sig = gr.Dropdown(choices=VARIANT_SIGNIFICANCE, value="", label="Significance") variant_consequence = gr.Dropdown(choices=VARIANT_CONSEQUENCE, value="", label="Consequence") # === Article filters === with gr.Group(visible=False) as article_group: gr.Markdown("### Article Search") with gr.Row(): article_gene = gr.Textbox(label="Gene (-g)", placeholder="e.g., BRAF") article_disease = gr.Textbox(label="Disease (-d)", placeholder="e.g., melanoma") with gr.Row(): article_since = gr.Textbox(label="Since", placeholder="e.g., 2024-01-01") article_source = gr.Dropdown(choices=ARTICLE_SOURCE, value="", label="Source") # === Trial filters === with gr.Group(visible=False) as trial_group: gr.Markdown("### Trial Search") with gr.Row(): trial_condition = gr.Textbox(label="Condition (-c)", placeholder="e.g., melanoma") trial_status = gr.Dropdown(choices=TRIAL_STATUS, value="", label="Status") trial_phase = gr.Dropdown(choices=TRIAL_PHASES, value="", label="Phase") with gr.Row(): trial_source = gr.Dropdown(choices=TRIAL_SOURCE, value="", label="Source") trial_lat = gr.Textbox(label="Latitude", placeholder="e.g., 42.3601") trial_lon = gr.Textbox(label="Longitude", placeholder="e.g., -71.0589") trial_distance = gr.Textbox(label="Distance (mi)", placeholder="e.g., 50") # === Drug filters === with gr.Group(visible=False) as drug_group: gr.Markdown("### Drug Search") with gr.Row(): drug_query = gr.Textbox(label="Query", placeholder="e.g., pembrolizumab, kinase inhibitor") drug_region = gr.Dropdown( choices=[ ("— Default (US + EU auto)", ""), ("🇪🇺 EU (European Medicines Agency)", "eu"), ], value="", label="Region", ) # === Pathway filters === with gr.Group(visible=False) as pathway_group: gr.Markdown("### Pathway Search") pathway_query = gr.Textbox(label="Query", placeholder='e.g., "MAPK signaling", "Pathways in cancer"') # === Protein filters === with gr.Group(visible=False) as protein_group: gr.Markdown("### Protein Search") with gr.Row(): protein_query = gr.Textbox(label="Query", placeholder="e.g., kinase") protein_all_species = gr.Checkbox(label="All species") # === Adverse Event filters === with gr.Group(visible=False) as ae_group: gr.Markdown("### Adverse Event Search") with gr.Row(): ae_drug = gr.Textbox(label="Drug", placeholder="e.g., pembrolizumab") ae_serious = gr.Checkbox(label="Serious only") with gr.Row(): ae_type = gr.Dropdown(choices=["", "device"], value="", label="Type") ae_manufacturer = gr.Textbox(label="Manufacturer", placeholder="e.g., Medtronic") ae_product_code = gr.Textbox(label="Product code", placeholder="e.g., PQP") # === PGx filters === with gr.Group(visible=False) as pgx_group: gr.Markdown("### PGx Search") with gr.Row(): pgx_gene = gr.Textbox(label="Gene (-g)", placeholder="e.g., CYP2D6") pgx_drug = gr.Textbox(label="Drug (-d)", placeholder="e.g., warfarin") # === GWAS filters === with gr.Group(visible=False) as gwas_group: gr.Markdown("### GWAS Search") with gr.Row(): gwas_gene = gr.Textbox(label="Gene (-g)", placeholder="e.g., TCF7L2") gwas_trait = gr.Textbox(label="Trait", placeholder='e.g., "type 2 diabetes"') # === Phenotype filters === with gr.Group(visible=False) as phenotype_group: gr.Markdown("### Phenotype Search (Monarch Semsim)") phenotype_terms = gr.Textbox(label="HPO Terms", placeholder="e.g., HP:0001250 HP:0001263") # Dynamic visibility entity_groups = { "all": all_group, "gene": gene_group, "disease": disease_group, "variant": variant_group, "article": article_group, "trial": trial_group, "drug": drug_group, "pathway": pathway_group, "protein": protein_group, "adverse-event": ae_group, "pgx": pgx_group, "gwas": gwas_group, "phenotype": phenotype_group, } def toggle_visibility(selected): return [gr.Group(visible=(k == selected)) for k in entity_groups] entity.change( fn=toggle_visibility, inputs=[entity], outputs=list(entity_groups.values()), ) # Example button example_display = gr.Markdown("") def show_example(ent): if ent in SEARCH_EXAMPLES: hint, desc = SEARCH_EXAMPLES[ent] return f"**Example:** `biomcp search {ent} {hint}` — {desc}" return "" entity.change(fn=show_example, inputs=[entity], outputs=[example_display]) # Run button run_btn = gr.Button("🔎 Search", variant="primary") output_md = gr.Markdown(label="Results") with gr.Accordion("Raw JSON", open=False): output_json = gr.Code(language="json") def run_search( ent, lim, off, skip_cache, keys, # all a_gene, a_disease, a_keyword, a_since, a_counts, a_debug, # gene g_query, # disease d_query, d_source, # variant v_gene, v_hgvsp, v_sig, v_cons, # article ar_gene, ar_disease, ar_since, ar_source, # trial t_cond, t_status, t_phase, t_source, t_lat, t_lon, t_dist, # drug dr_query, dr_region, # pathway pw_query, # protein pr_query, pr_all_species, # adverse event ae_d, ae_s, ae_t, ae_m, ae_pc, # pgx pgx_g, pgx_d, # gwas gw_g, gw_t, # phenotype ph_terms, ): args = ["search", ent] lim = int(lim) if lim else 10 off = int(off) if off else 0 if ent == "all": if a_gene: args.extend(["--gene", a_gene.strip()]) if a_disease: args.extend(["--disease", a_disease.strip()]) if a_keyword: args.extend(["--keyword", a_keyword.strip()]) if a_since: args.extend(["--since", a_since.strip()]) if a_counts: args.append("--counts-only") if a_debug: args.append("--debug-plan") elif ent == "gene": if g_query: args.extend(["-q", g_query.strip()]) elif ent == "disease": if d_query: args.extend(["-q", d_query.strip()]) if d_source: args.extend(["--source", d_source]) elif ent == "variant": if v_gene: args.extend(["-g", v_gene.strip()]) if v_hgvsp: args.extend(["--hgvsp", v_hgvsp.strip()]) if v_sig: args.extend(["--significance", v_sig]) if v_cons: args.extend(["--consequence", v_cons]) elif ent == "article": if ar_gene: args.extend(["-g", ar_gene.strip()]) if ar_disease: args.extend(["-d", ar_disease.strip()]) if ar_since: args.extend(["--since", ar_since.strip()]) if ar_source: args.extend(["--source", ar_source]) elif ent == "trial": if t_cond: args.extend(["-c", t_cond.strip()]) if t_status: args.extend(["--status", t_status]) if t_phase: args.extend(["--phase", t_phase]) if t_source: args.extend(["--source", t_source]) if t_lat: args.extend(["--lat", t_lat.strip()]) if t_lon: args.extend(["--lon", t_lon.strip()]) if t_dist: args.extend(["--distance", t_dist.strip()]) elif ent == "drug": if dr_query: args.extend(["-q", dr_query.strip()]) if dr_region: args.extend(["--region", dr_region]) elif ent == "pathway": if pw_query: args.extend(["-q", pw_query.strip()]) elif ent == "protein": if pr_query: args.extend(["-q", pr_query.strip()]) if pr_all_species: args.append("--all-species") elif ent == "adverse-event": if ae_d: args.extend(["--drug", ae_d.strip()]) if ae_s: args.append("--serious") if ae_t: args.extend(["--type", ae_t]) if ae_m: args.extend(["--manufacturer", ae_m.strip()]) if ae_pc: args.extend(["--product-code", ae_pc.strip()]) elif ent == "pgx": if pgx_g: args.extend(["-g", pgx_g.strip()]) if pgx_d: args.extend(["-d", pgx_d.strip()]) elif ent == "gwas": if gw_g: args.extend(["-g", gw_g.strip()]) if gw_t: args.extend(["--trait", gw_t.strip()]) elif ent == "phenotype": if ph_terms: args.append(ph_terms.strip()) # Add limit/offset args.extend(["--limit", str(lim)]) if off > 0: args.extend(["--offset", str(off)]) env = config.build_env_overrides(keys) result = runner.run(args, json_mode=True, no_cache=skip_cache, env_overrides=env) if not result["success"]: raise gr.Error(f"BioMCP error: {result['error']}") md, js = format_result(result) return md, js all_inputs = [ entity, limit, offset, no_cache, session_keys, # all all_gene, all_disease, all_keyword, all_since, all_counts_only, all_debug_plan, # gene gene_query, # disease disease_query, disease_source, # variant variant_gene, variant_hgvsp, variant_sig, variant_consequence, # article article_gene, article_disease, article_since, article_source, # trial trial_condition, trial_status, trial_phase, trial_source, trial_lat, trial_lon, trial_distance, # drug drug_query, drug_region, # pathway pathway_query, # protein protein_query, protein_all_species, # adverse-event ae_drug, ae_serious, ae_type, ae_manufacturer, ae_product_code, # pgx pgx_gene, pgx_drug, # gwas gwas_gene, gwas_trait, # phenotype phenotype_terms, ] run_btn.click( fn=run_search, inputs=all_inputs, outputs=[output_md, output_json], )