Download cli/preflight.py from LoveLogicAI/kanbot-backend: direct link, hf CLI and curl.
- Browser
- Download file 3.7 kB
-
https://huggingface.co/spaces/LoveLogicAI/kanbot-backend/resolve/main/cli/preflight.py
- Command line
-
hf download hf://spaces/LoveLogicAI/kanbot-backend/cli/preflight.py
-
curl -L -o preflight.py https://huggingface.co/spaces/LoveLogicAI/kanbot-backend/resolve/main/cli/preflight.py
3.7 kB
| #!/usr/bin/env python3 | |
| """ | |
| Preflight β verify every Path B dependency before flipping KanBot to live. | |
| Probes the real services (no mocks). Prints a checklist and exits non-zero if | |
| anything the live cycle needs is missing, so you never flip DRY_RUN=0 into a | |
| half-ready stack. | |
| β Postgres reachable on DATABASE_URL, schema present | |
| β Ollama reachable on OLLAMA_HOST | |
| β both configured models pulled | |
| β dry-run status reported (informational) | |
| Run: python cli/preflight.py | |
| """ | |
| from __future__ import annotations | |
| import asyncio | |
| import os | |
| import sys | |
| sys.path.insert(0, ".") | |
| GREEN, RED, YEL, DIM, RST = "\033[32m", "\033[31m", "\033[33m", "\033[2m", "\033[0m" | |
| OK, BAD, WARN = f"{GREEN}β{RST}", f"{RED}β{RST}", f"{YEL}!{RST}" | |
| DSN = os.environ.get("DATABASE_URL", "postgresql://kanbot:kanbot@localhost:5432/kanbot") | |
| OLLAMA = os.environ.get("OLLAMA_HOST", "http://localhost:11434") | |
| MODEL_FAST = os.environ.get("KANBOT_MODEL_FAST", "qwen2.5:8b-instruct-q4_K_M") | |
| MODEL_DAILY = os.environ.get("KANBOT_MODEL_DAILY", "qwen2.5:14b-instruct-q4_K_M") | |
| async def check_postgres() -> tuple[bool, str]: | |
| try: | |
| import asyncpg | |
| except ImportError: | |
| return False, "asyncpg not installed (pip install -r requirements.txt)" | |
| try: | |
| conn = await asyncpg.connect(DSN, timeout=4) | |
| except Exception as e: | |
| return False, f"cannot connect to {DSN}: {e}" | |
| try: | |
| has = await conn.fetchval( | |
| "SELECT to_regclass('public.cards') IS NOT NULL") | |
| return (bool(has), | |
| "connected, schema present" if has | |
| else "connected but 'cards' table missing (apply sql/001_schema.sql)") | |
| finally: | |
| await conn.close() | |
| async def check_ollama() -> tuple[bool, str, set[str]]: | |
| try: | |
| import httpx | |
| except ImportError: | |
| return False, "httpx not installed", set() | |
| try: | |
| async with httpx.AsyncClient(timeout=4) as c: | |
| r = await c.get(f"{OLLAMA}/api/tags") | |
| r.raise_for_status() | |
| names = {m["name"] for m in r.json().get("models", [])} | |
| return True, f"reachable at {OLLAMA}", names | |
| except Exception as e: | |
| return False, f"unreachable at {OLLAMA}: {e}", set() | |
| async def main() -> int: | |
| print(f"\n KanBot preflight β Path B readiness\n") | |
| fail = 0 | |
| ok, msg = await check_postgres() | |
| print(f" {OK if ok else BAD} postgres {DIM}{msg}{RST}") | |
| fail += not ok | |
| ok, msg, models = await check_ollama() | |
| print(f" {OK if ok else BAD} ollama {DIM}{msg}{RST}") | |
| fail += not ok | |
| if ok: | |
| for label, m in (("daily/critic", MODEL_DAILY), ("fast head", MODEL_FAST)): | |
| have = any(m.split(":")[0] in n or m == n for n in models) | |
| print(f" {OK if have else BAD} model {DIM}{label}: {m} " | |
| f"{'pulled' if have else 'MISSING β ollama pull ' + m}{RST}") | |
| fail += not have | |
| else: | |
| print(f" {WARN} model {DIM}skipped (ollama unreachable){RST}") | |
| dry = os.environ.get("KANBOT_DRY_RUN", "1") == "1" | |
| print(f" {WARN} dry-run {DIM}KANBOT_DRY_RUN={'1 (proposals only)' if dry else '0 (LIVE β commands execute)'}{RST}") | |
| print() | |
| if fail: | |
| print(f" {RED}NOT READY: {fail} dependency check(s) failed. See GOLIVE.md.{RST}\n") | |
| return 1 | |
| print(f" {GREEN}READY: all Path B dependencies satisfied.{RST}") | |
| if dry: | |
| print(f" {DIM}Still in dry-run. Flip KANBOT_DRY_RUN=0 when you want live execution.{RST}\n") | |
| else: | |
| print(f" {YEL}LIVE MODE ON β approved cards will execute through the sandbox.{RST}\n") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(asyncio.run(main())) | |