#!/usr/bin/env python3 """Cheap live probes for the OpenAI model catalogue (re-runnable). 1. GET /v1/models/{id} for representative ids (free) — do docs-only ids resolve? do live-only ids have metadata? 2. Minimal POST /v1/responses ("Reply with OK.", max_output_tokens 16) against the newest text models to record LIVE_VERIFIED vs error and the `model` echoed in the response (reveals the snapshot). Every call is logged to reports/live-requests.jsonl by scripts/live.py. Raw bodies -> tmp-live/ (gitignored), sanitized summary -> sources/openai/live-model-probes.json (consumed by scripts/build_openai_models.py). Usage: python3 scripts/probe_openai_models.py [--skip-post] """ from __future__ import annotations import json import sys from datetime import datetime, timezone from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from scripts.live import openai_request, interesting_headers, save_sanitized, mask # noqa: E402 ROOT = Path(__file__).resolve().parent.parent OUT = ROOT / "sources/openai/live-model-probes.json" RAW = ROOT / "tmp-live/openai-model-probes" GET_IDS = [ # newest live ids "gpt-5.6-luna", "gpt-6-astra", "gpt-image-2.5-flare", "gpt-live-1", # docs-only ids "gpt-5.6-cyber", "gpt-daybreak-blue-latest", "gpt-oss-120b", "dall-e-3", "computer-use-preview", "codex-mini-latest", "o1-mini", "chatgpt-4o-latest", "gpt-rosalind-research", "gpt-5.5-cyber", # alias documented in prose only "gpt-5.6", # live-only ids (no model page) "gpt-5-search-api", "gpt-3.5-turbo-16k", "tts-1-1106", # documented as shut down 2026-07-23 but still listed "gpt-5-codex", "gpt-5.1-codex", "o3-deep-research", ] # (model, reasoning effort) — cheapest settings the docs allow. Astra does not support `none`. POST_MODELS = [ ("gpt-5.6-luna", "none"), ("gpt-5.6-sol", "none"), ("gpt-5.6-terra", "none"), ("gpt-6-astra", "low"), ("gpt-5.5", "none"), ("gpt-5.4-mini", "none"), ] # standard USD per 1M tokens (input, cached_input, output) from pricing.md — for cost estimates only PRICE = { "gpt-5.6-luna": (0.20, 0.02, 1.20), "gpt-5.6-sol": (4.0, 0.4, 20.0), "gpt-5.6-terra": (2.0, 0.2, 12.0), "gpt-6-astra": (10.0, 1.0, 50.0), "gpt-5.5": (5.0, 0.5, 30.0), "gpt-5.4-mini": (0.75, 0.075, 4.5), } def main() -> None: skip_post = "--skip-post" in sys.argv RAW.mkdir(parents=True, exist_ok=True) result = {"probed_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), "get_models": {}, "responses": {}, "observed_headers": [], "estimated_cost_usd": 0.0} for mid in GET_IDS: st, body, hdrs = openai_request("GET", f"/v1/models/{mid}", note=f"model probe GET {mid}") save_sanitized(body, RAW / f"get-{mid}.json") entry = {"status": st} if isinstance(body, dict): if st == 200: entry.update({k: body.get(k) for k in ("id", "object", "created", "owned_by", "shutdown_date") if k in body}) elif "error" in body: err = body["error"] entry.update({"error_type": err.get("type"), "error_code": err.get("code"), "error_message": mask(str(err.get("message")))[:300]}) result["get_models"][mid] = entry print(f"GET /v1/models/{mid:<28} -> {st} {entry.get('error_code') or entry.get('owned_by') or ''}") if not skip_post: for mid, effort in POST_MODELS: payload = {"model": mid, "input": "Reply with OK.", "max_output_tokens": 16, "reasoning": {"effort": effort}} st, body, hdrs = openai_request("POST", "/v1/responses", payload, note=f"model probe POST {mid} effort={effort}") save_sanitized(body, RAW / f"post-{mid}.json") entry = {"status": st, "request": payload} cost = 0.0 if isinstance(body, dict): if st == 200: usage = body.get("usage") or {} cached = (usage.get("input_tokens_details") or {}).get("cached_tokens", 0) reasoning = (usage.get("output_tokens_details") or {}).get("reasoning_tokens", 0) text = "" for item in body.get("output", []): for c in item.get("content", []) or []: if c.get("type") == "output_text": text += c.get("text", "") entry.update({"model_echo": body.get("model"), "status_field": body.get("status"), "incomplete_reason": (body.get("incomplete_details") or {}).get("reason"), "service_tier": body.get("service_tier"), "usage": usage, "reasoning_tokens": reasoning, "output_text": text[:64]}) p = PRICE.get(mid) if p: cost = ((usage.get("input_tokens", 0) - cached) * p[0] + cached * p[1] + usage.get("output_tokens", 0) * p[2]) / 1e6 elif "error" in body: err = body["error"] entry.update({"error_type": err.get("type"), "error_code": err.get("code"), "error_param": err.get("param"), "error_message": mask(str(err.get("message")))[:300]}) entry["est_cost_usd"] = round(cost, 6) result["estimated_cost_usd"] = round(result["estimated_cost_usd"] + cost, 6) rl = {k: v for k, v in interesting_headers(hdrs).items() if k.lower().startswith("x-ratelimit")} if rl: result["observed_headers"].append({"date": result["probed_at"][:10], "request": f"POST /v1/responses model={mid}", "headers": rl, "note": "observed for OUR key — account-specific, do not generalize"}) result["responses"][mid] = entry print(f"POST /v1/responses {mid:<16} -> {st} echo={entry.get('model_echo')} {entry.get('error_code') or ''} " f"out={entry.get('output_text','')!r} reasoning_tokens={entry.get('reasoning_tokens')} cost=${cost:.5f}") save_sanitized(result, OUT) print(f"\nsaved {OUT.relative_to(ROOT)}; estimated total cost ${result['estimated_cost_usd']:.4f}") if __name__ == "__main__": main()