OpenAI adapter for governing tool execution with BIGHUB.
Project description
bighub-openai - Production-Safe OpenAI Agents
bighub-openai makes OpenAI tool-calling agents production-safe.
Where it fits:
OpenAI Responses API -> bighub-openai -> BIGHUB Control Plane -> execute / block / approve
bighub-openai depends on both bighub and the official openai Python SDK.
It targets the Responses API (client.responses.create) and requires
openai>=2.0.0,<3.0.0.
Useful links:
- Core SDK package (PyPI): bighub
- OpenAI Responses API docs: platform.openai.com/docs/api-reference/responses
Before any registered tool executes, the adapter:
- Validates the action against BIGHUB policies
- Enforces execution boundaries
- Blocks or escalates risky decisions
- Ingests governed execution into Future Memory for pattern learning
- Sets
store=falseby default so tool-call data is not persisted on OpenAI servers
Decision enforcement outcomes:
allowed-> execute toolblocked-> do not execute toolrequires_approval-> do not execute tool; adapter output status isapproval_required
Install
pip install bighub-openai
Requires Python 3.9+. Recommended: use a dedicated virtual environment to avoid
dependency conflicts with other packages that pin different openai versions.
Quickstart (5 lines)
import os
from bighub_openai import GuardedOpenAI
def refund_payment(order_id: str, amount: float) -> dict:
return {"ok": True, "order_id": order_id, "amount": amount}
guard = GuardedOpenAI(
openai_api_key=os.getenv("OPENAI_API_KEY"),
bighub_api_key=os.getenv("BIGHUB_API_KEY"),
actor="AI_AGENT_001",
domain="customer_transactions",
)
guard.tool("refund_payment", refund_payment, value_from_args=lambda a: a["amount"])
response = guard.run(
messages=[
{"role": "user", "content": "Refund order ord_123 for 199.99"},
],
model="gpt-4.1",
)
print(response)
Async quickstart
import os
from bighub_openai import AsyncGuardedOpenAI
guard = AsyncGuardedOpenAI(
openai_api_key=os.getenv("OPENAI_API_KEY"),
bighub_api_key=os.getenv("BIGHUB_API_KEY"),
actor="AI_AGENT_001",
domain="customer_transactions",
)
guard.tool(...) auto-generates a strict JSON schema from your Python function signature.
Provide parameters_schema=... only when you need custom schema constraints.
run(...) returns a structured payload with both model output and governance execution events:
{
"llm_response": {...},
"execution": {
"events": [...],
"last": {
"tool": "refund_payment",
"status": "executed" | "blocked" | "approval_required",
"decision": {...}
}
}
}
run_stream(...) yields structured stream events mapped from the Responses API
streaming protocol:
| Adapter event type | Responses API event | Description |
|---|---|---|
llm_delta |
response.output_text.delta |
Incremental text token |
llm_text_done |
response.output_text.done |
Complete text segment |
output_item_added |
response.output_item.added |
New output item started |
function_call_args_delta |
response.function_call_arguments.delta |
Streaming function args |
function_call_args_done |
response.function_call_arguments.done |
Complete function args |
refusal_delta |
response.refusal.delta |
Model refusal chunk |
response_done |
response.completed |
Response finished |
response_failed |
response.failed |
Response error |
execution_event |
(adapter) | Governed tool decision/execution result |
final_response |
(adapter) | Final payload, same shape as run(...) |
Example:
for event in guard.run_stream(
messages=[{"role": "user", "content": "Refund order ord_123 for 199.99"}],
model="gpt-4.1",
):
if event["type"] == "llm_delta":
print(event["delta"], end="")
elif event["type"] == "execution_event":
print("\n[tool]", event["event"]["tool"], event["event"]["status"])
elif event["type"] == "final_response":
print("\nDone:", event["response"]["output_text"])
Async example:
async for event in guard.run_stream(
messages=[{"role": "user", "content": "Refund order ord_123 for 199.99"}],
model="gpt-4.1",
):
if event["type"] == "llm_delta":
print(event["delta"], end="")
elif event["type"] == "execution_event":
print("\n[tool]", event["event"]["tool"], event["event"]["status"])
elif event["type"] == "final_response":
print("\nDone:", event["response"]["output_text"])
Decision modes
decision_mode="submit"(default) -> callsclient.actions.submit(...)decision_mode="submit_v2"-> callsclient.actions.submit_v2(...)
You can set it globally on GuardedOpenAI(...) or per tool in register_tool(..., decision_mode="submit_v2").
Audit hook
Use on_decision to forward structured events to your observability stack:
def log_decision(event: dict) -> None:
# Avoid dumping full payloads/secrets to logs.
minimal = {
"trace_id": event.get("trace_id"),
"request_id": event.get("request_id"),
"event_id": event.get("event_id"),
"allowed": event.get("allowed"),
}
print(minimal)
guard = GuardedOpenAI(..., on_decision=log_decision)
Event payload contract includes stable identifiers:
trace_id(run correlation id)request_id(BIGHUB validation id when available)event_id(adapter event id)
Silent mode
When you want to evaluate governance without executing tools:
decision = guard.check_tool("refund_payment", {"order_id": "ord_123", "amount": 199.0})
Approval loop helper
Use HITL helpers when a tool decision returns requires_approval:
run_with_approval(...)runs, captures pending approval, and can resume with a callbackresume_after_approval(...)resolves one approval request then resumes tool execution
Approval callbacks should run server-side (not in the client) to avoid exposing approval credentials.
Example:
result = guard.run_with_approval(
messages=[{"role": "user", "content": "Refund order ord_123 for 5000"}],
model="gpt-4.1",
on_approval_required=lambda ctx: {
"resolution": "approved",
"comment": "approved by on-call",
},
)
print(result["approval_loop"])
Async example:
result = await guard.run_with_approval(
messages=[{"role": "user", "content": "Refund order ord_123 for 5000"}],
model="gpt-4.1",
on_approval_required=lambda ctx: {
"resolution": "approved",
"comment": "approved by on-call",
},
)
print(result["approval_loop"])
Future memory ingest
Note: Future Memory is experimental and may contain bugs.
By default, GuardedOpenAI ingests governed execution events to BIGHUB future memory:
guard = GuardedOpenAI(..., memory_enabled=True, memory_source="openai_adapter")
The adapter ingests memory in best-effort mode:
- short timeout (
memory_ingest_timeout_ms, default300) - exceptions are swallowed
- governance execution path is never blocked by telemetry
Each event includes idempotency/versioning metadata for stable analytics:
event_id(dedupe key)seq(position within run)schema_version(current:1)source_version(for examplebighub-openai@0.2.x)
This powers pattern learning and context endpoints such as
client.actions.memory_context(...).
Fail Modes
fail_mode="closed"(default): if policy check fails, tool execution is blocked.fail_mode="open": if policy check fails unexpectedly, tool execution proceeds.
Provider resilience knobs
GuardedOpenAI and AsyncGuardedOpenAI expose provider resilience settings:
provider_timeout_secondsprovider_max_retriesprovider_retry_backoff_secondsprovider_retry_max_backoff_secondsprovider_retry_jitter_secondsprovider_circuit_breaker_failures(set>0to enable)provider_circuit_breaker_reset_seconds
Retries only trigger on transient OpenAI errors (APIConnectionError,
APITimeoutError, RateLimitError). Non-retryable errors (e.g.
BadRequestError, AuthenticationError) fail immediately.
Example:
import os
guard = GuardedOpenAI(
openai_api_key=os.getenv("OPENAI_API_KEY"),
bighub_api_key=os.getenv("BIGHUB_API_KEY"),
actor="AI_AGENT_001",
domain="customer_transactions",
provider_timeout_seconds=20,
provider_max_retries=3,
provider_retry_backoff_seconds=0.2,
provider_retry_max_backoff_seconds=2.0,
provider_retry_jitter_seconds=0.15,
provider_circuit_breaker_failures=5,
provider_circuit_breaker_reset_seconds=30,
)
Responses API compatibility
This adapter is built specifically for the OpenAI Responses API
(client.responses.create). Key design choices:
- Tool schema: Uses flat
{type: "function", name, parameters, strict}format (not the nested{type: "function", function: {...}}from Chat Completions). - Multi-turn context: Uses
previous_response_idfor efficient multi-turn loops. Reasoning items from prior turns are preserved automatically by the API. store: false: Set by default on all provider calls. Override viaextra_create_args={"store": True}if you need statefulness.- Output parsing: Handles
response.output_textnatively and falls back toresponse.output[].content[].textextraction for edge cases. - Function calls: Parses
function_callitems fromresponse.output[]and ignores reasoning, message, and other item types safely. function_call_output: Returns results as{type: "function_call_output", call_id, output}per the Responses API specification.
Notes
- This adapter is intentionally provider-specific.
- Core policy and transport behavior remain in the
bighubSDK. - Compatible with
openai>=2.0.0,<3.0.0(Responses API).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bighub_openai-0.2.3.tar.gz.
File metadata
- Download URL: bighub_openai-0.2.3.tar.gz
- Upload date:
- Size: 21.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27c1fc5ffa6f8f33795dad1b355f18de0cf93a0cbc47f821af784ea32f454fa8
|
|
| MD5 |
dde26f25900de5fb0d4c20aee69595b4
|
|
| BLAKE2b-256 |
16e80abb335f9c6ec6e6cde359c4b8ec2f70a8cc92a08e4ab2e08a27670715bb
|
File details
Details for the file bighub_openai-0.2.3-py3-none-any.whl.
File metadata
- Download URL: bighub_openai-0.2.3-py3-none-any.whl
- Upload date:
- Size: 14.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
adc6bdba522963022ef7252ceac564ba41453c898600979d0e75313d4ac606c3
|
|
| MD5 |
b9dac8aed90f11e3f1fd31d7c9787219
|
|
| BLAKE2b-256 |
069ee4099e837ce3a73a8153773723ca7fea27b31a99a82a7c698cc15f2ffb8b
|