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%
6.2 KB · 122 lines python
Raw Blame History
1#!/usr/bin/env python32"""Dump the OpenAPI paths + resolved schemas for the platform domain into tmp/platform-spec/.3Usage: python3 tmp/platform_extract.py4"""5import json, re, sys, yaml6from pathlib import Path78ROOT = Path(__file__).resolve().parent.parent9SPEC = ROOT / "sources/openai/openapi/openapi-master.yaml"10OUT = ROOT / "tmp/platform-spec"11OUT.mkdir(parents=True, exist_ok=True)12spec = yaml.safe_load(SPEC.read_text())13schemas = spec["components"]["schemas"]14PREFIXES = ("/files", "/uploads", "/vector_stores", "/batches", "/fine_tuning", "/evals")1516ops = {}17for path, item in spec["paths"].items():18    if not path.startswith(PREFIXES):19        continue20    for method, op in item.items():21        if method not in ("get", "post", "delete", "patch", "put"):22            continue23        ops[f"{method.upper()} {path}"] = op2425def deref(node, depth=0, seen=None):26    """Resolve $ref recursively, keep names in 'x-ref'."""27    seen = seen or set()28    if isinstance(node, dict):29        if "$ref" in node:30            name = node["$ref"].split("/")[-1]31            if name in seen or depth > 12:32                return {"x-ref": name}33            sub = deref(schemas[name], depth + 1, seen | {name})34            if isinstance(sub, dict):35                sub = {"x-ref": name, **sub}36            return sub37        return {k: deref(v, depth + 1, seen) for k, v in node.items()}38    if isinstance(node, list):39        return [deref(v, depth + 1, seen) for v in node]40    return node4142def flatten(schema, prefix="", out=None, required_parent=None, depth=0):43    """Flatten a resolved schema into dotted parameter rows."""44    out = out if out is not None else []45    if not isinstance(schema, dict) or depth > 10:46        return out47    for comb in ("oneOf", "anyOf", "allOf"):48        if comb in schema:49            for alt in schema[comb]:50                flatten(alt, prefix, out, required_parent, depth + 1)51    props = schema.get("properties", {})52    req = set(schema.get("required", []))53    for name, sub in props.items():54        p = f"{prefix}{name}"55        typ = sub.get("type")56        if not typ and ("oneOf" in sub or "anyOf" in sub):57            alts = sub.get("oneOf") or sub.get("anyOf")58            typ = " | ".join(str(a.get("type") or a.get("x-ref") or "object") for a in alts)59        if typ == "array":60            it = sub.get("items", {})61            typ = f"array<{it.get('type') or it.get('x-ref') or 'object'}>"62        row = {63            "parameter": p, "type": typ, "required": name in req,64            "default": sub.get("default"), "minimum": sub.get("minimum"), "maximum": sub.get("maximum"),65            "enum": sub.get("enum"), "description": (sub.get("description") or "")[:600],66            "nullable": sub.get("nullable"), "x-ref": sub.get("x-ref"),67        }68        out.append({k: v for k, v in row.items() if v not in (None, False, "") or k in ("parameter", "type", "required")})69        # nested70        if sub.get("type") == "object" or "properties" in sub or "oneOf" in sub or "anyOf" in sub or "allOf" in sub:71            flatten(sub, p + ".", out, None, depth + 1)72        if sub.get("type") == "array" and isinstance(sub.get("items"), dict):73            flatten(sub["items"], p + "[].", out, None, depth + 1)74    return out7576summary = {}77for key, op in ops.items():78    rec = {"operationId": op.get("operationId"), "summary": op.get("summary"),79           "description": (op.get("description") or "")[:800],80           "params": [], "body_schema": None, "body_content_type": None, "responses": {}}81    for prm in op.get("parameters", []):82        prm = deref(prm)83        sch = prm.get("schema", {})84        rec["params"].append({"name": prm["name"], "in": prm["in"], "required": prm.get("required", False),85                              "type": sch.get("type"), "default": sch.get("default"), "enum": sch.get("enum"),86                              "description": (prm.get("description") or "")[:400]})87    rb = op.get("requestBody")88    if rb:89        content = rb["content"]90        ct = list(content.keys())[0]91        rec["body_content_type"] = ct92        sch = content[ct]["schema"]93        rec["body_ref"] = sch.get("$ref", "").split("/")[-1] or None94        resolved = deref(sch)95        rec["body_schema"] = resolved96        rec["body_flat"] = flatten(resolved)97    for code, resp in op.get("responses", {}).items():98        c = resp.get("content", {})99        if c:100            ct = list(c.keys())[0]101            s = c[ct]["schema"]102            rec["responses"][code] = {"content_type": ct, "ref": s.get("$ref", "").split("/")[-1] or s.get("type")}103    summary[key] = rec104    fn = re.sub(r"[^A-Za-z0-9]+", "_", key).strip("_")105    (OUT / f"{fn}.json").write_text(json.dumps(rec, indent=1, default=str))106107(OUT / "_index.json").write_text(json.dumps(108    {k: {"operationId": v["operationId"], "summary": v["summary"], "body_ref": v.get("body_ref"),109         "body_ct": v["body_content_type"], "responses": v["responses"], "params": [p["name"] for p in v["params"]]}110     for k, v in summary.items()}, indent=1))111112# Object schemas of interest113OBJ = [s for s in schemas if re.search(r"^(OpenAIFile|Upload|UploadPart|VectorStore|VectorStoreFile|VectorStoreFileBatch|VectorStoreSearchResult|Batch|BatchRequestInput|BatchRequestOutput|FineTuningJob|FineTuningJobEvent|FineTuningJobCheckpoint|FineTuningCheckpointPermission|Grader|Eval|EvalRun|EvalRunOutputItem|FineTuneMethod|FineTuneSupervisedMethod|FineTuneDPOMethod|FineTuneReinforcementMethod|FineTune.*Hyperparameters|StaticChunking|AutoChunking|ChunkingStrategy|ComparisonFilter|CompoundFilter|VectorStoreExpirationAfter|FileExpirationAfter|ListFilesResponse|ListBatchesResponse|CreateEvalRequest|CreateEvalRunRequest|EvalItem|EvalJsonl|EvalStoredCompletions|EvalLogs|EvalCustomDataSourceConfig|EvalResponses|EvalCompletions|FineTuneChatRequestInput|FineTunePreferenceRequestInput|FineTuneReinforcementRequestInput|FineTuneChatCompletionRequestAssistantMessage|RunGrader|ValidateGrader).*", s)]114objs = {}115for name in OBJ:116    r = deref(schemas[name])117    objs[name] = {"schema": r, "flat": flatten(r)}118    fn = re.sub(r"[^A-Za-z0-9]+", "_", name)119    (OUT / f"schema_{fn}.json").write_text(json.dumps(objs[name], indent=1, default=str))120(OUT / "_schemas_index.txt").write_text("\n".join(sorted(OBJ)))121print(len(ops), "ops;", len(OBJ), "schemas ->", OUT)122