Files
mims-harvard__tooluniverse/examples/icd_tools_example.py
Shanghua Gao d75c3edd91 Async features + new tools and new skills (#71)
* Convert ProteinsPlus and SwissDock to AsyncPollingTool

- Converted both ProteinsPlus (5 tools) and SwissDock (3 tools) to use AsyncPollingTool base class
- Eliminated 123 lines of polling boilerplate across both tools
- Automatic polling, progress reporting, and timeout management
- Maintains 100% backward compatibility
- All 8 async tools load successfully
- Added comprehensive documentation and conversion examples

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Clean up root directory: move temp docs and test files

Moved 81 markdown documentation files and 14 Python test scripts
to temp_docs_and_tests/ folder to keep root directory clean.

Files moved:
- 81 temporary .md documentation files
- 12 test_*.py scripts
- 2 validation scripts (devtu_validation.py, validate_proteinsplus.py)

Preserved:
- README.md (kept in root)
- All production code and configuration

Updated .gitignore to exclude temp_docs_and_tests/ folder.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Complete AsyncPollingTool conversion testing

Comprehensive testing suite confirms conversion is production-ready:

Test Results:
- ✅ 8/8 compatibility tests passed
- ✅ 44/44 async-related pytest tests passed
- ✅ 79/80 core tests passed (1 non-critical mock issue)
- ✅ All 1,264 tools load correctly
- ✅ No regressions in existing functionality

Verified:
- ProteinsPlus (5 tools): All inherit from AsyncPollingTool
- SwissDock (3 tools): All inherit from AsyncPollingTool
- Tool loading and instantiation
- Parameter validation
- Error handling
- Return schema compatibility
- Sync tools unaffected

Code improvements:
- 123 lines of polling boilerplate eliminated
- 39 net lines reduced
- 100% polling automation
- Consistent structure across all async tools

Status: PRODUCTION READY ✅

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Complete MCP operations verification

Comprehensive double-check of all MCP-based operations confirms everything works:

Test Results:
✅ 7/7 MCP operation test suites passed (100%)
✅ SMCP server with TaskManager fully functional
✅ All MCP Tasks handlers implemented correctly
✅ AsyncPollingTool tools work seamlessly with MCP
✅ ToolUniverse auto-detects async tools
✅ Progress reporting flows through entire stack
✅ No regressions from AsyncPollingTool conversion

Components Verified:
- SMCP Server (smcp.py) - MCP Tasks support
- TaskManager (task_manager.py) - All CRUD operations
- TaskProgress (task_progress.py) - Progress updates
- AsyncPollingTool (async_base.py) - Base class functionality
- ProteinsPlus & SwissDock - Converted async tools
- ToolUniverse (execute_function.py) - Async detection
- MCP Client Tools - All present and functional

Integration Points:
✅ SMCP → TaskManager
✅ TaskManager → ToolUniverse
✅ ToolUniverse → AsyncPollingTool
✅ AsyncPollingTool → TaskProgress

Documentation:
- MCP_OPERATIONS_VERIFICATION.md (comprehensive report)
- EXECUTE_FUNCTION_ANALYSIS.md (complexity analysis)
- test_mcp_operations.py (7 test suites)

Status: FULLY VERIFIED - PRODUCTION READY ✅

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Add comprehensive async tools guide to documentation

Created complete guide for AsyncPollingTool in ToolUniverse documentation:

Content:
- Overview and when to use AsyncPollingTool
- Quick start with minimal example
- Complete workflow explanation
- Real-world examples (ProteinsPlus, SwissDock)
- Progress reporting integration
- Error handling patterns
- MCP Tasks integration
- Testing strategies
- Best practices and common patterns
- Migration guide from manual polling
- Troubleshooting section
- Complete API reference

Features:
✅ 800+ lines comprehensive guide
✅ Working code examples throughout
✅ Real ProteinsPlus & SwissDock examples
✅ Common patterns and anti-patterns
✅ Troubleshooting common issues
✅ Migration guide for existing tools
✅ Integration with MCP Tasks explained
✅ Added to documentation index

Target audience:
- Developers creating new async tools
- Developers migrating existing async tools
- Users understanding async tool behavior

Location: docs/expand_tooluniverse/async_tools_guide.rst

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix linting errors: remove unused variables and convert lambda to def

- Fix F841 unused variable errors in test files
- Fix E731 lambda expression errors by converting to def
- Remove unused composed_cache_key in execute_function.py
- Fix unused report variables in DDI skill examples

* Move implementation notes from docs/ to temp_docs_and_tests/

- Move 13 implementation/research md files to temp folder
- Keep MCP_TASKS_GUIDE.md (referenced in README) and DOCUMENTATION_STRUCTURE.md
- Files moved: api_research_*, biogrid, ICD, LOINC, SASBDB, proteinsplus, ncbi_sra implementation docs

* Add .claude/ to gitignore and fix composed_cache_key bug

- Add .claude/ to .gitignore to exclude Claude Code config
- Remove .claude/settings.json from git tracking
- Fix F841 linting error: restore composed_cache_key for singleflight_guard
- Remove unused composed_cache_key initialization in second function

* Move test files and temp docs from root to temp_docs_and_tests/

- Move test_async_conversion_compatibility.py
- Move test_mcp_operations.py
- Move ASYNC_CONVERSION_TESTING_COMPLETE.md
- Move EXECUTE_FUNCTION_ANALYSIS.md
- Move MCP_OPERATIONS_VERIFICATION.md

These are temporary files that should not be in the root directory.

* Fix test_task_manager.py mock configuration

- Create separate mock tool instances to avoid shared state issues
- Add _get_tool_instance method to mock ToolUniverse
- Fix test_get_result_waits_for_completion to use AsyncMock with side_effect
- All 27 tests now pass

* Fix test_tooluniverse_cache_integration.py

- Fix test_batch_run_deduplicates_work to use return_message=True
- Add .get() to safely access 'role' key in messages
- All 6 cache integration tests now pass

* Fix test_run_parameters.py batch test

- Add return_message=True to test_run_batch_parallel_preserves_order_and_cache_flag
- Change msg['role'] to msg.get('role') for safety
- All 7 tests in test_run_parameters.py now pass

* Remove temp_docs_and_tests/ from git tracking

The temp folder should not be pushed to GitHub.
Files are kept locally but removed from repository.

* Add devtu-github skill for CI debugging and test fixing

- Comprehensive guide for fixing GitHub CI failures
- Pre-commit hook setup and management
- Common test failure patterns and fixes:
  * KeyError 'role' - missing return_message=True
  * Mock not subscriptable - fix mock configuration
  * Linting errors F841/E731
  * Temp files in git tracking
- Systematic debugging workflow
- Real examples from today's 40 test fixes
- Quick reference commands

Skill helps ensure clean CI pipelines and reliable tests.

* Enhance devtu-github skill: add explicit what-to-push guide

- Add comprehensive 'What to Push and What NOT to Push' section
- ✅ ALWAYS Push: source code, tests, docs, config
- ❌ NEVER Push: temp folders, build artifacts, logs, .env, IDE files
- ⚠️ MAYBE Push: skills (use git add -f), small data files
- How to check what will be pushed before committing
- Emergency commands to unstage wrong files
- Verifying .gitignore works correctly

Makes it crystal clear which files belong in git and which don't.

* Simplify Usage & Integration section to links only

- Resolve merge conflict in README.md
- Keep simple link list instead of detailed code examples
- Users can click links for full tutorials
2026-02-12 12:32:34 -05:00

207 lines
6.0 KiB
Python

"""ICD-10/ICD-11 disease classification tools -- usage examples.
ICD-11 tools require ICD_CLIENT_ID / ICD_CLIENT_SECRET env vars (register at https://icd.who.int/icdapi).
ICD-10 tools require no authentication.
"""
from tooluniverse import ToolUniverse
def example_icd10_search():
"""Example: Search ICD-10-CM codes by disease name."""
print("\n" + "=" * 80)
print("Example 1: Search ICD-10-CM codes")
print("=" * 80)
tu = ToolUniverse()
tu.load_tools()
# Search for diabetes codes
result = tu.tools.ICD10_search_codes(
query="diabetes mellitus type 2",
limit=5
)
print("\nQuery: 'diabetes mellitus type 2'")
print(f"\nTotal results: {result['data']['total']}")
print("\nTop 5 codes:")
for item in result['data']['results'][:5]:
print(f" {item['code']}: {item['name']}")
def example_icd10_code_lookup():
"""Example: Get details for a specific ICD-10-CM code."""
print("\n" + "=" * 80)
print("Example 2: Look up specific ICD-10-CM code")
print("=" * 80)
tu = ToolUniverse()
tu.load_tools()
# Look up E11.9 (Type 2 diabetes without complications)
result = tu.tools.ICD10_get_code_info(code="E11.9")
print("\nCode: E11.9")
if result['data']['results']:
code_info = result['data']['results'][0]
print(f"Name: {code_info['name']}")
print(f"Code: {code_info['code']}")
def example_icd11_search():
"""Example: Search ICD-11 for diseases (requires authentication)."""
print("\n" + "=" * 80)
print("Example 3: Search ICD-11 diseases")
print("=" * 80)
tu = ToolUniverse()
tu.load_tools()
# Search for hypertension
result = tu.tools.ICD11_search_diseases(
query="essential hypertension",
linearization="mms",
flatResults=True,
language="en"
)
if 'error' in result:
print(f"\nError: {result['error']}")
print("\nNote: ICD-11 tools require authentication.")
print("Register at: https://icd.who.int/icdapi")
return
print("\nQuery: 'essential hypertension'")
print("\nResults:")
for entity in result['data'].get('destinationEntities', [])[:5]:
print(f" Code: {entity.get('theCode', 'N/A')}")
print(f" Title: {entity.get('title', 'N/A')}")
print(f" Score: {entity.get('score', 'N/A')}")
print()
def example_icd11_entity_details():
"""Example: Get detailed ICD-11 entity information."""
print("\n" + "=" * 80)
print("Example 4: Get ICD-11 entity details")
print("=" * 80)
tu = ToolUniverse()
tu.load_tools()
# Get details for a specific entity (diabetes mellitus)
result = tu.tools.ICD11_get_entity(
entity_id="1435254666",
linearization="mms",
language="en"
)
if 'error' in result:
print(f"\nError: {result['error']}")
return
print("\nEntity ID: 1435254666")
data = result['data']
print(f"Title: {data.get('title', {}).get('@value', 'N/A')}")
print(f"Code: {data.get('code', 'N/A')}")
print(f"Browser URL: {data.get('browserUrl', 'N/A')}")
if 'definition' in data:
definition = data['definition'].get('@value', 'N/A')
print(f"\nDefinition: {definition[:200]}...")
def example_disease_research_workflow():
"""Example: Complete workflow for disease research."""
print("\n" + "=" * 80)
print("Example 5: Disease Research Workflow")
print("=" * 80)
tu = ToolUniverse()
tu.load_tools()
disease = "hypertension"
print(f"\nResearch Topic: {disease}")
print("\n" + "-" * 40)
# Step 1: Get ICD-10 code
print("\n1. Finding ICD-10 code...")
icd10 = tu.tools.ICD10_search_codes(query=disease, limit=1)
if icd10['data']['results']:
code = icd10['data']['results'][0]
print(f" ICD-10 Code: {code['code']}")
print(f" Description: {code['name']}")
# Step 2: Get ICD-11 information (if authenticated)
print("\n2. Searching ICD-11...")
icd11 = tu.tools.ICD11_search_diseases(
query=disease,
flatResults=True,
language="en"
)
if 'error' not in icd11:
entities = icd11['data'].get('destinationEntities', [])
if entities:
print(f" ICD-11 Code: {entities[0].get('theCode', 'N/A')}")
print(f" Title: {entities[0].get('title', 'N/A')}")
# Step 3: Find related targets (using existing tools)
print("\n3. Finding disease targets...")
try:
targets = tu.tools.OpenTargets_get_associated_targets_by_disease_name(
disease_name=disease,
limit=3
)
if 'data' in targets:
print(f" Found {len(targets['data'])} associated targets")
for target in targets['data'][:3]:
print(f" - {target.get('approvedSymbol', 'N/A')}")
except Exception as e:
print(f" (Optional) Could not fetch targets: {e}")
# Step 4: Search literature
print("\n4. Searching literature...")
try:
papers = tu.tools.PubMed_search_publications(
query=f"{disease} [Title/Abstract]",
retmax=3
)
if 'data' in papers:
print(f" Found {papers['data'].get('count', 0)} papers")
for article in papers['data'].get('articles', [])[:3]:
title = article.get('title', 'No title')[:60]
print(f" - {title}...")
except Exception as e:
print(f" (Optional) Could not fetch papers: {e}")
print("\n" + "-" * 40)
print("Workflow complete!")
def main():
"""Run all examples."""
print("\n" + "=" * 80)
print("ICD-10/ICD-11 Disease Classification Tools - Examples")
print("=" * 80)
# ICD-10 examples (no auth required)
example_icd10_search()
example_icd10_code_lookup()
# ICD-11 examples (require auth)
example_icd11_search()
example_icd11_entity_details()
# Integrated workflow
example_disease_research_workflow()
print("\n" + "=" * 80)
print("Examples complete!")
print("=" * 80)
if __name__ == "__main__":
main()