kirbah commited on
Commit
b13be81
·
1 Parent(s): d65d181

Implemented Step-by-Step Evaluation for questions and download/upload for progress

Browse files
Files changed (1) hide show
  1. app.py +316 -145
app.py CHANGED
@@ -1,202 +1,373 @@
1
  import os
2
  import gradio as gr
3
  import requests
4
- import inspect
5
  import pandas as pd
 
6
  from basic_agent import BasicAgent
 
 
7
 
8
-
9
- # (Keep Constants as is)
10
  # --- Constants ---
11
- DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
-
13
- NO_SUBMIT = True
14
-
15
-
16
- def run_and_submit_all(profile: gr.OAuthProfile | None):
17
- """
18
- Fetches all questions, runs the BasicAgent on them, submits all answers,
19
- and displays the results.
20
- """
21
- # --- Determine HF Space Runtime URL and Repo URL ---
22
- # Get the SPACE_ID for sending link to the code
23
- space_id = os.getenv("SPACE_ID")
24
 
25
- if profile:
26
- username = f"{profile.username}"
27
- print(f"User logged in: {username}")
28
- else:
29
- print("User not logged in.")
30
- return "Please Login to Hugging Face with the button.", None
31
 
32
- api_url = DEFAULT_API_URL
33
- questions_url = f"{api_url}/questions"
34
- submit_url = f"{api_url}/submit"
35
 
36
- # 1. Instantiate Agent ( modify this part to create your agent)
37
  try:
38
- agent = BasicAgent()
39
  except Exception as e:
40
  print(f"Error instantiating agent: {e}")
41
- return f"Error initializing agent: {e}", None
42
- # In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
43
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
44
- print(agent_code)
45
 
46
- # 2. Fetch Questions
47
- print(f"Fetching questions from: {questions_url}")
 
 
 
 
 
 
 
 
 
 
 
 
48
  try:
49
- response = requests.get(questions_url, timeout=15)
50
  response.raise_for_status()
51
  questions_data = response.json()
52
  if not questions_data:
53
- print("Fetched questions list is empty.")
54
- return "Fetched questions list is empty or invalid format.", None
55
  print(f"Fetched {len(questions_data)} questions.")
56
- except requests.exceptions.RequestException as e:
57
- print(f"Error fetching questions: {e}")
58
- return f"Error fetching questions: {e}", None
59
- except requests.exceptions.JSONDecodeError as e:
60
- print(f"Error decoding JSON response from questions endpoint: {e}")
61
- print(f"Response text: {response.text[:500]}")
62
- return f"Error decoding server response for questions: {e}", None
63
  except Exception as e:
64
- print(f"An unexpected error occurred fetching questions: {e}")
65
- return f"An unexpected error occurred fetching questions: {e}", None
66
 
67
- # 3. Run your Agent
68
  results_log = []
69
  answers_payload = []
70
- print(f"Running agent on {len(questions_data)} questions...")
71
- for item in questions_data[:5]:
72
- task_id = item.get("task_id")
73
- question_text = item.get("question")
74
- if not task_id or question_text is None:
75
- print(f"Skipping item with missing task_id or question: {item}")
76
  continue
77
  try:
78
- submitted_answer = agent(question_text)
 
79
  answers_payload.append(
80
  {"task_id": task_id, "submitted_answer": submitted_answer})
81
  results_log.append(
82
- {"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
83
  except Exception as e:
84
- print(f"Error running agent on task {task_id}: {e}")
85
  results_log.append(
86
- {"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
87
-
 
88
  if not answers_payload:
89
- print("Agent did not produce any answers to submit.")
90
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
91
 
92
- # 4. Prepare Submission
93
  submission_data = {"username": username.strip(
94
  ), "agent_code": agent_code, "answers": answers_payload}
95
- status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
96
- print(status_update)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- # 5. Submit
 
 
 
 
 
 
 
 
 
 
 
 
99
  if NO_SUBMIT:
100
- return
101
 
102
- print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
103
  try:
104
- response = requests.post(submit_url, json=submission_data, timeout=60)
 
105
  response.raise_for_status()
106
  result_data = response.json()
107
- final_status = (
108
- f"Submission Successful!\n"
109
- f"User: {result_data.get('username')}\n"
110
- f"Overall Score: {result_data.get('score', 'N/A')}% "
111
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
112
- f"Message: {result_data.get('message', 'No message received.')}"
113
- )
114
- print("Submission successful.")
115
- results_df = pd.DataFrame(results_log)
116
- return final_status, results_df
117
- except requests.exceptions.HTTPError as e:
118
- error_detail = f"Server responded with status {e.response.status_code}."
119
- try:
120
- error_json = e.response.json()
121
- error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
122
- except requests.exceptions.JSONDecodeError:
123
- error_detail += f" Response: {e.response.text[:500]}"
124
- status_message = f"Submission Failed: {error_detail}"
125
- print(status_message)
126
- results_df = pd.DataFrame(results_log)
127
- return status_message, results_df
128
- except requests.exceptions.Timeout:
129
- status_message = "Submission Failed: The request timed out."
130
- print(status_message)
131
- results_df = pd.DataFrame(results_log)
132
- return status_message, results_df
133
- except requests.exceptions.RequestException as e:
134
- status_message = f"Submission Failed: Network error - {e}"
135
- print(status_message)
136
- results_df = pd.DataFrame(results_log)
137
- return status_message, results_df
138
  except Exception as e:
139
- status_message = f"An unexpected error occurred during submission: {e}"
140
- print(status_message)
141
- results_df = pd.DataFrame(results_log)
142
- return status_message, results_df
143
-
144
-
145
- # --- Build Gradio Interface using Blocks ---
146
- with gr.Blocks() as demo:
147
- gr.Markdown("# Basic Agent Evaluation Runner")
148
- gr.Markdown(
149
- """
150
- **Instructions:**
151
-
152
- 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
153
- 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
154
- 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
155
-
156
- ---
157
- **Disclaimers:**
158
- Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
159
- This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a seperate action or even to answer the questions in async.
160
- """
161
- )
162
 
163
  gr.LoginButton()
164
 
165
- run_button = gr.Button("Run Evaluation & Submit All Answers")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
- status_output = gr.Textbox(
168
- label="Run Status / Submission Result", lines=5, interactive=False)
169
- # Removed max_rows=10 from DataFrame constructor
170
- results_table = gr.DataFrame(
171
- label="Questions and Agent Answers", wrap=True)
 
 
 
 
 
 
 
172
 
173
- run_button.click(
174
- fn=run_and_submit_all,
175
- outputs=[status_output, results_table]
176
  )
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  if __name__ == "__main__":
179
  print("\n" + "-"*30 + " App Starting " + "-"*30)
180
- # Check for SPACE_HOST and SPACE_ID at startup for information
181
  space_host_startup = os.getenv("SPACE_HOST")
182
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
183
-
184
  if space_host_startup:
185
- print(f"✅ SPACE_HOST found: {space_host_startup}")
186
  print(
187
- f" Runtime URL should be: https://{space_host_startup}.hf.space")
188
  else:
189
- print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
190
-
191
- if space_id_startup: # Print repo URLs if SPACE_ID is found
192
- print(f"✅ SPACE_ID found: {space_id_startup}")
193
- print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
194
  print(
195
- f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
196
  else:
197
- print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
198
-
199
  print("-"*(60 + len(" App Starting ")) + "\n")
200
-
201
- print("Launching Gradio Interface for Basic Agent Evaluation...")
202
- demo.launch(debug=True, share=False)
 
1
  import os
2
  import gradio as gr
3
  import requests
 
4
  import pandas as pd
5
+ # Ensure basic_agent.py is in the same directory
6
  from basic_agent import BasicAgent
7
+ import json
8
+ import tempfile
9
 
 
 
10
  # --- Constants ---
11
+ DEFAULT_API_URL = os.getenv(
12
+ "API_URL", "https://agents-course-unit4-scoring.hf.space")
13
+ QUESTIONS_URL = f"{DEFAULT_API_URL}/questions"
14
+ SUBMIT_URL = f"{DEFAULT_API_URL}/submit"
15
+ NO_SUBMIT = False
16
+ PLACEHOLDER_UNATTEMPTED = "_NOT_ATTEMPTED_"
 
 
 
 
 
 
 
17
 
18
+ # --- Agent Instantiation Helper ---
 
 
 
 
 
19
 
 
 
 
20
 
21
+ def get_agent_instance():
22
  try:
23
+ return BasicAgent()
24
  except Exception as e:
25
  print(f"Error instantiating agent: {e}")
26
+ gr.Warning(f"Error initializing agent: {e}")
27
+ return None
28
+
29
+ # --- Original run_and_submit_all function ---
30
 
31
+
32
+ def run_and_submit_all(profile: gr.OAuthProfile | None):
33
+ space_id = os.getenv("SPACE_ID")
34
+ if not profile:
35
+ gr.Warning("Please Login first.")
36
+ return "Login required.", pd.DataFrame()
37
+ username = profile.username
38
+ print(f"User logged in: {username}")
39
+ agent = get_agent_instance()
40
+ if not agent:
41
+ return "Failed to initialize agent.", pd.DataFrame()
42
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" if space_id else "local_run"
43
+
44
+ print(f"Fetching questions from: {QUESTIONS_URL}")
45
  try:
46
+ response = requests.get(QUESTIONS_URL, timeout=15)
47
  response.raise_for_status()
48
  questions_data = response.json()
49
  if not questions_data:
50
+ return "Fetched questions list is empty.", pd.DataFrame()
 
51
  print(f"Fetched {len(questions_data)} questions.")
 
 
 
 
 
 
 
52
  except Exception as e:
53
+ return f"Error fetching/decoding questions: {e}", pd.DataFrame()
 
54
 
 
55
  results_log = []
56
  answers_payload = []
57
+ print(f"Running agent on all {len(questions_data)} questions...")
58
+ for item in questions_data:
59
+ task_id, q_text = item.get("task_id"), item.get("question")
60
+ if not task_id or q_text is None:
61
+ print(f"Skipping item: {item}")
 
62
  continue
63
  try:
64
+ print(f"Running agent for Task ID {task_id}...")
65
+ submitted_answer = agent(q_text)
66
  answers_payload.append(
67
  {"task_id": task_id, "submitted_answer": submitted_answer})
68
  results_log.append(
69
+ {"Task ID": task_id, "Question": q_text, "Submitted Answer": submitted_answer})
70
  except Exception as e:
 
71
  results_log.append(
72
+ {"Task ID": task_id, "Question": q_text, "Submitted Answer": f"AGENT ERROR: {e}"})
73
+ results_df = pd.DataFrame(results_log, columns=[
74
+ "Task ID", "Question", "Submitted Answer"]) # Ensure column order
75
  if not answers_payload:
76
+ return "Agent produced no answers.", results_df
 
77
 
 
78
  submission_data = {"username": username.strip(
79
  ), "agent_code": agent_code, "answers": answers_payload}
80
+ if NO_SUBMIT:
81
+ return f"Submission SKIPPED (NO_SUBMIT=True). {len(answers_payload)} answers prepared.", results_df
82
+ print(f"Submitting {len(answers_payload)} answers to: {SUBMIT_URL}")
83
+ try:
84
+ response = requests.post(
85
+ SUBMIT_URL, json=submission_data, timeout=max(60, len(answers_payload) * 2))
86
+ response.raise_for_status()
87
+ result_data = response.json()
88
+ return (f"Submission Successful! User: {result_data.get('username')}, "
89
+ f"Score: {result_data.get('score', 'N/A')}% ({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')}), "
90
+ f"Msg: {result_data.get('message', '')}"), results_df
91
+ except Exception as e:
92
+ return f"Submission Failed: {e}", results_df
93
+
94
+ # --- Step-by-Step Action Functions ---
95
+
96
+
97
+ def load_questions_action(profile: gr.OAuthProfile | None):
98
+ if not profile:
99
+ gr.Warning("Please Login first.")
100
+ return "Login required.", [], pd.DataFrame(), None
101
+ print(f"Fetching questions for {profile.username} from: {QUESTIONS_URL}")
102
+ try:
103
+ response = requests.get(QUESTIONS_URL, timeout=15)
104
+ response.raise_for_status()
105
+ questions_server_data = response.json()
106
+ if not questions_server_data:
107
+ return "Fetched questions list is empty.", [], pd.DataFrame(), None
108
+
109
+ new_results_log = [
110
+ {"Task ID": q.get("task_id"), "Question": q.get(
111
+ "question"), "Submitted Answer": PLACEHOLDER_UNATTEMPTED}
112
+ for q in questions_server_data if q.get("task_id") and q.get("question") is not None
113
+ ]
114
+
115
+ msg = f"Fetched {len(new_results_log)} questions. Progress reset."
116
+ gr.Info(msg)
117
+ return (
118
+ msg,
119
+ # For results_log_list_state (this is the single source of truth now)
120
+ new_results_log,
121
+ pd.DataFrame(new_results_log, columns=[
122
+ "Task ID", "Question", "Submitted Answer"]), # For results_display_table
123
+ None # For q_number_input (reset selection)
124
+ )
125
+ except Exception as e:
126
+ msg = f"Error fetching questions: {e}"
127
+ gr.Error(msg)
128
+ return msg, [], pd.DataFrame(), None
129
+
130
+
131
+ def run_single_question_action(profile: gr.OAuthProfile | None, q_idx: int | None, current_results_log: list):
132
+ if not profile:
133
+ gr.Warning("Please Login first.")
134
+ return "Login required.", current_results_log, pd.DataFrame(current_results_log)
135
+ # current_results_log is results_log_list_state, which has 'Task ID', 'Question', 'Submitted Answer'
136
+ if not current_results_log:
137
+ gr.Warning("No questions loaded.")
138
+ return "No questions loaded.", current_results_log, pd.DataFrame(current_results_log)
139
+ if q_idx is None:
140
+ gr.Warning("Select question or enter index.")
141
+ return "Invalid index.", current_results_log, pd.DataFrame(current_results_log)
142
+ if not 0 <= q_idx < len(current_results_log):
143
+ return f"Index {q_idx} out of bounds.", current_results_log, pd.DataFrame(current_results_log)
144
+
145
+ agent = get_agent_instance()
146
+ if not agent:
147
+ return "Agent init failed.", current_results_log, pd.DataFrame(current_results_log)
148
+
149
+ # Get question details from the selected row in current_results_log
150
+ item_to_process = current_results_log[q_idx]
151
+ task_id, q_text = item_to_process.get(
152
+ "Task ID"), item_to_process.get("Question")
153
+ if not task_id or q_text is None:
154
+ return f"Invalid question data at index {q_idx}.", current_results_log, pd.DataFrame(current_results_log)
155
+
156
+ print(f"Running for Task ID {task_id} (Index {q_idx}): {q_text[:50]}...")
157
+ try:
158
+ submitted_answer = agent(q_text)
159
+ status_msg = f"Successfully processed Task ID {task_id}."
160
+ except Exception as e:
161
+ submitted_answer = f"AGENT ERROR: {e}"
162
+ status_msg = f"Error on task {task_id}: {e}"
163
+ gr.Error(status_msg)
164
+
165
+ updated_results_log = list(current_results_log) # Make a mutable copy
166
+ updated_results_log[q_idx] = {
167
+ "Task ID": task_id, "Question": q_text, "Submitted Answer": submitted_answer}
168
+
169
+ gr.Info(status_msg if "AGENT ERROR" not in submitted_answer else "Agent run finished with error.")
170
+ return status_msg, updated_results_log, pd.DataFrame(updated_results_log, columns=["Task ID", "Question", "Submitted Answer"])
171
+
172
+
173
+ def download_progress_action(results_log_list: list):
174
+ if not results_log_list:
175
+ gr.Info("No progress to download.")
176
+ return None
177
+ try:
178
+ with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json", encoding='utf-8') as tmpfile:
179
+ json.dump(results_log_list, tmpfile, indent=2)
180
+ gr.Info("Progress file ready.")
181
+ return gr.File(value=tmpfile.name, label="progress.json")
182
+ except Exception as e:
183
+ gr.Error(f"Error preparing download: {e}")
184
+ return None
185
+
186
+
187
+ def load_progress_action(uploaded_file_obj):
188
+ if uploaded_file_obj is None:
189
+ gr.Warning("No file uploaded.")
190
+ return "No file.", [], pd.DataFrame(), None
191
+ try:
192
+ with open(uploaded_file_obj.name, "r", encoding='utf-8') as f:
193
+ loaded_data = json.load(f)
194
+ if not isinstance(loaded_data, list) or \
195
+ not all(isinstance(item, dict) and all(k in item for k in ["Task ID", "Question", "Submitted Answer"]) for item in loaded_data):
196
+ raise ValueError(
197
+ "Invalid file format. Expects list of {'Task ID': ..., 'Question': ..., 'Submitted Answer': ...}")
198
+
199
+ new_results_log_list = loaded_data
200
+ msg = f"Loaded {len(new_results_log_list)} entries from file."
201
+ gr.Info(msg)
202
+ return (
203
+ msg,
204
+ new_results_log_list,
205
+ pd.DataFrame(new_results_log_list, columns=[
206
+ "Task ID", "Question", "Submitted Answer"]),
207
+ None # Reset selected index
208
+ )
209
+ except Exception as e:
210
+ msg = f"Error loading progress: {e}"
211
+ gr.Error(msg)
212
+ return msg, [], pd.DataFrame(), None
213
+
214
+
215
+ def submit_current_results_action(profile: gr.OAuthProfile | None, results_log_list: list):
216
+ if not profile:
217
+ gr.Warning("Please Login first.")
218
+ return "Login required."
219
+ username = profile.username
220
+ if not results_log_list:
221
+ return "No results to submit."
222
 
223
+ space_id = os.getenv("SPACE_ID")
224
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" if space_id else "local_run"
225
+
226
+ answers_payload = [
227
+ {"task_id": e["Task ID"], "submitted_answer": e["Submitted Answer"]}
228
+ for e in results_log_list
229
+ if e["Submitted Answer"] != PLACEHOLDER_UNATTEMPTED and "AGENT ERROR" not in str(e.get("Submitted Answer", ""))
230
+ ]
231
+ if not answers_payload:
232
+ return "No attempted (non-error) answers to submit."
233
+
234
+ submission_data = {"username": username.strip(
235
+ ), "agent_code": agent_code, "answers": answers_payload}
236
  if NO_SUBMIT:
237
+ return f"Submission SKIPPED. {len(answers_payload)} answers ready."
238
 
239
+ gr.Info(f"Submitting {len(answers_payload)} answers for '{username}'...")
240
  try:
241
+ response = requests.post(
242
+ SUBMIT_URL, json=submission_data, timeout=max(60, len(answers_payload)*2))
243
  response.raise_for_status()
244
  result_data = response.json()
245
+ return (f"Submission Successful! User: {result_data.get('username')}, Score: {result_data.get('score', 'N/A')}% "
246
+ f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')}), Msg: {result_data.get('message', '')}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  except Exception as e:
248
+ return f"Submission Failed: {e}"
249
+
250
+
251
+ # --- Build Gradio Interface ---
252
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
253
+ gr.Markdown("# Enhanced Agent Evaluation Runner")
254
+ # ... Instructions markdown ...
255
+
256
+ # Single source of truth for the state of all questions and their answers
257
+ results_log_list_state = gr.State([])
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
  gr.LoginButton()
260
 
261
+ with gr.Tabs():
262
+ with gr.TabItem("Step-by-Step Evaluation"):
263
+ gr.Markdown("## Evaluation Workflow")
264
+
265
+ with gr.Row():
266
+ load_questions_button = gr.Button(
267
+ "1. Load Questions from Server", variant="secondary")
268
+ load_q_status = gr.Textbox(
269
+ label="Load Status", interactive=False, lines=1)
270
+
271
+ gr.Markdown("### 2. Select a Question and Run Agent")
272
+ # This table is now the main display for questions and answers
273
+ results_display_table = gr.DataFrame(
274
+ label="Questions & Answers (Select row to run agent)",
275
+ headers=["Task ID", "Question", "Submitted Answer"],
276
+ row_count=10,
277
+ wrap=True,
278
+ interactive=True # Allows row selection
279
+ )
280
+ with gr.Row():
281
+ q_number_input = gr.Number(
282
+ label="Selected Question Index", minimum=0, precision=0, step=1, value=None, interactive=True)
283
+ run_single_q_button = gr.Button(
284
+ "Run Agent for Selected Index", variant="primary")
285
+ single_q_status = gr.Textbox(
286
+ label="Run Single Status", interactive=False, lines=1)
287
+
288
+ with gr.Accordion("3. Manage Full Progress (Download/Upload)", open=False):
289
+ download_file_output = gr.File(
290
+ label="Download Link", interactive=False)
291
+ download_button = gr.Button("Download All Progress")
292
+ with gr.Row():
293
+ upload_file_input = gr.File(
294
+ label="Upload Progress File (JSON)", type="filepath", file_types=[".json"])
295
+ load_progress_button = gr.Button("Load Uploaded File")
296
+ upload_status = gr.Textbox(
297
+ label="Upload Status", interactive=False, lines=1)
298
+
299
+ gr.Markdown("### 4. Submit Results")
300
+ submit_step_by_step_button = gr.Button(
301
+ "Submit Attempted Answers", variant="primary")
302
+ submit_sbs_status = gr.Textbox(
303
+ label="Submission Status", lines=3, interactive=False)
304
+
305
+ with gr.TabItem("Run All & Submit (Original Batch)"):
306
+ gr.Markdown("## Original Batch Runner")
307
+ original_run_button = gr.Button(
308
+ "Run All Questions & Submit", variant="primary")
309
+ original_status_output = gr.Textbox(
310
+ label="Batch Run Status / Result", lines=3, interactive=False)
311
+ original_results_table = gr.DataFrame(label="Batch Run Q&A", wrap=True, interactive=False, headers=[
312
+ "Task ID", "Question", "Submitted Answer"])
313
+
314
+ # --- Wire up Step-by-Step controls ---
315
+ load_questions_button.click(
316
+ fn=load_questions_action, inputs=[],
317
+ outputs=[load_q_status, results_log_list_state,
318
+ results_display_table, q_number_input]
319
+ )
320
 
321
+ def handle_select_question_from_results_table(evt: gr.SelectData):
322
+ if evt.index is not None:
323
+ # evt.index should be the row index (int) for single row selection
324
+ # If it's a tuple (row, col) for cell selection, take index[0]
325
+ if isinstance(evt.index, tuple):
326
+ return evt.index[0]
327
+ elif isinstance(evt.index, int):
328
+ return evt.index
329
+ # Handle list for multi-select if it were enabled (take first)
330
+ elif isinstance(evt.index, list) and evt.index:
331
+ return evt.index[0]
332
+ return None # No change or clear if no valid selection
333
 
334
+ results_display_table.select(
335
+ fn=handle_select_question_from_results_table, inputs=None, outputs=[q_number_input], show_progress="hidden"
 
336
  )
337
 
338
+ run_single_q_button.click(
339
+ fn=run_single_question_action,
340
+ inputs=[q_number_input, results_log_list_state],
341
+ outputs=[single_q_status, results_log_list_state, results_display_table]
342
+ )
343
+ download_button.click(download_progress_action, [
344
+ results_log_list_state], [download_file_output])
345
+ load_progress_button.click(
346
+ load_progress_action, [upload_file_input],
347
+ [upload_status, results_log_list_state,
348
+ results_display_table, q_number_input]
349
+ )
350
+ submit_step_by_step_button.click(
351
+ submit_current_results_action, [
352
+ results_log_list_state], [submit_sbs_status]
353
+ )
354
+
355
+ original_run_button.click(run_and_submit_all, [], [
356
+ original_status_output, original_results_table])
357
+
358
  if __name__ == "__main__":
359
  print("\n" + "-"*30 + " App Starting " + "-"*30)
 
360
  space_host_startup = os.getenv("SPACE_HOST")
361
+ space_id_startup = os.getenv("SPACE_ID")
 
362
  if space_host_startup:
 
363
  print(
364
+ f" SPACE_HOST: {space_host_startup}, URL: https://{space_host_startup}.hf.space")
365
  else:
366
+ print("ℹ️ SPACE_HOST not found (local run?).")
367
+ if space_id_startup:
 
 
 
368
  print(
369
+ f" SPACE_ID: {space_id_startup}, Repo: https://huggingface.co/spaces/{space_id_startup}")
370
  else:
371
+ print("ℹ️ SPACE_ID not found. Repo URL cannot be determined.")
 
372
  print("-"*(60 + len(" App Starting ")) + "\n")
373
+ demo.launch(debug=True)