OpenAI¶
Available · Chat Completions and Responses API · no SDK needed
The renderers return plain dicts. Import the functions by name
(from agent_adapter.integrations.openai import ...), so the openai SDK name stays free.
Functions¶
Function (agent_adapter.integrations.openai) |
Returns |
|---|---|
chat_tools(registry, *, strict=True) |
every tool in Chat Completions format |
chat_tool(spec, *, strict=True) |
one tool: {"type": "function", "function": {...}} |
responses_tools(registry, *, strict=True) |
every tool in Responses API format |
responses_tool(spec, *, strict=True) |
one tool, flat: {"type", "name", "description", "parameters", "strict"} |
INTEGRATION |
the integration object (Chat format); OpenAIIntegration(api="responses", strict=False) for others. bind_context(call) reads call_id, else id |
Round trip¶
Pass the rendered list as tools=. Run each tool call the model sends through one
Executor, never with spec.invoke: invoke skips approval, budgets, run guards, the
cache and the ledger, so a confirm=True tool would run unapproved.
from agent_adapter import Executor, ToolCall
from agent_adapter.integrations.openai import chat_tools
executor = Executor(registry) # once per process; no approval port, so confirm tools are denied
tools = chat_tools(registry) # pass as tools= to chat.completions.create
replies = []
for call in response.choices[0].message.tool_calls:
# arguments is a JSON string; strict mode sends null for "use the default".
args = json.loads(call.function.arguments)
result = await executor.run_call(
ToolCall(name=call.function.name, arguments=args, id=call.id)
)
data = result.to_dict() # a denial, bad arguments or an unknown tool land in "error"
payload = data["value"] if result.ok else {"error": data["error"]}
replies.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(payload)})
sequenceDiagram
participant App
participant OA as OpenAI API
participant Ex as Executor
App->>OA: chat.completions.create(messages, tools=chat_tools(registry))
OA-->>App: tool_calls[i]: id, function.name, function.arguments (JSON string)
App->>Ex: run_call(ToolCall(name, json.loads(arguments), id))
Ex-->>App: ToolResult (approval, budgets and guards applied)
App->>OA: {"role": "tool", "tool_call_id": id, "content": value or error}
| Step | Notes |
|---|---|
| Executor | build it once, with approval= if any tool has confirm=True; with no port such a call is denied (approval) |
| Several calls | the loop runs them in order; executor.run_many(calls) runs them at once and keeps their order |
| Whole loop | run_tool_loop with OpenAIModel sends the calls and answers them for you |
| Responses API | the call is a function_call item: use its name, arguments and call_id, and answer with {"type": "function_call_output", "call_id", "output"} |
Strict mode¶
strict=True (the default) makes every field required and optional ones nullable, and
sets additionalProperties: false everywhere. Each argument's default is written into
its description (Default: "c".). When the model sends null, validation applies your
default. See Strict mode and nulls.
strict=False gives the plain schema: optional fields stay optional and keep default.
Errors to expect¶
run_call returns every failure as result.error; it never raises for them.
result.error.kind |
When |
|---|---|
tool-not-found |
the model names a tool you did not register |
tool-args-invalid |
the arguments fail validation; the error tells the model what to fix |
approval-denied |
a confirm tool was denied, or no approval port is set |
approval-pending |
a DeferredApproval port parked the call (human in the loop) |
json.loads raises json.JSONDecodeError when the model sends malformed JSON. That
comes from the standard library, not the adapter.