Spaces:
Sleeping
Sleeping
| 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 | |
| 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 | |
| # ----------------------------- | |
| 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() | |