GenAI-Hackathon / agent.py
PrashantKansagara9880's picture
Added required python scripts
4d7825a
Raw
History Blame Contribute Delete
6.79 kB
import json
import os
from safety import safe_call
class Agent:
def __init__(self, llm, tools, max_steps=5):
self.llm = llm
self.tools = {tool.name: tool for tool in tools}
self.max_steps = max_steps
self.memory = {}
self.current_image=None
if os.path.exists("memory.json"):
with open("memory.json", "r") as f:
self.memory = json.load(f)
else:
self.memory = {}
@safe_call
def call_llm(self, prompt):
return self.llm.invoke(prompt)
@safe_call
def execute_tool(self, tool_name, tool_input, thread_id):
if tool_name not in self.tools:
raise ValueError(f"Tool '{tool_name}' does not exist.")
if tool_name == "search_documents":
return self.tools[tool_name].func(
tool_input,
thread_id
)
elif tool_name == "describe_image":
if self.current_image is None:
raise ValueError("No image has been uploaded.")
else:
return self.tools[tool_name].invoke(self.current_image)
else:
return self.tools[tool_name].invoke(tool_input)
@safe_call
def persist_memory(self):
with open("memory.json", "w") as f:
json.dump(self.memory, f, indent=4)
def _build_prompt(self, user_query, scratchpad,history):
tool_descriptions = "\n".join(
f"- {name}" for name in self.tools.keys()
)
return f"""
You are an AI Agent.
Available tools:
{tool_descriptions}
search_documents
Search the user's uploaded documents for information relevent to the query.Use this when the
user asks about content from a PDF they uploaded, or references 'the document', 'my notes',
or 'the file'.
Do NOT use this for general knowledge or current events
web_search
Use this tool to search the web for real-time information.
Input: a search query string.
Output: a summary of the most relevant search results.
Use this for current events, news, live prices, or anything published after 2024.
describe_image
Use ONLY when an uploaded image must be analysed.
You MUST respond ONLY with valid JSON.
If you need a tool:
{{
"action": "tool",
"tool_name": "<tool_name>",
"tool_input": "<query>"
}}
If the tool you are using is describe_image then you can respond like this:
{{
"action": "tool",
"tool_name": "<tool_name>",
"tool_input": "CURRENT_IMAGE"
}}
here CURRENT_IMAGE is a special keyword that tells the agent to use the image that was last uploaded by the user.
Never invent another value.Never include the image itself.The runtime will automatically replace CURRENT_IMAGE with the uploaded image.
If you have enough information to answer the user's question, respond like this:
{{
"action": "final",
"answer": "<answer>"
}}
Conversation history:
{history}
Current user question:
{user_query}
Previous reasoning:
{scratchpad}
"""
def save_message(self, thread_id, role, content):
if thread_id not in self.memory:
self.memory[thread_id] = []
self.memory[thread_id].append(
{
"role": role,
"content": content
}
)
self.persist_memory()
def get_history(self, thread_id):
return self.memory.get(thread_id, [])
def final_summary(self,user_query,scratchpad,thread_id,history):
summary_prompt = f"""
History:
{history}
Question:
{user_query}
Collected facts:
{scratchpad}
Write a concise answer for the user.
"""
response = self.call_llm(summary_prompt)
if isinstance(response, str):
return response
answer = response.content
self.save_message(thread_id,"assistant",answer)
return answer
def run(self, user_query,thread_id):
scratchpad = ""
self.save_message(
thread_id,
"user",
user_query
)
history = self.get_history(thread_id)
for step in range(self.max_steps):
prompt = self._build_prompt(
user_query=user_query,
scratchpad=scratchpad,
history=history
)
response = self.call_llm(prompt)
if isinstance(response, str):
return response, scratchpad
text = response.content
try:
decision = json.loads(text)
except Exception:
return (f"Invalid JSON returned:\n{text}",scratchpad)
action = decision.get("action")
if action not in ("tool", "final"):
return (
"โš ๏ธ The planner returned an invalid action.",
scratchpad
)
if action == "final":
return self.final_summary(user_query,scratchpad,thread_id,history),scratchpad
if action == "tool":
tool_name = decision["tool_name"]
tool_input = decision["tool_input"]
result = self.execute_tool(
tool_name,
tool_input,
thread_id
)
if result == "No documents uploaded yet":
return (
"๐Ÿ“„ No document has been uploaded yet.\n\n"
"Please upload a PDF in the **Document QA** tab first.",
scratchpad
)
if result == "No relevant content found in the uploaded documents.":
return (
"I couldn't find information about that in the uploaded document.",
scratchpad
)
tool_failed = (isinstance(result, str) and
(result.startswith("โš ๏ธ")
or result.startswith("โŒ")
or result.startswith("๐ŸŒ")
or result.startswith("๐Ÿ”‘")
or result.startswith("๐Ÿšซ")
)
)
scratchpad += f"""
Step {step+1}
Tool Used:
{tool_name}
Tool Input:
{tool_input}
Tool Result:
{result}
"""
if tool_failed:
continue
return ("Maximum reasoning steps exceeded.",scratchpad)