dawid-lorek's picture
Update agent.py
e225216 verified
raw
history blame
5.61 kB
import os
import tempfile
import requests
import re
import pandas as pd
from langchain_openai import ChatOpenAI
from langchain.agents import initialize_agent, Tool
from langchain.agents.agent_types import AgentType
from langchain_community.tools import DuckDuckGoSearchRun
# Audio transcription tool (OpenAI Whisper)
def transcribe_audio_tool(file_url: str) -> str:
import openai
openai.api_key = os.getenv("OPENAI_API_KEY")
try:
r = requests.get(file_url, timeout=20)
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
f.write(r.content)
f.flush()
path = f.name
transcript = openai.Audio.transcribe("whisper-1", open(path, "rb"))
return transcript.get("text", "")
except Exception as e:
return ""
# Excel reading tool
def read_excel_tool(file_url: str) -> str:
try:
r = requests.get(file_url, timeout=20)
with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f:
f.write(r.content)
f.flush()
path = f.name
df = pd.read_excel(path)
if 'Type' in df.columns and 'Sales' in df.columns:
total = df[df['Type'].str.lower() == 'food']['Sales'].sum()
return str(round(total, 2))
# fallback: sum all numbers
total = df.select_dtypes(include='number').sum().sum()
return str(round(total, 2))
except Exception as e:
return ""
# Python code execution tool (CAUTION: sandbox this for production!)
def execute_python_tool(code_url: str) -> str:
try:
r = requests.get(code_url, timeout=20)
code = r.content.decode("utf-8")
import io, contextlib
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
exec(code, {})
output = buf.getvalue().strip().split('\n')[-1]
# Only final numeric output if possible
numbers = re.findall(r'[-+]?\d*\.\d+|\d+', output)
return numbers[-1] if numbers else output
except Exception as e:
return ""
# Number extraction tool (example of "reasoning" tool)
def extract_numbers(text: str) -> str:
nums = re.findall(r'\b\d+\b', text)
return ', '.join(nums) if nums else ""
def extract_names(text: str) -> str:
words = re.findall(r'\b[A-Z][a-z]{2,}\b', text)
return ', '.join(words) if words else ""
# Tools list
tools = [
Tool(
name="DuckDuckGo Search",
func=DuckDuckGoSearchRun().run,
description="Use to find factual information or recent events."
),
Tool(
name="Transcribe Audio",
func=transcribe_audio_tool,
description="Use to transcribe an audio file from a URL (mp3 or wav)."
),
Tool(
name="Read Excel File",
func=read_excel_tool,
description="Use to read an Excel spreadsheet file from a URL (xlsx) and sum food sales or extract tables."
),
Tool(
name="Execute Python",
func=execute_python_tool,
description="Use to execute a Python file from a URL and get the final output."
),
Tool(
name="Extract Numbers",
func=extract_numbers,
description="Use to extract all numbers from provided text."
),
Tool(
name="Extract Names",
func=extract_names,
description="Use to extract capitalized names from provided text."
)
]
PROMPT = (
"You are a general AI assistant. I will ask you a question. "
"Reason step by step, and use tools as needed. Only after you are sure, answer with the template: "
"FINAL ANSWER: [YOUR FINAL ANSWER]. YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. "
"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. "
"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. "
"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."
)
llm = ChatOpenAI(model="gpt-4o", temperature=0)
class BasicAgent:
def __init__(self):
self.agent = initialize_agent(
tools=tools,
llm=llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=False,
handle_parsing_errors=True
)
self.prompt = PROMPT
def fetch_file_url(self, task_id):
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
try:
url = f"{DEFAULT_API_URL}/files/{task_id}"
r = requests.head(url, timeout=5)
if r.status_code == 200:
return url
except:
pass
return None
def __call__(self, question: str, task_id: str = None) -> str:
file_url = self.fetch_file_url(task_id) if task_id else None
if file_url:
# If file is attached, add the info for the agent
question_aug = f"{question}\nThis task has assigned file at this URL: {file_url}"
else:
question_aug = question
# Add instruction prompt
full_prompt = self.prompt + "\n" + question_aug
result = self.agent.run(full_prompt)
# Extract only FINAL ANSWER
for line in result.splitlines():
if line.strip().lower().startswith("final answer:"):
return line.split(":", 1)[-1].strip(" .\"'")
return result