Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,8 +4,8 @@ import requests
|
|
| 4 |
import pandas as pd
|
| 5 |
from typing import TypedDict, Annotated, Sequence
|
| 6 |
import operator
|
| 7 |
-
from langchain_core.messages import BaseMessage, HumanMessage
|
| 8 |
-
from
|
| 9 |
from langchain_openai import ChatOpenAI
|
| 10 |
from langgraph.graph import StateGraph, END
|
| 11 |
from langgraph.prebuilt import ToolNode, tools_condition
|
|
@@ -16,22 +16,68 @@ from langgraph.prebuilt import ToolNode, tools_condition
|
|
| 16 |
class AgentState(TypedDict):
|
| 17 |
messages: Annotated[Sequence[BaseMessage], operator.add]
|
| 18 |
|
| 19 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
def create_langgraph_agent():
|
| 21 |
print("Initializing LangGraph Agent with OpenAI...")
|
| 22 |
|
| 23 |
-
# 1. Set up the LLM (The "Brain")
|
| 24 |
-
llm = ChatOpenAI(model="gpt-
|
| 25 |
|
| 26 |
-
# 2. Define the Tools,
|
| 27 |
-
tools = [
|
| 28 |
llm_with_tools = llm.bind_tools(tools)
|
| 29 |
print("LLM and tools initialized.")
|
| 30 |
|
| 31 |
-
# 3. Define the
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
def agent_node(state):
|
| 33 |
print("Calling agent node...")
|
| 34 |
-
|
|
|
|
|
|
|
| 35 |
return {"messages": [response]}
|
| 36 |
|
| 37 |
tool_node = ToolNode(tools)
|
|
@@ -41,7 +87,6 @@ def create_langgraph_agent():
|
|
| 41 |
graph = StateGraph(AgentState)
|
| 42 |
graph.add_node("agent", agent_node)
|
| 43 |
graph.add_node("tools", tool_node)
|
| 44 |
-
|
| 45 |
graph.set_entry_point("agent")
|
| 46 |
graph.add_conditional_edges("agent", tools_condition)
|
| 47 |
graph.add_edge("tools", "agent")
|
|
@@ -51,12 +96,20 @@ def create_langgraph_agent():
|
|
| 51 |
print("LangGraph agent compiled and ready.")
|
| 52 |
return app
|
| 53 |
|
| 54 |
-
# This function runs the agent
|
| 55 |
-
def run_agent(agent_executor, question: str) -> str:
|
| 56 |
print(f"Agent received question: {question}")
|
| 57 |
try:
|
| 58 |
-
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
except Exception as e:
|
| 61 |
print(f"Error during agent execution: {e}")
|
| 62 |
final_answer = f"Error: Agent failed to execute. {e}"
|
|
@@ -71,8 +124,9 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
|
|
| 71 |
return "Please Login to Hugging Face with the button.", None
|
| 72 |
username = f"{profile.username}"
|
| 73 |
|
| 74 |
-
|
| 75 |
-
|
|
|
|
| 76 |
|
| 77 |
try:
|
| 78 |
agent_executor = create_langgraph_agent()
|
|
@@ -93,13 +147,13 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
|
|
| 93 |
for item in questions_data:
|
| 94 |
task_id, question_text = item.get("task_id"), item.get("question")
|
| 95 |
if task_id and question_text:
|
| 96 |
-
submitted_answer = run_agent(agent_executor, question_text)
|
| 97 |
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
|
| 98 |
|
| 99 |
submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
|
| 100 |
submit_url = f"https://agents-course-unit4-scoring.hf.space/submit"
|
| 101 |
try:
|
| 102 |
-
response = requests.post(submit_url, json=submission_data, timeout=
|
| 103 |
response.raise_for_status()
|
| 104 |
result_data = response.json()
|
| 105 |
final_status = (
|
|
@@ -115,7 +169,7 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
|
|
| 115 |
|
| 116 |
# Gradio Interface
|
| 117 |
with gr.Blocks() as demo:
|
| 118 |
-
gr.Markdown("# Agent Evaluation Runner (
|
| 119 |
gr.LoginButton()
|
| 120 |
run_button = gr.Button("Run Evaluation & Submit All Answers")
|
| 121 |
status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
|
|
|
|
| 4 |
import pandas as pd
|
| 5 |
from typing import TypedDict, Annotated, Sequence
|
| 6 |
import operator
|
| 7 |
+
from langchain_core.messages import BaseMessage, HumanMessage, ToolMessage
|
| 8 |
+
from langchain.tools import tool
|
| 9 |
from langchain_openai import ChatOpenAI
|
| 10 |
from langgraph.graph import StateGraph, END
|
| 11 |
from langgraph.prebuilt import ToolNode, tools_condition
|
|
|
|
| 16 |
class AgentState(TypedDict):
|
| 17 |
messages: Annotated[Sequence[BaseMessage], operator.add]
|
| 18 |
|
| 19 |
+
# NEW: Custom tool for downloading files from the course API
|
| 20 |
+
@tool
|
| 21 |
+
def download_file(task_id: str) -> str:
|
| 22 |
+
"""
|
| 23 |
+
Downloads a file associated with a specific task_id from the course API.
|
| 24 |
+
The file will be saved in the /tmp/ directory.
|
| 25 |
+
Use this tool when a question mentions an attached file (e.g., image, excel, audio).
|
| 26 |
+
"""
|
| 27 |
+
try:
|
| 28 |
+
# NOTE: This assumes the Space is running on the Hugging Face platform
|
| 29 |
+
# and can access the scoring API directly.
|
| 30 |
+
file_url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
|
| 31 |
+
response = requests.get(file_url)
|
| 32 |
+
response.raise_for_status()
|
| 33 |
+
|
| 34 |
+
# Extract filename from headers, or default to task_id
|
| 35 |
+
content_disposition = response.headers.get('content-disposition')
|
| 36 |
+
if content_disposition:
|
| 37 |
+
parts = content_disposition.split(';')
|
| 38 |
+
for part in parts:
|
| 39 |
+
if 'filename=' in part:
|
| 40 |
+
filename = part.split('=')[1].strip('"')
|
| 41 |
+
break
|
| 42 |
+
else:
|
| 43 |
+
# Fallback filename if header is not present
|
| 44 |
+
# This is a simplification; a real app might need better content-type handling
|
| 45 |
+
filename = task_id
|
| 46 |
+
|
| 47 |
+
# Save the file to a temporary directory within the Space
|
| 48 |
+
save_path = f"/tmp/{filename}"
|
| 49 |
+
with open(save_path, "wb") as f:
|
| 50 |
+
f.write(response.content)
|
| 51 |
+
|
| 52 |
+
return f"Successfully downloaded file for task {task_id}. It is available at path: {save_path}"
|
| 53 |
+
except Exception as e:
|
| 54 |
+
return f"An error occurred while downloading the file: {str(e)}"
|
| 55 |
+
|
| 56 |
+
# This function builds our final, robust agent.
|
| 57 |
def create_langgraph_agent():
|
| 58 |
print("Initializing LangGraph Agent with OpenAI...")
|
| 59 |
|
| 60 |
+
# 1. Set up the LLM (The "Brain")
|
| 61 |
+
llm = ChatOpenAI(model="gpt-4o", temperature=0) # Switched to gpt-4o for better performance
|
| 62 |
|
| 63 |
+
# 2. Define the Tools, including our new file downloader
|
| 64 |
+
tools = [TavilySearchResults(max_results=3), download_file]
|
| 65 |
llm_with_tools = llm.bind_tools(tools)
|
| 66 |
print("LLM and tools initialized.")
|
| 67 |
|
| 68 |
+
# 3. Define the agent's logic (the "agent" node)
|
| 69 |
+
# NEW: We are using a much more specific system prompt based on the GAIA paper
|
| 70 |
+
system_prompt = """You are a general AI assistant. I will ask you a question. Report your thoughts, and finish your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER].
|
| 71 |
+
YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.
|
| 72 |
+
If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise.
|
| 73 |
+
If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise.
|
| 74 |
+
If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string."""
|
| 75 |
+
|
| 76 |
def agent_node(state):
|
| 77 |
print("Calling agent node...")
|
| 78 |
+
# Add the system prompt to the beginning of the conversation
|
| 79 |
+
messages_with_system_prompt = [("system", system_prompt)] + state["messages"]
|
| 80 |
+
response = llm_with_tools.invoke(messages_with_system_prompt)
|
| 81 |
return {"messages": [response]}
|
| 82 |
|
| 83 |
tool_node = ToolNode(tools)
|
|
|
|
| 87 |
graph = StateGraph(AgentState)
|
| 88 |
graph.add_node("agent", agent_node)
|
| 89 |
graph.add_node("tools", tool_node)
|
|
|
|
| 90 |
graph.set_entry_point("agent")
|
| 91 |
graph.add_conditional_edges("agent", tools_condition)
|
| 92 |
graph.add_edge("tools", "agent")
|
|
|
|
| 96 |
print("LangGraph agent compiled and ready.")
|
| 97 |
return app
|
| 98 |
|
| 99 |
+
# This function runs the agent and extracts the final answer.
|
| 100 |
+
def run_agent(agent_executor, question: str, task_id: str) -> str:
|
| 101 |
print(f"Agent received question: {question}")
|
| 102 |
try:
|
| 103 |
+
# Pass the task_id to the agent's input so the file downloader can use it
|
| 104 |
+
response = agent_executor.invoke({"messages": [HumanMessage(content=f"Task ID: {task_id}\n\nQuestion: {question}")]})
|
| 105 |
+
|
| 106 |
+
# NEW: Extract only the part after "FINAL ANSWER:"
|
| 107 |
+
raw_answer = response['messages'][-1].content
|
| 108 |
+
if "FINAL ANSWER:" in raw_answer:
|
| 109 |
+
final_answer = raw_answer.split("FINAL ANSWER:")[-1].strip()
|
| 110 |
+
else:
|
| 111 |
+
final_answer = raw_answer # Fallback if the model doesn't follow the format
|
| 112 |
+
|
| 113 |
except Exception as e:
|
| 114 |
print(f"Error during agent execution: {e}")
|
| 115 |
final_answer = f"Error: Agent failed to execute. {e}"
|
|
|
|
| 124 |
return "Please Login to Hugging Face with the button.", None
|
| 125 |
username = f"{profile.username}"
|
| 126 |
|
| 127 |
+
# API key checks
|
| 128 |
+
if not os.getenv("TAVILY_API_KEY") or not os.getenv("OPENAI_API_KEY"):
|
| 129 |
+
return "One or more API keys (TAVILY_API_KEY, OPENAI_API_KEY) are not set. Please set them in your Space secrets.", None
|
| 130 |
|
| 131 |
try:
|
| 132 |
agent_executor = create_langgraph_agent()
|
|
|
|
| 147 |
for item in questions_data:
|
| 148 |
task_id, question_text = item.get("task_id"), item.get("question")
|
| 149 |
if task_id and question_text:
|
| 150 |
+
submitted_answer = run_agent(agent_executor, question_text, task_id)
|
| 151 |
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
|
| 152 |
|
| 153 |
submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
|
| 154 |
submit_url = f"https://agents-course-unit4-scoring.hf.space/submit"
|
| 155 |
try:
|
| 156 |
+
response = requests.post(submit_url, json=submission_data, timeout=180) # Increased timeout for gpt-4
|
| 157 |
response.raise_for_status()
|
| 158 |
result_data = response.json()
|
| 159 |
final_status = (
|
|
|
|
| 169 |
|
| 170 |
# Gradio Interface
|
| 171 |
with gr.Blocks() as demo:
|
| 172 |
+
gr.Markdown("# Agent Evaluation Runner (GPT-4o + LangGraph)")
|
| 173 |
gr.LoginButton()
|
| 174 |
run_button = gr.Button("Run Evaluation & Submit All Answers")
|
| 175 |
status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
|