import time from typing import List, Dict, Any, Optional from pydantic import BaseModel, Field class MessageRecord(BaseModel): role: str # 'user' | 'assistant' content: str timestamp: float = Field(default_factory=time.time) retrieved_context: Optional[List[Dict[str, Any]]] = None agent_decisions: Optional[Dict[str, Any]] = None class ConversationSession(BaseModel): session_id: str repo_id: str history: List[MessageRecord] = Field(default_factory=list) summary: str = "" class ConversationManager: """ Manages multi-session conversation tracking, caching history in-memory. Persists context, questions, answers, and internal timeline decisions. """ def __init__(self): self._sessions: Dict[str, ConversationSession] = {} def get_or_create_session(self, session_id: str, repo_id: str) -> ConversationSession: if session_id not in self._sessions: self._sessions[session_id] = ConversationSession(session_id=session_id, repo_id=repo_id) return self._sessions[session_id] def add_message( self, session_id: str, repo_id: str, role: str, content: str, retrieved_context: Optional[List[Dict[str, Any]]] = None, agent_decisions: Optional[Dict[str, Any]] = None ) -> MessageRecord: session = self.get_or_create_session(session_id, repo_id) record = MessageRecord( role=role, content=content, timestamp=time.time(), retrieved_context=retrieved_context, agent_decisions=agent_decisions ) session.history.append(record) # Keep summary updated with the last user prompt summary or simple description if role == "user" and not session.summary: # First query acts as session title/summary session.summary = content[:40] + ("..." if len(content) > 40 else "") return record def update_summary(self, session_id: str, summary: str): if session_id in self._sessions: self._sessions[session_id].summary = summary def get_session(self, session_id: str) -> Optional[ConversationSession]: return self._sessions.get(session_id) def list_sessions_for_repo(self, repo_id: str) -> List[Dict[str, Any]]: return [ { "session_id": s.session_id, "repo_id": s.repo_id, "summary": s.summary, "message_count": len(s.history), "last_updated": s.history[-1].timestamp if s.history else time.time() } for s in self._sessions.values() if s.repo_id == repo_id ] def clear_sessions(self): self._sessions.clear() # Global conversation manager singleton conversation_manager = ConversationManager()