| import logging |
| import json |
| from langchain_core.messages import SystemMessage, AIMessage, ToolMessage, HumanMessage |
| from src.LLMResponse.models.orchastrator_state import State |
| from src.LLMResponse.llm.llm_loader import llm |
| from src.LLMResponse.prompts import CHAT_LLM_SYSTEM_PROMPT |
| from utils.asyncHandler import asyncHandler |
|
|
|
|
| def _get_current_turn_tool_msg(messages: list) -> ToolMessage | None: |
| last_human_idx = next( |
| (i for i in range(len(messages) - 1, -1, -1) if isinstance(messages[i], HumanMessage)), |
| None |
| ) |
| if last_human_idx is None: |
| return None |
| return next( |
| (m for m in messages[last_human_idx:] |
| if isinstance(m, ToolMessage) and m.name == "fetch_recommendations_from_db"), |
| None |
| ) |
|
|
|
|
| def _trim_tool_message(msg: ToolMessage) -> ToolMessage: |
| try: |
| parsed = json.loads(msg.content) |
| if isinstance(parsed, dict) and "results" in parsed: |
| clean_results = [{"page_content": r.get("page_content", "")} for r in parsed["results"]] |
| return ToolMessage( |
| content=json.dumps({"status": "success", "results": clean_results}), |
| name=msg.name, |
| tool_call_id=msg.tool_call_id |
| ) |
| except Exception: |
| pass |
| return msg |
|
|
|
|
| def _build_filtered_messages(messages: list) -> list: |
| filtered = [ |
| _trim_tool_message(msg) |
| if (isinstance(msg, ToolMessage) and msg.name == "fetch_recommendations_from_db") |
| else msg |
| for msg in messages |
| ] |
| if filtered and isinstance(filtered[-1], AIMessage) and not filtered[-1].tool_calls: |
| filtered.pop() |
| return filtered |
|
|
|
|
| @asyncHandler |
| async def chat_llm_node(state: State) -> dict: |
| logging.info("chat_llm_node — entered node") |
| logging.debug(f"chat_llm_node — total messages in history: {len(state['messages'])}") |
|
|
| messages = state["messages"] |
|
|
| current_tool_msg = _get_current_turn_tool_msg(messages) |
| db_results = [] |
| if current_tool_msg is not None: |
| try: |
| parsed = json.loads(current_tool_msg.content) |
| if isinstance(parsed, dict) and "results" in parsed: |
| db_results = parsed["results"] |
| logging.info(f"chat_llm_node — extracted {len(db_results)} db_results from current turn") |
| except Exception: |
| pass |
|
|
| filtered_messages = _build_filtered_messages(messages) |
| messages_for_llm = [SystemMessage(content=CHAT_LLM_SYSTEM_PROMPT)] + filtered_messages |
|
|
| logging.info("chat_llm_node — invoking LLM") |
| response: AIMessage = await llm.ainvoke(messages_for_llm) |
|
|
| logging.info("chat_llm_node — response generated") |
| logging.debug(f"chat_llm_node — response preview: {response.content[:200]}...") |
|
|
| return { |
| "messages": [response], |
| "final_response": response.content, |
| "db_results": db_results, |
| } |