#!/usr/bin/env python3 """Generate parameter + object fragments for the platform domain from the OpenAPI spec (+ curated overrides). Writes generated/fragments/parameters/openai-{files,uploads,vector-stores,batch,fine-tuning,graders,evals}.json and generated/fragments/objects/openai-platform-objects.json """ from __future__ import annotations import json, sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "tmp")) import platform_extract as X # noqa: E402 (re-runs extraction, cheap) RETRIEVED = "2026-09-18" REF = "https://developers.openai.com/api/reference/resources/" SRC = { "files": REF + "files", "uploads": REF + "uploads", "vector_stores": REF + "vector_stores", "batches": REF + "batches", "fine_tuning": REF + "fine_tuning", "graders": REF + "graders", "evals": REF + "evals", } DEPRECATION = { "fine_tuning": ["DEPRECATED"], # self-serve fine-tuning winds down (Jan 6 2027 job creation cutoff) "graders": ["DEPRECATED"], # graders deprecated with evals + fine-tuning (Nov 30 2026 / Jan 6 2027) "evals": ["DEPRECATED"], # Evals API shuts down Nov 30 2026 (read-only Oct 31 2026) } FAMILY = { # api_family per path prefix "/files": "files", "/uploads": "uploads", "/vector_stores": "vector_stores", "/batches": "batches", "/fine_tuning/alpha/graders": "graders", "/fine_tuning": "fine_tuning", "/evals": "evals", } OUTNAME = {"files": "openai-files", "uploads": "openai-uploads", "vector_stores": "openai-vector-stores", "batches": "openai-batch", "fine_tuning": "openai-fine-tuning", "graders": "openai-graders", "evals": "openai-evals"} def family(path: str) -> str: for p, f in FAMILY.items(): if path.startswith(p): return f raise KeyError(path) def norm_type(t): if t is None: return "object" return t rows_by_family: dict[str, list[dict]] = {k: [] for k in OUTNAME} seen = set() for key, op in X.summary.items(): method, path = key.split(" ", 1) fam = family(path) ep = f"{method} /v1{path}" status = ["DOCUMENTED"] + DEPRECATION.get(fam, []) for p in op["params"]: rid = (ep, p["name"], p["in"]) if rid in seen: continue seen.add(rid) rows_by_family[fam].append({ "provider": "openai", "endpoint": ep, "parameter": p["name"], "location": p["in"], "type": norm_type(p["type"]), "required": bool(p["required"]), "default": p["default"], "minimum": None, "maximum": None, "enum": p["enum"], "description": p["description"].strip(), "compatible_models": [], "beta_header": None, "status": status, "source": SRC[fam], }) for r in op.get("body_flat", []): rid = (ep, r["parameter"], "body", json.dumps(r.get("enum")), r.get("type")) if rid in seen: continue seen.add(rid) desc = r.get("description", "").strip() if r.get("x-ref"): desc = (desc + f" [schema: {r['x-ref']}]").strip() rows_by_family[fam].append({ "provider": "openai", "endpoint": ep, "parameter": r["parameter"], "location": "body" if op["body_content_type"] != "multipart/form-data" else "body(multipart)", "type": norm_type(r.get("type")), "required": bool(r["required"]), "default": r.get("default"), "minimum": r.get("minimum"), "maximum": r.get("maximum"), "enum": r.get("enum"), "description": desc, "compatible_models": [], "beta_header": None, "status": status, "source": SRC[fam], }) # ---- curated additions / corrections (facts from guides, not in the spec) def add(fam, ep, param, loc, typ, req, desc, **kw): row = {"provider": "openai", "endpoint": ep, "parameter": param, "location": loc, "type": typ, "required": req, "default": kw.get("default"), "minimum": kw.get("minimum"), "maximum": kw.get("maximum"), "enum": kw.get("enum"), "description": desc, "compatible_models": kw.get("models", []), "beta_header": None, "status": kw.get("status", ["DOCUMENTED"] + DEPRECATION.get(fam, [])), "source": kw.get("source", SRC[fam])} rows_by_family[fam].append(row) add("files", "POST /v1/files", "file", "body(multipart)", "binary", True, "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. " "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.", source=REF + "files/methods/create", status=["DOCUMENTED", "LIVE_VERIFIED"]) add("files", "POST /v1/files", "purpose", "body(multipart)", "string", True, "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; " "response-side purposes also include assistants_output, batch_output, fine-tune-results. Files with purpose=batch expire after 30 days by default.", enum=["assistants", "batch", "fine-tune", "vision", "user_data", "evals"], source=REF + "files/methods/create", status=["DOCUMENTED", "LIVE_VERIFIED"]) add("vector_stores", "POST /v1/vector_stores/{vector_store_id}/files", "attributes.", "body", "string | number | boolean", False, "Attribute map used by search filters: at most 16 keys, each key and string value <= 256 characters (Retrieval guide).", maximum=16, source="https://developers.openai.com/api/docs/guides/retrieval#attributes") add("vector_stores", "POST /v1/vector_stores/{vector_store_id}/search", "ranking_options.hybrid_search.embedding_weight", "body", "number", False, "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).", source="https://developers.openai.com/api/docs/guides/retrieval#ranking", status=["DOCUMENTED", "UNVERIFIED"]) add("vector_stores", "POST /v1/vector_stores/{vector_store_id}/search", "ranking_options.hybrid_search.text_weight", "body", "number", False, "Reciprocal-rank-fusion weight for sparse keyword matches (alias rrf_text_weight). Documented in the Retrieval guide only.", source="https://developers.openai.com/api/docs/guides/retrieval#ranking", status=["DOCUMENTED", "UNVERIFIED"]) add("fine_tuning", "POST /v1/fine_tuning/jobs", "method.reinforcement.response_format", "body", "object", False, "Structured-output format applied to samples during RFT training ({type: json_schema, json_schema: {name, strict, schema}}); used by the RFT guide, " "populates sample.output_json for graders. Present in guide/reference examples; not in the FineTuneReinforcementMethod spec properties.", source="https://developers.openai.com/api/docs/guides/reinforcement-fine-tuning", status=["DOCUMENTED", "UNVERIFIED", "DEPRECATED"]) add("fine_tuning", "POST /v1/fine_tuning/jobs", "method.dpo.hyperparameters.beta", "body", "number | 'auto'", False, "DPO only. Float in [0, 2]: higher = more conservative (stick to reference behaviour), lower = follow the preferences more aggressively. Default auto.", default="auto", minimum=0, maximum=2, source="https://developers.openai.com/api/docs/guides/direct-preference-optimization") add("fine_tuning", "POST /v1/fine_tuning/jobs", "model", "body", "string", True, "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). " "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.", 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"], source="https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods") # training-file line formats (documented as pseudo-parameters of the training file) add("fine_tuning", "training_file (JSONL line, method=supervised)", "messages[]", "file", "array", True, "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.", source="https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data") add("fine_tuning", "training_file (JSONL line, method=supervised)", "messages[].weight", "file", "integer", False, "Assistant messages only. 0 = do not train on this message, 1 = train (default).", enum=[0, 1], source=REF + "fine_tuning") add("fine_tuning", "training_file (JSONL line, method=dpo)", "input.messages[]", "file", "array", True, "Prompt messages (one-turn conversations only). Optional input.tools[], input.parallel_tool_calls.", source="https://developers.openai.com/api/docs/guides/direct-preference-optimization#data-format") add("fine_tuning", "training_file (JSONL line, method=dpo)", "preferred_output[]", "file", "array", True, "Ideal assistant response (must be the last assistant message).", source="https://developers.openai.com/api/docs/guides/direct-preference-optimization#data-format") add("fine_tuning", "training_file (JSONL line, method=dpo)", "non_preferred_output[]", "file", "array", True, "Suboptimal assistant response.", source="https://developers.openai.com/api/docs/guides/direct-preference-optimization#data-format") add("fine_tuning", "training_file (JSONL line, method=reinforcement)", "messages[]", "file", "array", True, "Prompt messages; every other top-level key of the line is exposed to the grader as `item.` (e.g. reference_answer). No assistant answer is required.", source="https://developers.openai.com/api/docs/guides/reinforcement-fine-tuning#prepare-your-dataset") add("fine_tuning", "training_file (JSONL line, vision SFT)", "messages[].content[].image_url", "file", "object", False, "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.", source="https://developers.openai.com/api/docs/guides/vision-fine-tuning#image-data-requirements") add("batches", "input_file (JSONL line)", "custom_id", "file", "string", True, "Unique per line; echoed in the output/error lines (output order is not guaranteed).", source="https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file", status=["DOCUMENTED", "LIVE_VERIFIED"]) add("batches", "input_file (JSONL line)", "method", "file", "string", True, "Always POST.", enum=["POST"], source="https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file", status=["DOCUMENTED", "LIVE_VERIFIED"]) add("batches", "input_file (JSONL line)", "url", "file", "string", True, "Must equal the batch `endpoint`; all lines target one endpoint and one model.", enum=["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions", "/v1/moderations", "/v1/images/generations", "/v1/images/edits", "/v1/videos"], source="https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file", status=["DOCUMENTED", "LIVE_VERIFIED"]) add("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).", source="https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file", status=["DOCUMENTED", "LIVE_VERIFIED"]) for fam, rows in rows_by_family.items(): out = ROOT / "generated/fragments/parameters" / f"{OUTNAME[fam]}.json" out.parent.mkdir(parents=True, exist_ok=True) out.write_text(json.dumps(rows, indent=1, ensure_ascii=False) + "\n") print(out.name, len(rows)) # ---------------------------------------------------------------- objects OBJECTS = [ ("File", "OpenAIFile", "files", "file"), ("Upload", "Upload", "uploads", "upload"), ("UploadPart", "UploadPart", "uploads", "upload.part"), ("VectorStore", "VectorStoreObject", "vector_stores", "vector_store"), ("VectorStoreFile", "VectorStoreFileObject", "vector_stores", "vector_store.file"), ("VectorStoreFileBatch", "VectorStoreFileBatchObject", "vector_stores", "vector_store.files_batch"), ("VectorStoreSearchResultsPage", "VectorStoreSearchResultsPage", "vector_stores", "vector_store.search_results.page"), ("VectorStoreFileContentPage", "VectorStoreFileContentResponse", "vector_stores", "vector_store.file_content.page"), ("Batch", "Batch", "batches", "batch"), ("BatchRequestCounts", "BatchRequestCounts", "batches", None), ("BatchError", "BatchError", "batches", None), ("FineTuningJob", "FineTuningJob", "fine_tuning", "fine_tuning.job"), ("FineTuningJobEvent", "FineTuningJobEvent", "fine_tuning", "fine_tuning.job.event"), ("FineTuningJobCheckpoint", "FineTuningJobCheckpoint", "fine_tuning", "fine_tuning.job.checkpoint"), ("CheckpointPermission", "FineTuningCheckpointPermission", "fine_tuning", "checkpoint.permission"), ("GraderStringCheck", "GraderStringCheck", "graders", None), ("GraderTextSimilarity", "GraderTextSimilarity", "graders", None), ("GraderScoreModel", "GraderScoreModel", "graders", None), ("GraderLabelModel", "GraderLabelModel", "graders", None), ("GraderPython", "GraderPython", "graders", None), ("GraderMulti", "GraderMulti", "graders", None), ("RunGraderResponse", "RunGraderResponse", "graders", None), ("ValidateGraderResponse", "ValidateGraderResponse", "graders", None), ("Eval", "Eval", "evals", "eval"), ("EvalRun", "EvalRun", "evals", "eval.run"), ("EvalRunOutputItem", "EvalRunOutputItem", "evals", "eval.run.output_item"), ("EvalRunOutputItemResult", "EvalRunOutputItemResult", "evals", None), ("EvalApiError", "EvalApiError", "evals", None), ("ListFilesResponse", "ListFilesResponse", "files", "list"), ("ListBatchesResponse", "ListBatchesResponse", "batches", "list"), ("EvalList", "EvalList", "evals", "list"), ("EvalRunList", "EvalRunList", "evals", "list"), ("EvalRunOutputItemList", "EvalRunOutputItemList", "evals", "list"), ("ComparisonFilter", "ComparisonFilter", "vector_stores", None), ("CompoundFilter", "CompoundFilter", "vector_stores", None), ("StaticChunkingStrategy", "StaticChunkingStrategy", "vector_stores", None), ("VectorStoreExpirationAfter", "VectorStoreExpirationAfter", "vector_stores", None), ("FileExpirationAfter", "FileExpirationAfter", "files", None), ("BatchFileExpirationAfter", "BatchFileExpirationAfter", "batches", None), ("FineTuneMethod", "FineTuneMethod", "fine_tuning", None), ("FineTuneSupervisedHyperparameters", "FineTuneSupervisedHyperparameters", "fine_tuning", None), ("FineTuneDPOHyperparameters", "FineTuneDPOHyperparameters", "fine_tuning", None), ("FineTuneReinforcementHyperparameters", "FineTuneReinforcementHyperparameters", "fine_tuning", None), ("FineTuneChatCompletionRequestAssistantMessage", "FineTuneChatCompletionRequestAssistantMessage", "fine_tuning", None), ("EvalCustomDataSourceConfig", "EvalCustomDataSourceConfig", "evals", None), ("EvalLogsDataSourceConfig", "EvalLogsDataSourceConfig", "evals", None), ("EvalStoredCompletionsDataSourceConfig", "EvalStoredCompletionsDataSourceConfig", "evals", None), ("EvalJsonlFileContentSource", "EvalJsonlFileContentSource", "evals", None), ("EvalJsonlFileIdSource", "EvalJsonlFileIdSource", "evals", None), ("EvalStoredCompletionsSource", "EvalStoredCompletionsSource", "evals", None), ("EvalResponsesSource", "EvalResponsesSource", "evals", None), ] objs = [] for name, schema_name, fam, obj_type in OBJECTS: sch = X.schemas.get(schema_name) if sch is None: print("missing schema", schema_name); continue resolved = X.deref(sch) flat = X.flatten(resolved) fields = [] for r in flat: if r["parameter"].count(".") > 3: continue 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, "")} | {"parameter": r["parameter"], "type": r.get("type") or "object", "description": (r.get("description") or "")[:300].strip()}) objs.append({ "provider": "openai", "api_family": fam, "name": name, "spec_schema": schema_name, "object_type": obj_type, "description": (resolved.get("description") or "").strip()[:500], "required": resolved.get("required") or [], "fields": fields, "status": ["DOCUMENTED"] + DEPRECATION.get(fam, []), "verification": {"method": "docs_only"}, "sources": [{"url": SRC[fam], "retrieved_at": RETRIEVED}, {"url": "https://developers.openai.com/api/openapi (openapi-master.yaml)", "retrieved_at": RETRIEVED}], }) # pseudo-objects for JSONL lines (not named in the spec) objs.append({ "provider": "openai", "api_family": "batches", "name": "BatchRequestInputLine", "spec_schema": None, "object_type": None, "description": "One line of the batch input JSONL file.", "required": ["custom_id", "method", "url", "body"], "fields": [{"parameter": "custom_id", "type": "string", "required": True, "description": "Unique id per line, echoed in output."}, {"parameter": "method", "type": "string", "required": True, "enum": ["POST"], "description": "HTTP method."}, {"parameter": "url", "type": "string", "required": True, "description": "Target endpoint path; must match batch.endpoint."}, {"parameter": "body", "type": "object", "required": True, "description": "Request body of the target endpoint."}], "status": ["DOCUMENTED"], "verification": {"method": "docs_only"}, "sources": [{"url": "https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file", "retrieved_at": RETRIEVED}], }) objs.append({ "provider": "openai", "api_family": "batches", "name": "BatchRequestOutputLine", "spec_schema": None, "object_type": None, "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.", "required": ["id", "custom_id", "response", "error"], "fields": [{"parameter": "id", "type": "string", "required": True, "description": "batch_req_… id."}, {"parameter": "custom_id", "type": "string", "required": True, "description": "Echo of the input custom_id."}, {"parameter": "response", "type": "object | null", "required": True, "description": "{status_code, request_id, body} — body is the normal endpoint response."}, {"parameter": "response.status_code", "type": "integer", "required": True, "description": "HTTP status of the individual request."}, {"parameter": "response.request_id", "type": "string", "required": True, "description": "req_… id usable with support."}, {"parameter": "response.body", "type": "object", "required": True, "description": "Endpoint response object (e.g. Response, ChatCompletion, embeddings list)."}, {"parameter": "error", "type": "object | null", "required": True, "description": "{code, message}; e.g. code=batch_expired for requests cancelled at the 24h window."}], "status": ["DOCUMENTED"], "verification": {"method": "docs_only"}, "sources": [{"url": "https://developers.openai.com/api/docs/guides/batch#5-retrieve-the-results", "retrieved_at": RETRIEVED}], }) out = ROOT / "generated/fragments/objects/openai-platform-objects.json" out.parent.mkdir(parents=True, exist_ok=True) out.write_text(json.dumps(objs, indent=1, ensure_ascii=False) + "\n") print(out.name, len(objs))