| |
| """Manage HealthExpert LLM Microservices. |
| |
| This script allows you to spin up, spin down, and check the status |
| of the local LLM inference endpoints (gen_llm.py and embed_llm.py). |
| |
| Usage: |
| python manage_llm.py status # show status of LLM servers |
| python manage_llm.py up # start both LLM servers |
| python manage_llm.py down # stop both LLM servers |
| """ |
| import argparse |
| import subprocess |
| import time |
| import os |
| import sys |
| from pathlib import Path |
| import urllib.request |
|
|
| GEN_PORT = 8002 |
| EMBED_PORT = 8003 |
| BASE_DIR = Path(__file__).parent.resolve() |
|
|
| def _check_port(port: int) -> bool: |
| """Check if a port is actively listening by making a simple HTTP request.""" |
| try: |
| |
| req = urllib.request.Request(f"http://127.0.0.1:{port}/") |
| urllib.request.urlopen(req, timeout=1) |
| return True |
| except urllib.error.URLError as e: |
| |
| if hasattr(e, 'code'): |
| return True |
| |
| return False |
| except Exception: |
| return False |
|
|
| def _kill_port(port: int) -> None: |
| """Kill any process listening on the given port.""" |
| try: |
| |
| subprocess.run(["fuser", "-k", f"{port}/tcp"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
| except FileNotFoundError: |
| try: |
| |
| pids = subprocess.check_output(["lsof", "-t", f"-i:{port}"]).decode().strip().split('\n') |
| for pid in pids: |
| if pid: |
| subprocess.run(["kill", "-9", pid]) |
| except Exception: |
| pass |
|
|
| def status() -> None: |
| """Show the status of the LLM servers.""" |
| gen_alive = _check_port(GEN_PORT) |
| embed_alive = _check_port(EMBED_PORT) |
|
|
| print("\nββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ") |
| print(" HealthExpert β LLM Microservices Status") |
| print("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ") |
| |
| print("\nββ Generation LLM (agents/gen_llm.py) βββββββββββββββββββββββ") |
| print(f" Port : {GEN_PORT}") |
| print(f" Status : {'π’ RUNNING' if gen_alive else 'π΄ STOPPED'}") |
| |
| print("\nββ Embedding LLM (agents/embed_llm.py) ββββββββββββββββββββββ") |
| print(f" Port : {EMBED_PORT}") |
| print(f" Status : {'π’ RUNNING' if embed_alive else 'π΄ STOPPED'}") |
| print("β" * 62) |
| print("") |
|
|
| def down() -> None: |
| """Stop the LLM servers.""" |
| print("Stopping LLM services...") |
| _kill_port(GEN_PORT) |
| _kill_port(EMBED_PORT) |
| |
| |
| subprocess.run(["pkill", "-f", "agents/gen_llm.py"], stderr=subprocess.DEVNULL) |
| subprocess.run(["pkill", "-f", "agents/embed_llm.py"], stderr=subprocess.DEVNULL) |
| |
| time.sleep(1) |
| print("LLM services stopped.") |
|
|
| def up(hf_mode: bool = False) -> None: |
| """Start the LLM servers.""" |
| gen_alive = _check_port(GEN_PORT) |
| embed_alive = _check_port(EMBED_PORT) |
|
|
| if gen_alive and embed_alive: |
| print("Both LLM services are already running.") |
| return |
|
|
| print("Starting LLM services...") |
| |
| env = os.environ.copy() |
| if hf_mode: |
| env["HF_MODE"] = "1" |
| print("Running in HF CPU mode (HF_MODE=1)") |
|
|
| |
| if not embed_alive: |
| print(f"[1/2] Starting embed_llm on port {EMBED_PORT}...") |
| subprocess.Popen( |
| [sys.executable, str(BASE_DIR / "agents" / "embed_llm.py")], |
| cwd=BASE_DIR, |
| stdout=subprocess.DEVNULL, |
| stderr=subprocess.DEVNULL, |
| start_new_session=True, |
| env=env |
| ) |
| else: |
| print(f"[1/2] embed_llm is already running on port {EMBED_PORT}.") |
|
|
| |
| if not gen_alive: |
| print(f"[2/2] Starting gen_llm on port {GEN_PORT}...") |
| subprocess.Popen( |
| [sys.executable, str(BASE_DIR / "agents" / "gen_llm.py")], |
| cwd=BASE_DIR, |
| stdout=subprocess.DEVNULL, |
| stderr=subprocess.DEVNULL, |
| start_new_session=True, |
| env=env |
| ) |
| else: |
| print(f"[2/2] gen_llm is already running on port {GEN_PORT}.") |
|
|
| print("\nWaiting for services to initialize...") |
| time.sleep(3) |
| status() |
|
|
|
|
| def main() -> None: |
| parser = argparse.ArgumentParser( |
| description="Manage HealthExpert LLM microservices", |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| epilog=__doc__, |
| ) |
| |
| |
| parser.add_argument("command", nargs="?", choices=["up", "down", "status"], default="status", |
| help="Action to perform (default: status)") |
| parser.add_argument("-hf", "--hf", action="store_true", help="Start servers in HuggingFace/CPU mode") |
| |
| args = parser.parse_args() |
|
|
| if args.command == "up": |
| up(hf_mode=args.hf) |
| elif args.command == "down": |
| down() |
| else: |
| status() |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|