MasterOfHugs commited on
Commit
dfad6bb
·
verified ·
1 Parent(s): 51ed13b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -56
app.py CHANGED
@@ -1,5 +1,4 @@
1
  #!/usr/bin/env python3
2
-
3
  import os
4
  import ast
5
  import operator
@@ -10,11 +9,12 @@ import re
10
  import requests
11
  import pandas as pd
12
  import gradio as gr
 
13
 
14
  from smolagents import CodeAgent, HfApiModel, tool
15
 
16
  # -------------------------
17
- # Minimal tools
18
  # -------------------------
19
  _allowed_ops = {
20
  ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
@@ -34,25 +34,43 @@ def _eval_node(node):
34
  raise ValueError("Unsupported expression")
35
 
36
  def safe_calc(expr: str):
 
37
  tree = ast.parse(expr, mode='eval')
38
  return _eval_node(tree.body)
39
 
 
40
  @tool
41
  def calculator(expr: str) -> str:
42
  """
43
  Safely evaluate a mathematical expression.
44
 
45
  Args:
46
- expr (str): A string containing a math expression like "2 + 2 * 3".
 
47
 
48
  Returns:
49
- str: JSON string with {"expression": expr, "result": value} or {"error": "..."} on failure.
50
  """
51
  try:
52
- val = safe_calc(expr)
53
- return json.dumps({"expression": expr, "result": float(val)})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  except Exception as e:
55
- return json.dumps({"error": f"Calc error: {e}"})
56
 
57
 
58
  @tool
@@ -67,11 +85,13 @@ def get_current_time_in_timezone(timezone: str) -> str:
67
  str: JSON string with {"timezone": timezone, "local_time": "..."} or {"error": "..."} on failure.
68
  """
69
  try:
 
 
70
  tz = pytz.timezone(timezone)
71
  local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
72
  return json.dumps({"timezone": timezone, "local_time": local_time})
73
  except Exception as e:
74
- return json.dumps({"error": f"Timezone error: {e}"})
75
 
76
 
77
  # -------------------------
@@ -79,22 +99,26 @@ def get_current_time_in_timezone(timezone: str) -> str:
79
  # -------------------------
80
  prompt_templates = None
81
  try:
82
- import yaml
83
  with open("prompts.yaml", "r") as fh:
84
  prompt_templates = yaml.safe_load(fh)
85
  except Exception:
86
  prompt_templates = None
87
 
88
-
89
  # -------------------------
90
  # HfApiModel + CodeAgent
91
  # -------------------------
92
- hf_token = os.getenv("HF_API_TOKEN") # Assurez-vous que votre token HF est défini
 
 
 
 
 
 
93
 
94
  model = HfApiModel(
95
- model_id="HuggingFaceTB/SmolLM-135M-Instruct",
96
- max_tokens=1024,
97
- temperature=0.5,
98
  )
99
 
100
  code_agent = CodeAgent(
@@ -105,74 +129,95 @@ code_agent = CodeAgent(
105
  prompt_templates=prompt_templates
106
  )
107
 
108
-
109
  # -------------------------
110
- # GAIA Agent wrapper
111
  # -------------------------
112
  class GaiaAgentMinimal:
113
  def __init__(self, code_agent):
114
  self.code_agent = code_agent
115
 
116
  def _is_calc(self, q: str) -> bool:
117
- return bool(re.search(r'[\d]', q)) and any(op in q for op in ['+', '-', '*', '/', '%', '^'])
 
 
 
 
 
 
 
 
 
 
 
118
 
119
  def _is_time(self, q: str) -> bool:
 
 
120
  ql = q.lower()
121
- return "time" in ql or "heure" in ql or "quelle heure" in ql or "what time" in ql
122
 
123
  def run(self, question: str) -> str:
124
  try:
125
- q = question.strip()
 
126
 
127
- # Calculator queries
128
  if self._is_calc(q):
 
129
  m = re.search(r'([0-9\.\s\+\-\*\/\^\%\(\)]+)', q)
130
  expr = m.group(1) if m else q
131
  return calculator(expr)
132
 
133
- # Time queries
134
  if self._is_time(q):
135
- if "paris" in q.lower() or "france" in q.lower():
136
- tz = "Europe/Paris"
137
- else:
138
- tz = "UTC"
139
  return get_current_time_in_timezone(tz)
140
 
141
- # fallback LLM
142
- resp = self.code_agent.run(q)
 
 
 
 
 
 
 
 
143
  if isinstance(resp, dict):
 
144
  for key in ("final_answer", "answer", "result", "output"):
145
  if key in resp:
146
  return str(resp[key])
147
  return json.dumps(resp)
148
- return str(resp)
 
 
 
 
 
 
 
149
  except Exception as e:
150
- return json.dumps({"error": f"Agent internal error: {e}"})
151
-
152
 
 
153
  gaia_agent = GaiaAgentMinimal(code_agent)
154
 
155
-
156
  # -------------------------
157
- # GAIA runner
158
  # -------------------------
159
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
160
 
161
  def run_and_submit_all(profile: gr.OAuthProfile | None):
162
  space_id = os.getenv("SPACE_ID")
163
-
164
- if profile:
165
- username = f"{profile.username}"
166
- else:
167
  return "Please Login to Hugging Face with the button.", None
 
168
 
169
- api_url = DEFAULT_API_URL
170
- questions_url = f"{api_url}/questions"
171
- submit_url = f"{api_url}/submit"
172
-
173
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" if space_id else "unknown"
174
 
175
- # Fetch questions
176
  try:
177
  response = requests.get(questions_url, timeout=15)
178
  response.raise_for_status()
@@ -182,7 +227,6 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
182
  except Exception as e:
183
  return f"Error fetching questions: {e}", None
184
 
185
- # Run agent
186
  results_log = []
187
  answers_payload = []
188
  for item in questions_data:
@@ -200,40 +244,31 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
200
  if not answers_payload:
201
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
202
 
203
- submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
204
-
205
- # Submit
206
  try:
207
  response = requests.post(submit_url, json=submission_data, timeout=60)
208
  response.raise_for_status()
209
  result_data = response.json()
210
  final_status = (
211
- f"Submission Successful!\n"
212
- f"User: {result_data.get('username')}\n"
213
  f"Overall Score: {result_data.get('score', 'N/A')}% "
214
  f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
215
  f"Message: {result_data.get('message', 'No message received.')}"
216
  )
217
- results_df = pd.DataFrame(results_log)
218
- return final_status, results_df
219
  except Exception as e:
220
- results_df = pd.DataFrame(results_log)
221
- return f"Submission failed: {e}", results_df
222
-
223
 
224
  # -------------------------
225
  # Gradio UI
226
  # -------------------------
227
  with gr.Blocks() as demo:
228
  gr.Markdown("# Minimal GAIA Agent Runner")
229
- gr.Markdown(
230
- "Log in to Hugging Face, click 'Run Evaluation & Submit All Answers' to fetch questions, run the agent, and submit answers."
231
- )
232
  gr.LoginButton()
233
  run_button = gr.Button("Run Evaluation & Submit All Answers")
234
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
235
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
236
-
237
  run_button.click(fn=run_and_submit_all, outputs=[status_output, results_table])
238
 
239
  if __name__ == "__main__":
 
1
  #!/usr/bin/env python3
 
2
  import os
3
  import ast
4
  import operator
 
9
  import requests
10
  import pandas as pd
11
  import gradio as gr
12
+ import yaml
13
 
14
  from smolagents import CodeAgent, HfApiModel, tool
15
 
16
  # -------------------------
17
+ # Minimal tools (safe)
18
  # -------------------------
19
  _allowed_ops = {
20
  ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
 
34
  raise ValueError("Unsupported expression")
35
 
36
  def safe_calc(expr: str):
37
+ # expr must already be validated (only allowed chars)
38
  tree = ast.parse(expr, mode='eval')
39
  return _eval_node(tree.body)
40
 
41
+
42
  @tool
43
  def calculator(expr: str) -> str:
44
  """
45
  Safely evaluate a mathematical expression.
46
 
47
  Args:
48
+ expr (str): Mathematical expression to evaluate, e.g. "2 + 2 * 3".
49
+ Allowed characters: digits, spaces, parentheses, + - * / % ^ .
50
 
51
  Returns:
52
+ str: JSON string {"expression": expr, "result": value} or {"error": "..."} on failure.
53
  """
54
  try:
55
+ if expr is None:
56
+ return json.dumps({"error": "No expression provided"})
57
+ # sanitize: remove newlines, tabs and leading/trailing whitespace
58
+ expr_clean = str(expr).replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').strip()
59
+ # allow caret ^ as exponent -> convert to **
60
+ expr_clean = expr_clean.replace('^', '**')
61
+ # validate chars: only digits, operators, parentheses, dot and spaces
62
+ if not re.fullmatch(r"[0-9\.\s\+\-\*\/\%\(\)\*]+", expr_clean):
63
+ return json.dumps({"error": "Expression contains invalid characters or is not a simple math expression", "original": expr})
64
+ # extra safety: prevent huge exponentiation etc (limit length)
65
+ if len(expr_clean) > 200:
66
+ return json.dumps({"error": "Expression too long"})
67
+ # parse & evaluate safely
68
+ val = safe_calc(expr_clean)
69
+ return json.dumps({"expression": expr_clean, "result": float(val)})
70
+ except (SyntaxError, ValueError, IndentationError) as e:
71
+ return json.dumps({"error": f"Calc parse error: {str(e)}", "original": expr})
72
  except Exception as e:
73
+ return json.dumps({"error": f"Calc error: {str(e)}", "original": expr})
74
 
75
 
76
  @tool
 
85
  str: JSON string with {"timezone": timezone, "local_time": "..."} or {"error": "..."} on failure.
86
  """
87
  try:
88
+ if not timezone:
89
+ timezone = "UTC"
90
  tz = pytz.timezone(timezone)
91
  local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
92
  return json.dumps({"timezone": timezone, "local_time": local_time})
93
  except Exception as e:
94
+ return json.dumps({"error": f"Timezone error: {e}", "timezone": timezone})
95
 
96
 
97
  # -------------------------
 
99
  # -------------------------
100
  prompt_templates = None
101
  try:
 
102
  with open("prompts.yaml", "r") as fh:
103
  prompt_templates = yaml.safe_load(fh)
104
  except Exception:
105
  prompt_templates = None
106
 
 
107
  # -------------------------
108
  # HfApiModel + CodeAgent
109
  # -------------------------
110
+ # IMPORTANT: set HF_API_TOKEN secret in your Space settings (or export locally)
111
+ # HF will often provide token internally in Spaces; otherwise add secret HF_API_TOKEN.
112
+ hf_token = os.getenv("HF_API_TOKEN")
113
+ if hf_token:
114
+ print("HF_API_TOKEN found in environment.")
115
+ else:
116
+ print("Warning: HF_API_TOKEN not set. HfApiModel may fail if token required by environment.")
117
 
118
  model = HfApiModel(
119
+ model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
120
+ max_tokens=2048,
121
+ temperature=0.5
122
  )
123
 
124
  code_agent = CodeAgent(
 
129
  prompt_templates=prompt_templates
130
  )
131
 
 
132
  # -------------------------
133
+ # GAIA Agent wrapper (fixed)
134
  # -------------------------
135
  class GaiaAgentMinimal:
136
  def __init__(self, code_agent):
137
  self.code_agent = code_agent
138
 
139
  def _is_calc(self, q: str) -> bool:
140
+ # strict heuristic: require an explicit operator or explicit math intent
141
+ if q is None:
142
+ return False
143
+ ql = q.lower()
144
+ # common trigger words indicating calculation
145
+ triggers = ["calculate", "compute", "what is", "how many", "evaluate"]
146
+ if any(tr in ql for tr in triggers) and re.search(r"\d", ql):
147
+ return True
148
+ # or presence of arithmetic operators near digits
149
+ if re.search(r"\d\s*[\+\-\*\/\%\^]\s*\d", q):
150
+ return True
151
+ return False
152
 
153
  def _is_time(self, q: str) -> bool:
154
+ if q is None:
155
+ return False
156
  ql = q.lower()
157
+ return any(tok in ql for tok in ["time", "heure", "quelle heure", "what time", "current time", "local time"])
158
 
159
  def run(self, question: str) -> str:
160
  try:
161
+ q = question.strip() if question else ""
162
+ print(f"[gaia run] question preview: {q[:120]}")
163
 
164
+ # 1) Calculator: strict
165
  if self._is_calc(q):
166
+ # try to extract the math subexpression (first match)
167
  m = re.search(r'([0-9\.\s\+\-\*\/\^\%\(\)]+)', q)
168
  expr = m.group(1) if m else q
169
  return calculator(expr)
170
 
171
+ # 2) Time queries
172
  if self._is_time(q):
173
+ tz = "Europe/Paris" if "paris" in q.lower() or "france" in q.lower() else "UTC"
 
 
 
174
  return get_current_time_in_timezone(tz)
175
 
176
+ # 3) LLM fallback via HfApiModel (wrapped)
177
+ try:
178
+ resp = self.code_agent.run(q)
179
+ except Exception as e:
180
+ # return structured error so GAIA runner sees it
181
+ return json.dumps({"error": f"LLM runtime error: {str(e)}"})
182
+
183
+ # Normalize responses: allow string, dict, number
184
+ if resp is None:
185
+ return json.dumps({"error": "LLM returned no output"})
186
  if isinstance(resp, dict):
187
+ # prefer common keys
188
  for key in ("final_answer", "answer", "result", "output"):
189
  if key in resp:
190
  return str(resp[key])
191
  return json.dumps(resp)
192
+ # primitives (int/float) -> convert to string
193
+ if isinstance(resp, (int, float)):
194
+ return str(resp)
195
+ # otherwise assume string
196
+ s = str(resp).strip()
197
+ if s == "":
198
+ return json.dumps({"error": "LLM returned empty string"})
199
+ return s
200
  except Exception as e:
201
+ return json.dumps({"error": f"Agent internal error: {str(e)}"})
 
202
 
203
+ # instantiate agent
204
  gaia_agent = GaiaAgentMinimal(code_agent)
205
 
 
206
  # -------------------------
207
+ # GAIA runner (unchanged behavior)
208
  # -------------------------
209
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
210
 
211
  def run_and_submit_all(profile: gr.OAuthProfile | None):
212
  space_id = os.getenv("SPACE_ID")
213
+ if not profile:
 
 
 
214
  return "Please Login to Hugging Face with the button.", None
215
+ username = profile.username.strip()
216
 
217
+ questions_url = f"{DEFAULT_API_URL}/questions"
218
+ submit_url = f"{DEFAULT_API_URL}/submit"
 
 
219
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" if space_id else "unknown"
220
 
 
221
  try:
222
  response = requests.get(questions_url, timeout=15)
223
  response.raise_for_status()
 
227
  except Exception as e:
228
  return f"Error fetching questions: {e}", None
229
 
 
230
  results_log = []
231
  answers_payload = []
232
  for item in questions_data:
 
244
  if not answers_payload:
245
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
246
 
247
+ submission_data = {"username": username, "agent_code": agent_code, "answers": answers_payload}
 
 
248
  try:
249
  response = requests.post(submit_url, json=submission_data, timeout=60)
250
  response.raise_for_status()
251
  result_data = response.json()
252
  final_status = (
253
+ f"Submission Successful!\nUser: {result_data.get('username')}\n"
 
254
  f"Overall Score: {result_data.get('score', 'N/A')}% "
255
  f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
256
  f"Message: {result_data.get('message', 'No message received.')}"
257
  )
258
+ return final_status, pd.DataFrame(results_log)
 
259
  except Exception as e:
260
+ return f"Submission failed: {e}", pd.DataFrame(results_log)
 
 
261
 
262
  # -------------------------
263
  # Gradio UI
264
  # -------------------------
265
  with gr.Blocks() as demo:
266
  gr.Markdown("# Minimal GAIA Agent Runner")
267
+ gr.Markdown("Log in to Hugging Face, click 'Run Evaluation & Submit All Answers' to fetch questions, run the agent, and submit answers.")
 
 
268
  gr.LoginButton()
269
  run_button = gr.Button("Run Evaluation & Submit All Answers")
270
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
271
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
 
272
  run_button.click(fn=run_and_submit_all, outputs=[status_output, results_table])
273
 
274
  if __name__ == "__main__":