Spaces:
Running
Running
| import requests | |
| from langchain_core.tools import tool | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 1. PubMed β primary literature search | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def search_pubmed(query: str) -> str: | |
| """Search PubMed for peer-reviewed papers. | |
| IMPORTANT: Use short 2-5 keyword queries only (e.g. 'MGMT GBM TMZ resistance', | |
| 'pseudoprogression GBM IDH-wildtype', 'cfDNA glioma liquid biopsy'). | |
| Do NOT use long sentences or phrases β PubMed requires every word to match. | |
| You can call this multiple times with different short queries.""" | |
| search_url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi" | |
| params = { | |
| "db": "pubmed", | |
| "term": query, # short keywords only | |
| "retmax": 5, | |
| "retmode": "json", | |
| "sort": "relevance", | |
| # removed "field": "tiab" β not a valid esearch param | |
| } | |
| r = requests.get(search_url, params=params, timeout=15) | |
| ids = r.json()["esearchresult"]["idlist"] | |
| if not ids: | |
| return f"No PubMed results found for '{query}'. Try a shorter query with 2-4 keywords." | |
| fetch_url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi" | |
| params = { | |
| "db": "pubmed", | |
| "id": ",".join(ids), | |
| "rettype": "abstract", | |
| "retmode": "text", | |
| } | |
| r = requests.get(fetch_url, params=params, timeout=15) | |
| return r.text[:3000] | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 2. ClinicalTrials.gov β recruiting/relevant trials | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def search_clinical_trials(query: str) -> str: | |
| """Search ClinicalTrials.gov for active or recruiting trials related to | |
| a condition, drug, gene status, or treatment combination. Use this to | |
| find trials a patient might be eligible for, or to see what treatment | |
| approaches are currently under investigation.""" | |
| url = "https://clinicaltrials.gov/api/v2/studies" | |
| params = { | |
| "query.term": query, | |
| "pageSize": 5, | |
| "fields": "NCTId,BriefTitle,OverallStatus,Condition,InterventionName,BriefSummary", | |
| } | |
| r = requests.get(url, params=params, timeout=15) | |
| data = r.json() | |
| studies = data.get("studies", []) | |
| if not studies: | |
| return f"No clinical trials found for '{query}'." | |
| out = [] | |
| for s in studies: | |
| proto = s.get("protocolSection", {}) | |
| ident = proto.get("identificationModule", {}) | |
| status = proto.get("statusModule", {}) | |
| out.append( | |
| f"NCT ID: {ident.get('nctId')}\n" | |
| f"Title: {ident.get('briefTitle')}\n" | |
| f"Status: {status.get('overallStatus')}\n" | |
| ) | |
| return "\n".join(out)[:3000] | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 3. bioRxiv β latest preprints | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def search_biorxiv(query: str) -> str: | |
| """Search bioRxiv for recent preprints on a biological mechanism or | |
| topic. Use this when you want the most recent, not-yet-peer-reviewed | |
| research β useful for cutting-edge mechanisms that may not yet be in | |
| PubMed. Note: bioRxiv's API is date-range based rather than full-text | |
| search, so this returns recent preprints that you can scan for | |
| relevance to the query.""" | |
| url = "https://api.biorxiv.org/details/biorxiv/2024-01-01/2025-12-31/0" | |
| r = requests.get(url, timeout=15) | |
| data = r.json() | |
| collection = data.get("collection", []) | |
| if not collection: | |
| return f"No bioRxiv results retrieved (query context: '{query}')." | |
| out = [] | |
| for paper in collection[:5]: | |
| out.append( | |
| f"Title: {paper.get('title')}\n" | |
| f"Date: {paper.get('date')}\n" | |
| f"Abstract: {paper.get('abstract', '')[:300]}\n" | |
| ) | |
| return "\n".join(out)[:3000] | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 4. OMIM β gene <-> disease associations | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def search_omim(gene_or_disease: str) -> str: | |
| """Search OMIM (Online Mendelian Inheritance in Man) for gene-disease | |
| associations. Use this when you need to understand the genetic basis | |
| or known disease associations of a specific gene (e.g. MGMT, EGFR, | |
| IDH1) or condition. Requires an OMIM API key (set OMIM_API_KEY).""" | |
| import os | |
| api_key = os.environ.get("OMIM_API_KEY", "") | |
| if not api_key: | |
| return ( | |
| "OMIM API key not configured. Set OMIM_API_KEY environment " | |
| "variable to enable this tool. Skipping OMIM search for " | |
| f"'{gene_or_disease}'." | |
| ) | |
| url = "https://api.omim.org/api/entry/search" | |
| params = { | |
| "search": gene_or_disease, | |
| "limit": 5, | |
| "format": "json", | |
| "apiKey": api_key, | |
| } | |
| r = requests.get(url, params=params, timeout=15) | |
| data = r.json() | |
| entries = data.get("omim", {}).get("searchResponse", {}).get("entryList", []) | |
| if not entries: | |
| return f"No OMIM results found for '{gene_or_disease}'." | |
| out = [] | |
| for e in entries: | |
| entry = e.get("entry", {}) | |
| out.append( | |
| f"MIM Number: {entry.get('mimNumber')}\n" | |
| f"Title: {entry.get('titles', {}).get('preferredTitle')}\n" | |
| ) | |
| return "\n".join(out)[:3000] | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 5. DrugBank β drug mechanisms / interactions | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def search_drugbank(drug_name: str) -> str: | |
| """Look up a drug's mechanism of action, target, and known interactions | |
| via DrugBank. Use this when the hypothesis involves a specific drug | |
| (e.g. Temozolomide) and you need its pharmacological mechanism. | |
| Requires a DrugBank API key (set DRUGBANK_API_KEY).""" | |
| import os | |
| api_key = os.environ.get("DRUGBANK_API_KEY", "") | |
| if not api_key: | |
| return ( | |
| "DrugBank API key not configured. Set DRUGBANK_API_KEY " | |
| "environment variable to enable this tool. Skipping DrugBank " | |
| f"search for '{drug_name}'." | |
| ) | |
| url = "https://api.drugbank.com/v1/drug_interactions" | |
| headers = {"Authorization": api_key} | |
| params = {"q": drug_name} | |
| r = requests.get(url, headers=headers, params=params, timeout=15) | |
| if r.status_code != 200: | |
| return f"DrugBank lookup failed for '{drug_name}' (status {r.status_code})." | |
| return r.text[:3000] | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 6. Internal FAISS RAG β RANO, Zetterberg, curated GBM papers | |
| # (Agent RAG index β built separately, path provided below) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # NOTE: Set this to the path of your pre-built Agent RAG FAISS index. | |
| AGENT_RAG_INDEX_PATH = r"NeuroAgent\NeuroBio_faiss_index" | |
| _agent_rag_retriever = None | |
| def _get_agent_rag_retriever(): | |
| """Lazy-load the FAISS retriever so it's only loaded once, on first use.""" | |
| global _agent_rag_retriever | |
| if _agent_rag_retriever is None: | |
| from langchain_community.vectorstores import FAISS | |
| from langchain_huggingface import HuggingFaceEmbeddings | |
| # Swap embedding model below for whatever was used to BUILD the index. | |
| embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2") | |
| vectorstore = FAISS.load_local( | |
| AGENT_RAG_INDEX_PATH, | |
| embeddings, | |
| allow_dangerous_deserialization=True, | |
| ) | |
| _agent_rag_retriever = vectorstore.as_retriever(search_kwargs={"k": 4}) | |
| return _agent_rag_retriever | |
| def query_agent_rag(query: str) -> str: | |
| """Search the curated internal research library (RANO criteria, | |
| Zetterberg cfDNA papers, and other pre-vetted GBM literature) via | |
| FAISS retrieval. Use this for grounded, pre-vetted reference material | |
| in addition to live PubMed/bioRxiv search.""" | |
| retriever = _get_agent_rag_retriever() | |
| docs = retriever.invoke(query) | |
| if not docs: | |
| return f"No results found in Agent RAG index for '{query}'." | |
| out = [] | |
| for d in docs: | |
| source = d.metadata.get("source", "unknown") | |
| out.append(f"[Source: {source}]\n{d.page_content[:600]}") | |
| return "\n\n".join(out)[:3000] | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Tool registry β imported by nodes.py for bind_tools() | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| all_tools = [ | |
| search_pubmed, | |
| search_clinical_trials, | |
| search_biorxiv, | |
| search_omim, | |
| search_drugbank, | |
| query_agent_rag, | |
| ] |