darrenphodgson76 commited on
Commit
42f801e
·
verified ·
1 Parent(s): 56f1088

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -35
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
  import openai
3
  from openai import OpenAI
4
  import gradio as gr
@@ -6,54 +7,84 @@ import requests
6
  import pandas as pd
7
  from smolagents import CodeAgent, DuckDuckGoSearchTool, tool
8
 
 
 
 
 
9
  # --- Constants ---
10
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
11
 
12
  # --- Configure OpenAI SDK & Client ---
13
  openai_api_key = os.getenv("OPENAI_API_KEY")
14
  if not openai_api_key:
15
- raise RuntimeError("Please set OPENAI_API_KEY in your Space secrets or env!")
16
  openai.api_key = openai_api_key
17
  client = OpenAI()
18
  OPENAI_MODEL_ID = os.getenv("OPENAI_MODEL_ID", "gpt-4.1")
19
 
20
- # --- Model Wrapper for CodeAgent compatibility ---
21
  class OpenAIModelWrapper:
22
  def __init__(self, model_id: str, client: OpenAI):
23
  self.model_id = model_id
24
  self.client = client
25
 
26
  def __call__(self, prompt: str, **kwargs) -> str:
27
- # ignore extra kwargs (stop_sequences, temperature, etc.)
28
- resp = self.client.responses.create(
29
- model=self.model_id,
30
- input=prompt
31
- )
32
- return resp.output_text
 
 
 
33
 
34
  # --- Tool Definitions ---
 
35
  @tool
36
  def summarize_query(query: str) -> str:
37
  """
38
- Reframes an unclear query into a concise one.
 
 
 
 
 
 
39
  """
40
  return f"Summarize and reframe: {query}"
41
 
42
  @tool
43
  def wikipedia_search(page: str) -> str:
44
  """
45
- Fetches the summary extract of an English Wikipedia page.
 
 
 
 
 
 
46
  """
47
- url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{page}"
48
- r = requests.get(url, timeout=10)
49
- r.raise_for_status()
50
- return r.json().get("extract", "")
 
 
 
 
 
 
 
51
 
 
52
  search_tool = DuckDuckGoSearchTool()
53
  wiki_tool = wikipedia_search
54
  summarize_tool = summarize_query
55
 
56
- # --- ReACT + Scratchpad + Auto-Retry Instruction Prompt ---
 
57
  instruction_prompt = """
58
  You are a ReACT agent with three tools:
59
  • DuckDuckGoSearchTool(query: str)
@@ -71,64 +102,78 @@ Internally, for each question:
71
 
72
  Finally, output your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER]. YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
73
 
74
-
75
- Rules:
76
- - Numbers: digits only.
77
- - Lists: comma-separated, no extra punctuation.
78
- - Strings: no filler words.
79
  """
80
 
81
- # --- Build the CodeAgent ---
 
82
  llm_wrapper = OpenAIModelWrapper(model_id=OPENAI_MODEL_ID, client=client)
83
  smart_agent = CodeAgent(
84
  tools=[search_tool, wiki_tool, summarize_tool],
85
  model=llm_wrapper
86
  )
87
 
88
- # --- BasicAgent wrapper for Gradio ---
 
89
  class BasicAgent:
90
  def __init__(self):
91
- print("SmolAgent (GPT-4.1) with ReACT & tools initialized.")
92
 
93
  def __call__(self, question: str) -> str:
 
 
 
 
94
  prompt = instruction_prompt.strip() + "\n\nQUESTION: " + question.strip()
 
 
 
95
  try:
96
  return smart_agent.run(prompt)
97
  except Exception as e:
98
- return f"AGENT ERROR: {e}"
 
 
 
99
 
100
- # --- Gradio / Submission Logic ---
101
  def run_and_submit_all(profile: gr.OAuthProfile | None):
102
  if not profile:
103
  return "Please log in to Hugging Face.", None
104
- username = profile.username
105
- space_id = os.getenv("SPACE_ID", "")
 
106
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
107
- agent = BasicAgent()
108
 
109
  # Fetch questions
110
  try:
111
  resp = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15)
112
  resp.raise_for_status()
113
- questions = resp.json() or []
 
 
 
114
  except Exception as e:
 
115
  return f"Error fetching questions: {e}", None
116
 
117
  # Run agent
118
- logs, payload = [], []
119
  for item in questions:
120
  tid = item.get("task_id")
121
  q = item.get("question")
122
  if not tid or not q:
 
123
  continue
124
  ans = agent(q)
125
  logs.append({"Task ID": tid, "Question": q, "Submitted Answer": ans})
126
  payload.append({"task_id": tid, "submitted_answer": ans})
 
 
127
 
128
  if not payload:
129
  return "Agent did not produce any answers.", pd.DataFrame(logs)
130
 
131
- # Submit
132
  submission = {"username": username, "agent_code": agent_code, "answers": payload}
133
  try:
134
  post = requests.post(f"{DEFAULT_API_URL}/submit", json=submission, timeout=60)
@@ -137,26 +182,28 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
137
  status = (
138
  f"Submission Successful!\n"
139
  f"User: {res.get('username')}\n"
140
- f"Overall Score: {res.get('score','N/A')}% "
141
  f"({res.get('correct_count','?')}/{res.get('total_attempted','?')})\n"
142
  f"Message: {res.get('message','')}"
143
  )
144
  return status, pd.DataFrame(logs)
145
  except Exception as e:
 
146
  return f"Submission Failed: {e}", pd.DataFrame(logs)
147
 
148
- # --- Gradio Interface ---
 
149
  with gr.Blocks() as demo:
150
  gr.Markdown("# SmolAgent GAIA Runner 🚀")
151
  gr.Markdown("""
152
  **Instructions:**
153
  1. Clone this space.
154
- 2. Add `OPENAI_API_KEY` (and optionally `OPENAI_MODEL_ID`) in Settings → Secrets.
155
  3. Log in to Hugging Face.
156
  4. Click **Run Evaluation & Submit All Answers**.
157
  """)
158
  gr.LoginButton()
159
- run_btn = gr.Button("Run Evaluation & Submit All Answers")
160
  status_out = gr.Textbox(label="Status", lines=5, interactive=False)
161
  table_out = gr.DataFrame(label="Questions & Answers", wrap=True)
162
  run_btn.click(fn=run_and_submit_all, outputs=[status_out, table_out])
 
1
  import os
2
+ import logging
3
  import openai
4
  from openai import OpenAI
5
  import gradio as gr
 
7
  import pandas as pd
8
  from smolagents import CodeAgent, DuckDuckGoSearchTool, tool
9
 
10
+ # --- Setup logging ---
11
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
12
+ logger = logging.getLogger(__name__)
13
+
14
  # --- Constants ---
15
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
16
+ MAX_PROMPT_LENGTH = 15000 # characters, naive cap to avoid token overflow
17
 
18
  # --- Configure OpenAI SDK & Client ---
19
  openai_api_key = os.getenv("OPENAI_API_KEY")
20
  if not openai_api_key:
21
+ raise RuntimeError("Environment variable OPENAI_API_KEY is required.")
22
  openai.api_key = openai_api_key
23
  client = OpenAI()
24
  OPENAI_MODEL_ID = os.getenv("OPENAI_MODEL_ID", "gpt-4.1")
25
 
26
+ # --- Adapter so CodeAgent can call the OpenAI client correctly ---
27
  class OpenAIModelWrapper:
28
  def __init__(self, model_id: str, client: OpenAI):
29
  self.model_id = model_id
30
  self.client = client
31
 
32
  def __call__(self, prompt: str, **kwargs) -> str:
33
+ try:
34
+ resp = self.client.responses.create(
35
+ model=self.model_id,
36
+ input=prompt
37
+ )
38
+ return getattr(resp, "output_text", str(resp))
39
+ except Exception as e:
40
+ logger.exception("OpenAI inference error")
41
+ return f"AGENT ERROR (inference): {e}"
42
 
43
  # --- Tool Definitions ---
44
+
45
  @tool
46
  def summarize_query(query: str) -> str:
47
  """
48
+ Reframes an unclear search query to improve relevance.
49
+
50
+ Args:
51
+ query (str): The original search query needing refinement.
52
+
53
+ Returns:
54
+ str: A concise, improved version of the query.
55
  """
56
  return f"Summarize and reframe: {query}"
57
 
58
  @tool
59
  def wikipedia_search(page: str) -> str:
60
  """
61
+ Fetches the summary extract of an English Wikipedia page via the REST API.
62
+
63
+ Args:
64
+ page (str): Title of the Wikipedia page (e.g. 'Mercedes_Sosa_discography').
65
+
66
+ Returns:
67
+ str: The page's summary (or an error message).
68
  """
69
+ try:
70
+ url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{page}"
71
+ resp = requests.get(url, timeout=10)
72
+ resp.raise_for_status()
73
+ extract = resp.json().get("extract", "")
74
+ if not extract:
75
+ logger.warning("Wikipedia returned empty extract for %s", page)
76
+ return extract
77
+ except Exception as e:
78
+ logger.exception("Wikipedia lookup failed")
79
+ return f"Wikipedia error: {e}"
80
 
81
+ # Instantiate tools
82
  search_tool = DuckDuckGoSearchTool()
83
  wiki_tool = wikipedia_search
84
  summarize_tool = summarize_query
85
 
86
+ # --- ReACT Prompt ---
87
+
88
  instruction_prompt = """
89
  You are a ReACT agent with three tools:
90
  • DuckDuckGoSearchTool(query: str)
 
102
 
103
  Finally, output your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER]. YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
104
 
 
 
 
 
 
105
  """
106
 
107
+ # --- Build CodeAgent ---
108
+
109
  llm_wrapper = OpenAIModelWrapper(model_id=OPENAI_MODEL_ID, client=client)
110
  smart_agent = CodeAgent(
111
  tools=[search_tool, wiki_tool, summarize_tool],
112
  model=llm_wrapper
113
  )
114
 
115
+ # --- BasicAgent for Gradio ---
116
+
117
  class BasicAgent:
118
  def __init__(self):
119
+ logger.info("Initialized SmolAgent (GPT-4.1) with ReACT & tools.")
120
 
121
  def __call__(self, question: str) -> str:
122
+ # Validate question
123
+ if not question or len(question.strip()) == 0:
124
+ return "AGENT ERROR: Empty question."
125
+ # Build and truncate prompt
126
  prompt = instruction_prompt.strip() + "\n\nQUESTION: " + question.strip()
127
+ if len(prompt) > MAX_PROMPT_LENGTH:
128
+ prompt = prompt[:MAX_PROMPT_LENGTH] # naive trim
129
+ logger.warning("Prompt truncated to %d chars", MAX_PROMPT_LENGTH)
130
  try:
131
  return smart_agent.run(prompt)
132
  except Exception as e:
133
+ logger.exception("Agent run error")
134
+ return f"AGENT ERROR (run): {e}"
135
+
136
+ # --- Submission logic ---
137
 
 
138
  def run_and_submit_all(profile: gr.OAuthProfile | None):
139
  if not profile:
140
  return "Please log in to Hugging Face.", None
141
+
142
+ username = profile.username
143
+ space_id = os.getenv("SPACE_ID", "")
144
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
145
+ agent = BasicAgent()
146
 
147
  # Fetch questions
148
  try:
149
  resp = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15)
150
  resp.raise_for_status()
151
+ questions = resp.json()
152
+ if not isinstance(questions, list):
153
+ raise ValueError("Invalid questions format")
154
+ logger.info("Fetched %d questions", len(questions))
155
  except Exception as e:
156
+ logger.exception("Failed to fetch questions")
157
  return f"Error fetching questions: {e}", None
158
 
159
  # Run agent
160
+ logs, payload, skipped = [], [], 0
161
  for item in questions:
162
  tid = item.get("task_id")
163
  q = item.get("question")
164
  if not tid or not q:
165
+ skipped += 1
166
  continue
167
  ans = agent(q)
168
  logs.append({"Task ID": tid, "Question": q, "Submitted Answer": ans})
169
  payload.append({"task_id": tid, "submitted_answer": ans})
170
+ if skipped:
171
+ logger.warning("Skipped %d malformed items", skipped)
172
 
173
  if not payload:
174
  return "Agent did not produce any answers.", pd.DataFrame(logs)
175
 
176
+ # Submit answers
177
  submission = {"username": username, "agent_code": agent_code, "answers": payload}
178
  try:
179
  post = requests.post(f"{DEFAULT_API_URL}/submit", json=submission, timeout=60)
 
182
  status = (
183
  f"Submission Successful!\n"
184
  f"User: {res.get('username')}\n"
185
+ f"Score: {res.get('score','N/A')}% "
186
  f"({res.get('correct_count','?')}/{res.get('total_attempted','?')})\n"
187
  f"Message: {res.get('message','')}"
188
  )
189
  return status, pd.DataFrame(logs)
190
  except Exception as e:
191
+ logger.exception("Submission failed")
192
  return f"Submission Failed: {e}", pd.DataFrame(logs)
193
 
194
+ # --- Gradio UI ---
195
+
196
  with gr.Blocks() as demo:
197
  gr.Markdown("# SmolAgent GAIA Runner 🚀")
198
  gr.Markdown("""
199
  **Instructions:**
200
  1. Clone this space.
201
+ 2. Add `OPENAI_API_KEY` and optionally `OPENAI_MODEL_ID` in Settings → Secrets.
202
  3. Log in to Hugging Face.
203
  4. Click **Run Evaluation & Submit All Answers**.
204
  """)
205
  gr.LoginButton()
206
+ run_btn = gr.Button("Run Evaluation & Submit All Answers")
207
  status_out = gr.Textbox(label="Status", lines=5, interactive=False)
208
  table_out = gr.DataFrame(label="Questions & Answers", wrap=True)
209
  run_btn.click(fn=run_and_submit_all, outputs=[status_out, table_out])