Skip to content

OpenAI Agents SDK adapter

For the OpenAI Agents SDK. Wraps three things:

  • Tool execution — every tool call becomes a FunctionTool whose on_invoke_tool runs through the Kite Logik policy gate
  • Handoffsregister_handoff() produces a governed Handoff that evaluates an agent.delegate policy event before transferring control
  • Agent-as-toolregister_agent_as_tool() wraps agent.as_tool() with the same delegation governance

This is the right adapter when you're using multi-agent orchestration (handoffs, sub-agents) — the others don't have a first-class delegation hook.

Install

bash
pip install kitelogik openai-agents

openai-agents is not a hard dependency — the import only runs when you call agent_tools(), register_handoff(), or register_agent_as_tool().

Setup

python
from kitelogik import OPAClient, PolicyGate, SessionContext
from kitelogik.adapters.openai_agents import OpenAIAgentsAdapter

gate = PolicyGate(opa_client=OPAClient())
context = SessionContext(
    session_id="sess_001",
    user_role="support_agent",
    session_scopes=["read_customer", "approve_refund_under_100"],
)

adapter = OpenAIAgentsAdapter(gate=gate, context=context)

Register tools

register(name, fn, description="", params=None, action=None). The params dict is the JSON-Schema properties block — Kite Logik wraps it as {"type": "object", "properties": params} for the SDK.

python
async def search_docs(query: str, limit: int = 10) -> str:
    return f"Found {limit} results for {query}"

adapter.register(
    "search_docs",
    search_docs,
    description="Search internal documentation.",
    params={
        "query": {"type": "string"},
        "limit": {"type": "integer", "default": 10},
    },
)

Pass the governed tools to an Agent

python
from agents import Agent, Runner

agent = Agent(
    name="support_agent",
    instructions="Help customers with refunds and lookups.",
    tools=adapter.agent_tools(),       # ← governed FunctionTool list
)

result = await Runner.run(agent, "Refund $50 to cust_001")
print(result.final_output)

Every tool invocation passes through the policy gate. Denied calls return a JSON string {"blocked": true, "reason": "..."} as the tool result — the agent loop continues, the model sees the denial.

strict_json_schema

The adapter sets strict_json_schema=False on the generated FunctionTool so ad-hoc params schemas don't get rejected for missing fields like additionalProperties: false. If you need strict mode, register the tool yourself with the SDK and use @governed on the underlying function.

Multi-agent governance — handoffs

Wrap any handoff with an agent.delegate policy check:

python
from agents import Agent

triage_agent  = Agent(name="triage_agent",  ...)
refund_agent  = Agent(name="refund_agent",  ...)
support_agent = Agent(name="support_agent", ...)

# Wrap the handoff so every delegation hits the policy gate
to_refund = adapter.register_handoff(
    target_agent=refund_agent,
    action="agent.delegate",                  # OPA action — defaults to this
    on_handoff=lambda ctx: print("Handing off to refund"),  # optional
)

triage_agent.handoffs = [to_refund, adapter.register_handoff(support_agent)]

The handoff is evaluated as an agent.delegate event with delegation_target = "refund_agent". If the policy denies, the SDK raises and the handoff is rejected; the optional on_handoff callback only fires on allow.

Input-typed handoffs

Handoffs that take typed input (input_type=...) aren't supported through register_handoff(). Call agents.handoff() directly with a custom callback that invokes governed_handoff yourself.

Multi-agent governance — agent-as-tool

Sometimes you want a sub-agent exposed to the parent as a callable tool rather than a handoff. Wrap that with delegation governance too:

python
report_agent = Agent(name="report_agent", ...)

generate_report_tool = adapter.register_agent_as_tool(
    agent=report_agent,
    tool_name="generate_report",
    tool_description="Produce a quarterly report from the data warehouse.",
    action="agent.delegate",
)

main_agent = Agent(
    name="main_agent",
    tools=[*adapter.agent_tools(), generate_report_tool],
)

Same agent.delegate evaluation as a handoff. Denied invocations return {"blocked": true, "reason": "..."} so the parent agent can recover.

Methods at a glance

MethodUse
register(name, fn, description="", params=None, action=None)Register a tool. Chainable.
agent_tools()Returns governed FunctionTool list for Agent(tools=…).
register_handoff(target_agent, action=…, on_handoff=…)Build a governed Handoff for Agent(handoffs=…).
register_agent_as_tool(agent, tool_name, tool_description, action=…)Build a governed FunctionTool from agent.as_tool().

Source

kitelogik/adapters/openai_agents.py on GitHub.

Released under the Apache 2.0 License.