Spaces:
Sleeping
Sleeping
| """ | |
| Agent 3: PAPER AGENT | |
| --------------------- | |
| Job: Given a topic, find the most important / most-cited research papers. | |
| Tool used: OpenAlex API. It is free, structured, and does not require an | |
| API key. Results are sorted by citation count. | |
| """ | |
| import re | |
| import time | |
| import requests | |
| OPENALEX_WORKS_URL = "https://api.openalex.org/works" | |
| OPENALEX_MAILTO = "noreply@edu-research-agent.local" | |
| def _abstract_from_inverted_index(inverted_index: dict | None) -> str: | |
| if not inverted_index: | |
| return "" | |
| positions = [] | |
| for word, indexes in inverted_index.items(): | |
| for index in indexes: | |
| positions.append((index, word)) | |
| return " ".join(word for _, word in sorted(positions)) | |
| def _get_openalex(params: dict, headers: dict) -> dict: | |
| params = {**params, "mailto": OPENALEX_MAILTO} | |
| last_error = None | |
| for wait_seconds in (0, 2, 5): | |
| if wait_seconds: | |
| time.sleep(wait_seconds) | |
| try: | |
| resp = requests.get( | |
| OPENALEX_WORKS_URL, params=params, headers=headers, timeout=10 | |
| ) | |
| resp.raise_for_status() | |
| return resp.json() | |
| except requests.HTTPError as e: | |
| last_error = e | |
| if e.response is None or e.response.status_code != 429: | |
| raise | |
| except requests.RequestException as e: | |
| last_error = e | |
| raise | |
| raise last_error | |
| def run_paper_agent(api_key: str, topic: str, max_results: int = 10) -> dict: | |
| query = re.sub(r"\bner\b", "named entity recognition", topic, flags=re.IGNORECASE) | |
| params = { | |
| "filter": f"title_and_abstract.search:{query}", | |
| "per-page": max_results, | |
| "sort": "cited_by_count:desc", | |
| "select": ( | |
| "display_name,authorships,publication_year,cited_by_count," | |
| "abstract_inverted_index,doi,id,primary_location" | |
| ), | |
| } | |
| headers = {"User-Agent": "edu-research-agent/1.0"} | |
| try: | |
| data = _get_openalex(params, headers) | |
| except requests.RequestException as e: | |
| return {"agent": "paper_agent", "papers": [], "error": str(e)} | |
| if not data.get("results"): | |
| params.pop("filter", None) | |
| params["search"] = query | |
| try: | |
| data = _get_openalex(params, headers) | |
| except requests.RequestException as e: | |
| return {"agent": "paper_agent", "papers": [], "error": str(e)} | |
| cleaned = [] | |
| for paper in data.get("results", [])[:max_results]: | |
| authors = ", ".join( | |
| item.get("author", {}).get("display_name", "") | |
| for item in (paper.get("authorships") or [])[:3] | |
| ) | |
| location = paper.get("primary_location") or {} | |
| source = location.get("source") or {} | |
| url = location.get("landing_page_url") or paper.get("doi") or paper.get("id") | |
| abstract = _abstract_from_inverted_index(paper.get("abstract_inverted_index")) | |
| cleaned.append({ | |
| "title": paper.get("display_name"), | |
| "authors": authors or "Unknown", | |
| "year": paper.get("publication_year"), | |
| "citations": paper.get("cited_by_count"), | |
| "venue": source.get("display_name"), | |
| "url": url, | |
| "abstract": abstract[:300], | |
| }) | |
| return {"agent": "paper_agent", "papers": cleaned, "error": None} | |