"""Files API with the Python SDK: generate a minimal 1-page PDF, upload it (client.files.upload), reference it as a document by file_id with citations enabled (page_location), list with the ids filter, then delete. STATUS: LIVE_VERIFIED 2026-09-18 (claude-haiku-4-5-20251001): upload 200 (607 bytes, downloadable False), message 200 with page_location citation, list-by-ids 1 hit, delete type file_deleted. ≈$0.002. Run: .venv/bin/python examples/anthropic/files/files_pdf_reference.py """ from __future__ import annotations import os import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[3])) from scripts import live # noqa: E402 import anthropic # noqa: E402 MODEL = os.environ.get("ANTHROPIC_MODEL", "claude-haiku-4-5-20251001") client = anthropic.Anthropic(max_retries=1) def tiny_pdf(text: str) -> bytes: stream = f"BT /F1 18 Tf 40 750 Td ({text}) Tj ET".encode() objs = [b"<< /Type /Catalog /Pages 2 0 R >>", b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>", b"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>", b"<< /Length %d >>\nstream\n" % len(stream) + stream + b"\nendstream", b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>"] out, offs = b"%PDF-1.4\n", [] for i, o in enumerate(objs, 1): offs.append(len(out)); out += f"{i} 0 obj\n".encode() + o + b"\nendobj\n" xref = len(out) out += f"xref\n0 {len(objs)+1}\n0000000000 65535 f \n".encode() + b"".join(f"{o:010d} 00000 n \n".encode() for o in offs) return out + f"trailer\n<< /Size {len(objs)+1} /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF\n".encode() f = client.files.upload(file=("atlas.pdf", tiny_pdf("The sky is blue. Grass is green."), "application/pdf")) print("uploaded:", f.id, f.mime_type, f.size_bytes, "downloadable=", f.downloadable, "expires_at=", getattr(f, "expires_at", None)) live.log_request("anthropic", "POST", "/v1/files", 200, 0, "example files/files_pdf_reference.py upload") try: msg = client.messages.create(model=MODEL, max_tokens=60, messages=[{"role": "user", "content": [ {"type": "document", "source": {"type": "file", "file_id": f.id}, "title": "Colors PDF", "citations": {"enabled": True}}, {"type": "text", "text": "What color is grass? One sentence, cite."}]}]) cites = [c for b in msg.content if b.type == "text" and b.citations for c in b.citations] print("answer:", msg.content[0].text, "| citation:", cites[0].model_dump() if cites else None, "| input_tokens:", msg.usage.input_tokens) live.log_request("anthropic", "POST", "/v1/messages", 200, 0.002, f"example files/files_pdf_reference.py message {MODEL}") page = client.files.list(ids=[f.id]) if "ids" in client.files.list.__code__.co_varnames else None listed = [x.id for x in (page.data if page else client.files.list(limit=5).data)] print("list contains upload:", f.id in listed) live.log_request("anthropic", "GET", "/v1/files", 200, 0, "example files/files_pdf_reference.py list") finally: deleted = client.files.delete(f.id) print("deleted:", deleted.id, deleted.type) live.log_request("anthropic", "DELETE", "/v1/files/{file_id}", 200, 0, "example files/files_pdf_reference.py delete")