Source code for tooluniverse.allen_cell_types_tool
"""
Allen Cell Types tool for ToolUniverse — single-neuron ephys/morphology specimens.
The Allen Cell Types Database catalogs individual neurons characterized by
electrophysiology, morphology, and transcriptomics (human + mouse). This wraps the
Brain-Map RMA query for ApiCellTypesSpecimenDetail. It is distinct from TU's existing
AllenBrain tools, which cover gene-expression datasets and brain structures.
API: http://api.brain-map.org/api/v2/data/query.json (public, no authentication, RMA)
"""
from typing import Any, Dict, List, Optional
import requests
from .base_tool import BaseTool
from .tool_registry import register_tool
BRAINMAP_QUERY = "http://api.brain-map.org/api/v2/data/query.json"
_MODEL = "model::ApiCellTypesSpecimenDetail"
[docs]
@register_tool("AllenCellTypesSpecimensTool")
class AllenCellTypesSpecimensTool(BaseTool):
"""Search Allen Cell Types single-neuron specimens (ephys/morphology)."""
[docs]
def __init__(self, tool_config: Dict[str, Any]):
super().__init__(tool_config)
self.timeout = tool_config.get("fields", {}).get("timeout", 30)
[docs]
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
criteria = _MODEL
filters: List[str] = []
species = (arguments.get("species") or "").strip()
if species:
filters.append(f"donor__species$eq'{species}'")
structure = (arguments.get("structure") or "").strip()
if structure:
filters.append(f"structure__name$il'%{structure}%'")
if filters:
criteria += ",rma::criteria," + ",".join(f"[{f}]" for f in filters)
try:
num_rows = max(1, min(int(arguments.get("limit") or 20), 100))
except (TypeError, ValueError):
num_rows = 20
params = {"criteria": criteria, "num_rows": num_rows}
try:
resp = requests.get(
BRAINMAP_QUERY,
params=params,
headers={"Accept": "application/json"},
timeout=self.timeout,
)
resp.raise_for_status()
payload = resp.json()
except requests.exceptions.Timeout:
return {
"status": "error",
"error": f"Allen Brain-Map request timed out after {self.timeout}s",
}
except requests.exceptions.RequestException as e:
return {"status": "error", "error": f"Allen Brain-Map request failed: {e}"}
except ValueError:
return {
"status": "error",
"error": "Allen Brain-Map returned a non-JSON response",
}
if not payload.get("success", False):
return {
"status": "error",
"error": f"Allen Brain-Map query error: {payload.get('msg')}",
}
rows = payload.get("msg", []) or []
return {
"status": "success",
"data": [self._summarize(r) for r in rows if isinstance(r, dict)],
"metadata": {
"total_available": payload.get("total_rows"),
"returned": len(rows),
"query": {"species": species or None, "structure": structure or None},
"source": "Allen Cell Types Database",
},
}
[docs]
@staticmethod
def _summarize(r: Dict[str, Any]) -> Dict[str, Any]:
def clean(v: Any) -> Optional[str]:
return v.strip('"') if isinstance(v, str) else v
return {
"specimen_name": clean(r.get("name")),
"species": clean(r.get("donor__species")),
"sex": clean(r.get("donor__sex")),
"age": clean(r.get("donor__age")),
"disease_state": clean(r.get("donor__disease_state")),
"brain_structure": clean(r.get("structure__name")),
"transgenic_line": clean(r.get("line_name")) or None,
"avg_firing_rate": r.get("ef__avg_firing_rate"),
"input_resistance": r.get("ef__ri"),
"tau": r.get("ef__tau"),
"upstroke_downstroke_ratio": r.get(
"ef__upstroke_downstroke_ratio_long_square"
),
"has_reconstruction": r.get("nr__reconstruction_type") not in (None, ""),
}