Source code for tooluniverse.enamine_tool

"""
Enamine tool for ToolUniverse.

Enamine is a leading supplier of make-on-demand compounds, building
blocks, and screening libraries for drug discovery.

Website: https://enamine.net/
"""

import requests
from typing import Dict, Any, Optional, List
from .base_tool import BaseTool
from .tool_registry import register_tool

# Enamine base URL
ENAMINE_BASE_URL = "https://enamine.net"
# Enamine Store API
ENAMINE_STORE_URL = "https://new.enaminestore.com/api"


[docs] @register_tool("EnamineTool") class EnamineTool(BaseTool): """ Tool for searching Enamine compound libraries. Enamine provides: - Make-on-demand compounds (REAL database) - Building blocks - Screening libraries - Fragment libraries Note: Full API access may require registration. Basic search functionality available without API key. """
[docs] def __init__(self, tool_config: Dict[str, Any]): super().__init__(tool_config) self.timeout: int = tool_config.get("timeout", 30) self.parameter = tool_config.get("parameter", {})
[docs] def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """Execute Enamine query based on operation type.""" operation = arguments.get("operation", "") if operation == "search_catalog": return self._search_catalog(arguments) elif operation == "get_compound": return self._get_compound(arguments) elif operation == "search_smiles": return self._search_smiles(arguments) elif operation == "get_libraries": return self._get_libraries(arguments) else: return { "status": "error", "error": f"Unknown operation: {operation}. Supported: search_catalog, get_compound, search_smiles, get_libraries", }
[docs] def _search_catalog(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Search Enamine catalog by keyword or compound name. Args: arguments: Dict containing: - query: Search query - catalog: Catalog type (REAL, BB, SCR) - default: all """ query = arguments.get("query", "") if not query: return {"status": "error", "error": "Missing required parameter: query"} catalog = arguments.get("catalog", "all") try: # Try Enamine Store API response = requests.get( f"{ENAMINE_STORE_URL}/catalog/search", params={"q": query, "catalog": catalog}, timeout=self.timeout, headers={ "User-Agent": "ToolUniverse/Enamine", "Accept": "application/json", }, ) if response.status_code == 200 and "json" in response.headers.get( "Content-Type", "" ): data = response.json() results = ( data if isinstance(data, list) else data.get("compounds", data.get("results", [])) ) return { "status": "success", "data": { "query": query, "catalog": catalog, "results": results[:20], "count": len(results), }, "metadata": {"source": "Enamine"}, } # Return search guidance return { "status": "success", "data": { "query": query, "catalog": catalog, "results": [], "search_url": f"{ENAMINE_BASE_URL}/compound-search/quick?q={query}", "note": "Visit search_url to search Enamine catalog. REAL database contains 37B+ make-on-demand compounds.", }, "metadata": {"source": "Enamine"}, } except requests.exceptions.RequestException as e: return {"status": "error", "error": f"Request failed: {str(e)}"} except Exception as e: return {"status": "error", "error": f"Unexpected error: {str(e)}"}
[docs] def _get_compound(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Get compound by Enamine ID. Args: arguments: Dict containing: - enamine_id: Enamine compound ID (e.g., Z1234567890) """ enamine_id = arguments.get("enamine_id", "") if not enamine_id: return { "status": "error", "error": "Missing required parameter: enamine_id", } try: response = requests.get( f"{ENAMINE_STORE_URL}/catalog/compound/{enamine_id}", timeout=self.timeout, headers={ "User-Agent": "ToolUniverse/Enamine", "Accept": "application/json", }, ) if response.status_code == 200 and "json" in response.headers.get( "Content-Type", "" ): data = response.json() return { "status": "success", "data": data, "metadata": { "source": "Enamine", "enamine_id": enamine_id, }, } # Return compound URL return { "status": "success", "data": { "enamine_id": enamine_id, "url": f"{ENAMINE_BASE_URL}/compound/{enamine_id}", "note": "Visit url to view compound details and ordering information", }, "metadata": {"source": "Enamine", "enamine_id": enamine_id}, } except requests.exceptions.RequestException as e: return {"status": "error", "error": f"Request failed: {str(e)}"} except Exception as e: return {"status": "error", "error": f"Unexpected error: {str(e)}"}
[docs] def _search_smiles(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Search Enamine by SMILES structure. Args: arguments: Dict containing: - smiles: SMILES string - search_type: exact, substructure, similarity (default: similarity) """ smiles = arguments.get("smiles", "") if not smiles: return {"status": "error", "error": "Missing required parameter: smiles"} search_type = arguments.get("search_type", "similarity") try: response = requests.post( f"{ENAMINE_STORE_URL}/catalog/structure-search", json={"smiles": smiles, "type": search_type}, timeout=self.timeout, headers={ "User-Agent": "ToolUniverse/Enamine", "Accept": "application/json", }, ) if response.status_code == 200 and "json" in response.headers.get( "Content-Type", "" ): data = response.json() results = data if isinstance(data, list) else data.get("compounds", []) return { "status": "success", "data": { "query_smiles": smiles, "search_type": search_type, "results": results[:20], "count": len(results), }, "metadata": {"source": "Enamine"}, } # Return search guidance import urllib.parse encoded_smiles = urllib.parse.quote(smiles) return { "status": "success", "data": { "query_smiles": smiles, "search_type": search_type, "results": [], "search_url": f"{ENAMINE_BASE_URL}/compound-search/structure?smiles={encoded_smiles}", "note": "Visit search_url to perform structure search on Enamine catalog", }, "metadata": {"source": "Enamine"}, } except requests.exceptions.RequestException as e: return {"status": "error", "error": f"Request failed: {str(e)}"} except Exception as e: return {"status": "error", "error": f"Unexpected error: {str(e)}"}
[docs] def _get_libraries(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Get available Enamine compound libraries. Returns information about Enamine's screening libraries. """ try: response = requests.get( f"{ENAMINE_STORE_URL}/libraries", timeout=self.timeout, headers={ "User-Agent": "ToolUniverse/Enamine", "Accept": "application/json", }, ) if response.status_code == 200 and "json" in response.headers.get( "Content-Type", "" ): data = response.json() return { "status": "success", "data": {"libraries": data}, "metadata": {"source": "Enamine"}, } # Return known libraries info return { "status": "success", "data": { "libraries": [ { "name": "REAL Database", "description": "37+ billion make-on-demand compounds", "url": f"{ENAMINE_BASE_URL}/compound-collections/real-compounds", }, { "name": "Building Blocks", "description": "300,000+ in-stock building blocks for synthesis", "url": f"{ENAMINE_BASE_URL}/building-blocks", }, { "name": "Screening Compounds", "description": "3M+ ready-to-ship screening compounds", "url": f"{ENAMINE_BASE_URL}/compound-collections/screening-compounds", }, { "name": "Fragment Library", "description": "Diverse fragment collection for FBDD", "url": f"{ENAMINE_BASE_URL}/compound-collections/fragments", }, { "name": "Covalent Library", "description": "Compounds with reactive warheads", "url": f"{ENAMINE_BASE_URL}/compound-collections/covalent-screening", }, ], "main_url": f"{ENAMINE_BASE_URL}/compound-collections", }, "metadata": {"source": "Enamine"}, } except requests.exceptions.RequestException as e: return {"status": "error", "error": f"Request failed: {str(e)}"} except Exception as e: return {"status": "error", "error": f"Unexpected error: {str(e)}"}