Quickstart · Python · ~5 min

Python SDK

Add behavioral intelligence to any Python LLM app. The fastest path to value is one call: observe()reads a conversation turn your own model already produced and hands back the user's emotional state and a risk level — no LLM config, no onboarding, nothing to block your reply.

5 minutes to your first signal
Install → key → observe() → read r.user_state.mood and r.risk.level. That's the whole loop. Everything below it — full processing, human takeover — is opt-in once you want it.

1. Install

bash
pip install humane-ai

The package requires Python 3.10+ and ships both a synchronous (HumaneClient) and an async (AsyncHumaneClient) client. It works in any web framework, worker, or script.

2. Get an API key

Sign up on your Humane dashboard, then go to Dashboard → API and click New Key. The raw key is shown once — copy it immediately. It looks like hx_2dc54433….

3. Your first call — observe()

You already have a chatbot — a support assistant, an AI companion, a coaching app. It generates replies with your own model. Send each completed turn to observe() and get back behavioral state + risk in one round-trip. It never generates a reply and never blocks yours.

python
from humane_ai import HumaneClient

client = HumaneClient(api_key="hx_...")

r = client.observe(
    user_id="u_8f2",                          # your stable id for this end-user
    user_message="honestly I don't know why I keep trying",
    assistant_message="I'm here with you — tell me more.",  # optional: your AI's reply
)

print(r.user_state.mood)        # 0.28  — lower = lower mood
print(r.user_state.energy)      # 0.41
print(r.user_state.trust)       # 0.40
print(r.risk.level)             # "none" | "elevated" | "critical"
print(r.risk.flags)             # [] or e.g. ["self_harm"]
print(r.alerts)                 # [Alert(type="safety", severity="critical"), ...]
What observe() returns
An ObserveResult with three fields: user_state (the four scalars mood / energy / trust / sentiment), risk (.level + .flags), and alerts (a list raised on elevated / critical risk). Wire r.risk.level into your own routing — escalate to a human, soften the next reply, or just log it.

Prefer the context-manager form so the HTTP session always closes cleanly:

python
with HumaneClient(api_key="hx_...") as client:
    r = client.observe(user_id="u_8f2", user_message="I feel a lot better today")
    if r.risk.level != "none":
        notify_on_call(user_id="u_8f2", flags=r.risk.flags)
observe() works before onboarding
observe() is the frictionless entry point — it runs on heuristics, needs no model configured, and is the one call that works the moment you have a key. Read more in Concepts → Observe.

4. Let Humane generate the reply — process()

When you want Humane to own the reply — run the behavioral engines, generate a response with your configured model, and apply the safety gate — use process() instead of observe().

python
r = client.process(
    user_id="u_8f2",
    message="I'm anxious about tomorrow",
    conversation_history=[                       # optional prior turns
        {"role": "user", "content": "hey"},
        {"role": "assistant", "content": "hi! how are you doing?"},
    ],
    channel="api",                               # origin tag (default "api")
    metadata={"plan": "pro"},                    # optional free-form bag
)

print(r.response)               # the AI reply (or None if blocked / no model)
print(r.user_state.mood)        # 0.31
print(r.safety.action)          # "PROCEED" | "HOLD" | "BLOCK"
print(r.safety.risk_score)      # 0.04
print(r.safety.flags)           # []
print(r.raw["timing"])          # rich timing / analysis / context blocks live on .raw
process() needs an LLM configured (409 if not)
Unlike observe(), process() must generate a reply — so the tenant has to have finished onboarding (an LLM provider + model selected). If not, the call returns 409 with body {"code": "onboarding_required"}. Catch it and fall back to observe() (which never needs onboarding) or prompt the user to finish setup:
python
import requests

try:
    r = client.process(user_id="u_8f2", message="I'm anxious about tomorrow")
    reply = r.response
except requests.exceptions.HTTPError as exc:
    if exc.response.status_code == 409 and \
       exc.response.json().get("code") == "onboarding_required":
        # No model configured yet — monitor instead of generating.
        r = client.observe(user_id="u_8f2", user_message="I'm anxious about tomorrow")
        reply = my_own_model(user_id="u_8f2")   # your existing generation path
    else:
        raise

5. Bring a human into the loop — takeover

Sometimes the right move is to step a real person into the conversation. The client.takeover namespace lets an operator open a session, send operator-authored messages (which pause the AI), and hand back to the bot when done.

Takeover helpers are rolling out
The client.takeover Python helpers shown below are shipping incrementally. The underlying REST endpoints under /api/sdk/takeover are live today — see Concepts → Human Takeover → REST endpoints to drive the same lifecycle directly while the typed wrappers land.
python
# An operator decides to step in for this user.
session = client.takeover.start(
    user_id="u_8f2", operator_id="op_1", operator_name="Dana",
)

# Ask the AI co-pilot for reply options, then send one as the operator.
options = client.takeover.drafts("u_8f2", count=3)
client.takeover.send("u_8f2", body=options[0] if options else "Hey, it's Dana — I'm here.")

# Hand the conversation back to the AI.
client.takeover.release("u_8f2", handback_message="Handing you back to the assistant 💚")

While a takeover is active, your /process calls come back with suppress_ai: trueand the operator's pending messages — your bot renders those instead of an AI reply. The full contract is in Concepts → Human Takeover.

Next steps

You're wired. Pick what to read next: