Spaces:
Sleeping
Sleeping
| import logging | |
| import sys | |
| import json | |
| import re | |
| from src.Blog.models.RouterDecision_model import RouterDecision | |
| from langchain_core.messages import SystemMessage, HumanMessage | |
| from src.Blog.models.State_model import State | |
| from src.Blog.llm import llm | |
| from src.Blog.prompts import ROUTER_SYSTEM | |
| from exception import MyException | |
| from utils.asyncHandler import asyncHandler | |
| async def router_node(state: State): | |
| logging.info("Entering router_node") | |
| topic = state['topic'] | |
| logging.debug(f"Topic: {topic}") | |
| try: | |
| try: | |
| runnable = llm.with_structured_output(RouterDecision) | |
| decision = await runnable.ainvoke( | |
| [ | |
| SystemMessage(content=ROUTER_SYSTEM), | |
| HumanMessage(content=f"Topic: {topic}") | |
| ] | |
| ) | |
| if decision: | |
| logging.info(f"Router decision (structured): needs_research={decision.needs_research}, mode={decision.mode}") | |
| return { | |
| "needs_research": decision.needs_research, | |
| "mode": decision.mode, | |
| "queries": decision.queries, | |
| } | |
| except Exception as e: | |
| logging.warning(f"Structured output failed: {str(e)}. Attempting manual parse.") | |
| raw_response = await llm.ainvoke( | |
| [ | |
| SystemMessage(content=ROUTER_SYSTEM + "\n\nCRITICAL: You MUST return a valid JSON object. Do not include any text before or after the JSON."), | |
| HumanMessage(content=f"Topic: {topic}") | |
| ] | |
| ) | |
| content = raw_response.content | |
| logging.debug(f"Raw LLM content for fallback: {content}") | |
| json_str = "" | |
| markdown_match = re.search(r'```json\s*(.*?)\s*```', content, re.DOTALL) | |
| if markdown_match: | |
| json_str = markdown_match.group(1) | |
| else: | |
| start = content.find('{') | |
| end = content.rfind('}') | |
| if start != -1 and end != -1: | |
| json_str = content[start:end+1] | |
| if json_str: | |
| try: | |
| data = json.loads(json_str) | |
| except json.JSONDecodeError: | |
| # Progressive truncation fallback | |
| success = False | |
| temp_str = json_str | |
| while '}' in temp_str: | |
| try: | |
| data = json.loads(temp_str) | |
| success = True | |
| break | |
| except json.JSONDecodeError: | |
| last_brace = temp_str.rfind('}') | |
| if last_brace == -1: break | |
| temp_str = temp_str[:last_brace] | |
| if not success: | |
| raise ValueError("Failed to parse JSON even after structural truncation") | |
| needs_res = str(data.get("needs_research", "")).lower() in ["true", "1", "yes"] | |
| decision = RouterDecision( | |
| needs_research=needs_res, | |
| mode=data.get("mode", "open_book"), | |
| queries=data.get("queries", []) | |
| ) | |
| logging.info(f"Router decision (manual): needs_research={decision.needs_research}, mode={decision.mode}") | |
| return { | |
| "needs_research": decision.needs_research, | |
| "mode": decision.mode, | |
| "queries": decision.queries, | |
| } | |
| logging.error("Failed to extract JSON from LLM response") | |
| raise ValueError("LLM failed to return a valid RouterDecision. Please check prompts or model output.") | |
| except Exception as e: | |
| logging.error(f"Error in router_node: {str(e)}") | |
| raise | |
| def route_next(state: State) -> str: | |
| needs_research = state.get("needs_research", False) | |
| logging.info(f"Routing next based on research need: {needs_research}") | |
| return "research" if needs_research else "orchestrator" |