from typing import Annotated, TypedDict, Optional
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
from supermemory import Supermemory
from dotenv import load_dotenv
load_dotenv()
class SupportAgent:
def __init__(self):
self.llm = ChatOpenAI(model="gpt-4o", temperature=0.3)
self.memory = Supermemory()
self.app = self._build_graph()
def _build_graph(self):
class State(TypedDict):
messages: Annotated[list, add_messages]
user_id: str
context: str
category: Optional[str]
def retrieve_context(state: State):
"""Fetch user profile and relevant past tickets."""
user_id = state["user_id"]
query = state["messages"][-1].content
result = self.memory.profile(
container_tag=user_id,
q=query,
threshold=0.5
)
static = result.profile.static or []
dynamic = result.profile.dynamic or []
memories = result.search_results.results if result.search_results else []
context = f"""
## User Profile
{chr(10).join(f"- {fact}" for fact in static) if static else "New user, no history."}
## Current Context
{chr(10).join(f"- {ctx}" for ctx in dynamic) if dynamic else "No recent activity."}
## Related Past Tickets
{chr(10).join(f"- {m.memory}" for m in memories[:3]) if memories else "No similar issues found."}
"""
return {"context": context}
def categorize(state: State):
"""Determine ticket category for routing."""
query = state["messages"][-1].content.lower()
if any(word in query for word in ["billing", "payment", "charge", "invoice"]):
return {"category": "billing"}
elif any(word in query for word in ["bug", "error", "broken", "crash"]):