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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -89
app.py CHANGED
@@ -1,46 +1,31 @@
1
  import os
2
  import logging
3
- import openai
4
- from openai import OpenAI
5
  import gradio as gr
6
  import requests
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:
@@ -48,37 +33,33 @@ def summarize_query(query: str) -> str:
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
@@ -100,113 +81,114 @@ Internally, for each question:
100
  Record new Observation.
101
  5. Thought: integrate observations.
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)
 
 
 
 
180
  post.raise_for_status()
181
- res = post.json()
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])
210
 
211
  if __name__ == "__main__":
212
  demo.launch(debug=True, share=False)
 
1
  import os
2
  import logging
3
+
 
4
  import gradio as gr
5
  import requests
6
  import pandas as pd
7
+ import openai
8
+ from openai import OpenAI
9
+
10
  from smolagents import CodeAgent, DuckDuckGoSearchTool, tool
11
+ from smolagents.models import OpenAIServerModel
12
 
13
+ # --- Logging ---
14
  logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
15
  logger = logging.getLogger(__name__)
16
 
17
  # --- Constants ---
18
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
19
+ OPENAI_MODEL_ID = os.getenv("OPENAI_MODEL_ID", "gpt-4.1")
20
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
21
+ if not OPENAI_API_KEY:
22
+ raise RuntimeError("Please set OPENAI_API_KEY in your Space secrets.")
23
+
24
+ # --- Configure OpenAI SDK (for tools if needed) ---
25
+ openai.api_key = OPENAI_API_KEY
26
  client = OpenAI()
 
 
 
 
 
 
 
27
 
28
+ # --- Tools ---
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  @tool
31
  def summarize_query(query: str) -> str:
 
33
  Reframes an unclear search query to improve relevance.
34
 
35
  Args:
36
+ query (str): The original search query.
37
 
38
  Returns:
39
+ str: A concise, improved version.
40
  """
41
  return f"Summarize and reframe: {query}"
42
 
43
  @tool
44
  def wikipedia_search(page: str) -> str:
45
  """
46
+ Fetches the summary extract of an English Wikipedia page.
47
 
48
  Args:
49
+ page (str): e.g. 'Mercedes_Sosa_discography'
50
 
51
  Returns:
52
+ str: The pages extract text.
53
  """
54
  try:
55
  url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{page}"
56
+ r = requests.get(url, timeout=10)
57
+ r.raise_for_status()
58
+ return r.json().get("extract", "")
 
 
 
59
  except Exception as e:
60
  logger.exception("Wikipedia lookup failed")
61
  return f"Wikipedia error: {e}"
62
 
 
63
  search_tool = DuckDuckGoSearchTool()
64
  wiki_tool = wikipedia_search
65
  summarize_tool = summarize_query
 
81
  Record new Observation.
82
  5. Thought: integrate observations.
83
 
84
+ Finally, output your answer with the following template:
85
+ FINAL ANSWER: [YOUR FINAL ANSWER].
86
+ YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.
87
+ 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.
88
+ 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.
89
+ 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.
90
 
91
  """
92
 
93
+ # --- Build the Agent with OpenAIServerModel ---
94
+
95
+ model = OpenAIServerModel(
96
+ model_id=OPENAI_MODEL_ID,
97
+ api_key=OPENAI_API_KEY
98
+ )
99
 
 
100
  smart_agent = CodeAgent(
101
  tools=[search_tool, wiki_tool, summarize_tool],
102
+ model=model
103
  )
104
 
105
+ # --- Gradio Wrapper ---
106
 
107
  class BasicAgent:
108
  def __init__(self):
109
+ logger.info("Initialized SmolAgent with OpenAI GPT-4.1")
110
 
111
  def __call__(self, question: str) -> str:
112
+ if not question.strip():
113
+ return "AGENT ERROR: empty question"
 
 
114
  prompt = instruction_prompt.strip() + "\n\nQUESTION: " + question.strip()
 
 
 
115
  try:
116
  return smart_agent.run(prompt)
117
  except Exception as e:
118
  logger.exception("Agent run error")
119
+ return f"AGENT ERROR: {e}"
120
 
121
+ # --- Submission Logic ---
122
 
123
  def run_and_submit_all(profile: gr.OAuthProfile | None):
124
  if not profile:
125
  return "Please log in to Hugging Face.", None
126
 
127
+ username = profile.username
128
+ space_id = os.getenv("SPACE_ID", "")
129
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
130
+ agent = BasicAgent()
131
 
132
+ # fetch
133
  try:
134
  resp = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15)
135
  resp.raise_for_status()
136
+ questions = resp.json() or []
 
 
 
137
  except Exception as e:
138
+ logger.exception("Failed fetch")
139
  return f"Error fetching questions: {e}", None
140
 
141
+ logs, payload = [], []
 
142
  for item in questions:
143
  tid = item.get("task_id")
144
  q = item.get("question")
145
  if not tid or not q:
 
146
  continue
147
  ans = agent(q)
148
  logs.append({"Task ID": tid, "Question": q, "Submitted Answer": ans})
149
  payload.append({"task_id": tid, "submitted_answer": ans})
 
 
150
 
151
  if not payload:
152
  return "Agent did not produce any answers.", pd.DataFrame(logs)
153
 
154
+ # submit
 
155
  try:
156
+ post = requests.post(
157
+ f"{DEFAULT_API_URL}/submit",
158
+ json={"username": username, "agent_code": agent_code, "answers": payload},
159
+ timeout=60
160
+ )
161
  post.raise_for_status()
162
+ result = post.json()
163
  status = (
164
  f"Submission Successful!\n"
165
+ f"User: {result.get('username')}\n"
166
+ f"Score: {result.get('score','N/A')}%\n"
167
+ f"({result.get('correct_count','?')}/"
168
+ f"{result.get('total_attempted','?')})\n"
169
+ f"Message: {result.get('message','')}"
170
  )
171
  return status, pd.DataFrame(logs)
172
  except Exception as e:
173
+ logger.exception("Submit failed")
174
  return f"Submission Failed: {e}", pd.DataFrame(logs)
175
 
176
+ # --- Gradio App ---
177
 
178
  with gr.Blocks() as demo:
179
  gr.Markdown("# SmolAgent GAIA Runner 🚀")
180
  gr.Markdown("""
181
+ **Instructions:**
182
+ 1. Clone this space.
183
+ 2. In Settings → Secrets, add `OPENAI_API_KEY` and (optionally) `OPENAI_MODEL_ID`.
184
+ 3. Log in to Hugging Face.
185
  4. Click **Run Evaluation & Submit All Answers**.
186
  """)
187
  gr.LoginButton()
188
+ btn = gr.Button("Run Evaluation & Submit All Answers")
189
+ out_status = gr.Textbox(label="Status", lines=5, interactive=False)
190
+ out_table = gr.DataFrame(label="Questions & Answers", wrap=True)
191
+ btn.click(run_and_submit_all, outputs=[out_status, out_table])
192
 
193
  if __name__ == "__main__":
194
  demo.launch(debug=True, share=False)