nitdaa / manage_llm.py
AI Agent
Update architecture design with project intelligence
05a1493
Raw
History Blame
5.58 kB
#!/usr/bin/env python3
"""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:
# Just a healthcheck to see if server responds, we expect 404 or 200
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 it's an HTTPError (e.g. 404 Not Found), the server is alive
if hasattr(e, 'code'):
return True
# ConnectionRefusedError usually means nothing is listening
return False
except Exception:
return False
def _kill_port(port: int) -> None:
"""Kill any process listening on the given port."""
try:
# Using fuser to kill processes on the port
subprocess.run(["fuser", "-k", f"{port}/tcp"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except FileNotFoundError:
try:
# Fallback to lsof if fuser is not available
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)
# Also kill by script name as a fallback
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)")
# Start Embed LLM
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}.")
# Start Gen LLM
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__,
)
# Optional positional argument for the command
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()