Concepts

Observe

observe() is the frictionless entry point to Humane. You already have a chatbot generating replies with your own model — send each completed turn to observe()and get back the user's emotional state and a risk assessment. No LLM runs on our side, nothing is generated, and it never blocks your reply.

What it is

Observe is no-LLM behavioural monitoring. It takes a conversation turn — the user's message and, optionally, the assistant message your own model produced — runs heuristic behavioural and safety analysis, blends the result into the end-user's persistent state, and returns three things: the updated state, a risk level, and any alerts. It is the one call that works the moment you have an API key.

Observe vs. Process
observe() watches a turn you already produced — it never generates text and needs no model configured. process() is the opposite: Humane runs the engines, generates the reply with your configured LLM, and applies the safety gate. Start with observe; reach for process when you want Humane to own the reply.

The call

python
from humane_ai import HumaneClient

with HumaneClient(api_key="hx_...") as client:
    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
    )

Three arguments: user_id (required), user_message (required), and assistant_message (optional, defaults to ""). The end-user is created on first sight and reused thereafter, so state accumulates across calls.

The ObserveResult shape

observe() returns an ObserveResult with exactly three fields:

python
r.user_state.mood        # float — lower = lower mood
r.user_state.energy      # float
r.user_state.trust       # float
r.user_state.sentiment   # float

r.risk.level             # "none" | "elevated" | "critical"
r.risk.flags             # list[str], e.g. [] or ["self_harm"]

r.alerts                 # list[Alert] — each Alert has .type and .severity

The raw JSON the endpoint returns mirrors that shape one-to-one:

json
{
  "user_state": { "mood": 0.28, "energy": 0.41, "trust": 0.40, "sentiment": 0.33 },
  "risk":       { "level": "none", "flags": [] },
  "alerts":     []
}
FieldTypeMeaning
user_state.moodfloat 0–1Blended mood. EMA-smoothed across turns; lower is worse.
user_state.energyfloat 0–1Conversational energy / engagement.
user_state.trustfloat 0–1Ticks up gently each interaction; relationship depth.
user_state.sentimentfloat 0–1Valence of the turn, blended over time (0.5 neutral).
risk.levelenumnone, elevated, or critical.
risk.flagslist[str]Matched safety categories, e.g. self_harm, violence.
alerts[]listOne alert per elevated / critical risk, with type + severity.

It never blocks

Observe is a read. It returns state and risk; it does not gate, rewrite, or withhold your reply. Even on critical risk you get a normal 200 with the flags populated — what you do next is your call. That makes observe safe to drop into a live product path: the worst case is an extra HTTP round-trip, never a swallowed response. (The endpoint also degrades gracefully internally — a slow memory write can never fail the call.)

python
r = client.observe(user_id="u_8f2", user_message=text)

# You decide what risk means for your product:
if r.risk.level == "critical":
    page_on_call(user_id="u_8f2", flags=r.risk.flags)
elif r.risk.level == "elevated":
    soften_next_reply()

reply = your_own_model(text)   # observe never touched this

Works before onboarding

Because observe runs on heuristics and never calls an LLM, it has no onboarding requirement. It works the instant you create a key — before you pick a model, before you configure a provider. This is the one deliberate asymmetry between the two SDK entry points:

  • observe() — zero onboarding. Always available.
  • process() — needs a configured LLM. Returns 409 onboarding_required until you finish setup.
A clean fallback
If a process() call comes back 409 onboarding_required, fall back to observe() — you still get state + risk while you finish configuring a model. They share the same end-user, so no state is lost.

When to use it

  • Frictionless first integration. One line, one key, instant signal — the fastest way to prove value.
  • You keep generating replies yourself. Bring your own model; let Humane watch the conversation and surface risk.
  • Monitoring an existing product. Add behavioural state + safety alerts to a chatbot that already ships, without changing its reply path.
  • Routing decisions. Use r.risk.level to escalate to a human, trigger a takeover, or adjust tone downstream.

Endpoint

Under the hood, the SDK posts to one endpoint (auth header X-API-Key):

bash
curl -X POST https://humaneai.vaarak.com/api/sdk/observe \
  -H "Content-Type: application/json" \
  -H "X-API-Key: hx_your_key" \
  -d '{
    "user_id": "u_8f2",
    "user_message": "I feel a lot better today",
    "assistant_message": "That's really good to hear."
  }'