Python 88.3%
TypeScript 7.6%
Shell 4.1%
1#!/usr/bin/env python32"""Re-runnable OpenAI model discovery: GET /v1/models -> sanitized snapshot + diff against the previous run.34- Calls GET /v1/models (free) via scripts/live.py (auth injected, call logged to reports/live-requests.jsonl).5- Diffs the returned ids against sources/openai/models-api-raw.json (previous run): prints NEW / REMOVED ids and6 any change of `shutdown_date`, and writes sources/openai/models-diff.json.7- Saves the sanitized listing to sources/openai/models-api-raw.json and the id list to sources/openai/model-ids.txt8 (unless --dry-run). The previous listing is kept as sources/openai/models-api-raw.prev.json.910Usage: python3 scripts/discover_openai_models.py [--dry-run]11Exit code 0 always (a diff is information, not an error); prints a one-line summary.12"""13from __future__ import annotations1415import json16import sys17from datetime import datetime, timezone18from pathlib import Path1920sys.path.insert(0, str(Path(__file__).resolve().parent.parent))21from scripts.live import openai_request, save_sanitized # noqa: E4022223ROOT = Path(__file__).resolve().parent.parent24RAW = ROOT / "sources/openai/models-api-raw.json"25PREV = ROOT / "sources/openai/models-api-raw.prev.json"26IDS = ROOT / "sources/openai/model-ids.txt"27DIFF = ROOT / "sources/openai/models-diff.json"282930def main() -> int:31 dry = "--dry-run" in sys.argv32 previous = json.loads(RAW.read_text()) if RAW.exists() else {"data": []}33 prev_models = {m["id"]: m for m in previous.get("data", [])}3435 st, body, hdrs = openai_request("GET", "/v1/models", note="discover_openai_models")36 if st != 200 or not isinstance(body, dict):37 print(f"GET /v1/models -> HTTP {st}; aborting without writing", file=sys.stderr)38 return 039 models = sorted(body.get("data", []), key=lambda m: (m.get("created", 0), m["id"]))40 cur_models = {m["id"]: m for m in models}4142 new_ids = sorted(set(cur_models) - set(prev_models))43 removed_ids = sorted(set(prev_models) - set(cur_models))44 changed = []45 for mid in sorted(set(cur_models) & set(prev_models)):46 a, b = prev_models[mid], cur_models[mid]47 for k in ("shutdown_date", "owned_by", "created"):48 if a.get(k) != b.get(k):49 changed.append({"id": mid, "field": k, "before": a.get(k), "after": b.get(k)})5051 now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")52 diff = {53 "checked_at": now,54 "previous_count": len(prev_models), "current_count": len(cur_models),55 "new": [cur_models[i] for i in new_ids],56 "removed": [prev_models[i] for i in removed_ids],57 "changed": changed,58 "dry_run": dry,59 }60 for m in diff["new"]:61 print(f"NEW {m['id']} (owned_by={m.get('owned_by')}, shutdown_date={m.get('shutdown_date')})")62 for m in diff["removed"]:63 print(f"REMOVED {m['id']} (was shutdown_date={m.get('shutdown_date')})")64 for c in changed:65 print(f"CHANGED {c['id']}.{c['field']}: {c['before']} -> {c['after']}")66 print(f"summary: {len(cur_models)} ids live, {len(new_ids)} new, {len(removed_ids)} removed, {len(changed)} changed")6768 save_sanitized(diff, DIFF)69 if not dry:70 if RAW.exists():71 PREV.write_text(RAW.read_text())72 save_sanitized({"object": body.get("object", "list"), "retrieved_at": now, "data": models}, RAW)73 IDS.write_text("\n".join(sorted(cur_models)) + "\n")74 print(f"wrote {RAW.relative_to(ROOT)}, {IDS.relative_to(ROOT)}, {DIFF.relative_to(ROOT)}")75 else:76 print(f"dry run: wrote only {DIFF.relative_to(ROOT)}")77 return 0787980if __name__ == "__main__":81 sys.exit(main())82