Source code for tooluniverse.dbaasp_tool

"""DBAASP antimicrobial peptide tools (live REST, keyless).

DBAASP — Database of Antimicrobial Activity and Structure of Peptides
(https://dbaasp.org). Two tools:

- ``DBAASPGetPeptideTool`` (DBAASP_get_peptide): full peptide record by ID.
- ``DBAASPSearchPeptidesTool`` (DBAASP_search_peptides): paginated search/filter.

The public API is keyless, with no login or CAPTCHA. The OpenAPI 3.0 spec is
served at https://dbaasp.org/v3/api-docs. Parameter names are exact (e.g.
``sequence.value`` not ``sequence``) and pagination uses ``limit``/``offset``.
"""

from typing import Any, Dict

import requests

from .base_tool import BaseTool
from .tool_registry import register_tool

_BASE_URL = "https://dbaasp.org/peptides"
_TIMEOUT = 30
_HEADERS = {"Accept": "application/json"}


def _err(message: str, **extra: Any) -> Dict[str, Any]:
    out: Dict[str, Any] = {"status": "error", "error": message}
    out.update(extra)
    return out


[docs] @register_tool( "DBAASPGetPeptideTool", config={ "name": "DBAASP_get_peptide", "type": "DBAASPGetPeptideTool", "description": ( "Get a full DBAASP antimicrobial peptide record by numeric peptide " "ID. Returns the sequence, N/C-terminus modifications, structure " "(PDB / 3D model), per-target activity measurements (MIC values " "with units, medium, CFU), hemolytic/cytotoxic activity, " "antibiofilm activity, source genes, UniProt cross-references, " "SMILES, and references. Keyless DBAASP v3/v4 API." ), "parameter": { "type": "object", "properties": { "peptideId": { "type": ["integer", "string"], "description": ( "DBAASP numeric peptide ID. Example: 107 (Dermaseptin " "S4 (1-16)[M4K], sequence ALWKTLLKKVLKAAAK). Accepts a " "bare integer or a 'DBAASPR_107'-style ID (digits are " "extracted)." ), } }, "required": ["peptideId"], }, }, ) class DBAASPGetPeptideTool(BaseTool): """Fetch a single DBAASP peptide record by ID."""
[docs] def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: raw = arguments.get("peptideId") if raw is None or (isinstance(raw, str) and not raw.strip()): return _err("peptideId is required") # Extract digits to tolerate 'DBAASPR_107' / '107' / 107 pid = "".join(ch for ch in str(raw) if ch.isdigit()) if not pid: return _err(f"Invalid peptideId: {raw!r} (no numeric ID found)") url = f"{_BASE_URL}/{pid}" try: resp = requests.get(url, headers=_HEADERS, timeout=_TIMEOUT) except requests.exceptions.RequestException as exc: return _err(f"Request to DBAASP failed: {exc}", url=url) if resp.status_code == 404: return _err(f"No DBAASP peptide found with ID {pid}", url=url) if resp.status_code != 200: return _err( f"DBAASP returned HTTP {resp.status_code}", url=url, response_snippet=(resp.text or "")[:200], ) try: data = resp.json() except ValueError: return _err( "DBAASP returned a non-JSON response", url=url, response_snippet=(resp.text or "")[:200], ) if not isinstance(data, dict) or not data.get("id"): return _err(f"No DBAASP peptide record for ID {pid}", url=url) target_activities = data.get("targetActivities") or [] return { "status": "success", "data": data, "metadata": { "source": "DBAASP v3/v4 (dbaasp.org)", "url": url, "peptide_id": data.get("id"), "dbaasp_id": data.get("dbaaspId"), "name": data.get("name"), "sequence": data.get("sequence"), "sequence_length": data.get("sequenceLength"), "target_activity_count": len(target_activities), }, }
[docs] @register_tool( "DBAASPSearchPeptidesTool", config={ "name": "DBAASP_search_peptides", "type": "DBAASPSearchPeptidesTool", "description": ( "Search/filter DBAASP antimicrobial peptides by sequence " "(exact/substring), peptide name, target organism/species, target " "group, sequence length, synthesis type, source kingdom, UniProt " "xref, or DBAASP ID. Returns a paginated list of matching AMPs " "with a total count. Enables sequence->AMP lookup, 'what peptides " "are active against organism X', and target-organism activity " "surveys. Keyless DBAASP v3/v4 API; pagination is limit/offset." ), "parameter": { "type": "object", "properties": { "sequence": { "type": "string", "description": ( "Amino-acid sequence to match (single-letter code). " "Pair with sequence_option ('full' for exact, 'part' " "for substring). Example: 'GLFDIVKKVVGALGSL'." ), }, "sequence_option": { "type": "string", "description": ( "How to match 'sequence': 'full' (exact, default) or " "'part' (substring). Maps to DBAASP sequence.option." ), }, "name": { "type": "string", "description": "Peptide name substring. Example: 'Magainin'.", }, "target_species": { "type": "string", "description": ( "Target organism / species name. Example: " "'Staphylococcus aureus'. Maps to targetSpecies.value." ), }, "target_group": { "type": "string", "description": ( "Target group (e.g. 'Gram+', 'Gram-'). Maps to " "targetGroup.value." ), }, "sequence_length": { "type": ["integer", "string"], "description": ( "Exact peptide length filter. Maps to sequenceLength.value." ), }, "synthesis_type": { "type": "string", "description": ( "Synthesis type, e.g. 'Ribosomal', 'Synthetic'. Maps " "to synthesisType.value." ), }, "kingdom": { "type": "string", "description": ( "Source kingdom/taxonomy filter. Maps to kingdom.value." ), }, "uniprot": { "type": "string", "description": ( "UniProt accession cross-reference. Maps to uniprot.value." ), }, "dbaasp_id": { "type": ["integer", "string"], "description": "DBAASP numeric ID filter. Maps to id.value.", }, "limit": { "type": "integer", "description": "Max results to return (default 25).", }, "offset": { "type": "integer", "description": "Result offset for pagination (default 0).", }, }, "required": [], }, }, ) class DBAASPSearchPeptidesTool(BaseTool): """Search / filter DBAASP peptides; returns paginated list + total count.""" # user-arg -> DBAASP API query parameter. "sequence_option" only refines a # sequence match, so it is excluded from the substantive-filter check below. _PARAM_MAP = { "sequence": "sequence.value", "sequence_option": "sequence.option", "name": "name.value", "target_species": "targetSpecies.value", "target_group": "targetGroup.value", "sequence_length": "sequenceLength.value", "synthesis_type": "synthesisType.value", "kingdom": "kingdom.value", "uniprot": "uniprot.value", "dbaasp_id": "id.value", } _FILTER_KEYS = tuple(k for k in _PARAM_MAP if k != "sequence_option")
[docs] def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: arguments = arguments or {} def _provided(key: str) -> bool: val = arguments.get(key) return val is not None and str(val).strip() != "" params: Dict[str, Any] = {} for arg_key, api_key in self._PARAM_MAP.items(): if _provided(arg_key): params[api_key] = arguments[arg_key] # Require at least one substantive filter (not just sequence_option). if not any(_provided(k) for k in self._FILTER_KEYS): return _err( "At least one search filter is required (e.g. sequence, name, " "target_species, target_group, sequence_length, synthesis_type, " "kingdom, uniprot, or dbaasp_id)." ) # Default the sequence match option when a sequence is given. if arguments.get("sequence") and "sequence.option" not in params: params["sequence.option"] = "full" def _as_int(value: Any, default: int) -> int: try: return int(value) except (TypeError, ValueError): return default params["limit"] = _as_int(arguments.get("limit", 25), 25) params["offset"] = _as_int(arguments.get("offset", 0), 0) try: resp = requests.get( _BASE_URL, params=params, headers=_HEADERS, timeout=_TIMEOUT ) except requests.exceptions.RequestException as exc: return _err(f"Request to DBAASP failed: {exc}") if resp.status_code != 200: return _err( f"DBAASP returned HTTP {resp.status_code}", url=resp.url, response_snippet=(resp.text or "")[:200], ) try: payload = resp.json() except ValueError: return _err( "DBAASP returned a non-JSON response", url=resp.url, response_snippet=(resp.text or "")[:200], ) if not isinstance(payload, dict): return _err("Unexpected DBAASP response shape", url=resp.url) results = payload.get("data") or [] total = payload.get("totalCount", 0) return { "status": "success", "data": results, "metadata": { "source": "DBAASP v3/v4 (dbaasp.org)", "url": resp.url, "total_count": total, "returned_count": len(results), "limit": params["limit"], "offset": params["offset"], }, }