Download app.py from eigbney/AI-Unit2: direct link, hf CLI and curl.
- Browser
- Download file 8.26 kB
-
https://huggingface.co/spaces/eigbney/AI-Unit2/resolve/main/app.py
- Command line
-
hf download hf://spaces/eigbney/AI-Unit2/app.py
-
curl -L -o app.py https://huggingface.co/spaces/eigbney/AI-Unit2/resolve/main/app.py
8.26 kB
| """ | |
| app.py β HuggingFace Agents Course Unit 4 Final Assignment | |
| Uses Google Gemini (gemini-2.0-flash) as the agent model. | |
| Deploy to HF Spaces: | |
| - SDK: Gradio | |
| - Add secret: Gemini | |
| Get your free Gemini API key at: https://aistudio.google.com/apikey | |
| Scoring API docs: https://agents-course-unit4-scoring.hf.space/docs | |
| Course leaderboard: https://huggingface.co/spaces/agents-course/Students_leaderboard | |
| """ | |
| import os | |
| import re | |
| import time | |
| import gradio as gr | |
| import requests | |
| from google import genai | |
| from google.genai import types | |
| # ββ Constants βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space" | |
| MODEL = "gemini-2.0-flash" | |
| SYSTEM_PROMPT = ( | |
| "You are a precise AI assistant. Answer the question as concisely as possible. " | |
| "Your final answer must be a number, as few words as possible, or a comma-separated " | |
| "list of numbers and/or strings. " | |
| "Do not include units (e.g. $, %) unless explicitly asked. " | |
| "Do not use articles or abbreviations for place names. " | |
| "Write numbers as digits, not words, unless asked otherwise. " | |
| "Reply with ONLY the answer β no explanation, no preamble." | |
| ) | |
| # ββ Agent βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def run_agent(question: str, task_id: str, api_url: str) -> str: | |
| """Call Gemini to answer one GAIA question. Attaches file if one exists.""" | |
| api_key = os.environ.get("Gemini", "") | |
| if not api_key: | |
| return "[ERROR] Gemini secret is not set in this Space." | |
| client = genai.Client(api_key=api_key) | |
| # Build message parts β try to fetch any associated file | |
| parts = [] | |
| try: | |
| file_resp = requests.get(f"{api_url}/files/{task_id}", timeout=10) | |
| if file_resp.status_code == 200 and file_resp.content: | |
| import base64 | |
| content_type = file_resp.headers.get("content-type", "") | |
| if "pdf" in content_type or "image" in content_type: | |
| media_type = content_type.split(";")[0].strip() | |
| parts.append(types.Part( | |
| inline_data=types.Blob( | |
| mime_type=media_type, | |
| data=base64.standard_b64encode(file_resp.content).decode(), | |
| ) | |
| )) | |
| else: | |
| try: | |
| text_content = file_resp.content.decode("utf-8", errors="replace") | |
| parts.append(types.Part(text=f"[Attached file content]\n{text_content}\n")) | |
| except Exception: | |
| pass | |
| except Exception: | |
| pass # No file or fetch failed β continue without it | |
| parts.append(types.Part(text=question)) | |
| config = types.GenerateContentConfig( | |
| system_instruction=SYSTEM_PROMPT, | |
| tools=[types.Tool(google_search=types.GoogleSearch())], | |
| safety_settings=[ | |
| types.SafetySetting(category="HARM_CATEGORY_HARASSMENT", threshold="BLOCK_NONE"), | |
| types.SafetySetting(category="HARM_CATEGORY_HATE_SPEECH", threshold="BLOCK_NONE"), | |
| types.SafetySetting(category="HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold="BLOCK_NONE"), | |
| types.SafetySetting(category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="BLOCK_NONE"), | |
| ], | |
| ) | |
| for attempt in range(5): | |
| try: | |
| response = client.models.generate_content( | |
| model=MODEL, | |
| contents=parts, | |
| config=config, | |
| ) | |
| answer = response.text.strip() | |
| answer = re.sub(r"(?i)^final answer:\s*", "", answer).strip() | |
| return answer | |
| except Exception as e: | |
| msg = str(e) | |
| if "429" in msg or "RESOURCE_EXHAUSTED" in msg: | |
| wait = 30 * (attempt + 1) # 30s, 60s, 90s, 120s, 150s | |
| time.sleep(wait) | |
| elif attempt == 4: | |
| return f"[ERROR] {e}" | |
| else: | |
| time.sleep(5) | |
| return "[ERROR] All retries failed." | |
| # ββ Gradio logic ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def run_and_submit(profile: gr.OAuthProfile | None, *args): | |
| """Fetch questions β run agent β submit answers β return results.""" | |
| if profile is None: | |
| return "Please log in with your Hugging Face account first.", None | |
| username = profile.username | |
| space_id = os.environ.get("SPACE_ID", "") | |
| agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" if space_id else "N/A" | |
| api_url = DEFAULT_API_URL | |
| # Fetch questions | |
| status_lines = [f"Logged in as: **{username}**", "Fetching questionsβ¦"] | |
| try: | |
| resp = requests.get(f"{api_url}/questions", timeout=15) | |
| resp.raise_for_status() | |
| questions = resp.json() | |
| except Exception as e: | |
| return f"Failed to fetch questions: {e}", None | |
| if not questions: | |
| return "No questions returned by the API.", None | |
| status_lines.append(f"Fetched {len(questions)} questions. Running agentβ¦") | |
| # Run agent on each question | |
| results_log = [] | |
| answers_payload = [] | |
| for i, item in enumerate(questions, 1): | |
| task_id = item.get("task_id", "") | |
| question = item.get("question", "") | |
| answer = run_agent(question, task_id, api_url) | |
| time.sleep(4) # stay under free-tier RPM limit (15 req/min) | |
| answers_payload.append({"task_id": task_id, "submitted_answer": answer}) | |
| results_log.append({ | |
| "Task ID": task_id, | |
| "Question": question[:80] + ("β¦" if len(question) > 80 else ""), | |
| "Answer": answer, | |
| }) | |
| status_lines.append(f"[{i}/{len(questions)}] {task_id} β {answer[:60]}") | |
| # Submit answers | |
| status_lines.append("Submitting answersβ¦") | |
| submit_payload = { | |
| "username": username, | |
| "agent_code": agent_code, | |
| "answers": answers_payload, | |
| } | |
| try: | |
| submit_resp = requests.post(f"{api_url}/submit", json=submit_payload, timeout=60) | |
| submit_resp.raise_for_status() | |
| result = submit_resp.json() | |
| score = result.get("score", "N/A") | |
| correct = result.get("correct_count", "?") | |
| total = result.get("total_attempted", len(questions)) | |
| message = result.get("message", "") | |
| status_lines.append(f"\n**Score: {score}% ({correct}/{total} correct)**\n{message}") | |
| except Exception as e: | |
| status_lines.append(f"Submission failed: {e}") | |
| return "\n\n".join(status_lines), results_log | |
| # ββ Gradio UI βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| with gr.Blocks(title="GAIA Agent β Unit 4 Final Assignment") as demo: | |
| gr.Markdown( | |
| """ | |
| # GAIA Agent β HF Agents Course Unit 4 | |
| This agent fetches GAIA benchmark questions, answers them using **Gemini 2.0 Flash** | |
| with web search, and submits your score to the | |
| [course leaderboard](https://huggingface.co/spaces/agents-course/Students_leaderboard). | |
| **Before running:** make sure `Gemini` is set as a secret in your Space settings. | |
| Get a free key at [aistudio.google.com/apikey](https://aistudio.google.com/apikey). | |
| """ | |
| ) | |
| login_btn = gr.LoginButton() | |
| run_btn = gr.Button("Run Evaluation & Submit", variant="primary") | |
| status_box = gr.Markdown(label="Status") | |
| results_table = gr.Dataframe( | |
| headers=["Task ID", "Question", "Answer"], | |
| datatype=["str", "str", "str"], | |
| label="Results", | |
| wrap=True, | |
| ) | |
| run_btn.click( | |
| fn=run_and_submit, | |
| inputs=[login_btn], | |
| outputs=[status_box, results_table], | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(debug=True) |