| |
| |
| |
| |
|
|
| import os |
| import gradio as gr |
| import requests |
| import pandas as pd |
|
|
| from smolagents import ( |
| CodeAgent, |
| InferenceClientModel, |
| DuckDuckGoSearchTool, |
| VisitWebpageTool, |
| WikipediaSearchTool, |
| PythonInterpreterTool, |
| ) |
|
|
|
|
| |
| |
| |
|
|
| |
| |
| DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space" |
|
|
|
|
| |
| |
| |
|
|
| class BasicAgent: |
|
|
| def __init__(self): |
|
|
| print("=" * 60) |
| print("Initializing General Purpose GAIA Agent...") |
| print("=" * 60) |
|
|
| |
| |
| |
|
|
| hf_token = os.getenv("HF_TOKEN") |
|
|
| if not hf_token: |
|
|
| raise RuntimeError( |
| "HF_TOKEN is missing.\n" |
| "Go to Space Settings -> Secrets and create:\n" |
| "Name: HF_TOKEN\n" |
| "Value: Your Hugging Face token" |
| ) |
|
|
| |
| |
| |
|
|
| self.model = InferenceClientModel( |
| model_id="Qwen/Qwen3-Next-80B-A3B-Thinking", |
| token=hf_token, |
| temperature=0.1, |
| max_tokens=3000, |
| ) |
|
|
| |
| |
| |
|
|
| self.search_tool = DuckDuckGoSearchTool( |
| max_results=8, |
| rate_limit=1.0 |
| ) |
|
|
| |
| |
| |
|
|
| self.webpage_tool = VisitWebpageTool( |
| max_output_length=40000 |
| ) |
|
|
| |
| |
| |
|
|
| self.wikipedia_tool = WikipediaSearchTool( |
| user_agent="StructuralGPT-GAIA-Agent/1.0", |
| language="en", |
| content_type="text", |
| extract_format="WIKI" |
| ) |
|
|
| |
| |
| |
|
|
| self.python_tool = PythonInterpreterTool( |
| authorized_imports=[ |
| "math", |
| "statistics", |
| "datetime", |
| "json", |
| "re", |
| "decimal" |
| ], |
| timeout_seconds=30 |
| ) |
|
|
| |
| |
| |
|
|
| self.instructions = """ |
| You are a highly capable general-purpose AI agent designed |
| to solve GAIA benchmark questions. |
| |
| Your objective is to provide the CORRECT answer to the |
| user's question. |
| |
| IMPORTANT RULES |
| =============== |
| |
| 1. READ THE COMPLETE QUESTION CAREFULLY. |
| |
| 2. Determine exactly what the question is asking. |
| |
| 3. Use tools whenever they are useful. |
| |
| 4. Use web search for: |
| - current information |
| - factual information |
| - obscure information |
| - information that should be verified |
| - people, companies, events, dates, statistics, etc. |
| |
| 5. Use the webpage tool when the question provides a URL |
| or when you need the contents of a webpage. |
| |
| 6. Use Wikipedia search when the question specifically |
| refers to Wikipedia or historical/general information. |
| |
| 7. Use Python for: |
| - arithmetic |
| - percentages |
| - dates |
| - counting |
| - comparisons |
| - numerical reasoning |
| - data processing |
| |
| 8. NEVER guess a numerical answer when you can calculate it. |
| |
| 9. When a question contains several steps: |
| solve every required step before giving the answer. |
| |
| 10. Verify important facts whenever possible. |
| |
| 11. Do not invent information. |
| |
| 12. Pay close attention to: |
| - exact dates |
| - names |
| - numbers |
| - units |
| - percentages |
| - spelling |
| - requested formats |
| |
| 13. If the question asks: |
| "How many?" |
| return the number. |
| |
| 14. If it asks: |
| "Who?" |
| return the person's name. |
| |
| 15. If it asks: |
| "When?" |
| return the date/year. |
| |
| 16. If it asks for a calculation, |
| calculate it using Python. |
| |
| 17. If the question asks for a specific list, |
| provide exactly the requested list. |
| |
| 18. If the question contains a URL, |
| investigate the URL rather than guessing. |
| |
| 19. Do not include citations unless the question asks for them. |
| |
| 20. Do not say "FINAL ANSWER". |
| |
| 21. Do not write: |
| "Here is the answer:" |
| "The answer is:" |
| or unnecessary explanations. |
| |
| 22. The final response must contain ONLY the answer |
| required by the question. |
| |
| 23. The GAIA evaluator uses exact matching, so be precise |
| and concise. |
| |
| 24. Think through the problem carefully before producing |
| the final response. |
| |
| 25. NEVER intentionally return a generic response. |
| |
| SPECIAL CASES |
| ============= |
| |
| For calculations: |
| - Use Python. |
| - Verify the result. |
| - Give the exact required number. |
| |
| For web research: |
| - Search using precise keywords. |
| - Open useful results. |
| - Compare information when needed. |
| |
| For Wikipedia questions: |
| - Use Wikipedia search. |
| - Pay attention to the requested Wikipedia version/date. |
| |
| For questions involving a webpage: |
| - Visit the webpage. |
| - Extract the relevant information. |
| - Answer only what is requested. |
| |
| For civil/structural engineering questions: |
| - You may use engineering knowledge. |
| - Use Indian Standards when appropriate. |
| - Keep the final answer focused on what was requested. |
| |
| FINAL RESPONSE |
| ============== |
| |
| Return ONLY the concise answer. |
| """ |
|
|
|
|
| |
| |
| |
|
|
| self.agent = CodeAgent( |
| model=self.model, |
|
|
| tools=[ |
| self.search_tool, |
| self.webpage_tool, |
| self.wikipedia_tool, |
| self.python_tool, |
| ], |
|
|
| max_steps=12, |
|
|
| instructions=self.instructions, |
|
|
| add_base_tools=True |
| ) |
|
|
| print("GAIA Agent initialized successfully.") |
| print("=" * 60) |
|
|
|
|
| |
| |
| |
|
|
| def __call__(self, question: str) -> str: |
|
|
| print("\n") |
| print("=" * 70) |
| print("NEW GAIA QUESTION") |
| print("=" * 70) |
| print(question) |
| print("=" * 70) |
|
|
| try: |
|
|
| result = self.agent.run(question) |
|
|
| answer = str(result).strip() |
|
|
| |
| |
| |
|
|
| unwanted_prefixes = [ |
| "FINAL ANSWER:", |
| "FINAL ANSWER", |
| "Answer:", |
| "ANSWER:", |
| "The answer is:", |
| "The answer is" |
| ] |
|
|
| for prefix in unwanted_prefixes: |
|
|
| if answer.lower().startswith(prefix.lower()): |
|
|
| answer = answer[len(prefix):].strip() |
|
|
| print("\nAGENT ANSWER:") |
| print(answer) |
| print("=" * 70) |
|
|
| return answer |
|
|
| except Exception as e: |
|
|
| print("\nAGENT ERROR:") |
| print(str(e)) |
| print("=" * 70) |
|
|
| return f"Agent execution error: {e}" |
|
|
|
|
| |
| |
| |
|
|
| def run_and_submit_all(profile: gr.OAuthProfile | None): |
|
|
| """ |
| Fetches all GAIA questions, |
| runs the General Purpose Agent, |
| submits all answers, |
| and displays the results. |
| """ |
|
|
| |
| |
| |
|
|
| if profile: |
|
|
| username = profile.username |
|
|
| print( |
| f"User logged in: {username}" |
| ) |
|
|
| else: |
|
|
| print("User is not logged in.") |
|
|
| return ( |
| "Please Login to Hugging Face with the button.", |
| None |
| ) |
|
|
|
|
| |
| |
| |
|
|
| api_url = DEFAULT_API_URL |
|
|
| questions_url = f"{api_url}/questions" |
|
|
| submit_url = f"{api_url}/submit" |
|
|
|
|
| |
| |
| |
|
|
| space_id = os.getenv("SPACE_ID") |
|
|
| if not space_id: |
|
|
| print( |
| "WARNING: SPACE_ID was not found." |
| ) |
|
|
| agent_code = "SPACE_ID_NOT_FOUND" |
|
|
| else: |
|
|
| agent_code = ( |
| f"https://huggingface.co/spaces/" |
| f"{space_id}/tree/main" |
| ) |
|
|
| print( |
| f"Agent code URL: {agent_code}" |
| ) |
|
|
|
|
| |
| |
| |
|
|
| try: |
|
|
| agent = BasicAgent() |
|
|
| except Exception as e: |
|
|
| print( |
| f"Error initializing agent: {e}" |
| ) |
|
|
| return ( |
| f"Error initializing agent: {e}", |
| None |
| ) |
|
|
|
|
| |
| |
| |
|
|
| print("\n") |
| print("=" * 70) |
| print("FETCHING GAIA QUESTIONS") |
| print("=" * 70) |
|
|
| try: |
|
|
| response = requests.get( |
| questions_url, |
| timeout=30 |
| ) |
|
|
| response.raise_for_status() |
|
|
| questions_data = response.json() |
|
|
| if not questions_data: |
|
|
| print("Questions list is empty.") |
|
|
| return ( |
| "Fetched questions list is empty.", |
| None |
| ) |
|
|
| print( |
| f"Fetched {len(questions_data)} questions." |
| ) |
|
|
| except requests.exceptions.RequestException as e: |
|
|
| print( |
| f"Error fetching questions: {e}" |
| ) |
|
|
| return ( |
| f"Error fetching questions: {e}", |
| None |
| ) |
|
|
| except ValueError as e: |
|
|
| print( |
| f"Invalid JSON response: {e}" |
| ) |
|
|
| return ( |
| f"Invalid JSON response: {e}", |
| None |
| ) |
|
|
| except Exception as e: |
|
|
| print( |
| f"Unexpected error fetching questions: {e}" |
| ) |
|
|
| return ( |
| f"Unexpected error fetching questions: {e}", |
| None |
| ) |
|
|
|
|
| |
| |
| |
|
|
| results_log = [] |
|
|
| answers_payload = [] |
|
|
| total_questions = len(questions_data) |
|
|
| print("\n") |
| print("=" * 70) |
| print("RUNNING GENERAL PURPOSE AGENT") |
| print("=" * 70) |
|
|
|
|
| for number, item in enumerate( |
| questions_data, |
| start=1 |
| ): |
|
|
| task_id = item.get( |
| "task_id" |
| ) |
|
|
| question_text = item.get( |
| "question" |
| ) |
|
|
| |
| |
| |
|
|
| if not task_id: |
|
|
| print( |
| f"Skipping question {number}: " |
| "missing task_id." |
| ) |
|
|
| continue |
|
|
| if question_text is None: |
|
|
| print( |
| f"Skipping question {number}: " |
| "missing question text." |
| ) |
|
|
| continue |
|
|
|
|
| print("\n") |
| print( |
| f"QUESTION {number}/{total_questions}" |
| ) |
| print( |
| f"TASK ID: {task_id}" |
| ) |
|
|
|
|
| |
| |
| |
|
|
| try: |
|
|
| submitted_answer = agent( |
| question_text |
| ) |
|
|
| answers_payload.append( |
| { |
| "task_id": task_id, |
| "submitted_answer": submitted_answer |
| } |
| ) |
|
|
| results_log.append( |
| { |
| "Task ID": task_id, |
| "Question": question_text, |
| "Submitted Answer": |
| submitted_answer |
| } |
| ) |
|
|
| except Exception as e: |
|
|
| error_message = ( |
| f"AGENT ERROR: {e}" |
| ) |
|
|
| print(error_message) |
|
|
| results_log.append( |
| { |
| "Task ID": task_id, |
| "Question": question_text, |
| "Submitted Answer": |
| error_message |
| } |
| ) |
|
|
|
|
| |
| |
| |
|
|
| if not answers_payload: |
|
|
| print( |
| "Agent did not produce any answers." |
| ) |
|
|
| return ( |
| "Agent did not produce any answers.", |
| pd.DataFrame(results_log) |
| ) |
|
|
|
|
| print("\n") |
| print("=" * 70) |
| print( |
| f"Agent produced {len(answers_payload)} " |
| f"answers." |
| ) |
| print("=" * 70) |
|
|
|
|
| |
| |
| |
|
|
| submission_data = { |
|
|
| "username": |
| username.strip(), |
|
|
| "agent_code": |
| agent_code, |
|
|
| "answers": |
| answers_payload |
| } |
|
|
|
|
| status_update = ( |
| f"Agent finished.\n" |
| f"Submitting {len(answers_payload)} answers " |
| f"for user '{username}'..." |
| ) |
|
|
| print(status_update) |
|
|
|
|
| |
| |
| |
|
|
| print("\n") |
| print("=" * 70) |
| print("SUBMITTING TO GAIA SCORING SERVER") |
| print("=" * 70) |
|
|
| try: |
|
|
| response = requests.post( |
| submit_url, |
| json=submission_data, |
| timeout=120 |
| ) |
|
|
| response.raise_for_status() |
|
|
| result_data = response.json() |
|
|
|
|
| |
| |
| |
|
|
| score = result_data.get( |
| "score", |
| "N/A" |
| ) |
|
|
| correct_count = result_data.get( |
| "correct_count", |
| "?" |
| ) |
|
|
| total_attempted = result_data.get( |
| "total_attempted", |
| "?" |
| ) |
|
|
| message = result_data.get( |
| "message", |
| "No message received." |
| ) |
|
|
|
|
| final_status = ( |
|
|
| "Submission Successful!\n\n" |
|
|
| f"User: " |
| f"{result_data.get('username', username)}\n" |
|
|
| f"Overall Score: " |
| f"{score}%\n" |
|
|
| f"Correct: " |
| f"{correct_count}/" |
| f"{total_attempted}\n\n" |
|
|
| f"Message: " |
| f"{message}" |
| ) |
|
|
|
|
| print(final_status) |
|
|
|
|
| results_df = pd.DataFrame( |
| results_log |
| ) |
|
|
|
|
| return ( |
| final_status, |
| results_df |
| ) |
|
|
|
|
| |
| |
| |
|
|
| except requests.exceptions.HTTPError as e: |
|
|
| error_detail = ( |
| f"Server responded with " |
| f"status {e.response.status_code}." |
| ) |
|
|
| try: |
|
|
| error_json = e.response.json() |
|
|
| error_detail += ( |
| f" Detail: " |
| f"{error_json.get('detail', '')}" |
| ) |
|
|
| except Exception: |
|
|
| error_detail += ( |
| f" Response: " |
| f"{e.response.text[:500]}" |
| ) |
|
|
|
|
| status_message = ( |
| f"Submission Failed: " |
| f"{error_detail}" |
| ) |
|
|
| print(status_message) |
|
|
|
|
| return ( |
| status_message, |
| pd.DataFrame(results_log) |
| ) |
|
|
|
|
| |
| |
| |
|
|
| except requests.exceptions.Timeout: |
|
|
| status_message = ( |
| "Submission Failed: " |
| "The request timed out." |
| ) |
|
|
| print(status_message) |
|
|
|
|
| return ( |
| status_message, |
| pd.DataFrame(results_log) |
| ) |
|
|
|
|
| |
| |
| |
|
|
| except requests.exceptions.RequestException as e: |
|
|
| status_message = ( |
| f"Submission Failed: " |
| f"Network error - {e}" |
| ) |
|
|
| print(status_message) |
|
|
|
|
| return ( |
| status_message, |
| pd.DataFrame(results_log) |
| ) |
|
|
|
|
| |
| |
| |
|
|
| except Exception as e: |
|
|
| status_message = ( |
| f"Unexpected error during submission: " |
| f"{e}" |
| ) |
|
|
| print(status_message) |
|
|
|
|
| return ( |
| status_message, |
| pd.DataFrame(results_log) |
| ) |
|
|
|
|
| |
| |
| |
|
|
| with gr.Blocks() as demo: |
|
|
| gr.Markdown( |
| """ |
| # 🤖 General Purpose GAIA Agent |
| |
| This agent uses: |
| |
| - Qwen through Hugging Face |
| - Web Search |
| - Webpage retrieval |
| - Wikipedia |
| - Python calculations |
| - Multi-step reasoning |
| |
| It is designed for the Hugging Face Agents Course |
| Unit 4 GAIA evaluation. |
| """ |
| ) |
|
|
|
|
| gr.Markdown( |
| """ |
| ### Instructions |
| |
| 1. Log in to your Hugging Face account. |
| 2. Make sure `HF_TOKEN` is configured as a Space Secret. |
| 3. Click **Run Evaluation & Submit All Answers**. |
| 4. Wait while the agent processes all questions. |
| 5. Your score will appear below. |
| |
| **Important:** The evaluation can take several minutes |
| because the agent processes each question individually. |
| """ |
| ) |
|
|
|
|
| |
| |
| |
|
|
| gr.LoginButton() |
|
|
|
|
| |
| |
| |
|
|
| run_button = gr.Button( |
| "Run Evaluation & Submit All Answers", |
| variant="primary" |
| ) |
|
|
|
|
| |
| |
| |
|
|
| status_output = gr.Textbox( |
| label="Run Status / Submission Result", |
| lines=8, |
| interactive=False |
| ) |
|
|
|
|
| |
| |
| |
|
|
| results_table = gr.DataFrame( |
| label="Questions and Agent Answers", |
| wrap=True |
| ) |
|
|
|
|
| |
| |
| |
|
|
| run_button.click( |
| fn=run_and_submit_all, |
| outputs=[ |
| status_output, |
| results_table |
| ] |
| ) |
|
|
|
|
| |
| |
| |
|
|
| if __name__ == "__main__": |
|
|
| print( |
| "\n" + |
| "-" * 30 + |
| " App Starting " + |
| "-" * 30 |
| ) |
|
|
|
|
| |
| |
| |
|
|
| space_host_startup = os.getenv( |
| "SPACE_HOST" |
| ) |
|
|
| space_id_startup = os.getenv( |
| "SPACE_ID" |
| ) |
|
|
|
|
| |
| |
| |
|
|
| if space_host_startup: |
|
|
| print( |
| f"SPACE_HOST found: " |
| f"{space_host_startup}" |
| ) |
|
|
| print( |
| "Runtime URL:" |
| ) |
|
|
| print( |
| f"https://{space_host_startup}.hf.space" |
| ) |
|
|
| else: |
|
|
| print( |
| "SPACE_HOST not found." |
| ) |
|
|
|
|
| |
| |
| |
|
|
| if space_id_startup: |
|
|
| print( |
| f"SPACE_ID found: " |
| f"{space_id_startup}" |
| ) |
|
|
| print( |
| "Repository URL:" |
| ) |
|
|
| print( |
| f"https://huggingface.co/spaces/" |
| f"{space_id_startup}" |
| ) |
|
|
| print( |
| "Code URL:" |
| ) |
|
|
| print( |
| f"https://huggingface.co/spaces/" |
| f"{space_id_startup}/tree/main" |
| ) |
|
|
| else: |
|
|
| print( |
| "SPACE_ID not found." |
| ) |
|
|
|
|
| print( |
| "\nLaunching General Purpose GAIA Agent..." |
| ) |
|
|
|
|
| |
| |
| |
|
|
| demo.launch( |
| debug=True, |
| share=False |
| ) |