kamorou commited on
Commit
3175eb4
·
verified ·
1 Parent(s): 821a1ee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -117
app.py CHANGED
@@ -199,7 +199,6 @@
199
  # demo.launch(debug=True, share=False)
200
 
201
 
202
-
203
  import os
204
  import gradio as gr
205
  import requests
@@ -210,44 +209,41 @@ from typing import TypedDict, Annotated, List
210
 
211
  # ==============================================================================
212
  # PART 1: YOUR AGENT'S LOGIC AND DEFINITION
213
- # All of this is new. It replaces the old placeholder.
214
  # ==============================================================================
215
 
216
  # LangChain and LangGraph imports
217
  from langchain_huggingface import HuggingFaceEndpoint
218
- from langchain_community.tools.tavily_search import TavilySearchResults
 
219
  from langchain_experimental.tools import PythonREPLTool
220
  from langchain_core.messages import BaseMessage, HumanMessage
221
  from langgraph.graph import StateGraph, END
222
  from langgraph.prebuilt import ToolNode
 
 
 
 
223
 
224
  # Load API keys from .env file or Space secrets
225
  load_dotenv()
226
  hf_token = os.getenv("HF_TOKEN")
227
  tavily_api_key = os.getenv("TAVILY_API_KEY")
228
 
229
- # Set the Tavily API key for the tool to use
230
  if tavily_api_key:
231
  os.environ["TAVILY_API_KEY"] = tavily_api_key
232
  else:
233
  print("Warning: TAVILY_API_KEY not found. Web search tool will not work.")
234
 
235
  # --- Define Agent Tools ---
 
236
  tools = [
237
- TavilySearchResults(max_results=3, description="A search engine for finding up-to-date information on the web."),
238
  PythonREPLTool()
239
  ]
240
  tool_node = ToolNode(tools)
241
 
242
  # --- Configure the LLM "Brain" ---
243
  repo_id = "meta-llama/Meta-Llama-3-8B-Instruct"
244
- SYSTEM_PROMPT = """You are a highly capable AI agent. Your mission is to accurately answer complex questions.
245
- **Instructions:**
246
- 1. **Analyze:** Read the question to understand what is being asked.
247
- 2. **Plan:** Think step-by-step. Break the problem into smaller tasks. Decide which tool is best for each task.
248
- 3. **Execute:** Call ONE tool at a time.
249
- 4. **Observe & Reason:** After getting a tool's result, observe it. Decide if you have the final answer or if you need to use another tool.
250
- 5. **Final Answer:** Once confident, provide a clear, direct, and concise final answer."""
251
 
252
  llm = HuggingFaceEndpoint(
253
  repo_id=repo_id,
@@ -255,179 +251,130 @@ llm = HuggingFaceEndpoint(
255
  temperature=0,
256
  max_new_tokens=2048,
257
  )
258
- llm_with_tools = llm.bind_tools(tools)
 
 
 
 
 
 
 
 
 
 
 
259
 
260
  # --- Build the LangGraph Agent ---
261
  class AgentState(TypedDict):
262
- messages: Annotated[List[BaseMessage], lambda x, y: x + y]
 
 
 
263
 
 
264
  def agent_node(state):
265
- last_message = state['messages'][-1]
266
- prompt_with_system = [HumanMessage(content=SYSTEM_PROMPT, name="system_prompt"), last_message]
267
- response = llm_with_tools.invoke(prompt_with_system)
268
- return {"messages": [response]}
269
-
270
- def should_continue(state):
271
- if state["messages"][-1].tool_calls:
272
- return "tools"
273
- return END
274
-
275
- workflow = StateGraph(AgentState)
276
- workflow.add_node("agent", agent_node)
277
- workflow.add_node("tools", tool_node)
278
- workflow.set_entry_point("agent")
279
- workflow.add_conditional_edges("agent", should_continue, {"tools": "tools", "end": END})
280
- workflow.add_edge("tools", "agent")
281
-
282
- # Compile the graph into a runnable app
283
- compiled_agent_app = workflow.compile()
284
-
285
- # --- THIS IS THE NEW BasicAgent CLASS THAT REPLACES THE PLACEHOLDER ---
286
  class BasicAgent:
287
  def __init__(self):
288
- # Check for API keys during initialization
289
  if not hf_token or not tavily_api_key:
290
  raise ValueError("HF_TOKEN or TAVILY_API_KEY not set. Please add them to your Space secrets.")
291
  print("LangGraph Agent initialized successfully.")
 
 
 
292
 
293
  def __call__(self, question: str) -> str:
294
  print(f"Agent received question (first 80 chars): {question[:80]}...")
295
  try:
296
- inputs = {"messages": [HumanMessage(content=question)]}
297
- final_response = ""
298
- for s in compiled_agent_app.stream(inputs, {"recursion_limit": 15}):
299
- if "agent" in s and s["agent"]["messages"][-1].content:
300
- final_response = s["agent"]["messages"][-1].content
301
-
302
- if not final_response:
303
- final_response = "Agent finished but did not produce a clear final answer."
304
-
305
- print(f"Agent returning final answer (first 80 chars): {final_response[:80]}...")
306
- return final_response
307
  except Exception as e:
308
  print(f"An error occurred in agent execution: {e}")
309
  return f"Error: {e}"
310
 
 
311
  # ==============================================================================
312
- # PART 2: THE GRADIO TEST HARNESS UI
313
- # The rest of this file remains exactly as it was provided in the template.
314
  # ==============================================================================
315
-
316
  # --- Constants ---
317
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
318
 
319
  def run_and_submit_all(profile: gr.OAuthProfile | None):
320
- """
321
- Fetches all questions, runs the BasicAgent on them, submits all answers,
322
- and displays the results.
323
- """
324
- # (The rest of this function remains unchanged)
325
- # --- Determine HF Space Runtime URL and Repo URL ---
326
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
327
-
328
  if profile:
329
  username= f"{profile.username}"
330
  print(f"User logged in: {username}")
331
  else:
332
  print("User not logged in.")
333
  return "Please Login to Hugging Face with the button.", None
334
-
335
  api_url = DEFAULT_API_URL
336
  questions_url = f"{api_url}/questions"
337
  submit_url = f"{api_url}/submit"
338
-
339
- # 1. Instantiate Agent (this now calls YOUR agent class from above)
340
  try:
341
  agent = BasicAgent()
342
  except Exception as e:
343
  print(f"Error instantiating agent: {e}")
344
  return f"Error initializing agent: {e}", None
345
-
346
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
347
- print(agent_code)
348
-
349
- # 2. Fetch Questions
350
  print(f"Fetching questions from: {questions_url}")
351
  try:
352
  response = requests.get(questions_url, timeout=15)
353
  response.raise_for_status()
354
  questions_data = response.json()
355
- if not questions_data:
356
- print("Fetched questions list is empty.")
357
- return "Fetched questions list is empty or invalid format.", None
358
  print(f"Fetched {len(questions_data)} questions.")
359
  except Exception as e:
360
- print(f"An unexpected error occurred fetching questions: {e}")
361
  return f"An unexpected error occurred fetching questions: {e}", None
362
-
363
- # 3. Run your Agent
364
- results_log = []
365
- answers_payload = []
366
  print(f"Running agent on {len(questions_data)} questions...")
367
  for item in questions_data:
368
- task_id = item.get("task_id")
369
- question_text = item.get("question")
370
- if not task_id or question_text is None:
371
- print(f"Skipping item with missing task_id or question: {item}")
372
- continue
373
  try:
374
- # This line now calls your __call__ method in your new BasicAgent
375
  submitted_answer = agent(question_text)
376
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
377
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
378
  except Exception as e:
379
- print(f"Error running agent on task {task_id}: {e}")
380
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
381
-
382
- if not answers_payload:
383
- print("Agent did not produce any answers to submit.")
384
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
385
-
386
- # 4. Prepare Submission
387
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
388
- status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
389
- print(status_update)
390
-
391
- # 5. Submit
392
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
393
  try:
394
  response = requests.post(submit_url, json=submission_data, timeout=60)
395
  response.raise_for_status()
396
  result_data = response.json()
397
- final_status = (
398
- f"Submission Successful!\n"
399
- f"User: {result_data.get('username')}\n"
400
- f"Overall Score: {result_data.get('score', 'N/A')}% "
401
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
402
- f"Message: {result_data.get('message', 'No message received.')}"
403
- )
404
- print("Submission successful.")
405
- results_df = pd.DataFrame(results_log)
406
- return final_status, results_df
407
  except Exception as e:
408
- status_message = f"An unexpected error occurred during submission: {e}"
409
- print(status_message)
410
- results_df = pd.DataFrame(results_log)
411
- return status_message, results_df
412
-
413
 
414
- # --- Build Gradio Interface using Blocks ---
415
  with gr.Blocks() as demo:
416
  gr.Markdown("# GAIA Agent Evaluation Runner")
417
- gr.Markdown(
418
- """
419
- 1. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
420
- 2. Click 'Run Evaluation & Submit All Answers' to run your custom agent and see the score.
421
- """
422
- )
423
  gr.LoginButton()
424
  run_button = gr.Button("Run Evaluation & Submit All Answers")
425
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
426
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
427
- run_button.click(
428
- fn=run_and_submit_all,
429
- outputs=[status_output, results_table]
430
- )
431
 
432
  if __name__ == "__main__":
433
  demo.launch(debug=True, share=False)
 
199
  # demo.launch(debug=True, share=False)
200
 
201
 
 
202
  import os
203
  import gradio as gr
204
  import requests
 
209
 
210
  # ==============================================================================
211
  # PART 1: YOUR AGENT'S LOGIC AND DEFINITION
 
212
  # ==============================================================================
213
 
214
  # LangChain and LangGraph imports
215
  from langchain_huggingface import HuggingFaceEndpoint
216
+ # NEW: Import TavilySearch from the new package
217
+ from langchain_tavily import TavilySearch
218
  from langchain_experimental.tools import PythonREPLTool
219
  from langchain_core.messages import BaseMessage, HumanMessage
220
  from langgraph.graph import StateGraph, END
221
  from langgraph.prebuilt import ToolNode
222
+ # NEW: Import the compatible agent constructor and prompt hub
223
+ from langchain.agents import create_tool_calling_agent
224
+ from langchain import hub
225
+
226
 
227
  # Load API keys from .env file or Space secrets
228
  load_dotenv()
229
  hf_token = os.getenv("HF_TOKEN")
230
  tavily_api_key = os.getenv("TAVILY_API_KEY")
231
 
 
232
  if tavily_api_key:
233
  os.environ["TAVILY_API_KEY"] = tavily_api_key
234
  else:
235
  print("Warning: TAVILY_API_KEY not found. Web search tool will not work.")
236
 
237
  # --- Define Agent Tools ---
238
+ # NEW: Using TavilySearch from the correct package
239
  tools = [
240
+ TavilySearch(max_results=3, description="A search engine for finding up-to-date information on the web."),
241
  PythonREPLTool()
242
  ]
243
  tool_node = ToolNode(tools)
244
 
245
  # --- Configure the LLM "Brain" ---
246
  repo_id = "meta-llama/Meta-Llama-3-8B-Instruct"
 
 
 
 
 
 
 
247
 
248
  llm = HuggingFaceEndpoint(
249
  repo_id=repo_id,
 
251
  temperature=0,
252
  max_new_tokens=2048,
253
  )
254
+
255
+ # --- THE FIX: Create Agent with a Compatible Method ---
256
+ # REMOVED: llm_with_tools = llm.bind_tools(tools)
257
+ # This was causing the error.
258
+
259
+ # NEW: We pull a pre-made prompt that knows how to handle tool calls.
260
+ prompt = hub.pull("hwchase17/react-json")
261
+
262
+ # NEW: We use `create_tool_calling_agent`. This function correctly combines the LLM,
263
+ # the tools, and the prompt, without needing the .bind_tools() method.
264
+ agent_runnable = create_tool_calling_agent(llm, tools, prompt)
265
+
266
 
267
  # --- Build the LangGraph Agent ---
268
  class AgentState(TypedDict):
269
+ # The 'messages' key is no longer used, 'input' and 'agent_outcome' are standard for this agent type
270
+ input: str
271
+ chat_history: list[BaseMessage]
272
+ agent_outcome: dict
273
 
274
+ # NEW: The agent_node is much simpler now. It just calls the runnable we created.
275
  def agent_node(state):
276
+ outcome = agent_runnable.invoke(state)
277
+ return {"agent_outcome": outcome}
278
+
279
+ def tool_node_executor(state):
280
+ # The agent_runnable provides tool calls in a specific format. We execute them.
281
+ tool_calls = state["agent_outcome"].tool_calls
282
+ tool_outputs = []
283
+ for tool_call in tool_calls:
284
+ tool_name = tool_call["name"]
285
+ tool_to_call = {tool.name: tool for tool in tools}[tool_name]
286
+ tool_output = tool_to_call.invoke(tool_call["args"])
287
+ tool_outputs.append({"output": tool_output, "tool_call_id": tool_call["id"]})
288
+ return {"intermediate_steps": tool_outputs}
289
+
290
+
291
+ # This setup is more complex but correctly models the ReAct loop in LangGraph
 
 
 
 
 
292
  class BasicAgent:
293
  def __init__(self):
 
294
  if not hf_token or not tavily_api_key:
295
  raise ValueError("HF_TOKEN or TAVILY_API_KEY not set. Please add them to your Space secrets.")
296
  print("LangGraph Agent initialized successfully.")
297
+ # We need an agent executor to run the loop
298
+ from langchain.agents import AgentExecutor
299
+ self.agent_executor = AgentExecutor(agent=agent_runnable, tools=tools, verbose=True)
300
 
301
  def __call__(self, question: str) -> str:
302
  print(f"Agent received question (first 80 chars): {question[:80]}...")
303
  try:
304
+ # The AgentExecutor expects a dictionary with an "input" key.
305
+ response = self.agent_executor.invoke({"input": question})
306
+ final_answer = response.get("output", "Agent did not produce an output.")
307
+ print(f"Agent returning final answer (first 80 chars): {final_answer[:80]}...")
308
+ return final_answer
 
 
 
 
 
 
309
  except Exception as e:
310
  print(f"An error occurred in agent execution: {e}")
311
  return f"Error: {e}"
312
 
313
+
314
  # ==============================================================================
315
+ # PART 2: THE GRADIO TEST HARNESS UI (UNCHANGED)
 
316
  # ==============================================================================
 
317
  # --- Constants ---
318
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
319
 
320
  def run_and_submit_all(profile: gr.OAuthProfile | None):
321
+ # This entire function remains the same as the template
322
+ space_id = os.getenv("SPACE_ID")
 
 
 
 
 
 
323
  if profile:
324
  username= f"{profile.username}"
325
  print(f"User logged in: {username}")
326
  else:
327
  print("User not logged in.")
328
  return "Please Login to Hugging Face with the button.", None
 
329
  api_url = DEFAULT_API_URL
330
  questions_url = f"{api_url}/questions"
331
  submit_url = f"{api_url}/submit"
 
 
332
  try:
333
  agent = BasicAgent()
334
  except Exception as e:
335
  print(f"Error instantiating agent: {e}")
336
  return f"Error initializing agent: {e}", None
 
337
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
 
 
 
338
  print(f"Fetching questions from: {questions_url}")
339
  try:
340
  response = requests.get(questions_url, timeout=15)
341
  response.raise_for_status()
342
  questions_data = response.json()
 
 
 
343
  print(f"Fetched {len(questions_data)} questions.")
344
  except Exception as e:
 
345
  return f"An unexpected error occurred fetching questions: {e}", None
346
+ results_log, answers_payload = [], []
 
 
 
347
  print(f"Running agent on {len(questions_data)} questions...")
348
  for item in questions_data:
349
+ task_id, question_text = item.get("task_id"), item.get("question")
350
+ if not task_id or question_text is None: continue
 
 
 
351
  try:
 
352
  submitted_answer = agent(question_text)
353
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
354
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
355
  except Exception as e:
356
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
357
+ if not answers_payload: return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
 
 
 
 
 
 
358
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
 
 
 
 
359
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
360
  try:
361
  response = requests.post(submit_url, json=submission_data, timeout=60)
362
  response.raise_for_status()
363
  result_data = response.json()
364
+ final_status = (f"Submission Successful!\nUser: {result_data.get('username')}\nOverall Score: {result_data.get('score', 'N/A')}% ({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\nMessage: {result_data.get('message', '')}")
365
+ return final_status, pd.DataFrame(results_log)
 
 
 
 
 
 
 
 
366
  except Exception as e:
367
+ return f"An unexpected error occurred during submission: {e}", pd.DataFrame(results_log)
 
 
 
 
368
 
369
+ # --- Gradio Interface (Unchanged) ---
370
  with gr.Blocks() as demo:
371
  gr.Markdown("# GAIA Agent Evaluation Runner")
372
+ gr.Markdown("1. Log in. 2. Click 'Run Evaluation'.")
 
 
 
 
 
373
  gr.LoginButton()
374
  run_button = gr.Button("Run Evaluation & Submit All Answers")
375
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
376
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
377
+ run_button.click(fn=run_and_submit_all, outputs=[status_output, results_table])
 
 
 
378
 
379
  if __name__ == "__main__":
380
  demo.launch(debug=True, share=False)