Skip to main content

OpenAI Agents SDK

Add Sekuire governance to OpenAI Agents SDK workflows. A governed OpenAI client intercepts every chat.completions.create() call the Agents SDK makes - the agent has no idea governance is happening.


What You Get

  • Every chat.completions.create() call is intercepted - both regular and streaming
  • Model allowlist enforcement before the request leaves your process
  • Tool name validation on both request and response
  • Rate limiting (request count and token usage)
  • Post-call token tracking for budget enforcement
  • Zero changes to your agent code - just pass a different client

How It Works

The integration uses a proxy pattern. It wraps the OpenAI client's chat.completions.create method with a governance layer. The OpenAI Agents SDK passes its client through to internal API calls, so every request hits the proxy.

┌─────────────────┐     ┌──────────────────────┐     ┌──────────────┐
│ OpenAI Agents │────>│ Governed OpenAI │────>│ OpenAI API │
│ SDK │ │ Client (Proxy) │ │ │
│ │ │ │ │ │
│ Agent.run() │ │ 1. enforceModel() │ │ chat. │
│ creates a │ │ 2. enforceRateLimit() │ │ completions │
│ chat.completions│ │ 3. enforceTool() │ │ .create() │
│ .create() call │ │ 4. pass to original │ │ │
│ │<────│ 5. post-call checks │<────│ │
└─────────────────┘ └──────────────────────┘ └──────────────┘

Prerequisites

  • Node.js 18+
  • @sekuire/sdk, openai, @openai/agents
  • An OPENAI_API_KEY

Installation

pnpm add @sekuire/sdk openai @openai/agents

Configure Governance

sekuire.yml
project:
name: openai-agents-integration
version: 1.0.0

agent:
name: Research Assistant
system_prompt: ./system_prompt.md
tools: ./tools.json
llm:
provider: openai
model: gpt-4o-mini
api_key_env: OPENAI_API_KEY
temperature: 0.7
max_tokens: 1024
models:
allowed_models:
- gpt-4o-mini
- gpt-4o
blocked_models:
- gpt-3.5-turbo
toolsets:
allowed_tools:
- name: web_search
- name: "files:read"
- name: calculator
blocked_tools:
- file_delete
- env_set

permissions:
network:
enabled: true
require_tls: true
allowed_domains:
- api.openai.com
- "*.wikipedia.org"
blocked_domains:
- "*.malware.net"

rate_limits:
per_agent:
requests_per_minute: 10

Create the Governed Client

The governed client loads your Sekuire policy and wraps the OpenAI client with a proxy that intercepts every API call.

governed-client.ts
import OpenAI from "openai";
import { getAgent, PolicyEnforcer } from "@sekuire/sdk";

export interface GovernedClientOptions {
apiKey?: string;
configPath?: string;
agentName?: string;
}

export async function loadSekuirePolicy(
configPath?: string,
agentName?: string
): Promise<PolicyEnforcer> {
const agent = await getAgent(agentName, undefined, configPath);
const enforcer = agent.getPolicyEnforcer();
if (!enforcer) {
throw new Error("No policy enforcer available. Ensure sekuire.yml has policy configuration.");
}
return enforcer;
}

export async function createGovernedOpenAIClient(
options: GovernedClientOptions = {}
): Promise<OpenAI> {
const { apiKey, configPath = "./sekuire.yml", agentName } = options;

const enforcer = await loadSekuirePolicy(configPath, agentName);

const client = new OpenAI({
apiKey: apiKey ?? process.env.OPENAI_API_KEY,
});

return proxyClient(client, enforcer);
}

The proxy intercepts chat.completions.create and enforces policy before and after every call:

governed-client.ts
function proxyClient(client: OpenAI, enforcer: PolicyEnforcer): OpenAI {
const originalCreate = client.chat.completions.create.bind(client.chat.completions);

client.chat.completions.create = function governedCreate(body: any, options?: any): any {
// Pre-call enforcement
enforcer.enforceModel(body.model);
enforcer.enforceRateLimit("request");

// Validate all tool definitions
if (body.tools) {
for (const tool of body.tools) {
if (tool.type === "function" && tool.function?.name) {
enforcer.enforceTool(tool.function.name);
}
}
}

if (body.stream) {
return proxyStream(originalCreate(body, options) as any, enforcer);
}

const promise = originalCreate(body, options) as Promise<any>;
return promise.then((response: any) => {
// Post-call enforcement
if (response.usage?.total_tokens) {
enforcer.enforceRateLimit("token", response.usage.total_tokens);
}
// Validate tool calls in the response
if (response.choices) {
for (const choice of response.choices) {
if (choice.message?.tool_calls) {
for (const tc of choice.message.tool_calls) {
if (tc.type === "function" && tc.function?.name) {
enforcer.enforceTool(tc.function.name);
}
}
}
}
}
return response;
});
} as any;

return client;
}

For streaming responses, the proxy wraps the async iterator to check tool calls and token usage as chunks arrive:

governed-client.ts
async function proxyStream(
streamPromise: Promise<AsyncIterable<any>>,
enforcer: PolicyEnforcer
): Promise<AsyncIterable<any>> {
const originalStream = await streamPromise;

async function* governedIterator() {
for await (const chunk of originalStream) {
if (chunk.choices) {
for (const choice of chunk.choices) {
if (choice.delta?.tool_calls) {
for (const tc of choice.delta.tool_calls) {
if (tc.type === "function" && tc.function?.name) {
enforcer.enforceTool(tc.function.name);
}
}
}
}
}
if (chunk.usage?.total_tokens) {
enforcer.enforceRateLimit("token", chunk.usage.total_tokens);
}
yield chunk;
}
}

return new Proxy(originalStream as any, {
get(target, prop, receiver) {
if (prop === Symbol.asyncIterator) {
return () => governedIterator();
}
const value = Reflect.get(target, prop, receiver);
return typeof value === "function" ? value.bind(target) : value;
},
});
}

Use with the Agents SDK

Pass the governed client to any Agent:

index.ts
import { Agent, run } from "@openai/agents";
import { PolicyViolationError } from "@sekuire/sdk";
import { createGovernedOpenAIClient } from "./governed-client";

const client = await createGovernedOpenAIClient({
configPath: "./sekuire.yml",
});

const agent = new Agent({
name: "Research Assistant",
instructions: "You are a helpful research assistant. Be concise.",
model: "gpt-4o-mini",
client,
});

try {
const result = await run(agent, "What is the capital of France?");
console.log(result.finalOutput);
} catch (err) {
if (err instanceof PolicyViolationError) {
console.log(`Blocked by policy: [${err.rule}] ${err.message}`);
}
}

Blocked Model Demo

If an agent tries to use a blocked model, the proxy throws before any API call:

const blockedAgent = new Agent({
name: "Blocked Assistant",
instructions: "You should never run.",
model: "gpt-3.5-turbo",
client,
});

try {
await run(blockedAgent, "Hello");
} catch (err) {
if (err instanceof PolicyViolationError) {
console.log(`gpt-3.5-turbo blocked: [${err.rule}] ${err.message}`);
}
}
gpt-3.5-turbo blocked: [model_blocked] Model gpt-3.5-turbo is not allowed

Policy Enforcement Without an API Key

You can validate policy rules without making any API calls. This is useful for testing policy configuration:

import { PolicyViolationError } from "@sekuire/sdk";
import { loadSekuirePolicy } from "./governed-client";

const enforcer = await loadSekuirePolicy("./sekuire.yml");

// Model checks
for (const model of ["gpt-4o-mini", "gpt-4o", "gpt-3.5-turbo"]) {
try {
enforcer.enforceModel(model);
console.log(`${model}: ALLOWED`);
} catch (err) {
if (err instanceof PolicyViolationError) {
console.log(`${model}: BLOCKED`);
}
}
}

// Tool checks
for (const tool of ["web_search", "calculator", "file_delete", "env_set"]) {
try {
enforcer.enforceTool(tool);
console.log(`${tool}: ALLOWED`);
} catch (err) {
if (err instanceof PolicyViolationError) {
console.log(`${tool}: BLOCKED`);
}
}
}

// Rate limit checks
for (let i = 1; i <= 12; i++) {
try {
enforcer.enforceRateLimit("request");
console.log(`Request ${i}: ALLOWED`);
} catch (err) {
if (err instanceof PolicyViolationError) {
console.log(`Request ${i}: BLOCKED`);
}
}
}

What Gets Intercepted

CheckWhenWhat Happens
enforceModel()Before every create() callThrows if model is blocked
enforceRateLimit("request")Before every create() callThrows if rate limit exceeded
enforceTool()Before call (request tools)Throws if tool definition is blocked
enforceTool()After call (response tool calls)Throws if LLM returned a blocked tool call
enforceRateLimit("token")After call (token usage)Throws if token budget exceeded
Stream tool callsDuring streamingThrows mid-stream if blocked tool appears

Next Steps