Skip to content

Instantly share code, notes, and snippets.

@ts-sz
Last active March 29, 2026 22:30
Show Gist options
  • Select an option

  • Save ts-sz/caa22322ecb73274f68f69a25f047536 to your computer and use it in GitHub Desktop.

Select an option

Save ts-sz/caa22322ecb73274f68f69a25f047536 to your computer and use it in GitHub Desktop.
Convert /v1/models response to OpenClaw models config block
#!/usr/bin/env python3
"""
Convert /v1/models response to OpenClaw models config block.
Usage:
# Print standalone models JSON to stdout:
python3 models_to_config.py
# Merge into an existing config file (replaces agents.defaults.models in-place):
python3 models_to_config.py --config config.json
Environment variables (used as fallback if not set below):
CLIPROXY_URL Base URL of the proxy (e.g. https://my-proxy.example.com)
CLIPROXY_APIKEY Bearer token for authentication
CLIPROXY_PREFIX Prefix for model keys (e.g. "my-provider")
"""
import argparse
import json
import os
import re
import sys
import urllib.request
import urllib.error
# Set these directly, or leave empty to read from environment variables
CLIPROXY_URL = ""
CLIPROXY_APIKEY = ""
CLIPROXY_PREFIX = ""
def format_alias(model_id: str, owned_by: str) -> str:
"""Build a human-readable alias from a model ID and its owner."""
# Keep only the last segment (e.g. "openrouter/claude-xxx" -> "claude-xxx")
name = model_id.split("/")[-1]
# Split on dashes and underscores into individual tokens
parts = re.split(r"[-_]", name)
words = []
for p in parts:
if not p:
continue
if re.match(r"^\d", p): # version number: append to previous word
if words:
words[-1] = words[-1] + " " + p
else:
words.append(p)
continue
else:
words.append(p.capitalize())
alias_name = " ".join(words)
return f"{alias_name} [{owned_by}]"
def fetch_models(base_url: str, api_key: str) -> dict:
"""Fetch /v1/models from the proxy and return the JSON response."""
# Strip trailing /v1/models (or /v1) in case the user included it in the URL
base_url = re.sub(r"/v1(/models)?/?$", "", base_url.rstrip("/"))
url = f"{base_url}/v1/models"
print(f"→ Fetching {url}", file=sys.stderr)
req = urllib.request.Request(url)
req.add_header("Authorization", f"Bearer {api_key}")
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())
def main():
"""Fetch models from the proxy and print or merge the config."""
parser = argparse.ArgumentParser(description="Convert /v1/models to OpenClaw config.")
parser.add_argument("--config", "-c", help="Existing config JSON file to update in-place")
args = parser.parse_args()
# Resolve config: in-script values take priority, then env vars
base_url = CLIPROXY_URL or os.environ.get("CLIPROXY_URL", "")
api_key = CLIPROXY_APIKEY or os.environ.get("CLIPROXY_APIKEY", "")
prefix = CLIPROXY_PREFIX or os.environ.get("CLIPROXY_PREFIX", "")
if not base_url or not api_key or not prefix:
sys.exit("Error: CLIPROXY_URL, CLIPROXY_APIKEY, and CLIPROXY_PREFIX must be set (in script or as env vars).")
# Fetch the model list from the proxy
data = fetch_models(base_url, api_key)
# Build the models config mapping
models = {}
for m in data.get("data", []):
model_id = m["id"]
owned_by = m.get("owned_by", "unknown")
key = f"{prefix}/{model_id}"
alias = format_alias(model_id, owned_by)
models[key] = {"alias": alias}
if args.config:
# Merge into existing config file, replacing agents.defaults.models
with open(args.config) as f:
config = json.load(f)
config["agents"]["defaults"]["models"] = models
with open(args.config, "w") as f:
json.dump(config, f, indent=2, ensure_ascii=False)
f.write("\n")
print(f"Updated agents.defaults.models in {args.config} ({len(models)} models)")
else:
# Print standalone JSON to stdout
output = {"models": models}
print(json.dumps(output, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment