Spaces:
Sleeping
Sleeping
Initial deployment: ClinicalMatch AI v2.0 — FHIR R4 · MCP (9 tools) · A2A workflow · SHARP compliance · 100k synthetic patients · Neo4j graph · GraphRAG chatbot
59abb4f | import httpx | |
| import asyncio | |
| from typing import Optional | |
| import os | |
| CTGOV_BASE = "https://clinicaltrials.gov/api/v2/studies" | |
| async def search_trials(condition: str, phase: Optional[str] = None, status: str = "RECRUITING", page_size: int = 20) -> list[dict]: | |
| params = { | |
| "query.cond": condition, | |
| "filter.overallStatus": status, | |
| "pageSize": page_size, | |
| "format": "json", | |
| "sort": "LastUpdatePostDate:desc", | |
| } | |
| if phase: | |
| params["filter.phase"] = f"PHASE{phase.replace('Phase ', '').replace('I', '1').replace('II', '2').replace('III', '3').replace('IV', '4')}" | |
| async with httpx.AsyncClient(timeout=30.0) as client: | |
| try: | |
| resp = await client.get(CTGOV_BASE, params=params) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| studies = data.get("studies", []) | |
| return [_normalize_study(s) for s in studies] | |
| except Exception as e: | |
| print(f"ClinicalTrials.gov API error: {e}") | |
| return _fallback_trials(condition) | |
| async def get_trial_details(nct_id: str) -> dict: | |
| params = {"query.id": nct_id, "format": "json"} | |
| async with httpx.AsyncClient(timeout=30.0) as client: | |
| try: | |
| resp = await client.get(CTGOV_BASE, params=params) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| studies = data.get("studies", []) | |
| if studies: | |
| return _normalize_study(studies[0]) | |
| except Exception as e: | |
| print(f"ClinicalTrials.gov detail error: {e}") | |
| return {} | |
| def _normalize_study(study: dict) -> dict: | |
| proto = study.get("protocolSection", {}) | |
| ident = proto.get("identificationModule", {}) | |
| status_module = proto.get("statusModule", {}) | |
| desc = proto.get("descriptionModule", {}) | |
| eligibility = proto.get("eligibilityModule", {}) | |
| design = proto.get("designModule", {}) | |
| contacts = proto.get("contactsLocationsModule", {}) | |
| sponsor = proto.get("sponsorCollaboratorsModule", {}) | |
| outcomes = proto.get("outcomesModule", {}) | |
| locations = [] | |
| for loc in contacts.get("locations", [])[:5]: | |
| locations.append({ | |
| "city": loc.get("city", ""), | |
| "state": loc.get("state", ""), | |
| "country": loc.get("country", "US"), | |
| "facility": loc.get("facility", ""), | |
| "lat": loc.get("geoPoint", {}).get("lat"), | |
| "lon": loc.get("geoPoint", {}).get("lon"), | |
| }) | |
| phases = design.get("phases", []) | |
| return { | |
| "nct_id": ident.get("nctId", ""), | |
| "title": ident.get("briefTitle", ""), | |
| "status": status_module.get("overallStatus", ""), | |
| "phase": phases[0] if phases else "N/A", | |
| "brief_summary": desc.get("briefSummary", ""), | |
| "eligibility_criteria": eligibility.get("eligibilityCriteria", ""), | |
| "min_age": eligibility.get("minimumAge", ""), | |
| "max_age": eligibility.get("maximumAge", ""), | |
| "sex": eligibility.get("sex", "ALL"), | |
| "enrollment": design.get("enrollmentInfo", {}).get("count", 0), | |
| "start_date": status_module.get("startDateStruct", {}).get("date", ""), | |
| "completion_date": status_module.get("completionDateStruct", {}).get("date", ""), | |
| "last_updated": status_module.get("lastUpdatePostDateStruct", {}).get("date", ""), | |
| "sponsor": sponsor.get("leadSponsor", {}).get("name", ""), | |
| "primary_outcomes": [o.get("measure", "") for o in outcomes.get("primaryOutcomes", [])[:3]], | |
| "locations": locations, | |
| "location_count": len(contacts.get("locations", [])), | |
| "ctgov_url": f"https://clinicaltrials.gov/study/{ident.get('nctId', '')}", | |
| } | |
| def _fallback_trials(condition: str) -> list[dict]: | |
| """Realistic fallback when API is unavailable.""" | |
| return [ | |
| { | |
| "nct_id": "NCT04889131", | |
| "title": f"Precision Medicine Study for {condition}", | |
| "status": "RECRUITING", | |
| "phase": "PHASE2", | |
| "brief_summary": f"A randomized controlled trial evaluating targeted therapy for {condition} in adult patients.", | |
| "eligibility_criteria": "Inclusion Criteria:\n- Age 18-75\n- Confirmed diagnosis\n- ECOG performance status 0-2\nExclusion Criteria:\n- Prior treatment failure\n- Active autoimmune disease", | |
| "min_age": "18 Years", | |
| "max_age": "75 Years", | |
| "sex": "ALL", | |
| "enrollment": 150, | |
| "start_date": "2024-01", | |
| "completion_date": "2026-06", | |
| "sponsor": "Academic Medical Center", | |
| "primary_outcomes": ["Overall Survival", "Progression-Free Survival"], | |
| "locations": [ | |
| {"city": "Boston", "state": "MA", "country": "US", "facility": "Dana-Farber Cancer Institute", "lat": 42.3376, "lon": -71.1083}, | |
| {"city": "Houston", "state": "TX", "country": "US", "facility": "MD Anderson Cancer Center", "lat": 29.7066, "lon": -95.3990}, | |
| ], | |
| "location_count": 2, | |
| }, | |
| { | |
| "nct_id": "NCT05123456", | |
| "title": f"Immunotherapy Combination for Advanced {condition}", | |
| "status": "RECRUITING", | |
| "phase": "PHASE3", | |
| "brief_summary": f"Phase III trial of combination immunotherapy in patients with advanced {condition}.", | |
| "eligibility_criteria": "Inclusion Criteria:\n- Age ≥ 18\n- Histologically confirmed diagnosis\n- Measurable disease per RECIST 1.1\nExclusion Criteria:\n- Brain metastases\n- Prior PD-1/PD-L1 therapy", | |
| "min_age": "18 Years", | |
| "max_age": "N/A", | |
| "sex": "ALL", | |
| "enrollment": 400, | |
| "start_date": "2023-06", | |
| "completion_date": "2027-12", | |
| "sponsor": "Pharma Innovations Inc", | |
| "primary_outcomes": ["Overall Survival at 24 months"], | |
| "locations": [ | |
| {"city": "New York", "state": "NY", "country": "US", "facility": "Memorial Sloan Kettering", "lat": 40.7644, "lon": -73.9581}, | |
| {"city": "San Francisco", "state": "CA", "country": "US", "facility": "UCSF Medical Center", "lat": 37.7631, "lon": -122.4578}, | |
| {"city": "Chicago", "state": "IL", "country": "US", "facility": "Northwestern Medicine", "lat": 41.8827, "lon": -87.6233}, | |
| ], | |
| "location_count": 3, | |
| }, | |
| ] | |
| def search_trials_sync(condition: str, phase: Optional[str] = None, status: str = "RECRUITING", page_size: int = 20) -> list[dict]: | |
| """Synchronous version using httpx.Client — safe to call from any context.""" | |
| params = { | |
| "query.cond": condition, | |
| "filter.overallStatus": status, | |
| "pageSize": page_size, | |
| "format": "json", | |
| "sort": "LastUpdatePostDate:desc", | |
| } | |
| if phase: | |
| params["filter.phase"] = f"PHASE{phase.replace('Phase ', '').replace('I', '1').replace('II', '2').replace('III', '3').replace('IV', '4')}" | |
| with httpx.Client(timeout=30.0) as client: | |
| try: | |
| resp = client.get(CTGOV_BASE, params=params) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| return [_normalize_study(s) for s in data.get("studies", [])] | |
| except Exception as e: | |
| print(f"ClinicalTrials.gov API error (sync): {e}") | |
| return _fallback_trials(condition) | |
| def get_trial_details_sync(nct_id: str) -> dict: | |
| """Synchronous version using httpx.Client — safe to call from any context.""" | |
| params = {"query.id": nct_id, "format": "json"} | |
| with httpx.Client(timeout=30.0) as client: | |
| try: | |
| resp = client.get(CTGOV_BASE, params=params) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| studies = data.get("studies", []) | |
| if studies: | |
| return _normalize_study(studies[0]) | |
| except Exception as e: | |
| print(f"ClinicalTrials.gov detail error (sync): {e}") | |
| return {} | |