Spaces:
Sleeping
Sleeping
Update agent.py
Browse files
agent.py
CHANGED
|
@@ -169,6 +169,7 @@ from transformers import Blip2Processor, Blip2ForConditionalGeneration
|
|
| 169 |
|
| 170 |
from tavily import TavilyClient
|
| 171 |
|
|
|
|
| 172 |
GPT_4o_API_KEY = os.environ["GPT_4o_API_KEY"]
|
| 173 |
TAVILY_API_KEY = os.environ["TAVILY_API_KEY"]
|
| 174 |
Pixtral12B_API = os.environ["Pixtral12B_API"]
|
|
@@ -183,24 +184,17 @@ model = ChatOpenAI(openai_api_key=GPT_4o_API_KEY,
|
|
| 183 |
tavily = TavilyClient(api_key=TAVILY_API_KEY)
|
| 184 |
|
| 185 |
|
| 186 |
-
|
| 187 |
# === 1. AGENT STATE ===
|
| 188 |
-
|
| 189 |
-
class GaiaAgentState
|
| 190 |
-
|
| 191 |
-
question: str
|
| 192 |
-
files:
|
| 193 |
-
level:
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
# def input_parser(state: GaiaAgentState) -> dict:
|
| 199 |
-
# return {
|
| 200 |
-
# "question": state["question"],
|
| 201 |
-
# "files": state.get("files", []),
|
| 202 |
-
# "level": state.get("level", 1)
|
| 203 |
-
# }
|
| 204 |
|
| 205 |
# === 2. TOOL STUBS ===
|
| 206 |
|
|
@@ -259,11 +253,9 @@ def encode_image_to_base64(image_path):
|
|
| 259 |
def vision_caption_via_api(image_path: str, max_length: Optional[int] = 64) -> str:
|
| 260 |
"""
|
| 261 |
Generate a natural‑language caption for the image at `image_path`.
|
| 262 |
-
|
| 263 |
Args:
|
| 264 |
image_path: Local filesystem path (or URL fetched locally) to an image file.
|
| 265 |
max_length: Maximum number of tokens in the generated caption (default: 64).
|
| 266 |
-
|
| 267 |
Returns:
|
| 268 |
A concise, human‑readable caption describing the image content.
|
| 269 |
"""
|
|
@@ -298,7 +290,7 @@ def vision_caption_via_api(image_path: str, max_length: Optional[int] = 64) -> s
|
|
| 298 |
"messages": messages
|
| 299 |
}
|
| 300 |
|
| 301 |
-
response = requests.post(
|
| 302 |
response.raise_for_status()
|
| 303 |
|
| 304 |
# The HF endpoint returns JSON like {"generated_text": "...caption..."}
|
|
@@ -360,11 +352,9 @@ TOOLS: Dict[str, Any] = {
|
|
| 360 |
def planner_node(state: GaiaAgentState) -> dict:
|
| 361 |
# Build metadata for each file
|
| 362 |
files_metadata = []
|
| 363 |
-
print(
|
| 364 |
-
|
| 365 |
-
for f_path in state['files']:
|
| 366 |
# for f_path in state.files:
|
| 367 |
-
print(5555555555555555555555555555555555555555555555555555555)
|
| 368 |
mime, _ = mimetypes.guess_type(f_path)
|
| 369 |
kind = "image" if mime and mime.startswith("image/") else "text"
|
| 370 |
files_metadata.append({
|
|
@@ -373,9 +363,9 @@ def planner_node(state: GaiaAgentState) -> dict:
|
|
| 373 |
})
|
| 374 |
|
| 375 |
human_content = json.dumps({
|
| 376 |
-
"question": state
|
| 377 |
"files": files_metadata,
|
| 378 |
-
"level": state
|
| 379 |
})
|
| 380 |
|
| 381 |
messages = [
|
|
@@ -389,25 +379,23 @@ def planner_node(state: GaiaAgentState) -> dict:
|
|
| 389 |
|
| 390 |
def tool_executor(state: GaiaAgentState) -> dict:
|
| 391 |
tool_results = []
|
| 392 |
-
for step in state
|
| 393 |
tool_obj = TOOLS[step["tool"]]
|
| 394 |
out = tool_obj.run(tool_input=step["args"])
|
| 395 |
tool_results.append(out)
|
| 396 |
-
return {'tool_results':
|
| 397 |
|
| 398 |
|
| 399 |
# ===== 2. The node implementation =====
|
| 400 |
def synthesizer(state: GaiaAgentState) -> dict:
|
| 401 |
|
| 402 |
# Format tool outputs as bullet points
|
| 403 |
-
snippet_text = "\n".join(f"- {s}" for s in state
|
| 404 |
|
| 405 |
human_payload = f"""
|
| 406 |
-
Question: {state
|
| 407 |
-
|
| 408 |
Tool outputs:
|
| 409 |
{snippet_text}
|
| 410 |
-
|
| 411 |
Using the above, answer the question.
|
| 412 |
"""
|
| 413 |
messages = [
|
|
@@ -432,26 +420,51 @@ def synthesizer(state: GaiaAgentState) -> dict:
|
|
| 432 |
return {"final_answer": final}
|
| 433 |
|
| 434 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 435 |
|
| 436 |
-
# === 4. BUILD & COMPILE GRAPH ===
|
| 437 |
-
# agent.py
|
| 438 |
class EsiAgent:
|
| 439 |
def __init__(self):
|
| 440 |
# 1. Build & compile your graph
|
| 441 |
builder = StateGraph(GaiaAgentState)
|
| 442 |
|
| 443 |
-
|
| 444 |
builder.add_node("planner", planner_node)
|
| 445 |
builder.add_node("execute", tool_executor)
|
| 446 |
builder.add_node("synthesize", synthesizer)
|
| 447 |
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
builder.add_edge(START, "planner")
|
| 451 |
builder.add_edge("planner", "execute")
|
| 452 |
builder.add_edge("execute", "synthesize")
|
| 453 |
builder.add_edge("synthesize", END)
|
| 454 |
-
|
| 455 |
self.graph = builder.compile()
|
| 456 |
|
| 457 |
|
|
@@ -462,75 +475,18 @@ class EsiAgent:
|
|
| 462 |
files: optional list of file paths/URLs (default empty list)
|
| 463 |
level: GAIA difficulty (1,2,3)
|
| 464 |
"""
|
| 465 |
-
|
| 466 |
-
# print(11111111111, question)
|
| 467 |
payload = {
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
}
|
| 473 |
-
# initial = {
|
| 474 |
-
# "question": question,
|
| 475 |
-
# "files": [], # or your file list
|
| 476 |
-
# "level": 1,
|
| 477 |
-
# "task_id": 1,
|
| 478 |
-
# "plan": [], # planner_node will overwrite this
|
| 479 |
-
# "tool_results": [] # executor will overwrite this
|
| 480 |
-
# "final_answer": ""
|
| 481 |
-
# }
|
| 482 |
-
|
| 483 |
-
# print(2222222222, payload)
|
| 484 |
-
|
| 485 |
-
# 2) Wrap in your custom state class
|
| 486 |
state = GaiaAgentState(raw_payload=payload)
|
| 487 |
-
# state = GaiaAgentState(dict)
|
| 488 |
-
|
| 489 |
-
# print(6666666666666, state)
|
| 490 |
-
# 3) Invoke the graph end-to-end
|
| 491 |
final_state = self.graph.invoke(state)
|
| 492 |
|
| 493 |
# 4) Pull out the final answer
|
| 494 |
return final_state.final_answer
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
# final_state = graph.invoke({
|
| 498 |
-
# "task_id": None,
|
| 499 |
-
# "question": question,
|
| 500 |
-
# "files": [],
|
| 501 |
-
# "level": 1,
|
| 502 |
-
# "plan": [],
|
| 503 |
-
# "tool_results": [],
|
| 504 |
-
# # plus any other initial keys your nodes expect
|
| 505 |
-
# })
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
# def run(self, question: str) -> str:
|
| 509 |
-
# """
|
| 510 |
-
# Runs the agent end‑to‑end on a single question string and returns
|
| 511 |
-
# just the answer text (stripped of any prefix).
|
| 512 |
-
# """
|
| 513 |
-
# # Wrap in the same state shape your graph expects
|
| 514 |
-
# payload = {
|
| 515 |
-
# "task_id": None,
|
| 516 |
-
# "question": question,
|
| 517 |
-
# "files": [], # no attachments
|
| 518 |
-
# "level": 1 # default to Level 1
|
| 519 |
-
# }
|
| 520 |
-
# # 2) Wrap in your state object
|
| 521 |
-
# initial_state = GaiaAgentState(raw_payload=payload)
|
| 522 |
-
|
| 523 |
-
# # initial_state = {"messages": [HumanMessage(content=question)]}
|
| 524 |
-
# # Invoke the graph
|
| 525 |
-
# final_state = self.graph.invoke(initial_state)
|
| 526 |
-
# # Extract the last message
|
| 527 |
-
# msgs = final_state["messages"]
|
| 528 |
-
# last = msgs[-1].content
|
| 529 |
-
# # If you prefix with "FINAL ANSWER: " remove that
|
| 530 |
-
# if last.startswith("FINAL ANSWER:"):
|
| 531 |
-
# return last[len("FINAL ANSWER:"):].strip()
|
| 532 |
-
# return last.strip()
|
| 533 |
-
|
| 534 |
# Optional: allow running agent.py directly for quick tests
|
| 535 |
if __name__ == "__main__":
|
| 536 |
agent = EsiAgent()
|
|
|
|
| 169 |
|
| 170 |
from tavily import TavilyClient
|
| 171 |
|
| 172 |
+
# GPT_4o_API_KEY = os.environ["DeepSeek_API_KEY"]
|
| 173 |
GPT_4o_API_KEY = os.environ["GPT_4o_API_KEY"]
|
| 174 |
TAVILY_API_KEY = os.environ["TAVILY_API_KEY"]
|
| 175 |
Pixtral12B_API = os.environ["Pixtral12B_API"]
|
|
|
|
| 184 |
tavily = TavilyClient(api_key=TAVILY_API_KEY)
|
| 185 |
|
| 186 |
|
|
|
|
| 187 |
# === 1. AGENT STATE ===
|
| 188 |
+
@dataclass
|
| 189 |
+
class GaiaAgentState:
|
| 190 |
+
raw_payload: Dict[str, Any]
|
| 191 |
+
question: str = ""
|
| 192 |
+
files: List[str] = field(default_factory=list)
|
| 193 |
+
level: int = 1
|
| 194 |
+
# downstream nodes can write into these:
|
| 195 |
+
plan: List[Dict[str,Any]] = field(default_factory=list)
|
| 196 |
+
tool_results: List[Any] = field(default_factory=list)
|
| 197 |
+
final_answer: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
|
| 199 |
# === 2. TOOL STUBS ===
|
| 200 |
|
|
|
|
| 253 |
def vision_caption_via_api(image_path: str, max_length: Optional[int] = 64) -> str:
|
| 254 |
"""
|
| 255 |
Generate a natural‑language caption for the image at `image_path`.
|
|
|
|
| 256 |
Args:
|
| 257 |
image_path: Local filesystem path (or URL fetched locally) to an image file.
|
| 258 |
max_length: Maximum number of tokens in the generated caption (default: 64).
|
|
|
|
| 259 |
Returns:
|
| 260 |
A concise, human‑readable caption describing the image content.
|
| 261 |
"""
|
|
|
|
| 290 |
"messages": messages
|
| 291 |
}
|
| 292 |
|
| 293 |
+
response = requests.post(API_URL, headers=headers, json=payload)
|
| 294 |
response.raise_for_status()
|
| 295 |
|
| 296 |
# The HF endpoint returns JSON like {"generated_text": "...caption..."}
|
|
|
|
| 352 |
def planner_node(state: GaiaAgentState) -> dict:
|
| 353 |
# Build metadata for each file
|
| 354 |
files_metadata = []
|
| 355 |
+
# print(55555555555555,state.files)
|
| 356 |
+
for f_path in state.files:
|
|
|
|
| 357 |
# for f_path in state.files:
|
|
|
|
| 358 |
mime, _ = mimetypes.guess_type(f_path)
|
| 359 |
kind = "image" if mime and mime.startswith("image/") else "text"
|
| 360 |
files_metadata.append({
|
|
|
|
| 363 |
})
|
| 364 |
|
| 365 |
human_content = json.dumps({
|
| 366 |
+
"question": state.question,
|
| 367 |
"files": files_metadata,
|
| 368 |
+
"level": state.level
|
| 369 |
})
|
| 370 |
|
| 371 |
messages = [
|
|
|
|
| 379 |
|
| 380 |
def tool_executor(state: GaiaAgentState) -> dict:
|
| 381 |
tool_results = []
|
| 382 |
+
for step in state.plan:
|
| 383 |
tool_obj = TOOLS[step["tool"]]
|
| 384 |
out = tool_obj.run(tool_input=step["args"])
|
| 385 |
tool_results.append(out)
|
| 386 |
+
return {'tool_results': tool_results}
|
| 387 |
|
| 388 |
|
| 389 |
# ===== 2. The node implementation =====
|
| 390 |
def synthesizer(state: GaiaAgentState) -> dict:
|
| 391 |
|
| 392 |
# Format tool outputs as bullet points
|
| 393 |
+
snippet_text = "\n".join(f"- {s}" for s in state.tool_results)
|
| 394 |
|
| 395 |
human_payload = f"""
|
| 396 |
+
Question: {state.question}
|
|
|
|
| 397 |
Tool outputs:
|
| 398 |
{snippet_text}
|
|
|
|
| 399 |
Using the above, answer the question.
|
| 400 |
"""
|
| 401 |
messages = [
|
|
|
|
| 420 |
return {"final_answer": final}
|
| 421 |
|
| 422 |
|
| 423 |
+
# builder = StateGraph(dict)
|
| 424 |
+
from dataclasses import dataclass, field
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
@dataclass
|
| 428 |
+
class GaiaAgentState:
|
| 429 |
+
raw_payload: Dict[str, Any]
|
| 430 |
+
# these will get populated by the input_parser node:
|
| 431 |
+
question: str = ""
|
| 432 |
+
files: List[str] = field(default_factory=list)
|
| 433 |
+
level: int = 1
|
| 434 |
+
# downstream nodes can write into these:
|
| 435 |
+
plan: List[Dict[str,Any]] = field(default_factory=list)
|
| 436 |
+
tool_results: List[Any] = field(default_factory=list)
|
| 437 |
+
final_answer: str = ""
|
| 438 |
+
|
| 439 |
+
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
# 2) Input‑parser node: unpacks raw_payload→working state
|
| 443 |
+
def input_parser(state: GaiaAgentState) -> dict:
|
| 444 |
+
return {
|
| 445 |
+
"question": state.raw_payload["question"],
|
| 446 |
+
"files": state.raw_payload.get("files", []),
|
| 447 |
+
"level": state.raw_payload.get("level", 1),
|
| 448 |
+
}
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
|
| 452 |
|
|
|
|
|
|
|
| 453 |
class EsiAgent:
|
| 454 |
def __init__(self):
|
| 455 |
# 1. Build & compile your graph
|
| 456 |
builder = StateGraph(GaiaAgentState)
|
| 457 |
|
| 458 |
+
builder.add_node("parse", input_parser)
|
| 459 |
builder.add_node("planner", planner_node)
|
| 460 |
builder.add_node("execute", tool_executor)
|
| 461 |
builder.add_node("synthesize", synthesizer)
|
| 462 |
|
| 463 |
+
builder.add_edge(START, "parse")
|
| 464 |
+
builder.add_edge("parse", "planner")
|
|
|
|
| 465 |
builder.add_edge("planner", "execute")
|
| 466 |
builder.add_edge("execute", "synthesize")
|
| 467 |
builder.add_edge("synthesize", END)
|
|
|
|
| 468 |
self.graph = builder.compile()
|
| 469 |
|
| 470 |
|
|
|
|
| 475 |
files: optional list of file paths/URLs (default empty list)
|
| 476 |
level: GAIA difficulty (1,2,3)
|
| 477 |
"""
|
| 478 |
+
|
|
|
|
| 479 |
payload = {
|
| 480 |
+
"task_id": None,
|
| 481 |
+
"question": question,
|
| 482 |
+
"files": [],
|
| 483 |
+
"level": 1
|
| 484 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
state = GaiaAgentState(raw_payload=payload)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 486 |
final_state = self.graph.invoke(state)
|
| 487 |
|
| 488 |
# 4) Pull out the final answer
|
| 489 |
return final_state.final_answer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 490 |
# Optional: allow running agent.py directly for quick tests
|
| 491 |
if __name__ == "__main__":
|
| 492 |
agent = EsiAgent()
|