4ea36783d6
Extracted main.py (556 lines) into focused modules: - cli/parser.py: Argument parsing (151 lines) - cli/main_runner.py: Main application logic (320 lines) - cli/test_runner.py: Test mode runner (81 lines) - cli/tool_server.py: Tool server runner (69 lines) - utils/network.py: Network utilities (IP detection) main.py is now 99 lines (down from 556). All 35 tests pass. Note: main_runner.py at 320 lines is slightly over 300 limit, will address in subsequent refactoring.
100 lines
2.6 KiB
Python
100 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Local Swarm - Automatically configure and run a swarm of small coding LLMs
|
|
|
|
NOTE: On macOS with Apple Silicon, we use multiprocessing with spawn method
|
|
to safely handle multiple MLX models. This prevents GPU conflicts.
|
|
"""
|
|
|
|
import sys
|
|
import multiprocessing as mp
|
|
|
|
# CRITICAL: Set spawn method BEFORE any other imports on macOS
|
|
if sys.platform == "darwin":
|
|
try:
|
|
mp.set_start_method("spawn", force=True)
|
|
except RuntimeError:
|
|
pass
|
|
|
|
import asyncio
|
|
from pathlib import Path
|
|
|
|
# Add src to path
|
|
src_path = Path(__file__).parent.resolve() / "src"
|
|
sys.path.insert(0, str(src_path))
|
|
if str(Path(__file__).parent.resolve()) not in sys.path:
|
|
sys.path.insert(0, str(Path(__file__).parent.resolve()))
|
|
|
|
from cli.parser import parse_args
|
|
from cli.tool_server import run_tool_server
|
|
from utils.network import get_local_ip
|
|
from utils.logging_config import setup_logging
|
|
from hardware.detector import detect_hardware
|
|
from interactive import print_hardware_info
|
|
|
|
# Set up logging
|
|
setup_logging()
|
|
|
|
|
|
def handle_detect_mode(hardware) -> int:
|
|
"""Handle --detect mode."""
|
|
print_hardware_info(hardware)
|
|
print("\n✅ Detection complete")
|
|
return 0
|
|
|
|
|
|
def handle_tool_server_mode(args, hardware) -> int:
|
|
"""Handle --tool-server mode."""
|
|
print("\n🔧 Starting Tool Execution Server...")
|
|
host = args.host if args.host else get_local_ip()
|
|
|
|
try:
|
|
asyncio.run(run_tool_server(host, args.tool_port))
|
|
return 0
|
|
except KeyboardInterrupt:
|
|
print("\n\nTool server stopped")
|
|
return 0
|
|
|
|
|
|
async def run_main_mode(args, hardware) -> int:
|
|
"""Run the main application mode."""
|
|
from cli.main_runner import MainRunner
|
|
|
|
runner = MainRunner(hardware, args)
|
|
return await runner.run()
|
|
|
|
|
|
def main() -> int:
|
|
"""Main entry point."""
|
|
args = parse_args()
|
|
|
|
# Detect hardware first
|
|
print("\n🔍 Detecting hardware...")
|
|
try:
|
|
hardware = detect_hardware()
|
|
except Exception as e:
|
|
print(f"\n❌ Error detecting hardware: {e}", file=sys.stderr)
|
|
return 1
|
|
|
|
# Handle detect mode
|
|
if args.detect:
|
|
return handle_detect_mode(hardware)
|
|
|
|
# Handle tool server mode
|
|
if args.tool_server:
|
|
return handle_tool_server_mode(args, hardware)
|
|
|
|
# Run main mode
|
|
try:
|
|
return asyncio.run(run_main_mode(args, hardware))
|
|
except KeyboardInterrupt:
|
|
print("\n\nReceived stop signal")
|
|
return 0
|
|
except Exception as e:
|
|
print(f"\n❌ Error: {e}", file=sys.stderr)
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|