diff --git a/RAGManager/app/agents/graph.py b/RAGManager/app/agents/graph.py index 8916ea5..665feec 100644 --- a/RAGManager/app/agents/graph.py +++ b/RAGManager/app/agents/graph.py @@ -23,12 +23,12 @@ def create_agent_graph() -> StateGraph: Create and configure the LangGraph agent graph. The graph implements the following flow: - 1. START -> agent_host (Nodo 1) - Prepares state, no DB operations + 1. START -> agent_host (Nodo 1) - Prepares state and retrieves chat history 2. agent_host -> guard_inicial (Nodo 2) - Validates for malicious content 3. guard_inicial -> [conditional]: - - malicious -> fallback_inicial -> END (stops processing, no DB save) + - malicious -> fallback_inicial -> END (stops processing, chat history available) - continue -> parafraseo (Nodo 4) - 4. parafraseo -> Saves message to DB, retrieves chat history, paraphrases + 4. parafraseo -> Saves message to DB and paraphrases using chat history 5. parafraseo -> retriever (Nodo 5) - Retrieves relevant chunks from vector DB 6. retriever -> context_builder (Nodo 6) - Builds enriched query and generates response 7. context_builder -> guard_final (Nodo 8) - Validates response for risky content diff --git a/RAGManager/app/agents/nodes/agent_host.py b/RAGManager/app/agents/nodes/agent_host.py index b619573..e1e1aa1 100644 --- a/RAGManager/app/agents/nodes/agent_host.py +++ b/RAGManager/app/agents/nodes/agent_host.py @@ -1,8 +1,11 @@ """Nodo 1: Agent Host - Entry point that prepares initial state.""" import logging +from uuid import UUID from app.agents.state import AgentState +from app.core.database_connection import SessionLocal +from app.services.chat import get_chat_history logger = logging.getLogger(__name__) @@ -14,16 +17,17 @@ def agent_host(state: AgentState) -> AgentState: This node: 1. Receives the initial prompt and optional chat_session_id 2. Extracts the prompt from messages - 3. Prepares state for validation (no DB operations yet) + 3. Retrieves chat history if session_id is provided + 4. Prepares state with context for the entire flow - Note: Chat history retrieval and message saving is deferred to parafraseo - node to ensure malicious messages are not saved to the database. + Note: Chat history is retrieved here (not in parafraseo) so it's available + throughout the entire flow, regardless of which path is taken (normal or fallback). Args: state: Agent state containing the user prompt and optional chat_session_id Returns: - Updated state with prompt and initial_context set (no DB operations) + Updated state with prompt, chat_messages, and initial_context set """ updated_state = state.copy() @@ -32,18 +36,50 @@ def agent_host(state: AgentState) -> AgentState: last_message = messages[-1] if messages else None prompt = last_message.content if last_message else "" - # Validate user_id is provided - user_id = state.get("user_id") - if not user_id: - logger.error("user_id is required in state but was not provided") - updated_state["error_message"] = "user_id is required" - return updated_state - - # Set prompt and initial context (no DB operations) + # Set prompt and initial context updated_state["prompt"] = prompt updated_state["initial_context"] = prompt - updated_state["chat_messages"] = None # Will be set in parafraseo after validation - logger.debug("Agent host prepared state for validation (no DB operations)") + # Retrieve chat history if session exists + chat_session_id = state.get("chat_session_id") + + if chat_session_id: + try: + # Convert to UUID if string + session_uuid = UUID(chat_session_id) if isinstance(chat_session_id, str) else chat_session_id + + db = SessionLocal() + try: + # Get chat history from database + chat_messages = get_chat_history(db, session_uuid) + + # Convert SQLAlchemy models to dicts for state + updated_state["chat_messages"] = [ + { + "id": msg.id, + "sender": msg.sender, + "message": msg.message, + "created_at": msg.created_at.isoformat() if msg.created_at else None, + } + for msg in chat_messages + ] + + logger.info(f"Retrieved {len(chat_messages)} messages for session {session_uuid}") + finally: + db.close() + + except ValueError as e: + # Session doesn't exist - this is okay for new sessions + logger.info(f"Chat session not found (likely new session): {e}") + updated_state["chat_messages"] = [] + except Exception as e: + logger.error(f"Error retrieving chat history: {e}", exc_info=True) + updated_state["chat_messages"] = [] + else: + # No session ID provided - new conversation + logger.info("No chat_session_id provided - starting new conversation") + updated_state["chat_messages"] = [] + + logger.debug(f"Agent host prepared state with {len(updated_state.get('chat_messages', []))} chat messages") return updated_state diff --git a/RAGManager/app/agents/nodes/parafraseo.py b/RAGManager/app/agents/nodes/parafraseo.py index 675e19a..eed68a9 100644 --- a/RAGManager/app/agents/nodes/parafraseo.py +++ b/RAGManager/app/agents/nodes/parafraseo.py @@ -1,4 +1,4 @@ -"""Nodo 4: Parafraseo - Saves message, retrieves chat history, and paraphrases user input.""" +"""Nodo 4: Parafraseo - Saves message and paraphrases user input using chat history.""" import json import logging @@ -14,21 +14,22 @@ def parafraseo(state: AgentState) -> AgentState: """ - Parafraseo node - Saves message to DB, retrieves chat history, and paraphrases user input. + Parafraseo node - Saves message to DB and paraphrases user input using chat history. This node: 1. Receives a validated user message (after guard_inicial validation) - 2. Saves the user's message to the chat session in PostgreSQL (endpoint 1 - placeholder) - 3. Retrieves the last 10 messages of the conversation (endpoint 2 - placeholder) - 4. Uses the last message to understand user's intentions and the remaining 9 (older) messages as context + 2. Saves the user's message to the chat session in PostgreSQL (TODO - placeholder) + 3. Uses chat history (already retrieved by agent_host) as context + 4. Uses the last message to understand user's intentions and chat history as context 5. Sends to LLM with instructions to return 3 differently phrased statements that encapsulate the user's intentions according to the last message and chat history Args: - state: Agent state containing validated user message, chat_session_id, and user_id + state: Agent state containing validated user message, chat_messages (from agent_host), + chat_session_id, and user_id Returns: - Updated state with chat_messages, paraphrased_text, and paraphrased_statements set + Updated state with paraphrased_text and paraphrased_statements set """ updated_state = state.copy() @@ -43,31 +44,16 @@ def parafraseo(state: AgentState) -> AgentState: last_user_message = messages[-1] user_message_content = last_user_message.content if hasattr(last_user_message, 'content') else str(last_user_message) - # TODO: Endpoint 1 - Save message to PostgreSQL database according to chat session + # TODO: Save message to PostgreSQL database according to chat session # This should: - # 1. Call an endpoint (not yet developed) that: - # - Saves the current user message to the chat session in PostgreSQL - # - Uses chat_session_id and user_id from state - # - Returns success/failure status - # 2. Handle errors appropriately (session not found, permission denied, etc.) - logger.info("Endpoint 1 (save message to DB) not yet implemented - skipping") - - # TODO: Endpoint 2 - Retrieve last 10 messages of the conversation - # This should: - # 1. Call an endpoint (not yet developed) that: - # - Retrieves the last 10 messages for the chat session - # - Returns a list of message dictionaries with structure: [{"sender": "user|assistant|system", "message": "...", "created_at": "..."}, ...] - # - Messages should be ordered from oldest to newest (or newest to oldest, depending on API design) - # 2. Update state with chat_messages from the endpoint response + # 1. Call a service or endpoint to save the current user message to the chat session + # 2. Use chat_session_id from state # 3. Handle errors appropriately (session not found, permission denied, etc.) + logger.info("Message save to DB not yet implemented - skipping") - # Placeholder: For now, we'll simulate chat history with just the current message - # Once the endpoint is implemented, replace this with the actual endpoint call - chat_messages = [ - {"sender": "user", "message": user_message_content, "created_at": "2025-01-01T00:00:00"} - ] - updated_state["chat_messages"] = chat_messages - logger.warning("Endpoint 2 (retrieve chat history) not yet implemented - using current message only") + # Get chat history from state (already retrieved by agent_host) + chat_messages = state.get("chat_messages", []) + logger.info(f"Using {len(chat_messages)} chat messages from state (retrieved by agent_host)") # Process chat history: last message (intentions) + 9 older messages (context) # The last message is the most recent one (for understanding intentions)