kamorou commited on
Commit
f1ecd68
·
verified ·
1 Parent(s): 7985c25

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +59 -112
app.py CHANGED
@@ -259,10 +259,14 @@ from typing import TypedDict, Annotated, List
259
  import operator
260
 
261
  # --- LangChain & LangGraph Imports ---
262
- from langchain_core.messages import BaseMessage, HumanMessage, ToolMessage, AIMessage, SystemMessage
263
  from langchain_core.tools import tool
264
- from langchain_huggingface import HuggingFaceEndpoint
 
 
 
265
  from langgraph.graph import StateGraph, END
 
266
  from tavily import TavilyClient
267
  import pypdf
268
 
@@ -271,32 +275,22 @@ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
271
  FILES_DIR = "./files"
272
  os.makedirs(FILES_DIR, exist_ok=True)
273
 
274
- # --- System Prompt (Updated for Manual Tool Calling) ---
 
275
  AGENT_SYSTEM_PROMPT = """You are a world-class AI agent, specialized in solving complex problems from the GAIA benchmark.
276
  Your task is to analyze the user's question, think step-by-step, and use the provided tools to find the correct answer.
277
-
278
- **TOOL USAGE INSTRUCTIONS:**
279
- When you need to use a tool, you MUST respond with a JSON object containing the tool name and its arguments. The JSON object should have two keys: "tool_name" and "parameters".
280
-
281
- Here is an example of how to call the `tavily_search` tool:
282
- ```json
283
- {
284
- "tool_name": "tavily_search",
285
- "parameters": {
286
- "query": "What was the score of the 2023 FIFA Women's World Cup final?"
287
- }
288
- }```
289
-
290
- **CRITICAL FINAL ANSWER INSTRUCTIONS:**
291
- Once you have gathered all the necessary information and are absolutely certain of the answer, you MUST provide it directly and concisely.
292
- - Your final response must ONLY be the answer itself.
293
- - DO NOT wrap the final answer in a JSON object or include any conversational text like 'The answer is...'.
294
-
295
  EXAMPLES OF CORRECT FINAL ANSWERS:
296
- - `2023`
297
- - `John Doe`
298
- - `42`
299
- - `broccoli, celery, lettuce, sweet potatoes`
 
300
  """
301
 
302
  #
@@ -347,73 +341,28 @@ def python_interpreter(code: str) -> str:
347
 
348
  #
349
  # ================================================================================================
350
- # ✅ 2. CONFIGURE AND BUILD THE AGENT GRAPH
351
  # ================================================================================================
352
  #
353
  class AgentState(TypedDict):
354
- messages: Annotated[List[BaseMessage], operator.add]
 
 
355
 
356
  def build_agent_graph():
357
- """Builds the LangGraph agent."""
358
  tools = [tavily_search, read_file, python_interpreter]
359
- tool_map = {tool.name: tool for tool in tools}
360
-
361
- repo_id = "CohereForAI/c4ai-command-r-plus"
362
-
363
- # <<<--- CHANGE 1: Explicitly set `task="conversational"` --->>>
364
- # This is the crucial fix. We are telling the endpoint to use the correct API pipeline.
365
- llm = HuggingFaceEndpoint(
366
- repo_id=repo_id,
367
- task="conversational", # This is the key fix!
368
- max_new_tokens=1024,
369
- temperature=0.1,
370
- huggingfacehub_api_token=os.getenv("HUGGINGFACEHUB_API_TOKEN")
371
- )
372
 
373
- def call_model(state: AgentState):
374
- """Invokes the LLM using the conversational task."""
375
- # <<<--- CHANGE 2: The conversational task takes a list of messages directly --->>>
376
- # This is cleaner and the correct way to use this pipeline.
377
- response = llm.invoke(state['messages'])
378
- return {"messages": [response]}
379
-
380
- def should_continue(state: AgentState) -> str:
381
- """Determines whether to call a tool or end the loop."""
382
- last_message_content = state['messages'][-1].content.strip()
383
- if last_message_content.startswith('{') and last_message_content.endswith('}'):
384
- try:
385
- json.loads(last_message_content)
386
- return "action"
387
- except json.JSONDecodeError:
388
- return "end"
389
- else:
390
- return "end"
391
-
392
- def call_tool_node(state: AgentState):
393
- """Parses the tool call from the LLM output and executes it."""
394
- last_message_content = state['messages'][-1].content.strip()
395
- try:
396
- tool_call_data = json.loads(last_message_content)
397
- tool_name = tool_call_data.get("tool_name")
398
- parameters = tool_call_data.get("parameters", {})
399
 
400
- if tool_name not in tool_map:
401
- return {"messages": [ToolMessage(content=f"Error: Tool '{tool_name}' not found.", tool_call_id="error")]}
 
402
 
403
- selected_tool = tool_map[tool_name]
404
- tool_output = selected_tool.invoke(parameters)
405
- return {"messages": [ToolMessage(content=str(tool_output), tool_call_id=tool_name)]}
406
-
407
- except Exception as e:
408
- return {"messages": [ToolMessage(content=f"Error processing tool call: {e}. Content: '{last_message_content}'", tool_call_id="error")]}
409
-
410
- workflow = StateGraph(AgentState)
411
- workflow.add_node("agent", call_model)
412
- workflow.add_node("action", call_tool_node)
413
- workflow.set_entry_point("agent")
414
- workflow.add_conditional_edges("agent", should_continue, {"action": "action", "end": END})
415
- workflow.add_edge('action', 'agent')
416
- return workflow.compile()
417
 
418
  #
419
  # ================================================================================================
@@ -422,23 +371,25 @@ def build_agent_graph():
422
  #
423
  class GaiaAgent:
424
  def __init__(self):
425
- print("GaiaAgent initialized. Building Command R+ agent with 'conversational' task...")
426
  self.agent_app = build_agent_graph()
427
 
428
  def __call__(self, question: str) -> str:
429
  print(f"\n{'='*60}\nAgent received question: {question[:100]}...\n{'='*60}")
430
- initial_input = {"messages": [SystemMessage(content=AGENT_SYSTEM_PROMPT), HumanMessage(content=question)]}
431
- final_state = None
432
- for i, step in enumerate(self.agent_app.stream(initial_input, {"recursion_limit": 15})):
433
- if i == 0: print("--- Starting Agentic Loop ---")
434
- final_state = list(step.values())[0]
435
-
436
- final_answer_message = final_state['messages'][-1]
437
- final_answer = str(final_answer_message.content).strip()
438
- print(f"\n--- Agent finished. Final Answer: {final_answer} ---\n")
439
- return final_answer
440
-
441
- # --- The rest of the file (run_and_submit_all, Gradio UI) remains the same ---
 
 
442
  def run_and_submit_all( profile: gr.OAuthProfile | None):
443
  space_id = os.getenv("SPACE_ID")
444
  if not profile: return "Please Login to Hugging Face with the button.", None
@@ -453,16 +404,13 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
453
  response = requests.get(questions_url, timeout=15)
454
  response.raise_for_status()
455
  questions_data = response.json()
456
- except Exception as e:
457
- return f"An unexpected error occurred fetching questions: {e}", None
458
 
459
- results_log = []
460
- answers_payload = []
461
  agent_instance = GaiaAgent()
462
 
463
  for item in questions_data:
464
- task_id = item.get("task_id")
465
- question_text = item.get("question")
466
  if not task_id or question_text is None: continue
467
  try:
468
  submitted_answer = agent_instance(question_text)
@@ -472,10 +420,9 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
472
  print(f"Error running agent on task {task_id}: {e}")
473
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
474
 
475
- if not answers_payload:
476
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
477
-
478
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
 
479
  try:
480
  response = requests.post(submit_url, json=submission_data, timeout=90)
481
  response.raise_for_status()
@@ -488,15 +435,15 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
488
  f"Message: {result_data.get('message', 'No message received.')}"
489
  )
490
  return final_status, pd.DataFrame(results_log)
491
- except Exception as e:
492
- return f"An unexpected error occurred during submission: {e}", pd.DataFrame(results_log)
493
 
494
  with gr.Blocks() as demo:
495
- gr.Markdown("# GAIA Agent Final Assessment (Open Source: Command R+ - Corrected Task)")
496
  gr.Markdown(
497
  """
498
- **Instructor's Note:** This version corrects the `HuggingFaceEndpoint` invocation by specifying `task="conversational"`.
499
- This is the final key required to make the Command R+ model work correctly with the Hugging Face Inference API for our agent.
 
500
  """
501
  )
502
  gr.LoginButton()
@@ -507,5 +454,5 @@ with gr.Blocks() as demo:
507
 
508
  if __name__ == "__main__":
509
  print("\n" + "-"*30 + " App Starting " + "-"*30)
510
- demo.launch(debug=True, share=False, ssr_mode=False)
511
- #demo.launch(debug=True, share=False)
 
259
  import operator
260
 
261
  # --- LangChain & LangGraph Imports ---
262
+ from langchain_core.messages import BaseMessage, HumanMessage, ToolMessage
263
  from langchain_core.tools import tool
264
+ # <<<--- CHANGE: Import ChatCohere and Cohere's Tool annd ToolCall classes--->>>
265
+ from langchain_cohere import ChatCohere
266
+ from langchain_cohere.cohere_agent import CohereAgent
267
+ from langchain.agents import AgentExecutor
268
  from langgraph.graph import StateGraph, END
269
+ from langgraph.prebuilt import ToolNode
270
  from tavily import TavilyClient
271
  import pypdf
272
 
 
275
  FILES_DIR = "./files"
276
  os.makedirs(FILES_DIR, exist_ok=True)
277
 
278
+ # --- System Prompt (Unchanged) ---
279
+ # This prompt is excellent and requires no changes.
280
  AGENT_SYSTEM_PROMPT = """You are a world-class AI agent, specialized in solving complex problems from the GAIA benchmark.
281
  Your task is to analyze the user's question, think step-by-step, and use the provided tools to find the correct answer.
282
+ CRITICAL INSTRUCTIONS:
283
+ 1. **Analyze the Goal:** First, understand what the user is asking for.
284
+ 2. **Plan & Execute:** Formulate a plan and use the available tools (`tavily_search`, `read_file`, `python_interpreter`) to gather information.
285
+ 3. **Final Answer Format:** Once you are absolutely certain of the answer, you MUST provide it directly and concisely.
286
+ - DO NOT include your reasoning, thoughts, or any conversational text like 'The answer is...', 'Here is the result:', or 'Based on my search...'.
287
+ - Your final response must ONLY be the answer itself.
 
 
 
 
 
 
 
 
 
 
 
 
288
  EXAMPLES OF CORRECT FINAL ANSWERS:
289
+ - If the question asks for a year: `2023`
290
+ - If it asks for a name: `John Doe`
291
+ - If it asks for a number: `42`
292
+ - If it asks for a comma-separated list: `item1, item2, item3`
293
+ Think, use your tools, and then provide ONLY the final, precise answer.
294
  """
295
 
296
  #
 
341
 
342
  #
343
  # ================================================================================================
344
+ # ✅ 2. CONFIGURE AND BUILD THE AGENT (Using ChatCohere)
345
  # ================================================================================================
346
  #
347
  class AgentState(TypedDict):
348
+ input: str
349
+ chat_history: List[BaseMessage]
350
+ agent_outcome: dict | None
351
 
352
  def build_agent_graph():
353
+ """Builds the agent using the direct ChatCohere integration."""
354
  tools = [tavily_search, read_file, python_interpreter]
 
 
 
 
 
 
 
 
 
 
 
 
 
355
 
356
+ # <<<--- CHANGE: Instantiate ChatCohere directly --->>>
357
+ # It will use the COHERE_API_KEY from your secrets.
358
+ # We use command-r-plus, Cohere's most powerful model.
359
+ llm = ChatCohere(model="command-r-plus", temperature=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
 
361
+ # <<<--- This is much simpler now, as ChatCohere has built-in agent capabilities --->>>
362
+ agent = CohereAgent(llm=llm, tools=tools)
363
+ agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
364
 
365
+ return agent_executor
 
 
 
 
 
 
 
 
 
 
 
 
 
366
 
367
  #
368
  # ================================================================================================
 
371
  #
372
  class GaiaAgent:
373
  def __init__(self):
374
+ print("GaiaAgent initialized. Building agent with direct ChatCohere integration...")
375
  self.agent_app = build_agent_graph()
376
 
377
  def __call__(self, question: str) -> str:
378
  print(f"\n{'='*60}\nAgent received question: {question[:100]}...\n{'='*60}")
379
+ try:
380
+ # The Cohere agent executor expects 'input' and a 'preamble' for the system message.
381
+ response = self.agent_app.invoke({
382
+ "input": question,
383
+ "preamble": AGENT_SYSTEM_PROMPT
384
+ })
385
+ final_answer = str(response.get("output", "")).strip()
386
+ print(f"\n--- Agent finished. Final Answer: {final_answer} ---\n")
387
+ return final_answer
388
+ except Exception as e:
389
+ print(f"An error occurred during agent execution: {e}")
390
+ return f"AGENT_EXECUTION_ERROR: {e}"
391
+
392
+ # --- The rest of the file is mostly the same ---
393
  def run_and_submit_all( profile: gr.OAuthProfile | None):
394
  space_id = os.getenv("SPACE_ID")
395
  if not profile: return "Please Login to Hugging Face with the button.", None
 
404
  response = requests.get(questions_url, timeout=15)
405
  response.raise_for_status()
406
  questions_data = response.json()
407
+ except Exception as e: return f"An unexpected error occurred fetching questions: {e}", None
 
408
 
409
+ results_log, answers_payload = [], []
 
410
  agent_instance = GaiaAgent()
411
 
412
  for item in questions_data:
413
+ task_id, question_text = item.get("task_id"), item.get("question")
 
414
  if not task_id or question_text is None: continue
415
  try:
416
  submitted_answer = agent_instance(question_text)
 
420
  print(f"Error running agent on task {task_id}: {e}")
421
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
422
 
423
+ if not answers_payload: return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
 
 
424
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
425
+
426
  try:
427
  response = requests.post(submit_url, json=submission_data, timeout=90)
428
  response.raise_for_status()
 
435
  f"Message: {result_data.get('message', 'No message received.')}"
436
  )
437
  return final_status, pd.DataFrame(results_log)
438
+ except Exception as e: return f"An unexpected error occurred during submission: {e}", pd.DataFrame(results_log)
 
439
 
440
  with gr.Blocks() as demo:
441
+ gr.Markdown("# GAIA Agent Final Assessment (Direct Cohere Integration)")
442
  gr.Markdown(
443
  """
444
+ **Instructor's Note:** We are now using the direct `langchain-cohere` integration. This is the most reliable way to use the Command R+ model.
445
+ 1. Ensure you have a **`COHERE_API_KEY`** and a **`TAVILY_API_KEY`** set in your Space secrets.
446
+ 2. Ensure your `requirements.txt` includes `langchain-cohere`.
447
  """
448
  )
449
  gr.LoginButton()
 
454
 
455
  if __name__ == "__main__":
456
  print("\n" + "-"*30 + " App Starting " + "-"*30)
457
+ # Disable experimental SSR to prevent startup crashes
458
+ demo.launch(debug=True, share=False, ssr_mode=False)