Skip to main content

Wontopos — long-term memory for AI agents

pip install wontopos
from wontopos import Client

mem = Client(api_key="wos-...")

# Each end-user / agent / topic gets its own store — create it once.
# (A "default" store already exists, so you can skip this and omit the id.)
mem.create_store("alice")
mem.add("she prefers tea over coffee", user_id="alice")

# one call → short-term + long-term + context, ready for your LLM prompt
ctx = mem.recall("what does alice drink?", user_id="alice")

Why

  • Pure semantic retrieval — no keyword/BM25. Identical recall in every language (한국어 · 日本語 · 中文 · English).
  • No LLM in the loopstore / search / recall never call a language model. You pay embeddings, not generation.
  • Bounded retrievalrecall() returns a small, fixed-size slice (~1,200 tokens) regardless of how much you've stored. Your LLM bill stops growing with history.

Methods

Method Purpose
add(content, user_id, **metadata) Store one memory
add_turn(user_msg, assistant_msg, user_id?) Store a conversation exchange
add_bulk(content, user_id, category=, timestamp=) Backfill a long history
update(old_memory_id, new_content, user_id?) Supersede an old fact
search(query, user_id, limit=10, **opts) Semantic search
recall(query, user_id) One-call context (short + long + surrounding)
history(user_id) Recent turns (short-term)
stats(user_id) Counts
get(user_id, memory_id) Fetch one memory by id (original text, no vector)
list_memories(user_id, limit=100, cursor=) Browse/export a store's raw memories, paged
delete(user_id, memory_id) Delete one memory
delete_all(user_id) GDPR erase (delete every memory for the user)
add_speaker(speaker, user_id?) Register a person (explicit, up to 50 to start)
list_speakers(user_id?) Registered people + per-person memory counts
remove_speaker(speaker, user_id?) Unregister; memories stay, the tag goes

All methods take a user_id — it names the store: one isolated memory space per end-user, agent, or topic, then per account (your API key). WHO said each memory inside a store is the speaker tag below — storing the assistant's own words never needs a separate id.

Who said it (speakers)

Every memory can carry a speaker: "me" for the assistant's own words, or a person's name. Speakers are explicit, like stores: register a person once, then store under their name — a typo can never silently become a new person. Search accepts a speaker too, so you can recall one person's words only.

mem.add_speaker("Bob", user_id="alice")      # once per person
mem.add("I promised to send the report on Friday", user_id="alice", speaker="me")
mem.add("Bob said the deadline moved to Tuesday", user_id="alice", speaker="Bob")
mem.search("what did Bob say about deadlines?", user_id="alice", speaker="Bob")

A store registers up to 50 people to start (a limit we plan to raise); "me" never needs registration and never counts against it.

Async

Same surface, awaitable — needs the extra:

pip install "wontopos[async]"
from wontopos import AsyncClient

async with AsyncClient(api_key="wos-...", user_id="alice") as mem:
    await mem.add("she prefers tea over coffee")
    hits = await mem.search("what does alice drink?")

Every Client method exists on AsyncClient with identical arguments and semantics (retries, redirect refusal, guards). Close with async with or await mem.aclose().

Recall caching

Opt in per search and repeated or extended queries reuse the previous result at 10% of the normal rate (Tablet and Scroll models).

It is not free to turn on: the FIRST call writes the cache and bills the query tokens at 2x for a 5m TTL, 3x for 1h. Only hits inside the TTL bill at 0.1x. So it pays for a query you repeat or extend, and costs more for one you issue once — do not switch it on globally. Any write to the store invalidates its cache at once, so a hit can never predate a new memory.

hits = mem.search("...the conversation so far...", user_id="alice",
                  cache_control={"ttl": "5m"})   # or "1h"

Reliability

Built in, no configuration needed:

  • Automatic retries — 429 / 502 / 503 and connection errors retry twice with exponential backoff + jitter, honoring the server's Retry-After. Tune with Client(retries=...); retries=0 disables.
  • Redirects refused — the API key never follows a 3xx to another host.
  • Timeouts — 30s per attempt by default (Client(timeout=...)).
  • Key never in logsrepr(client) masks the API key.
  • Wipe guarddelete() without a memory_id raises instead of silently meaning "delete everything"; wiping a store is only ever the explicit delete_all(user_id) / delete_store(user_id).

Security

Built in, none of it configurable off:

  • TLS 1.2 floor and certificate verification that cannot be disabled.
  • Redirects refused — a 3xx is an error, so the key never follows one to another host.
  • Response size cap — anything over 64MB is refused instead of buffered.
  • Key hygiene — keys are trimmed (a stray newline from a file otherwise becomes a mystery 401) and inner whitespace is rejected; model names are validated before they reach a header.
  • Client.from_env() reads WONTOPOS_API_KEY (or WOS_API_KEY) — keep keys out of source code.
  • Plain-HTTP base URLs on non-local hosts warn. One dependency (requests, floor >=2.32 for its certificate-verification fix).

Errors

Any non-2xx response raises WosError(status, message). When the server sent a request id it's on e.request_id — include it when contacting support.

from wontopos import Client, WosError

try:
    mem.search("...", user_id="alice")
except WosError as e:
    if e.status == 401:
        print("API key invalid or revoked")
    elif e.status == 429:
        print("Rate limited — back off")   # already retried twice by then
    else:
        print(e.status, e.message, e.request_id)

Self-host

Point at your own engine:

mem = Client(api_key="...", base_url="https://wos.your-host.com")

Links

Reporting a bug

Found something wrong, or something that looks unsafe? Tell us — one person reads every report.

Include the SDK version (wontopos.__version__) and the language. If it involves a store id or a memory, describe the shape rather than pasting the contents — we do not need your data to fix it.

Changelog

The three clients release in lockstep — same version, same surface, same day. Patch releases are additive: nothing is removed or reordered within a minor line.

  • 2.2.30 — documentation only: this changelog, which had not been updated in the shipped package since 2.2.10.
  • 2.2.29 — a store id that was PASSED but unusable no longer becomes the default store. 2.2.28 caught a blank string; the value a failed tenant lookup actually produces in Python is an integer primary key. user_id=0 is falsy and fell through to the client default, and any other integer died inside the warn helper as 'int' object has no attribute 'lower'. Anything but a non-blank string now raises, on the destructive calls too. Omitting the argument still means "use the default". Also: delete_store now warns when a store id folds (delete_all already did, and it is the call that removes a whole store), and an idempotency_key ending in a newline is refused here instead of failing inside http.client as Invalid header value.
  • 2.2.28 — audit: warn caches made thread-safe, a whitespace memory_id can no longer read as a whole-store delete, a blank store id raises instead of silently using the default, and a gzip bomb no longer bypasses the response cap on the async client.
  • 2.2.11–2.2.27 — additive fixes and hardening across all three clients.
  • 2.2.10Memory fix: the relevance field is similarity (not score); added typed importance, category, is_superseded, superseded_by, created_at, event_date.
  • 2.2.4–2.2.9 — one version across Python, TypeScript and Rust, released in lockstep; retries, redirect refusal, response cap, key masking, speakers.

License: MIT.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wontopos-2.2.32.tar.gz (62.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wontopos-2.2.32-py3-none-any.whl (41.8 kB view details)

Uploaded Python 3

File details

Details for the file wontopos-2.2.32.tar.gz.

File metadata

  • Download URL: wontopos-2.2.32.tar.gz
  • Upload date:
  • Size: 62.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.18

File hashes

Hashes for wontopos-2.2.32.tar.gz
Algorithm Hash digest
SHA256 20cc7837cdd2972f3047b36ae61458b32b6b14884d0aee4b2e28e14cbe0fb4f3
MD5 07d0c56143944d936c85606f88e6fda3
BLAKE2b-256 10a7652e13a0beaf6bb66bf19d835936a884dcbb1706b98fed0fe6b5b1dc0efd

See more details on using hashes here.

File details

Details for the file wontopos-2.2.32-py3-none-any.whl.

File metadata

  • Download URL: wontopos-2.2.32-py3-none-any.whl
  • Upload date:
  • Size: 41.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.18

File hashes

Hashes for wontopos-2.2.32-py3-none-any.whl
Algorithm Hash digest
SHA256 ef57c04cbbe36699574c3c5ffd09c075755d374fa62d5ed9f978ed4b8f7764bb
MD5 2dbcf02c789f674a1fa3af15d5955a2e
BLAKE2b-256 7faefcc5c1ba7111711abad758e7d838161d87f5fb1230f335725afd81ee7e7b

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

2.2.32 This release

2 files

2.2.31

2 files

2.2.30

2 files

2.2.29

2 files

2.2.28

2 files

2.2.27

2 files

2.2.26

2 files

2.2.25

2 files

2.2.24

2 files

2.2.23

2 files

2.2.22

2 files

2.2.21

2 files

2.2.20

2 files

2.2.19

2 files

2.2.18

2 files

2.2.17

2 files

2.2.16

2 files

2.2.15

2 files

2.2.14

2 files

2.2.13

2 files

2.2.12

2 files

2.2.11

2 files

2.2.10

2 files

2.2.9

2 files

2.2.8

2 files

2.2.7

2 files

2.2.6

2 files

2.2.5

2 files

2.2.4

2 files

2.2.2

2 files

2.2.1

2 files

2.2.0

2 files

2.1.1

2 files

2.1.0

2 files

2.0.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page