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%
19.7 KB · 229 lines python
Raw Blame History
1#!/usr/bin/env python32"""Generate parameter + object fragments for the platform domain from the OpenAPI spec (+ curated overrides).3Writes generated/fragments/parameters/openai-{files,uploads,vector-stores,batch,fine-tuning,graders,evals}.json4and generated/fragments/objects/openai-platform-objects.json5"""6from __future__ import annotations7import json, sys8from pathlib import Path9ROOT = Path(__file__).resolve().parent.parent10sys.path.insert(0, str(ROOT / "tmp"))11import platform_extract as X  # noqa: E402  (re-runs extraction, cheap)1213RETRIEVED = "2026-09-18"14REF = "https://developers.openai.com/api/reference/resources/"15SRC = {16    "files": REF + "files", "uploads": REF + "uploads", "vector_stores": REF + "vector_stores",17    "batches": REF + "batches", "fine_tuning": REF + "fine_tuning", "graders": REF + "graders", "evals": REF + "evals",18}19DEPRECATION = {20    "fine_tuning": ["DEPRECATED"],   # self-serve fine-tuning winds down (Jan 6 2027 job creation cutoff)21    "graders": ["DEPRECATED"],       # graders deprecated with evals + fine-tuning (Nov 30 2026 / Jan 6 2027)22    "evals": ["DEPRECATED"],         # Evals API shuts down Nov 30 2026 (read-only Oct 31 2026)23}24FAMILY = {  # api_family per path prefix25    "/files": "files", "/uploads": "uploads", "/vector_stores": "vector_stores", "/batches": "batches",26    "/fine_tuning/alpha/graders": "graders", "/fine_tuning": "fine_tuning", "/evals": "evals",27}28OUTNAME = {"files": "openai-files", "uploads": "openai-uploads", "vector_stores": "openai-vector-stores",29           "batches": "openai-batch", "fine_tuning": "openai-fine-tuning", "graders": "openai-graders", "evals": "openai-evals"}303132def family(path: str) -> str:33    for p, f in FAMILY.items():34        if path.startswith(p):35            return f36    raise KeyError(path)373839def norm_type(t):40    if t is None:41        return "object"42    return t434445rows_by_family: dict[str, list[dict]] = {k: [] for k in OUTNAME}46seen = set()47for key, op in X.summary.items():48    method, path = key.split(" ", 1)49    fam = family(path)50    ep = f"{method} /v1{path}"51    status = ["DOCUMENTED"] + DEPRECATION.get(fam, [])52    for p in op["params"]:53        rid = (ep, p["name"], p["in"])54        if rid in seen:55            continue56        seen.add(rid)57        rows_by_family[fam].append({58            "provider": "openai", "endpoint": ep, "parameter": p["name"], "location": p["in"],59            "type": norm_type(p["type"]), "required": bool(p["required"]), "default": p["default"],60            "minimum": None, "maximum": None, "enum": p["enum"], "description": p["description"].strip(),61            "compatible_models": [], "beta_header": None, "status": status, "source": SRC[fam],62        })63    for r in op.get("body_flat", []):64        rid = (ep, r["parameter"], "body", json.dumps(r.get("enum")), r.get("type"))65        if rid in seen:66            continue67        seen.add(rid)68        desc = r.get("description", "").strip()69        if r.get("x-ref"):70            desc = (desc + f" [schema: {r['x-ref']}]").strip()71        rows_by_family[fam].append({72            "provider": "openai", "endpoint": ep, "parameter": r["parameter"],73            "location": "body" if op["body_content_type"] != "multipart/form-data" else "body(multipart)",74            "type": norm_type(r.get("type")), "required": bool(r["required"]), "default": r.get("default"),75            "minimum": r.get("minimum"), "maximum": r.get("maximum"), "enum": r.get("enum"), "description": desc,76            "compatible_models": [], "beta_header": None, "status": status, "source": SRC[fam],77        })7879# ---- curated additions / corrections (facts from guides, not in the spec)80def add(fam, ep, param, loc, typ, req, desc, **kw):81    row = {"provider": "openai", "endpoint": ep, "parameter": param, "location": loc, "type": typ, "required": req,82           "default": kw.get("default"), "minimum": kw.get("minimum"), "maximum": kw.get("maximum"), "enum": kw.get("enum"),83           "description": desc, "compatible_models": kw.get("models", []), "beta_header": None,84           "status": kw.get("status", ["DOCUMENTED"] + DEPRECATION.get(fam, [])), "source": kw.get("source", SRC[fam])}85    rows_by_family[fam].append(row)8687add("files", "POST /v1/files", "file", "body(multipart)", "binary", True,88    "File bytes (multipart field). Max 512 MB per file; project storage cap 2.5 TB; upload endpoint rate limit 1,000 req/min per user. "89    "Batch inputs must be .jsonl <= 200 MB; fine-tune inputs must be .jsonl; vector-store ingestion needs a supported MIME type and <= 5M tokens/file.",90    source=REF + "files/methods/create", status=["DOCUMENTED", "LIVE_VERIFIED"])91add("files", "POST /v1/files", "purpose", "body(multipart)", "string", True,92    "Intended purpose. `evals` is accepted by the API (used by the Evals guide) although the OpenAPI enum only lists assistants|batch|fine-tune|vision|user_data|evals; "93    "response-side purposes also include assistants_output, batch_output, fine-tune-results. Files with purpose=batch expire after 30 days by default.",94    enum=["assistants", "batch", "fine-tune", "vision", "user_data", "evals"], source=REF + "files/methods/create", status=["DOCUMENTED", "LIVE_VERIFIED"])95add("vector_stores", "POST /v1/vector_stores/{vector_store_id}/files", "attributes.<key>", "body", "string | number | boolean", False,96    "Attribute map used by search filters: at most 16 keys, each key and string value <= 256 characters (Retrieval guide).",97    maximum=16, source="https://developers.openai.com/api/docs/guides/retrieval#attributes")98add("vector_stores", "POST /v1/vector_stores/{vector_store_id}/search", "ranking_options.hybrid_search.embedding_weight", "body", "number", False,99    "Reciprocal-rank-fusion weight for semantic (embedding) matches (alias rrf_embedding_weight). At least one of the two weights must be > 0. Documented in the Retrieval guide only (not in the OpenAPI spec).",100    source="https://developers.openai.com/api/docs/guides/retrieval#ranking", status=["DOCUMENTED", "UNVERIFIED"])101add("vector_stores", "POST /v1/vector_stores/{vector_store_id}/search", "ranking_options.hybrid_search.text_weight", "body", "number", False,102    "Reciprocal-rank-fusion weight for sparse keyword matches (alias rrf_text_weight). Documented in the Retrieval guide only.",103    source="https://developers.openai.com/api/docs/guides/retrieval#ranking", status=["DOCUMENTED", "UNVERIFIED"])104add("fine_tuning", "POST /v1/fine_tuning/jobs", "method.reinforcement.response_format", "body", "object", False,105    "Structured-output format applied to samples during RFT training ({type: json_schema, json_schema: {name, strict, schema}}); used by the RFT guide, "106    "populates sample.output_json for graders. Present in guide/reference examples; not in the FineTuneReinforcementMethod spec properties.",107    source="https://developers.openai.com/api/docs/guides/reinforcement-fine-tuning", status=["DOCUMENTED", "UNVERIFIED", "DEPRECATED"])108add("fine_tuning", "POST /v1/fine_tuning/jobs", "method.dpo.hyperparameters.beta", "body", "number | 'auto'", False,109    "DPO only. Float in [0, 2]: higher = more conservative (stick to reference behaviour), lower = follow the preferences more aggressively. Default auto.",110    default="auto", minimum=0, maximum=2, source="https://developers.openai.com/api/docs/guides/direct-preference-optimization")111add("fine_tuning", "POST /v1/fine_tuning/jobs", "model", "body", "string", True,112    "Base model or previously fine-tuned model id. Per model pages (2026-09-18) fine-tuning is 'Supported' for: gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, gpt-4o, gpt-4o-mini, o4-mini (RFT), gpt-4, gpt-3.5-turbo (legacy). "113    "Guides pin snapshots: SFT/DPO -> gpt-4.1-2025-04-14 / -mini / -nano; vision SFT -> gpt-4o-2024-08-06; RFT -> o4-mini-2025-04-16. No gpt-5.x model supports fine-tuning.",114    models=["gpt-4.1-2025-04-14", "gpt-4.1-mini-2025-04-14", "gpt-4.1-nano-2025-04-14", "gpt-4o-2024-08-06", "gpt-4o-mini-2024-07-18", "o4-mini-2025-04-16", "gpt-4", "gpt-3.5-turbo"],115    source="https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods")116# training-file line formats (documented as pseudo-parameters of the training file)117add("fine_tuning", "training_file (JSONL line, method=supervised)", "messages[]", "file", "array<message>", True,118    "Chat-format example: system/developer, user, assistant, tool messages; assistant messages may carry tool_calls and `weight` (0|1) to exclude a turn from the loss. Optional top-level `tools[]`, `parallel_tool_calls`, `functions[]`(legacy). Min 10 lines.",119    source="https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data")120add("fine_tuning", "training_file (JSONL line, method=supervised)", "messages[].weight", "file", "integer", False,121    "Assistant messages only. 0 = do not train on this message, 1 = train (default).", enum=[0, 1], source=REF + "fine_tuning")122add("fine_tuning", "training_file (JSONL line, method=dpo)", "input.messages[]", "file", "array<message>", True,123    "Prompt messages (one-turn conversations only). Optional input.tools[], input.parallel_tool_calls.",124    source="https://developers.openai.com/api/docs/guides/direct-preference-optimization#data-format")125add("fine_tuning", "training_file (JSONL line, method=dpo)", "preferred_output[]", "file", "array<assistant message>", True,126    "Ideal assistant response (must be the last assistant message).", source="https://developers.openai.com/api/docs/guides/direct-preference-optimization#data-format")127add("fine_tuning", "training_file (JSONL line, method=dpo)", "non_preferred_output[]", "file", "array<assistant message>", True,128    "Suboptimal assistant response.", source="https://developers.openai.com/api/docs/guides/direct-preference-optimization#data-format")129add("fine_tuning", "training_file (JSONL line, method=reinforcement)", "messages[]", "file", "array<message>", True,130    "Prompt messages; every other top-level key of the line is exposed to the grader as `item.<key>` (e.g. reference_answer). No assistant answer is required.",131    source="https://developers.openai.com/api/docs/guides/reinforcement-fine-tuning#prepare-your-dataset")132add("fine_tuning", "training_file (JSONL line, vision SFT)", "messages[].content[].image_url", "file", "object", False,133    "Image inputs as HTTP URL or data URL; JPEG/PNG/WEBP, RGB/RGBA, <= 10 MB each, <= 10 images per example, <= 50,000 image examples per file; `detail: low` -> 85 tokens/image. No images in assistant messages.",134    source="https://developers.openai.com/api/docs/guides/vision-fine-tuning#image-data-requirements")135add("batches", "input_file (JSONL line)", "custom_id", "file", "string", True, "Unique per line; echoed in the output/error lines (output order is not guaranteed).",136    source="https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file", status=["DOCUMENTED", "LIVE_VERIFIED"])137add("batches", "input_file (JSONL line)", "method", "file", "string", True, "Always POST.", enum=["POST"],138    source="https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file", status=["DOCUMENTED", "LIVE_VERIFIED"])139add("batches", "input_file (JSONL line)", "url", "file", "string", True, "Must equal the batch `endpoint`; all lines target one endpoint and one model.",140    enum=["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions", "/v1/moderations", "/v1/images/generations", "/v1/images/edits", "/v1/videos"],141    source="https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file", status=["DOCUMENTED", "LIVE_VERIFIED"])142add("batches", "input_file (JSONL line)", "body", "file", "object", True, "Same JSON body as the synchronous endpoint (stream must be false/absent; videos/images must be JSON not multipart, assets referenced by file_id/image_url).",143    source="https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file", status=["DOCUMENTED", "LIVE_VERIFIED"])144145for fam, rows in rows_by_family.items():146    out = ROOT / "generated/fragments/parameters" / f"{OUTNAME[fam]}.json"147    out.parent.mkdir(parents=True, exist_ok=True)148    out.write_text(json.dumps(rows, indent=1, ensure_ascii=False) + "\n")149    print(out.name, len(rows))150151# ---------------------------------------------------------------- objects152OBJECTS = [153    ("File", "OpenAIFile", "files", "file"), ("Upload", "Upload", "uploads", "upload"), ("UploadPart", "UploadPart", "uploads", "upload.part"),154    ("VectorStore", "VectorStoreObject", "vector_stores", "vector_store"), ("VectorStoreFile", "VectorStoreFileObject", "vector_stores", "vector_store.file"),155    ("VectorStoreFileBatch", "VectorStoreFileBatchObject", "vector_stores", "vector_store.files_batch"),156    ("VectorStoreSearchResultsPage", "VectorStoreSearchResultsPage", "vector_stores", "vector_store.search_results.page"),157    ("VectorStoreFileContentPage", "VectorStoreFileContentResponse", "vector_stores", "vector_store.file_content.page"),158    ("Batch", "Batch", "batches", "batch"), ("BatchRequestCounts", "BatchRequestCounts", "batches", None), ("BatchError", "BatchError", "batches", None),159    ("FineTuningJob", "FineTuningJob", "fine_tuning", "fine_tuning.job"), ("FineTuningJobEvent", "FineTuningJobEvent", "fine_tuning", "fine_tuning.job.event"),160    ("FineTuningJobCheckpoint", "FineTuningJobCheckpoint", "fine_tuning", "fine_tuning.job.checkpoint"),161    ("CheckpointPermission", "FineTuningCheckpointPermission", "fine_tuning", "checkpoint.permission"),162    ("GraderStringCheck", "GraderStringCheck", "graders", None), ("GraderTextSimilarity", "GraderTextSimilarity", "graders", None),163    ("GraderScoreModel", "GraderScoreModel", "graders", None), ("GraderLabelModel", "GraderLabelModel", "graders", None),164    ("GraderPython", "GraderPython", "graders", None), ("GraderMulti", "GraderMulti", "graders", None),165    ("RunGraderResponse", "RunGraderResponse", "graders", None), ("ValidateGraderResponse", "ValidateGraderResponse", "graders", None),166    ("Eval", "Eval", "evals", "eval"), ("EvalRun", "EvalRun", "evals", "eval.run"), ("EvalRunOutputItem", "EvalRunOutputItem", "evals", "eval.run.output_item"),167    ("EvalRunOutputItemResult", "EvalRunOutputItemResult", "evals", None), ("EvalApiError", "EvalApiError", "evals", None),168    ("ListFilesResponse", "ListFilesResponse", "files", "list"), ("ListBatchesResponse", "ListBatchesResponse", "batches", "list"),169    ("EvalList", "EvalList", "evals", "list"), ("EvalRunList", "EvalRunList", "evals", "list"), ("EvalRunOutputItemList", "EvalRunOutputItemList", "evals", "list"),170    ("ComparisonFilter", "ComparisonFilter", "vector_stores", None), ("CompoundFilter", "CompoundFilter", "vector_stores", None),171    ("StaticChunkingStrategy", "StaticChunkingStrategy", "vector_stores", None), ("VectorStoreExpirationAfter", "VectorStoreExpirationAfter", "vector_stores", None),172    ("FileExpirationAfter", "FileExpirationAfter", "files", None), ("BatchFileExpirationAfter", "BatchFileExpirationAfter", "batches", None),173    ("FineTuneMethod", "FineTuneMethod", "fine_tuning", None), ("FineTuneSupervisedHyperparameters", "FineTuneSupervisedHyperparameters", "fine_tuning", None),174    ("FineTuneDPOHyperparameters", "FineTuneDPOHyperparameters", "fine_tuning", None), ("FineTuneReinforcementHyperparameters", "FineTuneReinforcementHyperparameters", "fine_tuning", None),175    ("FineTuneChatCompletionRequestAssistantMessage", "FineTuneChatCompletionRequestAssistantMessage", "fine_tuning", None),176    ("EvalCustomDataSourceConfig", "EvalCustomDataSourceConfig", "evals", None), ("EvalLogsDataSourceConfig", "EvalLogsDataSourceConfig", "evals", None),177    ("EvalStoredCompletionsDataSourceConfig", "EvalStoredCompletionsDataSourceConfig", "evals", None),178    ("EvalJsonlFileContentSource", "EvalJsonlFileContentSource", "evals", None), ("EvalJsonlFileIdSource", "EvalJsonlFileIdSource", "evals", None),179    ("EvalStoredCompletionsSource", "EvalStoredCompletionsSource", "evals", None), ("EvalResponsesSource", "EvalResponsesSource", "evals", None),180]181objs = []182for name, schema_name, fam, obj_type in OBJECTS:183    sch = X.schemas.get(schema_name)184    if sch is None:185        print("missing schema", schema_name); continue186    resolved = X.deref(sch)187    flat = X.flatten(resolved)188    fields = []189    for r in flat:190        if r["parameter"].count(".") > 3:191            continue192        fields.append({k: r.get(k) for k in ("parameter", "type", "required", "enum", "default", "minimum", "maximum", "nullable") if r.get(k) not in (None, False, "")} |193                      {"parameter": r["parameter"], "type": r.get("type") or "object", "description": (r.get("description") or "")[:300].strip()})194    objs.append({195        "provider": "openai", "api_family": fam, "name": name, "spec_schema": schema_name, "object_type": obj_type,196        "description": (resolved.get("description") or "").strip()[:500], "required": resolved.get("required") or [],197        "fields": fields, "status": ["DOCUMENTED"] + DEPRECATION.get(fam, []), "verification": {"method": "docs_only"},198        "sources": [{"url": SRC[fam], "retrieved_at": RETRIEVED}, {"url": "https://developers.openai.com/api/openapi (openapi-master.yaml)", "retrieved_at": RETRIEVED}],199    })200# pseudo-objects for JSONL lines (not named in the spec)201objs.append({202    "provider": "openai", "api_family": "batches", "name": "BatchRequestInputLine", "spec_schema": None, "object_type": None,203    "description": "One line of the batch input JSONL file.", "required": ["custom_id", "method", "url", "body"],204    "fields": [{"parameter": "custom_id", "type": "string", "required": True, "description": "Unique id per line, echoed in output."},205               {"parameter": "method", "type": "string", "required": True, "enum": ["POST"], "description": "HTTP method."},206               {"parameter": "url", "type": "string", "required": True, "description": "Target endpoint path; must match batch.endpoint."},207               {"parameter": "body", "type": "object", "required": True, "description": "Request body of the target endpoint."}],208    "status": ["DOCUMENTED"], "verification": {"method": "docs_only"},209    "sources": [{"url": "https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file", "retrieved_at": RETRIEVED}],210})211objs.append({212    "provider": "openai", "api_family": "batches", "name": "BatchRequestOutputLine", "spec_schema": None, "object_type": None,213    "description": "One line of the batch output (output_file_id) or error (error_file_id) JSONL file. Exactly one of response/error is non-null.",214    "required": ["id", "custom_id", "response", "error"],215    "fields": [{"parameter": "id", "type": "string", "required": True, "description": "batch_req_… id."},216               {"parameter": "custom_id", "type": "string", "required": True, "description": "Echo of the input custom_id."},217               {"parameter": "response", "type": "object | null", "required": True, "description": "{status_code, request_id, body} — body is the normal endpoint response."},218               {"parameter": "response.status_code", "type": "integer", "required": True, "description": "HTTP status of the individual request."},219               {"parameter": "response.request_id", "type": "string", "required": True, "description": "req_… id usable with support."},220               {"parameter": "response.body", "type": "object", "required": True, "description": "Endpoint response object (e.g. Response, ChatCompletion, embeddings list)."},221               {"parameter": "error", "type": "object | null", "required": True, "description": "{code, message}; e.g. code=batch_expired for requests cancelled at the 24h window."}],222    "status": ["DOCUMENTED"], "verification": {"method": "docs_only"},223    "sources": [{"url": "https://developers.openai.com/api/docs/guides/batch#5-retrieve-the-results", "retrieved_at": RETRIEVED}],224})225out = ROOT / "generated/fragments/objects/openai-platform-objects.json"226out.parent.mkdir(parents=True, exist_ok=True)227out.write_text(json.dumps(objs, indent=1, ensure_ascii=False) + "\n")228print(out.name, len(objs))229