kamorou commited on
Commit
f568d06
·
verified ·
1 Parent(s): db2d14f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +445 -39
app.py CHANGED
@@ -1,23 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
- import gradio as gr
3
  import requests
4
  import inspect
5
  import pandas as pd
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  # (Keep Constants as is)
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- # --- Basic Agent Definition ---
12
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
13
- class BasicAgent:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  def __init__(self):
15
- print("BasicAgent initialized.")
 
 
 
16
  def __call__(self, question: str) -> str:
17
- print(f"Agent received question (first 50 chars): {question[:50]}...")
18
- fixed_answer = "This is a default answer."
19
- print(f"Agent returning fixed answer: {fixed_answer}")
20
- return fixed_answer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  def run_and_submit_all( profile: gr.OAuthProfile | None):
23
  """
@@ -40,7 +462,10 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
40
 
41
  # 1. Instantiate Agent ( modify this part to create your agent)
42
  try:
43
- agent = BasicAgent()
 
 
 
44
  except Exception as e:
45
  print(f"Error instantiating agent: {e}")
46
  return f"Error initializing agent: {e}", None
@@ -91,7 +516,7 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
91
  print("Agent did not produce any answers to submit.")
92
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
93
 
94
- # 4. Prepare Submission
95
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
96
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
97
  print(status_update)
@@ -142,19 +567,13 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
142
 
143
  # --- Build Gradio Interface using Blocks ---
144
  with gr.Blocks() as demo:
145
- gr.Markdown("# Basic Agent Evaluation Runner")
146
  gr.Markdown(
147
  """
148
- **Instructions:**
149
-
150
- 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
151
- 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
152
- 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
153
-
154
- ---
155
- **Disclaimers:**
156
- 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).
157
- 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.
158
  """
159
  )
160
 
@@ -163,7 +582,6 @@ with gr.Blocks() as demo:
163
  run_button = gr.Button("Run Evaluation & Submit All Answers")
164
 
165
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
166
- # Removed max_rows=10 from DataFrame constructor
167
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
168
 
169
  run_button.click(
@@ -173,24 +591,12 @@ with gr.Blocks() as demo:
173
 
174
  if __name__ == "__main__":
175
  print("\n" + "-"*30 + " App Starting " + "-"*30)
176
- # Check for SPACE_HOST and SPACE_ID at startup for information
177
- space_host_startup = os.getenv("SPACE_HOST")
178
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
179
-
180
- if space_host_startup:
181
- print(f"✅ SPACE_HOST found: {space_host_startup}")
182
- print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
183
- else:
184
- print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
185
-
186
- if space_id_startup: # Print repo URLs if SPACE_ID is found
187
  print(f"✅ SPACE_ID found: {space_id_startup}")
188
- print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
189
- print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
190
  else:
191
- print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
192
-
193
  print("-"*(60 + len(" App Starting ")) + "\n")
194
 
195
- print("Launching Gradio Interface for Basic Agent Evaluation...")
196
  demo.launch(debug=True, share=False)
 
1
+ # import os
2
+ # import gradio as gr
3
+ # import requests
4
+ # import inspect
5
+ # import pandas as pd
6
+
7
+ # # (Keep Constants as is)
8
+ # # --- Constants ---
9
+ # DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
+
11
+ # # --- Basic Agent Definition ---
12
+ # # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
13
+ # class BasicAgent:
14
+ # def __init__(self):
15
+ # print("BasicAgent initialized.")
16
+ # def __call__(self, question: str) -> str:
17
+ # print(f"Agent received question (first 50 chars): {question[:50]}...")
18
+ # fixed_answer = "This is a default answer."
19
+ # print(f"Agent returning fixed answer: {fixed_answer}")
20
+ # return fixed_answer
21
+
22
+ # def run_and_submit_all( profile: gr.OAuthProfile | None):
23
+ # """
24
+ # Fetches all questions, runs the BasicAgent on them, submits all answers,
25
+ # and displays the results.
26
+ # """
27
+ # # --- Determine HF Space Runtime URL and Repo URL ---
28
+ # space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
29
+
30
+ # if profile:
31
+ # username= f"{profile.username}"
32
+ # print(f"User logged in: {username}")
33
+ # else:
34
+ # print("User not logged in.")
35
+ # return "Please Login to Hugging Face with the button.", None
36
+
37
+ # api_url = DEFAULT_API_URL
38
+ # questions_url = f"{api_url}/questions"
39
+ # submit_url = f"{api_url}/submit"
40
+
41
+ # # 1. Instantiate Agent ( modify this part to create your agent)
42
+ # try:
43
+ # agent = BasicAgent()
44
+ # except Exception as e:
45
+ # print(f"Error instantiating agent: {e}")
46
+ # return f"Error initializing agent: {e}", None
47
+ # # 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)
48
+ # agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
49
+ # print(agent_code)
50
+
51
+ # # 2. Fetch Questions
52
+ # print(f"Fetching questions from: {questions_url}")
53
+ # try:
54
+ # response = requests.get(questions_url, timeout=15)
55
+ # response.raise_for_status()
56
+ # questions_data = response.json()
57
+ # if not questions_data:
58
+ # print("Fetched questions list is empty.")
59
+ # return "Fetched questions list is empty or invalid format.", None
60
+ # print(f"Fetched {len(questions_data)} questions.")
61
+ # except requests.exceptions.RequestException as e:
62
+ # print(f"Error fetching questions: {e}")
63
+ # return f"Error fetching questions: {e}", None
64
+ # except requests.exceptions.JSONDecodeError as e:
65
+ # print(f"Error decoding JSON response from questions endpoint: {e}")
66
+ # print(f"Response text: {response.text[:500]}")
67
+ # return f"Error decoding server response for questions: {e}", None
68
+ # except Exception as e:
69
+ # print(f"An unexpected error occurred fetching questions: {e}")
70
+ # return f"An unexpected error occurred fetching questions: {e}", None
71
+
72
+ # # 3. Run your Agent
73
+ # results_log = []
74
+ # answers_payload = []
75
+ # print(f"Running agent on {len(questions_data)} questions...")
76
+ # for item in questions_data:
77
+ # task_id = item.get("task_id")
78
+ # question_text = item.get("question")
79
+ # if not task_id or question_text is None:
80
+ # print(f"Skipping item with missing task_id or question: {item}")
81
+ # continue
82
+ # try:
83
+ # submitted_answer = agent(question_text)
84
+ # answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
85
+ # results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
86
+ # except Exception as e:
87
+ # print(f"Error running agent on task {task_id}: {e}")
88
+ # results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
89
+
90
+ # if not answers_payload:
91
+ # print("Agent did not produce any answers to submit.")
92
+ # return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
93
+
94
+ # # 4. Prepare Submission
95
+ # submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
96
+ # status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
97
+ # print(status_update)
98
+
99
+ # # 5. Submit
100
+ # print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
101
+ # try:
102
+ # response = requests.post(submit_url, json=submission_data, timeout=60)
103
+ # response.raise_for_status()
104
+ # result_data = response.json()
105
+ # final_status = (
106
+ # f"Submission Successful!\n"
107
+ # f"User: {result_data.get('username')}\n"
108
+ # f"Overall Score: {result_data.get('score', 'N/A')}% "
109
+ # f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
110
+ # f"Message: {result_data.get('message', 'No message received.')}"
111
+ # )
112
+ # print("Submission successful.")
113
+ # results_df = pd.DataFrame(results_log)
114
+ # return final_status, results_df
115
+ # except requests.exceptions.HTTPError as e:
116
+ # error_detail = f"Server responded with status {e.response.status_code}."
117
+ # try:
118
+ # error_json = e.response.json()
119
+ # error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
120
+ # except requests.exceptions.JSONDecodeError:
121
+ # error_detail += f" Response: {e.response.text[:500]}"
122
+ # status_message = f"Submission Failed: {error_detail}"
123
+ # print(status_message)
124
+ # results_df = pd.DataFrame(results_log)
125
+ # return status_message, results_df
126
+ # except requests.exceptions.Timeout:
127
+ # status_message = "Submission Failed: The request timed out."
128
+ # print(status_message)
129
+ # results_df = pd.DataFrame(results_log)
130
+ # return status_message, results_df
131
+ # except requests.exceptions.RequestException as e:
132
+ # status_message = f"Submission Failed: Network error - {e}"
133
+ # print(status_message)
134
+ # results_df = pd.DataFrame(results_log)
135
+ # return status_message, results_df
136
+ # except Exception as e:
137
+ # status_message = f"An unexpected error occurred during submission: {e}"
138
+ # print(status_message)
139
+ # results_df = pd.DataFrame(results_log)
140
+ # return status_message, results_df
141
+
142
+
143
+ # # --- Build Gradio Interface using Blocks ---
144
+ # with gr.Blocks() as demo:
145
+ # gr.Markdown("# Basic Agent Evaluation Runner")
146
+ # gr.Markdown(
147
+ # """
148
+ # **Instructions:**
149
+
150
+ # 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
151
+ # 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
152
+ # 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
153
+
154
+ # ---
155
+ # **Disclaimers:**
156
+ # 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).
157
+ # 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.
158
+ # """
159
+ # )
160
+
161
+ # gr.LoginButton()
162
+
163
+ # run_button = gr.Button("Run Evaluation & Submit All Answers")
164
+
165
+ # status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
166
+ # # Removed max_rows=10 from DataFrame constructor
167
+ # results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
168
+
169
+ # run_button.click(
170
+ # fn=run_and_submit_all,
171
+ # outputs=[status_output, results_table]
172
+ # )
173
+
174
+ # if __name__ == "__main__":
175
+ # print("\n" + "-"*30 + " App Starting " + "-"*30)
176
+ # # Check for SPACE_HOST and SPACE_ID at startup for information
177
+ # space_host_startup = os.getenv("SPACE_HOST")
178
+ # space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
179
+
180
+ # if space_host_startup:
181
+ # print(f"✅ SPACE_HOST found: {space_host_startup}")
182
+ # print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
183
+ # else:
184
+ # print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
185
+
186
+ # if space_id_startup: # Print repo URLs if SPACE_ID is found
187
+ # print(f"✅ SPACE_ID found: {space_id_startup}")
188
+ # print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
189
+ # print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
190
+ # else:
191
+ # print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
192
+
193
+ # print("-"*(60 + len(" App Starting ")) + "\n")
194
+
195
+ # print("Launching Gradio Interface for Basic Agent Evaluation...")
196
+ # demo.launch(debug=True, share=False)
197
+
198
+ ##################################
199
+ #
200
+ # =================================================================================================
201
+ # ✅ --- ✅ FINAL ASSESSMENT AGENT - INSTRUCTOR'S VERSION ✅ --- ✅
202
+ # =================================================================================================
203
+ #
204
+ # Instructions:
205
+ # 1. Make sure you have a requirements.txt file with all the necessary packages.
206
+ # 2. Set your GROQ_API_KEY in the Hugging Face Space secrets.
207
+ # 3. This code replaces the original template entirely.
208
+ #
209
+ # =================================================================================================
210
+
211
  import os
212
+ import io
213
  import requests
214
  import inspect
215
  import pandas as pd
216
+ import gradio as gr
217
+ from contextlib import redirect_stdout
218
+ from typing import TypedDict, Annotated, List, Union
219
+ import operator
220
+
221
+ # --- LangChain & LangGraph Imports ---
222
+ from langchain_core.messages import BaseMessage, HumanMessage, ToolMessage, AIMessage
223
+ from langchain_core.tools import tool
224
+ from langchain_groq import ChatGroq
225
+ # from langchain_openai import ChatOpenAI #<-- Alternative LLM
226
+ from langgraph.graph import StateGraph, END
227
+ from langgraph.prebuilt import ToolExecutor
228
+
229
 
230
  # (Keep Constants as is)
231
  # --- Constants ---
232
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
233
+ FILES_DIR = "./files"
234
+ os.makedirs(FILES_DIR, exist_ok=True)
235
+
236
+ #
237
+ # ================================================================================================
238
+ # ✅ 1. DEFINE THE AGENT'S TOOLS
239
+ # ================================================================================================
240
+ # Each tool is a simple Python function decorated with `@tool`.
241
+ # The docstring of the function is CRUCIAL. The LLM uses it to decide which tool to use.
242
+ #
243
+
244
+ @tool
245
+ def web_search(query: str) -> str:
246
+ """
247
+ Searches the web using DuckDuckGo to find up-to-date information, facts, or answers to general questions.
248
+ Use this for any questions that require current event knowledge or broad-spectrum information.
249
+ """
250
+ print(f"--- Calling Web Search Tool with query: {query} ---")
251
+ from duckduckgo_search import DDGS
252
+ try:
253
+ with DDGS() as ddgs:
254
+ results = [r for r in ddgs.text(query, max_results=5)]
255
+ return str(results) if results else "No results found."
256
+ except Exception as e:
257
+ return f"Error during web search: {e}"
258
+
259
+ @tool
260
+ def read_file(url: str) -> str:
261
+ """
262
+ Downloads a file from a given URL, saves it locally, and returns its content.
263
+ Use this tool when the user provides a URL to a file that needs to be inspected.
264
+ The file is saved in the './files/' directory. The function returns the full text content.
265
+ """
266
+ print(f"--- Calling Read File Tool with URL: {url} ---")
267
+ try:
268
+ filename = os.path.join(FILES_DIR, os.path.basename(url))
269
+ response = requests.get(url)
270
+ response.raise_for_status() # Raise an exception for bad status codes
271
+ with open(filename, 'wb') as f:
272
+ f.write(response.content)
273
+
274
+ # Try to read as text, if it fails, it might be a binary file.
275
+ try:
276
+ with open(filename, 'r', encoding='utf-8') as f:
277
+ content = f.read()
278
+ return f"Successfully read file '{filename}'. Content:\n\n{content}"
279
+ except UnicodeDecodeError:
280
+ return f"Successfully downloaded binary file '{filename}'. Cannot display content."
281
+
282
+ except requests.exceptions.RequestException as e:
283
+ return f"Error downloading or reading file: {e}"
284
+
285
+ @tool
286
+ def python_interpreter(code: str) -> str:
287
+ """
288
+ Executes a given string of Python code and returns the output from stdout.
289
+ Use this for complex calculations, data manipulation, or any task that can be solved with code.
290
+ The code runs in a restricted environment. You can use libraries like pandas, requests etc.
291
+ Make sure to use a print() statement to capture the output.
292
+ """
293
+ print(f"--- Calling Python Interpreter Tool with code:\n{code} ---")
294
+ output_buffer = io.StringIO()
295
+ try:
296
+ with redirect_stdout(output_buffer):
297
+ exec(code, globals())
298
+ return f"Code executed successfully. Output:\n{output_buffer.getvalue()}"
299
+ except Exception as e:
300
+ return f"Error executing Python code: {e}"
301
+
302
+ #
303
+ # ================================================================================================
304
+ # ✅ 2. CONFIGURE THE AGENT'S STATE, BRAIN (LLM), AND TOOL EXECUTOR
305
+ # ================================================================================================
306
+ #
307
+
308
+ # The AgentState is the "memory" of our agent. It keeps track of the conversation history.
309
+ class AgentState(TypedDict):
310
+ messages: Annotated[List[BaseMessage], operator.add]
311
+
312
+ # List of all the tools our agent can use
313
+ tools = [web_search, read_file, python_interpreter]
314
+
315
+ # The ToolExecutor is a helper class that runs the tools for us
316
+ tool_executor = ToolExecutor(tools)
317
+
318
+ # The "Brain" of our agent. We're using Groq for speed.
319
+ # Make sure to set GROQ_API_KEY in your HF Space secrets
320
+ llm = ChatGroq(model="llama3-70b-8192", temperature=0)
321
+
322
+ # If you want to use OpenAI instead, uncomment the line below and set OPENAI_API_KEY
323
+ # llm = ChatOpenAI(model="gpt-4-turbo", temperature=0)
324
+
325
+ # We now bind the tools to the LLM. This tells the LLM what functions it can call.
326
+ llm_with_tools = llm.bind_tools(tools)
327
+
328
+ #
329
+ # ================================================================================================
330
+ # ✅ 3. DEFINE THE LANGGRAPH NODES AND EDGES
331
+ # ================================================================================================
332
+ # This is the core logic of our agent, defined as a graph.
333
+ #
334
+
335
+ # NODE 1: The Agent Node (call_model)
336
+ # This node invokes the LLM to decide the next action or to give a final answer.
337
+ def call_model(state: AgentState) -> dict:
338
+ print("--- Calling LLM ---")
339
+ messages = state['messages']
340
+ response = llm_with_tools.invoke(messages)
341
+ # We return a dict, because this node will always be part of a graph
342
+ return {"messages": [response]}
343
+
344
+ # NODE 2: The Tool Node (call_tool)
345
+ # This node executes the tool chosen by the LLM.
346
+ def call_tool(state: AgentState) -> dict:
347
+ last_message = state['messages'][-1] # Get the last message, which should be an AIMessage with tool calls
348
+
349
+ # We construct an ToolMessage with the output of the tool call
350
+ action = last_message.tool_calls[0]
351
+ print(f"--- Preparing to call tool: {action['name']} with args {action['args']} ---")
352
+ tool_output = tool_executor.invoke(action)
353
+ return {"messages": [ToolMessage(content=str(tool_output), tool_call_id=action['id'])]}
354
 
355
+ # EDGE: The Conditional Router (should_continue)
356
+ # This function decides which node to go to next.
357
+ def should_continue(state: AgentState) -> str:
358
+ last_message = state['messages'][-1]
359
+ # If the LLM made a tool call, we route to the 'action' node (call_tool)
360
+ if last_message.tool_calls:
361
+ print("--- Decision: Call a tool ---")
362
+ return "action"
363
+ # Otherwise, we are done, and we route to the 'end' state
364
+ else:
365
+ print("--- Decision: End of process ---")
366
+ return "end"
367
+
368
+ #
369
+ # ================================================================================================
370
+ # ✅ 4. BUILD AND COMPILE THE GRAPH
371
+ # ================================================================================================
372
+ #
373
+
374
+ # 1. Initialize the graph and add our state object
375
+ workflow = StateGraph(AgentState)
376
+
377
+ # 2. Add the two nodes we defined: 'agent' and 'action'
378
+ workflow.add_node("agent", call_model)
379
+ workflow.add_node("action", call_tool)
380
+
381
+ # 3. Set the entry point of the graph. The first thing to run is the 'agent' node.
382
+ workflow.set_entry_point("agent")
383
+
384
+ # 4. Add the conditional edge. This controls the flow of the graph.
385
+ workflow.add_conditional_edges(
386
+ "agent", # Start from the 'agent' node
387
+ should_continue, # Use our function to decide the path
388
+ {
389
+ "action": "action", # If it returns "action", go to the 'action' node
390
+ "end": END # If it returns "end", finish the graph
391
+ }
392
+ )
393
+
394
+ # 5. Add a normal edge. After 'action' runs, it should always go back to 'agent'.
395
+ workflow.add_edge('action', 'agent')
396
+
397
+ # 6. Compile the graph into a runnable app.
398
+ app = workflow.compile()
399
+
400
+
401
+ #
402
+ # ================================================================================================
403
+ # ✅ 5. CREATE THE AGENT CLASS THAT THE TEMPLATE USES
404
+ # ================================================================================================
405
+ # This class wraps our LangGraph agent in the format expected by the evaluation script.
406
+ #
407
+ class GaiaAgent:
408
  def __init__(self):
409
+ print("GaiaAgent initialized.")
410
+ # Any one-time setup can go here
411
+ self.agent_app = app
412
+
413
  def __call__(self, question: str) -> str:
414
+ print(f"Agent received question (first 100 chars): {question[:100]}...")
415
+
416
+ # The initial input for our graph is a list of messages.
417
+ initial_input = {"messages": [HumanMessage(content=question)]}
418
+
419
+ final_state = None
420
+ # Let's add a loop limit to prevent infinite cycles
421
+ for i, step in enumerate(self.agent_app.stream(initial_input, {"recursion_limit": 25})):
422
+ # We'll just take the final state. The stream is useful for seeing intermediate steps.
423
+ if i == 0:
424
+ print("--- Starting Agentic Loop ---")
425
+
426
+ # You can print keys to see what's happening at each step:
427
+ # print(f"Step {i}: {list(step.keys())}")
428
+
429
+ final_state = step
430
+
431
+ # The final answer is in the last AIMessage of the 'messages' list
432
+ final_answer_message = final_state['agent']['messages'][-1]
433
+ final_answer = final_answer_message.content
434
+
435
+ print(f"--- Agent finished. Final Answer: {final_answer} ---")
436
+ return final_answer
437
+
438
+ #
439
+ # ================================================================================================
440
+ # -- DO NOT MODIFY THE CODE BELOW THIS LINE --
441
+ # -- This is the Gradio App and Submission Logic from the course --
442
+ # ================================================================================================
443
 
444
  def run_and_submit_all( profile: gr.OAuthProfile | None):
445
  """
 
462
 
463
  # 1. Instantiate Agent ( modify this part to create your agent)
464
  try:
465
+ # -------------------------------------------------------------------
466
+ # THIS IS THE ONLY CHANGE IN THIS FUNCTION: We now use our GaiaAgent
467
+ agent = GaiaAgent()
468
+ # -------------------------------------------------------------------
469
  except Exception as e:
470
  print(f"Error instantiating agent: {e}")
471
  return f"Error initializing agent: {e}", None
 
516
  print("Agent did not produce any answers to submit.")
517
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
518
 
519
+ # 4. Prepare Submission
520
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
521
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
522
  print(status_update)
 
567
 
568
  # --- Build Gradio Interface using Blocks ---
569
  with gr.Blocks() as demo:
570
+ gr.Markdown("# GAIA Agent Final Assessment")
571
  gr.Markdown(
572
  """
573
+ **Instructor's Note:** This space is now powered by a LangGraph agent.
574
+ 1. Ensure your `GROQ_API_KEY` is set in the Space secrets.
575
+ 2. Make sure you have a `requirements.txt` file.
576
+ 3. Log in below and click 'Run Evaluation'. Good luck!
 
 
 
 
 
 
577
  """
578
  )
579
 
 
582
  run_button = gr.Button("Run Evaluation & Submit All Answers")
583
 
584
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
 
585
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
586
 
587
  run_button.click(
 
591
 
592
  if __name__ == "__main__":
593
  print("\n" + "-"*30 + " App Starting " + "-"*30)
594
+ space_id_startup = os.getenv("SPACE_ID")
595
+ if space_id_startup:
 
 
 
 
 
 
 
 
 
596
  print(f"✅ SPACE_ID found: {space_id_startup}")
 
 
597
  else:
598
+ print("ℹ️ SPACE_ID environment variable not found (running locally?).")
 
599
  print("-"*(60 + len(" App Starting ")) + "\n")
600
 
601
+ print("Launching Gradio Interface for GAIA Agent Evaluation...")
602
  demo.launch(debug=True, share=False)