#!/usr/bin/env python3 """ Main entry point for User Memory System with Separated Architecture Conversational agent handles dialogue, background processor handles memory """ import os import sys import json import logging import argparse import time from pathlib import Path from typing import Optional from conversational_agent import ConversationalAgent, ConversationConfig from background_memory_processor import BackgroundMemoryProcessor, MemoryProcessorConfig from config import Config, MemoryMode # Add evaluation framework support # We load it dynamically only when needed to avoid import conflicts EVALUATION_AVAILABLE = False UserMemoryEvaluationFramework = None TestCase = None # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) def print_section(title: str): """Print a formatted section header""" print("\n" + "="*80) print(f" {title}") print("="*80) def print_result(result: dict): """Print formatted result""" if result.get('success'): print("\nโœ… Task completed successfully!") if result.get('final_answer'): print("\n๐Ÿ“ Final Answer:") print("-"*40) print(result['final_answer']) else: print("\nโŒ Task failed!") if result.get('error'): print(f"Error: {result['error']}") print(f"\n๐Ÿ“Š Statistics:") print(f" - Iterations: {result.get('iterations', 0)}") print(f" - Tool calls: {len(result.get('tool_calls', []))}") if result.get('trajectory_file'): print(f"\n๐Ÿ’พ Trajectory saved to: {result['trajectory_file']}") # Show tool call summary if result.get('tool_calls'): print(f"\n๐Ÿ”ง Tool Call Summary:") tool_summary = {} for call in result['tool_calls']: tool_name = call.tool_name if tool_name not in tool_summary: tool_summary[tool_name] = { 'count': 0, 'success': 0, 'failed': 0 } tool_summary[tool_name]['count'] += 1 if call.error: tool_summary[tool_name]['failed'] += 1 else: tool_summary[tool_name]['success'] += 1 for tool_name, stats in tool_summary.items(): print(f" - {tool_name}: {stats['count']} calls " f"({stats['success']} success, {stats['failed']} failed)") # Show memory state if result.get('memory_state'): print(f"\n๐Ÿ’ญ Memory State:") print("-"*40) memory_preview = result['memory_state'][:500] if len(result['memory_state']) > 500: memory_preview += "..." print(memory_preview) def interactive_mode(user_id: str, memory_mode: MemoryMode = MemoryMode.NOTES, enable_background_processing: bool = True, conversation_interval: int = 1, provider: Optional[str] = None, model: Optional[str] = None): """Run the agent in interactive mode with separated architecture""" print_section(f"Interactive Mode - Conversational Agent (User: {user_id})") # Determine provider and get API key provider = (provider or Config.PROVIDER).lower() api_key = Config.get_api_key(provider) if not api_key: print(f"โŒ Error: Please set API key for provider '{provider}'") if provider in ["kimi", "moonshot"]: print(" export MOONSHOT_API_KEY='your-api-key-here'") elif provider in ["dashscope", "qwen", "bailian"]: print(" export DASHSCOPE_API_KEY='your-api-key-here'") elif provider == "siliconflow": print(" export SILICONFLOW_API_KEY='your-api-key-here'") elif provider == "doubao": print(" export DOUBAO_API_KEY='your-api-key-here'") elif provider == "openrouter": print(" export OPENROUTER_API_KEY='your-api-key-here'") return # Initialize conversational agent conv_config = ConversationConfig( enable_memory_context=True, enable_conversation_history=True ) agent = ConversationalAgent( user_id=user_id, api_key=api_key, provider=provider, model=model, config=conv_config, memory_mode=memory_mode, verbose=True ) # Initialize and start background memory processor if enabled memory_processor = None if enable_background_processing: proc_config = MemoryProcessorConfig( conversation_interval=conversation_interval, min_conversation_turns=1, context_window=10, enable_auto_processing=True, output_operations=True ) memory_processor = BackgroundMemoryProcessor( user_id=user_id, api_key=api_key, provider=provider, model=model, config=proc_config, memory_mode=memory_mode, verbose=True ) memory_processor.start_background_processing() print(f"\n๐Ÿง  Background memory processing enabled (every {conversation_interval} conversation{'s' if conversation_interval > 1 else ''})") print("\nโœ… Conversational agent initialized") print(f"๐Ÿ“ฆ Memory Mode: {memory_mode.value}") print(f"๐Ÿ†” Session: {agent.get_session_id()}") print(f"๐Ÿ”„ Background Processing: {'Enabled' if enable_background_processing else 'Disabled'}") if enable_background_processing: print(f"๐Ÿ“Š Processing Trigger: Every {conversation_interval} conversation{'s' if conversation_interval > 1 else ''}") print("\nAvailable commands:") print(" 'memory' - Show current memory state") print(" 'process' - Manually trigger memory processing") print(" 'save' - Save memory immediately") print(" 'reset' - Start new conversation session") print(" 'quit' - Exit immediately without saving") print(" 'exit' - Exit immediately without saving") print("\nOr enter any message to chat.") conversation_count = 0 while True: try: print("\n" + "-"*60) user_input = input("You > ").strip() if not user_input: continue if user_input.lower() in ['quit', 'exit']: # Immediate exit without saving if memory_processor: memory_processor.stop_background_processing() print("๐Ÿ‘‹ Goodbye! (Exited without saving)") break elif user_input.lower() == 'save': # Save memory immediately print("\n๐Ÿ’พ Saving memory...") if memory_processor: results = memory_processor.process_recent_conversations() print(f"โœ… Memory saved: {results}") else: print("โš ๏ธ Background processing is disabled. Memory is saved after each conversation.") continue elif user_input.lower() == 'memory': print("\n๐Ÿ’ญ Current Memory State:") print("-"*40) # Reload first: the background processor writes through its # own manager instance, so the agent's copy can be stale. agent.memory_manager.load_memory() print(agent.memory_manager.get_context_string()) elif user_input.lower() == 'process': if memory_processor: print("\n๐Ÿ”„ Manually triggering memory processing...") results = memory_processor.process_recent_conversations() # Display operations operations = results.get('operations', []) if operations: print(f"\n๐Ÿ“ Memory Operations ({len(operations)} total):") for i, op in enumerate(operations, 1): icon = {'add': 'โž•', 'update': '๐Ÿ“', 'delete': '๐Ÿ—‘๏ธ'}.get(op['action'], 'โ“') print(f"{i}. {icon} {op['action'].upper()}: {op.get('content', op.get('memory_id', 'N/A'))}") else: print("โ„น๏ธ No memory updates needed") summary = results.get('summary', {}) print(f"\nSummary: {summary.get('added', 0)} added, {summary.get('updated', 0)} updated, {summary.get('deleted', 0)} deleted") else: print("โŒ Background processing not enabled") elif user_input.lower() == 'reset': agent.reset_session() print("โœ… Started new conversation session") conversation_count = 0 else: # Have a conversation response = agent.chat(user_input) print(f"\n๐Ÿค– Assistant: {response}") conversation_count += 1 # Increment conversation counter in processor if memory_processor: memory_processor.increment_conversation_count() # Check if processing will trigger if memory_processor.should_process(): print(f"\n[Memory processing triggered after {conversation_interval} conversation{'s' if conversation_interval > 1 else ''}]") # Give a moment for background thread to process time.sleep(2) elif conversation_interval > 1: conversations_until_process = conversation_interval - (conversation_count % conversation_interval) if conversations_until_process < conversation_interval: print(f"\n[Memory processing in {conversations_until_process} more conversation{'s' if conversations_until_process > 1 else ''}]") except KeyboardInterrupt: print("\n\nโš ๏ธ Interrupted. Type 'save' to save memory, or 'quit'/'exit' to exit immediately without saving.") except Exception as e: print(f"\nโŒ Error: {str(e)}") logger.error(f"Error in interactive mode: {e}", exc_info=True) # Cleanup if memory_processor: memory_processor.stop_background_processing() def demo_memory_system(memory_mode: MemoryMode = None, provider: Optional[str] = None, model: Optional[str] = None): """Demonstrate the separated memory system architecture""" print_section("Demo: Separated Memory Architecture") # Determine provider and get API key provider = (provider or Config.PROVIDER).lower() api_key = Config.get_api_key(provider) if not api_key: print(f"โŒ Please set API key for provider '{provider}'") if provider in ["dashscope", "qwen", "bailian"]: print(" export DASHSCOPE_API_KEY='your-api-key-here'") return # Create test user user_id = "demo_user" # Use provided memory_mode or prompt for it if memory_mode is None: memory_mode = select_memory_mode_interactive() # Initialize conversational agent conv_config = ConversationConfig( enable_memory_context=True, enable_conversation_history=True ) agent = ConversationalAgent( user_id=user_id, api_key=api_key, provider=provider, model=model, config=conv_config, memory_mode=memory_mode, verbose=True ) # Initialize background processor proc_config = MemoryProcessorConfig( conversation_interval=2, # Process every 2 conversations for demo min_conversation_turns=1, output_operations=True ) processor = BackgroundMemoryProcessor( user_id=user_id, api_key=api_key, provider=provider, model=model, config=proc_config, memory_mode=memory_mode, verbose=True ) # Session 1: Have conversations print("\n๐Ÿ“ Session 1: Having conversations") print("-"*40) messages = [ "Hi! My name is Alice and I work as a product manager at TechCorp.", "I prefer Python for scripting and use VS Code as my IDE. I also like dark themes.", "I'm currently working on a new mobile app project for our company." ] for message in messages: print(f"\n๐Ÿ‘ค User: {message}") response = agent.chat(message) print(f"๐Ÿค– Assistant: {response[:200]}..." if len(response) > 200 else f"๐Ÿค– Assistant: {response}") time.sleep(1) # Brief pause between messages # Process memories print("\n\n๐Ÿ”„ Processing conversation for memory updates...") print("-"*40) # Increment conversation count to trigger processing for _ in range(len(messages)): processor.increment_conversation_count() # Process conversations results = processor.process_recent_conversations() # Display operations operations = results.get('operations', []) if operations: print(f"\n๐Ÿ“ Memory Operations ({len(operations)} total):") for i, op in enumerate(operations, 1): icon = {'add': 'โž•', 'update': '๐Ÿ“', 'delete': '๐Ÿ—‘๏ธ'}.get(op['action'], 'โ“') print(f"{i}. {icon} {op['action'].upper()}: {op.get('content', op.get('memory_id', 'N/A'))}") else: print("โ„น๏ธ No memory updates needed") summary = results.get('summary', {}) print(f"\nโœ… Summary: {summary.get('added', 0)} added, {summary.get('updated', 0)} updated, {summary.get('deleted', 0)} deleted") # Start new session to test memory persistence print("\n\n๐Ÿ“ Session 2: Testing memory persistence") print("-"*40) agent.reset_session() test_message = "What do you know about me and my work?" print(f"\n๐Ÿ‘ค User: {test_message}") response = agent.chat(test_message) print(f"๐Ÿค– Assistant: {response}") # Show final memory state print("\n\n๐Ÿ’ญ Final Memory State:") print("-"*40) print(agent.memory_manager.get_context_string()) def run_evaluation_mode(user_id: str, memory_mode: MemoryMode, verbose: bool = True, provider: Optional[str] = None, model: Optional[str] = None): """Run evaluation mode using the evaluation framework""" # Import the evaluation framework with proper module isolation from pathlib import Path eval_framework_path = Path(__file__).parent.parent / "user-memory-evaluation" try: # Save the current modules to avoid conflicts saved_modules = {} conflicting_modules = ['config', 'models', 'evaluator', 'framework'] # Temporarily remove conflicting modules from sys.modules for module_name in conflicting_modules: if module_name in sys.modules: saved_modules[module_name] = sys.modules[module_name] del sys.modules[module_name] # Temporarily add evaluation framework path with highest priority original_path = sys.path.copy() sys.path.insert(0, str(eval_framework_path)) # Import evaluation framework modules import config as eval_config import models as eval_models import evaluator as eval_evaluator import framework as eval_framework # Get the class we need framework_class = eval_framework.UserMemoryEvaluationFramework # Restore original path sys.path = original_path # Remove evaluation modules from sys.modules to avoid future conflicts for module_name in conflicting_modules: if module_name in sys.modules: del sys.modules[module_name] # Restore original modules for module_name, module in saved_modules.items(): sys.modules[module_name] = module except Exception as e: # Restore on error sys.path = original_path if 'original_path' in locals() else sys.path for module_name, module in saved_modules.items(): sys.modules[module_name] = module print(f"โŒ Error: Could not load evaluation framework: {e}") print("Please ensure user-memory-evaluation is properly installed.") import traceback traceback.print_exc() sys.exit(1) print_section("Evaluation Mode - Test Case Based Evaluation") # Initialize evaluation framework framework = framework_class() if not framework.test_suite: print("โŒ Error: No test cases loaded") sys.exit(1) print(f"\nโœ… Loaded {len(framework.test_suite.test_cases)} test cases") # Determine provider and get API key provider = (provider or Config.PROVIDER).lower() api_key = Config.get_api_key(provider) if not api_key: print(f"โŒ Error: Please set API key for provider '{provider}'") if provider in ["dashscope", "qwen", "bailian"]: print(" export DASHSCOPE_API_KEY='your-api-key-here'") sys.exit(1) # Initialize agents without incorrect parameters # ConversationConfig is a dataclass and doesn't take parameters in __init__ conv_config = ConversationConfig() conv_config.enable_memory_context = True conv_config.enable_conversation_history = True mem_config = MemoryProcessorConfig() mem_config.verbose = verbose # Initialize agents with correct parameters agent = ConversationalAgent( user_id=user_id, api_key=api_key, provider=provider, model=model, config=conv_config, memory_mode=memory_mode, verbose=verbose ) processor = BackgroundMemoryProcessor( user_id=user_id, api_key=api_key, provider=provider, model=model, config=mem_config, memory_mode=memory_mode, # Pass memory_mode here! verbose=verbose ) while True: print("\n" + "-"*60) print("Options:") print("1. Run a test case") print("2. View current memory state") print("3. Clear memory and start fresh") print("4. Exit evaluation mode") choice = input("\nEnter your choice (1-4): ").strip() if choice == "1": # First list test cases, then let user choose print("\n๐Ÿ“‹ Available Test Cases:") framework.display_test_case_summary(show_full_titles=True, by_category=True) # Now let user select a test case test_id = input("\nEnter test case ID to run (or 'cancel' to go back): ").strip() if test_id.lower() == 'cancel': continue test_case = framework.get_test_case(test_id) if not test_case: print(f"โŒ Test case '{test_id}' not found") continue print(f"\n{'='*60}") print(f"Running Test Case: {test_case.title}") print(f"Category: {test_case.category}") print("="*60) # CRITICAL: Clear ALL memory and conversation state before test print("\n๐Ÿงน Clearing all memory and conversation state before test...") # 1. Clear memory managers for both agent and processor if hasattr(agent.memory_manager, 'clear_all_memories'): agent.memory_manager.clear_all_memories() # Verify memory is cleared memory_check = agent.memory_manager.get_context_string() if "No previous memory" not in memory_check: print(f" โš ๏ธ Warning: Agent memory may not be fully cleared") else: print(f" โœ… Agent memory cleared successfully") if hasattr(processor.memory_manager, 'clear_all_memories'): processor.memory_manager.clear_all_memories() # Verify memory is cleared memory_check = processor.memory_manager.get_context_string() if "No previous memory" not in memory_check: print(f" โš ๏ธ Warning: Processor memory may not be fully cleared") else: print(f" โœ… Processor memory cleared successfully") # 2. Clear conversation history completely if agent.conversation_history: agent.conversation_history.conversations = [] agent.conversation_history.save_history() print(f" โœ… Cleared conversation history for user {user_id}") if processor.conversation_history: processor.conversation_history.conversations = [] processor.conversation_history.save_history() print(f" โœ… Cleared processor conversation history") # 3. Reset agent conversation state agent.conversation = [] agent._init_system_prompt() # 4. Reset any tool call counts if hasattr(agent, 'tool_call_counts'): agent.tool_call_counts = {} print(f" โœ… All memory and state cleared - ready for test case") # Process conversation histories print(f"\n๐Ÿ“š Processing {len(test_case.conversation_histories)} conversation histories...") # Build conversation contexts from test case histories conversation_contexts = [] for i, history in enumerate(test_case.conversation_histories, 1): print(f"\nConversation {i}/{len(test_case.conversation_histories)}: {history.conversation_id}") # Build conversation context for this history conversation = [] # Process each message in the conversation for msg in history.messages: if msg.role.value == "user": conversation.append({"role": "user", "content": msg.content}) elif msg.role.value == "assistant": conversation.append({"role": "assistant", "content": msg.content}) conversation_contexts.append(conversation) # Also add to the agent's conversation history for context # This is needed for the agent to have context when answering the question if agent.conversation_history and hasattr(agent.conversation_history, 'add_turn'): # Add pairs of user/assistant messages user_msg = None for msg in history.messages: if msg.role.value == "user": user_msg = msg.content elif msg.role.value == "assistant" and user_msg: agent.conversation_history.add_turn( session_id=f"eval_{history.conversation_id}", user_message=user_msg, assistant_message=msg.content ) user_msg = None # Process all conversations through the memory processor if conversation_contexts: print(f"\n๐Ÿ’พ Processing memory for all conversations...") try: results = processor.process_conversation_batch(conversation_contexts) # Summarize results total_added = sum(r.get('summary', {}).get('added', 0) for r in results) total_updated = sum(r.get('summary', {}).get('updated', 0) for r in results) total_deleted = sum(r.get('summary', {}).get('deleted', 0) for r in results) print(f" โœ… Memory processing complete:") print(f" - Added: {total_added} memories") print(f" - Updated: {total_updated} memories") print(f" - Deleted: {total_deleted} memories") except Exception as e: print(f" โš ๏ธ Memory processing error: {e}") # CRITICAL: Clear conversation history to simulate a new session # The evaluation should test whether STRUCTURED MEMORIES work, # not whether raw conversation history works. # The agent must rely only on processed memories to answer the question. if agent.conversation_history: # Save the current conversation history (for record keeping) saved_conversations = agent.conversation_history.conversations if hasattr(agent.conversation_history, 'conversations') else [] # Clear the conversations list to simulate a fresh session agent.conversation_history.conversations = [] print("\n๐Ÿ”„ Cleared conversation history - starting fresh session") print(" (Agent will use only structured memories)") # Reset the agent's conversation to start fresh agent.conversation = [] agent._init_system_prompt() # CRITICAL: Reload the agent's memory manager to get the memories saved by the processor # The processor and agent have separate memory manager instances, so we need to reload # from file to get the memories that were just saved agent.memory_manager.load_memory() # Display what memories are available memory_context = agent.memory_manager.get_context_string() if memory_context: print("\n๐Ÿ’พ Available memories:") print("-"*40) print(memory_context[:500] + "..." if len(memory_context) > 500 else memory_context) print("-"*40) else: print("\nโš ๏ธ No structured memories available") # Now answer the user question print(f"\n{'='*60}") print("USER QUESTION:") print("-"*60) print(test_case.user_question) print("="*60) # Get agent response (now only using structured memories) print("\n๐Ÿค” Generating response...") response = agent.chat(test_case.user_question) print("\n๐Ÿ“ Agent Response:") print("-"*60) print(response) print("-"*60) # Restore conversation history after evaluation if agent.conversation_history and 'saved_conversations' in locals(): agent.conversation_history.conversations = saved_conversations # Evaluate the response print("\nโš–๏ธ Evaluating response...") result = framework.submit_and_evaluate(test_id, response) if result: # Display evaluation result is_passed = result.passed if result.passed is not None else result.reward >= 0.6 status = "โœ… PASSED" if is_passed else "โŒ FAILED" print(f"\n{'='*60}") print("EVALUATION RESULT:") print("-"*60) print(f"Status: {status}") print(f"Reward Score: {result.reward:.3f}/1.000") if result.reasoning: print(f"\nReasoning:") print(result.reasoning) if result.suggestions: print(f"\nSuggestions:") print(result.suggestions) print("="*60) else: print("โŒ Evaluation failed") # Clear conversation history for next test agent.conversation_history = [] elif choice == "2": # View current memory print("\n๐Ÿ“„ Current Memory State:") print("-"*60) print(processor.memory_manager.get_context_string()) elif choice == "3": # Clear memory if input("\nโš ๏ธ Are you sure you want to clear all memory? (yes/no): ").lower() == "yes": # Clear memory using the new method if hasattr(agent.memory_manager, 'clear_all_memories'): agent.memory_manager.clear_all_memories() if hasattr(processor.memory_manager, 'clear_all_memories'): processor.memory_manager.clear_all_memories() # Clear conversation history if agent.conversation_history: agent.conversation_history.conversations = [] agent.conversation_history.save_history() # Reset agent conversation agent.conversation = [] agent._init_system_prompt() print("โœ… Memory and conversation history cleared") elif choice == "4": print("\nExiting evaluation mode...") break else: print(f"โŒ Invalid choice: {choice}") def select_mode_interactive() -> str: """ Interactively prompt the user to select an execution mode Returns: Selected mode string ('evaluation', 'interactive', or 'demo') """ print("\n" + "="*60) print(" ๐Ÿš€ SELECT EXECUTION MODE") print("="*60) print("\n1. Evaluation Mode") print(" - Run test cases from user-memory-evaluation framework") print(" - Test memory system with predefined scenarios") print(" - Get performance scores and feedback") print("\n2. Interactive Mode") print(" - Chat with the agent in real-time") print(" - Memory processes automatically in background") print(" - Commands: memory, process, save, reset, quit/exit") print("\n3. Demo Mode") print(" - Quick demonstration of memory system") print(" - Shows how conversations are processed into memories") print(" - Tests memory persistence across sessions") print("\n" + "-"*60) while True: try: choice = input("\nSelect mode (1-3): ").strip() if choice == '1': print("โœ… Selected: Evaluation Mode") return "evaluation" elif choice == '2': print("โœ… Selected: Interactive Mode") return "interactive" elif choice == '3': print("โœ… Selected: Demo Mode") return "demo" else: print("โŒ Invalid choice. Please enter 1, 2, or 3.") except KeyboardInterrupt: print("\n\nโš ๏ธ Operation cancelled by user") sys.exit(0) except Exception as e: print(f"โŒ Error: {e}") def select_memory_mode_interactive() -> MemoryMode: """ Interactively prompt the user to select a memory mode Returns: Selected MemoryMode """ print("\n" + "="*60) print(" ๐Ÿ“ SELECT MEMORY MODE") print("="*60) print("\n1. Simple Notes (Basic)") print(" - Store simple facts and preferences") print(" - Each memory is a single line or fact") print(" - Example: 'User email: john@example.com'") print("\n2. Enhanced Notes") print(" - Store comprehensive contextual information") print(" - Each memory can be a full paragraph with context") print(" - Example: 'User works at TechCorp as a senior engineer,") print(" specializing in ML for 3 years...'") print("\n3. JSON Cards (Basic)") print(" - Hierarchical structured memory") print(" - Format: category โ†’ subcategory โ†’ key โ†’ value") print(" - Example: personal.contact.email โ†’ 'john@example.com'") print("\n4. Advanced JSON Cards") print(" - Complete memory card objects with metadata") print(" - Each card includes backstory, person, relationship") print(" - Prevents confusion between different contexts") print(" - Example: Medical card for child vs elderly parent") print("\n" + "-"*60) while True: try: choice = input("\nSelect mode (1-4): ").strip() if choice == '1': print("โœ… Selected: Simple Notes Mode") return MemoryMode.NOTES elif choice == '2': print("โœ… Selected: Enhanced Notes Mode") return MemoryMode.ENHANCED_NOTES elif choice == '3': print("โœ… Selected: JSON Cards Mode") return MemoryMode.JSON_CARDS elif choice == '4': print("โœ… Selected: Advanced JSON Cards Mode") return MemoryMode.ADVANCED_JSON_CARDS else: print("โŒ Invalid choice. Please enter 1, 2, 3, or 4.") except KeyboardInterrupt: print("\n\nโš ๏ธ Operation cancelled by user") sys.exit(0) except Exception as e: print(f"โŒ Error: {e}") def main(): """Main function with command-line argument support""" parser = argparse.ArgumentParser( description="User Memory Agent with React Pattern - Following system-hint architecture" ) parser.add_argument( "--mode", choices=["interactive", "demo", "evaluation"], default=None, help="Execution mode (if not specified, prompts interactively)" ) parser.add_argument( "--user", type=str, default="default_user", help="User ID for memory system (default: default_user)" ) parser.add_argument( "--background-processing", type=bool, default=True, help="Enable background memory processing (default: True)" ) parser.add_argument( "--conversation-interval", type=int, default=1, help="Process memory after N conversations (default: 1 - every conversation)" ) parser.add_argument( "--memory-mode", choices=["notes", "enhanced_notes", "json_cards", "advanced_json_cards"], help="Memory mode (prompts interactively if not specified)" ) parser.add_argument( "--provider", choices=["dashscope", "qwen", "bailian", "siliconflow", "doubao", "kimi", "moonshot", "openrouter"], default=None, help="LLM provider (defaults to env PROVIDER or 'kimi')" ) parser.add_argument( "--model", type=str, default=None, help="Model name (defaults to provider's default model)" ) parser.add_argument( "--no-verbose", action="store_true", help="Disable verbose output (verbose is enabled by default)" ) args = parser.parse_args() # Set verbose based on no-verbose flag (default is verbose=True) verbose = not args.no_verbose # Determine provider provider = args.provider or Config.PROVIDER # Validate configuration if not Config.validate(provider): sys.exit(1) # Create necessary directories Config.create_directories() # Select execution mode if not specified execution_mode = args.mode if execution_mode is None: # Prompt user to select mode execution_mode = select_mode_interactive() # Configure memory mode if args.memory_mode: # Mode specified via command line mode_map = { "notes": MemoryMode.NOTES, "enhanced_notes": MemoryMode.ENHANCED_NOTES, "json_cards": MemoryMode.JSON_CARDS, "advanced_json_cards": MemoryMode.ADVANCED_JSON_CARDS } memory_mode = mode_map[args.memory_mode] else: # Interactive mode selection memory_mode = select_memory_mode_interactive() print("\n" + "๐Ÿง "*40) print(" USER MEMORY SYSTEM - SEPARATED ARCHITECTURE") print("๐Ÿง "*40) if execution_mode == "demo": demo_memory_system(memory_mode, provider, args.model) elif execution_mode == "evaluation": run_evaluation_mode(args.user, memory_mode, verbose, provider, args.model) elif execution_mode == "interactive": interactive_mode( user_id=args.user, memory_mode=memory_mode, enable_background_processing=args.background_processing, conversation_interval=args.conversation_interval, provider=provider, model=args.model ) else: # This should not happen, but handle it gracefully print(f"โŒ Unknown execution mode: {execution_mode}") sys.exit(1) print("\n๐Ÿ‘‹ Thank you for using User Memory Agent!") if __name__ == "__main__": main()