Spaces:
Sleeping
Sleeping
| """Admin CLI for managing the live llm-service deployment. | |
| Usage: | |
| python -m scripts.admin status | |
| python -m scripts.admin update-key <provider> <new-key> | |
| python -m scripts.admin list-models | |
| python -m scripts.admin add-models --provider <name> --key <api-key> | |
| python -m scripts.admin remove-model <model-id> | |
| python -m scripts.admin generate-consumer-key --alias <name> | |
| python -m scripts.admin revoke-consumer-key <key> | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import sys | |
| import tomllib | |
| from pathlib import Path | |
| import click | |
| import httpx | |
| import tomlkit | |
| import yaml | |
| SECRETS_PATH = Path(__file__).resolve().parent.parent / "secrets.toml" | |
| PROVIDERS_PATH = Path(__file__).resolve().parent.parent / "free_llm_providers.yaml" | |
| def load_service_config() -> tuple[str, str]: | |
| """Return (proxy_url, master_key) from secrets.toml.""" | |
| doc = tomllib.loads(SECRETS_PATH.read_text()) | |
| master_key = doc.get("service", {}).get("master_key", "") | |
| proxy_url = doc.get("service", {}).get("proxy_url", "https://ronheichman-llm-service.hf.space") | |
| return proxy_url, master_key | |
| def api_get(path: str, proxy_url: str, master_key: str) -> dict: | |
| with httpx.Client(timeout=120) as client: | |
| resp = client.get(f"{proxy_url}{path}", headers={"Authorization": f"Bearer {master_key}"}) | |
| resp.raise_for_status() | |
| return resp.json() | |
| def api_post(path: str, payload: dict, proxy_url: str, master_key: str) -> dict: | |
| with httpx.Client(timeout=120) as client: | |
| resp = client.post( | |
| f"{proxy_url}{path}", | |
| json=payload, | |
| headers={"Authorization": f"Bearer {master_key}"}, | |
| ) | |
| resp.raise_for_status() | |
| return resp.json() | |
| def cli() -> None: | |
| """Manage the live llm-service deployment.""" | |
| def status() -> None: | |
| """Show deployment health and model counts.""" | |
| proxy_url, master_key = load_service_config() | |
| health = api_get("/health", proxy_url, master_key) | |
| info = api_get("/model/info", proxy_url, master_key) | |
| models = info.get("data", []) | |
| providers: dict[str, int] = {} | |
| db_count = 0 | |
| yaml_count = 0 | |
| for m in models: | |
| mid = m.get("model_info", {}).get("id", "?") | |
| provider = mid.split("::")[0] | |
| providers[provider] = providers.get(provider, 0) + 1 | |
| if m.get("model_info", {}).get("db_model", False): | |
| db_count += 1 | |
| else: | |
| yaml_count += 1 | |
| click.echo(f"Health: {json.dumps(health, indent=2)[:200]}") | |
| click.echo(f"\n{len(models)} deployments ({yaml_count} from config, {db_count} from DB)") | |
| for p in sorted(providers): | |
| click.echo(f" {p}: {providers[p]}") | |
| def list_models() -> None: | |
| """List all model deployments with provider, model, and source.""" | |
| proxy_url, master_key = load_service_config() | |
| info = api_get("/model/info", proxy_url, master_key) | |
| for m in info.get("data", []): | |
| mid = m.get("model_info", {}).get("id", "?") | |
| db = m.get("model_info", {}).get("db_model", False) | |
| source = "db" if db else "yaml" | |
| click.echo(f" [{source}] {mid}") | |
| def update_key(provider: str, new_key: str) -> None: | |
| """Update a provider's API key on the live service. | |
| 1. Updates secrets.toml locally | |
| 2. Regenerates litellm config to get the deployment list | |
| 3. Adds all models for that provider via /model/new | |
| """ | |
| proxy_url, master_key = load_service_config() | |
| doc = tomlkit.loads(SECRETS_PATH.read_text()) | |
| keys_section = doc.get("keys", {}) | |
| if provider not in keys_section: | |
| click.echo(f"Provider '{provider}' not found in secrets.toml", err=True) | |
| sys.exit(1) | |
| keys_section[provider]["keys"] = [new_key] | |
| if "disabled_until" in keys_section[provider]: | |
| del keys_section[provider]["disabled_until"] | |
| SECRETS_PATH.write_text(tomlkit.dumps(doc)) | |
| click.echo(f"Updated secrets.toml for {provider}") | |
| from scripts.generate_config import generate_config | |
| tmp_config = Path("/tmp/admin_litellm_config.yaml") | |
| generate_config(PROVIDERS_PATH, SECRETS_PATH, tmp_config) | |
| config = yaml.safe_load(tmp_config.read_text()) | |
| provider_models = [ | |
| m for m in config["model_list"] | |
| if m["model_info"]["id"].startswith(f"{provider}::") | |
| ] | |
| if not provider_models: | |
| click.echo(f"No models generated for {provider} (filtered out?)", err=True) | |
| sys.exit(1) | |
| added = 0 | |
| for m in provider_models: | |
| m["litellm_params"]["api_key"] = new_key | |
| payload = { | |
| "model_name": m["model_name"], | |
| "litellm_params": m["litellm_params"], | |
| "model_info": m["model_info"], | |
| } | |
| resp = api_post("/model/new", payload, proxy_url, master_key) | |
| click.echo(f" Added {m['model_info']['id']}") | |
| added += 1 | |
| click.echo(f"\n{added} deployments added to live service for {provider}") | |
| click.echo("Note: old YAML-loaded deployments for this provider may still exist until next restart.") | |
| def add_models(provider: str, key: str) -> None: | |
| """Add all models for a provider to the live service.""" | |
| proxy_url, master_key = load_service_config() | |
| from scripts.generate_config import generate_config | |
| tmp_config = Path("/tmp/admin_litellm_config.yaml") | |
| doc = tomlkit.loads(SECRETS_PATH.read_text()) | |
| keys_section = doc.get("keys", {}) | |
| if provider not in keys_section: | |
| click.echo(f"Provider '{provider}' not in secrets.toml. Adding temporarily for config generation.") | |
| keys_section[provider] = tomlkit.table() | |
| keys_section[provider].add("keys", [key]) | |
| tmp_secrets = Path("/tmp/admin_secrets.toml") | |
| tmp_secrets.write_text(tomlkit.dumps(doc)) | |
| generate_config(PROVIDERS_PATH, tmp_secrets, tmp_config) | |
| else: | |
| generate_config(PROVIDERS_PATH, SECRETS_PATH, tmp_config) | |
| config = yaml.safe_load(tmp_config.read_text()) | |
| provider_models = [ | |
| m for m in config["model_list"] | |
| if m["model_info"]["id"].startswith(f"{provider}::") | |
| ] | |
| for m in provider_models: | |
| m["litellm_params"]["api_key"] = key | |
| payload = { | |
| "model_name": m["model_name"], | |
| "litellm_params": m["litellm_params"], | |
| "model_info": m["model_info"], | |
| } | |
| api_post("/model/new", payload, proxy_url, master_key) | |
| click.echo(f" Added {m['model_info']['id']}") | |
| click.echo(f"\n{len(provider_models)} deployments added") | |
| def remove_model(model_id: str) -> None: | |
| """Remove a model deployment by its ID (only works for DB-backed models).""" | |
| proxy_url, master_key = load_service_config() | |
| resp = api_post("/model/delete", {"id": model_id}, proxy_url, master_key) | |
| click.echo(f"Removed: {model_id}") | |
| def generate_consumer_key(alias: str, max_budget: float | None, duration: str | None) -> None: | |
| """Generate a virtual API key for a consumer.""" | |
| proxy_url, master_key = load_service_config() | |
| payload: dict = { | |
| "key_alias": alias, | |
| } | |
| if max_budget is not None: | |
| payload["max_budget"] = max_budget | |
| if duration is not None: | |
| payload["duration"] = duration | |
| resp = api_post("/key/generate", payload, proxy_url, master_key) | |
| key = resp.get("key", "?") | |
| click.echo(f"Consumer key for '{alias}': {key}") | |
| click.echo("Give this key to the consumer. They use it instead of the master key.") | |
| def revoke_consumer_key(key: str) -> None: | |
| """Revoke a consumer's virtual API key.""" | |
| proxy_url, master_key = load_service_config() | |
| resp = api_post("/key/delete", {"keys": [key]}, proxy_url, master_key) | |
| click.echo(f"Revoked: {key}") | |
| if __name__ == "__main__": | |
| cli() | |