Python 88.3%
TypeScript 7.6%
Shell 4.1%
1#!/usr/bin/env python32"""Live probe of the Anthropic Messages API core surface (run once on 2026-09-18).34STATUS: LIVE_VERIFIED — every call below ran against api.anthropic.com with the key in .env.5Sanitized raw responses are written to tmp-live/anthropic-core/<name>.json and every call is6logged to reports/live-requests.jsonl by scripts/live.py. Budget: ~40 tiny calls on7claude-haiku-4-5-20251001 (one on claude-sonnet-5), well under $0.30 total.89Run: .venv/bin/python examples/anthropic/messages/probe_core_live.py10"""11from __future__ import annotations1213import base6414import json15import sys16import zlib17from pathlib import Path1819ROOT = Path(__file__).resolve().parents[3]20sys.path.insert(0, str(ROOT))21from scripts.live import anthropic_request, interesting_headers, save_sanitized # noqa: E4022223OUT = ROOT / "tmp-live" / "anthropic-core"24MODEL = "claude-haiku-4-5-20251001"25PRICE = {"claude-haiku-4-5-20251001": (1.0, 5.0), "claude-sonnet-5": (3.0, 15.0)} # USD per 1M in/out (Haiku documented; Sonnet 5 assumed)26SUMMARY: list[dict] = []27TOTAL_COST = 0.0282930def cost(model: str, body) -> float:31 if not isinstance(body, dict) or "usage" not in body:32 return 0.033 u = body["usage"]34 pin, pout = PRICE.get(model, (3.0, 15.0))35 return (u.get("input_tokens", 0) + (u.get("cache_creation_input_tokens") or 0)) * pin / 1e6 + u.get("output_tokens", 0) * pout / 1e6363738def call(name: str, method: str, path: str, body=None, *, beta=None, headers=None, note="", model=MODEL):39 global TOTAL_COST40 st, out, hdrs = anthropic_request(method, path, body, beta=beta, extra_headers=headers, note=f"core-probe {name}: {note}")41 c = cost(model, out)42 TOTAL_COST += c43 rec = {"name": name, "method": method, "path": path, "status": st, "request": body, "beta": beta,44 "extra_headers": {k: ("***" if k.lower() == "x-api-key" else v) for k, v in (headers or {}).items()},45 "response_headers": interesting_headers(hdrs), "response": out if not isinstance(out, bytes) else out.decode("utf-8", "replace"),46 "est_cost_usd": round(c, 6)}47 save_sanitized(rec, OUT / f"{name}.json")48 SUMMARY.append({"name": name, "status": st, "est_cost_usd": round(c, 6),49 "stop_reason": out.get("stop_reason") if isinstance(out, dict) else None,50 "error": out.get("error") if isinstance(out, dict) and out.get("type") == "error" else None})51 print(f"[{st}] {name}")52 return st, out, hdrs535455def msg(text="Reply with OK.", **kw):56 b = {"model": MODEL, "max_tokens": 16, "messages": [{"role": "user", "content": text}]}57 b.update(kw)58 return b596061def stream_probe(name: str, body: dict, *, beta=None):62 """Capture the exact ordered SSE event list."""63 global TOTAL_COST64 st, gen, hdrs = anthropic_request("POST", "/v1/messages", body, beta=beta, stream=True, note=f"core-probe {name}: stream")65 events, raw_lines = [], []66 if st == 200:67 cur = {}68 for line in gen:69 raw_lines.append(line)70 if line.startswith("event:"):71 cur = {"event": line[6:].strip()}72 elif line.startswith("data:"):73 try:74 cur["data"] = json.loads(line[5:].strip())75 except Exception: # noqa: BLE00176 cur["data"] = line[5:].strip()77 events.append(cur)78 cur = {}79 final_usage = next((e["data"].get("usage") for e in events if e["event"] == "message_delta"), None)80 start_msg = next((e["data"]["message"] for e in events if e["event"] == "message_start"), {})81 c = 0.082 if final_usage:83 pin, pout = PRICE[MODEL]84 c = start_msg.get("usage", {}).get("input_tokens", 0) * pin / 1e6 + final_usage.get("output_tokens", 0) * pout / 1e685 TOTAL_COST += c86 else:87 c = 0.088 events = [{"event": "http_error", "data": gen if not isinstance(gen, bytes) else gen.decode()}]89 rec = {"name": name, "status": st, "request": body, "response_headers": interesting_headers(hdrs),90 "event_sequence": [e["event"] for e in events], "events": events, "raw_lines": raw_lines, "est_cost_usd": round(c, 6)}91 save_sanitized(rec, OUT / f"{name}.json")92 SUMMARY.append({"name": name, "status": st, "est_cost_usd": round(c, 6), "event_sequence": [e["event"] for e in events]})93 print(f"[{st}] {name}: {[e['event'] for e in events]}")94 return events959697def one_px_png() -> str:98 def chunk(t, d):99 return len(d).to_bytes(4, "big") + t + d + zlib.crc32(t + d).to_bytes(4, "big")100 ihdr = (1).to_bytes(4, "big") + (1).to_bytes(4, "big") + bytes([8, 2, 0, 0, 0])101 idat = zlib.compress(b"\x00\xff\x00\x00")102 png = b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", ihdr) + chunk(b"IDAT", idat) + chunk(b"IEND", b"")103 return base64.b64encode(png).decode()104105106TOOL = {"name": "get_weather", "description": "Get current weather for a city.",107 "input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}108109if __name__ == "__main__":110 OUT.mkdir(parents=True, exist_ok=True)111 # (a) minimal112 call("a_minimal", "POST", "/v1/messages", msg(), note="minimal; record undocumented fields")113 # (b) stream114 stream_probe("b_stream", msg(stream=True))115 # (c) system + sampling params + stop sequence116 st, out, _ = call("c_system_sampling_stop", "POST", "/v1/messages",117 msg("Count: 1 2 3 4 5", system="You are terse. Output only what is asked.", temperature=0.2, top_p=0.9, top_k=40,118 stop_sequences=["3"], max_tokens=32), note="temperature+top_p+top_k together + stop_sequences")119 if st == 400:120 call("c2_temperature_only_stop", "POST", "/v1/messages",121 msg("Count: 1 2 3 4 5", system="You are terse. Output only what is asked.", temperature=0.2, top_k=40,122 stop_sequences=["3"], max_tokens=32), note="retry without top_p")123 # (d) max_tokens 1124 call("d_max_tokens_1", "POST", "/v1/messages", msg("Write a paragraph about oceans.", max_tokens=1), note="expect stop_reason max_tokens")125 # (e) metadata.user_id126 call("e_metadata_user_id", "POST", "/v1/messages", msg(metadata={"user_id": "atlas-user-0001"}), note="metadata.user_id")127 # (f) prefill128 prefill = [{"role": "user", "content": "Say the word OK and nothing else."}, {"role": "assistant", "content": "The word is:"}]129 call("f_prefill_haiku", "POST", "/v1/messages", {"model": MODEL, "max_tokens": 8, "messages": prefill}, note="assistant prefill on Haiku 4.5")130 call("f2_prefill_sonnet5", "POST", "/v1/messages", {"model": "claude-sonnet-5", "max_tokens": 8, "messages": prefill},131 note="assistant prefill on claude-sonnet-5 (expect 400)", model="claude-sonnet-5")132 call("f3_multiturn", "POST", "/v1/messages", {"model": MODEL, "max_tokens": 8, "messages": [133 {"role": "user", "content": "Remember the code word: PLUM."}, {"role": "assistant", "content": "Noted."},134 {"role": "user", "content": "Reply with only the code word."}]}, note="multi-turn")135 # (g) count_tokens136 base_ct = {"model": MODEL, "messages": [{"role": "user", "content": "Reply with OK."}]}137 call("g1_count_simple", "POST", "/v1/messages/count_tokens", base_ct, note="count_tokens simple")138 call("g2_count_system", "POST", "/v1/messages/count_tokens", {**base_ct, "system": "You are a helpful assistant."}, note="with system")139 call("g3_count_tool", "POST", "/v1/messages/count_tokens", {**base_ct, "tools": [TOOL]}, note="with tool")140 call("g4_count_image", "POST", "/v1/messages/count_tokens", {"model": MODEL, "messages": [{"role": "user", "content": [141 {"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": one_px_png()}},142 {"type": "text", "text": "Reply with OK."}]}]}, note="with 1x1 PNG")143 call("g5_count_document", "POST", "/v1/messages/count_tokens", {"model": MODEL, "messages": [{"role": "user", "content": [144 {"type": "document", "source": {"type": "text", "media_type": "text/plain", "data": "The sky is blue."}, "title": "note",145 "citations": {"enabled": True}}, {"type": "text", "text": "Reply with OK."}]}]}, note="with text document + citations")146 call("g6_count_thinking", "POST", "/v1/messages/count_tokens", {**base_ct, "thinking": {"type": "enabled", "budget_tokens": 1024}}, note="with thinking enabled")147 call("g7_count_output_config", "POST", "/v1/messages/count_tokens", {**base_ct, "output_config": {"format": {"type": "json_schema", "schema": {148 "type": "object", "properties": {"answer": {"type": "string"}}, "required": ["answer"], "additionalProperties": False}}}}, note="with output_config.format")149 call("g8_count_server_tool", "POST", "/v1/messages/count_tokens", {**base_ct, "tools": [{"type": "web_search_20250305", "name": "web_search"}]},150 note="server tool (docs say rejected)")151 # (h) service_tier152 call("h1_service_tier_standard_only", "POST", "/v1/messages", msg(service_tier="standard_only"), note="service_tier standard_only")153 call("h2_service_tier_auto", "POST", "/v1/messages", msg(service_tier="auto"), note="service_tier auto")154 # (i) errors155 call("i1_invalid_model", "POST", "/v1/messages", msg(model="claude-does-not-exist"), note="expect 404 not_found_error")156 b = msg(); del b["max_tokens"]157 call("i2_missing_max_tokens", "POST", "/v1/messages", b, note="expect 400")158 call("i3_bogus_api_key", "POST", "/v1/messages", msg(), headers={"x-api-key": "invalid-key-placeholder"}, note="expect 401")159 call("i4_max_tokens_too_large", "POST", "/v1/messages", msg(max_tokens=1_000_000), note="expect 400")160 call("i5_invalid_version", "POST", "/v1/messages", msg(), headers={"anthropic-version": "2020-01-01"}, note="invalid anthropic-version")161 call("i6_unknown_beta", "POST", "/v1/messages", msg(), beta="does-not-exist-2099-01-01", note="unknown beta header value")162 call("i7_temperature_5", "POST", "/v1/messages", msg(temperature=5), note="expect 400")163 call("i8_top_k_string", "POST", "/v1/messages", msg(top_k="abc"), note="wrong type")164 call("i9_empty_messages", "POST", "/v1/messages", msg(messages=[]), note="empty messages")165 st, out, hdrs = anthropic_request("POST", "/v1/messages", data=b'{"model": ', note="core-probe i10_bad_json")166 save_sanitized({"name": "i10_bad_json", "status": st, "response": out if not isinstance(out, bytes) else out.decode(), "response_headers": interesting_headers(hdrs)}, OUT / "i10_bad_json.json")167 SUMMARY.append({"name": "i10_bad_json", "status": st, "error": out.get("error") if isinstance(out, dict) else None}); print(f"[{st}] i10_bad_json")168 call("i11_not_found_path", "GET", "/v1/does-not-exist", None, note="unknown path")169 call("i12_tool_use_without_result", "POST", "/v1/messages", {"model": MODEL, "max_tokens": 8, "tools": [TOOL], "messages": [170 {"role": "user", "content": "Weather in Paris?"},171 {"role": "assistant", "content": [{"type": "tool_use", "id": "toolu_01ABCDEFGHIJKLMNOPQRSTUV", "name": "get_weather", "input": {"city": "Paris"}}]},172 {"role": "user", "content": "thanks"}]}, note="tool_use without tool_result")173 call("i13_thinking_adaptive_haiku", "POST", "/v1/messages", msg(thinking={"type": "adaptive"}), note="adaptive thinking on 4.5 model (expect 400)")174 call("i14_output_format_legacy_field", "POST", "/v1/messages/count_tokens", {**base_ct, "output_format": {"type": "json_schema", "schema": {"type": "object"}}},175 note="legacy output_format field without beta")176 # (j) models177 call("j1_get_model_alias", "GET", "/v1/models/claude-haiku-4-5", None, note="alias resolution")178 call("j2_list_models", "GET", "/v1/models?limit=3", None, note="list models limit 3")179 # (k) legacy complete180 call("k1_complete_legacy", "POST", "/v1/complete", {"model": "claude-2.1", "max_tokens_to_sample": 8, "prompt": "\n\nHuman: Reply with OK.\n\nAssistant:"},181 note="legacy text completions, retired model", model="claude-2.1")182 call("k2_complete_haiku", "POST", "/v1/complete", {"model": MODEL, "max_tokens_to_sample": 8, "prompt": "\n\nHuman: Reply with OK.\n\nAssistant:"},183 note="legacy text completions with current model")184 # (l) old version header185 call("l_version_2023_01_01", "POST", "/v1/messages", msg(), headers={"anthropic-version": "2023-01-01"}, note="anthropic-version 2023-01-01")186 # tool_use stop reason (cheap) + stream with tool for input_json_delta187 call("m1_tool_use_stop", "POST", "/v1/messages", {"model": MODEL, "max_tokens": 64, "tools": [TOOL], "tool_choice": {"type": "tool", "name": "get_weather"},188 "messages": [{"role": "user", "content": "Weather in Paris?"}]}, note="forced tool_use → stop_reason tool_use")189 stream_probe("m2_stream_tool_use", {"model": MODEL, "max_tokens": 64, "stream": True, "tools": [TOOL], "tool_choice": {"type": "tool", "name": "get_weather"},190 "messages": [{"role": "user", "content": "Weather in Paris?"}]})191 # thinking stream on Haiku 4.5 (enabled, budget 1024) — small max_tokens to bound cost192 stream_probe("m3_stream_thinking", {"model": MODEL, "max_tokens": 1100, "stream": True, "thinking": {"type": "enabled", "budget_tokens": 1024},193 "messages": [{"role": "user", "content": "Reply with OK."}]})194 # cache_control (top-level) + max_tokens 0 pre-warm (documented) — tiny195 call("n1_max_tokens_0_prewarm", "POST", "/v1/messages", msg(max_tokens=0, cache_control={"type": "ephemeral"}), note="max_tokens 0 + top-level cache_control")196 # inference_geo param existence (may 400 depending on workspace)197 call("n2_inference_geo_us", "POST", "/v1/messages", msg(inference_geo="us"), note="inference_geo us")198 # stop_details / container / context_management presence with beta header for context management199 call("n3_beta_context_management", "POST", "/v1/messages", msg(context_management={"edits": [{"type": "clear_tool_uses_20250919"}]}),200 beta="context-management-2025-06-27", note="beta context_management shape")201 call("n4_speed_fast_haiku", "POST", "/v1/messages", msg(speed="fast"), beta="fast-mode-2026-02-01", note="speed fast on Haiku (expect 400)")202 call("n5_betas_field_in_body", "POST", "/v1/messages", msg(betas=["context-management-2025-06-27"]), note="betas as body field (SDK-only param?)")203204 summary = {"probe": "anthropic-core", "date": "2026-09-18", "model": MODEL, "total_est_cost_usd": round(TOTAL_COST, 6), "calls": SUMMARY}205 save_sanitized(summary, OUT / "_summary.json")206 print(json.dumps({"total_est_cost_usd": round(TOTAL_COST, 6), "n_calls": len(SUMMARY)}))207