GT5557 commited on
Commit
ebbd7e5
·
verified ·
1 Parent(s): 777ed76

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -237
app.py CHANGED
@@ -1,6 +1,5 @@
1
  # ==========================================================
2
- # UPDATED app.py
3
- # Observability + Model Logs + Adaptive Recursion
4
  # ==========================================================
5
 
6
  import os
@@ -17,57 +16,39 @@ from agent import (
17
  build_graph,
18
  extract_final_answer,
19
  extract_tools_used,
20
- classify_question,
21
  get_last_trace,
22
  )
23
 
24
  # ==========================================================
25
- # LIVE LOGGING
26
  # ==========================================================
27
 
28
  sys.stdout.reconfigure(line_buffering=True)
29
 
30
- # ==========================================================
31
- # CONFIG
32
- # ==========================================================
33
-
34
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
35
 
36
- TIMEOUTS = {
37
- "simple": 20,
38
- "medium": 35,
39
- "hard": 55,
40
- }
41
-
42
- PAUSE = {
43
- "simple": 0.5,
44
- "medium": 1.0,
45
- "hard": 1.5,
46
- }
47
-
48
- RECURSION_LIMITS = {
49
- "simple": 6,
50
- "medium": 10,
51
- "hard": 14,
52
- }
53
 
54
  # ==========================================================
55
- # LOGGER
56
  # ==========================================================
57
 
58
- def log(msg):
59
  print(msg, flush=True)
60
 
61
  # ==========================================================
62
- # THREAD TIMEOUT
63
  # ==========================================================
64
 
65
- def run_with_timeout(fn, timeout_seconds):
66
- state = {
67
- "done": False,
68
- "result": None,
69
- "error": None
70
- }
71
 
72
  def target():
73
  try:
@@ -82,11 +63,9 @@ def run_with_timeout(fn, timeout_seconds):
82
  thread.join(timeout_seconds)
83
 
84
  if not state["done"]:
85
- return None, True
86
-
87
  if state["error"]:
88
  raise state["error"]
89
-
90
  return state["result"], False
91
 
92
  # ==========================================================
@@ -95,331 +74,209 @@ def run_with_timeout(fn, timeout_seconds):
95
 
96
  class BenchmarkAgent:
97
  def __init__(self):
98
- log("Initializing agent...")
99
  self.graph = build_graph()
100
  log("Agent ready.")
101
 
102
- def __call__(self, question: str):
103
- difficulty = classify_question(question)
104
- recursion_limit = RECURSION_LIMITS[difficulty]
105
-
106
  result = self.graph.invoke(
107
  {"messages": [HumanMessage(content=question)]},
108
- {"recursion_limit": recursion_limit}
109
  )
110
-
111
- answer = extract_final_answer(result)
112
- tools = extract_tools_used(result)
113
- trace = get_last_trace()
114
-
115
- if not answer:
116
- answer = "N/A"
117
-
118
  return answer, tools, trace
119
 
120
  # ==========================================================
121
- # HELPERS
122
  # ==========================================================
123
 
124
- def fetch_questions(url):
125
  r = requests.get(url, timeout=20)
126
  r.raise_for_status()
127
  return r.json()
128
 
129
 
130
- def submit_answers(url, payload):
131
  r = requests.post(url, json=payload, timeout=60)
132
  r.raise_for_status()
133
  return r.json()
134
 
135
-
136
- def sort_questions(items):
137
- order = {
138
- "simple": 0,
139
- "medium": 1,
140
- "hard": 2
141
- }
142
-
143
- return sorted(
144
- items,
145
- key=lambda x: order.get(
146
- classify_question(
147
- x.get("question", "")
148
- ),
149
- 9
150
- )
151
- )
152
-
153
  # ==========================================================
154
  # MAIN RUNNER
155
  # ==========================================================
156
 
157
  def run_and_submit_all(profile: gr.OAuthProfile | None):
158
  if not profile:
159
- return "Please login first.", None
160
 
161
  space_id = os.getenv("SPACE_ID")
162
-
163
  if not space_id:
164
- return "SPACE_ID missing.", None
165
-
166
- username = profile.username.strip()
167
 
 
168
  questions_url = f"{DEFAULT_API_URL}/questions"
169
- submit_url = f"{DEFAULT_API_URL}/submit"
170
-
171
- agent_code = (
172
- f"https://huggingface.co/spaces/"
173
- f"{space_id}/tree/main"
174
- )
175
 
176
  log("=" * 80)
177
- log(f"User: {username}")
178
- log("Starting benchmark run...")
 
179
  log("=" * 80)
180
 
181
- # ------------------------------------------------------
182
- # INIT
183
- # ------------------------------------------------------
184
-
185
  try:
186
  agent = BenchmarkAgent()
187
  except Exception as e:
188
- return f"Agent init failed: {e}", None
189
-
190
- # ------------------------------------------------------
191
- # FETCH QUESTIONS
192
- # ------------------------------------------------------
193
 
 
194
  try:
195
  questions = fetch_questions(questions_url)
196
  except Exception as e:
197
- return f"Failed to fetch questions: {e}", None
198
-
199
- questions = sort_questions(questions)
200
 
201
- total = len(questions)
202
-
203
- answers = []
204
- logs = []
205
-
206
- answered = 0
207
  timeout_count = 0
208
- error_count = 0
209
-
210
- # ------------------------------------------------------
211
- # LOOP
212
- # ------------------------------------------------------
213
 
 
214
  for idx, item in enumerate(questions, start=1):
215
- task_id = item.get("task_id")
216
  question = item.get("question", "")
217
 
218
- difficulty = classify_question(question)
219
- timeout = TIMEOUTS[difficulty]
220
- recursion_limit = RECURSION_LIMITS[difficulty]
221
-
222
- log("\n" + "-" * 80)
223
- log(f"Question : {idx}/{total}")
224
- log(f"Task ID : {task_id}")
225
- log(f"Difficulty : {difficulty.upper()}")
226
- log(f"Timeout : {timeout}s")
227
- log(f"Recursion : {recursion_limit}")
228
- log(f"Question Text : {question[:180]}")
229
  log("-" * 80)
230
 
231
  start = time.time()
232
-
233
  submitted_answer = "N/A"
234
- tools_used = []
235
- trace = {}
236
  status = "UNKNOWN"
237
 
238
  try:
239
  def solve():
240
  return agent(question)
241
 
242
- result, did_timeout = run_with_timeout(
243
- solve,
244
- timeout
245
- )
246
-
247
- elapsed = round(
248
- time.time() - start,
249
- 1
250
- )
251
 
252
  if did_timeout:
253
  timeout_count += 1
254
  status = f"TIMEOUT ({elapsed}s)"
255
- submitted_answer = "N/A"
256
-
257
  else:
258
  submitted_answer, tools_used, trace = result
259
-
260
  if submitted_answer != "N/A":
261
  answered += 1
262
-
263
  status = f"OK ({elapsed}s)"
264
 
265
  except Exception as e:
266
- elapsed = round(
267
- time.time() - start,
268
- 1
269
- )
270
-
271
  error_count += 1
272
  status = f"ERROR ({elapsed}s)"
273
- submitted_answer = "N/A"
274
-
275
- trace = {
276
- "model": "N/A",
277
- "fallback": "N/A",
278
- "model_error": str(e)
279
- }
280
 
281
- # --------------------------------------------------
282
- # LOG DETAILS
283
- # --------------------------------------------------
284
-
285
- log(f"Model Used : {trace.get('model','N/A')}")
286
- log(f"Fallback Used : {trace.get('fallback','N/A')}")
287
- log(f"Model Error : {trace.get('model_error','None')}")
288
- log(f"Tools Used : {', '.join(tools_used) if tools_used else 'None'}")
289
- log(f"Answer : {submitted_answer}")
290
- log(f"Status : {status}")
291
-
292
- # --------------------------------------------------
293
- # STORE
294
- # --------------------------------------------------
295
 
296
  answers.append({
297
  "task_id": task_id,
298
- "submitted_answer": str(submitted_answer).strip()
299
  })
300
 
301
  logs.append({
302
- "No": idx,
303
- "Task ID": task_id,
304
- "Difficulty": difficulty.upper(),
305
- "Model Used": trace.get("model", "N/A"),
306
- "Fallback": trace.get("fallback", "N/A"),
307
- "Model Error": trace.get("model_error", "None"),
308
- "Tools Used": ", ".join(tools_used),
309
  "Submitted Answer": submitted_answer,
310
- "Status": status,
311
- "Question": question[:140]
312
  })
313
 
314
- time.sleep(PAUSE[difficulty])
315
-
316
- # ------------------------------------------------------
317
- # SAVE CSV
318
- # ------------------------------------------------------
319
 
 
320
  df = pd.DataFrame(logs)
321
-
322
  try:
323
  df.to_csv("last_run_results.csv", index=False)
324
- log("Saved results CSV.")
325
  except Exception:
326
  pass
327
 
328
- # ------------------------------------------------------
329
- # SUBMIT
330
- # ------------------------------------------------------
331
-
332
  payload = {
333
- "username": username,
334
  "agent_code": agent_code,
335
- "answers": answers
336
  }
337
 
338
  try:
339
- result = submit_answers(
340
- submit_url,
341
- payload
342
- )
343
-
344
  final_status = (
345
  f"SUCCESS\n\n"
346
- f"User: {result.get('username')}\n"
347
- f"Score: {result.get('score','N/A')}%\n"
348
- f"Correct: "
349
- f"{result.get('correct_count','?')}/"
350
- f"{result.get('total_attempted','?')}\n"
351
  f"Answered: {answered}/{total}\n"
352
  f"Timeouts: {timeout_count}\n"
353
- f"Errors: {error_count}\n"
354
- f"{result.get('message','')}"
355
  )
356
-
357
  log("=" * 80)
358
  log(final_status)
359
  log("=" * 80)
360
-
361
  return final_status, df
362
 
363
  except Exception as e:
364
  fail = (
365
- f"Submission failed:\n{e}\n\n"
366
  f"Answered: {answered}/{total}\n"
367
  f"Timeouts: {timeout_count}\n"
368
- f"Errors: {error_count}"
369
  )
370
-
371
  return fail, df
372
 
373
  # ==========================================================
374
- # UI
375
  # ==========================================================
376
 
377
  with gr.Blocks() as demo:
378
- gr.Markdown("# Benchmark Agent - Observability Mode")
379
-
380
  gr.Markdown(
381
  """
382
- ### Features
383
-
384
- - Adaptive recursion limits
385
- - Logs selected model per question
386
- - Logs fallback model usage
387
- - Logs rate/model errors
388
- - Logs tools used
389
- - CSV export
390
- - Auto submit
391
  """
392
  )
393
 
394
  gr.LoginButton()
395
 
396
- run_btn = gr.Button(
397
- "Run Evaluation & Submit",
398
- variant="primary",
399
- size="lg"
400
- )
401
-
402
- status_box = gr.Textbox(
403
- label="Status",
404
- lines=12
405
- )
406
 
407
- table = gr.DataFrame(
408
- label="Per Question Analytics",
409
- wrap=True
410
- )
411
 
412
- run_btn.click(
413
- fn=run_and_submit_all,
414
- outputs=[status_box, table]
415
- )
416
 
417
  # ==========================================================
418
- # MAIN
419
  # ==========================================================
420
 
421
  if __name__ == "__main__":
422
- demo.launch(
423
- debug=True,
424
- ssr_mode=False
425
- )
 
1
  # ==========================================================
2
+ # app.py — Flat routing, uniform timeout + recursion for all questions
 
3
  # ==========================================================
4
 
5
  import os
 
16
  build_graph,
17
  extract_final_answer,
18
  extract_tools_used,
 
19
  get_last_trace,
20
  )
21
 
22
  # ==========================================================
23
+ # CONFIG
24
  # ==========================================================
25
 
26
  sys.stdout.reconfigure(line_buffering=True)
27
 
 
 
 
 
28
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
29
 
30
+ # Single values — no per-difficulty branching
31
+ TIMEOUT_SECONDS = 60
32
+ RECURSION_LIMIT = 12
33
+ PAUSE_SECONDS = 1.0
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  # ==========================================================
36
+ # LOGGING
37
  # ==========================================================
38
 
39
+ def log(msg: str) -> None:
40
  print(msg, flush=True)
41
 
42
  # ==========================================================
43
+ # TIMEOUT WRAPPER
44
  # ==========================================================
45
 
46
+ def run_with_timeout(fn, timeout_seconds: int):
47
+ """
48
+ Run fn() in a daemon thread.
49
+ Returns (result, timed_out). Raises if fn raised.
50
+ """
51
+ state = {"done": False, "result": None, "error": None}
52
 
53
  def target():
54
  try:
 
63
  thread.join(timeout_seconds)
64
 
65
  if not state["done"]:
66
+ return None, True # timed out
 
67
  if state["error"]:
68
  raise state["error"]
 
69
  return state["result"], False
70
 
71
  # ==========================================================
 
74
 
75
  class BenchmarkAgent:
76
  def __init__(self):
77
+ log("Initializing agent graph...")
78
  self.graph = build_graph()
79
  log("Agent ready.")
80
 
81
+ def __call__(self, question: str) -> tuple[str, list, dict]:
 
 
 
82
  result = self.graph.invoke(
83
  {"messages": [HumanMessage(content=question)]},
84
+ {"recursion_limit": RECURSION_LIMIT},
85
  )
86
+ answer = extract_final_answer(result) or "N/A"
87
+ tools = extract_tools_used(result)
88
+ trace = get_last_trace()
 
 
 
 
 
89
  return answer, tools, trace
90
 
91
  # ==========================================================
92
+ # API HELPERS
93
  # ==========================================================
94
 
95
+ def fetch_questions(url: str) -> list[dict]:
96
  r = requests.get(url, timeout=20)
97
  r.raise_for_status()
98
  return r.json()
99
 
100
 
101
+ def submit_answers(url: str, payload: dict) -> dict:
102
  r = requests.post(url, json=payload, timeout=60)
103
  r.raise_for_status()
104
  return r.json()
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  # ==========================================================
107
  # MAIN RUNNER
108
  # ==========================================================
109
 
110
  def run_and_submit_all(profile: gr.OAuthProfile | None):
111
  if not profile:
112
+ return "Please log in first.", None
113
 
114
  space_id = os.getenv("SPACE_ID")
 
115
  if not space_id:
116
+ return "SPACE_ID environment variable is missing.", None
 
 
117
 
118
+ username = profile.username.strip()
119
  questions_url = f"{DEFAULT_API_URL}/questions"
120
+ submit_url = f"{DEFAULT_API_URL}/submit"
121
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
 
 
 
 
122
 
123
  log("=" * 80)
124
+ log(f"User : {username}")
125
+ log(f"Timeout : {TIMEOUT_SECONDS}s per question")
126
+ log(f"Recursion: {RECURSION_LIMIT}")
127
  log("=" * 80)
128
 
129
+ # --- Init agent ---
 
 
 
130
  try:
131
  agent = BenchmarkAgent()
132
  except Exception as e:
133
+ return f"Agent initialisation failed: {e}", None
 
 
 
 
134
 
135
+ # --- Fetch questions ---
136
  try:
137
  questions = fetch_questions(questions_url)
138
  except Exception as e:
139
+ return f"Could not fetch questions: {e}", None
 
 
140
 
141
+ total = len(questions)
142
+ answers = []
143
+ logs = []
144
+ answered = 0
 
 
145
  timeout_count = 0
146
+ error_count = 0
 
 
 
 
147
 
148
+ # --- Question loop ---
149
  for idx, item in enumerate(questions, start=1):
150
+ task_id = item.get("task_id", "")
151
  question = item.get("question", "")
152
 
153
+ log("")
154
+ log("-" * 80)
155
+ log(f"[{idx}/{total}] Task: {task_id}")
156
+ log(f"Question : {question[:200]}")
 
 
 
 
 
 
 
157
  log("-" * 80)
158
 
159
  start = time.time()
 
160
  submitted_answer = "N/A"
161
+ tools_used: list[str] = []
162
+ trace: dict = {}
163
  status = "UNKNOWN"
164
 
165
  try:
166
  def solve():
167
  return agent(question)
168
 
169
+ result, did_timeout = run_with_timeout(solve, TIMEOUT_SECONDS)
170
+ elapsed = round(time.time() - start, 1)
 
 
 
 
 
 
 
171
 
172
  if did_timeout:
173
  timeout_count += 1
174
  status = f"TIMEOUT ({elapsed}s)"
 
 
175
  else:
176
  submitted_answer, tools_used, trace = result
 
177
  if submitted_answer != "N/A":
178
  answered += 1
 
179
  status = f"OK ({elapsed}s)"
180
 
181
  except Exception as e:
182
+ elapsed = round(time.time() - start, 1)
 
 
 
 
183
  error_count += 1
184
  status = f"ERROR ({elapsed}s)"
185
+ trace = {"model": "N/A", "fallback": "N/A", "model_error": str(e)}
 
 
 
 
 
 
186
 
187
+ log(f"Model : {trace.get('model', 'N/A')}")
188
+ log(f"Fallback : {trace.get('fallback', 'N/A')}")
189
+ log(f"Error : {trace.get('model_error', 'None')}")
190
+ log(f"Tools : {', '.join(tools_used) if tools_used else 'None'}")
191
+ log(f"Answer : {submitted_answer}")
192
+ log(f"Status : {status}")
 
 
 
 
 
 
 
 
193
 
194
  answers.append({
195
  "task_id": task_id,
196
+ "submitted_answer": str(submitted_answer).strip(),
197
  })
198
 
199
  logs.append({
200
+ "No": idx,
201
+ "Task ID": task_id,
202
+ "Model Used": trace.get("model", "N/A"),
203
+ "Fallback": trace.get("fallback", "N/A"),
204
+ "Model Error": trace.get("model_error", "None"),
205
+ "Tools Used": ", ".join(tools_used),
 
206
  "Submitted Answer": submitted_answer,
207
+ "Status": status,
208
+ "Question": question[:140],
209
  })
210
 
211
+ time.sleep(PAUSE_SECONDS)
 
 
 
 
212
 
213
+ # --- Save CSV ---
214
  df = pd.DataFrame(logs)
 
215
  try:
216
  df.to_csv("last_run_results.csv", index=False)
217
+ log("Results saved to last_run_results.csv")
218
  except Exception:
219
  pass
220
 
221
+ # --- Submit ---
 
 
 
222
  payload = {
223
+ "username": username,
224
  "agent_code": agent_code,
225
+ "answers": answers,
226
  }
227
 
228
  try:
229
+ result = submit_answers(submit_url, payload)
 
 
 
 
230
  final_status = (
231
  f"SUCCESS\n\n"
232
+ f"User : {result.get('username')}\n"
233
+ f"Score : {result.get('score', 'N/A')}%\n"
234
+ f"Correct : {result.get('correct_count', '?')}/{result.get('total_attempted', '?')}\n"
 
 
235
  f"Answered: {answered}/{total}\n"
236
  f"Timeouts: {timeout_count}\n"
237
+ f"Errors : {error_count}\n"
238
+ f"{result.get('message', '')}"
239
  )
 
240
  log("=" * 80)
241
  log(final_status)
242
  log("=" * 80)
 
243
  return final_status, df
244
 
245
  except Exception as e:
246
  fail = (
247
+ f"Submission failed: {e}\n\n"
248
  f"Answered: {answered}/{total}\n"
249
  f"Timeouts: {timeout_count}\n"
250
+ f"Errors : {error_count}"
251
  )
 
252
  return fail, df
253
 
254
  # ==========================================================
255
+ # GRADIO UI
256
  # ==========================================================
257
 
258
  with gr.Blocks() as demo:
259
+ gr.Markdown("# Benchmark Agent")
 
260
  gr.Markdown(
261
  """
262
+ **Model**: `qwen/qwen3-32b` (primary) → `llama-3.3-70b-versatile` → `llama-3.1-8b-instant`
263
+ **Routing**: All questions use the same path — no classification.
264
+ **Timeout**: 60 s per question · **Recursion limit**: 12
 
 
 
 
 
 
265
  """
266
  )
267
 
268
  gr.LoginButton()
269
 
270
+ run_btn = gr.Button("Run Evaluation & Submit", variant="primary", size="lg")
 
 
 
 
 
 
 
 
 
271
 
272
+ status_box = gr.Textbox(label="Status", lines=12)
273
+ table = gr.DataFrame(label="Per-question results", wrap=True)
 
 
274
 
275
+ run_btn.click(fn=run_and_submit_all, outputs=[status_box, table])
 
 
 
276
 
277
  # ==========================================================
278
+ # ENTRY POINT
279
  # ==========================================================
280
 
281
  if __name__ == "__main__":
282
+ demo.launch(debug=True, ssr_mode=False)