Download app.py from agents-course/Final_Assignment_Template: direct link, hf CLI and curl.
- Browser
- Download file 10.1 kB
-
https://huggingface.co/spaces/agents-course/Final_Assignment_Template/resolve/refs%2Fpr%2F607/app.py
- Command line
-
hf download hf://spaces/agents-course/Final_Assignment_Template@refs/pr/607/app.py
-
curl -L -o app.py https://huggingface.co/spaces/agents-course/Final_Assignment_Template/resolve/refs%2Fpr%2F607/app.py
10.1 kB
| import spaces | |
| import os | |
| import re | |
| import time | |
| import gradio as gr | |
| import pandas as pd | |
| import requests | |
| from smolagents import ( | |
| CodeAgent, | |
| DuckDuckGoSearchTool, | |
| InferenceClientModel, | |
| LiteLLMModel, | |
| VisitWebpageTool, | |
| tool, | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # ์์ | |
| # --------------------------------------------------------------------------- | |
| DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space" | |
| FILES_DIR = "/tmp/task_files" | |
| os.makedirs(FILES_DIR, exist_ok=True) | |
| SLEEP_BETWEEN_QUESTIONS = float(os.getenv("SLEEP_BETWEEN_QUESTIONS", "2")) | |
| ANSWER_RULES = """ | |
| You are a careful research and reasoning agent solving GAIA-style questions. | |
| Rules: | |
| 1. Use the tools (web search, webpage visit, file reading, python code) whenever the | |
| question needs facts you are not 100% sure about. Do not guess. | |
| 2. The final answer is graded by EXACT MATCH. Return ONLY the answer value, with no | |
| explanation, no units unless asked, no trailing period, no "FINAL ANSWER:" prefix. | |
| 3. If the answer is a number: no thousands separators, no currency/percent symbols | |
| unless the question asks for them. | |
| 4. If the answer is a list: comma-separated, in the order the question requests. | |
| 5. If the answer is a string: no articles (a, an, the) and no abbreviations unless | |
| the question asks for them. | |
| 6. If a file path is given, read it with the read_local_file tool or with python code. | |
| 7. Finish by calling final_answer(...) with the answer only. | |
| """ | |
| # --------------------------------------------------------------------------- | |
| # ๋๊ตฌ | |
| # --------------------------------------------------------------------------- | |
| def read_local_file(path: str) -> str: | |
| """Reads a local file (txt, csv, json, py, md, xlsx) and returns its content as text. | |
| Args: | |
| path: Absolute path of the file to read. | |
| """ | |
| try: | |
| lower = path.lower() | |
| if lower.endswith((".xlsx", ".xls")): | |
| sheets = pd.read_excel(path, sheet_name=None) | |
| parts = [] | |
| for name, df in sheets.items(): | |
| parts.append(f"## Sheet: {name}\n{df.to_string(max_rows=200)}") | |
| return "\n\n".join(parts) | |
| if lower.endswith(".csv"): | |
| return pd.read_csv(path).to_string(max_rows=200) | |
| with open(path, "r", encoding="utf-8", errors="ignore") as f: | |
| return f.read()[:20000] | |
| except Exception as e: | |
| return f"Could not read file: {e}" | |
| # --------------------------------------------------------------------------- | |
| # ์์ด์ ํธ | |
| # --------------------------------------------------------------------------- | |
| def build_model(): | |
| gemini_key = os.getenv("GEMINI_API_KEY") | |
| if gemini_key: | |
| model_id = os.getenv("GEMINI_MODEL", "gemini-2.5-flash") | |
| print(f"[model] Gemini: {model_id}") | |
| return LiteLLMModel(model_id=f"gemini/{model_id}", api_key=gemini_key) | |
| openai_key = os.getenv("OPENAI_API_KEY") | |
| if openai_key: | |
| model_id = os.getenv("OPENAI_MODEL", "gpt-4o-mini") | |
| print(f"[model] OpenAI: {model_id}") | |
| return LiteLLMModel(model_id=f"openai/{model_id}", api_key=openai_key) | |
| model_id = os.getenv("HF_MODEL", "Qwen/Qwen2.5-Coder-32B-Instruct") | |
| print(f"[model] HF Inference: {model_id}") | |
| return InferenceClientModel(model_id=model_id, token=os.getenv("HF_TOKEN")) | |
| class BasicAgent: | |
| def __init__(self): | |
| print("BasicAgent ์ด๊ธฐํ ์ค...") | |
| self.agent = CodeAgent( | |
| tools=[DuckDuckGoSearchTool(), VisitWebpageTool(), read_local_file], | |
| model=build_model(), | |
| additional_authorized_imports=[ | |
| "pandas", "numpy", "re", "json", "math", "datetime", | |
| "collections", "itertools", "statistics", "requests", "bs4", | |
| ], | |
| max_steps=int(os.getenv("MAX_STEPS", "12")), | |
| verbosity_level=1, | |
| ) | |
| def _clean(answer) -> str: | |
| text = str(answer).strip() | |
| text = re.sub(r"^\s*final answer\s*:\s*", "", text, flags=re.IGNORECASE) | |
| return text.strip().rstrip(".").strip() | |
| def __call__(self, question: str, file_path: str | None = None) -> str: | |
| prompt = ANSWER_RULES + "\n\nQuestion:\n" + question | |
| if file_path: | |
| prompt += f"\n\nAn attached file is available at: {file_path}" | |
| result = self.agent.run(prompt) | |
| return self._clean(result) | |
| # --------------------------------------------------------------------------- | |
| # ์ฑ์ ์๋ฒ ํต์ ์ ํธ | |
| # --------------------------------------------------------------------------- | |
| def fetch_questions(api_url: str) -> list[dict]: | |
| resp = requests.get(f"{api_url}/questions", timeout=30) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| if not data: | |
| raise ValueError("์ง๋ฌธ ๋ชฉ๋ก์ด ๋น์ด ์์ต๋๋ค.") | |
| return data | |
| def download_task_file(api_url: str, task_id: str, file_name: str) -> str | None: | |
| if not file_name: | |
| return None | |
| try: | |
| resp = requests.get(f"{api_url}/files/{task_id}", timeout=60) | |
| resp.raise_for_status() | |
| path = os.path.join(FILES_DIR, f"{task_id}_{file_name}") | |
| with open(path, "wb") as f: | |
| f.write(resp.content) | |
| return path | |
| except Exception as e: | |
| print(f"ํ์ผ ๋ค์ด๋ก๋ ์คํจ ({task_id}): {e}") | |
| return None | |
| def run_questions(agent: BasicAgent, questions: list[dict], api_url: str): | |
| answers_payload, results_log = [], [] | |
| for i, item in enumerate(questions, start=1): | |
| task_id = item.get("task_id") | |
| question_text = item.get("question") | |
| if not task_id or question_text is None: | |
| continue | |
| print(f"\n===== [{i}/{len(questions)}] task_id={task_id} =====") | |
| file_path = download_task_file(api_url, task_id, item.get("file_name", "")) | |
| try: | |
| answer = agent(question_text, file_path) | |
| except Exception as e: | |
| print(f"์์ด์ ํธ ์ค๋ฅ: {e}") | |
| answer = f"AGENT ERROR: {e}" | |
| answers_payload.append({"task_id": task_id, "submitted_answer": answer}) | |
| results_log.append( | |
| {"Task ID": task_id, "Question": question_text, "Submitted Answer": answer} | |
| ) | |
| time.sleep(SLEEP_BETWEEN_QUESTIONS) | |
| return answers_payload, results_log | |
| # --------------------------------------------------------------------------- | |
| # Gradio ์ฝ๋ฐฑ | |
| # --------------------------------------------------------------------------- | |
| def run_and_submit_all(profile: gr.OAuthProfile | None): | |
| space_id = os.getenv("SPACE_ID") | |
| if not profile: | |
| return "๋จผ์ Hugging Face ๋ก๊ทธ์ธ ๋ฒํผ์ผ๋ก ๋ก๊ทธ์ธํด ์ฃผ์ธ์.", None | |
| username = profile.username | |
| api_url = DEFAULT_API_URL | |
| if not space_id: | |
| return "SPACE_ID๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. Space ์์์ ์คํํด์ผ ํฉ๋๋ค.", None | |
| agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" | |
| try: | |
| agent = BasicAgent() | |
| except Exception as e: | |
| return f"์์ด์ ํธ ์ด๊ธฐํ ์ค๋ฅ: {e}", None | |
| try: | |
| questions = fetch_questions(api_url) | |
| except Exception as e: | |
| return f"์ง๋ฌธ ๊ฐ์ ธ์ค๊ธฐ ์ค๋ฅ: {e}", None | |
| answers_payload, results_log = run_questions(agent, questions, api_url) | |
| results_df = pd.DataFrame(results_log) | |
| if not answers_payload: | |
| return "์ ์ถํ ๋ต๋ณ์ด ์์ต๋๋ค.", results_df | |
| submission = { | |
| "username": username.strip(), | |
| "agent_code": agent_code, | |
| "answers": answers_payload, | |
| } | |
| try: | |
| resp = requests.post(f"{api_url}/submit", json=submission, timeout=120) | |
| resp.raise_for_status() | |
| r = resp.json() | |
| status = ( | |
| "์ ์ถ ์ฑ๊ณต!\n" | |
| f"์ฌ์ฉ์: {r.get('username')}\n" | |
| f"์ ์: {r.get('score', 'N/A')}% " | |
| f"({r.get('correct_count', '?')}/{r.get('total_attempted', '?')} ์ ๋ต)\n" | |
| f"๋ฉ์์ง: {r.get('message', '')}" | |
| ) | |
| return status, results_df | |
| except requests.exceptions.HTTPError as e: | |
| detail = e.response.text[:500] if e.response is not None else str(e) | |
| return f"์ ์ถ ์คํจ: {detail}", results_df | |
| except Exception as e: | |
| return f"์ ์ถ ์ค ์ค๋ฅ: {e}", results_df | |
| def run_test_only(n_questions: int): | |
| api_url = DEFAULT_API_URL | |
| try: | |
| agent = BasicAgent() | |
| questions = fetch_questions(api_url)[: int(n_questions)] | |
| except Exception as e: | |
| return f"์ค๋น ์ค ์ค๋ฅ: {e}", None | |
| _, results_log = run_questions(agent, questions, api_url) | |
| return f"ํ ์คํธ ์๋ฃ: {len(results_log)}๊ฐ ์ง๋ฌธ ์คํ (์ ์ถํ์ง ์์)", pd.DataFrame(results_log) | |
| # --------------------------------------------------------------------------- | |
| # UI | |
| # --------------------------------------------------------------------------- | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Basic Agent Evaluation Runner") | |
| gr.Markdown( | |
| """ | |
| **Instructions:** | |
| 1. ์ด Space๋ฅผ ๋ณต์ (Duplicate)ํ ๋ค ์ฝ๋๋ฅผ ์์ ํด ์์ด์ ํธ ๋ก์ง์ ์ ์ํฉ๋๋ค. | |
| 2. ์๋ ๋ฒํผ์ผ๋ก Hugging Face์ ๋ก๊ทธ์ธํฉ๋๋ค. (HF ์ฌ์ฉ์๋ช ์ผ๋ก ์ ์ถ๋ฉ๋๋ค) | |
| 3. ๋จผ์ 'ํ ์คํธ ์คํ'์ผ๋ก ์์ ๋ฌธํญ์ ํ์ธํ๊ณ , ๋ฌธ์ ์์ผ๋ฉด 'Run Evaluation & Submit All Answers'๋ฅผ ๋๋ฆ ๋๋ค. | |
| """ | |
| ) | |
| gr.LoginButton() | |
| with gr.Row(): | |
| test_n = gr.Number(value=2, label="ํ ์คํธํ ๋ฌธํญ ์ (์ ์ถ ์ ํจ)", precision=0) | |
| test_button = gr.Button("ํ ์คํธ ์คํ (์ ์ถ ์ ํจ)") | |
| run_button = gr.Button("Run Evaluation & Submit All Answers") | |
| status_output = gr.Textbox(label="Run Status / Submission Result", lines=6, interactive=False) | |
| results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True) | |
| test_button.click(fn=run_test_only, inputs=[test_n], outputs=[status_output, results_table]) | |
| run_button.click(fn=run_and_submit_all, outputs=[status_output, results_table]) | |
| def dummy(): | |
| return "ok" | |
| if __name__ == "__main__": | |
| demo.launch(debug=True, share=False) |