yokewang commited on
Commit
a79be4e
·
verified ·
1 Parent(s): 8291e26

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -74
app.py CHANGED
@@ -1,29 +1,171 @@
1
-
2
  import os
3
- import gradio as gr
4
  import requests
5
- import inspect
6
  import pandas as pd
7
- from dotenv import load_dotenv
8
- from agent import build_graph
9
- from langchain_core.messages import HumanMessage
 
 
 
 
10
 
11
- load_dotenv()
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  # --- Constants ---
14
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
15
 
16
 
17
- def run_and_submit_all(profile: gr.OAuthProfile | None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  """
19
- Fetches all questions, runs the LangGraph Agent on them, submits all answers,
20
  and displays the results.
21
  """
22
  # --- Determine HF Space Runtime URL and Repo URL ---
23
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
24
 
25
  if profile:
26
- username = f"{profile.username}"
27
  print(f"User logged in: {username}")
28
  else:
29
  print("User not logged in.")
@@ -33,17 +175,15 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
33
  questions_url = f"{api_url}/questions"
34
  submit_url = f"{api_url}/submit"
35
 
36
- # 1. Instantiate Agent
37
  try:
38
- # Use the build_graph function from agent.py
39
- agent_graph = build_graph()
40
- print("LangGraph agent initialized.")
41
  except Exception as e:
42
- print(f"Error instantiating agent graph: {e}")
43
- return f"Error initializing agent graph: {e}", None
44
  # 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)
45
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" if space_id else "Agent code link unavailable (SPACE_ID not set)" # Added a check for SPACE_ID
46
- print(f"Agent code link: {agent_code}")
47
 
48
  # 2. Fetch Questions
49
  print(f"Fetching questions from: {questions_url}")
@@ -52,16 +192,16 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
52
  response.raise_for_status()
53
  questions_data = response.json()
54
  if not questions_data:
55
- print("Fetched questions list is empty.")
56
- return "Fetched questions list is empty or invalid format.", None
57
  print(f"Fetched {len(questions_data)} questions.")
58
  except requests.exceptions.RequestException as e:
59
  print(f"Error fetching questions: {e}")
60
  return f"Error fetching questions: {e}", None
61
  except requests.exceptions.JSONDecodeError as e:
62
- print(f"Error decoding JSON response from questions endpoint: {e}")
63
- print(f"Response text: {response.text[:500]}")
64
- return f"Error decoding server response for questions: {e}", None
65
  except Exception as e:
66
  print(f"An unexpected error occurred fetching questions: {e}")
67
  return f"An unexpected error occurred fetching questions: {e}", None
@@ -70,60 +210,25 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
70
  results_log = []
71
  answers_payload = []
72
  print(f"Running agent on {len(questions_data)} questions...")
73
-
74
- # Removed the problematic print statement from here
75
-
76
  for item in questions_data:
77
  task_id = item.get("task_id")
78
  question_text = item.get("question")
79
-
80
  if not task_id or question_text is None:
81
  print(f"Skipping item with missing task_id or question: {item}")
82
  continue
83
-
84
- # Moved the print statement inside the loop, after task_id and question_text are assigned
85
- print(f"--- Starting processing Task ID: {task_id}, Question: {question_text[:100]}...")
86
-
87
  try:
88
- # Invoke the LangGraph agent
89
- result_state = agent_graph.invoke({"messages": [HumanMessage(content=question_text)]})
90
-
91
- # Extract the final answer from the last message
92
- submitted_answer = "Error: Agent did not provide a response." # Default in case extraction fails
93
-
94
- if result_state and "messages" in result_state and result_state["messages"]:
95
- last_message = result_state["messages"][-1]
96
- # The final content is typically in the content attribute of the last message
97
- if hasattr(last_message, 'content') and last_message.content:
98
- submitted_answer = last_message.content
99
- # else: Handle cases where the last message might be a tool message etc.,
100
- # for simplicity, we just use the default error message if content is missing.
101
-
102
- # Ensure submitted_answer is a string
103
- if not isinstance(submitted_answer, str):
104
- submitted_answer = str(submitted_answer)
105
-
106
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
107
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
108
-
109
- # Moved this print statement inside the loop as well
110
- print(f"--- Finished processing Task ID: {task_id}")
111
- # Moved this print statement inside the loop as well
112
- print(f"--- Extracted answer for Task ID: {task_id}: {submitted_answer[:100]}...")
113
-
114
-
115
  except Exception as e:
116
- print(f"Error running agent graph on task {task_id}: {e}")
117
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
118
- # Note: If an error occurs, the 'Finished' and 'Extracted answer' prints for this specific task won't happen,
119
- # which is reasonable behavior.
120
 
121
  if not answers_payload:
122
  print("Agent did not produce any answers to submit.")
123
- # Even if no answers, show the log of errors
124
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
125
 
126
- # 4. Prepare Submission
127
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
128
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
129
  print(status_update)
@@ -174,15 +279,14 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
174
 
175
  # --- Build Gradio Interface using Blocks ---
176
  with gr.Blocks() as demo:
177
- gr.Markdown("# LangGraph Agent Evaluation Runner") # Updated title
178
  gr.Markdown(
179
  """
180
  **Instructions:**
181
 
182
- 1. Please clone this space, then modify the code in `agent.py` and `app.py` to define your agent's logic, the tools, the necessary packages, etc ...
183
- 2. **Make sure you have your `DEEPSEEK_API_KEY` set as a Space Secret.**
184
- 3. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
185
- 4. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
186
 
187
  ---
188
  **Disclaimers:**
@@ -196,6 +300,7 @@ with gr.Blocks() as demo:
196
  run_button = gr.Button("Run Evaluation & Submit All Answers")
197
 
198
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
 
199
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
200
 
201
  run_button.click(
@@ -204,10 +309,10 @@ with gr.Blocks() as demo:
204
  )
205
 
206
  if __name__ == "__main__":
207
- print("\n" + "-" * 30 + " App Starting " + "-" * 30)
208
  # Check for SPACE_HOST and SPACE_ID at startup for information
209
  space_host_startup = os.getenv("SPACE_HOST")
210
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
211
 
212
  if space_host_startup:
213
  print(f"✅ SPACE_HOST found: {space_host_startup}")
@@ -215,14 +320,14 @@ if __name__ == "__main__":
215
  else:
216
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
217
 
218
- if space_id_startup: # Print repo URLs if SPACE_ID is found
219
  print(f"✅ SPACE_ID found: {space_id_startup}")
220
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
221
  print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
222
  else:
223
  print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
224
 
225
- print("-" * (60 + len(" App Starting ")) + "\n")
226
 
227
- print("Launching Gradio Interface for LangGraph Agent Evaluation...") # Updated message
228
- demo.launch(debug=True, share=False, auth=None)
 
 
1
  import os
 
2
  import requests
 
3
  import pandas as pd
4
+ import gradio as gr
5
+ import tempfile
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Union, Optional
9
+ from smolagents import LiteLLMModel, DuckDuckGoSearchTool, CodeAgent, WikipediaSearchTool
10
+ from smolagents.tools import Tool
11
 
 
12
 
13
+ # --- Function to Configure Google Credentials (ESSENTIAL) ---
14
+ def setup_google_credentials():
15
+ """
16
+ Reads Google Cloud credential JSON content from an environment variable,
17
+ writes it to a temporary file, and sets the GOOGLE_APPLICATION_CREDENTIALS
18
+ environment variable to the path of that file.
19
+
20
+ This function should be called before any Google Cloud client library
21
+ (like the one used by LiteLLM for Vertex AI) is initialized.
22
+
23
+ Requires the service account key JSON content to be stored in an
24
+ environment variable named 'GOOGLE_APPLICATION_CREDENTIALS_JSON'.
25
+ Set this in your Hugging Face Space secrets.
26
+ """
27
+ credentials_json_str = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS_JSON")
28
+ if not credentials_json_str:
29
+ print("ERROR: 'GOOGLE_APPLICATION_CREDENTIALS_JSON' secret not found in environment variables.")
30
+ print(" Please ensure you have set this secret in your Hugging Face Space settings.")
31
+ # Depending on requirements, you might want to raise an error here
32
+ # raise ValueError("Secret 'GOOGLE_APPLICATION_CREDENTIALS_JSON' not set.")
33
+ return False # Indicate failure
34
+
35
+ try:
36
+ # Create a secure temporary file to store the credentials
37
+ # delete=False ensures the file persists until the process exits or it's manually cleaned up.
38
+ # We need the file path to set the environment variable.
39
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".json", delete=False, encoding='utf-8') as temp_f:
40
+ temp_f.write(credentials_json_str)
41
+ credentials_path = temp_f.name # Get the path to the temporary file
42
+
43
+ # Set the environment variable that Google client libraries expect
44
+ os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = credentials_path
45
+ print(f"Google Application Credentials successfully set to temporary file: {credentials_path}")
46
+ return True # Indicate success
47
+ except json.JSONDecodeError:
48
+ print("ERROR: Failed to parse the content of 'GOOGLE_APPLICATION_CREDENTIALS_JSON'. Ensure it's valid JSON.")
49
+ return False
50
+ except OSError as e:
51
+ print(f"ERROR: Failed to write credentials to temporary file: {e}")
52
+ return False
53
+ except Exception as e:
54
+ print(f"ERROR: An unexpected error occurred during Google credential setup: {e}")
55
+ # You might want to re-raise the exception depending on your error handling strategy
56
+ # raise e
57
+ return False
58
+
59
+ # --- Call Credential Setup EARLY ---
60
+ # This needs to run before any code (like BasicAgent initialization) tries to use Google Cloud services.
61
+ print("Attempting to configure Google Cloud credentials...")
62
+ CREDENTIALS_CONFIGURED = setup_google_credentials()
63
+ if not CREDENTIALS_CONFIGURED:
64
+ print("WARNING: Google Cloud credentials setup failed. Agent initialization might fail.")
65
+
66
+
67
+ # (Keep Constants as is)
68
  # --- Constants ---
69
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
70
 
71
 
72
+ ### Defining tools ###
73
+
74
+ class ExcelToTextTool(Tool):
75
+ """Render an Excel worksheet as Markdown text."""
76
+
77
+ name = "excel_to_text"
78
+ description = (
79
+ "Read an Excel file and return a Markdown table of the requested sheet. "
80
+ "Accepts either the sheet name or the zero-based index."
81
+ )
82
+
83
+ inputs = {
84
+ "excel_path": {
85
+ "type": "string",
86
+ "description": "Path to the Excel file (.xlsx / .xls).",
87
+ },
88
+ "sheet_name": {
89
+ "type": "string",
90
+ "description": (
91
+ "Worksheet name or zero‑based index *as a string* (optional; default first sheet)."
92
+ ),
93
+ "nullable": True,
94
+ },
95
+ }
96
+
97
+ output_type = "string"
98
+
99
+ def forward(
100
+ self,
101
+ excel_path: str,
102
+ sheet_name: Optional[str] = None,
103
+ ) -> str:
104
+ """Load *excel_path* and return the sheet as a Markdown table."""
105
+
106
+ path = Path(excel_path).expanduser().resolve()
107
+ if not path.exists():
108
+ return f"Error: Excel file not found at {path}"
109
+
110
+ try:
111
+ # Interpret sheet identifier
112
+ sheet: Union[str, int]
113
+ if sheet_name is None or sheet_name == "":
114
+ sheet = 0 # first sheet
115
+ else:
116
+ # If the user passed a numeric string (e.g. "1"), cast to int
117
+ sheet = int(sheet_name) if sheet_name.isdigit() else sheet_name
118
+
119
+ # Load worksheet
120
+ df = pd.read_excel(path, sheet_name=sheet)
121
+
122
+ # Render to Markdown, fallback to tabulate if needed
123
+ if hasattr(pd.DataFrame, "to_markdown"):
124
+ return df.to_markdown(index=False)
125
+ from tabulate import tabulate
126
+
127
+ return tabulate(df, headers="keys", tablefmt="github", showindex=False)
128
+
129
+ except Exception as exc: # broad catch keeps the agent chat‑friendly
130
+ return f"Error reading Excel file: {exc}"
131
+
132
+
133
+ # --- Basic Agent Definition ---
134
+ # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
135
+ class BasicAgent:
136
+ def __init__(self):
137
+ # Assuming you've set GOOGLE_API_KEY in secrets
138
+ google_api_key = os.environ.get("GOOGLE_API_KEY")
139
+ if not google_api_key:
140
+ raise ValueError("GOOGLE_API_KEY environment variable not set.")
141
+
142
+ # Check if the global credential setup was successful
143
+ if not CREDENTIALS_CONFIGURED:
144
+ raise ValueError("Google Cloud credentials could not be configured. Check startup logs and HF Secrets (ensure 'GOOGLE_APPLICATION_CREDENTIALS_JSON' is set correctly).")
145
+ self.agent = CodeAgent(
146
+ model=LiteLLMModel(model_id="gemini-2.0-flash"),
147
+ tools=[DuckDuckGoSearchTool(), WikipediaSearchTool(), ExcelToTextTool()],
148
+ add_base_tools=True,
149
+ additional_authorized_imports=['pandas','numpy','csv','subprocess']
150
+ )
151
+ print("BasicAgent initialized.")
152
+
153
+ def __call__(self, question: str) -> str:
154
+ print(f"Agent received question (first 50 chars): {question[:50]}...")
155
+ fixed_answer = self.agent.run(question)
156
+ print(f"Agent returning answer: {fixed_answer}")
157
+ return fixed_answer
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.")
 
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}")
 
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
 
210
  results_log = []
211
  answers_payload = []
212
  print(f"Running agent on {len(questions_data)} questions...")
 
 
 
213
  for item in questions_data:
214
  task_id = item.get("task_id")
215
  question_text = item.get("question")
 
216
  if not task_id or question_text is 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)
 
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:**
 
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(
 
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
  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")
327
  else:
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)