TypeScript - 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.
npm install wontopos
import { Client } from "wontopos"; const mem = new Client({ apiKey: "wos-live-..." });
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 im Konstruktor; überschreiben Sie einen einzelnen Aufruf mit withModel().
const mem = new Client({ apiKey: "wos-live-...", model: "tablet-1" }); // default mem.recall("...", "alice"); // tablet-1 mem.withModel("scroll-1").recall("...", "alice"); // or pick a model per call
listModels
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.
await mem.listModels();
[{"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.
await mem.ping(); // true, or throws 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.
await mem.add("she prefers tea over coffee", "alice"); await mem.add("I promised the summary by Friday", "alice", { speaker: "me" }); // its own words - no registration needed
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}addTurn
Einen Gesprächszug (Nutzer + Assistent) zugleich ins Kurzzeit- und Langzeitgedächtnis speichern.
await mem.addTurn("hi", "hello!", "alice");
{"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.
await mem.addSpeaker("Bob", "alice"); // once per person; "me" needs no registration await mem.add("I promised to send the report on Friday", "alice", { speaker: "me" }); await mem.add("Bob said the deadline moved to Tuesday", "alice", { speaker: "Bob" }); await mem.search("what did Bob say about deadlines?", "alice", 10, { speaker: "Bob" });
addBulk
Einen großen Textblock nachladen. Serverseitig aufgeteilt und indexiert - ideal für den Import bestehender Historie.
await mem.addBulk("Alice moved to Brooklyn in March. She works at a design studio downtown.", "alice");
{"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.
await mem.update("576700aa-...", "she switched to coffee this year", "alice");
{"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.
const r = await mem.search("what does she drink?", "alice", 1);
[{
"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"
}]| Feld | Bedeutung |
|---|---|
| similarity | Wie nah diese Erinnerung an Ihrer Abfrage liegt (0–1). |
| is_superseded | True, wenn dieser Fakt durch update() ersetzt wurde. |
| search_ms | Serverseitige 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.
const ctx = await mem.recall("what does she drink?", "alice");
{"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.
const turns = await mem.history("alice");
{"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.
await mem.stats("alice");
{"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.
const m = await mem.get("alice", "576700aa-...");
{"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}listMemories
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.
const page = await mem.listMemories("alice", { limit: 100 });
{"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}
]}iterMemories · exportMemories
Alle Erinnerungen ohne Cursor-Verwaltung durchlaufen oder den ganzen Store auf einmal holen.
for await (const m of mem.iterMemories("alice")) console.log(m.id, m.content); const everything = await mem.exportMemories("alice");
Löschen
delete
Eine einzelne Erinnerung per ID löschen.
await mem.delete("alice", "576700aa-...");
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}deleteAll
Alles für einen Nutzer löschen - ein Aufruf, DSGVO-bereit.
await mem.deleteAll("alice");
{"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.
import { NotFoundError, PaymentRequiredError } from "wontopos"; try { await mem.add("...", "alice"); } catch (e) { if (e instanceof NotFoundError) await mem.createStore("alice"); else if (e instanceof PaymentRequiredError) topUp(); // out of credit else throw e; }
rateLimit
Lies das verbleibende Kontingent nach jedem Aufruf und drossle, bevor du das Limit erreichst.
await mem.search("...", "alice"); const rl = mem.rateLimit; // { limit: 150, remaining: 3, reset: ... }
searchSelf
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.
const { memories, self_memories } = await mem.searchSelf("what did I promise?", "alice"); // memories = what others said · self_memories = the agent's OWN words
listEngrams
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.
const { engrams, forms } = await mem.listEngrams(); // 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.
await mem.search("what did we decide", "alice", 10, { filters: { categories: ["work"], event_from: "2026-01-01" }, // when it HAPPENED });
idempotencyKey
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.
await mem.add("she prefers tea", "alice", {}, { idempotencyKey: `import:${row.id}` });
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._:-].withTimeout / withRetries / withDeadline
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.
await mem.withTimeout(120_000).addBulk(bigBlob, "alice"); // this slow call only await mem.withRetries(0).add("...", "alice"); // you retry, not the SDK await mem.withDeadline(5_000).recall("...", "alice"); // 5s for the whole call await mem.withSignal(ctrl.signal).recall("...", "alice"); // caller can cancel