Spaces:
Runtime error
Runtime error
| import os | |
| from langgraph.graph import StateGraph, END | |
| from langgraph.prebuilt import ToolNode | |
| from langchain_core.messages import AIMessage | |
| from typing import Literal | |
| from nodes.intent import CreditCardState,intent_classifier_node,general_info_handler_node,oos_handler_node | |
| from recommender.vectror_db import query_refiner_node | |
| from recommender.graph_retrieval import neo4j_error_handler_node, neo4j_retrieval_node | |
| from recommender.vectordb_retrieval import ranked_card_retrieval_node | |
| from nodes.agent import agent_node,TOOLS | |
| from nodes.format import format_output_node | |
| from data import debug_print | |
| from nodes.chat import chat_agent_node,chat_tool_node | |
| from nodes.compare import compare_node_fn | |
| # --- Graph Definition --- | |
| graph = StateGraph(CreditCardState) | |
| graph.add_node("intent_classifier", intent_classifier_node) | |
| graph.add_node("general_info_handler", general_info_handler_node) | |
| graph.add_node("oos_handler", oos_handler_node) | |
| graph.add_node("query_refiner", query_refiner_node) | |
| graph.add_node("neo4j_retriever", neo4j_retrieval_node) | |
| graph.add_node("neo4j_error_handler", neo4j_error_handler_node) | |
| graph.add_node("ranked_card_retrieval", ranked_card_retrieval_node) | |
| graph.add_node("agent", agent_node) # Using our new React agent implementation | |
| graph.add_node("tools", ToolNode(TOOLS)) | |
| graph.add_node("format_output", format_output_node) | |
| graph.set_entry_point("intent_classifier") | |
| def route_after_intent_classification(state: CreditCardState): | |
| intent = state["intent"] | |
| debug_print("ROUTE", f"Intent classification routing with intent: '{intent}'") | |
| if intent == "credit-card-recommendation": | |
| return "query_refiner" | |
| elif intent == "general-credit-related": | |
| return "general_info_handler" | |
| else: # This will catch 'out-of-scope' and any other unexpected values | |
| return "oos_handler" | |
| def route_after_format_output(state: CreditCardState): | |
| if state.get("trigger_compare", False): | |
| return "compare_node" | |
| elif state.get("trigger_chat", False): | |
| return "chat_node" | |
| else: | |
| return END | |
| graph.add_conditional_edges( | |
| "intent_classifier", | |
| route_after_intent_classification, | |
| { | |
| "query_refiner": "query_refiner", | |
| "general_info_handler": "general_info_handler", | |
| "oos_handler": "oos_handler", | |
| }, | |
| ) | |
| graph.add_edge("general_info_handler", END) | |
| graph.add_edge("oos_handler", END) | |
| graph.add_edge("query_refiner", "neo4j_retriever") | |
| def route_after_neo4j_retriever(state: CreditCardState): | |
| debug_print("ROUTE", f"neo4j_error: {state.get('neo4j_error')}") | |
| if state.get("neo4j_error", False): | |
| return "neo4j_error_handler" | |
| else: | |
| return "ranked_card_retrieval" | |
| graph.add_conditional_edges( | |
| "neo4j_retriever", | |
| route_after_neo4j_retriever, | |
| { | |
| "neo4j_error_handler": "neo4j_error_handler", | |
| "ranked_card_retrieval": "ranked_card_retrieval", | |
| }, | |
| ) | |
| graph.add_edge("neo4j_error_handler", END) | |
| graph.add_edge("ranked_card_retrieval", "agent") | |
| def route_agent_output(state: CreditCardState) -> Literal["format_output", "tools"]: | |
| """Determine the next node based on the model's output. | |
| This function checks if the model's last message contains tool calls. | |
| Args: | |
| state (CreditCardState): The current state of the conversation. | |
| Returns: | |
| str: The name of the next node to call ("format_output" or "tools"). | |
| """ | |
| last_message = state["messages"][-1] | |
| if not isinstance(last_message, AIMessage): | |
| raise ValueError( | |
| f"Expected AIMessage in output edges, but got {type(last_message).__name__}" | |
| ) | |
| # If there is no tool call, then we move to format_output | |
| if not last_message.tool_calls: | |
| return "format_output" | |
| # Otherwise we execute the requested actions | |
| return "tools" | |
| graph.add_conditional_edges( | |
| "agent", | |
| route_agent_output | |
| ) | |
| graph.add_edge("tools", "agent") | |
| graph.add_edge("format_output",END) | |
| app = graph.compile() | |
| # --- Pipeline Function --- | |
| def run_langgraph_pipeline( | |
| query: str, | |
| preferences: str, | |
| query_intent: bool, | |
| include_cobranded: bool, | |
| use_eligibility: bool = False, | |
| age=None, | |
| income=None, | |
| cibil=None, | |
| min_joining_fee=None, | |
| max_joining_fee=None, | |
| min_annual_fee=None, | |
| max_annual_fee=None | |
| ): | |
| debug_print("PIPELINE", f"Starting pipeline with query: '{query}'") | |
| debug_print("PIPELINE", f"Preferences: '{preferences}'") | |
| debug_print("PIPELINE", f"Query intent: {query_intent}, Include cobranded: {include_cobranded}") | |
| debug_print("PIPELINE", f"Eligibility: {use_eligibility}, Age: {age}, Income: {income}, CIBIL: {cibil}") | |
| debug_print("PIPELINE", f"Join fee: {min_joining_fee}-{max_joining_fee}, Annual fee: {min_annual_fee}-{max_annual_fee}") | |
| inputs = { | |
| "query": query, | |
| "preferences": preferences, | |
| "query_intent": query_intent, | |
| "include_cobranded": include_cobranded, | |
| "use_eligibility": use_eligibility, | |
| "age": age, | |
| "income": income, | |
| "cibil": cibil, | |
| "min_joining_fee": min_joining_fee, | |
| "max_joining_fee": max_joining_fee, | |
| "min_annual_fee": min_annual_fee, | |
| "max_annual_fee": max_annual_fee, | |
| "agent_outcome": None, | |
| "messages": [], | |
| "trigger_chat": False, | |
| "trigger_compare": False, | |
| "selected_cards": [], | |
| "user_message": "", | |
| } | |
| debug_print("PIPELINE", f"Invoking LangGraph app") | |
| result = app.invoke(inputs) | |
| debug_print("PIPELINE", f"LangGraph execution complete") | |
| # Ensure card_file is a valid file path or None | |
| card_file = result.get("card_file", None) | |
| if card_file and os.path.isfile(card_file): | |
| debug_print("PIPELINE", f"Valid card file found: {card_file}") | |
| else: | |
| debug_print("PIPELINE", f"Invalid or missing card file: {card_file}") | |
| card_file = None | |
| debug_print("PIPELINE", f"Pipeline complete, returning results") | |
| return ( | |
| result.get("top_card_html", ""), | |
| result.get("card_rows", []), | |
| card_file, # Use the validated file path | |
| result.get("card_names", []), | |
| result.get("card_lookup", {}), | |
| ) | |
| def passthrough_node(state: CreditCardState) -> CreditCardState: | |
| return state | |
| def utility_router(state: CreditCardState): | |
| if state.get("trigger_compare", False): | |
| return "compare_node" | |
| elif state.get("trigger_chat", False): | |
| return "chat_agent" # Start with the agent | |
| else: | |
| raise ValueError("No trigger flag set for utility graph.") | |
| utility_graph = StateGraph(CreditCardState) | |
| # Correct: Add a valid node that returns the full state | |
| utility_graph.add_node("router", passthrough_node) | |
| # Add your real action nodes | |
| utility_graph.add_node("compare_node", compare_node_fn) | |
| utility_graph.add_node("chat_agent", chat_agent_node) | |
| utility_graph.add_node("chat_tools", chat_tool_node) | |
| # Set router as entry point | |
| utility_graph.set_entry_point("router") | |
| # Add conditional edges using utility_router (not as a node) | |
| utility_graph.add_conditional_edges( | |
| "router", | |
| utility_router, | |
| { | |
| "compare_node": "compare_node", | |
| "chat_agent": "chat_agent", | |
| }, | |
| ) | |
| def route_chat_agent_output(state: CreditCardState) -> Literal["chat_tools", "__end__"]: | |
| last_message = state["messages"][-1] | |
| if not isinstance(last_message, AIMessage): | |
| # This can happen if the tool call fails with an error | |
| return "__end__" | |
| if not last_message.tool_calls: | |
| return "__end__" | |
| return "chat_tools" | |
| utility_graph.add_conditional_edges( | |
| "chat_agent", | |
| route_chat_agent_output, | |
| { | |
| "chat_tools": "chat_tools", | |
| "__end__": "__end__" | |
| } | |
| ) | |
| utility_graph.add_edge("chat_tools", "chat_agent") | |
| utility_graph.add_edge("compare_node", END) | |
| utility_app = utility_graph.compile() |