Source code for tooluniverse.openaire_tool
import requests
from .base_tool import BaseTool
from .tool_registry import register_tool
[docs]
@register_tool("OpenAIRETool")
class OpenAIRETool(BaseTool):
"""
Search OpenAIRE Explore for research products (publications by default).
Parameters (arguments):
query (str): Query string
max_results (int): Max number of results (default 10, max 100)
type (str): product type filter: publications | datasets | software
"""
[docs]
def __init__(self, tool_config):
super().__init__(tool_config)
self.base_url = "https://api.openaire.eu/search/publications"
[docs]
def run(self, arguments=None):
arguments = arguments or {}
query = arguments.get("query")
max_results = int(arguments.get("max_results", 10))
prod_type = arguments.get("type", "publications")
if not query:
return {"error": "`query` parameter is required."}
endpoint = self._endpoint_for_type(prod_type)
if endpoint is None:
return {
"error": ("Unsupported type. Use publications/datasets/software."),
}
params = {
"format": "json",
"size": max(1, min(max_results, 100)),
"query": query,
}
try:
resp = requests.get(endpoint, params=params, timeout=20)
resp.raise_for_status()
data = resp.json()
except requests.RequestException as e:
return {
"error": "Network/API error calling OpenAIRE",
"reason": str(e),
}
except ValueError:
return {"error": "Failed to decode OpenAIRE response as JSON"}
return self._normalize(data, prod_type)
def _endpoint_for_type(self, prod_type):
if prod_type == "publications":
return "https://api.openaire.eu/search/publications"
if prod_type == "datasets":
return "https://api.openaire.eu/search/datasets"
if prod_type == "software":
return "https://api.openaire.eu/search/software"
return None
def _normalize(self, data, prod_type):
results = []
# OpenAIRE JSON has a root 'response' with 'results' → 'result' list
try:
items = data.get("response", {}).get("results", {}).get("result", [])
except Exception:
items = []
for it in items:
# header may contain identifiers, not used presently
_ = it.get("header", {}) if isinstance(it.get("header"), dict) else {}
metadata = (
it.get("metadata", {}) if isinstance(it.get("metadata"), dict) else {}
)
title = None
authors = []
year = None
doi = None
url = None
# Titles can be nested in 'oaf:result' structure
result_obj = metadata.get("oaf:result", {})
if isinstance(result_obj, dict):
t = result_obj.get("title")
if isinstance(t, list) and t:
title = t[0].get("$")
elif isinstance(t, dict):
title = t.get("$")
# Authors
creators = result_obj.get("creator", [])
if isinstance(creators, list):
for c in creators:
name = c.get("$")
if name:
authors.append(name)
# Year
date_obj = result_obj.get("dateofacceptance") or result_obj.get("date")
if isinstance(date_obj, dict):
year = date_obj.get("year") or date_obj.get("$")
# DOI and URL
pid = result_obj.get("pid", [])
if isinstance(pid, list):
for p in pid:
if p.get("@classid") == "doi":
doi = p.get("$")
bestaccessright = result_obj.get("bestaccessright", {})
if isinstance(bestaccessright, dict):
url_value = bestaccessright.get("$")
if url_value:
url = url_value
results.append(
{
"title": title,
"authors": authors,
"year": year,
"doi": doi,
"url": url,
"type": prod_type,
"source": "OpenAIRE",
}
)
return results