samsonDzealot commited on
Commit
2d4c66c
·
verified ·
1 Parent(s): d35ad51

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +193 -162
app.py CHANGED
@@ -6,207 +6,249 @@ import pandas as pd
6
  import json
7
  from duckduckgo_search import DDGS
8
  from dotenv import load_dotenv
 
9
 
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
 
12
 
13
  # --- Basic Agent Definition ---
14
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
15
  class BasicAgent:
16
  def __init__(self):
17
  print("BasicAgent initialized.")
18
- # Load environment variables
19
  load_dotenv()
20
  self.api_key = os.getenv("TEST_AGENT_KEY")
21
- self.api_url = DEFAULT_API_URL
 
 
 
 
 
22
  # Load system prompt
23
- with open("prompt.txt", "r") as file:
24
- self.system_prompt = file.read().strip()
 
 
 
 
 
 
 
 
 
25
  # Define tools
26
  self.tools = {
27
- "web_search_tool": lambda search_terms: web_search_tool(search_terms),
28
- "decimal_approximation_tool": lambda number, decimals: decimal_approximation_tool(number, decimals),
29
- "get_files_task_id_tool": lambda task_id: get_files_task_id_tool(task_id),
30
- "image_processing_tool": lambda file_content: image_processing_tool(file_content)
31
  }
32
- def __call__(self, question: dict) -> str:
33
- print(f"Agent received question (first 50 chars): {str(question)[:50]}...")
34
- task_id = question["task_id"]
35
- question_text = question["question"]
36
- conversation = [{"role": "system", "content": self.system_prompt},
37
- {"role": "user", "content": question_text}]
38
- while True:
39
- prompt = "\n".join([msg["content"] for msg in conversation])
40
- response = self._call_llm(prompt)
41
- if response.startswith("ANSWER:"):
42
- answer = response[len("ANSWER:"):].strip()
43
- break
44
- elif response.startswith("TOOL:"):
45
- tool_call = response[len("TOOL:"):].strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  try:
47
- tool_name, args_str = tool_call.split("(", 1)
48
- args_str = args_str.rstrip(")")
49
- if tool_name == "get_files_task_id_tool":
50
- observation = self.tools[tool_name](task_id)
 
 
 
 
 
 
 
 
 
 
 
 
51
  else:
52
- args = eval(args_str)
53
- observation = self.tools[tool_name](args)
54
- conversation.append({"role": "assistant", "content": response})
55
- conversation.append({"role": "user", "content": f"Observation: {observation}"})
 
56
  except Exception as e:
57
- conversation.append({"role": "user", "content": f"Error executing tool: {e}"})
 
 
58
  else:
59
- conversation.append({"role": "user", "content": "Please provide a tool call or the final answer."})
60
- print(f"Agent returning answer: {answer}")
61
- return answer
62
-
 
 
 
 
 
 
 
 
 
 
 
63
  def web_search_tool(search_terms: str) -> str:
64
  """
65
  Retrieves information from the internet using DuckDuckGo Search and returns results in JSON format.
66
-
67
- Args:
68
- search_terms (str): The search query to look up.
69
-
70
- Returns:
71
- str: JSON string containing search results.
72
-
73
- Example:
74
- >>> web_search_tool("H. pylori trial NIH")
75
- '{"results": [{"title": "H. pylori Trial", "snippet": "90 patients enrolled Jan-May 2018"}]}'
76
  """
 
77
  try:
78
  with DDGS() as ddgs:
79
  results = [r for r in ddgs.text(search_terms, max_results=3)]
80
- return str({"results": results})
81
  except Exception as e:
82
- return str({"error": f"Search failed: {str(e)}"})
 
83
 
84
  def decimal_approximation_tool(number: float, decimals: int = 1) -> float:
85
  """
86
  Adjusts a numerical answer to the specified number of decimal places.
87
-
88
  Args:
89
  number (float): The number to round.
90
  decimals (int): Number of decimal places to round to (default is 1).
91
-
92
- Returns:
93
- float: The rounded number.
94
-
95
- Example:
96
- >>> decimal_approximation_tool(4.567, 1)
97
- 4.6
98
  """
99
- return round(float(number), decimals)
 
 
 
 
 
100
 
101
  def get_files_task_id_tool(task_id: str) -> str:
102
  """
103
  Downloads the file associated with the given task_id by making an API call.
104
-
105
- Args:
106
- task_id (str): The ID of the task to fetch the file for.
107
-
108
- Returns:
109
- str: The file content as a string (e.g., text or image description), or an error message.
110
-
111
- Example:
112
- >>> get_files_task_id_tool("task_1")
113
- 'Nutrition facts: Calories 390, Butterfat 11%'
114
  """
 
115
  try:
116
- response = requests.get(f"{DEFAULT_API_URL}/files/{task_id}")
 
 
117
  if response.status_code == 200:
118
- return response.text # Assuming the API returns the file content as a string
119
  else:
120
- return f"Error fetching file for task_id {task_id}: Status {response.status_code}"
121
  except Exception as e:
 
122
  return f"Error fetching file for task_id {task_id}: {str(e)}"
123
 
124
- def image_processing_tool(file_content: str) -> str:
125
- """
126
- Processes an image file (or its description) and extracts text using OCR.
127
-
128
- Args:
129
- file_content (str): The file content or description (e.g., from get_files_task_id_tool).
130
-
131
- Returns:
132
- str: Extracted text from the image or description.
133
-
134
- Example:
135
- >>> image_processing_tool("Nutrition facts: Calories 390, Butterfat 11%")
136
- 'Calories: 390, Butterfat: 11%'
137
- """
138
- # Since API returns a string, we simulate OCR on the description for now
139
- # Placeholder for real OCR: If file_content were an image path or bytes, we'd use pytesseract
140
- try:
141
- # Simulated OCR processing on the string (as a placeholder)
142
- # In a real scenario, file_content would be an image file path or bytes
143
- extracted_text = file_content.replace("Nutrition facts: ", "") # Mock extraction
144
- return f"Extracted: {extracted_text}"
145
- except Exception as e:
146
- return f"OCR error: {str(e)}"
147
- # Uncomment below for actual OCR when file_content is an image path
148
- """
149
- try:
150
- from PIL import Image
151
- import pytesseract
152
- image = Image.open(file_content) # Assuming file_content is a path to an image
153
- extracted_text = pytesseract.image_to_string(image)
154
- return f"Extracted: {extracted_text.strip()}"
155
- except Exception as e:
156
- return f"OCR error: {str(e)}"
157
- """
158
-
159
- def run_and_submit_all( profile: gr.OAuthProfile | None):
160
  """
161
  Fetches all questions, runs the BasicAgent on them, submits all answers,
162
  and displays the results.
163
  """
164
- # --- Determine HF Space Runtime URL and Repo URL ---
165
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
166
 
167
  if profile:
168
- username= f"{profile.username}"
169
  print(f"User logged in: {username}")
170
  else:
171
  print("User not logged in.")
172
  return "Please Login to Hugging Face with the button.", None
173
 
174
- api_url = DEFAULT_API_URL
175
- questions_url = f"{api_url}/questions"
176
- submit_url = f"{api_url}/submit"
177
 
178
- # 1. Instantiate Agent ( modify this part to create your agent)
179
  try:
180
  agent = BasicAgent()
181
  except Exception as e:
182
  print(f"Error instantiating agent: {e}")
183
  return f"Error initializing agent: {e}", None
184
- # 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)
185
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
186
- print(agent_code)
187
 
188
- # 2. Fetch Questions
 
 
189
  print(f"Fetching questions from: {questions_url}")
190
  try:
191
  response = requests.get(questions_url, timeout=15)
192
  response.raise_for_status()
193
  questions_data = response.json()
194
  if not questions_data:
195
- print("Fetched questions list is empty.")
196
- return "Fetched questions list is empty or invalid format.", None
197
  print(f"Fetched {len(questions_data)} questions.")
198
  except requests.exceptions.RequestException as e:
199
  print(f"Error fetching questions: {e}")
200
  return f"Error fetching questions: {e}", None
201
  except requests.exceptions.JSONDecodeError as e:
202
- print(f"Error decoding JSON response from questions endpoint: {e}")
203
- print(f"Response text: {response.text[:500]}")
204
- return f"Error decoding server response for questions: {e}", None
205
  except Exception as e:
206
  print(f"An unexpected error occurred fetching questions: {e}")
207
  return f"An unexpected error occurred fetching questions: {e}", None
208
 
209
- # 3. Run your Agent
210
  results_log = []
211
  answers_payload = []
212
  print(f"Running agent on {len(questions_data)} questions...")
@@ -217,23 +259,24 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
217
  print(f"Skipping item with missing task_id or question: {item}")
218
  continue
219
  try:
220
- submitted_answer = agent(question_text)
221
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
222
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
 
223
  except Exception as e:
224
- print(f"Error running agent on task {task_id}: {e}")
225
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
 
 
226
 
227
  if not answers_payload:
228
  print("Agent did not produce any answers to submit.")
229
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
230
 
231
- # 4. Prepare Submission
232
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
233
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
234
  print(status_update)
235
 
236
- # 5. Submit
237
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
238
  try:
239
  response = requests.post(submit_url, json=submission_data, timeout=60)
@@ -247,8 +290,6 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
247
  f"Message: {result_data.get('message', 'No message received.')}"
248
  )
249
  print("Submission successful.")
250
- results_df = pd.DataFrame(results_log)
251
- return final_status, results_df
252
  except requests.exceptions.HTTPError as e:
253
  error_detail = f"Server responded with status {e.response.status_code}."
254
  try:
@@ -256,63 +297,54 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
256
  error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
257
  except requests.exceptions.JSONDecodeError:
258
  error_detail += f" Response: {e.response.text[:500]}"
259
- status_message = f"Submission Failed: {error_detail}"
260
- print(status_message)
261
- results_df = pd.DataFrame(results_log)
262
- return status_message, results_df
263
  except requests.exceptions.Timeout:
264
- status_message = "Submission Failed: The request timed out."
265
- print(status_message)
266
- results_df = pd.DataFrame(results_log)
267
- return status_message, results_df
268
  except requests.exceptions.RequestException as e:
269
- status_message = f"Submission Failed: Network error - {e}"
270
- print(status_message)
271
- results_df = pd.DataFrame(results_log)
272
- return status_message, results_df
273
  except Exception as e:
274
- status_message = f"An unexpected error occurred during submission: {e}"
275
- print(status_message)
276
- results_df = pd.DataFrame(results_log)
277
- return status_message, results_df
 
278
 
279
 
280
- # --- Build Gradio Interface using Blocks ---
281
  with gr.Blocks() as demo:
282
  gr.Markdown("# Basic Agent Evaluation Runner")
283
  gr.Markdown(
284
  """
285
  **Instructions:**
286
 
287
- 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
288
- 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
289
- 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
 
290
 
291
  ---
292
  **Disclaimers:**
293
- 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).
294
- 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.
295
  """
296
  )
297
 
298
  gr.LoginButton()
299
-
300
  run_button = gr.Button("Run Evaluation & Submit All Answers")
301
-
302
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
303
- # Removed max_rows=10 from DataFrame constructor
304
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
305
 
306
  run_button.click(
307
  fn=run_and_submit_all,
308
- outputs=[status_output, results_table]
 
309
  )
310
 
311
  if __name__ == "__main__":
312
  print("\n" + "-"*30 + " App Starting " + "-"*30)
313
- # Check for SPACE_HOST and SPACE_ID at startup for information
314
  space_host_startup = os.getenv("SPACE_HOST")
315
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
316
 
317
  if space_host_startup:
318
  print(f"✅ SPACE_HOST found: {space_host_startup}")
@@ -320,7 +352,7 @@ if __name__ == "__main__":
320
  else:
321
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
322
 
323
- if space_id_startup: # Print repo URLs if SPACE_ID is found
324
  print(f"✅ SPACE_ID found: {space_id_startup}")
325
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
326
  print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
@@ -328,6 +360,5 @@ if __name__ == "__main__":
328
  print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
329
 
330
  print("-"*(60 + len(" App Starting ")) + "\n")
331
-
332
  print("Launching Gradio Interface for Basic Agent Evaluation...")
333
  demo.launch(debug=True, share=False)
 
6
  import json
7
  from duckduckgo_search import DDGS
8
  from dotenv import load_dotenv
9
+ import ast # For safely evaluating literal structures if needed, though JSON is preferred
10
 
11
  # --- Constants ---
12
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
13
+ OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions"
14
+ MODEL_NAME = "deepseek/deepseek-chat-v3-0324" # Or your preferred OpenRouter model
15
 
16
  # --- Basic Agent Definition ---
 
17
  class BasicAgent:
18
  def __init__(self):
19
  print("BasicAgent initialized.")
 
20
  load_dotenv()
21
  self.api_key = os.getenv("TEST_AGENT_KEY")
22
+ if not self.api_key:
23
+ raise ValueError("TEST_AGENT_KEY (OpenRouter API Key) not found in environment variables.")
24
+ self.scorer_api_url = DEFAULT_API_URL # For fetching files/submitting
25
+ self.llm_api_url = OPENROUTER_API_URL
26
+ self.model_name = MODEL_NAME
27
+
28
  # Load system prompt
29
+ try:
30
+ with open("prompt.txt", "r") as file:
31
+ self.system_prompt = file.read().strip()
32
+ except FileNotFoundError:
33
+ print("Error: prompt.txt not found. Using a default system prompt.")
34
+ self.system_prompt = "You are a helpful AI assistant. Please answer the user's questions. Use tools if necessary by outputting TOOL: {\"name\": \"tool_name\", \"args\": {\"arg_name\": \"value\"}}. When you have the final answer, output ANSWER: your_final_answer."
35
+ except Exception as e:
36
+ print(f"Error loading prompt.txt: {e}. Using a default system prompt.")
37
+ self.system_prompt = "You are a helpful AI assistant. Please answer the user's questions. Use tools if necessary by outputting TOOL: {\"name\": \"tool_name\", \"args\": {\"arg_name\": \"value\"}}. When you have the final answer, output ANSWER: your_final_answer."
38
+
39
+
40
  # Define tools
41
  self.tools = {
42
+ "web_search_tool": web_search_tool,
43
+ "decimal_approximation_tool": decimal_approximation_tool,
44
+ "get_files_task_id_tool": get_files_task_id_tool
45
+ # image_processing_tool removed as requested
46
  }
47
+ print(f"Agent tools initialized: {list(self.tools.keys())}")
48
+
49
+ def _call_llm(self, conversation_history: list) -> str:
50
+ print(f"Calling LLM. Conversation history length: {len(conversation_history)}")
51
+ headers = {
52
+ "Authorization": f"Bearer {self.api_key}",
53
+ "Content-Type": "application/json",
54
+ "HTTP-Referer": os.getenv("SPACE_ID", "http://localhost"), # Recommended by OpenRouter
55
+ "X-Title": os.getenv("SPACE_TITLE", "Test Agent") # Recommended by OpenRouter
56
+ }
57
+ payload = {
58
+ "model": self.model_name,
59
+ "messages": conversation_history,
60
+ "temperature": 0.7, # Adjust as needed
61
+ # "max_tokens": 1000 # Adjust as needed
62
+ }
63
+ try:
64
+ response = requests.post(self.llm_api_url, headers=headers, json=payload, timeout=120)
65
+ response.raise_for_status()
66
+ llm_response_data = response.json()
67
+ if llm_response_data.get("choices") and llm_response_data["choices"][0].get("message"):
68
+ content = llm_response_data["choices"][0]["message"].get("content", "").strip()
69
+ print(f"LLM raw response: {content[:200]}...")
70
+ return content
71
+ else:
72
+ print(f"LLM response malformed: {llm_response_data}")
73
+ return "Error: LLM response was malformed."
74
+ except requests.exceptions.Timeout:
75
+ print("Error: LLM API call timed out.")
76
+ return "Error: LLM call timed out."
77
+ except requests.exceptions.RequestException as e:
78
+ print(f"Error calling LLM API: {e}")
79
+ if e.response is not None:
80
+ print(f"LLM Error Response Status: {e.response.status_code}")
81
+ print(f"LLM Error Response Body: {e.response.text}")
82
+ return f"Error: Failed to communicate with LLM. {str(e)}"
83
+ except Exception as e:
84
+ print(f"An unexpected error occurred during LLM call: {e}")
85
+ return f"Error: An unexpected error occurred communicating with LLM. {str(e)}"
86
+
87
+ def __call__(self, question_data: dict) -> str:
88
+ task_id = question_data.get("task_id")
89
+ question_text = question_data.get("question")
90
+ print(f"Agent received task_id: {task_id}, question (first 50 chars): {str(question_text)[:50]}...")
91
+
92
+ if not task_id or question_text is None:
93
+ print("Error: Missing task_id or question in agent input.")
94
+ return "Error: Invalid input to agent."
95
+
96
+ conversation = [
97
+ {"role": "system", "content": self.system_prompt},
98
+ {"role": "user", "content": question_text}
99
+ ]
100
+
101
+ max_loops = 10 # Prevent infinite loops
102
+ for loop_count in range(max_loops):
103
+ print(f"\nAgent Loop: {loop_count + 1}")
104
+ llm_response = self._call_llm(conversation)
105
+
106
+ if llm_response.startswith("ANSWER:"):
107
+ answer = llm_response[len("ANSWER:"):].strip()
108
+ print(f"Agent returning final answer: {answer}")
109
+ return answer
110
+ elif llm_response.startswith("TOOL:"):
111
+ tool_call_str = llm_response[len("TOOL:"):].strip()
112
+ print(f"Attempting tool call: {tool_call_str}")
113
  try:
114
+ tool_data = json.loads(tool_call_str) # Parse the JSON string
115
+ tool_name = tool_data.get("name")
116
+ tool_args_dict = tool_data.get("args", {})
117
+
118
+ if tool_name in self.tools:
119
+ print(f"Executing tool: {tool_name} with args: {tool_args_dict}")
120
+ # Special handling for get_files_task_id_tool if it doesn't take generic args
121
+ if tool_name == "get_files_task_id_tool":
122
+ # Ensure task_id is passed correctly, not from LLM args unless intended
123
+ observation = self.tools[tool_name](task_id)
124
+ else:
125
+ observation = self.tools[tool_name](**tool_args_dict)
126
+
127
+ print(f"Tool observation: {str(observation)[:200]}...")
128
+ conversation.append({"role": "assistant", "content": llm_response}) # LLM's tool request
129
+ conversation.append({"role": "user", "content": f"Observation: {observation}"}) # Tool result
130
  else:
131
+ print(f"Error: Unknown tool name: {tool_name}")
132
+ conversation.append({"role": "user", "content": f"Error: Unknown tool '{tool_name}'. Available tools are: {', '.join(self.tools.keys())}."})
133
+ except json.JSONDecodeError as e:
134
+ print(f"Error decoding JSON for tool call: {e} - String was: {tool_call_str}")
135
+ conversation.append({"role": "user", "content": f"Error: Invalid tool call format. Expected JSON. {e}"})
136
  except Exception as e:
137
+ print(f"Error executing tool or processing its call: {e}")
138
+ conversation.append({"role": "assistant", "content": llm_response}) # LLM's tool request
139
+ conversation.append({"role": "user", "content": f"Error executing tool {tool_name}: {e}"})
140
  else:
141
+ # If the LLM doesn't use the specified prefixes, treat its response as a potential direct answer or a misstep.
142
+ # Could also be a clarification question from the LLM.
143
+ print(f"LLM response did not start with ANSWER: or TOOL:. Treating as intermediate thought or error. Response: {llm_response[:100]}")
144
+ # Adding it as an assistant message and prompting for a structured response
145
+ conversation.append({"role": "assistant", "content": llm_response})
146
+ conversation.append({"role": "user", "content": "Please respond with either 'TOOL: {\"name\": \"tool_name\", \"args\": {}}' or 'ANSWER: your_final_answer'."})
147
+
148
+ if loop_count == max_loops - 1:
149
+ print("Agent reached max loops. Returning last LLM response or error.")
150
+ return f"Error: Agent reached maximum iteration limit. Last response: {llm_response}"
151
+
152
+ return "Error: Agent loop completed without returning an answer."
153
+
154
+
155
+ # --- Tool Definitions ---
156
  def web_search_tool(search_terms: str) -> str:
157
  """
158
  Retrieves information from the internet using DuckDuckGo Search and returns results in JSON format.
159
+ Args: search_terms (str): The search query to look up.
160
+ Returns: str: JSON string containing search results.
 
 
 
 
 
 
 
 
161
  """
162
+ print(f"Web search tool called with terms: {search_terms}")
163
  try:
164
  with DDGS() as ddgs:
165
  results = [r for r in ddgs.text(search_terms, max_results=3)]
166
+ return json.dumps({"results": results}) # Ensure it's a JSON string
167
  except Exception as e:
168
+ print(f"Web search failed: {e}")
169
+ return json.dumps({"error": f"Search failed: {str(e)}"})
170
 
171
  def decimal_approximation_tool(number: float, decimals: int = 1) -> float:
172
  """
173
  Adjusts a numerical answer to the specified number of decimal places.
 
174
  Args:
175
  number (float): The number to round.
176
  decimals (int): Number of decimal places to round to (default is 1).
177
+ Returns: float: The rounded number.
 
 
 
 
 
 
178
  """
179
+ print(f"Decimal approximation tool called with number: {number}, decimals: {decimals}")
180
+ try:
181
+ return round(float(number), int(decimals))
182
+ except Exception as e:
183
+ print(f"Decimal approximation failed: {e}")
184
+ return f"Error in decimal_approximation_tool: {str(e)}" # Return error as string
185
 
186
  def get_files_task_id_tool(task_id: str) -> str:
187
  """
188
  Downloads the file associated with the given task_id by making an API call.
189
+ Args: task_id (str): The ID of the task to fetch the file for.
190
+ Returns: str: The file content as a string or an error message.
 
 
 
 
 
 
 
 
191
  """
192
+ print(f"Get files tool called with task_id: {task_id}")
193
  try:
194
+ # Assuming DEFAULT_API_URL is the base for the /files endpoint
195
+ file_url = f"{DEFAULT_API_URL}/files/{task_id}"
196
+ response = requests.get(file_url, timeout=30)
197
  if response.status_code == 200:
198
+ return response.text
199
  else:
200
+ return f"Error fetching file for task_id {task_id}: Status {response.status_code}, Response: {response.text}"
201
  except Exception as e:
202
+ print(f"Error in get_files_task_id_tool: {e}")
203
  return f"Error fetching file for task_id {task_id}: {str(e)}"
204
 
205
+ # --- Gradio App ---
206
+ def run_and_submit_all(profile: gr.OAuthProfile | None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  """
208
  Fetches all questions, runs the BasicAgent on them, submits all answers,
209
  and displays the results.
210
  """
211
+ space_id = os.getenv("SPACE_ID")
 
212
 
213
  if profile:
214
+ username = f"{profile.username}"
215
  print(f"User logged in: {username}")
216
  else:
217
  print("User not logged in.")
218
  return "Please Login to Hugging Face with the button.", None
219
 
220
+ scorer_api_url = DEFAULT_API_URL
221
+ questions_url = f"{scorer_api_url}/questions"
222
+ submit_url = f"{scorer_api_url}/submit"
223
 
 
224
  try:
225
  agent = BasicAgent()
226
  except Exception as e:
227
  print(f"Error instantiating agent: {e}")
228
  return f"Error initializing agent: {e}", None
 
 
 
229
 
230
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" if space_id else "local_run_code_link_not_available"
231
+ print(f"Agent code link: {agent_code}")
232
+
233
  print(f"Fetching questions from: {questions_url}")
234
  try:
235
  response = requests.get(questions_url, timeout=15)
236
  response.raise_for_status()
237
  questions_data = response.json()
238
  if not questions_data:
239
+ print("Fetched questions list is empty.")
240
+ return "Fetched questions list is empty or invalid format.", None
241
  print(f"Fetched {len(questions_data)} questions.")
242
  except requests.exceptions.RequestException as e:
243
  print(f"Error fetching questions: {e}")
244
  return f"Error fetching questions: {e}", None
245
  except requests.exceptions.JSONDecodeError as e:
246
+ print(f"Error decoding JSON response from questions endpoint: {e}. Response text: {response.text[:500]}")
247
+ return f"Error decoding server response for questions: {e}", None
 
248
  except Exception as e:
249
  print(f"An unexpected error occurred fetching questions: {e}")
250
  return f"An unexpected error occurred fetching questions: {e}", None
251
 
 
252
  results_log = []
253
  answers_payload = []
254
  print(f"Running agent on {len(questions_data)} questions...")
 
259
  print(f"Skipping item with missing task_id or question: {item}")
260
  continue
261
  try:
262
+ # Pass the whole item dictionary to the agent
263
+ submitted_answer = agent(item)
264
+ answers_payload.append({"task_id": task_id, "submitted_answer": str(submitted_answer)}) # Ensure answer is string
265
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": str(submitted_answer)})
266
  except Exception as e:
267
+ print(f"Error running agent on task {task_id}: {e}")
268
+ import traceback
269
+ traceback.print_exc() # Print full traceback for agent errors
270
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
271
 
272
  if not answers_payload:
273
  print("Agent did not produce any answers to submit.")
274
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
275
 
 
276
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
277
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
278
  print(status_update)
279
 
 
280
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
281
  try:
282
  response = requests.post(submit_url, json=submission_data, timeout=60)
 
290
  f"Message: {result_data.get('message', 'No message received.')}"
291
  )
292
  print("Submission successful.")
 
 
293
  except requests.exceptions.HTTPError as e:
294
  error_detail = f"Server responded with status {e.response.status_code}."
295
  try:
 
297
  error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
298
  except requests.exceptions.JSONDecodeError:
299
  error_detail += f" Response: {e.response.text[:500]}"
300
+ final_status = f"Submission Failed: {error_detail}"
301
+ print(final_status)
 
 
302
  except requests.exceptions.Timeout:
303
+ final_status = "Submission Failed: The request timed out."
304
+ print(final_status)
 
 
305
  except requests.exceptions.RequestException as e:
306
+ final_status = f"Submission Failed: Network error - {e}"
307
+ print(final_status)
 
 
308
  except Exception as e:
309
+ final_status = f"An unexpected error occurred during submission: {e}"
310
+ print(final_status)
311
+
312
+ results_df = pd.DataFrame(results_log)
313
+ return final_status, results_df
314
 
315
 
 
316
  with gr.Blocks() as demo:
317
  gr.Markdown("# Basic Agent Evaluation Runner")
318
  gr.Markdown(
319
  """
320
  **Instructions:**
321
 
322
+ 1. Ensure your `TEST_AGENT_KEY` (OpenRouter API Key) is set in your Hugging Face Space secrets or `.env` file.
323
+ 2. Modify `prompt.txt` to guide the agent, especially for tool use and answer formatting. Remove references to the old `image_processing_tool`.
324
+ 3. Log in to your Hugging Face account using the button below.
325
+ 4. Click 'Run Evaluation & Submit All Answers'.
326
 
327
  ---
328
  **Disclaimers:**
329
+ Agent execution can take time. This setup is a starting point.
 
330
  """
331
  )
332
 
333
  gr.LoginButton()
 
334
  run_button = gr.Button("Run Evaluation & Submit All Answers")
 
335
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
 
336
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
337
 
338
  run_button.click(
339
  fn=run_and_submit_all,
340
+ outputs=[status_output, results_table],
341
+ api_name="run_evaluation" # Added api_name for programmatic access if needed
342
  )
343
 
344
  if __name__ == "__main__":
345
  print("\n" + "-"*30 + " App Starting " + "-"*30)
 
346
  space_host_startup = os.getenv("SPACE_HOST")
347
+ space_id_startup = os.getenv("SPACE_ID")
348
 
349
  if space_host_startup:
350
  print(f"✅ SPACE_HOST found: {space_host_startup}")
 
352
  else:
353
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
354
 
355
+ if space_id_startup:
356
  print(f"✅ SPACE_ID found: {space_id_startup}")
357
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
358
  print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
 
360
  print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
361
 
362
  print("-"*(60 + len(" App Starting ")) + "\n")
 
363
  print("Launching Gradio Interface for Basic Agent Evaluation...")
364
  demo.launch(debug=True, share=False)