frox-nano-v2 / src /scripts /serve.py
Hritik045678's picture
Upload folder using huggingface_hub
296a506 verified
Raw
History Blame Contribute Delete
4.12 kB
#!/usr/bin/env python3
"""
Frox AI — Serve the Morph API
Launches the FastAPI server every client (frox-chat, frox-code, a
future CLI, a future Android app) connects to.
Usage:
python scripts/serve.py --family classic --model ./frox-morph-1-1-output/classic_sft_final
python scripts/serve.py --family nano # untrained — proves the wiring works
python scripts/serve.py --family classic --model ./ckpt --quantization 4bit --port 8080
Then point any client at http://<this-host>:<port>/v1 as an
OpenAI-compatible endpoint. For frox-chat (Open WebUI): Settings →
Connections → add an OpenAI API connection with that base URL.
"""
from __future__ import annotations
import argparse
import os
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from utils.common import print_banner, FAMILY_TIERS
def main():
parser = argparse.ArgumentParser(description="Serve the Frox AI Morph API")
parser.add_argument("--family", choices=list(FAMILY_TIERS), default="classic",
help="Which Morph model-family tier to serve")
parser.add_argument("--model", type=str, default=None,
help="Path to a trained checkpoint. Omit to serve an "
"untrained model (useful only for testing the wiring).")
parser.add_argument("--quantization", choices=["4bit", "8bit"], default=None)
parser.add_argument("--host", type=str, default="0.0.0.0")
parser.add_argument("--port", type=int, default=8000)
parser.add_argument("--cors-origins", type=str, default="*",
help="Comma-separated allowed origins, e.g. "
"'http://localhost:3000,tauri://localhost'. "
"Defaults to '*' (fine for local dev, not for production).")
parser.add_argument("--memory-path", type=str, default="./data/memories.json")
parser.add_argument("--reload", action="store_true", help="Auto-reload on code changes (dev only)")
conductor_group = parser.add_argument_group("Conductor (Fugu-inspired orchestration)")
conductor_group.add_argument("--no-conductor", action="store_true",
help="Disable the 'conductor' pseudo-model entirely")
conductor_group.add_argument("--conductor-mode", choices=["auto", "fast", "deep"], default="auto",
help="Default orchestration depth when a client requests model=conductor")
conductor_group.add_argument("--worker", action="append", default=[], metavar="NAME=URL",
help="Register a remote worker for true multi-tier orchestration, e.g. "
"--worker code=http://localhost:8001/v1 (repeatable). Omit to use "
"self-orchestration on this process's one loaded model instead.")
args = parser.parse_args()
os.environ["MORPH_FAMILY"] = args.family
if args.model:
os.environ["MORPH_CHECKPOINT"] = args.model
if args.quantization:
os.environ["MORPH_QUANTIZATION"] = args.quantization
os.environ["MORPH_CORS_ORIGINS"] = args.cors_origins
os.environ["MORPH_MEMORY_PATH"] = args.memory_path
os.environ["MORPH_CONDUCTOR_ENABLED"] = "false" if args.no_conductor else "true"
os.environ["MORPH_CONDUCTOR_MODE"] = args.conductor_mode
for entry in args.worker:
if "=" not in entry:
parser.error(f"--worker expects NAME=URL, got: {entry!r}")
name, url = entry.split("=", 1)
os.environ[f"MORPH_WORKER_{name.strip().upper()}_URL"] = url.strip()
print_banner()
print(f"Serving Morph ({args.family}) at http://{args.host}:{args.port}")
print(f" OpenAI-compatible base URL for clients: http://{args.host}:{args.port}/v1")
if not args.model:
print(" âš  No --model given: serving an UNTRAINED model. Responses will be gibberish.")
print()
import uvicorn
uvicorn.run(
"api.server:app", host=args.host, port=args.port,
reload=args.reload, log_level="info",
)
if __name__ == "__main__":
main()