Download app.py from agents-course/Final_Assignment_Template: direct link, hf CLI and curl.
- Browser
- Download file 22.5 kB
-
https://huggingface.co/spaces/agents-course/Final_Assignment_Template/resolve/refs%2Fpr%2F537/app.py
- Command line
-
hf download hf://spaces/agents-course/Final_Assignment_Template@refs/pr/537/app.py
-
curl -L -o app.py https://huggingface.co/spaces/agents-course/Final_Assignment_Template/resolve/refs%2Fpr%2F537/app.py
22.5 kB
| # ============================================================ | |
| # HUGGING FACE AGENTS COURSE - UNIT 4 | |
| # GENERAL PURPOSE GAIA AGENT | |
| # ============================================================ | |
| import os | |
| import gradio as gr | |
| import requests | |
| import pandas as pd | |
| from smolagents import ( | |
| CodeAgent, | |
| InferenceClientModel, | |
| DuckDuckGoSearchTool, | |
| VisitWebpageTool, | |
| WikipediaSearchTool, | |
| PythonInterpreterTool, | |
| ) | |
| # ============================================================ | |
| # CONSTANTS | |
| # ============================================================ | |
| # IMPORTANT: | |
| # DO NOT CHANGE THIS URL. | |
| DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space" | |
| # ============================================================ | |
| # GENERAL PURPOSE GAIA AGENT | |
| # ============================================================ | |
| class BasicAgent: | |
| def __init__(self): | |
| print("=" * 60) | |
| print("Initializing General Purpose GAIA Agent...") | |
| print("=" * 60) | |
| # ---------------------------------------------------- | |
| # Hugging Face token | |
| # ---------------------------------------------------- | |
| 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" | |
| ) | |
| # ---------------------------------------------------- | |
| # MODEL | |
| # ---------------------------------------------------- | |
| self.model = InferenceClientModel( | |
| model_id="Qwen/Qwen3-Next-80B-A3B-Thinking", | |
| token=hf_token, | |
| temperature=0.1, | |
| max_tokens=3000, | |
| ) | |
| # ---------------------------------------------------- | |
| # WEB SEARCH TOOL | |
| # ---------------------------------------------------- | |
| self.search_tool = DuckDuckGoSearchTool( | |
| max_results=8, | |
| rate_limit=1.0 | |
| ) | |
| # ---------------------------------------------------- | |
| # WEBPAGE TOOL | |
| # ---------------------------------------------------- | |
| self.webpage_tool = VisitWebpageTool( | |
| max_output_length=40000 | |
| ) | |
| # ---------------------------------------------------- | |
| # WIKIPEDIA TOOL | |
| # ---------------------------------------------------- | |
| self.wikipedia_tool = WikipediaSearchTool( | |
| user_agent="StructuralGPT-GAIA-Agent/1.0", | |
| language="en", | |
| content_type="text", | |
| extract_format="WIKI" | |
| ) | |
| # ---------------------------------------------------- | |
| # PYTHON / CALCULATION TOOL | |
| # ---------------------------------------------------- | |
| self.python_tool = PythonInterpreterTool( | |
| authorized_imports=[ | |
| "math", | |
| "statistics", | |
| "datetime", | |
| "json", | |
| "re", | |
| "decimal" | |
| ], | |
| timeout_seconds=30 | |
| ) | |
| # ---------------------------------------------------- | |
| # SYSTEM INSTRUCTIONS | |
| # ---------------------------------------------------- | |
| 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. | |
| """ | |
| # ---------------------------------------------------- | |
| # CREATE AGENT | |
| # ---------------------------------------------------- | |
| 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) | |
| # ======================================================== | |
| # AGENT CALL | |
| # ======================================================== | |
| 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() | |
| # ------------------------------------------------ | |
| # Remove accidental answer labels | |
| # ------------------------------------------------ | |
| 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}" | |
| # ============================================================ | |
| # RUN AND SUBMIT ALL QUESTIONS | |
| # ============================================================ | |
| 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. | |
| """ | |
| # -------------------------------------------------------- | |
| # 1. CHECK HUGGING FACE LOGIN | |
| # -------------------------------------------------------- | |
| 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 | |
| ) | |
| # -------------------------------------------------------- | |
| # 2. SCORING API URLS | |
| # -------------------------------------------------------- | |
| api_url = DEFAULT_API_URL | |
| questions_url = f"{api_url}/questions" | |
| submit_url = f"{api_url}/submit" | |
| # -------------------------------------------------------- | |
| # 3. GET SPACE CODE URL | |
| # -------------------------------------------------------- | |
| 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}" | |
| ) | |
| # -------------------------------------------------------- | |
| # 4. CREATE AGENT | |
| # -------------------------------------------------------- | |
| try: | |
| agent = BasicAgent() | |
| except Exception as e: | |
| print( | |
| f"Error initializing agent: {e}" | |
| ) | |
| return ( | |
| f"Error initializing agent: {e}", | |
| None | |
| ) | |
| # -------------------------------------------------------- | |
| # 5. FETCH QUESTIONS | |
| # -------------------------------------------------------- | |
| 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 | |
| ) | |
| # -------------------------------------------------------- | |
| # 6. RUN AGENT ON EVERY QUESTION | |
| # -------------------------------------------------------- | |
| 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" | |
| ) | |
| # ---------------------------------------------------- | |
| # Validate 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}" | |
| ) | |
| # ---------------------------------------------------- | |
| # Run agent | |
| # ---------------------------------------------------- | |
| 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 | |
| } | |
| ) | |
| # -------------------------------------------------------- | |
| # 7. CHECK ANSWERS | |
| # -------------------------------------------------------- | |
| 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) | |
| # -------------------------------------------------------- | |
| # 8. PREPARE SUBMISSION | |
| # -------------------------------------------------------- | |
| 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) | |
| # -------------------------------------------------------- | |
| # 9. SUBMIT ANSWERS | |
| # -------------------------------------------------------- | |
| 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 | |
| # ---------------------------------------------------- | |
| 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 | |
| ) | |
| # -------------------------------------------------------- | |
| # HTTP ERROR | |
| # -------------------------------------------------------- | |
| 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) | |
| ) | |
| # -------------------------------------------------------- | |
| # TIMEOUT | |
| # -------------------------------------------------------- | |
| except requests.exceptions.Timeout: | |
| status_message = ( | |
| "Submission Failed: " | |
| "The request timed out." | |
| ) | |
| print(status_message) | |
| return ( | |
| status_message, | |
| pd.DataFrame(results_log) | |
| ) | |
| # -------------------------------------------------------- | |
| # NETWORK ERROR | |
| # -------------------------------------------------------- | |
| 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) | |
| ) | |
| # -------------------------------------------------------- | |
| # OTHER ERROR | |
| # -------------------------------------------------------- | |
| except Exception as e: | |
| status_message = ( | |
| f"Unexpected error during submission: " | |
| f"{e}" | |
| ) | |
| print(status_message) | |
| return ( | |
| status_message, | |
| pd.DataFrame(results_log) | |
| ) | |
| # ============================================================ | |
| # GRADIO INTERFACE | |
| # ============================================================ | |
| 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. | |
| """ | |
| ) | |
| # -------------------------------------------------------- | |
| # LOGIN | |
| # -------------------------------------------------------- | |
| gr.LoginButton() | |
| # -------------------------------------------------------- | |
| # RUN BUTTON | |
| # -------------------------------------------------------- | |
| run_button = gr.Button( | |
| "Run Evaluation & Submit All Answers", | |
| variant="primary" | |
| ) | |
| # -------------------------------------------------------- | |
| # STATUS | |
| # -------------------------------------------------------- | |
| status_output = gr.Textbox( | |
| label="Run Status / Submission Result", | |
| lines=8, | |
| interactive=False | |
| ) | |
| # -------------------------------------------------------- | |
| # RESULTS | |
| # -------------------------------------------------------- | |
| results_table = gr.DataFrame( | |
| label="Questions and Agent Answers", | |
| wrap=True | |
| ) | |
| # -------------------------------------------------------- | |
| # BUTTON ACTION | |
| # -------------------------------------------------------- | |
| run_button.click( | |
| fn=run_and_submit_all, | |
| outputs=[ | |
| status_output, | |
| results_table | |
| ] | |
| ) | |
| # ============================================================ | |
| # START APPLICATION | |
| # ============================================================ | |
| if __name__ == "__main__": | |
| print( | |
| "\n" + | |
| "-" * 30 + | |
| " App Starting " + | |
| "-" * 30 | |
| ) | |
| # -------------------------------------------------------- | |
| # SPACE INFORMATION | |
| # -------------------------------------------------------- | |
| space_host_startup = os.getenv( | |
| "SPACE_HOST" | |
| ) | |
| space_id_startup = os.getenv( | |
| "SPACE_ID" | |
| ) | |
| # -------------------------------------------------------- | |
| # SPACE HOST | |
| # -------------------------------------------------------- | |
| 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." | |
| ) | |
| # -------------------------------------------------------- | |
| # SPACE ID | |
| # -------------------------------------------------------- | |
| 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..." | |
| ) | |
| # -------------------------------------------------------- | |
| # LAUNCH | |
| # -------------------------------------------------------- | |
| demo.launch( | |
| debug=True, | |
| share=False | |
| ) |