Coverage for src/semware/cli.py: 0%

75 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-09 02:15 -0700

1#!/usr/bin/env python3 

2"""Command-line interface for SemWare.""" 

3 

4import argparse 

5import sys 

6import uvicorn 

7from pathlib import Path 

8 

9from .config import get_settings 

10from .utils.logging import setup_logging 

11from loguru import logger 

12 

13 

14def create_parser() -> argparse.ArgumentParser: 

15 """Create command-line argument parser.""" 

16 parser = argparse.ArgumentParser( 

17 prog="semware", 

18 description="SemWare - Semantic Search API Server", 

19 formatter_class=argparse.RawDescriptionHelpFormatter, 

20 epilog=""" 

21Examples: 

22 semware Start server with default settings 

23 semware --host 127.0.0.1 Start on localhost only 

24 semware --port 8080 Start on port 8080 

25 semware --workers 4 Start with 4 worker processes 

26 semware --debug Start in debug mode 

27 semware --reload Start with auto-reload (development) 

28  

29Environment variables: 

30 API_KEY API key for authentication (required) 

31 DEBUG Enable debug mode (true/false) 

32 DB_PATH Database storage path 

33 HOST Server host address 

34 PORT Server port 

35 LOG_LEVEL Logging level (DEBUG/INFO/WARNING/ERROR) 

36  

37For more information, visit: https://github.com/your-org/semware 

38 """, 

39 ) 

40 

41 parser.add_argument( 

42 "--host", 

43 type=str, 

44 default=None, 

45 help="Host to bind the server to (default: from config/env)", 

46 ) 

47 

48 parser.add_argument( 

49 "--port", 

50 type=int, 

51 default=None, 

52 help="Port to bind the server to (default: from config/env)", 

53 ) 

54 

55 parser.add_argument( 

56 "--workers", 

57 type=int, 

58 default=None, 

59 help="Number of worker processes (default: from config/env)", 

60 ) 

61 

62 parser.add_argument( 

63 "--debug", 

64 action="store_true", 

65 help="Enable debug mode with API documentation", 

66 ) 

67 

68 parser.add_argument( 

69 "--reload", 

70 action="store_true", 

71 help="Enable auto-reload for development (implies --workers 1)", 

72 ) 

73 

74 parser.add_argument( 

75 "--log-level", 

76 choices=["DEBUG", "INFO", "WARNING", "ERROR"], 

77 default=None, 

78 help="Set logging level (default: from config/env)", 

79 ) 

80 

81 parser.add_argument( 

82 "--version", 

83 action="version", 

84 version=f"SemWare 0.1.0", 

85 ) 

86 

87 return parser 

88 

89 

90def validate_environment() -> bool: 

91 """Validate that required environment is set up.""" 

92 try: 

93 settings = get_settings() 

94 

95 # Check if API key is set (not the default) 

96 if not settings.api_key or settings.api_key == "your-secret-api-key": 

97 logger.error("❌ API_KEY not configured!") 

98 logger.info("💡 Set your API key in .env file or environment variable:") 

99 logger.info(" export API_KEY=your-secret-api-key") 

100 logger.info(" Or create .env file from .env.example") 

101 return False 

102 

103 # Check if database directory exists and is writable 

104 db_path = Path(settings.db_path) 

105 try: 

106 db_path.mkdir(parents=True, exist_ok=True) 

107 except PermissionError: 

108 logger.error(f"❌ Cannot create database directory: {db_path}") 

109 logger.info("💡 Check directory permissions or set DB_PATH to writable location") 

110 return False 

111 

112 logger.info(f"✅ Database directory: {db_path.absolute()}") 

113 return True 

114 

115 except Exception as e: 

116 logger.error(f"❌ Configuration error: {e}") 

117 return False 

118 

119 

120def main() -> None: 

121 """Main CLI entry point.""" 

122 parser = create_parser() 

123 args = parser.parse_args() 

124 

125 # Load settings first 

126 try: 

127 settings = get_settings() 

128 except Exception as e: 

129 print(f"❌ Failed to load configuration: {e}", file=sys.stderr) 

130 sys.exit(1) 

131 

132 # Override settings with CLI arguments 

133 host = args.host or settings.host 

134 port = args.port or settings.port 

135 workers = args.workers or settings.workers 

136 debug = args.debug or settings.debug 

137 log_level = args.log_level or settings.log_level 

138 

139 # Setup logging 

140 setup_logging(level=log_level) 

141 

142 # Show startup banner 

143 print("🚀 SemWare - Semantic Search API Server") 

144 print("=" * 50) 

145 

146 # Validate environment 

147 if not validate_environment(): 

148 sys.exit(1) 

149 

150 # Show configuration 

151 logger.info(f"📡 Starting server on http://{host}:{port}") 

152 logger.info(f"🔧 Debug mode: {'ON' if debug else 'OFF'}") 

153 logger.info(f"👥 Workers: {workers}") 

154 logger.info(f"📝 Log level: {log_level}") 

155 

156 if debug: 

157 logger.info("📚 API Documentation:") 

158 logger.info(f" • Swagger UI: http://{host}:{port}/docs") 

159 logger.info(f" • ReDoc: http://{host}:{port}/redoc") 

160 

161 # Handle reload mode 

162 if args.reload: 

163 if workers > 1: 

164 logger.warning("⚠️ Auto-reload mode forces workers=1") 

165 workers = 1 

166 

167 try: 

168 # Start the server 

169 uvicorn.run( 

170 "semware.main:app", 

171 host=host, 

172 port=port, 

173 workers=workers, 

174 reload=args.reload, 

175 log_level=log_level.lower(), 

176 access_log=debug, 

177 loop="auto", 

178 ) 

179 except KeyboardInterrupt: 

180 logger.info("👋 Server stopped by user") 

181 except Exception as e: 

182 logger.error(f"❌ Failed to start server: {e}") 

183 sys.exit(1) 

184 

185 

186if __name__ == "__main__": 

187 main()