main.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. #!/usr/bin/env python3
  2. """
  3. Trixy Voice Assistant - Main Entry Point
  4. This module serves as the entry point for the Trixy voice assistant application,
  5. supporting three deployment modes: server, client/satellite, and standalone.
  6. Usage:
  7. python3 main.py [server|client|standalone] [--debug] [--config custom_config.json]
  8. Modes:
  9. server - Central hub with event handler, plugins, satellite management
  10. client - Satellite mode with wakeword detection and audio streaming
  11. standalone - Full system that can optionally connect to server for sync
  12. Arguments:
  13. --debug - Enable development mode (no TUI, print statements enabled)
  14. --config - Specify custom configuration file path
  15. Author: Generated with Claude Code
  16. """
  17. import sys
  18. import os
  19. import argparse
  20. import signal
  21. import logging
  22. import traceback
  23. from pathlib import Path
  24. from typing import Optional, Dict, Any
  25. # Global application container reference for cleanup
  26. _application_container = None
  27. _debug_mode = False
  28. def pprint(message: str) -> None:
  29. """
  30. Adaptive print function that behaves differently in debug vs production mode.
  31. In debug mode: Prints directly to stdout
  32. In production mode: Uses logging system with Textual TUI
  33. Args:
  34. message: The message to print/log
  35. """
  36. if _debug_mode:
  37. print(f"[TRIXY] {message}")
  38. else:
  39. # In production mode, use logging system
  40. # The TUI will capture and display log messages appropriately
  41. logging.info(message)
  42. def create_directory_structure() -> None:
  43. """
  44. Create the required directory structure for Trixy if it doesn't exist.
  45. Creates all necessary directories as specified in CLAUDE.md:
  46. - trixy_core/ with all component subdirectories
  47. - plugins/
  48. - models/ with subdirectories
  49. - config/
  50. - assets/ with default profile
  51. - trainer/ with data subdirectories
  52. """
  53. directories = [
  54. # Core system modules
  55. "trixy_core",
  56. "trixy_core/events",
  57. "trixy_core/network",
  58. "trixy_core/network/cmd",
  59. "trixy_core/satellites",
  60. "trixy_core/plugins",
  61. "trixy_core/config",
  62. "trixy_core/assets",
  63. "trixy_core/scheduler",
  64. "trixy_core/conversation",
  65. "trixy_core/arbitration",
  66. # Plugin directory
  67. "plugins",
  68. # ML model storage
  69. "models",
  70. "models/wakeword",
  71. "models/voice_recognition",
  72. # Configuration files
  73. "config",
  74. # Profile-based assets
  75. "assets",
  76. "assets/default",
  77. # ML training system
  78. "trainer",
  79. "trainer/data",
  80. "trainer/data/wakeword",
  81. "trainer/data/wakeword/raw",
  82. "trainer/data/voice_recognition",
  83. "trainer/data/voice_recognition/raw",
  84. "trainer/wakeword",
  85. "trainer/voice_recognition",
  86. ]
  87. base_path = Path.cwd()
  88. created_dirs = []
  89. for directory in directories:
  90. dir_path = base_path / directory
  91. if not dir_path.exists():
  92. try:
  93. dir_path.mkdir(parents=True, exist_ok=True)
  94. created_dirs.append(str(dir_path))
  95. pprint(f"Created directory: {dir_path}")
  96. except OSError as e:
  97. pprint(f"Failed to create directory {dir_path}: {e}")
  98. sys.exit(1)
  99. if created_dirs:
  100. pprint(f"Successfully created {len(created_dirs)} directories")
  101. else:
  102. pprint("All required directories already exist")
  103. def setup_logging(debug: bool = False) -> None:
  104. """
  105. Initialize the logging system based on the mode.
  106. In debug mode: Basic console logging
  107. In production mode: Structured logging for TUI integration
  108. Args:
  109. debug: Whether debug mode is enabled
  110. """
  111. global _debug_mode
  112. _debug_mode = debug
  113. if debug:
  114. # Debug mode: Simple console logging
  115. logging.basicConfig(
  116. level=logging.DEBUG,
  117. format='[%(asctime)s] [%(levelname)s] %(name)s: %(message)s',
  118. datefmt='%H:%M:%S'
  119. )
  120. pprint("Debug mode enabled - console logging active")
  121. else:
  122. # Production mode: Structured logging for TUI
  123. logging.basicConfig(
  124. level=logging.INFO,
  125. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  126. handlers=[
  127. logging.StreamHandler(sys.stdout)
  128. ]
  129. )
  130. pprint("Production mode enabled - TUI logging active")
  131. def parse_arguments() -> argparse.Namespace:
  132. """
  133. Parse command line arguments.
  134. Returns:
  135. Parsed arguments namespace
  136. """
  137. parser = argparse.ArgumentParser(
  138. description="Trixy Voice Assistant - Professional voice assistant system",
  139. formatter_class=argparse.RawDescriptionHelpFormatter,
  140. epilog="""
  141. Examples:
  142. python3 main.py server --debug
  143. python3 main.py client --config /path/to/client_config.json
  144. python3 main.py standalone
  145. """
  146. )
  147. parser.add_argument(
  148. 'mode',
  149. choices=['server', 'client', 'standalone'],
  150. help='Deployment mode: server (central hub), client (satellite), or standalone (full system)'
  151. )
  152. parser.add_argument(
  153. '--debug',
  154. action='store_true',
  155. help='Enable debug mode (no TUI, print statements enabled)'
  156. )
  157. parser.add_argument(
  158. '--config',
  159. type=str,
  160. metavar='CONFIG_FILE',
  161. help='Path to custom configuration file (overrides default config)'
  162. )
  163. return parser.parse_args()
  164. def validate_environment() -> None:
  165. """
  166. Validate the runtime environment and dependencies.
  167. Raises:
  168. SystemExit: If critical dependencies are missing
  169. """
  170. python_version = sys.version_info
  171. if python_version < (3, 8):
  172. pprint(f"Error: Python 3.8+ required, found {python_version.major}.{python_version.minor}")
  173. sys.exit(1)
  174. pprint(f"Python {python_version.major}.{python_version.minor}.{python_version.micro} detected")
  175. # Check for write permissions in current directory
  176. try:
  177. test_file = Path.cwd() / ".trixy_write_test"
  178. test_file.touch()
  179. test_file.unlink()
  180. pprint("Write permissions verified")
  181. except (OSError, PermissionError) as e:
  182. pprint(f"Error: No write permissions in current directory: {e}")
  183. sys.exit(1)
  184. def initialize_application_container(mode: str, config_path: Optional[str] = None):
  185. """
  186. Initialize and return the application container based on the deployment mode.
  187. This is a placeholder for the actual application container implementation.
  188. The container will be implemented in trixy_core/ modules.
  189. Args:
  190. mode: Deployment mode (server, client, standalone)
  191. config_path: Optional path to custom configuration file
  192. Returns:
  193. Application container instance
  194. Raises:
  195. ImportError: If core modules are not yet implemented
  196. """
  197. try:
  198. # This import will be available once the core modules are implemented
  199. from trixy_core.application import ApplicationContainer
  200. pprint(f"Initializing {mode} mode application container")
  201. container = ApplicationContainer(mode=mode, config_path=config_path)
  202. pprint("Application container initialized successfully")
  203. return container
  204. except ImportError as e:
  205. pprint(f"Core modules not yet implemented: {e}")
  206. pprint("This is expected during initial setup phase")
  207. pprint("The application container will be available once core modules are created")
  208. return None
  209. def setup_signal_handlers() -> None:
  210. """
  211. Setup signal handlers for graceful shutdown.
  212. """
  213. def signal_handler(signum, frame):
  214. pprint(f"Received signal {signum}, initiating graceful shutdown...")
  215. shutdown_application()
  216. sys.exit(0)
  217. # Handle common termination signals
  218. signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
  219. signal.signal(signal.SIGTERM, signal_handler) # Termination signal
  220. # Handle SIGQUIT on Unix systems
  221. if hasattr(signal, 'SIGQUIT'):
  222. signal.signal(signal.SIGQUIT, signal_handler)
  223. pprint("Signal handlers configured for graceful shutdown")
  224. def shutdown_application() -> None:
  225. """
  226. Perform graceful shutdown of the application.
  227. This function will:
  228. 1. Stop all running services
  229. 2. Close network connections
  230. 3. Save state and configuration
  231. 4. Clean up resources
  232. """
  233. global _application_container
  234. pprint("Starting graceful shutdown sequence...")
  235. if _application_container:
  236. try:
  237. # Call the application container's shutdown method
  238. # This will be implemented when the container is created
  239. if hasattr(_application_container, 'shutdown'):
  240. _application_container.shutdown()
  241. pprint("Application container shutdown completed")
  242. else:
  243. pprint("Application container shutdown method not available")
  244. except Exception as e:
  245. pprint(f"Error during application shutdown: {e}")
  246. if _debug_mode:
  247. traceback.print_exc()
  248. pprint("Graceful shutdown completed")
  249. def main() -> None:
  250. """
  251. Main entry point for the Trixy voice assistant application.
  252. This function:
  253. 1. Parses command line arguments
  254. 2. Validates the environment
  255. 3. Creates directory structure
  256. 4. Initializes logging
  257. 5. Sets up signal handlers
  258. 6. Initializes the application container
  259. 7. Starts the application
  260. 8. Handles graceful shutdown
  261. """
  262. global _application_container
  263. try:
  264. # Parse command line arguments
  265. args = parse_arguments()
  266. # Setup logging early
  267. setup_logging(args.debug)
  268. # Print startup banner
  269. pprint("="*50)
  270. pprint("Trixy Voice Assistant")
  271. pprint("Professional Voice Assistant System")
  272. pprint("="*50)
  273. pprint(f"Mode: {args.mode.upper()}")
  274. pprint(f"Debug: {'ENABLED' if args.debug else 'DISABLED'}")
  275. if args.config:
  276. pprint(f"Config: {args.config}")
  277. pprint("="*50)
  278. # Validate environment
  279. pprint("Validating runtime environment...")
  280. validate_environment()
  281. # Create directory structure
  282. pprint("Creating directory structure...")
  283. create_directory_structure()
  284. # Setup signal handlers for graceful shutdown
  285. pprint("Setting up signal handlers...")
  286. setup_signal_handlers()
  287. # Initialize application container
  288. pprint("Initializing application container...")
  289. _application_container = initialize_application_container(
  290. mode=args.mode,
  291. config_path=args.config
  292. )
  293. if _application_container:
  294. # Start the application
  295. pprint(f"Starting Trixy {args.mode} mode...")
  296. # The actual application start will be implemented
  297. # when the application container is created
  298. if hasattr(_application_container, 'start'):
  299. _application_container.start()
  300. else:
  301. pprint("Application container start method not available")
  302. pprint("Core system implementation is required")
  303. pprint("This main.py serves as the foundation for future development")
  304. else:
  305. pprint("Application container not available")
  306. pprint("Core modules need to be implemented")
  307. pprint("This main.py is ready for integration with core system")
  308. except KeyboardInterrupt:
  309. pprint("Received keyboard interrupt")
  310. shutdown_application()
  311. sys.exit(0)
  312. except Exception as e:
  313. pprint(f"Fatal error during startup: {e}")
  314. if _debug_mode:
  315. traceback.print_exc()
  316. shutdown_application()
  317. sys.exit(1)
  318. if __name__ == "__main__":
  319. main()