Guides /OpenAI & Anthropic

OpenAI & Anthropic

Wrap your model client once with heso.wrap(). Every completion is captured, checked against your policy, and signed — with no per-call changes.

The Python SDK ships heso.wrap. Pass your client through it once and you get a drop-in replacement: same methods, same return values, same provider exceptions. The model call now runs through the gate first. HESO records it as an action, checks it against your policy, and when the verdict allows, signs the result into a receipt and appends it to the audit chain.

Wrap an OpenAI client

Call heso.init() once at process start, then wrap the client. Keep calling client.chat.completions.create(…) exactly as you do today.

agent.pypython
import heso
from openai import OpenAI

heso.init()
client = heso.wrap(OpenAI())

resp = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Summarize this contract."}],
)

The wrapper reaches through the nested namespace to the real method, client.chat.completions.create, and gates it as the verb llm_call. The keyword arguments you pass become the action’s fields, so a policy can read model or any other parameter.

Wrap an Anthropic client

Anthropic works the same way. The wrapper reaches client.messages.create and gates it as llm_call, with the same kwargs-become-fields behavior.

agent.pypython
import heso
from anthropic import Anthropic

heso.init()
client = heso.wrap(Anthropic())

resp = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Summarize this contract."}],
)

Gate on the model or any field

Because the call kwargs become the action’s fields, a policy rule can match on them. This rule sends any GPT-4 variant to a human approver before the request goes out — the engine reads the model field straight off the captured call:

heso.tomltoml
[[rule]]
id = "gpt4-needs-approval"
order = 10
enabled = true
subject = { kind = "any" }
verb = "llm_call"
scope = "*"
conditions = [
  { field = "model", op = "regex", value = "^gpt-4", display = "model is a GPT-4 variant" },
]
decision = "require_approval"
approvers = ["oncall"]
sla_minutes = 30

The same pattern matches claude-* models, or any other kwarg. For the full operator set, see Conditions & operators.

Handle a refusal

A refused call raises before the request is sent, so it never reaches the provider — no charge, no completion. Two outcomes, two exceptions:

  • heso.BlockedError — policy returned block. The call is refused outright.
  • heso.SuspendedError — policy returned require_approval. The call is paused until a human co-signs it; the action is captured and an approval opens. The error carries action_hash and rule_id.
agent.pypython
import heso
from openai import OpenAI

heso.init()
client = heso.wrap(OpenAI())

try:
    resp = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "..."}],
    )
except heso.BlockedError:
    # Policy blocked the call. Nothing was sent to the provider.
    fall_back_to_a_cheaper_model()
except heso.SuspendedError as e:
    # Policy routed the call to a human. It is paused until someone approves.
    # e carries action_hash and rule_id; its message prints the next step.
    queue_for_approval(e.action_hash)

Blocking is the default. To observe without enforcing — every call still captured, signed, and audited, but a refusal does not raise — initialize with blocking=False:

agent.pypython
import heso

# Observe-only: refused calls are still captured, signed, and audited,
# but a block does not raise — the call runs ungated.
heso.init(blocking=False)

Gate at the tool level instead

If you would rather gate your own tools than the model client, decorate them with @heso.tool. Each call is captured as a tool_call action, checked against the same policy, and signed. The function body runs only if policy allows.

agent.pypython
import heso

heso.init()

@heso.tool                       # captured as a tool_call action, gated, signed
def lookup_contract(contract_id: str) -> str:
    return db.fetch(contract_id) # body runs only if policy allows

Wrapping the client and decorating tools compose — use both. For the full decorator surface, see the Python SDK reference.

What the receipt proves

A signed completion receipt proves the operator authorized this model call under a known policy — and at L1, that a human approved it with their own key. It does notprove the model’s output was correct or that the action succeeded downstream. HESO records intent and authorization, not the quality of the answer.

Next steps