#!/usr/bin/env python3 """Build the OpenAI *tools* fragments (tools, parameters, streaming events, containers+skills endpoints). Inputs : sources/openai/openapi/openapi-master.yaml (schemas), model pages "Supported tools" sections, live findings hard-coded below (from tmp-live/tools/*.json, run 2026-09-18). Outputs: generated/fragments/tools/openai-tools.json generated/fragments/parameters/openai-tools.json generated/fragments/streaming-events/openai-tools.json generated/fragments/endpoints/openai-containers-skills.json Run : .venv/bin/python scripts/build_openai_tools_fragments.py """ from __future__ import annotations import glob import json import re from pathlib import Path import yaml ROOT = Path(__file__).resolve().parent.parent SPEC = ROOT / "sources/openai/openapi/openapi-master.yaml" FRAG = ROOT / "generated/fragments" RETRIEVED = "2026-09-18" VERIFIED = "2026-09-18" DOCS = "https://developers.openai.com/api/docs" REF = "https://developers.openai.com/api/reference" spec = yaml.load(SPEC.read_text(), Loader=yaml.CSafeLoader) S = spec["components"]["schemas"] def resolve(o, depth=0, seen=()): """Inline $refs (bounded) so fragments are self-contained.""" if depth > 14: return {"$comment": "depth-limited"} if isinstance(o, dict): if "$ref" in o: n = o["$ref"].split("/")[-1] if n in seen: return {"$ref": n, "$comment": "recursive"} return resolve(S[n], depth + 1, seen + (n,)) return {k: resolve(v, depth + 1, seen) for k, v in o.items() if k not in ("x-oaiMeta", "x-stainless-naming", "x-oaiTypeLabel", "x-oaiExpandable")} if isinstance(o, list): return [resolve(x, depth + 1, seen) for x in o] return o def src(url: str) -> dict: return {"url": url, "retrieved_at": RETRIEVED} def ver(result: str, status: int, note: str, method: str = "live_api") -> dict: return {"method": method, "verified_at": VERIFIED, "result": result, "http_status": status, "request_note": note} # --------------------------------------------------------------------------- model matrix (from model pages) MODEL_TOOL_KEY = { # tool type -> key used in model pages "Supported tools" "function": "function_calling", "custom": "function_calling", "namespace": "function_calling", "web_search": "web_search", "web_search_2025_08_26": "web_search", "web_search_preview": "web_search", "web_search_preview_2025_03_11": "web_search", "file_search": "file_search", "code_interpreter": "code_interpreter", "image_generation": "image_generation", "mcp": "mcp", "shell": "hosted_shell", "apply_patch": "apply_patch", "computer": "computer_use", "computer_use_preview": "computer_use", "tool_search": "tool_search", "skill_reference": "skills", } matrix: dict[str, list[str]] = {} for f in sorted(glob.glob(str(ROOT / "sources/openai/pages/api/docs/models/*.md"))): t = Path(f).read_text() m = re.search(r"## Supported tools\s*\n(.*?)(?=\n## |\Z)", t, re.S) if m: matrix[Path(f).stem] = re.findall(r"^- *([a-z_0-9]+)", m.group(1), re.M) def models_for(tool_type: str) -> list[str]: key = MODEL_TOOL_KEY.get(tool_type) return sorted(m for m, tools in matrix.items() if key in tools) if key else [] # --------------------------------------------------------------------------- tool records RESP = "POST /v1/responses" CHAT = "POST /v1/chat/completions" LIVE = ["DOCUMENTED", "LIVE_VERIFIED"] NANO = "gpt-5.4-nano" TOOL_EVENTS = { "function": ["response.output_item.added", "response.function_call_arguments.delta", "response.function_call_arguments.done", "response.output_item.done"], "custom": ["response.output_item.added", "response.custom_tool_call_input.delta", "response.custom_tool_call_input.done", "response.output_item.done"], "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"], "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"], "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"], "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"], "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"], "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"], "apply_patch": ["response.output_item.added", "response.apply_patch_call_operation_diff.delta", "response.apply_patch_call_operation_diff.done", "response.output_item.done"], "computer_use_preview": ["response.output_item.added", "response.output_item.done"], "computer": ["response.output_item.added", "response.output_item.done"], "tool_search": ["response.output_item.added", "response.output_item.done"], "namespace": ["response.output_item.added", "response.function_call_arguments.delta", "response.function_call_arguments.done", "response.output_item.done"], "programmatic_tool_calling": ["response.output_item.added", "response.output_item.done"], "local_shell": [], "skill_reference": [], } TOOL_SCHEMA = { "function": "FunctionTool", "custom": "CustomToolParam", "namespace": "NamespaceToolParam", "tool_search": "ToolSearchToolParam", "programmatic_tool_calling": "ProgrammaticToolCallingParam", "web_search": "WebSearchTool", "web_search_2025_08_26": "WebSearchTool", "web_search_preview": "WebSearchPreviewTool", "web_search_preview_2025_03_11": "WebSearchPreviewTool", "file_search": "FileSearchTool", "code_interpreter": "CodeInterpreterTool", "computer": "ComputerTool", "computer_use_preview": "ComputerUsePreviewTool", "image_generation": "ImageGenTool", "mcp": "MCPTool", "shell": "FunctionShellToolParam", "local_shell": "LocalShellToolParam", "apply_patch": "ApplyPatchToolParam", "skill_reference": "SkillReferenceParam", } RESULT_ITEM = { "function": ["FunctionToolCall", "FunctionCallOutputItemParam"], "custom": ["CustomToolCall", "CustomToolCallOutput"], "namespace": ["FunctionToolCall", "CustomToolCall"], "tool_search": ["ToolSearchCall", "ToolSearchOutput"], "programmatic_tool_calling": ["FunctionToolCall"], "web_search": ["WebSearchToolCall"], "web_search_2025_08_26": ["WebSearchToolCall"], "web_search_preview": ["WebSearchToolCall"], "web_search_preview_2025_03_11": ["WebSearchToolCall"], "file_search": ["FileSearchToolCall"], "code_interpreter": ["CodeInterpreterToolCall"], "computer": ["ComputerToolCall", "ComputerToolCallOutput"], "computer_use_preview": ["ComputerToolCall", "ComputerToolCallOutput"], "image_generation": ["ImageGenToolCall"], "mcp": ["MCPListTools", "MCPToolCall", "MCPApprovalRequest", "MCPApprovalResponse"], "shell": ["FunctionShellCall", "FunctionShellCallOutputItemParam"], "local_shell": ["LocalShellToolCall", "LocalShellToolCallOutput"], "apply_patch": ["ApplyPatchToolCall", "ApplyPatchToolCallOutputItemParam"], "skill_reference": ["FunctionShellCall"], } TC_COMMON = {"modes": ["none", "auto", "required"], "allowed_tools": {"type": "allowed_tools", "mode": "auto|required", "tools": ["{type,name,...}"]}} T = {} # per-tool hand-written metadata def tool(type_, name, category, description, endpoints, tc, parallel, result_shape, billing, limitations, security, status, examples, verification, extra_sources=(), beta=None, notes=None, models=None): ex_dir = examples rec = { "provider": "openai", "name": name, "type": type_, "category": category, "description": description, "compatible_models": models if models is not None else models_for(type_), "compatible_endpoints": endpoints, "parameters_schema": resolve(S[TOOL_SCHEMA[type_]]), "tool_choice_support": tc, "parallel": parallel, "streaming_events": TOOL_EVENTS.get(type_, []), "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_, [])], "summary": result_shape}, "billing": billing, "limitations": limitations, "security": security, "beta_header": beta, "status": status, "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 {}, "verification": verification, "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)")], "last_verified": VERIFIED, } if notes: rec["notes"] = notes T[type_] = rec PRICING = f"{DOCS}/pricing#built-in-tools" tool("function", "Function calling (custom function tool)", "client", "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.", [RESP, CHAT, "POST /v1/realtime (session.tools)", "Agents API (agent tool config)"], {**TC_COMMON, "specific": {"type": "function", "name": ""}, "chat_completions": {"type": "function", "function": {"name": ""}}}, {"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."}, "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}.", {"model": "Function definitions are injected into the system message and billed as input tokens; call arguments are output tokens.", "per_call": None, "source": PRICING}, ["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."], ["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)."], LIVE, "function-calling", {"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"), "streaming": ver("success", 200, "response.output_item.added -> response.function_call_arguments.delta x N -> response.function_call_arguments.done -> response.output_item.done"), "strict_invalid_schema": ver("success", 400, "missing additionalProperties:false -> 400 invalid_request_error code=invalid_function_parameters param=tools[0].parameters"), "allowed_tools": ver("success", 200, "tool_choice {type:allowed_tools, mode:required, tools:[{type:function,name:get_weather}]} + parallel_tool_calls:false -> function_call"), "chat_completions": ver("success", 200, "gpt-4.1-nano chat.completions tool_choice forced -> choices[0].message.tool_calls[0].function")}, [f"{DOCS}/guides/function-calling", f"{DOCS}/guides/async-tool-calling"]) tool("custom", "Custom tool (free-form text or CFG grammar)", "client", "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).", [RESP, CHAT], {**TC_COMMON, "specific": {"type": "custom", "name": ""}, "chat_completions": {"type": "custom", "custom": {"name": ""}}}, {"supported": True, "parameter": "parallel_tool_calls"}, "Output item `custom_tool_call` {id, call_id, name, input: string, status}; reply with `custom_tool_call_output` {call_id, output}.", {"model": "Token billing only.", "source": PRICING}, ["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}}}}."], ["Grammar constrains syntax, not semantics — still validate the input before acting on it."], LIVE, "custom-tools", {"regex_grammar": ver("success", 200, f"{NANO}: format grammar regex ^(yes|no)$ forced -> custom_tool_call input='yes'"), "text_format": ver("success", 200, f"{NANO}: default text format, tool_choice required -> custom_tool_call"), "streaming": ver("success", 200, "response.custom_tool_call_input.delta -> .done")}, [f"{DOCS}/guides/function-calling#custom-tools"]) tool("namespace", "Namespace (group of function/custom tools)", "client", "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`.", [RESP], {**TC_COMMON, "note": "tool_choice targets the inner tool by name"}, {"supported": True, "parameter": "parallel_tool_calls"}, "Inner tool call items gain `namespace: `; `function_call_output` may echo `namespace`.", {"model": "Token billing only.", "source": PRICING}, ["`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)."], ["Same as function calling."], LIVE, "function-calling", {"namespace_call": ver("success", 200, f"{NANO}: namespace 'weather' wrapping get_weather, tool_choice required -> function_call with namespace='weather'")}, [f"{DOCS}/guides/function-calling#namespaces", f"{DOCS}/guides/tools-tool-search"]) tool("tool_search", "Tool search (deferred tool loading)", "hosted", "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.", [RESP, "Agents API (session tools)"], {"modes": ["auto", "required", "none"], "note": "tool_choice applies to the tools currently callable in the turn"}, {"supported": False, "notes": "Search happens before the eventual function call."}, "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.", {"model": "Token billing only; saves input tokens by not sending deferred definitions up front.", "source": PRICING}, ["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."], ["Client-executed search lets the app decide which tools exist per turn — keep an allowlist."], LIVE, "tool-search", {"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'}")}, [f"{DOCS}/guides/tools-tool-search"]) tool("programmatic_tool_calling", "Programmatic Tool Calling", "hosted", "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}`.", [RESP, "Agents API (enabled by default)"], {"modes": ["auto", "required", "none"], "specific": {"type": "programmatic_tool_calling"}}, {"supported": True, "notes": "Program can call tools in parallel inside the runtime."}, "Standard Responses object; nested `function_call` items with caller.type = program; results returned as `function_call_output` with the same caller.", {"model": "Token billing only.", "source": PRICING}, ["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)."], ["Require application-level approval for high-impact actions regardless of caller; MCP `require_approval` can pause the program."], ["DOCUMENTED", "UNVERIFIED"], "", {"docs": ver("success", 0, "not called", "docs_only")}, [f"{DOCS}/guides/tools-programmatic-tool-calling"], models=[]) WS_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`."] WS_SEC = ["Treat page content as untrusted (prompt injection); prefer `filters.allowed_domains` for sensitive workflows.", "`external_web_access: false` runs cache-only."] WS_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} tool("web_search", "Web search (hosted)", "hosted", "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]).", [RESP, "Realtime/Live (web_search tool in Live sessions)", "Agents API"], {**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."}, {"supported": False, "notes": "Built-in tools are not batched with parallel function calls."}, "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}.", WS_BILL, WS_LIMITS, WS_SEC, LIVE, "web-search", {"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"), "auto_no_search": ver("success", 200, "same prompt with tool_choice auto -> model answered without calling the tool (search is optional under auto)"), "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"), "streaming": ver("success", 200, "response.web_search_call.in_progress -> searching -> completed")}, [f"{DOCS}/guides/tools-web-search", PRICING]) tool("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}, "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"]) tool("web_search_preview", "Web search preview (legacy)", "hosted", "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`.", [RESP], {**TC_COMMON, "specific": {"type": "web_search_preview"}}, {"supported": False}, "Same `web_search_call` item.", WS_BILL, WS_LIMITS + ["Docs recommend migrating to web_search."], WS_SEC, ["DOCUMENTED", "LEGACY", "UNVERIFIED"], "web-search", {"docs": ver("success", 0, "not called (legacy)", "docs_only")}, [f"{DOCS}/guides/tools-web-search"]) tool("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}, "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"]) tool("file_search", "File search (vector stores)", "hosted", "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.", [RESP, "Assistants API (legacy)"], {**TC_COMMON, "specific": {"type": "file_search"}}, {"supported": False}, "Output item `file_search_call` {id, status, queries: [...], results: [{file_id, filename, score, text, attributes, vector_store_id}] | null} + message with `file_citation` annotations.", {"per_call": "$2.50 / 1k calls", "storage": "$0.10 / GB / day (1 GB free)", "source": PRICING}, ["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)."], ["Only upload trusted files: retrieved text is model input (prompt injection)."], LIVE, "file-search", {"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)"), "max_output_tokens_32": ver("failure", 200, "with max_output_tokens 32 the response was incomplete and file_search_call.status='incomplete'"), "streaming": ver("success", 200, "response.file_search_call.in_progress -> searching -> completed")}, [f"{DOCS}/guides/tools-file-search", PRICING]) tool("code_interpreter", "Code interpreter (Python sandbox container)", "hosted", "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]`.", [RESP, "Assistants API (legacy)"], {**TC_COMMON, "specific": {"type": "code_interpreter"}}, {"supported": False}, "Output item `code_interpreter_call` {id, status: in_progress|interpreting|completed|incomplete|failed, container_id, code, outputs: [{type: logs, logs} | {type: image, url}] | null}.", {"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}, ["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'."], ["Network-enabled containers: prompt-injection driven exfiltration risk; only allowlist trusted domains; use domain_secrets instead of raw credentials."], LIVE, "code-interpreter", {"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)"), "streaming": ver("success", 0, "not streamed live (cost); events from spec", "docs_only")}, [f"{DOCS}/guides/tools-code-interpreter", f"{REF}/resources/containers", PRICING]) CU_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."] tool("computer_use_preview", "Computer use (preview tool + computer-use-preview model)", "client", "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.", [RESP], {**TC_COMMON, "specific": {"type": "computer_use_preview"}}, {"supported": False}, "`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.", {"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"}, ["Requires `truncation: auto` with computer-use-preview.", "Our key: model computer-use-preview -> 404 model_not_found (tiered/limited access)."], CU_SEC, ["DOCUMENTED", "PREVIEW", "ACCOUNT_RESTRICTED"], "computer-use", {"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'")}, [f"{DOCS}/guides/tools-computer-use", f"{DOCS}/guides/tools-computer-use-integration", f"{DOCS}/models/computer-use-preview"]) tool("computer", "Computer tool (current)", "client", "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.", [RESP], {**TC_COMMON, "specific": [{"type": "computer"}, {"type": "computer_use"}]}, {"supported": False}, "Same as computer_use_preview plus `actions[]` batches.", {"model": "Token billing (images as input tokens)", "source": PRICING}, ["Not live-tested (cost/safety); docs recommend code-execution integration for GPT-6 Astra."], CU_SEC, ["DOCUMENTED", "UNVERIFIED"], "computer-use", {"docs": ver("success", 0, "not called", "docs_only")}, [f"{DOCS}/guides/tools-computer-use", f"{DOCS}/guides/tools-computer-use-integration"]) tool("image_generation", "Image generation tool", "hosted", "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`.", [RESP], {**TC_COMMON, "specific": {"type": "image_generation"}}, {"supported": False}, "Output item `image_generation_call` {id, status: in_progress|generating|completed|failed, result: base64|null, revised_prompt, size, quality, background, output_format, action}.", {"per_image": "Image model token pricing (see pricing fragment / image generation guide calculator); mainline model tokens billed separately", "source": f"{DOCS}/pricing#image-generation"}, ["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-*."], ["Moderation `low` relaxes filtering; images may be revised by the mainline model (revised_prompt)."], ["DOCUMENTED", "LIVE_VERIFIED"], "image-generation", {"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)")}, [f"{DOCS}/guides/tools-image-generation"]) tool("mcp", "Remote MCP servers, connectors and Secure MCP Tunnel", "mcp", "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`.", [RESP, "POST /v1/realtime (mcp tool)", "Agents API (MCP connections)", "Deep research models"], {**TC_COMMON, "specific": {"type": "mcp", "server_label": "