Spaces:
Sleeping
Sleeping
| """일반 웹페이지 fetch + 마크다운 변환 툴. | |
| Content-Type 분기: | |
| - text/html → BeautifulSoup → markdownify | |
| - application/pdf → pypdf로 페이지별 텍스트 추출 (arxiv·NASA TR 등 외부 PDF용) | |
| """ | |
| import io | |
| import re | |
| import requests | |
| from bs4 import BeautifulSoup | |
| from markdownify import markdownify as md | |
| from smolagents import tool | |
| def _handle_pdf_url(content: bytes) -> str: | |
| """외부 PDF URL 본문을 페이지별 텍스트로 변환. attachments._handle_pdf와 동일 패턴.""" | |
| try: | |
| from pypdf import PdfReader | |
| reader = PdfReader(io.BytesIO(content)) | |
| parts = [] | |
| for i, page in enumerate(reader.pages): | |
| try: | |
| txt = page.extract_text() or "" | |
| except Exception as pe: | |
| txt = f"(extraction failed: {pe})" | |
| parts.append(f"--- Page {i+1} ---\n{txt}") | |
| combined = "\n\n".join(parts) | |
| if len(combined) > 12000: | |
| combined = combined[:12000] + "\n...[truncated]" | |
| return f"[PDF, {len(reader.pages)} pages]\n{combined}" | |
| except Exception as e: | |
| return f"PDF parse error: {e}" | |
| def visit_webpage(url: str) -> str: | |
| """Fetch a web page (HTML or PDF) and return its readable text (truncated to ~12k chars). | |
| HTML pages are converted to markdown. PDF URLs are parsed page-by-page via pypdf — | |
| useful for arxiv papers, NASA technical reports, and other linked PDF documents. | |
| Args: | |
| url: The full URL of the webpage or PDF to fetch. | |
| """ | |
| try: | |
| # 일부 사이트(특히 위키미디어 외)가 빈 User-Agent를 차단하므로 헤더를 명시한다. | |
| headers = {"User-Agent": "Mozilla/5.0 (compatible; GAIA-Agent/1.0)"} | |
| resp = requests.get(url, headers=headers, timeout=20) | |
| resp.raise_for_status() | |
| content_type = resp.headers.get("Content-Type", "").lower() | |
| # PDF: pypdf로 텍스트 추출. arxiv 논문 등 GAIA에 자주 등장. | |
| if "application/pdf" in content_type or url.lower().endswith(".pdf"): | |
| return _handle_pdf_url(resp.content) | |
| # HTML: 기존 흐름. | |
| soup = BeautifulSoup(resp.text, "html.parser") | |
| # 본문과 무관한 노이즈 제거: 스크립트/스타일/noscript 블록. | |
| for tag in soup(["script", "style", "noscript"]): | |
| tag.decompose() | |
| markdown = md(str(soup)) | |
| # markdownify가 종종 빈 줄을 줄줄이 만들어내므로 압축해서 토큰을 절약한다. | |
| markdown = re.sub(r"\n{3,}", "\n\n", markdown).strip() | |
| # LLM 컨텍스트 보호: 너무 큰 페이지는 잘라서 반환한다. | |
| if len(markdown) > 12000: | |
| markdown = markdown[:12000] + "\n...[truncated]" | |
| return markdown | |
| except Exception as e: | |
| return f"visit_webpage error: {e}" | |