MarginMind / app.py
Alexilori's picture
Update app.py
4df5a45 verified
Raw
History Blame Contribute Delete
5.19 kB
import os
from dotenv import load_dotenv
import gradio as gr
import smtplib
from email.message import EmailMessage
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, AIMessage
from langchain.agents import create_tool_calling_agent
from langchain.agents.agent import AgentExecutor
from langchain import hub
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# email function
def send_order_email(name, customer_email, phone, product, quantity, notes):
website_order_email = os.getenv("ORDER_RECEIVER_EMAIL")
sender_email = os.getenv("SENDER_EMAIL")
sender_password = os.getenv("SENDER_EMAIL_PASSWORD")
msg = EmailMessage()
msg["Subject"] = f"New Customer Order from {name}"
msg["From"] = sender_email
msg["To"] = website_order_email
msg.set_content(
f"""
New customer order received
Name: {name}
Customer Email: {customer_email}
Phone: {phone}
Product/Service: {product}
Quantity: {quantity}
Notes: {notes}
"""
)
with smtplib.SMTP("smtp.gmail.com", 587) as smtp:
smtp.starttls()
smtp.login(sender_email, sender_password)
smtp.send_message(msg)
return "Order email sent successfully."
# tools for sending email
@tool
def send_order(
name: str, customer_email: str, phone: str, product: str, quantity: str, notes: str
) -> str:
"""Send a customer order email to the business."""
return send_order_email(name, customer_email, phone, product, quantity, notes)
# -----------------------------
# 1) Load environment variables
# -----------------------------
load_dotenv()
# -----------------------------
# 2) Load local Chroma DB created by ingest_in_db.py
# -----------------------------
PERSIST_DIRECTORY = "chroma_db"
COLLECTION_NAME = "documents"
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
if not OPENAI_API_KEY:
raise RuntimeError("Missing OPENAI_API_KEY in .env (project root).")
import subprocess
import sys
if not os.path.isdir(PERSIST_DIRECTORY):
print("Chroma DB not found. Building from PDFs...")
subprocess.run([sys.executable, "ingest_in_db.py"])
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
vector_store = Chroma(
persist_directory=PERSIST_DIRECTORY,
embedding_function=embeddings,
collection_name=COLLECTION_NAME,
)
# -----------------------------
# 3) LLM + agent prompt
# -----------------------------
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-5.4", temperature=0)
prompt = hub.pull("hwchase17/openai-functions-agent")
# -----------------------------
# 4) Retriever tool
# -----------------------------
@tool
def retrieve(query: str) -> str:
"""Retrieve relevant chunks from the vector store."""
retrieved_docs = vector_store.similarity_search(query, k=2)
return "\n\n".join(
f"Source: {doc.metadata}\nContent: {doc.page_content}" for doc in retrieved_docs
)
tools = [retrieve, send_order]
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# -----------------------------
# 5) Gradio handler
# -----------------------------
def chat(user_message, history):
chat_history = []
if history:
for item in history:
if isinstance(item, (list, tuple)) and len(item) == 2:
u, a = item
if u:
chat_history.append(HumanMessage(content=str(u)))
if a:
chat_history.append(AIMessage(content=str(a)))
elif isinstance(item, dict):
role = item.get("role")
content = item.get("content")
if role == "user" and content:
chat_history.append(HumanMessage(content=str(content)))
elif role in ["assistant", "ai"] and content:
chat_history.append(AIMessage(content=str(content)))
result = agent_executor.invoke(
{"input": user_message, "chat_history": chat_history}
)
output = result.get("output", "")
# โ”€โ”€ FIX: gpt-5.4 sometimes returns output as a list of content blocks
# e.g. [{"type": "text", "text": "..."}] instead of a plain string.
# The old code called str() on the whole dict, leaking raw JSON into the UI.
if isinstance(output, list):
parts = []
for item in output:
if isinstance(item, str):
parts.append(item)
elif isinstance(item, dict):
parts.append(item.get("text") or item.get("content") or "")
output = "\n".join(p for p in parts if p)
elif isinstance(output, dict):
output = output.get("text") or output.get("content") or ""
output = str(output).strip()
return output or "I wasn't able to generate a response. Please try again."
# -----------------------------
# 6) Launch UI
# -----------------------------
demo = gr.ChatInterface(
fn=chat,
title="๐Ÿ“„ MarginMind (Gradio)",
description="Put PDFs in /documents, run python ingest_in_db.py, then chat here.",
)
if __name__ == "__main__":
demo.launch()