omega21's picture
Update app.py
4267a75 verified
Raw History Blame
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.
"""
# ---------------------------------------------------------------------------
# ๋„๊ตฌ
# ---------------------------------------------------------------------------
@tool
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,
)
@staticmethod
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])
@spaces.GPU
def dummy():
return "ok"
if __name__ == "__main__":
demo.launch(debug=True, share=False)