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%
61.4 KB · 563 lines python
Raw Blame History
1#!/usr/bin/env python32"""Build the OpenAI *tools* fragments (tools, parameters, streaming events, containers+skills endpoints).34Inputs : sources/openai/openapi/openapi-master.yaml (schemas), model pages "Supported tools" sections,5         live findings hard-coded below (from tmp-live/tools/*.json, run 2026-09-18).6Outputs: generated/fragments/tools/openai-tools.json7         generated/fragments/parameters/openai-tools.json8         generated/fragments/streaming-events/openai-tools.json9         generated/fragments/endpoints/openai-containers-skills.json10Run    : .venv/bin/python scripts/build_openai_tools_fragments.py11"""12from __future__ import annotations1314import glob15import json16import re17from pathlib import Path1819import yaml2021ROOT = Path(__file__).resolve().parent.parent22SPEC = ROOT / "sources/openai/openapi/openapi-master.yaml"23FRAG = ROOT / "generated/fragments"24RETRIEVED = "2026-09-18"25VERIFIED = "2026-09-18"26DOCS = "https://developers.openai.com/api/docs"27REF = "https://developers.openai.com/api/reference"2829spec = yaml.load(SPEC.read_text(), Loader=yaml.CSafeLoader)30S = spec["components"]["schemas"]313233def resolve(o, depth=0, seen=()):34    """Inline $refs (bounded) so fragments are self-contained."""35    if depth > 14:36        return {"$comment": "depth-limited"}37    if isinstance(o, dict):38        if "$ref" in o:39            n = o["$ref"].split("/")[-1]40            if n in seen:41                return {"$ref": n, "$comment": "recursive"}42            return resolve(S[n], depth + 1, seen + (n,))43        return {k: resolve(v, depth + 1, seen) for k, v in o.items()44                if k not in ("x-oaiMeta", "x-stainless-naming", "x-oaiTypeLabel", "x-oaiExpandable")}45    if isinstance(o, list):46        return [resolve(x, depth + 1, seen) for x in o]47    return o484950def src(url: str) -> dict:51    return {"url": url, "retrieved_at": RETRIEVED}525354def ver(result: str, status: int, note: str, method: str = "live_api") -> dict:55    return {"method": method, "verified_at": VERIFIED, "result": result, "http_status": status, "request_note": note}565758# --------------------------------------------------------------------------- model matrix (from model pages)59MODEL_TOOL_KEY = {  # tool type -> key used in model pages "Supported tools"60    "function": "function_calling", "custom": "function_calling", "namespace": "function_calling",61    "web_search": "web_search", "web_search_2025_08_26": "web_search", "web_search_preview": "web_search",62    "web_search_preview_2025_03_11": "web_search", "file_search": "file_search", "code_interpreter": "code_interpreter",63    "image_generation": "image_generation", "mcp": "mcp", "shell": "hosted_shell", "apply_patch": "apply_patch",64    "computer": "computer_use", "computer_use_preview": "computer_use", "tool_search": "tool_search",65    "skill_reference": "skills",66}67matrix: dict[str, list[str]] = {}68for f in sorted(glob.glob(str(ROOT / "sources/openai/pages/api/docs/models/*.md"))):69    t = Path(f).read_text()70    m = re.search(r"## Supported tools\s*\n(.*?)(?=\n## |\Z)", t, re.S)71    if m:72        matrix[Path(f).stem] = re.findall(r"^- *([a-z_0-9]+)", m.group(1), re.M)737475def models_for(tool_type: str) -> list[str]:76    key = MODEL_TOOL_KEY.get(tool_type)77    return sorted(m for m, tools in matrix.items() if key in tools) if key else []787980# --------------------------------------------------------------------------- tool records81RESP = "POST /v1/responses"82CHAT = "POST /v1/chat/completions"83LIVE = ["DOCUMENTED", "LIVE_VERIFIED"]84NANO = "gpt-5.4-nano"8586TOOL_EVENTS = {87    "function": ["response.output_item.added", "response.function_call_arguments.delta", "response.function_call_arguments.done", "response.output_item.done"],88    "custom": ["response.output_item.added", "response.custom_tool_call_input.delta", "response.custom_tool_call_input.done", "response.output_item.done"],89    "web_search": ["response.output_item.added", "response.web_search_call.in_progress", "response.web_search_call.searching", "response.web_search_call.completed", "response.output_item.done", "response.output_text.annotation.added"],90    "file_search": ["response.output_item.added", "response.file_search_call.in_progress", "response.file_search_call.searching", "response.file_search_call.completed", "response.output_item.done", "response.output_text.annotation.added"],91    "code_interpreter": ["response.output_item.added", "response.code_interpreter_call.in_progress", "response.code_interpreter_call_code.delta", "response.code_interpreter_call_code.done", "response.code_interpreter_call.interpreting", "response.code_interpreter_call.completed", "response.output_item.done", "response.output_text.annotation.added"],92    "image_generation": ["response.output_item.added", "response.image_generation_call.in_progress", "response.image_generation_call.generating", "response.image_generation_call.partial_image", "response.image_generation_call.completed", "response.output_item.done"],93    "mcp": ["response.output_item.added", "response.mcp_list_tools.in_progress", "response.mcp_list_tools.completed", "response.mcp_list_tools.failed", "response.mcp_call.in_progress", "response.mcp_call_arguments.delta", "response.mcp_call_arguments.done", "response.mcp_call.completed", "response.mcp_call.failed", "response.output_item.done"],94    "shell": ["response.output_item.added", "response.shell_call_command.added", "response.shell_call_command.delta", "response.shell_call_command.done", "response.shell_call_output_content.delta", "response.shell_call_output_content.done", "response.output_item.done"],95    "apply_patch": ["response.output_item.added", "response.apply_patch_call_operation_diff.delta", "response.apply_patch_call_operation_diff.done", "response.output_item.done"],96    "computer_use_preview": ["response.output_item.added", "response.output_item.done"],97    "computer": ["response.output_item.added", "response.output_item.done"],98    "tool_search": ["response.output_item.added", "response.output_item.done"],99    "namespace": ["response.output_item.added", "response.function_call_arguments.delta", "response.function_call_arguments.done", "response.output_item.done"],100    "programmatic_tool_calling": ["response.output_item.added", "response.output_item.done"],101    "local_shell": [],102    "skill_reference": [],103}104105TOOL_SCHEMA = {106    "function": "FunctionTool", "custom": "CustomToolParam", "namespace": "NamespaceToolParam", "tool_search": "ToolSearchToolParam",107    "programmatic_tool_calling": "ProgrammaticToolCallingParam", "web_search": "WebSearchTool", "web_search_2025_08_26": "WebSearchTool",108    "web_search_preview": "WebSearchPreviewTool", "web_search_preview_2025_03_11": "WebSearchPreviewTool", "file_search": "FileSearchTool",109    "code_interpreter": "CodeInterpreterTool", "computer": "ComputerTool", "computer_use_preview": "ComputerUsePreviewTool",110    "image_generation": "ImageGenTool", "mcp": "MCPTool", "shell": "FunctionShellToolParam", "local_shell": "LocalShellToolParam",111    "apply_patch": "ApplyPatchToolParam", "skill_reference": "SkillReferenceParam",112}113RESULT_ITEM = {114    "function": ["FunctionToolCall", "FunctionCallOutputItemParam"], "custom": ["CustomToolCall", "CustomToolCallOutput"],115    "namespace": ["FunctionToolCall", "CustomToolCall"], "tool_search": ["ToolSearchCall", "ToolSearchOutput"],116    "programmatic_tool_calling": ["FunctionToolCall"], "web_search": ["WebSearchToolCall"], "web_search_2025_08_26": ["WebSearchToolCall"],117    "web_search_preview": ["WebSearchToolCall"], "web_search_preview_2025_03_11": ["WebSearchToolCall"], "file_search": ["FileSearchToolCall"],118    "code_interpreter": ["CodeInterpreterToolCall"], "computer": ["ComputerToolCall", "ComputerToolCallOutput"],119    "computer_use_preview": ["ComputerToolCall", "ComputerToolCallOutput"], "image_generation": ["ImageGenToolCall"],120    "mcp": ["MCPListTools", "MCPToolCall", "MCPApprovalRequest", "MCPApprovalResponse"], "shell": ["FunctionShellCall", "FunctionShellCallOutputItemParam"],121    "local_shell": ["LocalShellToolCall", "LocalShellToolCallOutput"], "apply_patch": ["ApplyPatchToolCall", "ApplyPatchToolCallOutputItemParam"],122    "skill_reference": ["FunctionShellCall"],123}124125TC_COMMON = {"modes": ["none", "auto", "required"], "allowed_tools": {"type": "allowed_tools", "mode": "auto|required", "tools": ["{type,name,...}"]}}126127T = {}  # per-tool hand-written metadata128129130def tool(type_, name, category, description, endpoints, tc, parallel, result_shape, billing, limitations, security, status, examples, verification, extra_sources=(), beta=None, notes=None, models=None):131    ex_dir = examples132    rec = {133        "provider": "openai", "name": name, "type": type_, "category": category, "description": description,134        "compatible_models": models if models is not None else models_for(type_),135        "compatible_endpoints": endpoints,136        "parameters_schema": resolve(S[TOOL_SCHEMA[type_]]),137        "tool_choice_support": tc, "parallel": parallel,138        "streaming_events": TOOL_EVENTS.get(type_, []),139        "result_shape": {"items": [{"schema_name": n, "type": (S[n].get("properties", {}).get("type", {}).get("enum") or [None])[0], "schema": resolve(S[n])} for n in RESULT_ITEM.get(type_, [])],140                         "summary": result_shape},141        "billing": billing, "limitations": limitations, "security": security, "beta_header": beta, "status": status,142        "examples": {"curl": f"examples/openai/tools/{ex_dir}/{ex_dir.replace('-', '_')}.sh", "python": f"examples/openai/tools/{ex_dir}/{ex_dir.replace('-', '_')}.py", "typescript": f"examples/openai/tools/{ex_dir}/{ex_dir.replace('-', '_')}.ts"} if ex_dir else {},143        "verification": verification,144        "sources": [src(u) for u in extra_sources] + [src(f"{REF}/resources/responses/methods/create"), src("https://github.com/openai/openai-openapi (openapi-master.yaml)")],145        "last_verified": VERIFIED,146    }147    if notes:148        rec["notes"] = notes149    T[type_] = rec150151152PRICING = f"{DOCS}/pricing#built-in-tools"153154tool("function", "Function calling (custom function tool)", "client",155     "Developer-defined function described by a JSON Schema. The model emits a `function_call` item (name + JSON `arguments`); the application executes it and returns a `function_call_output` item. Supports `strict` schemas (structured outputs), `async: true` (GPT-6 Astra+), `defer_loading` (tool search), `allowed_callers` (programmatic tool calling), `output_schema`, and grouping in `namespace` tools.",156     [RESP, CHAT, "POST /v1/realtime (session.tools)", "Agents API (agent tool config)"],157     {**TC_COMMON, "specific": {"type": "function", "name": "<fn>"}, "chat_completions": {"type": "function", "function": {"name": "<fn>"}}},158     {"supported": True, "parameter": "parallel_tool_calls (default true)", "notes": "Built-in tools cannot be included in a parallel function-call batch (GPT-5+). Fine-tuned models: strict mode disabled when several functions are called in one turn."},159     "Output item `function_call` {id, call_id, name, arguments (JSON string), status, namespace?, caller?, async?}; reply with input item `function_call_output` {call_id, output: string | content array}.",160     {"model": "Function definitions are injected into the system message and billed as input tokens; call arguments are output tokens.", "per_call": None, "source": PRICING},161     ["Strict mode requires all properties in `required`, `additionalProperties: false`, supported JSON Schema subset; strict schemas are cached and not eligible for ZDR.", "Soft guidance: < 20 functions available at the start of a turn.", "Chat Completions: `strict` defaults to false; Responses: omitted `strict` = best effort strict.", "GPT-6 Astra requires the Responses API for tool calling."],162     ["Validate arguments server-side (schema/regex) before executing; the model can be prompt-injected by tool outputs.", "Reasoning items returned alongside tool calls must be passed back with the outputs (reasoning models)."],163     LIVE, "function-calling",164     {"forced_call": ver("success", 200, f"{NANO}: tool_choice {{type:function,name:get_weather}} -> function_call item, then function_call_output round trip via previous_response_id -> message"),165      "streaming": ver("success", 200, "response.output_item.added -> response.function_call_arguments.delta x N -> response.function_call_arguments.done -> response.output_item.done"),166      "strict_invalid_schema": ver("success", 400, "missing additionalProperties:false -> 400 invalid_request_error code=invalid_function_parameters param=tools[0].parameters"),167      "allowed_tools": ver("success", 200, "tool_choice {type:allowed_tools, mode:required, tools:[{type:function,name:get_weather}]} + parallel_tool_calls:false -> function_call"),168      "chat_completions": ver("success", 200, "gpt-4.1-nano chat.completions tool_choice forced -> choices[0].message.tool_calls[0].function")},169     [f"{DOCS}/guides/function-calling", f"{DOCS}/guides/async-tool-calling"])170171tool("custom", "Custom tool (free-form text or CFG grammar)", "client",172     "Like a function but the model emits an arbitrary string (`custom_tool_call.input`) instead of JSON arguments. `format` is `{type: text}` (default) or `{type: grammar, syntax: lark|regex, definition}` constraining sampling (LLGuidance; Rust regex syntax).",173     [RESP, CHAT], {**TC_COMMON, "specific": {"type": "custom", "name": "<tool>"}, "chat_completions": {"type": "custom", "custom": {"name": "<tool>"}}},174     {"supported": True, "parameter": "parallel_tool_calls"},175     "Output item `custom_tool_call` {id, call_id, name, input: string, status}; reply with `custom_tool_call_output` {call_id, output}.",176     {"model": "Token billing only.", "source": PRICING},177     ["Grammar too complex -> API error; keep terminals bounded; no verbose regex mode; Lark subset only.", "Chat Completions shape differs: {type: custom, custom: {name, description, format: {type: grammar, grammar: {syntax, definition}}}}."],178     ["Grammar constrains syntax, not semantics — still validate the input before acting on it."],179     LIVE, "custom-tools",180     {"regex_grammar": ver("success", 200, f"{NANO}: format grammar regex ^(yes|no)$ forced -> custom_tool_call input='yes'"),181      "text_format": ver("success", 200, f"{NANO}: default text format, tool_choice required -> custom_tool_call"),182      "streaming": ver("success", 200, "response.custom_tool_call_input.delta -> .done")},183     [f"{DOCS}/guides/function-calling#custom-tools"])184185tool("namespace", "Namespace (group of function/custom tools)", "client",186     "Groups function/custom tools under a shared namespace with its own description. Calls come back as `function_call`/`custom_tool_call` items carrying a `namespace` field. Namespaces are the recommended unit for deferred loading with `tool_search`.",187     [RESP], {**TC_COMMON, "note": "tool_choice targets the inner tool by name"}, {"supported": True, "parameter": "parallel_tool_calls"},188     "Inner tool call items gain `namespace: <name>`; `function_call_output` may echo `namespace`.",189     {"model": "Token billing only.", "source": PRICING},190     ["`defer_loading` applies to the tools inside the namespace, not the namespace object.", "Model pages do not list `namespace` separately; availability follows function calling (verified on gpt-5.4-nano)."],191     ["Same as function calling."], LIVE, "function-calling",192     {"namespace_call": ver("success", 200, f"{NANO}: namespace 'weather' wrapping get_weather, tool_choice required -> function_call with namespace='weather'")},193     [f"{DOCS}/guides/function-calling#namespaces", f"{DOCS}/guides/tools-tool-search"])194195tool("tool_search", "Tool search (deferred tool loading)", "hosted",196     "Lets the model discover tools marked `defer_loading: true` (functions, namespaces, MCP servers) on demand. `execution: server` (hosted, default) emits `tool_search_call` + `tool_search_output` items automatically; `execution: client` stops after `tool_search_call` and the app returns a `tool_search_output` item with tool definitions. Deferred tools are loaded at the end of context to preserve prompt caching.",197     [RESP, "Agents API (session tools)"], {"modes": ["auto", "required", "none"], "note": "tool_choice applies to the tools currently callable in the turn"},198     {"supported": False, "notes": "Search happens before the eventual function call."},199     "Output items `tool_search_call` {id, call_id|null, execution, arguments: {paths: [...]}, status} then `tool_search_output` {id, call_id, execution, tools: [tool definitions], status}, then normal tool calls.",200     {"model": "Token billing only; saves input tokens by not sending deferred definitions up front.", "source": PRICING},201     ["Responses API: only gpt-5.4 and later models (gpt-5.4-nano page does not list tool_search).", "`additional_tools` input item can inject tools mid-conversation."],202     ["Client-executed search lets the app decide which tools exist per turn — keep an allowlist."], LIVE, "tool-search",203     {"hosted": ver("success", 200, "gpt-5.4-mini: tool_search + namespace with deferred get_weather -> tool_search_call{arguments:{paths:['weather']}} , tool_search_output{tools:[namespace]}, function_call{namespace:'weather'}")},204     [f"{DOCS}/guides/tools-tool-search"])205206tool("programmatic_tool_calling", "Programmatic Tool Calling", "hosted",207     "Hosted tool letting the model write JavaScript (isolated V8, no Node/network) that orchestrates other tools. Eligible tools opt in with `allowed_callers: [\"programmatic\"]` (function, custom, mcp, apply_patch, shell, code_interpreter). Tool calls emitted from the program carry `caller: {type: program, caller_id}`.",208     [RESP, "Agents API (enabled by default)"], {"modes": ["auto", "required", "none"], "specific": {"type": "programmatic_tool_calling"}},209     {"supported": True, "notes": "Program can call tools in parallel inside the runtime."},210     "Standard Responses object; nested `function_call` items with caller.type = program; results returned as `function_call_output` with the same caller.",211     {"model": "Token billing only.", "source": PRICING},212     ["Check the model page before enabling; not listed on gpt-5.4-nano.", "Supports ZDR without a persistent container.", "Not live-tested (no cheap eligible model identified)."],213     ["Require application-level approval for high-impact actions regardless of caller; MCP `require_approval` can pause the program."],214     ["DOCUMENTED", "UNVERIFIED"], "", {"docs": ver("success", 0, "not called", "docs_only")}, [f"{DOCS}/guides/tools-programmatic-tool-calling"], models=[])215216WS_LIMITS = ["Search context window limited to 128k even on 1M models.", "Not supported with gpt-5 `minimal` reasoning; gpt-5.4 reasoning `none` may degrade quality.", "Up to 100 allowed_domains / 100 blocked_domains.", "Rate limits = underlying model tiered limits.", "`web_search_preview` ignores `external_web_access`, no `filters`/`return_token_budget`."]217WS_SEC = ["Treat page content as untrusted (prompt injection); prefer `filters.allowed_domains` for sensitive workflows.", "`external_web_access: false` runs cache-only."]218WS_BILL = {"per_call": "$10.00 / 1k calls (web_search, all models; image web search same) + search content tokens billed at model rates", "preview": "$10/1k (reasoning models) or $25/1k with free content tokens (non-reasoning)", "source": PRICING}219tool("web_search", "Web search (hosted)", "hosted",220     "Hosted web search. Actions `search` (with `queries`, `sources` when `include: [web_search_call.action.sources]`), `open_page` and `find_in_page` (reasoning models). Citations come back as `url_citation` annotations on the message. Live fields echoed by the API but absent from the OpenAPI spec: `return_token_budget` (default|unlimited), `filters.blocked_domains`, `search_content_types` ([text, image]).",221     [RESP, "Realtime/Live (web_search tool in Live sessions)", "Agents API"],222     {**TC_COMMON, "specific": {"type": "web_search"}, "note": "Live: tool_choice {type: web_search} accepted and echoed back as {type: web_search_preview}; spec ToolChoiceTypes enum only lists web_search_preview."},223     {"supported": False, "notes": "Built-in tools are not batched with parallel function calls."},224     "Output item `web_search_call` {id, status, action: {type: search|open_page|find_in_page, query, queries, sources: [{type: url, url} | {type: api, name: oai-time|oai-weather|oai-sports|oai-finance}]}}; message annotations `url_citation` {url, title, start_index, end_index}.",225     WS_BILL, WS_LIMITS, WS_SEC, LIVE, "web-search",226     {"forced_search": ver("success", 200, f"{NANO}: tool_choice {{type:web_search}}, search_context_size low, include web_search_call.action.sources -> web_search_call action.type=search, sources=[{{type:'api',name:'oai-time'}}] (non-URL source shape not in spec); answer 'Saturday, September 19, 2026 Toronto'; usage 4671 in / 329 out"),227      "auto_no_search": ver("success", 200, "same prompt with tool_choice auto -> model answered without calling the tool (search is optional under auto)"),228      "max_output_tokens_64": ver("failure", 200, "first attempt with max_output_tokens 64 ended status=incomplete (reason max_output_tokens) before any tool call — leave >= 300 tokens for reasoning+search"),229      "streaming": ver("success", 200, "response.web_search_call.in_progress -> searching -> completed")},230     [f"{DOCS}/guides/tools-web-search", PRICING])231tool("web_search_2025_08_26", "Web search (dated snapshot 2025-08-26)", "hosted", "Dated alias of `web_search` (same schema, WebSearchTool).", [RESP], {**TC_COMMON}, {"supported": False},232     "Same as web_search.", WS_BILL, WS_LIMITS, WS_SEC, ["DOCUMENTED", "UNVERIFIED"], "web-search", {"docs": ver("success", 0, "alias not called", "docs_only")}, [f"{DOCS}/guides/tools-web-search"])233tool("web_search_preview", "Web search preview (legacy)", "hosted",234     "Earlier hosted web search tool kept for legacy integrations. Supports `user_location`, `search_context_size`, `search_content_types`; does not support `filters`, `external_web_access`, `return_token_budget`. Docs: migrate to `web_search`.",235     [RESP], {**TC_COMMON, "specific": {"type": "web_search_preview"}}, {"supported": False}, "Same `web_search_call` item.", WS_BILL,236     WS_LIMITS + ["Docs recommend migrating to web_search."], WS_SEC, ["DOCUMENTED", "LEGACY", "UNVERIFIED"], "web-search",237     {"docs": ver("success", 0, "not called (legacy)", "docs_only")}, [f"{DOCS}/guides/tools-web-search"])238tool("web_search_preview_2025_03_11", "Web search preview (dated snapshot 2025-03-11)", "hosted", "Dated alias of `web_search_preview`.", [RESP], {**TC_COMMON, "specific": {"type": "web_search_preview_2025_03_11"}}, {"supported": False},239     "Same `web_search_call` item.", WS_BILL, WS_LIMITS, WS_SEC, ["DOCUMENTED", "LEGACY", "UNVERIFIED"], "web-search", {"docs": ver("success", 0, "not called", "docs_only")}, [f"{DOCS}/guides/tools-web-search"])240241tool("file_search", "File search (vector stores)", "hosted",242     "Semantic + keyword (hybrid) retrieval over vector stores. Configure `vector_store_ids`, `max_num_results` (1-50), `filters` (comparison/compound on file attributes), `ranking_options` {ranker, score_threshold, hybrid_search}. Results are hidden unless `include: [file_search_call.results]`; citations are `file_citation` annotations.",243     [RESP, "Assistants API (legacy)"], {**TC_COMMON, "specific": {"type": "file_search"}}, {"supported": False},244     "Output item `file_search_call` {id, status, queries: [...], results: [{file_id, filename, score, text, attributes, vector_store_id}] | null} + message with `file_citation` annotations.",245     {"per_call": "$2.50 / 1k calls", "storage": "$0.10 / GB / day (1 GB free)", "source": PRICING},246     ["max_num_results 1-50.", "Deep research models support only `type` and `vector_store_ids`.", "Vector store CRUD is documented in the vector-stores fragment (other agent)."],247     ["Only upload trusted files: retrieved text is model input (prompt injection)."], LIVE, "file-search",248     {"search": ver("success", 200, f"{NANO}: vector store 'atlas-tools-agent' + 1 txt file, include file_search_call.results -> file_search_call queries=[3 rewrites], results[0].score=0.7626, text returned; answer PELICAN-42; results[0].vector_store_id was '' (empty string, spec says string)"),249      "max_output_tokens_32": ver("failure", 200, "with max_output_tokens 32 the response was incomplete and file_search_call.status='incomplete'"),250      "streaming": ver("success", 200, "response.file_search_call.in_progress -> searching -> completed")},251     [f"{DOCS}/guides/tools-file-search", PRICING])252253tool("code_interpreter", "Code interpreter (Python sandbox container)", "hosted",254     "Runs Python in a sandboxed container. `container` is either a container id (explicit mode, created via POST /v1/containers) or `{type: auto, file_ids?, memory_limit?: 1g|4g|16g|64g, network_policy?}`. Generated files are cited as `container_file_citation` annotations; outputs (`logs`, `image`) are returned only with `include: [code_interpreter_call.outputs]`.",255     [RESP, "Assistants API (legacy)"], {**TC_COMMON, "specific": {"type": "code_interpreter"}}, {"supported": False},256     "Output item `code_interpreter_call` {id, status: in_progress|interpreting|completed|incomplete|failed, container_id, code, outputs: [{type: logs, logs} | {type: image, url}] | null}.",257     {"per_session": "1 GB $0.03 · 4 GB $0.12 · 16 GB $0.48 · 64 GB $1.92 per 20-minute session per container (shared with hosted shell)", "source": PRICING},258     ["Container expires after 20 min of inactivity (auto containers: expires_after last_active_at 20 min); expired containers cannot be revived.", "No outbound network by default (network_policy allowlist/disabled; org allow-list applies).", "Model knows the tool as the 'python tool'."],259     ["Network-enabled containers: prompt-injection driven exfiltration risk; only allowlist trusted domains; use domain_secrets instead of raw credentials."], LIVE, "code-interpreter",260     {"auto_container": ver("success", 200, f"{NANO}: container auto, include code_interpreter_call.outputs -> code 'print(2+2)', outputs [{{type:logs, logs:'4\\n'}}], container_id cntr_…; then GET/LIST/DELETE container 200 (see containers endpoints)"),261      "streaming": ver("success", 0, "not streamed live (cost); events from spec", "docs_only")},262     [f"{DOCS}/guides/tools-code-interpreter", f"{REF}/resources/containers", PRICING])263264CU_SEC = ["Isolated browser/VM, site allow-list, treat screen content as untrusted, confirm at the point of risk, enforce step/time/cost limits, acknowledge `pending_safety_checks` explicitly."]265tool("computer_use_preview", "Computer use (preview tool + computer-use-preview model)", "client",266     "Model returns structured GUI actions (`computer_call` with action click/double_click/drag/keypress/move/screenshot/scroll/type/wait and `pending_safety_checks`); the app executes them and returns `computer_call_output` {call_id, output: {type: computer_screenshot, image_url|file_id}, acknowledged_safety_checks}. Requires `display_width`, `display_height`, `environment` (windows|mac|linux|ubuntu|browser). Historically paired with model `computer-use-preview`; newer docs recommend the `computer` tool or code-execution with GPT-6 Astra.",267     [RESP], {**TC_COMMON, "specific": {"type": "computer_use_preview"}}, {"supported": False},268     "`computer_call` {id, call_id, action, pending_safety_checks, status}; input `computer_call_output`; `include: [computer_call_output.output.image_url]` returns screenshot URLs in retrieved items.",269     {"model": "computer-use-preview $3 / 1M input, $12 / 1M output (model page); newer models at their own rates", "source": f"{DOCS}/models/computer-use-preview"},270     ["Requires `truncation: auto` with computer-use-preview.", "Our key: model computer-use-preview -> 404 model_not_found (tiered/limited access)."], CU_SEC,271     ["DOCUMENTED", "PREVIEW", "ACCOUNT_RESTRICTED"], "computer-use",272     {"computer_use_preview_model": ver("restricted", 404, "POST /v1/responses model=computer-use-preview with 1x1 PNG input_image + computer_use_preview tool -> 404 invalid_request_error code=model_not_found 'does not exist or you do not have access'")},273     [f"{DOCS}/guides/tools-computer-use", f"{DOCS}/guides/tools-computer-use-integration", f"{DOCS}/models/computer-use-preview"])274tool("computer", "Computer tool (current)", "client",275     "Current computer-use tool (`{type: computer}`, no display parameters in the spec). Same `computer_call`/`computer_call_output` loop; `computer_call.actions[]` carries flattened batched actions for `computer_use`. Listed as `computer_use` on gpt-5.4/5.4-mini/5.4-pro/5.5/5.6*/gpt-6-astra model pages.",276     [RESP], {**TC_COMMON, "specific": [{"type": "computer"}, {"type": "computer_use"}]}, {"supported": False},277     "Same as computer_use_preview plus `actions[]` batches.", {"model": "Token billing (images as input tokens)", "source": PRICING},278     ["Not live-tested (cost/safety); docs recommend code-execution integration for GPT-6 Astra."], CU_SEC, ["DOCUMENTED", "UNVERIFIED"], "computer-use",279     {"docs": ver("success", 0, "not called", "docs_only")}, [f"{DOCS}/guides/tools-computer-use", f"{DOCS}/guides/tools-computer-use-integration"])280281tool("image_generation", "Image generation tool", "hosted",282     "Lets a mainline model call GPT Image models (`model`: gpt-image-1, gpt-image-1-mini, gpt-image-1.5, gpt-image-2[-2026-04-21], gpt-image-2.5-sunburst|flare[-2026-09-08]). Parameters: size, quality (low|medium|high|xhigh|max|auto), background, output_format, output_compression, moderation, input_fidelity, input_image_mask, partial_images (0-3, streaming), action (generate|edit|auto). Result is base64 in `image_generation_call.result`.",283     [RESP], {**TC_COMMON, "specific": {"type": "image_generation"}}, {"supported": False},284     "Output item `image_generation_call` {id, status: in_progress|generating|completed|failed, result: base64|null, revised_prompt, size, quality, background, output_format, action}.",285     {"per_image": "Image model token pricing (see pricing fragment / image generation guide calculator); mainline model tokens billed separately", "source": f"{DOCS}/pricing#image-generation"},286     ["Guide model list: gpt-5.5, gpt-5.4-mini, gpt-5.4-nano, gpt-5.2, gpt-5, gpt-5-nano, o3, gpt-4.1(-mini/-nano), gpt-4o(-mini).", "Size must be divisible by 16 (live error).", "xhigh/max quality only on gpt-image-2.5-*."],287     ["Moderation `low` relaxes filtering; images may be revised by the mainline model (revised_prompt)."], ["DOCUMENTED", "LIVE_VERIFIED"], "image-generation",288     {"invalid_size_error": ver("success", 400, f"{NANO}: size '1x1' -> 400 type=image_generation_user_error code=invalid_value param=tools 'Width and height must both be divisible by 16.' (generation itself skipped: RUN_IMAGE_TESTS)")},289     [f"{DOCS}/guides/tools-image-generation"])290291tool("mcp", "Remote MCP servers, connectors and Secure MCP Tunnel", "mcp",292     "Connects the model to a remote MCP server (`server_url`, Streamable HTTP or HTTP/SSE), a Secure MCP Tunnel (`tunnel_id`) or a legacy connector (`connector_id`, deprecated for models released after 2025-09-01). Auth via `authorization` (OAuth token, never stored/echoed) or `headers`. `allowed_tools` (list or {tool_names, read_only}), `require_approval` (always|never|{always:{...}, never:{...}}, default always), `defer_loading`, `allowed_callers`, `server_description`.",293     [RESP, "POST /v1/realtime (mcp tool)", "Agents API (MCP connections)", "Deep research models"],294     {**TC_COMMON, "specific": {"type": "mcp", "server_label": "<label>", "name": "<tool or null>"}}, {"supported": False},295     "Items: `mcp_list_tools` {server_label, tools: [{name, description, input_schema, annotations}], error}, `mcp_call` {server_label, name, arguments, output, error: mcp_protocol_error|mcp_tool_execution_error|http_error, status, approval_request_id}, `mcp_approval_request` {id, server_label, name, arguments} answered by input `mcp_approval_response` {approval_request_id, approve, reason}.",296     {"per_call": "No OpenAI per-call fee; imported tool definitions and tool outputs are billed as model tokens", "source": PRICING},297     ["`mcp_list_tools` is fetched once per response chain; keep the item in context.", "Deep research requires search+fetch MCP servers with require_approval never.", "Connector ids: connector_dropbox, connector_gmail, connector_googlecalendar, connector_googledrive, connector_microsoftteams, connector_outlookcalendar, connector_outlookemail, connector_sharepoint."],298     ["Only connect trusted servers; tool outputs are untrusted input (prompt injection, exfiltration through arguments).", "Approvals default to always; `authorization` value is redacted from the stored Response.", "Org/project admins can disable MCP."],299     LIVE, "mcp-and-connectors",300     {"deepwiki_call": ver("success", 200, f"{NANO}: server_url https://mcp.deepwiki.com/mcp, require_approval never, allowed_tools [read_wiki_structure] -> mcp_list_tools (1 tool, annotations.read_only=false), mcp_call completed with output, message"),301      "approval_flow": ver("success", 200, "require_approval always + tool_choice {type:mcp,...} -> mcp_approval_request; replied mcp_approval_response approve=false reason -> model message acknowledging rejection"),302      "streaming": ver("success", 200, "response.mcp_list_tools.in_progress/completed, response.mcp_call.in_progress, response.mcp_call_arguments.delta/done, response.mcp_call.completed")},303     [f"{DOCS}/guides/tools-connectors-mcp", f"{DOCS}/guides/secure-mcp-tunnels", f"{DOCS}/guides/realtime-mcp"])304305tool("shell", "Shell tool (hosted container or local runtime)", "hosted",306     "Model emits `shell_call` {action: {commands[], timeout_ms, max_output_length}}. `environment`: `{type: container_auto, file_ids?, memory_limit?, network_policy?, skills?}` (hosted: OpenAI runs the commands and returns `shell_call_output` automatically), `{type: container_reference, container_id}` (reuse a container from /v1/containers) or `{type: local}` (your runtime executes and returns `shell_call_output` {call_id, output: [{stdout, stderr, outcome: {type: exit, exit_code} | {type: timeout}}]}). Default cwd `/mnt/data`; skills mount under `/home/oai/skills/<name>-<version>`.",307     [RESP], {**TC_COMMON, "specific": {"type": "shell"}}, {"supported": False},308     "`shell_call` {id, call_id, action.commands, environment: null | {type: local} | {type: container_reference, container_id}, status}; `shell_call_output` items (hosted: server-emitted).",309     {"per_session": "Hosted containers: same rates as code interpreter (1 GB $0.03 … 64 GB $1.92 per 20-min session)", "local": "Token billing only", "source": PRICING},310     ["Not available in Chat Completions.", "Hosted shell: no interactive TTY; no outbound network by default; org domain allow-list.", "Model page key: hosted_shell (gpt-5.2, gpt-5.2/5.3-codex, gpt-5.4*, gpt-5.5, gpt-5.6*, gpt-6-astra)."],311     ["Local mode: you execute arbitrary commands — sandbox, enforce timeouts, filter dangerous commands; timeout_ms is only a hint.", "Hosted allowlists + domain_secrets reduce credential leakage; prompt injection via fetched content remains."],312     LIVE, "shell",313     {"local_env": ver("success", 200, f"{NANO}: environment local, tool_choice {{type:shell}} -> shell_call action.commands ['echo OK'], environment null (nothing executed by us)"),314      "hosted_with_skill": ver("success", 200, f"{NANO}: environment container_auto + skills [skill_reference] -> two shell_call/shell_call_output pairs (cat SKILL.md, echo OK) with environment container_reference; final message 'OK'"),315      "streaming": ver("success", 200, "response.shell_call_command.added -> .delta -> .done (local env); shell_call_output_content.* not observed locally")},316     [f"{DOCS}/guides/tools-shell", PRICING])317318tool("local_shell", "Local shell (legacy, codex-mini-latest)", "client",319     "Legacy tool for Codex CLI / codex-mini-latest: `local_shell_call` {action: {type: exec, command[], timeout_ms, working_directory, env, user}} answered by `local_shell_call_output` {id, output}. Superseded by `shell` with environment local.",320     [RESP], {**TC_COMMON}, {"supported": False}, "`local_shell_call` / `local_shell_call_output`.", {"model": "Token billing only", "source": PRICING},321     ["Live: gpt-5.4-nano -> 400 'The local_shell tool is no longer supported.' Only codex-mini-latest documented."],322     ["Same as shell local mode."], ["DOCUMENTED", "LEGACY", "FAILED_VERIFICATION"], "shell",323     {"nano": ver("failure", 400, f"{NANO}: tools [{{type:local_shell}}] -> 400 invalid_request_error param=tools 'The local_shell tool is no longer supported.'")},324     [f"{DOCS}/guides/tools-local-shell"], models=["codex-mini-latest"])325326tool("apply_patch", "Apply patch (structured file diffs)", "client",327     "Model emits `apply_patch_call` {operation: {type: create_file|update_file|delete_file, path, diff}}; the app applies it and returns `apply_patch_call_output` {call_id, status: completed|failed, output}. No input schema to declare. Often paired with `shell` for file discovery.",328     [RESP], {**TC_COMMON, "specific": {"type": "apply_patch"}}, {"supported": False},329     "`apply_patch_call` {id, call_id, status, operation}; input `apply_patch_call_output`.", {"model": "Token billing only", "source": PRICING},330     ["Guide table: Responses only; models GPT-5.1, 5.2, 5.4, 5.5 (model pages also list gpt-5.4-nano/mini, 5.6*, gpt-6-astra)."],331     ["Validate paths (no traversal), restrict to allowed dirs, return status failed with a message on error."], LIVE, "apply-patch",332     {"create_file": ver("success", 200, f"{NANO}: tool_choice {{type:apply_patch}} -> apply_patch_call operation {{type:create_file, path:'hello.txt', diff:'+OK\\n'}} (not applied by us)"),333      "streaming": ver("success", 200, "LIVE_DISCOVERED events response.apply_patch_call_operation_diff.delta / .done (absent from OpenAPI ResponseStreamEvent union)")},334     [f"{DOCS}/guides/tools-apply-patch"])335336tool("skill_reference", "Skills (attachments for hosted shell / containers) — not a tool type", "hosted",337     "Skills are versioned ZIP bundles (SKILL.md + files) uploaded through /v1/skills and mounted into hosted shell containers via `environment.skills[]` = `{type: skill_reference, skill_id, version?}` or `{type: inline, name, description, source: {type: base64, media_type: application/zip, data}}`, or at container creation (`skills[]`). Local shell uses `{name, description, path}` entries instead. The platform adds each skill's name/description/path to the user-prompt context.",338     [RESP + " (tools[type=shell].environment.skills)", "POST /v1/containers (skills)", "Agents API sandboxes"], {"modes": [], "note": "not a tool; no tool_choice"}, {"supported": False},339     "No dedicated item; shell_call commands read /home/oai/skills/<name>-<version>/SKILL.md.", {"model": "Container session + tokens", "source": PRICING},340     ["Exactly one SKILL.md per bundle; <= 500 files; <= 25 MB uncompressed; up to 200 skills per container_auto; default_version cannot be deleted.", "Hosted shell only accepts skill_reference/inline; local shell only paths."],341     ["Inspect every skill: SKILL.md instructions are user-level prompt input (prompt injection); do not let end-users attach arbitrary skills."], LIVE, "skills",342     {"mount_and_use": ver("success", 200, f"{NANO}: shell container_auto with skills [skill_reference skill_…] -> model listed /home/oai/skills/atlas-hello-1, read SKILL.md, ran echo OK")},343     [f"{DOCS}/guides/tools-skills", f"{REF}/resources/skills"])344345TOOLS_OUT = {"$fragment": "openai-tools", "generated_by": "scripts/build_openai_tools_fragments.py", "generated_at": VERIFIED,346             "include_values_for_tools": {k: v for k, v in {347                 "file_search_call.results": "file_search results text/scores", "web_search_call.results": "web search results (legacy)",348                 "web_search_call.action.sources": "full list of consulted sources", "code_interpreter_call.outputs": "logs/image outputs",349                 "computer_call_output.output.image_url": "screenshot URLs in computer_call_output"}.items()},350             "tool_model_matrix": matrix, "records": list(T.values())}351352# --------------------------------------------------------------------------- parameters353P: list[dict] = []354355356def walk(schema: dict, prefix: str, endpoint: str, status: list[str], source: str, location: str = "body", compatible_models: list[str] | None = None, depth: int = 0, required_parent=True):357    if depth > 7 or not isinstance(schema, dict):358        return359    variants = schema.get("oneOf") or schema.get("anyOf")360    if variants and "properties" not in schema:361        for v in variants:362            if isinstance(v, dict) and v.get("type") != "null":363                walk(v, prefix, endpoint, status, source, location, compatible_models, depth + 1)364        return365    props = schema.get("properties") or {}366    req = {r for r in (schema.get("required") or []) if isinstance(r, str)}367    for k, v in props.items():368        if not isinstance(v, dict):369            continue370        path = f"{prefix}.{k}" if prefix else k371        alts = [a for a in (v.get("oneOf") or v.get("anyOf") or []) if isinstance(a, dict)]372        nn = [a for a in alts if a.get("type") != "null"]373        base = nn[0] if len(nn) == 1 else v374        typ = v.get("type") or base.get("type") or ("oneOf" if nn else "object")375        if isinstance(typ, list):376            typ = "|".join(typ)377        enum = v.get("enum") or base.get("enum")378        rec = {"provider": "openai", "endpoint": endpoint, "parameter": path, "location": location, "type": typ,379               "required": k in req, "default": v.get("default", base.get("default")), "minimum": v.get("minimum", base.get("minimum")),380               "maximum": v.get("maximum", base.get("maximum")), "enum": enum,381               "description": str(v.get("description") or base.get("description") or "").strip(),382               "compatible_models": compatible_models or [], "beta_header": None, "status": status, "source": source}383        for lim in ("minItems", "maxItems", "minLength", "maxLength", "pattern", "format"):384            val = v.get(lim, base.get(lim))385            if val is not None:386                rec.setdefault("constraints", {})[lim] = val387        if any(p["parameter"] == path and p["endpoint"] == endpoint for p in P):388            continue389        P.append(rec)390        if "properties" in base:391            walk(base, path, endpoint, status, source, location, compatible_models, depth + 1)392        sub = nn if len(nn) > 1 else [a for a in (base.get("oneOf") or base.get("anyOf") or []) if isinstance(a, dict) and a.get("type") != "null"]393        for a in sub:394            walk(a, path, endpoint, status, source, location, compatible_models, depth + 1)395        items = base.get("items") if isinstance(base.get("items"), dict) else None396        if items:397            walk(items, path + "[]", endpoint, status, source, location, compatible_models, depth + 1)398399400for ttype, sname in TOOL_SCHEMA.items():401    if ttype in ("web_search_2025_08_26", "web_search_preview_2025_03_11"):402        continue403    st = T[ttype]["status"]404    guide = T[ttype]["sources"][0]["url"]405    prefix = f"tools[type={ttype}]" if ttype != "skill_reference" else "tools[type=shell].environment.skills[]"406    walk(resolve(S[sname]), prefix, RESP, [s for s in st if s in ("DOCUMENTED", "LIVE_VERIFIED", "LEGACY", "PREVIEW")] or ["DOCUMENTED"], guide, compatible_models=T[ttype]["compatible_models"])407# top-level tool controls408walk(resolve({"properties": {"tool_choice": S["ToolChoiceParam"], "parallel_tool_calls": S["ParallelToolCalls"],409                             "include": {"type": "array", "items": S["IncludeEnum"], "description": "Extra output fields; tool-related values: file_search_call.results, web_search_call.results, web_search_call.action.sources, code_interpreter_call.outputs, computer_call_output.output.image_url."},410                             "max_tool_calls": {"type": "integer", "description": "Maximum number of built-in tool calls (web search, file search, MCP…) in a response; used notably with deep research models."}}}),411     "", RESP, ["DOCUMENTED", "LIVE_VERIFIED"], f"{DOCS}/guides/function-calling")412# live-discovered web_search params not in spec413for name, typ, enum, desc in (("return_token_budget", "string", ["default", "unlimited"], "Token budget for returned web search content (GPT-5+ reasoning web search). Documented in the guide, echoed live (default), absent from OpenAPI spec."),414                              ("filters.blocked_domains", "array", None, "Up to 100 blocked domains (guide); absent from OpenAPI spec."),415                              ("search_content_types", "array", ["text", "image"], "Image search: include 'image' (and 'text'). Echoed live as ['text']; present in spec only on web_search_preview.")):416    P.append({"provider": "openai", "endpoint": RESP, "parameter": f"tools[type=web_search].{name}", "location": "body", "type": typ, "required": False, "default": None,417              "minimum": None, "maximum": None, "enum": enum, "description": desc, "compatible_models": models_for("web_search"), "beta_header": None,418              "status": ["DOCUMENTED", "LIVE_DISCOVERED"], "source": f"{DOCS}/guides/tools-web-search"})419# chat completions tool shapes420walk(resolve({"properties": {"tools[]": {"oneOf": [S["ChatCompletionTool"], S["CustomToolChatCompletions"]]}, "tool_choice": S["ChatCompletionToolChoiceOption"],421                             "parallel_tool_calls": S["ParallelToolCalls"], "web_search_options": {"type": "object", "description": "Chat Completions web search (search models gpt-5-search-api, gpt-4o(-mini)-search-preview): {user_location: {type: approximate, approximate: {country, region, city, timezone}}, search_context_size}."}}}),422     "", CHAT, ["DOCUMENTED"], f"{DOCS}/guides/function-calling")423# containers & skills bodies / queries424walk(resolve(S["CreateContainerBody"]), "", "POST /v1/containers", ["DOCUMENTED", "LIVE_VERIFIED"], f"{REF}/resources/containers/methods/create")425walk(resolve(S["CreateContainerFileBody"]), "", "POST /v1/containers/{container_id}/files", ["DOCUMENTED", "LIVE_VERIFIED"], f"{REF}/resources/containers/subresources/files/methods/create")426walk(resolve(S["CreateSkillBody"]), "", "POST /v1/skills", ["DOCUMENTED", "LIVE_VERIFIED"], f"{REF}/resources/skills/methods/create")427walk(resolve(S["CreateSkillVersionBody"]), "", "POST /v1/skills/{skill_id}/versions", ["DOCUMENTED", "LIVE_VERIFIED"], f"{REF}/resources/skills/subresources/versions/methods/create")428walk(resolve(S["SetDefaultSkillVersionBody"]), "", "POST /v1/skills/{skill_id}", ["DOCUMENTED", "FAILED_VERIFICATION"], f"{REF}/resources/skills/methods/update")429for path, ops in spec["paths"].items():430    if not (path.startswith("/containers") or path.startswith("/skills")):431        continue432    for m, op in ops.items():433        if m not in ("get", "post", "delete"):434            continue435        for p in op.get("parameters", []):436            if not isinstance(p, dict) or "name" not in p:437                continue438            sch = p.get("schema") or {}439            P.append({"provider": "openai", "endpoint": f"{m.upper()} /v1{path}", "parameter": p["name"], "location": p.get("in"), "type": sch.get("type", "string"),440                      "required": bool(p.get("required")), "default": sch.get("default"), "minimum": sch.get("minimum"), "maximum": sch.get("maximum"), "enum": sch.get("enum"),441                      "description": (p.get("description") or "").strip(), "compatible_models": [], "beta_header": None, "status": ["DOCUMENTED"],442                      "source": f"{REF}/resources/{'containers' if path.startswith('/containers') else 'skills'}"})443444PARAMS_OUT = {"$fragment": "openai-tools-parameters", "generated_by": "scripts/build_openai_tools_fragments.py", "generated_at": VERIFIED, "records": P}445446# --------------------------------------------------------------------------- streaming events447LIVE_SAMPLES = {448    "response.function_call_arguments.delta": {"type": "response.function_call_arguments.delta", "delta": "{\"", "item_id": "fc_…", "obfuscation": "LDQNEzSvV7mpzI", "output_index": 0, "sequence_number": 3},449    "response.function_call_arguments.done": {"type": "response.function_call_arguments.done", "arguments": "{\"city\":\"Rome\"}", "item_id": "fc_…", "output_index": 0, "sequence_number": 8},450    "response.custom_tool_call_input.delta": {"type": "response.custom_tool_call_input.delta", "delta": "yes", "item_id": "ctc_…", "obfuscation": "edRTib4zuVyeT", "output_index": 0, "sequence_number": 3},451    "response.custom_tool_call_input.done": {"type": "response.custom_tool_call_input.done", "input": "yes", "item_id": "ctc_…", "output_index": 0, "sequence_number": 4},452    "response.shell_call_command.added": {"type": "response.shell_call_command.added", "command": "", "command_index": 0, "output_index": 0, "sequence_number": 3},453    "response.shell_call_command.delta": {"type": "response.shell_call_command.delta", "command_index": 0, "delta": "echo", "obfuscation": "PGP4AcyqhWaD", "output_index": 0, "sequence_number": 4},454    "response.shell_call_command.done": {"type": "response.shell_call_command.done", "command": "echo OK", "command_index": 0, "output_index": 0, "sequence_number": 6},455    "response.apply_patch_call_operation_diff.delta": {"type": "response.apply_patch_call_operation_diff.delta", "delta": "+", "item_id": "apc_…", "obfuscation": "11F10pEBNExMTzK", "output_index": 0, "sequence_number": 3},456    "response.apply_patch_call_operation_diff.done": {"type": "response.apply_patch_call_operation_diff.done", "diff": "+OK\n", "item_id": "apc_…", "output_index": 0, "sequence_number": 6},457    "response.mcp_list_tools.in_progress": {"type": "response.mcp_list_tools.in_progress", "item_id": "mcpl_…", "output_index": 0, "sequence_number": 3},458    "response.mcp_list_tools.completed": {"type": "response.mcp_list_tools.completed", "item_id": "mcpl_…", "output_index": 0, "sequence_number": 4},459    "response.mcp_call_arguments.delta": {"type": "response.mcp_call_arguments.delta", "delta": "{\"repoName\":\"openai/openai-python\"}", "item_id": "mcp_…", "obfuscation": "oWNr7kyWBnPAy", "output_index": 1, "sequence_number": 8},460    "response.mcp_call_arguments.done": {"type": "response.mcp_call_arguments.done", "arguments": "{\"repoName\":\"openai/openai-python\"}", "item_id": "mcp_…", "output_index": 1, "sequence_number": 9},461    "response.mcp_call.in_progress": {"type": "response.mcp_call.in_progress", "item_id": "mcp_…", "output_index": 1, "sequence_number": 7},462    "response.mcp_call.completed": {"type": "response.mcp_call.completed", "item_id": "mcp_…", "output_index": 1, "sequence_number": 10},463    "response.file_search_call.in_progress": {"type": "response.file_search_call.in_progress", "item_id": "fs_…", "output_index": 1, "sequence_number": 5},464    "response.file_search_call.searching": {"type": "response.file_search_call.searching", "item_id": "fs_…", "output_index": 1, "sequence_number": 6},465    "response.file_search_call.completed": {"type": "response.file_search_call.completed", "item_id": "fs_…", "output_index": 1, "sequence_number": 7},466    "response.web_search_call.in_progress": {"type": "response.web_search_call.in_progress", "item_id": "ws_…", "output_index": 1, "sequence_number": 5},467    "response.web_search_call.searching": {"type": "response.web_search_call.searching", "item_id": "ws_…", "output_index": 1, "sequence_number": 6},468    "response.web_search_call.completed": {"type": "response.web_search_call.completed", "item_id": "ws_…", "output_index": 1, "sequence_number": 7},469}470TOOL_EVENT_KEYS = ("function_call", "web_search", "file_search", "code_interpreter", "image_generation", "mcp", "custom_tool", "shell_call", "apply_patch")471E: list[dict] = []472rse = S["ResponseStreamEvent"]473for m in rse.get("oneOf", rse.get("anyOf", [])):474    n = m["$ref"].split("/")[-1]475    sch = S[n]476    et = (sch.get("properties", {}).get("type", {}).get("enum") or [None])[0]477    if not et or not any(k in et for k in TOOL_EVENT_KEYS):478        continue479    live = et in LIVE_SAMPLES480    E.append({"provider": "openai", "api": "responses", "direction": "server→client", "event": et,481              "description": (sch.get("description") or "").strip(),482              "schema": {k: {kk: vv for kk, vv in resolve(v).items() if kk in ("type", "enum", "description")} for k, v in sch.get("properties", {}).items()},483              "example": LIVE_SAMPLES.get(et), "status": ["DOCUMENTED", "LIVE_VERIFIED"] if live else ["DOCUMENTED"],484              "source": f"{REF}/resources/responses/streaming-events"})485for et in ("response.apply_patch_call_operation_diff.delta", "response.apply_patch_call_operation_diff.done"):486    E.append({"provider": "openai", "api": "responses", "direction": "server→client", "event": et,487              "description": "Streams the `diff` of an apply_patch_call operation (delta chunks, then the final diff). Observed live on gpt-5.4-nano; NOT present in the OpenAPI ResponseStreamEvent union nor in the streaming-events reference page at retrieval time.",488              "schema": {"type": {"type": "string"}, "item_id": {"type": "string"}, "output_index": {"type": "integer"}, "sequence_number": {"type": "integer"},489                         ("delta" if et.endswith("delta") else "diff"): {"type": "string"}, **({"obfuscation": {"type": "string"}} if et.endswith("delta") else {})},490              "example": LIVE_SAMPLES[et], "status": ["LIVE_DISCOVERED", "LIVE_VERIFIED"], "source": "live observation 2026-09-18 (tmp-live/tools/apply_patch_stream.json)"})491E.append({"provider": "openai", "api": "responses", "direction": "server→client", "event": "response.output_item.added / response.output_item.done (tool items)",492          "description": "Generic item lifecycle events wrap every tool item: function_call, custom_tool_call, web_search_call, file_search_call, code_interpreter_call, image_generation_call, mcp_list_tools, mcp_call, mcp_approval_request, shell_call, shell_call_output, apply_patch_call, computer_call, tool_search_call, tool_search_output. `item` carries the full item; tool-specific events refer to it by item_id/output_index.",493          "schema": {"type": {"type": "string"}, "output_index": {"type": "integer"}, "sequence_number": {"type": "integer"}, "item": {"type": "object"}},494          "example": {"type": "response.output_item.added", "item": {"id": "fc_…", "type": "function_call", "status": "in_progress", "arguments": "", "call_id": "call_…", "name": "get_weather"}, "output_index": 0, "sequence_number": 2},495          "status": ["DOCUMENTED", "LIVE_VERIFIED"], "source": f"{REF}/resources/responses/streaming-events"})496EVENTS_OUT = {"$fragment": "openai-tools-streaming-events", "generated_by": "scripts/build_openai_tools_fragments.py", "generated_at": VERIFIED,497              "note": "Tool-related SSE events only; core message/reasoning events are covered by the responses-core fragment. Realtime MCP/function events (response.function_call_arguments.*, response.mcp_call*.*, mcp_list_tools.*) exist with the same names on the Realtime API (RealtimeServerEvent*).",498              "records": E}499500# --------------------------------------------------------------------------- endpoints (containers + skills)501LIVE_EP = {502    "GET /v1/containers": ver("success", 200, "?limit=2 -> list with data[] of container objects"),503    "POST /v1/containers": ver("success", 200, "{name:'atlas-tools-agent', memory_limit:'1g', expires_after:{anchor:last_active_at, minutes:5}} -> container {id cntr_…, object:'container', status:'running', memory_limit, expires_after, last_active_at}"),504    "GET /v1/containers/{container_id}": ver("success", 200, "auto container from code_interpreter -> status running, expires_after {last_active_at, 20}, memory_limit 1g, name 'auto'"),505    "DELETE /v1/containers/{container_id}": ver("success", 200, "-> {id, object:'container.deleted', deleted:true}"),506    "POST /v1/containers/{container_id}/files": ver("success", 200, "multipart file=hello.txt -> {id cfile_…, object:'container.file', bytes 11, path '/mnt/data/<hash>-hello.txt', source 'user'}"),507    "GET /v1/containers/{container_id}/files": ver("success", 200, "-> {object:list, data:[], first_id, last_id, has_more}"),508    "GET /v1/containers/{container_id}/files/{file_id}": ver("success", 200, "-> container.file object"),509    "DELETE /v1/containers/{container_id}/files/{file_id}": ver("success", 200, "-> {id, object:'container.file.deleted', deleted:true}"),510    "GET /v1/containers/{container_id}/files/{file_id}/content": ver("success", 200, "-> raw bytes 'hello atlas'"),511    "POST /v1/skills": ver("success", 200, "multipart files=<zip with atlas-hello/SKILL.md (frontmatter name/description)> -> skill {id skill_…, object:'skill', name, description, default_version:'1', latest_version:'1'}"),512    "GET /v1/skills": ver("success", 200, "?limit=5 -> {object:list, data:[], first_id:null, last_id:null, has_more:false}"),513    "GET /v1/skills/{skill_id}": ver("success", 200, "-> skill object"),514    "POST /v1/skills/{skill_id}": ver("failure", 404, "{default_version:'2'} right after creating version 2 -> 404 'Skill version 2 was not found' (param default_version) — versions appear not immediately addressable; possible eventual consistency or id-vs-number mismatch"),515    "DELETE /v1/skills/{skill_id}": ver("success", 200, "-> {id, object:'skill.deleted', deleted:true}"),516    "GET /v1/skills/{skill_id}/content": ver("success", 200, "-> application/zip bytes (PK header)"),517    "POST /v1/skills/{skill_id}/versions": ver("success", 200, "multipart files=<same zip> -> {id skillver_…, object:'skill.version', version:'2', skill_id, name, description}"),518    "GET /v1/skills/{skill_id}/versions": ver("failure", 200, "-> data [] although version 1 (default) existed; list did not reflect versions (LIVE discrepancy)"),519    "GET /v1/skills/{skill_id}/versions/{version}": ver("failure", 404, "/versions/1 -> 404 (version numbers not resolvable right after creation)"),520    "DELETE /v1/skills/{skill_id}/versions/{version}": ver("failure", 404, "/versions/1 -> 404 'Skill version 1 was not found' (default version is also documented as non-deletable)"),521    "GET /v1/skills/{skill_id}/versions/{version}/content": ver("success", 0, "not called", "docs_only"),522}523EP: list[dict] = []524for path, ops in spec["paths"].items():525    if not (path.startswith("/containers") or path.startswith("/skills")):526        continue527    fam = "containers" if path.startswith("/containers") else "skills"528    for m, op in ops.items():529        if m not in ("get", "post", "delete"):530            continue531        key = f"{m.upper()} /v1{path}"532        rb = op.get("requestBody", {}).get("content", {})533        r200 = op.get("responses", {}).get("200", {}).get("content", {})534        v = LIVE_EP.get(key)535        status = ["DOCUMENTED"] + (["LIVE_VERIFIED"] if v and v["result"] == "success" and v["method"] == "live_api" else (["FAILED_VERIFICATION"] if v and v["result"] == "failure" else []))536        EP.append({"provider": "openai", "api_family": fam, "method": m.upper(), "path": f"/v1{path}", "name": op.get("operationId"),537                   "description": (op.get("summary") or "") + (" — " + (op.get("description") or "").strip()[:400] if op.get("description") else ""),538                   "status": status, "auth": "Bearer API key (project key)", "beta_header": None,539                   "request": {"content_type": list(rb.keys()) or None, "body_ref": [c.get("schema", {}).get("$ref", "").split("/")[-1] for c in rb.values()] or None,540                               "query": [p["name"] for p in op.get("parameters", []) if isinstance(p, dict) and p.get("in") == "query"],541                               "path_params": [p["name"] for p in op.get("parameters", []) if isinstance(p, dict) and p.get("in") == "path"]},542                   "response": {"content_type": list(r200.keys()) or None, "schema_ref": [c.get("schema", {}).get("$ref", "").split("/")[-1] for c in r200.values()] or None,543                                "schema": {n: resolve(S[n]) for n in [c.get("schema", {}).get("$ref", "").split("/")[-1] for c in r200.values()] if n in S}},544                   "streaming": {"supported": False, "events_ref": None},545                   "pagination": ("cursor (limit, order, after)" if any(p.get("name") == "after" for p in op.get("parameters", []) if isinstance(p, dict)) else None),546                   "idempotency": None,547                   "sdk": {"python": f"client.{fam}.{'files.' if '/files' in path else ''}{'versions.' if '/versions' in path else ''}{'content.' if path.endswith('/content') else ''}{ {'get': 'retrieve' if '{' in path.split('/')[-1] or path.endswith('/content') else 'list', 'post': 'create', 'delete': 'delete'}[m] }()",548                           "node": f"client.{fam}.{'files.' if '/files' in path else ''}{'versions.' if '/versions' in path else ''}{'content.' if path.endswith('/content') else ''}{ {'get': 'retrieve' if '{' in path.split('/')[-1] or path.endswith('/content') else 'list', 'post': 'create', 'delete': 'delete'}[m] }()"},549                   "verification": v or ver("success", 0, "not called", "docs_only"),550                   "sources": [src(f"{REF}/resources/{fam}"), src("https://github.com/openai/openai-openapi (openapi-master.yaml)")]})551EP_OUT = {"$fragment": "openai-containers-skills", "generated_by": "scripts/build_openai_tools_fragments.py", "generated_at": VERIFIED,552          "notes": ["Container lifecycle: status running -> expires 20 min after last_active_at (auto) or expires_after.minutes; deleted containers return object container.deleted.",553                    "Skills: POST /v1/skills fixes default_version=1; versions are immutable; content endpoints return application/zip.",554                    "POST /v1/skills/{skill_id} (UpdateSkillDefaultVersion) is operationId UpdateSkillDefaultVersion; the SDK name may differ."],555          "records": EP}556557for rel, obj in (("tools/openai-tools.json", TOOLS_OUT), ("parameters/openai-tools.json", PARAMS_OUT),558                 ("streaming-events/openai-tools.json", EVENTS_OUT), ("endpoints/openai-containers-skills.json", EP_OUT)):559    out = FRAG / rel560    out.parent.mkdir(parents=True, exist_ok=True)561    out.write_text(json.dumps(obj, indent=1, ensure_ascii=False, default=str) + "\n")562    print(f"wrote {out.relative_to(ROOT)} ({len(obj['records'])} records, {out.stat().st_size // 1024} KB)")563