Skip to Content
Python SDK

Python SDK

The khwan Python package is a thin HTTP wrapper over the REST API — no engine code, just requests. It is open source: read it at github.com/khwanlabs/khwan-client-python . “Thin” is a claim about code that handles your prompts, so it is one worth checking rather than taking.

pip install khwan
from khwan import Khwan, Turn, KhwanError

Khwan(...)

The client. All arguments are keyword-only.

kw = Khwan( api_key="kwk_live_xxx", # required user_id="alice", # optional — X-Khwan-User → isolated per-user sub-brain core=None, # optional — sets X-Khwan-Core (isolated brain) base_url="https://api.khwan.ai", # optional; override for on-prem model=None, # optional session model hint constitution=None, # optional named constitution profile timeout=60, # optional request timeout (seconds) )
ArgumentTypeDefaultNotes
api_keystrRequired. Your Khwan key (kwk_...). Raises ValueError if missing.
user_idstr | NoneNoneOptional. End-user id; sent as X-Khwan-User. Omit for one shared brain per account/core; set it to give each end-user an isolated per-user sub-brain (paid — the free plan includes a few). Composes with core.
corestr | NoneNoneCore slug; sent as X-Khwan-Core. Selects an isolated brain. Omit for the default core.
base_urlstrhttps://api.khwan.aiOverride to point at an on-prem instance.
modelstr | NoneNoneForwarded on prepare; may be overridden by dashboard settings.
constitutionstr | NoneNoneNamed constitution profile reference.
timeoutint60Per-request timeout in seconds.

memory= and embedder= are not configurable here — they are server-managed and exist only in the on-prem engine. Passing them raises TypeError.

Isolated cores

Each named core is a fully separate brain (own memory, identity, learning). Omit core for the account’s default core. Quota is pooled at the account level.

client1 = Khwan(api_key="kwk_live_xxx", user_id="alice", core="client1")

Methods

prepare(user_input) -> Turn

Builds the turn context (memory + constitution + coherence). No LLM call. POST /prepare.

turn = kw.prepare("remember I prefer short answers in Thai")

record(turn, answer) -> dict

Hands your model’s answer back so Khwan can persist and learn. POST /record.

kw.record(turn, answer)
ParamTypeNotes
turnTurnThe object returned by prepare.
answerstrYour model’s reply text.

sync() -> dict

Triggers a learning / consolidation pass. POST /sync.

kw.sync()

memory(limit=20) -> dict

Returns recent memory entries for the selected core. GET /memory?limit=....

mem = kw.memory(limit=50)

cores() -> dict

Lists the account’s cores. GET /cores.

cores = kw.cores()

metrics() -> dict

Returns coherence / learning / usage metrics for the selected core. GET /metrics.

stats = kw.metrics()

Turn

Returned by prepare(). Feed .messages to your model, then pass the object to record().

PropertyTypeNotes
messageslist[dict][{role, content}] brief for your model.
coherencefloat | NoneCoherence signal.
sourceslistMemory / retrieval sources used.
allowedboolFalse if the turn was gated. Defaults to True.
reasonstr | NoneWhy it was gated.
turn_tokenstr | NoneOpaque handle used by record().
raw()dictFull server payload.

KhwanError

Raised on any non-2xx response. Has a .status attribute (the HTTP code).

try: turn = kw.prepare("hi") except KhwanError as e: print(e.status, e) # e.g. 429 quota exceeded — you are over your plan's limit

Full example

from khwan import Khwan, KhwanError import anthropic kw = Khwan(api_key="kwk_live_xxx", user_id="alice", core="client1") llm = anthropic.Anthropic(api_key="sk-ant-...") def my_own_llm(messages): system = next((m["content"] for m in messages if m["role"] == "system"), "") chat = [m for m in messages if m["role"] != "system"] r = llm.messages.create(model="claude-sonnet-4-6", max_tokens=1024, system=system, messages=chat) return r.content[0].text try: turn = kw.prepare("remember I prefer short answers in Thai") if turn.allowed: answer = my_own_llm(turn.messages) kw.record(turn, answer) print(answer) else: print("blocked:", turn.reason) except KhwanError as e: print("Khwan error", e.status, e)
Last updated on