krinya's picture
feat: Add configurable recursion limit for LangGraph agent
67e3a24
Raw
History Blame Contribute Delete
7 kB
"""
Main entry point for the Sales Assistant application.
This file serves as the primary interface to run the sales assistant agent.
It uses the agent runner from agent_main/agent_runner.py to create and manage
the conversational agent with database exploration capabilities.
"""
import os
import sys
from typing import Optional
from dotenv import load_dotenv
# Add the src directory to the path so we can import from sales_assistant
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from sales_assistant.agent_main.agent_runner import (
ConversationConfig,
run_interactive_loop,
create_agent_runner,
run_conversation_turn
)
from sales_assistant.agent_main.tools_node import get_all_tools
# Load environment variables
load_dotenv()
def check_environment_setup() -> bool:
"""
Check if the environment is properly set up for the agent.
Returns:
True if environment is properly configured, False otherwise
"""
required_vars = [
"OPENAI_API_KEY",
"MODEL_PROVIDER",
"MODEL_NAME",
"MYSQL_HOST",
"MYSQL_USER",
"MYSQL_PASSWORD",
"MYSQL_DB"
]
missing_vars = [var for var in required_vars if not os.getenv(var)]
if missing_vars:
print("❌ Missing required environment variables:")
for var in missing_vars:
print(f" - {var}")
print("\nπŸ’‘ Please set these variables in your .env file")
return False
print("βœ… Environment configuration looks good!")
return True
def display_available_tools() -> None:
"""Display available tools information."""
try:
tools = get_all_tools()
print("\nπŸ› οΈ Available Tools:")
print("-" * 30)
for i, tool in enumerate(tools, 1):
tool_name = getattr(tool, 'name', 'Unknown Tool')
tool_description = getattr(tool, 'description', 'No description available')
print(f"{i}. {tool_name}")
print(f" πŸ“ {tool_description}")
print(f"\nπŸ“Š Total tools available: {len(tools)}")
print("-" * 30)
except Exception as e:
print(f"⚠️ Could not load tools information: {e}")
def create_custom_config(
session_id: Optional[str] = None,
user_id: Optional[str] = None,
enable_langsmith: bool = True,
recursion_limit: int = int(os.getenv("LANGRAPH_RECURSION_LIMIT", "100"))
) -> ConversationConfig:
"""
Create a custom configuration for the agent.
Args:
session_id: Optional custom session ID
user_id: Optional user identifier
enable_langsmith: Whether to enable LangSmith tracing
recursion_limit: Maximum recursion limit for LangGraph
Returns:
ConversationConfig object
"""
config = ConversationConfig(
enable_langsmith=enable_langsmith,
langsmith_project=os.getenv("LANGSMITH_PROJECT", "sales-assistant-prod"),
recursion_limit=recursion_limit
)
if session_id:
config.session_id = session_id
if user_id:
config.user_id = user_id
return config
def run_sales_assistant(
interactive: bool = True,
custom_config: Optional[ConversationConfig] = None
) -> None:
"""
Main function to run the sales assistant.
Args:
interactive: Whether to run in interactive mode
custom_config: Optional custom configuration
"""
print("πŸš€ Starting Sales Assistant...")
# Check environment setup
if not check_environment_setup():
return
# Display available tools
display_available_tools()
try:
if interactive:
print("\nπŸ’¬ Starting interactive mode...")
config = custom_config or ConversationConfig()
run_interactive_loop(config)
else:
print("\nβš™οΈ Agent initialized and ready for programmatic use")
config = custom_config or ConversationConfig()
compiled_graph, checkpointer, callback_manager, thread_id = create_agent_runner(config)
print(f"πŸ“Š Session ID: {config.session_id}")
print(f"🧡 Thread ID: {thread_id}")
print("πŸ’‘ Use the returned objects to run conversations programmatically")
return compiled_graph, checkpointer, callback_manager, thread_id
except KeyboardInterrupt:
print("\nπŸ‘‹ Sales Assistant stopped by user")
except Exception as e:
print(f"❌ Error running Sales Assistant: {str(e)}")
sys.exit(1)
def single_query(query: str, config: Optional[ConversationConfig] = None) -> str:
"""
Run a single query against the agent without interactive mode.
Args:
query: The question to ask the agent
config: Optional configuration
Returns:
Agent's response
"""
if not check_environment_setup():
return "Environment not properly configured"
try:
config = config or ConversationConfig()
compiled_graph, checkpointer, callback_manager, thread_id = create_agent_runner(config)
response = run_conversation_turn(
compiled_graph,
thread_id,
query,
callback_manager
)
return response
except Exception as e:
return f"Error processing query: {str(e)}"
def main():
"""
Main entry point when running the script directly.
"""
import argparse
parser = argparse.ArgumentParser(
description="Sales Assistant - Database-powered product inquiry agent"
)
parser.add_argument(
"--mode",
choices=["interactive", "single"],
default="interactive",
help="Mode to run the assistant in"
)
parser.add_argument(
"--query",
type=str,
help="Single query to run (only for single mode)"
)
parser.add_argument(
"--no-langsmith",
action="store_true",
help="Disable LangSmith tracing"
)
parser.add_argument(
"--session-id",
type=str,
help="Custom session ID"
)
parser.add_argument(
"--user-id",
type=str,
help="User identifier"
)
args = parser.parse_args()
# Create configuration
config = create_custom_config(
session_id=args.session_id,
user_id=args.user_id,
enable_langsmith=not args.no_langsmith
)
if args.mode == "single":
if not args.query:
print("❌ --query is required for single mode")
sys.exit(1)
print(f"πŸ” Processing query: {args.query}")
response = single_query(args.query, config)
print(f"πŸ€– Response: {response}")
else: # interactive mode
run_sales_assistant(interactive=True, custom_config=config)
if __name__ == "__main__":
main()