Files
python-sdk/pkg/hanzo-tools-api/scripts/preload_specs.py
T
zooqueenandHanzo Dev 0834abe2aa style: auto-fix ruff lint and format for Python 3.12
Run ruff check --fix and ruff format across the repo to fix ~495
auto-fixable lint errors (mostly I001 import sorting) and reformat
225 files to match the py312 target.

Co-authored-by: Hanzo Dev <[email protected]>
2026-02-20 21:29:31 -08:00

178 lines
4.5 KiB
Python

#!/usr/bin/env python3
"""Pre-download and cache popular OpenAPI specs for faster startup.
Usage:
python scripts/preload_specs.py # Download top 50 specs
python scripts/preload_specs.py --all # Download all specs with URLs
python scripts/preload_specs.py --list # List available specs
python scripts/preload_specs.py github stripe # Download specific specs
"""
import argparse
import asyncio
import sys
from pathlib import Path
# Add parent to path for local development
sys.path.insert(0, str(Path(__file__).parent.parent))
from hanzo_tools.api.client import APIClient
from hanzo_tools.api.providers import get_provider_config, list_providers_with_specs
# Top 50 most popular APIs to pre-cache
POPULAR_APIS = [
# AI/ML
"openai",
"anthropic",
"together",
"groq",
"mistral",
"cohere",
"replicate",
# Developer
"github",
"gitlab",
"bitbucket",
"vercel",
"netlify",
"fly",
"railway",
"render",
# Cloud
"cloudflare",
"digitalocean",
"hetzner",
"linode",
"vultr",
# Payment
"stripe",
"paypal",
"adyen-com-accountservice",
"square",
# Communication
"twilio",
"sendgrid",
"resend",
"slack",
"discord",
# Databases
"supabase",
"neon",
"planetscale",
"mongodb",
"redis",
# Search
"algolia",
"elasticsearch",
"meilisearch",
"typesense",
# Monitoring
"datadog",
"sentry",
"newrelic",
# Other popular
"notion",
"airtable",
"hubspot",
"shopify",
"jira",
]
async def preload_specs(
providers: list[str] | None = None,
concurrent: int = 10,
verbose: bool = True,
) -> dict[str, bool]:
"""Pre-download and cache OpenAPI specs.
Args:
providers: List of provider names, or None for POPULAR_APIS
concurrent: Number of concurrent downloads
verbose: Print progress
Returns:
Dict mapping provider name to success status
"""
client = APIClient()
if providers is None:
providers = POPULAR_APIS
# Filter to only those with spec URLs
valid_providers = []
for p in providers:
config = get_provider_config(p)
if config and config.spec_url:
valid_providers.append(p)
elif verbose:
print(f" ⚠ {p}: no spec URL configured")
if verbose:
print(f"Pre-loading {len(valid_providers)} OpenAPI specs...")
print()
results: dict[str, bool] = {}
semaphore = asyncio.Semaphore(concurrent)
async def fetch_one(provider: str) -> tuple[str, bool]:
async with semaphore:
try:
await client.spec(provider)
if verbose:
print(f" ✓ {provider}")
return provider, True
except Exception as e:
if verbose:
print(f" ✗ {provider}: {e}")
return provider, False
tasks = [fetch_one(p) for p in valid_providers]
for coro in asyncio.as_completed(tasks):
provider, success = await coro
results[provider] = success
if verbose:
success_count = sum(1 for v in results.values() if v)
print()
print(f"Downloaded {success_count}/{len(results)} specs")
print(f"Cached at: {client._spec_cache.cache_dir}")
return results
async def main():
parser = argparse.ArgumentParser(description="Pre-download OpenAPI specs")
parser.add_argument("providers", nargs="*", help="Specific providers to download")
parser.add_argument("--all", action="store_true", help="Download all available specs")
parser.add_argument("--list", action="store_true", help="List available specs")
parser.add_argument("-n", "--concurrent", type=int, default=10, help="Concurrent downloads")
parser.add_argument("-q", "--quiet", action="store_true", help="Quiet mode")
args = parser.parse_args()
if args.list:
specs = list_providers_with_specs()
print(f"Available specs ({len(specs)}):")
for i, name in enumerate(specs, 1):
config = get_provider_config(name)
display = config.display_name if config else name
print(f" {i:4}. {name}: {display}")
return
providers = None
if args.providers:
providers = args.providers
elif args.all:
providers = list_providers_with_specs()
await preload_specs(
providers=providers,
concurrent=args.concurrent,
verbose=not args.quiet,
)
if __name__ == "__main__":
asyncio.run(main())