import asyncio import atexit import os import signal import subprocess import sys from urllib.parse import urlparse import uvicorn # 将项目根目录添加到 python 路径 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) _SIDECAR_PROCESS: subprocess.Popen | None = None def _parse_sidecar_target() -> tuple[str, int]: sidecar_url = os.getenv("COPILOT_SIDECAR_URL", "http://127.0.0.1:8787").strip() parsed = urlparse(sidecar_url) host = parsed.hostname or "127.0.0.1" port = parsed.port or 8787 return host, port def _stop_sidecar() -> None: global _SIDECAR_PROCESS proc = _SIDECAR_PROCESS if proc is None: return if proc.poll() is None: proc.terminate() try: proc.wait(timeout=5) except subprocess.TimeoutExpired: proc.kill() proc.wait(timeout=3) _SIDECAR_PROCESS = None def _start_sidecar_if_needed() -> None: global _SIDECAR_PROCESS sidecar_dir = os.path.abspath( os.path.join(os.path.dirname(__file__), "..", "copilot-sidecar") ) host, port = _parse_sidecar_target() cmd = [ sys.executable, "-m", "uvicorn", "server:app", "--host", host, "--port", str(port), "--log-level", os.getenv("COPILOT_SIDECAR_LOG_LEVEL", "warning"), ] _SIDECAR_PROCESS = subprocess.Popen(cmd, cwd=sidecar_dir) print(f"[run_server] sidecar started at {host}:{port}.") if __name__ == "__main__": # Windows 设置事件循环策略 if sys.platform == "win32": asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) atexit.register(_stop_sidecar) signal.signal(signal.SIGTERM, lambda *_: _stop_sidecar()) signal.signal(signal.SIGINT, lambda *_: _stop_sidecar()) _start_sidecar_if_needed() try: # 用 uvicorn.run 支持 workers 参数 uvicorn.run( "app.main:app", host="0.0.0.0", port=8000, # workers=2, # 这里可以设置多进程 loop="asyncio", ) finally: _stop_sidecar()