|
|
|
|
|
"""
|
|
|
API interaction module for GAIA Benchmark Agent.
|
|
|
|
|
|
This module handles all interactions with the GAIA benchmark API,
|
|
|
including fetching questions, downloading files, and submitting answers.
|
|
|
"""
|
|
|
|
|
|
import json
|
|
|
import requests
|
|
|
from typing import Dict, List, Any, Optional
|
|
|
from pathlib import Path
|
|
|
|
|
|
from gaiaX.config import logger, API_BASE_URL
|
|
|
|
|
|
def get_all_questions(api_base_url: str = API_BASE_URL) -> List[Dict[str, Any]]:
|
|
|
"""
|
|
|
Retrieve all available questions from the GAIA benchmark.
|
|
|
|
|
|
Args:
|
|
|
api_base_url: Base URL for the GAIA API
|
|
|
|
|
|
Returns:
|
|
|
List of question dictionaries
|
|
|
|
|
|
Raises:
|
|
|
requests.RequestException: If the API request fails
|
|
|
ValueError: If the response is not valid JSON or doesn't contain expected data
|
|
|
"""
|
|
|
try:
|
|
|
response = requests.get(f"{api_base_url}/questions")
|
|
|
response.raise_for_status()
|
|
|
|
|
|
questions = response.json()
|
|
|
|
|
|
if not isinstance(questions, list):
|
|
|
raise ValueError("Expected a list of questions but received a different format")
|
|
|
|
|
|
return questions
|
|
|
|
|
|
except requests.RequestException as e:
|
|
|
logger.error(f"Error fetching questions: {e}")
|
|
|
raise
|
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
logger.error("Error decoding response as JSON")
|
|
|
raise ValueError("Invalid JSON response from the API")
|
|
|
|
|
|
|
|
|
def get_random_question(api_base_url: str = API_BASE_URL) -> Dict[str, Any]:
|
|
|
"""
|
|
|
Retrieve a random question from the GAIA benchmark.
|
|
|
|
|
|
Args:
|
|
|
api_base_url: Base URL for the GAIA API
|
|
|
|
|
|
Returns:
|
|
|
A single question dictionary
|
|
|
|
|
|
Raises:
|
|
|
requests.RequestException: If the API request fails
|
|
|
ValueError: If the response is not valid JSON or doesn't contain expected data
|
|
|
"""
|
|
|
try:
|
|
|
response = requests.get(f"{api_base_url}/questions/random")
|
|
|
response.raise_for_status()
|
|
|
|
|
|
question = response.json()
|
|
|
|
|
|
if not isinstance(question, dict):
|
|
|
raise ValueError("Expected a question dictionary but received a different format")
|
|
|
|
|
|
return question
|
|
|
|
|
|
except requests.RequestException as e:
|
|
|
logger.error(f"Error fetching random question: {e}")
|
|
|
raise
|
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
logger.error("Error decoding response as JSON")
|
|
|
raise ValueError("Invalid JSON response from the API")
|
|
|
|
|
|
|
|
|
def download_file_for_task(api_base_url: str, task_id: str, download_path: str) -> str:
|
|
|
"""
|
|
|
Download a file associated with a specific task.
|
|
|
|
|
|
Args:
|
|
|
api_base_url: Base URL for the GAIA API
|
|
|
task_id: ID of the task to download files for
|
|
|
download_path: Directory path where the file should be saved
|
|
|
|
|
|
Returns:
|
|
|
Path to the downloaded file
|
|
|
|
|
|
Raises:
|
|
|
requests.RequestException: If the API request fails
|
|
|
IOError: If there's an error writing the file
|
|
|
ValueError: If the task_id is invalid or the response is unexpected
|
|
|
"""
|
|
|
if not task_id:
|
|
|
raise ValueError("Task ID cannot be empty")
|
|
|
|
|
|
|
|
|
download_dir = Path(download_path)
|
|
|
download_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
try:
|
|
|
response = requests.get(
|
|
|
f"{api_base_url}/tasks/{task_id}/file",
|
|
|
stream=True
|
|
|
)
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
|
content_disposition = response.headers.get('Content-Disposition', '')
|
|
|
filename = None
|
|
|
|
|
|
if 'filename=' in content_disposition:
|
|
|
filename = content_disposition.split('filename=')[1].strip('"\'')
|
|
|
|
|
|
if not filename:
|
|
|
filename = f"{task_id}_file.txt"
|
|
|
|
|
|
file_path = download_dir / filename
|
|
|
|
|
|
|
|
|
with open(file_path, 'wb') as f:
|
|
|
for chunk in response.iter_content(chunk_size=8192):
|
|
|
f.write(chunk)
|
|
|
|
|
|
return str(file_path)
|
|
|
|
|
|
except requests.RequestException as e:
|
|
|
logger.error(f"Error downloading file for task {task_id}: {e}")
|
|
|
raise
|
|
|
|
|
|
except IOError as e:
|
|
|
logger.error(f"Error writing file to {download_path}: {e}")
|
|
|
raise
|
|
|
|
|
|
|
|
|
def submit_answers(
|
|
|
api_base_url: str,
|
|
|
username: str,
|
|
|
agent_code_link: str,
|
|
|
answers: Dict[str, Any]
|
|
|
) -> Dict[str, Any]:
|
|
|
"""
|
|
|
Submit answers to the GAIA benchmark.
|
|
|
|
|
|
Args:
|
|
|
api_base_url: Base URL for the GAIA API
|
|
|
username: Hugging Face username
|
|
|
agent_code_link: Link to the agent code (e.g., GitHub repository)
|
|
|
answers: Dictionary of answers to submit
|
|
|
|
|
|
Returns:
|
|
|
Response from the API containing submission results
|
|
|
|
|
|
Raises:
|
|
|
requests.RequestException: If the API request fails
|
|
|
ValueError: If the response is not valid JSON or contains an error message
|
|
|
"""
|
|
|
if not username:
|
|
|
raise ValueError("Username cannot be empty")
|
|
|
|
|
|
if not agent_code_link:
|
|
|
raise ValueError("Agent code link cannot be empty")
|
|
|
|
|
|
if not answers or not isinstance(answers, dict):
|
|
|
raise ValueError("Answers must be a non-empty dictionary")
|
|
|
|
|
|
payload = {
|
|
|
"username": username,
|
|
|
"agent_code_link": agent_code_link,
|
|
|
"answers": answers
|
|
|
}
|
|
|
|
|
|
try:
|
|
|
response = requests.post(
|
|
|
f"{api_base_url}/submit",
|
|
|
json=payload,
|
|
|
headers={"Content-Type": "application/json"}
|
|
|
)
|
|
|
response.raise_for_status()
|
|
|
|
|
|
result = response.json()
|
|
|
|
|
|
|
|
|
if isinstance(result, dict) and result.get("error"):
|
|
|
raise ValueError(f"API returned an error: {result['error']}")
|
|
|
|
|
|
return result
|
|
|
|
|
|
except requests.RequestException as e:
|
|
|
logger.error(f"Error submitting answers: {e}")
|
|
|
raise
|
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
logger.error("Error decoding response as JSON")
|
|
|
raise ValueError("Invalid JSON response from the API")
|
|
|
|
|
|
|
|
|
def get_question_details(task_id: str, api_base_url: str = API_BASE_URL) -> Dict[str, Any]:
|
|
|
"""
|
|
|
Get detailed information about a specific question/task.
|
|
|
|
|
|
Args:
|
|
|
task_id: The ID of the task to get details for
|
|
|
api_base_url: Base URL for the GAIA API
|
|
|
|
|
|
Returns:
|
|
|
Dictionary containing question details
|
|
|
"""
|
|
|
try:
|
|
|
response = requests.get(f"{api_base_url}/questions/{task_id}")
|
|
|
response.raise_for_status()
|
|
|
return response.json()
|
|
|
except requests.RequestException as e:
|
|
|
logger.error(f"Failed to get question details: {str(e)}")
|
|
|
return {"error": f"Failed to get question details: {str(e)}"}
|
|
|
except json.JSONDecodeError:
|
|
|
logger.error("Invalid JSON response from the API")
|
|
|
return {"error": "Invalid JSON response from the API"} |