GT5557 commited on
Commit
dd2e1fb
Β·
verified Β·
1 Parent(s): d22ff97

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -54
app.py CHANGED
@@ -1,15 +1,49 @@
1
  import os
2
  import time
 
3
  import gradio as gr
4
  import requests
5
  import pandas as pd
6
  from langchain_core.messages import HumanMessage
7
- from agent import build_graph, extract_final_answer
8
 
9
  # --- Constants ---
10
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
11
 
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  class BasicAgent:
14
  """A LangGraph agent with multi-model fallback."""
15
 
@@ -22,11 +56,8 @@ class BasicAgent:
22
 
23
  inputs = {"messages": [HumanMessage(content=question)]}
24
  result = self.graph.invoke(inputs)
25
-
26
- # Use the robust extractor from agent.py
27
  answer = extract_final_answer(result)
28
 
29
- # Guard against empty answers
30
  if not answer or not answer.strip():
31
  answer = "N/A"
32
 
@@ -34,13 +65,16 @@ class BasicAgent:
34
  return answer
35
 
36
 
 
 
 
 
37
  def run_and_submit_all(profile: gr.OAuthProfile | None):
38
- """Fetches questions, runs the LangGraph agent, and submits results."""
39
 
40
- # Check SPACE_ID early
41
  space_id = os.getenv("SPACE_ID")
42
  if not space_id:
43
- return "SPACE_ID environment variable is not set. Add it in your Space settings.", None
44
 
45
  if profile:
46
  username = profile.username
@@ -72,50 +106,78 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
72
  if not questions_data:
73
  return "No questions received from the server.", None
74
 
 
 
 
 
 
 
 
 
 
75
  # Run Agent on Each Question
76
  results_log = []
77
  answers_payload = []
 
 
78
 
79
  for i, item in enumerate(questions_data):
80
  task_id = item.get("task_id")
81
  question_text = item.get("question")
82
- print(f"\n--- Question {i+1}/{len(questions_data)} | Task ID: {task_id} ---")
83
- print(f"Question: {question_text[:100]}...")
 
 
 
 
84
 
85
  try:
86
- submitted_answer = agent(question_text)
87
-
88
- # βœ… CORRECT FIELD NAME: "submitted_answer" (NOT "model_answer")
89
- answers_payload.append({
90
- "task_id": task_id,
91
- "submitted_answer": submitted_answer
92
- })
93
- results_log.append({
94
- "Task ID": task_id,
95
- "Question": question_text,
96
- "Submitted Answer": submitted_answer,
97
- "Status": "OK"
98
- })
 
 
 
99
 
100
  except Exception as e:
101
- print(f"Error on task {task_id}: {e}")
102
- # Still append a placeholder so we don't skip the task entirely
103
- answers_payload.append({
104
- "task_id": task_id,
105
- "submitted_answer": "N/A"
106
- })
107
- results_log.append({
108
- "Task ID": task_id,
109
- "Question": question_text,
110
- "Submitted Answer": f"ERROR: {e}",
111
- "Status": "Error"
112
- })
113
-
114
- # Delay between questions to avoid rate limits
115
- time.sleep(5)
 
 
 
 
 
 
116
 
117
  if not answers_payload:
118
- return "Agent failed on all questions. Check logs.", pd.DataFrame(results_log)
 
 
 
 
119
 
120
  # Submit Answers
121
  submission_data = {
@@ -132,48 +194,59 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
132
  result_data = response.json()
133
 
134
  final_status = (
135
- f"Submission Successful!\n"
136
  f"User: {result_data.get('username')}\n"
137
- f"Overall Score: {result_data.get('score', 'N/A')}% "
138
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
 
139
  f"Message: {result_data.get('message', '')}"
140
  )
141
  print(final_status)
142
  return final_status, pd.DataFrame(results_log)
143
 
144
  except requests.exceptions.HTTPError as e:
145
- # Show the actual API error body for easier debugging
146
  try:
147
  error_detail = e.response.json()
148
  except Exception:
149
  error_detail = e.response.text
150
- msg = f"Submission Failed: {e}\nAPI Response: {error_detail}"
151
  print(msg)
152
  return msg, pd.DataFrame(results_log)
153
 
154
  except Exception as e:
155
- msg = f"Submission Failed: {e}"
156
  print(msg)
157
  return msg, pd.DataFrame(results_log)
158
 
159
 
160
- # --- Gradio UI ---
 
 
 
161
  with gr.Blocks() as demo:
162
- gr.Markdown("# LangGraph Agent β€” GAIA Evaluation")
163
- gr.Markdown("**Instructions:**")
164
- gr.Markdown("1. Login with your Hugging Face account")
165
- gr.Markdown("2. Click **Run Evaluation** to start")
166
- gr.Markdown("3. The agent will answer all questions and submit automatically")
167
- gr.Markdown("_Note: This may take several minutes due to rate-limit delays between questions._")
 
 
 
 
 
 
 
 
168
 
169
  gr.LoginButton()
170
 
171
- run_button = gr.Button("Run Evaluation and Submit All Answers", variant="primary")
172
 
173
  status_output = gr.Textbox(
174
  label="Submission Status",
175
- lines=6,
176
- placeholder="Status will appear here after submission..."
177
  )
178
 
179
  results_table = gr.DataFrame(
 
1
  import os
2
  import time
3
+ import signal
4
  import gradio as gr
5
  import requests
6
  import pandas as pd
7
  from langchain_core.messages import HumanMessage
8
+ from agent import build_graph, extract_final_answer, sort_questions_by_difficulty, classify_difficulty, QUESTION_TIMEOUT_SECONDS
9
 
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
 
13
 
14
+ # ============================================================
15
+ # TIMEOUT HANDLER
16
+ # ============================================================
17
+
18
+ class QuestionTimeoutError(Exception):
19
+ pass
20
+
21
+
22
+ def timeout_handler(signum, frame):
23
+ raise QuestionTimeoutError("Question timed out")
24
+
25
+
26
+ def run_with_timeout(fn, timeout_seconds):
27
+ """Run fn() with a hard timeout. Returns (result, timed_out)."""
28
+ # Use SIGALRM on Unix systems (works on Linux/HF Spaces)
29
+ try:
30
+ signal.signal(signal.SIGALRM, timeout_handler)
31
+ signal.alarm(timeout_seconds)
32
+ result = fn()
33
+ signal.alarm(0) # cancel alarm
34
+ return result, False
35
+ except QuestionTimeoutError:
36
+ signal.alarm(0)
37
+ return None, True
38
+ except Exception as e:
39
+ signal.alarm(0)
40
+ raise e
41
+
42
+
43
+ # ============================================================
44
+ # AGENT CLASS
45
+ # ============================================================
46
+
47
  class BasicAgent:
48
  """A LangGraph agent with multi-model fallback."""
49
 
 
56
 
57
  inputs = {"messages": [HumanMessage(content=question)]}
58
  result = self.graph.invoke(inputs)
 
 
59
  answer = extract_final_answer(result)
60
 
 
61
  if not answer or not answer.strip():
62
  answer = "N/A"
63
 
 
65
  return answer
66
 
67
 
68
+ # ============================================================
69
+ # MAIN EVALUATION FUNCTION
70
+ # ============================================================
71
+
72
  def run_and_submit_all(profile: gr.OAuthProfile | None):
73
+ """Fetches questions, sorts by difficulty, runs agent with timeout, submits."""
74
 
 
75
  space_id = os.getenv("SPACE_ID")
76
  if not space_id:
77
+ return "SPACE_ID environment variable is not set.", None
78
 
79
  if profile:
80
  username = profile.username
 
106
  if not questions_data:
107
  return "No questions received from the server.", None
108
 
109
+ # βœ… Sort questions: simple β†’ medium β†’ hard
110
+ original_count = len(questions_data)
111
+ questions_data = sort_questions_by_difficulty(questions_data)
112
+ print(f"\nπŸ“Š Question difficulty breakdown:")
113
+ for item in questions_data:
114
+ q = item.get("question", "")
115
+ d = classify_difficulty(q)
116
+ print(f" [{d.upper():6}] {q[:60]}...")
117
+
118
  # Run Agent on Each Question
119
  results_log = []
120
  answers_payload = []
121
+ skipped = 0
122
+ timed_out = 0
123
 
124
  for i, item in enumerate(questions_data):
125
  task_id = item.get("task_id")
126
  question_text = item.get("question")
127
+ difficulty = classify_difficulty(question_text)
128
+
129
+ print(f"\n--- Question {i+1}/{original_count} | [{difficulty.upper()}] | Task ID: {task_id} ---")
130
+ print(f"Q: {question_text[:100]}...")
131
+
132
+ start_time = time.time()
133
 
134
  try:
135
+ # Run with hard timeout
136
+ def run_agent():
137
+ return agent(question_text)
138
+
139
+ submitted_answer, did_timeout = run_with_timeout(run_agent, QUESTION_TIMEOUT_SECONDS)
140
+
141
+ elapsed = time.time() - start_time
142
+
143
+ if did_timeout:
144
+ print(f"⏰ TIMED OUT after {QUESTION_TIMEOUT_SECONDS}s β€” skipping.")
145
+ timed_out += 1
146
+ status = f"TIMEOUT ({QUESTION_TIMEOUT_SECONDS}s)"
147
+ submitted_answer = "N/A"
148
+ else:
149
+ print(f"βœ… Answered in {elapsed:.1f}s")
150
+ status = f"OK ({elapsed:.1f}s)"
151
 
152
  except Exception as e:
153
+ elapsed = time.time() - start_time
154
+ print(f"❌ Error on task {task_id} after {elapsed:.1f}s: {e}")
155
+ submitted_answer = "N/A"
156
+ status = f"ERROR ({elapsed:.1f}s)"
157
+
158
+ # Always append β€” even skipped/timed-out questions get "N/A"
159
+ answers_payload.append({
160
+ "task_id": task_id,
161
+ "submitted_answer": str(submitted_answer).strip()
162
+ })
163
+ results_log.append({
164
+ "Task ID": task_id,
165
+ "Difficulty": difficulty.upper(),
166
+ "Question": question_text[:80],
167
+ "Submitted Answer": submitted_answer,
168
+ "Status": status
169
+ })
170
+
171
+ # Short delay between questions
172
+ delay = 3 if difficulty == "simple" else 5
173
+ time.sleep(delay)
174
 
175
  if not answers_payload:
176
+ return "Agent failed on all questions.", pd.DataFrame(results_log)
177
+
178
+ # Summary before submit
179
+ answered = sum(1 for r in results_log if r["Submitted Answer"] != "N/A")
180
+ print(f"\nπŸ“Š Summary: {answered}/{original_count} answered | {timed_out} timed out | {skipped} skipped")
181
 
182
  # Submit Answers
183
  submission_data = {
 
194
  result_data = response.json()
195
 
196
  final_status = (
197
+ f"βœ… Submission Successful!\n"
198
  f"User: {result_data.get('username')}\n"
199
+ f"Score: {result_data.get('score', 'N/A')}%\n"
200
+ f"Correct: {result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')}\n"
201
+ f"Answered: {answered}/{original_count} | Timed out: {timed_out}\n"
202
  f"Message: {result_data.get('message', '')}"
203
  )
204
  print(final_status)
205
  return final_status, pd.DataFrame(results_log)
206
 
207
  except requests.exceptions.HTTPError as e:
 
208
  try:
209
  error_detail = e.response.json()
210
  except Exception:
211
  error_detail = e.response.text
212
+ msg = f"❌ Submission Failed (HTTP {e.response.status_code}):\n{error_detail}"
213
  print(msg)
214
  return msg, pd.DataFrame(results_log)
215
 
216
  except Exception as e:
217
+ msg = f"❌ Submission Failed: {e}"
218
  print(msg)
219
  return msg, pd.DataFrame(results_log)
220
 
221
 
222
+ # ============================================================
223
+ # GRADIO UI
224
+ # ============================================================
225
+
226
  with gr.Blocks() as demo:
227
+ gr.Markdown("# πŸ€– LangGraph GAIA Benchmark Agent")
228
+
229
+ gr.Markdown("""
230
+ **Strategy:**
231
+ - βœ… Questions sorted: Simple β†’ Medium β†’ Hard
232
+ - ⏰ Hard questions skipped if they exceed 90 seconds
233
+ - 🎯 Target: 30%+ success rate on simple questions first
234
+ - πŸ”„ Model stack: Qwen3 32B β†’ Llama 3.3 70B β†’ Llama 3.1 8B
235
+
236
+ **Instructions:**
237
+ 1. Login with Hugging Face
238
+ 2. Click **Run Evaluation**
239
+ 3. Agent answers all questions and submits automatically
240
+ """)
241
 
242
  gr.LoginButton()
243
 
244
+ run_button = gr.Button("Run Evaluation and Submit All Answers", variant="primary", size="lg")
245
 
246
  status_output = gr.Textbox(
247
  label="Submission Status",
248
+ lines=8,
249
+ interactive=False
250
  )
251
 
252
  results_table = gr.DataFrame(