Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,49 @@ kw.record(turn, answer) # Khwan persist
kw.record(turn, answer, background=True) # → {"queued": True}
```

## On an event loop

Every agent framework worth integrating is async, and a blocking client on an
event loop either stalls it or grows a thread pool to hide the stall. `AsyncKhwan`
is the same loop, the same retry rules — they live at module level, so the two
clients cannot drift — and the same errors.

```bash
pip install "khwan[async]"
```

```python
from khwan import AsyncKhwan

# Holds one connection pool, so keep it open rather than building one per turn.
async with AsyncKhwan(api_key="kwk_live_xxx", core="acme", user_id="Web") as kw:
turn = await kw.prepare("what did we decide about billing?")
answer = await your_model(turn.messages)
await kw.record(turn, answer)

await kw.record(turn, answer, background=True) # → {"queued": True}
```

`background=True` schedules the write and returns immediately; `aclose()` — which
`async with` calls for you — waits for anything still in flight, so a fire-and-
forget record is not lost when the process ends.

## What the brain already knew

`prepare` returns the raw turns it retrieved *and* the rules synthesis has
distilled from many past turns. Both are already inside `turn.messages`; they are
also exposed so a caller building its own context — a recall tool, a subagent
brief — can take the distilled rules without replaying the whole prompt.

```python
turn.lessons # ["Answer in Thai.", …] standing rules
turn.sources # the raw turns retrieved for THIS turn, each with a similarity
```

Retrieval applies a relevance floor, so an empty `sources` is an answer: the brain
has nothing close to this question. Read it as "not known here" rather than
reaching for whichever memory was nearest.

## Gate the answer, review what it learned

```python
Expand Down
11 changes: 10 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
[project]
name = "khwan"
version = "0.2.0"
version = "0.3.0"
description = "Khwan hosted client — the cognition layer (memory + identity + learning) for your own agent. Bring your own model."
readme = "README.md"
requires-python = ">=3.9"
license = { text = "MIT" }
dependencies = ["requests>=2.28"]

# httpx only for AsyncKhwan — a sync user should not pay for it.
[project.optional-dependencies]
async = ["httpx>=0.24"]

[project.urls]
Homepage = "https://khwan.ai"
Documentation = "https://docs.khwan.ai"
# PyPI renders this as "Source" in the sidebar. Without it the page offers no
# route to the code at all — and a reviewer who went looking for exactly this
# repository concluded it was closed, which it never was.
Repository = "https://github.com/khwanlabs/khwan-client-python"
Issues = "https://github.com/khwanlabs/khwan-client-python/issues"

[build-system]
requires = ["hatchling<1.28"]
Expand Down
Loading
Loading