Source code for tooluniverse.swissmodel_tool
# swissmodel_tool.py
"""
SWISS-MODEL Repository REST API tool for ToolUniverse.
SWISS-MODEL Repository is a database of annotated 3D protein structure
models generated by the SWISS-MODEL homology-modelling pipeline.
It provides pre-computed models for UniProt entries.
API: https://swissmodel.expasy.org/repository
No authentication required. Free for academic/research use.
"""
import requests
from typing import Dict, Any
from .base_tool import BaseTool
from .tool_registry import register_tool
SWISSMODEL_BASE_URL = "https://swissmodel.expasy.org/repository"
[docs]
@register_tool("SwissModelTool")
class SwissModelTool(BaseTool):
"""
Tool for querying the SWISS-MODEL Repository.
Provides pre-computed protein homology models for UniProt entries,
including template structures, sequence coverage, quality metrics,
and download coordinates.
No authentication required.
"""
[docs]
def __init__(self, tool_config: Dict[str, Any]):
super().__init__(tool_config)
self.timeout = tool_config.get("timeout", 30)
self.endpoint_type = tool_config.get("fields", {}).get(
"endpoint_type", "get_models"
)
[docs]
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Execute the SWISS-MODEL API call."""
try:
return self._dispatch(arguments)
except requests.exceptions.Timeout:
return {
"error": f"SWISS-MODEL API request timed out after {self.timeout} seconds"
}
except requests.exceptions.ConnectionError:
return {
"error": "Failed to connect to SWISS-MODEL API. Check network connectivity."
}
except requests.exceptions.HTTPError as e:
return {"error": f"SWISS-MODEL API HTTP error: {e.response.status_code}"}
except Exception as e:
return {"error": f"Unexpected error querying SWISS-MODEL: {str(e)}"}
[docs]
def _dispatch(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Route to appropriate endpoint based on config."""
if self.endpoint_type == "get_models":
return self._get_models(arguments)
elif self.endpoint_type == "get_model_summary":
return self._get_model_summary(arguments)
else:
return {"error": f"Unknown endpoint_type: {self.endpoint_type}"}
[docs]
def _get_models(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Get all available homology models for a UniProt accession."""
uniprot_id = arguments.get("uniprot_id", "")
if not uniprot_id:
return {
"error": "uniprot_id parameter is required (e.g., 'P04637' for human p53)"
}
url = f"{SWISSMODEL_BASE_URL}/uniprot/{uniprot_id}.json"
response = requests.get(url, timeout=self.timeout)
response.raise_for_status()
raw = response.json()
result_data = raw.get("result", {})
# Extract models
models = []
for s in result_data.get("structures", []):
model = {
"template": s.get("template", ""),
"method": s.get("method", ""),
"coverage": s.get("coverage"),
"from_residue": s.get("from"),
"to_residue": s.get("to"),
"created_date": s.get("created_date", ""),
"provider": s.get("provider", ""),
}
# Quality metrics
if "qmean" in s:
qmean = s["qmean"]
if isinstance(qmean, dict):
model["qmean_global"] = qmean.get("qmean4_global_score")
model["qmean_z_score"] = qmean.get("qmean_z_score")
# Complex partners if any
complex_with = s.get("in_complex_with", {})
if complex_with:
partners = []
for chain_id, chain_proteins in complex_with.items():
if isinstance(chain_proteins, list):
for p in chain_proteins:
if isinstance(p, dict):
partners.append(
{
"chain": chain_id,
"uniprot_ac": p.get("uniprot_ac", ""),
"description": p.get("description", ""),
}
)
if partners:
model["complex_partners"] = partners[:10]
models.append(model)
result = {
"uniprot_id": uniprot_id,
"sequence_length": result_data.get("sequence_length"),
"crc64": result_data.get("crc64"),
"model_count": len(models),
"models": models[:30],
}
return {
"data": result,
"metadata": {
"source": "SWISS-MODEL Repository",
"api_version": raw.get("api_version", ""),
"query": uniprot_id,
"endpoint": "get_models",
},
}
[docs]
def _get_model_summary(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Get a summary of the best available model for a UniProt accession."""
uniprot_id = arguments.get("uniprot_id", "")
if not uniprot_id:
return {
"error": "uniprot_id parameter is required (e.g., 'P04637' for human p53)"
}
url = f"{SWISSMODEL_BASE_URL}/uniprot/{uniprot_id}.json"
response = requests.get(url, timeout=self.timeout)
response.raise_for_status()
raw = response.json()
result_data = raw.get("result", {})
structures = result_data.get("structures", [])
# Find best model (highest coverage)
best = None
best_coverage = 0
for s in structures:
cov = s.get("coverage", 0) or 0
if cov > best_coverage:
best_coverage = cov
best = s
summary = {
"uniprot_id": uniprot_id,
"sequence_length": result_data.get("sequence_length"),
"total_models": len(structures),
}
if best:
summary["best_model"] = {
"template": best.get("template", ""),
"method": best.get("method", ""),
"coverage": best.get("coverage"),
"from_residue": best.get("from"),
"to_residue": best.get("to"),
"created_date": best.get("created_date", ""),
"coordinates_url": best.get("coordinates", ""),
}
if "qmean" in best and isinstance(best["qmean"], dict):
summary["best_model"]["qmean_global"] = best["qmean"].get(
"qmean4_global_score"
)
else:
summary["best_model"] = None
# Methods summary
methods = {}
for s in structures:
m = s.get("method", "Unknown")
methods[m] = methods.get(m, 0) + 1
summary["methods_distribution"] = methods
return {
"data": summary,
"metadata": {
"source": "SWISS-MODEL Repository",
"query": uniprot_id,
"endpoint": "get_model_summary",
},
}