tooluniverse 0.2.0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tooluniverse might be problematic. Click here for more details.
- tooluniverse/__init__.py +340 -4
- tooluniverse/admetai_tool.py +84 -0
- tooluniverse/agentic_tool.py +563 -0
- tooluniverse/alphafold_tool.py +96 -0
- tooluniverse/base_tool.py +129 -6
- tooluniverse/boltz_tool.py +207 -0
- tooluniverse/chem_tool.py +192 -0
- tooluniverse/compose_scripts/__init__.py +1 -0
- tooluniverse/compose_scripts/biomarker_discovery.py +293 -0
- tooluniverse/compose_scripts/comprehensive_drug_discovery.py +186 -0
- tooluniverse/compose_scripts/drug_safety_analyzer.py +89 -0
- tooluniverse/compose_scripts/literature_tool.py +34 -0
- tooluniverse/compose_scripts/output_summarizer.py +279 -0
- tooluniverse/compose_scripts/tool_description_optimizer.py +681 -0
- tooluniverse/compose_scripts/tool_discover.py +705 -0
- tooluniverse/compose_scripts/tool_graph_composer.py +448 -0
- tooluniverse/compose_tool.py +371 -0
- tooluniverse/ctg_tool.py +1002 -0
- tooluniverse/custom_tool.py +81 -0
- tooluniverse/dailymed_tool.py +108 -0
- tooluniverse/data/admetai_tools.json +155 -0
- tooluniverse/data/agentic_tools.json +1156 -0
- tooluniverse/data/alphafold_tools.json +87 -0
- tooluniverse/data/boltz_tools.json +9 -0
- tooluniverse/data/chembl_tools.json +16 -0
- tooluniverse/data/clait_tools.json +108 -0
- tooluniverse/data/clinicaltrials_gov_tools.json +326 -0
- tooluniverse/data/compose_tools.json +202 -0
- tooluniverse/data/dailymed_tools.json +70 -0
- tooluniverse/data/dataset_tools.json +646 -0
- tooluniverse/data/disease_target_score_tools.json +712 -0
- tooluniverse/data/efo_tools.json +17 -0
- tooluniverse/data/embedding_tools.json +319 -0
- tooluniverse/data/enrichr_tools.json +31 -0
- tooluniverse/data/europe_pmc_tools.json +22 -0
- tooluniverse/data/expert_feedback_tools.json +10 -0
- tooluniverse/data/fda_drug_adverse_event_tools.json +491 -0
- tooluniverse/data/fda_drug_labeling_tools.json +1 -1
- tooluniverse/data/fda_drugs_with_brand_generic_names_for_tool.py +76929 -148860
- tooluniverse/data/finder_tools.json +209 -0
- tooluniverse/data/gene_ontology_tools.json +113 -0
- tooluniverse/data/gwas_tools.json +1082 -0
- tooluniverse/data/hpa_tools.json +333 -0
- tooluniverse/data/humanbase_tools.json +47 -0
- tooluniverse/data/idmap_tools.json +74 -0
- tooluniverse/data/mcp_client_tools_example.json +113 -0
- tooluniverse/data/mcpautoloadertool_defaults.json +28 -0
- tooluniverse/data/medlineplus_tools.json +141 -0
- tooluniverse/data/monarch_tools.json +1 -1
- tooluniverse/data/openalex_tools.json +36 -0
- tooluniverse/data/opentarget_tools.json +1 -1
- tooluniverse/data/output_summarization_tools.json +101 -0
- tooluniverse/data/packages/bioinformatics_core_tools.json +1756 -0
- tooluniverse/data/packages/categorized_tools.txt +206 -0
- tooluniverse/data/packages/cheminformatics_tools.json +347 -0
- tooluniverse/data/packages/earth_sciences_tools.json +74 -0
- tooluniverse/data/packages/genomics_tools.json +776 -0
- tooluniverse/data/packages/image_processing_tools.json +38 -0
- tooluniverse/data/packages/machine_learning_tools.json +789 -0
- tooluniverse/data/packages/neuroscience_tools.json +62 -0
- tooluniverse/data/packages/original_tools.txt +0 -0
- tooluniverse/data/packages/physics_astronomy_tools.json +62 -0
- tooluniverse/data/packages/scientific_computing_tools.json +560 -0
- tooluniverse/data/packages/single_cell_tools.json +453 -0
- tooluniverse/data/packages/software_tools.json +4954 -0
- tooluniverse/data/packages/structural_biology_tools.json +396 -0
- tooluniverse/data/packages/visualization_tools.json +399 -0
- tooluniverse/data/pubchem_tools.json +215 -0
- tooluniverse/data/pubtator_tools.json +68 -0
- tooluniverse/data/rcsb_pdb_tools.json +1332 -0
- tooluniverse/data/reactome_tools.json +19 -0
- tooluniverse/data/semantic_scholar_tools.json +26 -0
- tooluniverse/data/special_tools.json +2 -25
- tooluniverse/data/tool_composition_tools.json +88 -0
- tooluniverse/data/toolfinderkeyword_defaults.json +34 -0
- tooluniverse/data/txagent_client_tools.json +9 -0
- tooluniverse/data/uniprot_tools.json +211 -0
- tooluniverse/data/url_fetch_tools.json +94 -0
- tooluniverse/data/uspto_downloader_tools.json +9 -0
- tooluniverse/data/uspto_tools.json +811 -0
- tooluniverse/data/xml_tools.json +3275 -0
- tooluniverse/dataset_tool.py +296 -0
- tooluniverse/default_config.py +165 -0
- tooluniverse/efo_tool.py +42 -0
- tooluniverse/embedding_database.py +630 -0
- tooluniverse/embedding_sync.py +396 -0
- tooluniverse/enrichr_tool.py +266 -0
- tooluniverse/europe_pmc_tool.py +52 -0
- tooluniverse/execute_function.py +1775 -95
- tooluniverse/extended_hooks.py +444 -0
- tooluniverse/gene_ontology_tool.py +194 -0
- tooluniverse/graphql_tool.py +158 -36
- tooluniverse/gwas_tool.py +358 -0
- tooluniverse/hpa_tool.py +1645 -0
- tooluniverse/humanbase_tool.py +389 -0
- tooluniverse/logging_config.py +254 -0
- tooluniverse/mcp_client_tool.py +764 -0
- tooluniverse/mcp_integration.py +413 -0
- tooluniverse/mcp_tool_registry.py +925 -0
- tooluniverse/medlineplus_tool.py +337 -0
- tooluniverse/openalex_tool.py +228 -0
- tooluniverse/openfda_adv_tool.py +283 -0
- tooluniverse/openfda_tool.py +393 -160
- tooluniverse/output_hook.py +1122 -0
- tooluniverse/package_tool.py +195 -0
- tooluniverse/pubchem_tool.py +158 -0
- tooluniverse/pubtator_tool.py +168 -0
- tooluniverse/rcsb_pdb_tool.py +38 -0
- tooluniverse/reactome_tool.py +108 -0
- tooluniverse/remote/boltz/boltz_mcp_server.py +50 -0
- tooluniverse/remote/depmap_24q2/depmap_24q2_mcp_tool.py +442 -0
- tooluniverse/remote/expert_feedback/human_expert_mcp_tools.py +2013 -0
- tooluniverse/remote/expert_feedback/simple_test.py +23 -0
- tooluniverse/remote/expert_feedback/start_web_interface.py +188 -0
- tooluniverse/remote/expert_feedback/web_only_interface.py +0 -0
- tooluniverse/remote/expert_feedback_mcp/human_expert_mcp_server.py +1611 -0
- tooluniverse/remote/expert_feedback_mcp/simple_test.py +34 -0
- tooluniverse/remote/expert_feedback_mcp/start_web_interface.py +91 -0
- tooluniverse/remote/immune_compass/compass_tool.py +327 -0
- tooluniverse/remote/pinnacle/pinnacle_tool.py +328 -0
- tooluniverse/remote/transcriptformer/transcriptformer_tool.py +586 -0
- tooluniverse/remote/uspto_downloader/uspto_downloader_mcp_server.py +61 -0
- tooluniverse/remote/uspto_downloader/uspto_downloader_tool.py +120 -0
- tooluniverse/remote_tool.py +99 -0
- tooluniverse/restful_tool.py +53 -30
- tooluniverse/scripts/generate_tool_graph.py +408 -0
- tooluniverse/scripts/visualize_tool_graph.py +829 -0
- tooluniverse/semantic_scholar_tool.py +62 -0
- tooluniverse/smcp.py +2452 -0
- tooluniverse/smcp_server.py +975 -0
- tooluniverse/test/mcp_server_test.py +0 -0
- tooluniverse/test/test_admetai_tool.py +370 -0
- tooluniverse/test/test_agentic_tool.py +129 -0
- tooluniverse/test/test_alphafold_tool.py +71 -0
- tooluniverse/test/test_chem_tool.py +37 -0
- tooluniverse/test/test_compose_lieraturereview.py +63 -0
- tooluniverse/test/test_compose_tool.py +448 -0
- tooluniverse/test/test_dailymed.py +69 -0
- tooluniverse/test/test_dataset_tool.py +200 -0
- tooluniverse/test/test_disease_target_score.py +56 -0
- tooluniverse/test/test_drugbank_filter_examples.py +179 -0
- tooluniverse/test/test_efo.py +31 -0
- tooluniverse/test/test_enrichr_tool.py +21 -0
- tooluniverse/test/test_europe_pmc_tool.py +20 -0
- tooluniverse/test/test_fda_adv.py +95 -0
- tooluniverse/test/test_fda_drug_labeling.py +91 -0
- tooluniverse/test/test_gene_ontology_tools.py +66 -0
- tooluniverse/test/test_gwas_tool.py +139 -0
- tooluniverse/test/test_hpa.py +625 -0
- tooluniverse/test/test_humanbase_tool.py +20 -0
- tooluniverse/test/test_idmap_tools.py +61 -0
- tooluniverse/test/test_mcp_server.py +211 -0
- tooluniverse/test/test_mcp_tool.py +247 -0
- tooluniverse/test/test_medlineplus.py +220 -0
- tooluniverse/test/test_openalex_tool.py +32 -0
- tooluniverse/test/test_opentargets.py +28 -0
- tooluniverse/test/test_pubchem_tool.py +116 -0
- tooluniverse/test/test_pubtator_tool.py +37 -0
- tooluniverse/test/test_rcsb_pdb_tool.py +86 -0
- tooluniverse/test/test_reactome.py +54 -0
- tooluniverse/test/test_semantic_scholar_tool.py +24 -0
- tooluniverse/test/test_software_tools.py +147 -0
- tooluniverse/test/test_tool_description_optimizer.py +49 -0
- tooluniverse/test/test_tool_finder.py +26 -0
- tooluniverse/test/test_tool_finder_llm.py +252 -0
- tooluniverse/test/test_tools_find.py +195 -0
- tooluniverse/test/test_uniprot_tools.py +74 -0
- tooluniverse/test/test_uspto_tool.py +72 -0
- tooluniverse/test/test_xml_tool.py +113 -0
- tooluniverse/tool_finder_embedding.py +267 -0
- tooluniverse/tool_finder_keyword.py +693 -0
- tooluniverse/tool_finder_llm.py +699 -0
- tooluniverse/tool_graph_web_ui.py +955 -0
- tooluniverse/tool_registry.py +416 -0
- tooluniverse/uniprot_tool.py +155 -0
- tooluniverse/url_tool.py +253 -0
- tooluniverse/uspto_tool.py +240 -0
- tooluniverse/utils.py +369 -41
- tooluniverse/xml_tool.py +369 -0
- tooluniverse-1.0.0.dist-info/METADATA +377 -0
- tooluniverse-1.0.0.dist-info/RECORD +186 -0
- tooluniverse-1.0.0.dist-info/entry_points.txt +9 -0
- tooluniverse/generate_mcp_tools.py +0 -113
- tooluniverse/mcp_server.py +0 -3340
- tooluniverse-0.2.0.dist-info/METADATA +0 -139
- tooluniverse-0.2.0.dist-info/RECORD +0 -21
- tooluniverse-0.2.0.dist-info/entry_points.txt +0 -4
- {tooluniverse-0.2.0.dist-info → tooluniverse-1.0.0.dist-info}/WHEEL +0 -0
- {tooluniverse-0.2.0.dist-info → tooluniverse-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {tooluniverse-0.2.0.dist-info → tooluniverse-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Simple Test for Human Expert System
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from tooluniverse import ToolUniverse
|
|
7
|
+
|
|
8
|
+
# Initialize tool universe
|
|
9
|
+
tooluni = ToolUniverse()
|
|
10
|
+
tooluni.load_tools()
|
|
11
|
+
|
|
12
|
+
# Test queries for expert feedback tools
|
|
13
|
+
test_queries = [
|
|
14
|
+
{"name": "expert_get_expert_status", "arguments": {}},
|
|
15
|
+
{
|
|
16
|
+
"name": "expert_consult_human_expert",
|
|
17
|
+
"arguments": {
|
|
18
|
+
"question": "What is aspirin used for?",
|
|
19
|
+
"specialty": "general",
|
|
20
|
+
"priority": "normal",
|
|
21
|
+
"timeout_minutes": 1,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
print(tooluni.tool_specification("expert_consult_human_expert"))
|
|
27
|
+
|
|
28
|
+
for idx, query in enumerate(test_queries):
|
|
29
|
+
print(
|
|
30
|
+
f"\n[{idx+1}] Running tool: {query['name']} with arguments: {query['arguments']}"
|
|
31
|
+
)
|
|
32
|
+
result = tooluni.run(query)
|
|
33
|
+
print("✅ Success. Example output snippet:")
|
|
34
|
+
print(result if isinstance(result, dict) else str(result))
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Start Web Interface Only
|
|
4
|
+
|
|
5
|
+
This script starts only the web interface for expert consultations.
|
|
6
|
+
The MCP server must be running separately.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import requests
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def check_mcp_server():
|
|
16
|
+
"""Check if MCP server is running"""
|
|
17
|
+
try:
|
|
18
|
+
# Try the MCP endpoint first (follows redirects)
|
|
19
|
+
response = requests.get("http://localhost:7002/mcp/", timeout=10)
|
|
20
|
+
if response.status_code in [200, 405, 406]: # 405/406 means endpoint exists
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
# Fallback: try without trailing slash
|
|
24
|
+
response = requests.get("http://localhost:7002/mcp", timeout=10)
|
|
25
|
+
if response.status_code in [200, 405, 406, 307]: # 307 redirect is also OK
|
|
26
|
+
return True
|
|
27
|
+
|
|
28
|
+
# Last resort: try the tool endpoint
|
|
29
|
+
response = requests.post(
|
|
30
|
+
"http://localhost:7002/tools/get_expert_status", json={}, timeout=10
|
|
31
|
+
)
|
|
32
|
+
return response.status_code == 200
|
|
33
|
+
except Exception:
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def main():
|
|
38
|
+
print("🌐 Starting Human Expert Web Interface")
|
|
39
|
+
print("=" * 50)
|
|
40
|
+
|
|
41
|
+
# Check if MCP server is running
|
|
42
|
+
print("🔍 Checking for MCP server...")
|
|
43
|
+
if not check_mcp_server():
|
|
44
|
+
print("❌ MCP Server not detected!")
|
|
45
|
+
print("📡 Please start MCP server first:")
|
|
46
|
+
print(" python start_mcp_server.py")
|
|
47
|
+
print(" or")
|
|
48
|
+
print(" python human_expert_mcp_server.py")
|
|
49
|
+
print()
|
|
50
|
+
choice = input("Continue anyway? (y/N): ").strip().lower()
|
|
51
|
+
if choice != "y":
|
|
52
|
+
return 1
|
|
53
|
+
else:
|
|
54
|
+
print("✅ MCP Server is running")
|
|
55
|
+
|
|
56
|
+
print("🌐 Web Interface will run on port 8080")
|
|
57
|
+
print("🖥️ Browser should open automatically")
|
|
58
|
+
print("👨⚕️ Expert dashboard will be available at http://localhost:8080")
|
|
59
|
+
print("\nPress Ctrl+C to stop")
|
|
60
|
+
print("=" * 50)
|
|
61
|
+
|
|
62
|
+
# Find the main server script
|
|
63
|
+
script_path = Path(__file__).parent / "human_expert_mcp_server.py"
|
|
64
|
+
|
|
65
|
+
if not script_path.exists():
|
|
66
|
+
print(f"❌ Server script not found: {script_path}")
|
|
67
|
+
return 1
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
# Check Flask availability
|
|
71
|
+
try:
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
print("✅ Flask is available")
|
|
75
|
+
except ImportError:
|
|
76
|
+
print("❌ Flask not found. Install with: pip install flask")
|
|
77
|
+
return 1
|
|
78
|
+
|
|
79
|
+
# Start web interface only
|
|
80
|
+
subprocess.run([sys.executable, str(script_path), "--web-only"])
|
|
81
|
+
return 0
|
|
82
|
+
except KeyboardInterrupt:
|
|
83
|
+
print("\n👋 Web Interface stopped")
|
|
84
|
+
return 0
|
|
85
|
+
except Exception as e:
|
|
86
|
+
print(f"❌ Error starting web interface: {str(e)}")
|
|
87
|
+
return 1
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
sys.exit(main())
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""
|
|
2
|
+
COMPASS Prediction Tool - MCP Server
|
|
3
|
+
|
|
4
|
+
This module provides an MCP (Model Context Protocol) server for running immune checkpoint
|
|
5
|
+
inhibitor (ICI) response predictions using the COMPASS (COMprehensive Pathway Analysis
|
|
6
|
+
for Single-cell Sequencing) model. The tool processes tumor gene expression data to
|
|
7
|
+
predict patient responsiveness to immunotherapy.
|
|
8
|
+
|
|
9
|
+
The COMPASS model analyzes gene expression profiles to identify key immune cell
|
|
10
|
+
populations and pathways that contribute to treatment response prediction.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import torch
|
|
16
|
+
import pandas as pd
|
|
17
|
+
import asyncio
|
|
18
|
+
import uuid
|
|
19
|
+
from fastmcp import FastMCP
|
|
20
|
+
from typing import List, Tuple, Optional
|
|
21
|
+
|
|
22
|
+
sys.path.insert(
|
|
23
|
+
0, f'{os.getenv("COMPASS_MODEL_PATH")}/immune-compass/COMPASS'
|
|
24
|
+
) # noqa: E402
|
|
25
|
+
|
|
26
|
+
from compass import loadcompass # noqa: E402
|
|
27
|
+
|
|
28
|
+
# Initialize MCP Server for COMPASS predictions
|
|
29
|
+
server = FastMCP("COMPASS Prediction SMCP Server")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CompassTool:
|
|
33
|
+
"""
|
|
34
|
+
A comprehensive tool for running immune checkpoint inhibitor (ICI) response predictions
|
|
35
|
+
using the COMPASS model.
|
|
36
|
+
|
|
37
|
+
This class provides functionality to:
|
|
38
|
+
- Load pre-trained COMPASS model checkpoints
|
|
39
|
+
- Process gene expression data (TPM format)
|
|
40
|
+
- Predict ICI treatment response
|
|
41
|
+
- Extract key immune cell populations contributing to predictions
|
|
42
|
+
|
|
43
|
+
The COMPASS model is trained to identify immune cell concepts and pathways
|
|
44
|
+
that are predictive of patient response to checkpoint inhibitor therapy.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
root_path: Optional[str] = None,
|
|
50
|
+
ckp_path: str = "pft_leave_IMVigor210.pt",
|
|
51
|
+
device: str = "cpu",
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
Initializes the COMPASS tool by loading the pre-trained model checkpoint.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
root_path (str, optional): Path to the directory containing model checkpoints.
|
|
58
|
+
If None, uses COMPASS_MODEL_PATH/immune-compass/checkpoint.
|
|
59
|
+
ckp_path (str): Name of the checkpoint file to load.
|
|
60
|
+
Defaults to "pft_leave_IMVigor210.pt" (IMVigor210 cohort).
|
|
61
|
+
device (str): Device for model inference ("cuda" or "cpu"). Defaults to "cuda".
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
FileNotFoundError: If the specified checkpoint file cannot be found.
|
|
65
|
+
Exception: If model loading fails due to compatibility or corruption issues.
|
|
66
|
+
"""
|
|
67
|
+
# Construct model checkpoint path
|
|
68
|
+
if root_path is None:
|
|
69
|
+
compass_model_path = os.getenv("COMPASS_MODEL_PATH")
|
|
70
|
+
if compass_model_path is None:
|
|
71
|
+
raise ValueError("COMPASS_MODEL_PATH environment variable is not set")
|
|
72
|
+
if not os.path.exists(
|
|
73
|
+
os.path.join(compass_model_path, "immune-compass", "checkpoint")
|
|
74
|
+
):
|
|
75
|
+
checkpoint_path = os.path.join(
|
|
76
|
+
compass_model_path, "immune-compass", "checkpoint"
|
|
77
|
+
)
|
|
78
|
+
raise FileNotFoundError(
|
|
79
|
+
f"COMPASS model checkpoint not found at {checkpoint_path}. Please check your COMPASS_MODEL_PATH."
|
|
80
|
+
)
|
|
81
|
+
root_path = os.path.join(compass_model_path, "immune-compass", "checkpoint")
|
|
82
|
+
|
|
83
|
+
self.model_path = os.path.join(root_path, ckp_path)
|
|
84
|
+
self.device = device
|
|
85
|
+
|
|
86
|
+
# Load the pre-trained COMPASS model
|
|
87
|
+
print(f"🛠️ Initializing COMPASS tool from checkpoint: {self.model_path}...")
|
|
88
|
+
self.finetuner = loadcompass(
|
|
89
|
+
self.model_path, weights_only=False, map_location=torch.device(self.device)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Configure device settings for CPU inference if needed
|
|
93
|
+
if self.device == "cpu":
|
|
94
|
+
self.finetuner.device = "cpu"
|
|
95
|
+
|
|
96
|
+
# Display model parameter count for transparency
|
|
97
|
+
self.finetuner.count_parameters()
|
|
98
|
+
print(
|
|
99
|
+
"[COMPASS] Tool initialized successfully (model loaded and ready for predictions)."
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def _get_top_columns_per_row(
|
|
103
|
+
self,
|
|
104
|
+
df: pd.DataFrame,
|
|
105
|
+
top_n: int = 44,
|
|
106
|
+
exclude: Optional[List[str]] = None,
|
|
107
|
+
) -> List[List[Tuple[str, float]]]:
|
|
108
|
+
"""
|
|
109
|
+
Extracts the top-scoring immune cell concepts for each sample from COMPASS output.
|
|
110
|
+
|
|
111
|
+
This method processes the COMPASS cell concept matrix to identify the most
|
|
112
|
+
influential immune cell populations contributing to the prediction for each sample.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
df (pd.DataFrame): DataFrame containing cell concept scores from COMPASS analysis.
|
|
116
|
+
Rows represent samples, columns represent immune cell concepts.
|
|
117
|
+
top_n (int): Maximum number of top concepts to return per sample. Defaults to 44.
|
|
118
|
+
exclude (List[str]): List of column names to exclude from results.
|
|
119
|
+
Defaults to ['CANCER', 'Reference'].
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
List[List[Tuple[str, float]]]: For each sample, a list of tuples containing
|
|
123
|
+
(concept_name, concept_score) sorted by score descending.
|
|
124
|
+
"""
|
|
125
|
+
# Set default excludes safely to avoid mutable default argument
|
|
126
|
+
if exclude is None:
|
|
127
|
+
exclude = ["CANCER", "Reference"]
|
|
128
|
+
# Sort concepts by score (descending) for each sample
|
|
129
|
+
sorted_concepts_indices = [
|
|
130
|
+
row.sort_values(ascending=False).index[:top_n] for _, row in df.iterrows()
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
results = []
|
|
134
|
+
for i, (_, row) in enumerate(df.iterrows()):
|
|
135
|
+
row_concepts = []
|
|
136
|
+
for col in sorted_concepts_indices[i]:
|
|
137
|
+
# Skip excluded columns (e.g., metadata columns)
|
|
138
|
+
if col not in exclude:
|
|
139
|
+
row_concepts.append((col, row[col]))
|
|
140
|
+
results.append(row_concepts)
|
|
141
|
+
return results
|
|
142
|
+
|
|
143
|
+
def predict(
|
|
144
|
+
self,
|
|
145
|
+
gene_expression_data_path: str,
|
|
146
|
+
threshold: float = 0.5,
|
|
147
|
+
batch_size: int = 128,
|
|
148
|
+
) -> Tuple[bool, List[Tuple[str, float]]]:
|
|
149
|
+
"""
|
|
150
|
+
Performs immune checkpoint inhibitor response prediction on gene expression data.
|
|
151
|
+
|
|
152
|
+
This method processes single-sample tumor gene expression data (in TPM format)
|
|
153
|
+
through the COMPASS model to predict treatment response and identify key
|
|
154
|
+
immune cell populations contributing to the prediction.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
gene_expression_data_path (str): Path to the TPM expression data file.
|
|
158
|
+
to their expression levels in Transcripts Per Million (TPM).
|
|
159
|
+
threshold (float): Prediction probability threshold for classifying samples as responders.
|
|
160
|
+
Values ≥ threshold are classified as responders. Defaults to 0.5.
|
|
161
|
+
batch_size (int): Batch size for model inference. Larger values may improve speed
|
|
162
|
+
but require more memory. Defaults to 128.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Tuple[bool, List[Tuple[str, float]]]: A tuple containing:
|
|
166
|
+
- bool: True if predicted as responder (probability ≥ threshold), False otherwise
|
|
167
|
+
- List[Tuple[str, float]]: Top immune cell concepts ranked by importance,
|
|
168
|
+
where each tuple contains (concept_name, concept_score)
|
|
169
|
+
|
|
170
|
+
Raises:
|
|
171
|
+
ValueError: If gene_expression_data is empty or contains invalid values.
|
|
172
|
+
RuntimeError: If model inference fails.
|
|
173
|
+
"""
|
|
174
|
+
# Convert gene expression dictionary to DataFrame format expected by COMPASS
|
|
175
|
+
df_tpm = pd.read_pickle(gene_expression_data_path)
|
|
176
|
+
df_tpm.index.name = "Index" # Required by COMPASS for gene indexing
|
|
177
|
+
|
|
178
|
+
# Extract immune cell concepts and generate predictions using COMPASS model
|
|
179
|
+
# dfct contains cell concept scores, dfpred contains response probabilities
|
|
180
|
+
_, _, dfct = self.finetuner.extract(
|
|
181
|
+
df_tpm, batch_size=batch_size, with_gene_level=True
|
|
182
|
+
)
|
|
183
|
+
_, dfpred = self.finetuner.predict(df_tpm)
|
|
184
|
+
|
|
185
|
+
# Extract and rank the most influential immune cell concepts
|
|
186
|
+
sorted_cell_concepts = self._get_top_columns_per_row(dfct)
|
|
187
|
+
|
|
188
|
+
# Classify sample as responder based on prediction probability threshold
|
|
189
|
+
# dfpred.iloc[:, 1] contains the responder probability (column 1)
|
|
190
|
+
responder = dfpred.iloc[:, 1].max() >= threshold
|
|
191
|
+
|
|
192
|
+
return responder, sorted_cell_concepts[0] if sorted_cell_concepts else []
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@server.tool()
|
|
196
|
+
async def run_compass_prediction(
|
|
197
|
+
gene_expression_data_path: str,
|
|
198
|
+
threshold: float = 0.5,
|
|
199
|
+
root_path: Optional[str] = None,
|
|
200
|
+
):
|
|
201
|
+
"""
|
|
202
|
+
MCP Tool: Predicts immune checkpoint inhibitor (ICI) response using COMPASS model.
|
|
203
|
+
|
|
204
|
+
This tool analyzes single-sample tumor gene expression data to predict patient
|
|
205
|
+
responsiveness to immune checkpoint inhibitor therapy. The COMPASS model leverages
|
|
206
|
+
immune cell concept analysis to provide both a binary prediction and interpretable
|
|
207
|
+
insights into the immune microenvironment factors driving the prediction.
|
|
208
|
+
|
|
209
|
+
Clinical Context:
|
|
210
|
+
- Designed for precision oncology applications
|
|
211
|
+
- Helps identify patients likely to benefit from ICI therapy
|
|
212
|
+
- Provides mechanistic insights through immune cell population analysis
|
|
213
|
+
- Based on validated cohorts including IMVigor210 (urothelial carcinoma)
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
gene_expression_data_path (str): Path to the TPM expression data file.
|
|
217
|
+
Keys should be standard gene symbols (e.g., "CD274", "PDCD1", "CTLA4")
|
|
218
|
+
Values should be normalized expression in TPM (Transcripts Per Million).
|
|
219
|
+
Minimum ~100 genes recommended for reliable predictions.
|
|
220
|
+
threshold (float): Probability threshold for responder classification (0.0-1.0).
|
|
221
|
+
Values ≥ threshold classify sample as likely responder.
|
|
222
|
+
Default 0.5 provides balanced sensitivity/specificity.
|
|
223
|
+
Consider lower thresholds (~0.3) for higher sensitivity.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
dict: Structured prediction results containing:
|
|
227
|
+
- 'prediction' (dict): Core prediction results with:
|
|
228
|
+
* 'is_responder' (bool): True if predicted responder (probability ≥ threshold)
|
|
229
|
+
* 'top_concepts' (list): Ranked immune cell concepts as dicts with:
|
|
230
|
+
- 'concept' (str): Name of immune cell population/concept
|
|
231
|
+
- 'score' (float): Importance score for this concept
|
|
232
|
+
- 'context_info' (list): Human-readable analysis summary and status messages
|
|
233
|
+
- 'error' (str, optional): Error description if prediction failed
|
|
234
|
+
"""
|
|
235
|
+
# Generate unique request ID for tracking and logging
|
|
236
|
+
request_id = str(uuid.uuid4())[:8]
|
|
237
|
+
print(f"[{request_id}] Received COMPASS ICI response prediction request")
|
|
238
|
+
|
|
239
|
+
# Initialize global COMPASS tool instance for MCP server
|
|
240
|
+
# This instance will be used by the MCP tool function to serve predictions
|
|
241
|
+
try:
|
|
242
|
+
compass_tool = CompassTool(root_path=root_path)
|
|
243
|
+
print("✅ COMPASS Prediction tool instance created and ready for MCP server")
|
|
244
|
+
except Exception as e:
|
|
245
|
+
print(f"❌ Error creating COMPASS Prediction tool: {str(e)}")
|
|
246
|
+
print(
|
|
247
|
+
"Please ensure COMPASS_MODEL_PATH is correctly set and model checkpoint exists."
|
|
248
|
+
)
|
|
249
|
+
raise e
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
# Brief async pause to allow for proper request handling
|
|
253
|
+
await asyncio.sleep(0.1)
|
|
254
|
+
|
|
255
|
+
# Validate input parameters
|
|
256
|
+
if (
|
|
257
|
+
not isinstance(gene_expression_data_path, str)
|
|
258
|
+
or not gene_expression_data_path
|
|
259
|
+
):
|
|
260
|
+
raise ValueError(
|
|
261
|
+
"Input 'gene_expression_data' must be a non-empty dictionary mapping gene symbols to TPM values."
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
if not (0.0 <= threshold <= 1.0):
|
|
265
|
+
raise ValueError(f"Threshold must be between 0.0 and 1.0, got {threshold}")
|
|
266
|
+
|
|
267
|
+
print(
|
|
268
|
+
f"[{request_id}] Processing {len(gene_expression_data_path)} genes with threshold {threshold}"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# Execute COMPASS model prediction
|
|
272
|
+
is_responder, top_concepts = compass_tool.predict(
|
|
273
|
+
gene_expression_data_path, threshold=threshold
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# Convert concept tuples to JSON-serializable format
|
|
277
|
+
serializable_concepts = [
|
|
278
|
+
{"concept": concept, "score": float(score)}
|
|
279
|
+
for concept, score in top_concepts
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
# Log successful completion
|
|
283
|
+
response_status = "RESPONDER" if is_responder else "NON-RESPONDER"
|
|
284
|
+
print(
|
|
285
|
+
f"[{request_id}] ✅ COMPASS prediction completed: {response_status} ({len(serializable_concepts)} concepts)"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
"prediction": {
|
|
290
|
+
"is_responder": is_responder,
|
|
291
|
+
"top_concepts": serializable_concepts,
|
|
292
|
+
},
|
|
293
|
+
"context_info": [
|
|
294
|
+
"COMPASS prediction completed successfully.",
|
|
295
|
+
f"Sample classified as: {'RESPONDER' if is_responder else 'NON-RESPONDER'}",
|
|
296
|
+
f"Analysis based on {len(gene_expression_data_path)} input genes.",
|
|
297
|
+
f"Top {len(serializable_concepts)} immune cell concepts identified.",
|
|
298
|
+
],
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
except (ValueError, FileNotFoundError) as e:
|
|
302
|
+
error_message = f"COMPASS prediction validation error: {str(e)}"
|
|
303
|
+
print(f"[{request_id}] {error_message}")
|
|
304
|
+
return {
|
|
305
|
+
"error": error_message,
|
|
306
|
+
"context_info": ["Please check input data format and model availability."],
|
|
307
|
+
}
|
|
308
|
+
except Exception as e:
|
|
309
|
+
error_message = f"Unexpected error during COMPASS prediction: {str(e)}"
|
|
310
|
+
print(f"[{request_id}] {error_message}")
|
|
311
|
+
return {
|
|
312
|
+
"error": error_message,
|
|
313
|
+
"context_info": ["Internal server error occurred during prediction."],
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
if __name__ == "__main__":
|
|
318
|
+
print("Starting MCP server for COMPASS Immune Response Prediction Tool...")
|
|
319
|
+
print("Model: COMPASS (COMprehensive Pathway Analysis for Single-cell Sequencing)")
|
|
320
|
+
print("Application: Immune Checkpoint Inhibitor Response Prediction")
|
|
321
|
+
print("Server: FastMCP with streamable HTTP transport")
|
|
322
|
+
print("Port: 7003 (configured to avoid conflicts with other biomedical tools)")
|
|
323
|
+
|
|
324
|
+
# Launch the MCP server with COMPASS prediction capabilities
|
|
325
|
+
server.run(
|
|
326
|
+
transport="streamable-http", host="0.0.0.0", port=7003, stateless_http=True
|
|
327
|
+
)
|