mgbam commited on
Commit
58b116f
·
verified ·
1 Parent(s): 7bd0ea1

Update tools/bioportal_tool.py

Browse files
Files changed (1) hide show
  1. tools/bioportal_tool.py +50 -19
tools/bioportal_tool.py CHANGED
@@ -1,41 +1,72 @@
1
- from langchain.tools import BaseTool
 
2
  from typing import Type, Optional
3
  from pydantic import BaseModel, Field
 
4
  from clinical_nlp.umls_bioportal import search_bioportal_term
5
  from services.logger import app_logger
6
  from services.metrics import log_tool_usage
7
 
8
  class BioPortalInput(BaseModel):
9
  term: str = Field(description="The medical term to search for.")
10
- ontology: Optional[str] = Field(default="SNOMEDCT", description="The specific ontology to search within BioPortal (e.g., SNOMEDCT, ICD10, RxNorm). Defaults to SNOMEDCT.")
 
 
 
 
 
 
11
 
12
  class BioPortalLookupTool(BaseTool):
13
  name: str = "bioportal_lookup"
14
  description: str = (
15
- "Useful for searching medical terms, codes, and definitions across various ontologies "
16
- "via BioPortal. Specify the term and optionally the ontology (e.g., SNOMEDCT, ICD10CM, RxNorm)."
 
 
17
  )
18
  args_schema: Type[BaseModel] = BioPortalInput
19
 
20
- def _run(self, term: str, ontology: Optional[str] = "SNOMEDCT") -> str:
21
- app_logger.info(f"BioPortal Tool called with term: {term}, ontology: {ontology}")
22
- log_tool_usage(self.name)
23
- results = search_bioportal_term(term, ontology=ontology or "SNOMEDCT")
24
- if "error" in results:
25
- return f"Error from BioPortal lookup: {results['error']}"
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- # Format results for LLM consumption
28
- collection = results.get("collection", [])
29
  if collection:
30
  formatted_results = []
31
- for item in collection[:3]: # Limit to 3 results
32
- defs = item.get('definition', ['N/A'])
33
- definition_str = "; ".join(defs) if defs else "N/A"
 
 
 
 
34
  formatted_results.append(
35
- f"- Term: {item.get('prefLabel', 'N/A')}, Definition: {definition_str}, CUIs: {item.get('cui', [])}"
36
  )
37
- return f"BioPortal Results (Ontology: {ontology}):\n" + "\n".join(formatted_results)
38
- return f"No results found in BioPortal for ontology {ontology}."
 
 
 
 
 
39
 
40
- async def _arun(self, term: str, ontology: Optional[str] = "SNOMEDCT") -> str:
 
41
  return self._run(term, ontology)
 
1
+ # /home/user/app/tools/bioportal_tool.py
2
+ from langchain_core.tools import BaseTool
3
  from typing import Type, Optional
4
  from pydantic import BaseModel, Field
5
+ # Assuming clinical_nlp.umls_bioportal.search_bioportal_term exists and works
6
  from clinical_nlp.umls_bioportal import search_bioportal_term
7
  from services.logger import app_logger
8
  from services.metrics import log_tool_usage
9
 
10
  class BioPortalInput(BaseModel):
11
  term: str = Field(description="The medical term to search for.")
12
+ ontology: Optional[str] = Field(
13
+ default="SNOMEDCT_US", # Default to SNOMEDCT US Edition
14
+ description=(
15
+ "The specific ontology acronym to search within BioPortal (e.g., SNOMEDCT_US, ICD10CM, RXNORM, MESH, LOINC, NCIT). "
16
+ "Defaults to SNOMEDCT_US if not specified."
17
+ )
18
+ )
19
 
20
  class BioPortalLookupTool(BaseTool):
21
  name: str = "bioportal_lookup"
22
  description: str = (
23
+ "Use this tool to search for medical terms, codes (like ICD-10, SNOMED CT, RxNorm codes), definitions, and concept details "
24
+ "across a wide range of biomedical ontologies via BioPortal. "
25
+ "Useful when you need information from a specific ontology or want to explore different coding systems. "
26
+ "Input is a dictionary with 'term' and optional 'ontology'. If ontology is not specified by the user, you should default to 'SNOMEDCT_US'."
27
  )
28
  args_schema: Type[BaseModel] = BioPortalInput
29
 
30
+ def _run(self, term: str, ontology: Optional[str] = "SNOMEDCT_US") -> str:
31
+ # Pydantic ensures 'term' is provided. 'ontology' will have its default if not given.
32
+ target_ontology = ontology or "SNOMEDCT_US" # Ensure a default if None is passed
33
+ app_logger.info(f"BioPortal Tool called with term: '{term}', ontology: '{target_ontology}'")
34
+ log_tool_usage(self.name, {"query_term": term, "ontology": target_ontology})
35
+
36
+ if not term or not term.strip():
37
+ return "Error from BioPortal lookup: No search term provided."
38
+
39
+ try:
40
+ results = search_bioportal_term(term, ontology=target_ontology)
41
+ except Exception as e:
42
+ app_logger.error(f"Exception during BioPortal search for '{term}' in '{target_ontology}': {e}", exc_info=True)
43
+ return f"Error performing BioPortal lookup for '{term}': An unexpected error occurred during the search."
44
+
45
+ if isinstance(results, dict) and "error" in results:
46
+ app_logger.warning(f"BioPortal lookup for '{term}' in '{target_ontology}' returned an error: {results['error']}")
47
+ return f"Error from BioPortal lookup for '{term}' (Ontology: {target_ontology}): {results['error']}"
48
 
49
+ collection = results.get("collection", []) if isinstance(results, dict) else []
 
50
  if collection:
51
  formatted_results = []
52
+ for item in collection[:3]: # Limit to 3 results for brevity
53
+ pref_label = item.get('prefLabel', 'N/A')
54
+ defs = item.get('definition', [])
55
+ definition_str = ("; ".join(d for d in defs if d) if defs else "No definition available.")[:200] + "..." # Truncate long defs
56
+ cui_list = item.get('cui', [])
57
+ cui_str = ", ".join(cui_list) if cui_list else "N/A"
58
+
59
  formatted_results.append(
60
+ f"- Term: {pref_label} (CUIs: {cui_str}). Definition: {definition_str}"
61
  )
62
+ if formatted_results:
63
+ return f"BioPortal Search Results for '{term}' (Ontology: {target_ontology}):\n" + "\n".join(formatted_results)
64
+ else: # Collection was present but no usable items after formatting
65
+ return f"No specific items found in BioPortal results for '{term}' (Ontology: {target_ontology}), though the query was successful."
66
+
67
+ app_logger.warning(f"No results or unexpected format from BioPortal for '{term}' in '{target_ontology}'. Raw: {str(results)[:200]}")
68
+ return f"No results found in BioPortal for term '{term}' in ontology '{target_ontology}'."
69
 
70
+ async def _arun(self, term: str, ontology: Optional[str] = "SNOMEDCT_US") -> str:
71
+ # For simplicity, using sync version. Implement true async if search_bioportal_term is async.
72
  return self._run(term, ontology)