hshkoukani's picture
Update agent.py
d06fea0 verified
Raw
History Blame Contribute Delete
6.56 kB
from typing import List, TypedDict, Annotated, Optional
from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
from langgraph.graph.message import add_messages
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_groq import ChatGroq
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.tools import tool
from langchain_community.tools import DuckDuckGoSearchRun
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
import requests, io, pandas as pd, PyPDF2, ast, pytesseract
from PIL import Image
class AgentState(TypedDict):
messages: Annotated[list[AnyMessage], add_messages]
@tool
def add(a: float, b: float) -> float:
"""
Adds two numbers.
Args:
a (float): First number.
b (float): Second number.
Returns:
float: Sum of a and b.
"""
return a + b
@tool
def subtract(a: float, b: float) -> float:
"""
Subtracts one number from another.
Args:
a (float): Minuend.
b (float): Subtrahend.
Returns:
float: Result of a - b.
"""
return a - b
@tool
def multiply(a: float, b: float) -> float:
"""
Multiplies two numbers.
Args:
a (float): First number.
b (float): Second number.
Returns:
float: Product of a and b.
"""
return a * b
@tool
def divide(a: float, b: float) -> float:
"""
Divides one number by another.
Args:
a (float): Dividend.
b (float): Divisor.
Raises:
ValueError: If b is zero.
Returns:
float: Result of a / b.
"""
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
@tool
def web_search(query: str) -> str:
"""
Searches the web for the given query.
Args:
query (str): The search query.
Returns:
str: The search results
"""
search = DuckDuckGoSearchRun()
print("search")
return search.invoke(query)
@tool
def wikisearch(query: str) -> str:
"""
Searches wikipedia for the given query.
Args:
query (str): The search query.
Returns:
str: The wikipedia results.
"""
wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
print("wiki")
return wikipedia.run(query)
@tool
def gaia_retriever_tool(task_id: str) -> str:
"""
Downloads a file from a Gaia task using the provided task_id and returns its raw text content.
Does NOT analyze or interpret the content.
Supports PDFs, Excel/CSV spreadsheets, Python scripts, and images.
"""
print("retriever")
url = f'https://agents-course-unit4-scoring.hf.space/files/{task_id}'
resp = requests.get(url)
if resp.status_code != 200:
return f"Failed to download file {task_id}"
file_bytes = io.BytesIO(resp.content)
text = ""
try:
df = pd.read_excel(file_bytes)
text = df.to_csv(index=False)
except Exception:
try:
file_bytes.seek(0)
df = pd.read_csv(file_bytes)
text = df.to_csv(index=False)
except Exception:
try:
file_bytes.seek(0)
reader = PyPDF2.PdfReader(file_bytes)
text = "\n".join([p.extract_text() or "" for p in reader.pages])
except Exception:
try:
file_bytes.seek(0)
img = Image.open(file_bytes)
text = pytesseract.image_to_string(img)
except Exception:
file_bytes.seek(0)
text = file_bytes.read().decode("utf-8", errors="ignore")
return text
tools = [add, subtract, multiply, divide, web_search, wikisearch, gaia_retriever_tool]
llm = ChatGoogleGenerativeAI(
model="gemini-2.5-flash",
api_key="...",
temperature=0.3,
)
llm_with_tools = llm.bind_tools(tools)
system_prompt = SystemMessage(
content="""
You are a concise AI assistant. Use tools only when strictly necessary.
**Math:** use add(a,b), subtract(a,b), multiply(a,b), divide(a,b).
**Search:**
- Always try wikisearch(query) first.
- If wiki fails or is insufficient, use web_search(query).
- Only fallback to general knowledge if both fail.
**Files:** use gaia_retriever_tool(task_id) **whenever the question mentions a file, attachment, picture, Excel, CSV, PDF, or Python file.**
- Variations like "attached Excel file", "following CSV file", or "image attached" count as explicit mentions.
- **The agent should pass the task_id from the question to gaia_retriever_tool.**
- **The tool itself handles downloading and extracting content.**
- **Do NOT attempt to download or read the file outside the tool.**
- **Do NOT call gaia_retriever_tool if no file is mentioned.**
**Answer rules:**
1. Give **exact answer only**, no explanations.
2. Numbers → output only the number.
3. Lists → comma-separated values, no words.
4. Text → 1–5 words.
5. Conceptual questions (opposites, synonyms, meanings) → answer correctly in 1 word.
6. Never include extra context, tool names, or sentences.
7. Your answer must strictly be short
**Example 1:**
Question: Who is the president of France?
Action: wikisearch("president of France") → returns "Emmanuel Macron"
Answer: Emmanuel Macron
**Example 2:**
Question: Some obscure topic, task_id: 123
Action: wikisearch("Some obscure topic") → returns ""
Since wiki returned nothing, call web_search("Some obscure topic")
Answer: [result from web_search]
**Do NOT call gaia_retriever_tool because the question does not mention a file.**
**Example 3:**
Question: The attached Excel file contains sales data, task_id: 7bd855d8-463d-4ed5-93ca-5fe35145f733
Action: gaia_retriever_tool("7bd855d8-463d-4ed5-93ca-5fe35145f733") → returns file content
Answer: 30420.00
"""
)
def assistant(state: AgentState):
return {
"messages": [llm_with_tools.invoke([system_prompt] + state["messages"])],
}
builder = StateGraph(AgentState)
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "assistant")
builder.add_conditional_edges("assistant", tools_condition)
builder.add_edge("tools", "assistant")
graph = builder.compile()
def run(prompt, task_id='1'):
state: AgentState = {"messages": [HumanMessage(prompt)]}
output = graph.invoke(state)
return output["messages"][-1].content