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.

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", # required — sets X-Khwan-User 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_idstrRequired. End-user id; sent as X-Khwan-User. Identifies the user, not a separate brain.
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