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
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.
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:
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:
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:
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
| Check | When | What Happens |
|---|---|---|
enforceModel() | Before every create() call | Throws if model is blocked |
enforceRateLimit("request") | Before every create() call | Throws 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 calls | During streaming | Throws mid-stream if blocked tool appears |
Next Steps
- Vercel AI SDK Integration - If you use the Vercel AI SDK
- LangChain Integration - If you use LangChain
- Policy Enforcement - Full
PolicyEnforcerAPI reference - Example source