""" Medical/Health Tool - Search medical literature and health data Free sources used: - PubMed/NCBI E-utilities API (completely free, no key required for basic use) - Disease.sh API (completely free, open disease data) - Health.gov API (free government health data) - OpenFDA API (free FDA data) All APIs are free and most don't require authentication. """ from __future__ import annotations import logging from datetime import datetime from typing import Optional import requests log = logging.getLogger(__name__) # Free medical API endpoints PUBMED_EUTILS_API = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils" DISEASE_API = "https://disease.sh/v3" OPENFDA_API = "https://api.fda.gov" HEALTH_GOV_API = "https://health.gov" def medical_search_pubmed( query: str, max_results: int = 10, ) -> dict: """ Search PubMed for medical/health research articles. Args: query: Search query (medical terms, diseases, treatments, etc.) max_results: Maximum number of results (default: 10) Returns: Dictionary with PubMed search results """ try: # First, search for article IDs search_url = f"{PUBMED_EUTILS_API}/esearch.fcgi" search_params = { "db": "pubmed", "term": query, "retmax": max_results, "retmode": "json", "sort": "relevance", } search_response = requests.get(search_url, params=search_params, timeout=15) search_response.raise_for_status() search_data = search_response.json() id_list = search_data.get("esearchresult", {}).get("idlist", []) if not id_list: return { "success": True, "source": "pubmed", "query": query, "results": [], "count": 0, "message": "No articles found for this query", } # Fetch article summaries fetch_url = f"{PUBMED_EUTILS_API}/esummary.fcgi" fetch_params = { "db": "pubmed", "id": ",".join(id_list), "retmode": "json", } fetch_response = requests.get(fetch_url, params=fetch_params, timeout=15) fetch_response.raise_for_status() fetch_data = fetch_response.json() results = [] for article_id in id_list: article = fetch_data.get("result", {}).get(article_id, {}) if article and "error" not in article: results.append({ "pmid": article_id, "title": article.get("title", ""), "authors": [a.get("name", "") for a in article.get("authors", [])], "journal": article.get("fulljournalname", article.get("source", "")), "pub_date": article.get("pubdate", ""), "doi": article.get("elocationid", ""), "url": f"https://pubmed.ncbi.nlm.nih.gov/{article_id}/", "abstract_available": "abstract" in article, }) return { "success": True, "source": "pubmed", "query": query, "results": results, "count": len(results), "total_found": int(search_data.get("esearchresult", {}).get("count", 0)), } except Exception as e: log.error(f"PubMed search failed: {e}") return { "success": False, "error": str(e), "source": "pubmed", } def medical_get_pubmed_abstract( pmid: str, ) -> dict: """ Get the abstract of a PubMed article. Args: pmid: PubMed ID Returns: Dictionary with article abstract """ try: fetch_url = f"{PUBMED_EUTILS_API}/efetch.fcgi" params = { "db": "pubmed", "id": pmid, "rettype": "abstract", "retmode": "text", } response = requests.get(fetch_url, params=params, timeout=15) response.raise_for_status() abstract_text = response.text.strip() return { "success": True, "source": "pubmed", "pmid": pmid, "abstract": abstract_text, "url": f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/", } except Exception as e: log.error(f"PubMed abstract fetch failed: {e}") return { "success": False, "error": str(e), "source": "pubmed", "pmid": pmid, } def medical_get_disease_data( disease: str = "covid", ) -> dict: """ Get current disease statistics from Disease.sh API. Args: disease: Disease type (covid, influenza, or all) Returns: Dictionary with disease statistics """ try: disease = disease.lower() if disease in ["covid", "covid-19", "coronavirus"]: url = f"{DISEASE_API}/covid-19/all" elif disease in ["influenza", "flu"]: url = f"{DISEASE_API}/influenza/cdc" elif disease == "all": url = f"{DISEASE_API}/all" else: # Try COVID-19 countries data url = f"{DISEASE_API}/covid-19/countries/{disease}" response = requests.get(url, timeout=10) response.raise_for_status() data = response.json() return { "success": True, "source": "disease.sh", "disease": disease, "data": data, "timestamp": datetime.now().isoformat(), } except Exception as e: log.error(f"Disease data fetch failed: {e}") return { "success": False, "error": str(e), "source": "disease.sh", } def medical_get_covid_country( country: str = "usa", ) -> dict: """ Get COVID-19 statistics for a specific country. Args: country: Country name or ISO code (e.g., usa, uk, germany, china) Returns: Dictionary with country COVID-19 data """ try: url = f"{DISEASE_API}/covid-19/countries/{country}" response = requests.get(url, timeout=10) response.raise_for_status() data = response.json() return { "success": True, "source": "disease.sh", "country": data.get("country", country), "cases": data.get("cases"), "today_cases": data.get("todayCases"), "deaths": data.get("deaths"), "today_deaths": data.get("todayDeaths"), "recovered": data.get("recovered"), "active": data.get("active"), "critical": data.get("critical"), "cases_per_million": data.get("casesPerOneMillion"), "deaths_per_million": data.get("deathsPerOneMillion"), "tests": data.get("tests"), "tests_per_million": data.get("testsPerOneMillion"), "population": data.get("population"), "continent": data.get("continent"), "updated": datetime.fromtimestamp(data.get("updated", 0) / 1000).isoformat() if data.get("updated") else None, } except Exception as e: log.error(f"COVID country data fetch failed: {e}") return { "success": False, "error": str(e), "source": "disease.sh", } def medical_search_fda( query: str, database: str = "drug", limit: int = 10, ) -> dict: """ Search FDA drug, device, or food databases. Args: query: Search query database: Database to search (drug, device, food, other) limit: Maximum results (default: 10) Returns: Dictionary with FDA search results """ try: # Map database names to FDA endpoints db_map = { "drug": "drug/label", "device": "device/510k", "food": "food/enforcement", "other": "other/substance", } endpoint = db_map.get(database.lower(), "drug/label") url = f"{OPENFDA_API}/{endpoint}.json" params = { "search": query, "limit": limit, } response = requests.get(url, params=params, timeout=15) response.raise_for_status() data = response.json() results = [] for item in data.get("results", []): if database.lower() == "drug": results.append({ "brand_name": item.get("openfda", {}).get("brand_name", [""])[0] if item.get("openfda") else "", "generic_name": item.get("openfda", {}).get("generic_name", [""])[0] if item.get("openfda") else "", "manufacturer": item.get("openfda", {}).get("manufacturer_name", [""])[0] if item.get("openfda") else "", "purpose": item.get("purpose", [""])[0] if item.get("purpose") else "", "indications": item.get("indications_and_usage", [""])[0][:500] if item.get("indications_and_usage") else "", "warnings": item.get("warnings", [""])[0][:500] if item.get("warnings") else "", }) else: results.append(item) return { "success": True, "source": "openfda", "database": database, "query": query, "results": results, "count": len(results), } except Exception as e: log.error(f"FDA search failed: {e}") return { "success": False, "error": str(e), "source": "openfda", } def medical_get_health_topics( topic: Optional[str] = None, limit: int = 10, ) -> dict: """ Get health topics from Health.gov. Args: topic: Health topic to search (optional) limit: Maximum results (default: 10) Returns: Dictionary with health topics """ try: url = f"{HEALTH_GOV_API}/myhealthfinder/api/v3/topicsearch.json" params = {"lang": "en"} if topic: params["topic"] = topic else: params["pageSize"] = limit response = requests.get(url, params=params, timeout=10) response.raise_for_status() data = response.json() topics = data.get("Result", {}).get("Resources", {}).get("Resource", []) results = [] for item in topics[:limit]: results.append({ "title": item.get("Title", ""), "url": item.get("AccessibleVersion", item.get("MyHealthfinder", "")), "image_url": item.get("ImageAltUrl", ""), "image_alt": item.get("ImageAltText", ""), "categories": item.get("Categories", ""), "content": item.get("Sections", {}).get("section", [{}])[0].get("Content", "")[:500] if item.get("Sections") else "", }) return { "success": True, "source": "health.gov", "topic": topic, "results": results, "count": len(results), } except Exception as e: log.error(f"Health topics fetch failed: {e}") return { "success": False, "error": str(e), "source": "health.gov", } # Tool schemas for OpenAI function calling MEDICAL_SEARCH_PUBMED_SCHEMA = { "type": "function", "function": { "name": "medical_search_pubmed", "description": "Search PubMed for medical and health research articles. Use for scientific medical literature.", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "Medical search query (disease, treatment, drug, symptom)", }, "max_results": { "type": "integer", "description": "Maximum number of results (default: 10)", "default": 10, }, }, "required": ["query"], }, }, } MEDICAL_GET_PUBMED_ABSTRACT_SCHEMA = { "type": "function", "function": { "name": "medical_get_pubmed_abstract", "description": "Get the full abstract of a PubMed article. Use after medical_search_pubmed to get detailed content.", "parameters": { "type": "object", "properties": { "pmid": { "type": "string", "description": "PubMed ID from search results", }, }, "required": ["pmid"], }, }, } MEDICAL_GET_DISEASE_DATA_SCHEMA = { "type": "function", "function": { "name": "medical_get_disease_data", "description": "Get current disease statistics (COVID-19, influenza). Use for outbreak data and statistics.", "parameters": { "type": "object", "properties": { "disease": { "type": "string", "description": "Disease type (covid, influenza, all)", "default": "covid", }, }, "required": [], }, }, } MEDICAL_GET_COVID_COUNTRY_SCHEMA = { "type": "function", "function": { "name": "medical_get_covid_country", "description": "Get COVID-19 statistics for a specific country. Use for country-specific pandemic data.", "parameters": { "type": "object", "properties": { "country": { "type": "string", "description": "Country name or ISO code (e.g., usa, uk, germany)", }, }, "required": [], }, }, } MEDICAL_SEARCH_FDA_SCHEMA = { "type": "function", "function": { "name": "medical_search_fda", "description": "Search FDA databases for drug information, device approvals, and food safety. Use for medication info.", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "Search query (drug name, ingredient, etc.)", }, "database": { "type": "string", "description": "Database to search (drug, device, food)", "default": "drug", "enum": ["drug", "device", "food"], }, "limit": { "type": "integer", "description": "Maximum results (default: 10)", "default": 10, }, }, "required": ["query"], }, }, } MEDICAL_GET_HEALTH_TOPICS_SCHEMA = { "type": "function", "function": { "name": "medical_get_health_topics", "description": "Get health information and topics from Health.gov. Use for general health advice and wellness topics.", "parameters": { "type": "object", "properties": { "topic": { "type": "string", "description": "Health topic to search (optional)", }, "limit": { "type": "integer", "description": "Maximum results (default: 10)", "default": 10, }, }, "required": [], }, }, }