Spaces:
Sleeping
Sleeping
Download tools.py from Viske/Final_Assignment_Template: direct link, hf CLI and curl.
- Browser
- Download file 15.2 kB
-
https://huggingface.co/spaces/Viske/Final_Assignment_Template/resolve/main/tools.py
- Command line
-
hf download hf://spaces/Viske/Final_Assignment_Template/tools.py
-
curl -L -o tools.py https://huggingface.co/spaces/Viske/Final_Assignment_Template/resolve/main/tools.py
15.2 kB
| import os | |
| import re | |
| import json | |
| import base64 | |
| import tempfile | |
| import subprocess | |
| import mimetypes | |
| import requests | |
| import pandas as pd | |
| from bs4 import BeautifulSoup | |
| from groq import Groq | |
| SCORING_API = "https://agents-course-unit4-scoring.hf.space" | |
| GROQ_API_KEY = os.getenv("GROQ_API_KEY") | |
| VISION_MODEL = os.getenv( | |
| "GROQ_VISION_MODEL", | |
| "qwen/qwen3.8-27b" | |
| ) | |
| AUDIO_MODEL = os.getenv( | |
| "GROQ_AUDIO_MODEL", | |
| "whisper-large-v3-turbo" | |
| ) | |
| groq_client = Groq( | |
| api_key=GROQ_API_KEY | |
| ) | |
| # --------------------------------------------------------- | |
| # Web search state | |
| # --------------------------------------------------------- | |
| LAST_SEARCH_RESULTS = [] | |
| # --------------------------------------------------------- | |
| # Web search | |
| # --------------------------------------------------------- | |
| def web_search(query): | |
| global LAST_SEARCH_RESULTS | |
| try: | |
| from ddgs import DDGS | |
| results = [] | |
| with DDGS() as ddgs: | |
| search_results = ddgs.text( | |
| query, | |
| max_results=6 | |
| ) | |
| for item in search_results: | |
| result = { | |
| "title": item.get("title", ""), | |
| "url": item.get("href", ""), | |
| "snippet": item.get("body", "") | |
| } | |
| results.append(result) | |
| LAST_SEARCH_RESULTS = results | |
| if not results: | |
| return "No search results found." | |
| output = [] | |
| for i, item in enumerate(results): | |
| output.append( | |
| f"[{i}]\n" | |
| f"TITLE: {item['title']}\n" | |
| f"URL: {item['url']}\n" | |
| f"SNIPPET: {item['snippet']}" | |
| ) | |
| return "\n\n".join(output) | |
| except Exception as e: | |
| return f"Web search error: {type(e).__name__}: {e}" | |
| # --------------------------------------------------------- | |
| # Read webpage | |
| # --------------------------------------------------------- | |
| def read_webpage( | |
| url=None, | |
| cursor=None, | |
| pattern=None | |
| ): | |
| global LAST_SEARCH_RESULTS | |
| # ----------------------------------------------------- | |
| # Resolve missing URL using latest search results | |
| # ----------------------------------------------------- | |
| if not url: | |
| if cursor is None: | |
| cursor = 0 | |
| try: | |
| cursor = int(cursor) | |
| except Exception: | |
| cursor = 0 | |
| if ( | |
| cursor < 0 | |
| or cursor >= len(LAST_SEARCH_RESULTS) | |
| ): | |
| return ( | |
| "ERROR: No valid URL was supplied and the requested " | |
| "search-result cursor does not exist." | |
| ) | |
| url = LAST_SEARCH_RESULTS[cursor].get("url", "") | |
| if not url: | |
| return "ERROR: Could not resolve webpage URL." | |
| # ----------------------------------------------------- | |
| # Fetch webpage | |
| # ----------------------------------------------------- | |
| try: | |
| response = requests.get( | |
| url, | |
| timeout=20, | |
| headers={ | |
| "User-Agent": ( | |
| "Mozilla/5.0 " | |
| "(compatible; GAIA-Agent/1.0)" | |
| ) | |
| } | |
| ) | |
| response.raise_for_status() | |
| soup = BeautifulSoup( | |
| response.text, | |
| "html.parser" | |
| ) | |
| for tag in soup( | |
| [ | |
| "script", | |
| "style", | |
| "noscript", | |
| "svg", | |
| "nav", | |
| "footer", | |
| "header" | |
| ] | |
| ): | |
| tag.decompose() | |
| text = soup.get_text( | |
| "\n", | |
| strip=True | |
| ) | |
| # ------------------------------------------------- | |
| # If model supplied a pattern, return context | |
| # around occurrences rather than the whole page. | |
| # ------------------------------------------------- | |
| if pattern: | |
| pattern_lower = pattern.lower() | |
| text_lower = text.lower() | |
| positions = [] | |
| start = 0 | |
| while True: | |
| position = text_lower.find( | |
| pattern_lower, | |
| start | |
| ) | |
| if position == -1: | |
| break | |
| positions.append(position) | |
| start = position + len(pattern_lower) | |
| if len(positions) >= 5: | |
| break | |
| if positions: | |
| chunks = [] | |
| for position in positions: | |
| left = max( | |
| 0, | |
| position - 1800 | |
| ) | |
| right = min( | |
| len(text), | |
| position + 3500 | |
| ) | |
| chunks.append( | |
| text[left:right] | |
| ) | |
| text = "\n\n--- MATCH ---\n\n".join( | |
| chunks | |
| ) | |
| if len(text) > 18000: | |
| text = text[:18000] | |
| return ( | |
| f"URL: {url}\n\n" | |
| f"CONTENT:\n{text}" | |
| ) | |
| except Exception as e: | |
| return ( | |
| f"Webpage read error: " | |
| f"{type(e).__name__}: {e}" | |
| ) | |
| # --------------------------------------------------------- | |
| # Download GAIA attachment | |
| # --------------------------------------------------------- | |
| def download_file(task_id): | |
| try: | |
| url = f"{SCORING_API}/files/{task_id}" | |
| response = requests.get( | |
| url, | |
| timeout=60 | |
| ) | |
| response.raise_for_status() | |
| content_type = response.headers.get( | |
| "content-type", | |
| "" | |
| ).lower() | |
| disposition = response.headers.get( | |
| "content-disposition", | |
| "" | |
| ) | |
| filename = None | |
| # ------------------------------------------------- | |
| # Try Content-Disposition | |
| # ------------------------------------------------- | |
| match = re.search( | |
| r'filename="?([^";]+)"?', | |
| disposition, | |
| re.IGNORECASE | |
| ) | |
| if match: | |
| filename = match.group(1) | |
| # ------------------------------------------------- | |
| # Try URL filename | |
| # ------------------------------------------------- | |
| if not filename: | |
| url_name = url.rstrip("/").split("/")[-1] | |
| if "." in url_name: | |
| filename = url_name | |
| # ------------------------------------------------- | |
| # Content-type fallback | |
| # ------------------------------------------------- | |
| if not filename: | |
| extension_map = { | |
| "audio/mpeg": ".mp3", | |
| "audio/mp3": ".mp3", | |
| "audio/wav": ".wav", | |
| "audio/x-wav": ".wav", | |
| "image/png": ".png", | |
| "image/jpeg": ".jpg", | |
| "image/jpg": ".jpg", | |
| "application/pdf": ".pdf", | |
| "text/plain": ".txt", | |
| "text/x-python": ".py", | |
| "application/json": ".json", | |
| "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx", | |
| "application/vnd.ms-excel": ".xls" | |
| } | |
| filename = ( | |
| "gaia_attachment" | |
| + extension_map.get( | |
| content_type, | |
| ".bin" | |
| ) | |
| ) | |
| # ------------------------------------------------- | |
| # Save | |
| # ------------------------------------------------- | |
| safe_filename = os.path.basename( | |
| filename | |
| ) | |
| path = os.path.join( | |
| tempfile.gettempdir(), | |
| safe_filename | |
| ) | |
| with open( | |
| path, | |
| "wb" | |
| ) as f: | |
| f.write(response.content) | |
| return path | |
| except Exception as e: | |
| raise RuntimeError( | |
| f"Could not download task file: " | |
| f"{type(e).__name__}: {e}" | |
| ) | |
| # --------------------------------------------------------- | |
| # Inspect text | |
| # --------------------------------------------------------- | |
| def inspect_text(path): | |
| with open( | |
| path, | |
| "r", | |
| encoding="utf-8", | |
| errors="replace" | |
| ) as f: | |
| text = f.read() | |
| if len(text) > 20000: | |
| text = text[:20000] | |
| return text | |
| # --------------------------------------------------------- | |
| # Inspect Excel | |
| # --------------------------------------------------------- | |
| def inspect_excel(path): | |
| sheets = pd.read_excel( | |
| path, | |
| sheet_name=None | |
| ) | |
| output = [] | |
| for sheet_name, df in sheets.items(): | |
| output.append( | |
| f"=== SHEET: {sheet_name} ===" | |
| ) | |
| output.append( | |
| f"Rows: {len(df)}" | |
| ) | |
| output.append( | |
| f"Columns: {list(df.columns)}" | |
| ) | |
| output.append( | |
| df.head(100).to_string( | |
| index=False | |
| ) | |
| ) | |
| return "\n\n".join(output) | |
| # --------------------------------------------------------- | |
| # Inspect PDF | |
| # --------------------------------------------------------- | |
| def inspect_pdf(path): | |
| from PyPDF2 import PdfReader | |
| reader = PdfReader(path) | |
| pages = [] | |
| for page in reader.pages: | |
| text = page.extract_text() or "" | |
| pages.append(text) | |
| if len("\n".join(pages)) > 20000: | |
| break | |
| text = "\n".join(pages) | |
| return text[:20000] | |
| # --------------------------------------------------------- | |
| # Inspect Python / source | |
| # --------------------------------------------------------- | |
| def inspect_source(path): | |
| return inspect_text(path) | |
| # --------------------------------------------------------- | |
| # Generic file inspection | |
| # --------------------------------------------------------- | |
| def inspect_file(task_id): | |
| path = download_file(task_id) | |
| extension = ( | |
| os.path.splitext(path)[1] | |
| .lower() | |
| ) | |
| if extension in [ | |
| ".xlsx", | |
| ".xls" | |
| ]: | |
| return inspect_excel(path) | |
| if extension == ".pdf": | |
| return inspect_pdf(path) | |
| if extension in [ | |
| ".txt", | |
| ".py", | |
| ".json", | |
| ".csv", | |
| ".md", | |
| ".xml", | |
| ".html" | |
| ]: | |
| return inspect_source(path) | |
| if extension in [ | |
| ".png", | |
| ".jpg", | |
| ".jpeg" | |
| ]: | |
| return ( | |
| f"Image file downloaded successfully: {path}\n" | |
| "Use analyze_image for visual analysis." | |
| ) | |
| if extension in [ | |
| ".mp3", | |
| ".wav", | |
| ".m4a", | |
| ".ogg" | |
| ]: | |
| return ( | |
| f"Audio file downloaded successfully: {path}\n" | |
| "Use transcribe_audio for speech analysis." | |
| ) | |
| return ( | |
| f"Downloaded file: {path}\n" | |
| f"Extension: {extension}" | |
| ) | |
| # --------------------------------------------------------- | |
| # Run Python | |
| # --------------------------------------------------------- | |
| def run_python(code): | |
| path = os.path.join( | |
| tempfile.gettempdir(), | |
| "gaia_agent_code.py" | |
| ) | |
| with open( | |
| path, | |
| "w", | |
| encoding="utf-8" | |
| ) as f: | |
| f.write(code) | |
| try: | |
| result = subprocess.run( | |
| [ | |
| "python", | |
| path | |
| ], | |
| capture_output=True, | |
| text=True, | |
| timeout=30 | |
| ) | |
| output = ( | |
| result.stdout | |
| + "\n" | |
| + result.stderr | |
| ).strip() | |
| if len(output) > 12000: | |
| output = output[:12000] | |
| return output | |
| except subprocess.TimeoutExpired: | |
| return "Python execution timed out after 30 seconds." | |
| except Exception as e: | |
| return ( | |
| f"Python execution error: " | |
| f"{type(e).__name__}: {e}" | |
| ) | |
| # --------------------------------------------------------- | |
| # Audio transcription | |
| # --------------------------------------------------------- | |
| def transcribe_audio(task_id): | |
| path = download_file(task_id) | |
| with open( | |
| path, | |
| "rb" | |
| ) as audio_file: | |
| transcription = groq_client.audio.transcriptions.create( | |
| file=audio_file, | |
| model=AUDIO_MODEL, | |
| response_format="text" | |
| ) | |
| return str(transcription) | |
| # --------------------------------------------------------- | |
| # Image analysis | |
| # --------------------------------------------------------- | |
| def analyze_image( | |
| task_id, | |
| question | |
| ): | |
| path = download_file(task_id) | |
| extension = ( | |
| os.path.splitext(path)[1] | |
| .lower() | |
| ) | |
| mime_map = { | |
| ".png": "image/png", | |
| ".jpg": "image/jpeg", | |
| ".jpeg": "image/jpeg", | |
| ".webp": "image/webp" | |
| } | |
| mime_type = mime_map.get( | |
| extension, | |
| "image/png" | |
| ) | |
| with open( | |
| path, | |
| "rb" | |
| ) as f: | |
| encoded = base64.b64encode( | |
| f.read() | |
| ).decode("utf-8") | |
| response = groq_client.chat.completions.create( | |
| model=VISION_MODEL, | |
| temperature=0, | |
| max_completion_tokens=1500, | |
| messages=[ | |
| { | |
| "role": "user", | |
| "content": [ | |
| { | |
| "type": "text", | |
| "text": ( | |
| "Analyze this image carefully.\n\n" | |
| f"Question: {question}\n\n" | |
| "Return the factual answer needed for the question." | |
| ) | |
| }, | |
| { | |
| "type": "image_url", | |
| "image_url": { | |
| "url": ( | |
| f"data:{mime_type};base64," | |
| f"{encoded}" | |
| ) | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| ) | |
| return response.choices[0].message.content | |
| # --------------------------------------------------------- | |
| # Tool dispatcher | |
| # --------------------------------------------------------- | |
| def execute_tool( | |
| name, | |
| args, | |
| task_id=None | |
| ): | |
| if name == "web_search": | |
| return web_search( | |
| args.get("query", "") | |
| ) | |
| if name == "read_webpage": | |
| return read_webpage( | |
| url=args.get("url"), | |
| cursor=args.get("cursor"), | |
| pattern=args.get("pattern") | |
| ) | |
| if name == "download_file": | |
| return download_file( | |
| args.get("task_id") or task_id | |
| ) | |
| if name == "inspect_file": | |
| return inspect_file( | |
| args.get("task_id") or task_id | |
| ) | |
| if name == "run_python": | |
| return run_python( | |
| args.get("code", "") | |
| ) | |
| if name == "transcribe_audio": | |
| return transcribe_audio( | |
| args.get("task_id") or task_id | |
| ) | |
| if name == "analyze_image": | |
| return analyze_image( | |
| task_id=args.get("task_id") or task_id, | |
| question=args.get("question", "") | |
| ) | |
| return f"Unknown tool: {name}" |