SPB Git forge

spb/doc-api

Public
2commits 1branches 0releases
15.7 MBsize
maindefault branch
13 days agolast push
Python 88.3% TypeScript 7.6% Shell 4.1%
5.4 KB · 84 lines python
Raw Blame History
1#!/usr/bin/env python32"""Live verification for the Anthropic models catalogue (cheap; every call logged).341. GET /v1/models/{id} for live ids + legacy/alias ids  -> 200 vs 404 (alias resolution, retired ids)52. GET /v1/models?limit=2 and ?limit=2&after_id=...     -> pagination behaviour63. POST /v1/messages "Reply with OK." max_tokens=8 on a handful of current models -> LIVE_VERIFIED + usage shape74. POST /v1/messages/count_tokens on the same models89Raw sanitized results: tmp-live/anthropic-models/*.json ; summary: tmp-live/anthropic-models/summary.json10"""11from __future__ import annotations1213import json14import sys15from pathlib import Path1617sys.path.insert(0, str(Path(__file__).resolve().parent))18import live  # noqa: E4021920ROOT = Path(__file__).resolve().parents[1]21OUT = ROOT / "tmp-live" / "anthropic-models"2223LIVE_IDS = ["claude-fable-5-1", "claude-fable-5", "claude-opus-5", "claude-sonnet-5", "claude-opus-4-8",24            "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-opus-4-5-20251101",25            "claude-sonnet-4-5-20250929", "claude-haiku-4-5-20251001"]26PROBE_IDS = ["claude-3-5-haiku-20241022", "claude-3-7-sonnet-20250219", "claude-sonnet-4-20250514",27             "claude-opus-4-1-20250805", "claude-haiku-4-5", "claude-sonnet-4-5", "claude-opus-4-5",28             "claude-3-opus-20240229", "claude-mythos-5", "claude-mythos-5-1", "claude-mythos-preview",29             "claude-3-5-sonnet-20241022", "claude-3-haiku-20240307"]30MSG_MODELS = ["claude-opus-5", "claude-sonnet-5", "claude-fable-5-1", "claude-opus-4-8", "claude-sonnet-4-6"]31# $/MTok input,output for cost estimate32PRICE = {"claude-opus-5": (5, 25), "claude-sonnet-5": (2, 10), "claude-fable-5-1": (10, 50),33         "claude-opus-4-8": (5, 25), "claude-sonnet-4-6": (3, 15)}343536def main() -> None:37    OUT.mkdir(parents=True, exist_ok=True)38    summary: dict = {"retrieved_at": live.now(), "get_model": {}, "pagination": {}, "messages": {}, "count_tokens": {}}3940    for mid in LIVE_IDS + PROBE_IDS:41        st, body, hdrs = live.anthropic_request("GET", f"/v1/models/{mid}", note="retrieve model (catalogue verification)")42        summary["get_model"][mid] = {"status": st, "resolved_id": body.get("id") if isinstance(body, dict) else None,43                                     "body": body, "ratelimit_headers": live.interesting_headers(hdrs)}44        print(f"GET /v1/models/{mid} -> {st} {body.get('id') if isinstance(body, dict) and 'id' in body else body.get('error', {}).get('message', '')[:100] if isinstance(body, dict) else ''}")4546    st, body, hdrs = live.anthropic_request("GET", "/v1/models?limit=2", note="pagination probe limit=2")47    summary["pagination"]["page1"] = {"status": st, "body": body, "headers": live.interesting_headers(hdrs)}48    print(f"GET /v1/models?limit=2 -> {st} ids={[m['id'] for m in body.get('data', [])]} has_more={body.get('has_more')} last_id={body.get('last_id')}")49    if st == 200 and body.get("last_id"):50        st2, body2, _ = live.anthropic_request("GET", f"/v1/models?limit=2&after_id={body['last_id']}", note="pagination probe after_id")51        summary["pagination"]["page2"] = {"status": st2, "body": body2}52        print(f"GET /v1/models?limit=2&after_id=... -> {st2} ids={[m['id'] for m in body2.get('data', [])]} has_more={body2.get('has_more')}")53        if st2 == 200 and body2.get("first_id"):54            st3, body3, _ = live.anthropic_request("GET", f"/v1/models?limit=2&before_id={body2['first_id']}", note="pagination probe before_id")55            summary["pagination"]["page_before"] = {"status": st3, "body": body3}56            print(f"GET /v1/models?limit=2&before_id=... -> {st3} ids={[m['id'] for m in body3.get('data', [])]}")57    st4, body4, _ = live.anthropic_request("GET", "/v1/models?limit=0", note="pagination probe limit=0 (expect 400)")58    summary["pagination"]["limit0"] = {"status": st4, "body": body4}59    print(f"GET /v1/models?limit=0 -> {st4} {json.dumps(body4)[:160]}")6061    total_cost = 0.062    for mid in MSG_MODELS:63        payload = {"model": mid, "max_tokens": 8, "messages": [{"role": "user", "content": "Reply with OK."}]}64        pi, po = PRICE[mid]65        est = (20 * pi + 8 * po) / 1e666        st, body, hdrs = live.anthropic_request("POST", "/v1/messages", payload, est_cost_usd=est, note=f"minimal message {mid} (models catalogue LIVE_VERIFIED)")67        usage = body.get("usage") if isinstance(body, dict) else None68        if isinstance(usage, dict):69            est = (usage.get("input_tokens", 0) * pi + usage.get("output_tokens", 0) * po) / 1e670        total_cost += est71        summary["messages"][mid] = {"status": st, "body": body, "ratelimit_headers": live.interesting_headers(hdrs), "est_cost_usd": round(est, 6)}72        print(f"POST /v1/messages {mid} -> {st} model={body.get('model') if isinstance(body, dict) else None} stop={body.get('stop_reason') if isinstance(body, dict) else None} usage={json.dumps(usage)[:300] if usage else body}")73        st, body, hdrs = live.anthropic_request("POST", "/v1/messages/count_tokens", {"model": mid, "messages": payload["messages"]}, note=f"count_tokens {mid}")74        summary["count_tokens"][mid] = {"status": st, "body": body, "ratelimit_headers": live.interesting_headers(hdrs)}75        print(f"POST /v1/messages/count_tokens {mid} -> {st} {json.dumps(body)[:200]}")7677    summary["total_est_cost_usd"] = round(total_cost, 6)78    live.save_sanitized(summary, OUT / "summary.json")79    print(f"total est cost USD {total_cost:.5f}; summary -> {OUT / 'summary.json'}")808182if __name__ == "__main__":83    main()84