abhishekp21's picture
Update app.py
47a920d verified
import os
import gradio as gr
import requests
import pandas as pd
from transformers import pipeline
from duckduckgo_search import ddg
# --- Constants ---
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
# --- Tool Definitions ---
class WebSearchTool:
"""A simple open-source web search tool using DuckDuckGo."""
def search(self, query: str) -> list[str]:
# Perform a DuckDuckGo search and return top 3 titles
try:
results = ddg(query, max_results=3)
if not results:
return []
return [item.get("title", "").strip() for item in results]
except Exception as e:
print(f"DuckDuckGo search error: {e}")
return []
class CalculatorTool:
"""Evaluates simple arithmetic expressions."""
def calculate(self, expression: str) -> float:
# WARNING: using eval; in production, use a safe parser
return eval(expression, {}, {})
# --- Agent Definition ---
class ToolsAgent:
def __init__(self):
print("ToolsAgent initialized.")
# Initialize LLM for general reasoning
self.llm = pipeline(
"text-generation", model="gpt2", tokenizer="gpt2", return_full_text=False
)
# Initialize tools
self.tools = {
"search": WebSearchTool(),
"calc": CalculatorTool(),
}
def __call__(self, question: str) -> str:
print(f"Agent received question: {question}")
q_lower = question.lower().strip()
# 1) If calculation question
if any(op in q_lower for op in ["+", "-", "*", "/"]):
try:
result = self.tools["calc"].calculate(q_lower)
return str(result)
except Exception as e:
print(f"Calc error: {e}")
# 2) If search instruction
if q_lower.startswith("search") or "find" in q_lower:
try:
titles = self.tools["search"].search(question)
# Return only the first word of the top title
return titles[0].split()[0] if titles else ""
except Exception as e:
print(f"Search error: {e}")
# 3) Fallback to LLM
try:
out = self.llm(question, max_length=50, num_return_sequences=1)[0]["generated_text"].strip()
# Return only the first token (single word/number)
return out.split()[0]
except Exception as e:
print(f"LLM error: {e}")
return ""
# --- Evaluation and Submission Logic ---
def run_and_submit_all(profile: gr.OAuthProfile | None):
space_id = os.getenv("SPACE_ID")
if not profile:
return "Please Login to Hugging Face with the button.", None
username = profile.username.strip()
questions_url = f"{DEFAULT_API_URL}/questions"
submit_url = f"{DEFAULT_API_URL}/submit"
try:
agent = ToolsAgent()
except Exception as e:
return f"Error initializing agent: {e}", None
try:
resp = requests.get(questions_url, timeout=15)
resp.raise_for_status()
qs = resp.json()
except Exception as e:
return f"Error fetching questions: {e}", None
answers, log = [], []
for item in qs:
tid = item.get("task_id")
q = item.get("question")
if not tid or q is None:
continue
ans = agent(q)
answers.append({"task_id": tid, "submitted_answer": ans})
log.append({"Task ID": tid, "Question": q, "Submitted Answer": ans})
if not answers:
return "No answers generated.", pd.DataFrame(log)
payload = {
"username": username,
"agent_code": f"https://huggingface.co/spaces/{space_id}/tree/main",
"answers": answers
}
try:
res = requests.post(submit_url, json=payload, timeout=60)
res.raise_for_status()
data = res.json()
status = (
f"Submission Successful! User: {data.get('username')} "
f"Score: {data.get('score', 'N/A')}%"
)
except Exception as e:
status = f"Submission Failed: {e}"
return status, pd.DataFrame(log)
# --- Gradio Interface ---
with gr.Blocks() as demo:
gr.Markdown("## Agents Course: DuckDuckGo Search + Calculator Agent")
gr.LoginButton()
run_btn = gr.Button("Run Evaluation & Submit All Answers")
out_txt = gr.Textbox(label="Result", lines=3)
out_df = gr.DataFrame(label="Log", wrap=True)
run_btn.click(fn=run_and_submit_all, outputs=[out_txt, out_df])
if __name__ == "__main__":
demo.launch(debug=True)