Python-SDK

Python - jede Methode, drei Gruppen.

Schreiben, Lesen, Löschen. Jedes Beispiel unten wurde am 2026-08-01 gegen die Live-API ausgeführt; die Antworten sind wortwörtlich wiedergegeben.

pip install wontopos
from wontopos import Client

mem = Client(api_key="wos-live-...")  # or read from an env var

Modell wählen

Der API-Schlüssel bestimmt, welches Gedächtnis (Ihr Konto); das Modell bestimmt, welche Engine es liest. Alle Modelle teilen sich ein Gedächtnis, Sie können also mit einem speichern und mit einem anderen abrufen. Setzen Sie einen Standard am Client; überschreiben Sie einen einzelnen Aufruf, indem Sie ihm model= übergeben.

mem = Client(api_key="wos-live-...", model="tablet-1")  # default engine
mem.recall("...", user_id="alice")                  # tablet-1
mem.recall("...", user_id="alice", model="scroll-1")  # or pick a model per call

list_models

Der Katalog - die IDs, die Sie an model übergeben können, und ob sie jeweils live sind. Modelle mit memory: "shared" lesen denselben Store; "isolated" führt einen eigenen. Benötigt keinen API-Schlüssel.

mem.list_models()
Tatsächliche Antwort
[{"id": "tablet-1", "name": "Tablet 1", "available": true, "memory": "shared"},
 {"id": "tablet-2", "name": "Tablet 2", "available": true, "memory": "shared"},
 {"id": "scroll-1", "name": "Scroll 1", "available": true, "memory": "shared"},
 {"id": "scroll-1.2", "name": "Scroll 1.2", "available": true, "memory": "shared"}]

ping

Verbindung und Gültigkeit des API-Schlüssels in einer Zeile prüfen.

mem.ping()   # True, or raises AuthenticationError / PaymentRequiredError

Der Katalog oben spiegelt stets die aktuell verfügbaren Modelle wider - übergeben Sie eine andere ID, erhalten Sie einen klaren Fehler. Neue Modelle erscheinen dort automatisch, sobald sie ausgeliefert werden.

Schreiben

add

Eine Erinnerung speichern. Kein LLM-Aufruf beim Eingang - Sie zahlen nur den Schreibtarif.

mem.add("she prefers tea over coffee", user_id="alice")
mem.add("I promised the summary by Friday", user_id="alice", speaker="me")  # its own words - no registration needed
Tatsächliche Antwort
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}

add_turn

Einen Gesprächszug (Nutzer + Assistent) zugleich ins Kurzzeit- und Langzeitgedächtnis speichern.

mem.add_turn("hi", "hello!", user_id="alice")
Tatsächliche Antwort
{"status": "ok"}

speaker

Jede Erinnerung kann tragen, wer sie gesagt hat. Registriere eine Person einmal und übergib danach ihren Namen als speaker; "me" (die eigenen Worte des Assistenten) braucht nie eine Registrierung. Auch die Suche akzeptiert speaker, um nur die Worte einer Person zu holen.

mem.add_speaker("Bob", user_id="alice")  # once per person; "me" needs no registration
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")
response
[{"content": "Bob said the deadline moved to Tuesday", "speaker": "Bob", ...}]
Sprecher sind explizit, wie Stores. Registriere die Person zuerst, speichere dann unter ihrem Namen: ein Tippfehler wird nie still zu einer neuen Person. Ein Store registriert zunächst bis zu 50 Personen (wir planen mehr), und "me" braucht nie Registrierung und zählt nie.

add_bulk

Einen großen Textblock nachladen. Serverseitig aufgeteilt und indexiert - ideal für den Import bestehender Historie.

mem.add_bulk("Alice moved to Brooklyn in March. She works at a design studio downtown.", user_id="alice")
Tatsächliche Antwort
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}

update

Ein Fakt hat sich geändert. Die alte Erinnerung wird als ersetzt markiert (für die Historie behalten); die neue nimmt ihren Platz im Recall ein.

mem.update("576700aa-...", "she switched to coffee this year", user_id="alice")
Tatsächliche Antwort
{"new_memory_id": "07e94433-b7cc-4e49-8d8f-f37fc1a392b7",
 "old_memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "superseded"}

Lesen

search

Semantische Suche, das Relevanteste zuerst. Jede Sprache findet jede Erinnerung, in welcher Sprache sie auch geschrieben wurde. Das SDK gibt das memories-Array direkt zurück; der rohe HTTP-Body steht unten. Manche Modelle antworten mit mehr als einem Ergebnissatz, und das SDK gibt sie zusammengeführt zurück - das Array kann also MEHR als max_results enthalten. Bemessen Sie Ihr Prompt-Fenster an dem, was Sie erhalten, nicht an der angefragten Zahl.

r = mem.search("what does she drink?", user_id="alice", limit=1)
Tatsächliche Antwort (HTTP-Body)
[{
   "id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624",
   "content": "she prefers tea over coffee",
   "category": "general",
   "time_bucket": "2026-06",
   "importance": 0.3,
   "similarity": 0.6316057443618774,
   "is_superseded": false,
   "superseded_by": null,
   "created_at": "2026-07-10T04:20:39.688276876Z"
 }]
FeldBedeutung
similarityWie nah diese Erinnerung an Ihrer Abfrage liegt (0–1).
is_supersededTrue, wenn dieser Fakt durch update() ersetzt wurde.
search_msServerseitige Abrufzeit.

recall

Ein Roundtrip liefert alles, was Ihr LLM braucht - fügen Sie das Ergebnis direkt in Ihren Prompt ein: ein begrenzter Kontext fester Größe, egal wie viel Sie gespeichert haben.

ctx = mem.recall("what does she drink?", user_id="alice")
Tatsächliche Antwort (Struktur - Listen gekürzt)
{"short_term":  {"count": 2, "turns": [{"role": "user", "content": "hi", ...}]},
 "long_term":   {"count": 4, "memories": [{"content": "she prefers tea over coffee",
                                           "similarity": 0.63, ...}]},
 "context":     {"count": 4, "around_top_memory": [
                  "[match] she prefers tea over coffee",
                  "[after] Alice moved to Brooklyn in March. ..."]},
 "instruction": "Use short_term for recent context, long_term for relevant
                 past memories, context for surrounding conversation of the
                 most relevant memory."}

history

Jüngste Gesprächszüge (Kurzzeitgedächtnis), älteste zuerst.

turns = mem.history("alice")
Tatsächliche Antwort (HTTP-Body)
{"count": 2, "turns": [
   {"role": "user",      "content": "hi",     "timestamp": "2026-07-10T04:20:40.989011337Z"},
   {"role": "assistant", "content": "hello!", "timestamp": "2026-07-10T04:20:40.989013416Z"}
 ], "user_id": "alice"}

stats

Anzahl der Erinnerungen für einen Nutzer.

mem.stats("alice")
Tatsächliche Antwort
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}

get

Ruft eine einzelne Erinnerung per id ab - die id, die add oder list_memories zurückgegeben hat. Nur der gespeicherte Originaltext plus Metadaten. Eine id aus einem anderen Store oder eine gelöschte/invalidierte Erinnerung ergibt 404.

m = mem.get("alice", memory_id="576700aa-...")
response
{"id": "576700aa-...", "content": "she prefers tea over coffee",
 "category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
 "is_superseded": false, "superseded_by": null}
Tatsächliche Antwort
{"memory": {"id": "8bd090de-...", "content": "the office moved to the seventh floor in June",
  "category": "general", "created_at": "2026-07-31T18:20:30.531518060+00:00", "event_date": null,
  "is_superseded": false, "superseded_by": null}, "user_id": "docs_livetest"}

list_memories

Listet die Erinnerungen eines Stores auf - nur der gespeicherte Originaltext und seine Metadaten. Seitenweise per Cursor: den zurückgegebenen next_cursor für die nächste Seite mitgeben.

page = mem.list_memories("alice", limit=100)
response
{"count": 2, "next_cursor": null, "memories": [
   {"id": "576700aa-...", "content": "she prefers tea over coffee",
    "category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
 "is_superseded": false, "superseded_by": null}
 ]}

iter_memories · export_memories

Alle Erinnerungen ohne Cursor-Verwaltung durchlaufen oder den ganzen Store auf einmal holen.

for m in mem.iter_memories("alice"):   # every page, no cursor bookkeeping
    print(m["id"], m["content"])
everything = mem.export_memories("alice")   # the whole store as a list

Löschen

delete

Eine einzelne Erinnerung per ID löschen.

mem.delete("alice", memory_id="576700aa-...")
Tatsächliche Antwort
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}

delete_all

Alles für einen Nutzer löschen - ein Aufruf, DSGVO-bereit.

mem.delete_all("alice")
Tatsächliche Antwort
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}

Fehler und Zuverlässigkeit

Jeder Fehler ist typisiert - fange den konkreten (Rate-Limit, Auth, Zahlung) oder alle mit dem Basis-WosError.

from wontopos import PaymentRequiredError, NotFoundError
try:
    mem.add("...", user_id="alice")
except NotFoundError:
    mem.create_store("alice")   # store didn't exist yet
except PaymentRequiredError:
    top_up()                       # out of credit - don't retry

rate_limit

Lies das verbleibende Kontingent nach jedem Aufruf und drossle, bevor du das Limit erreichst.

mem.search("...", user_id="alice")
rl = mem.rate_limit   # {"limit": 150, "remaining": 3, "reset": ...}

search_self

Beide Spuren aus einem Aufruf bei einem Selbstgedächtnis-Modell (Scroll 1.2+): was andere sagten und die EIGENEN Worte des Agenten - getrennt gehalten, damit der Leser nie verwechselt, wer was gesagt hat.

r = mem.search_self("what did I promise?", user_id="alice")
r["memories"]       # what others said / general memories
r["self_memories"]  # the agent's OWN words (speaker "me")

list_engrams

Frag den Dienst, welche Engramme und Delivery-Formen das gewählte Modell ausführen kann, statt Namen fest zu verdrahten, die veralten, sobald ein neues erscheint.

cat = mem.list_engrams()
[e["name"] for e in cat["engrams"]]   # ask, never hard-code

filters

Grenzt die Suche auf einen Teil des Stores ein. Wird vor dem Ranking angewendet, du bekommst also die besten Treffer innerhalb des Filters - kein nachträglich gefiltertes Top-N.

mem.search("what did we decide", user_id="alice", filters={
    "categories": ["work"],
    "event_from": "2026-01-01",   # when it HAPPENED
})
Schlüssel: categories · event_from / event_to (wann der Inhalt GESCHAH - metadata.event_date) · time_from / time_to (wann er geschrieben wurde) · min_importance. Nicht aufgeführte Schlüssel werden verworfen, nicht abgelehnt - ein Tippfehler erweitert die Suche also stillschweigend.

idempotency_key

Macht das Wiederholen eines Schreibvorgangs sicher. Nutze ihn, wenn der Retry von dir kommt - ein Job, der abgebrochen und neu gestartet wurde, eine Queue, die erneut zustellt.

mem.add("she prefers tea", "alice", idempotency_key=f"import:{row.id}")
Leite den Schlüssel aus dem ab, was gespeichert wird (import:row-42), nie eine Konstante: ein Schlüssel, der für zwei verschiedene Schreibvorgänge wiederverwendet wird, spielt den ersten erneut ab - der zweite geht stillschweigend verloren. Format: 1-128 Zeichen aus [A-Za-z0-9._:-].

with_timeout / with_retries / with_deadline

Stelle eine einzelne Aufrufstelle ein, ohne den bereits gebauten Client anzufassen: ein Klon mit längerem Timeout für einen großen Backfill oder mit abgeschalteten Retries innerhalb deiner eigenen Retry-Schleife.

timeout begrenzt einen Versuch, deshalb kann ein Aufruf mit Wiederholungen ihn überdauern: mit den Standardwerten hält ein einzelner Aufruf eine Verbindung 30 s, wartet, versucht es erneut und noch einmal. deadline begrenzt stattdessen den gesamten Aufruf — jeder Versuch wird auf die verbleibende Zeit gedeckelt, und keine Wartezeit wird über das Budget hinaus verschlafen. Setzen Sie es, wenn der Aufrufer eine echte Grenze hat, etwa ein Request-Handler mit fünf Sekunden.

mem.with_timeout(120).add_bulk(big_blob, "alice")  # this slow call only
mem.with_retries(0).add("...", "alice")              # you retry, not the SDK
mem.with_deadline(5).recall("...", "alice")             # 5s for the whole call