Source code for tooluniverse.zenodo_tool

import requests
from typing import Any, Dict
from .base_tool import BaseTool
from .base_rest_tool import BaseRESTTool
from .tool_registry import register_tool


[docs] @register_tool("ZenodoRESTTool") class ZenodoRESTTool(BaseRESTTool): """Generic REST tool for Zenodo API endpoints."""
[docs] def _get_param_mapping(self) -> Dict[str, str]: """Map Zenodo-specific parameter names.""" return { "query": "q", # query -> q "limit": "size", # limit -> size "community": "communities", # community -> communities }
[docs] def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: if self.tool_config.get("name") != "Zenodo_search_records": return super().run(arguments) arguments = dict(arguments or {}) limit = arguments.get("limit", 10) page = arguments.get("page", 1) if ( isinstance(limit, bool) or not isinstance(limit, int) or not 1 <= limit <= 200 ): return { "status": "error", "error": "limit must be an integer between 1 and 200", "parameter": "limit", } if isinstance(page, bool) or not isinstance(page, int) or page < 1: return { "status": "error", "error": "page must be a positive integer", "parameter": "page", } arguments["limit"] = limit arguments["page"] = page result = super().run(arguments) if result.get("status") != "success": return result data = result.get("data") or {} hits_block = data.get("hits") or {} if isinstance(data, dict) else {} hits = hits_block.get("hits") or [] if isinstance(hits_block, dict) else [] total = hits_block.get("total") if isinstance(hits_block, dict) else None if isinstance(total, dict): total = total.get("value") total_is_exact = isinstance(total, int) and not isinstance(total, bool) returned = len(hits) if isinstance(hits, list) else 0 links = data.get("links") or {} if isinstance(data, dict) else {} has_more = bool(links.get("next")) if total_is_exact: has_more = page * limit < total result["metadata"] = { "page": page, "limit": limit, "returned": returned, "total_results": total if total_is_exact else None, "total_results_is_exact": total_is_exact, "has_more": has_more, "next_page": page + 1 if has_more else None, "result_set_complete": page == 1 and total_is_exact and returned == total, } return result
[docs] def _process_response( self, response: requests.Response, url: str ) -> Dict[str, Any]: """Process Zenodo API response with search result handling.""" data = response.json() # Handle extract_path for nested data (e.g., files from record) extract_path = self.tool_config.get("fields", {}).get("extract_path") if extract_path and isinstance(data, dict): data = data.get(extract_path, data) # Build result result = { "status": "success", "data": data, "url": url, } # Add count - handle both list and search results if isinstance(data, list): result["count"] = len(data) elif isinstance(data, dict) and "hits" in data: # For search results result["count"] = len(data.get("hits", {}).get("hits", [])) return result