File size: 4,609 Bytes
10e9b7d
 
eccf8e4
3c4371f
47a920d
 
10e9b7d
e80aab9
3db6293
e80aab9
47a920d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31243f4
47a920d
 
 
 
 
 
 
 
 
 
4021bf3
47a920d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3c4371f
47a920d
 
 
 
7d65c66
47a920d
3c4371f
47a920d
 
e80aab9
31243f4
47a920d
31243f4
 
3c4371f
eccf8e4
47a920d
 
 
7d65c66
47a920d
e80aab9
47a920d
 
 
 
 
31243f4
47a920d
 
 
 
 
 
 
 
 
 
 
 
e80aab9
47a920d
 
 
 
 
 
e80aab9
7d65c66
47a920d
 
e80aab9
47a920d
e80aab9
47a920d
7e4a06b
47a920d
 
 
 
e80aab9
 
47a920d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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)