Rust - chaque méthode, en trois groupes.
Écrire, lire, supprimer. Chaque exemple ci-dessous a été exécuté contre l'API en production le 2026-08-01 ; les réponses sont verbatim.
cargo add wontopos
use wontopos::Client; let mem = Client::new("wos-live-...");
Choisir un modèle
La clé API détermine quelle mémoire (votre compte) ; le modèle détermine quel moteur la lit. Tous les modèles partagent une même mémoire : vous pouvez donc stocker avec l'un et rappeler avec un autre. Définissez un défaut avec with_model() ; chaînez-le à nouveau pour surcharger un appel isolé.
let mem = Client::new("wos-live-...").with_model("tablet-1"); // default mem.recall("...", "alice").await?; // tablet-1 mem.with_model("scroll-1").recall("...", "alice").await?; // or pick a model per call
list_models
Le catalogue - les ids que vous pouvez passer à with_model et la disponibilité de chacun. Les modèles memory: "shared" lisent le même store ; "isolated" garde le sien. Aucune clé API requise.
mem.list_models().await?;
[{"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
Vérifiez la connexion et que votre clé API fonctionne : un contrôle en une ligne.
mem.ping().await?; // Ok(true), or Err whose .kind() is Auth / PaymentRequired
Le catalogue ci-dessus reflète toujours les modèles disponibles à l'instant même - passez tout autre id et vous obtenez une erreur claire. Les nouveaux modèles y apparaissent automatiquement dès leur sortie.
Écrire
add
Stocke un souvenir. Aucun appel LLM à l’entrée : vous ne payez que le tarif d’écriture.
mem.add("she prefers tea over coffee", "alice", json!({})).await?; mem.add("I promised the summary by Friday", "alice", json!({"speaker": "me"})).await?; // its own words - no registration needed
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}add_turn
Stocke un tour de conversation (utilisateur + assistant) à la fois en mémoire court terme et long terme.
mem.add_turn("hi", "hello!", "alice").await?;
{"status": "ok"}speaker
Chaque souvenir peut porter son locuteur. Enregistrez une personne une fois, puis passez son nom comme speaker ; "me" (les mots de l'assistant) n'a jamais besoin d'enregistrement. La recherche accepte aussi speaker, pour ne rappeler que les mots d'une personne.
mem.add_speaker("Bob", "alice").await?; // once per person; "me" needs no registration mem.add("I promised to send the report on Friday", "alice", json!({"speaker": "me"})).await?; mem.add("Bob said the deadline moved to Tuesday", "alice", json!({"speaker": "Bob"})).await?; mem.search_with("what did Bob say about deadlines?", "alice", 10, json!({"speaker": "Bob"})).await?;
add_bulk
Importez un gros bloc de texte. Découpé et indexé côté serveur, idéal pour reprendre un historique existant.
mem.add_bulk("Alice moved to Brooklyn in March...", "alice", "general").await?;
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}update
Un fait a changé. L'ancien souvenir est marqué superseded (conservé pour l'historique) ; le nouveau prend sa place dans le recall.
mem.update("576700aa-...", "she switched to coffee this year", "alice").await?;
{"new_memory_id": "07e94433-b7cc-4e49-8d8f-f37fc1a392b7",
"old_memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "superseded"}Lire
search
Recherche sémantique, du plus pertinent au moins pertinent. N’importe quelle langue trouve n’importe quel souvenir, quelle que soit la langue dans laquelle il a été écrit. Le SDK renvoie directement le tableau memories ; le corps HTTP brut est affiché ci-dessous. Certains modèles répondent avec plusieurs ensembles de résultats et le SDK les renvoie fusionnés : le tableau peut donc contenir PLUS que max_results. Dimensionnez votre fenêtre de prompt sur ce que vous recevez, pas sur le nombre demandé.
let r = mem.search("what does she drink?", "alice", 1).await?;
[{
"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"
}]| Champ | Signification |
|---|---|
| similarity | À quel point ce souvenir est proche de votre requête (0–1). |
| is_superseded | Vrai si ce fait a été remplacé par update(). |
| search_ms | Temps de récupération côté serveur. |
recall
Un seul aller-retour renvoie tout ce dont votre LLM a besoin - collez le résultat directement dans votre prompt : un contexte borné, de taille fixe, quel que soit le volume stocké.
let ctx = mem.recall("what does she drink?", "alice").await?;
{"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
Tours de conversation récents (mémoire court terme), du plus ancien au plus récent.
let turns = mem.history("alice").await?;
{"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
Compteurs de souvenirs pour un utilisateur.
mem.stats("alice").await?;
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}get
Récupère une mémoire par id - l'id renvoyé par add ou list_memories. Uniquement le texte original stocké et ses métadonnées. Un id d'un autre store, ou une mémoire supprimée ou invalidée, renvoie 404.
let m = mem.get("alice", "576700aa-...").await?;
{"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}list_memories
Liste les mémoires d'un espace : uniquement le texte original que vous avez stocké et ses métadonnées. Paginé par curseur : renvoyez le next_cursor reçu pour la page suivante.
mem.list_memories("alice", 100, None).await?;
{"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}
]}list_all_memories
Parcourez toutes les mémoires sans gérer le curseur, ou récupérez tout l'espace d'un coup.
let all = mem.list_all_memories("alice").await?; // every page, collected
Supprimer
delete
Supprime un souvenir unique par id.
mem.delete("alice", "576700aa-...").await?;
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}delete_all
Efface tout pour un utilisateur - un seul appel, conforme RGPD.
mem.delete_all("alice").await?;
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}Erreurs et fiabilité
Chaque échec est une erreur typée : attrapez le cas précis (limite de débit, authentification, paiement) ou tous avec le WosError de base.
use wontopos::ErrorKind; match mem.search("...", "alice", 10).await { Ok(hits) => { /* use hits */ } Err(e) if e.kind() == ErrorKind::NotFound => { mem.create_store("alice").await?; } Err(e) if e.is_rate_limited() => { /* back off */ } Err(e) => return Err(e), }
rate_limit
Lisez le quota restant après chaque appel et ralentissez avant d'atteindre la limite.
mem.search("...", "alice", 10).await?; let rl = mem.rate_limit(); // Some(RateLimit { remaining: Some(3), .. })
search_self
Les deux voies en un seul appel sur un modèle à mémoire de soi (Scroll 1.2+) : ce que les autres ont dit, et les propres mots de l'agent - séparés, pour que le lecteur ne confonde jamais qui a parlé.
let r = mem.search_self("what did I promise?", "alice", 10).await?; // r.memories = what others said · r.self_memories = the agent's OWN words
list_engrams
Demandez au service quels engrammes et formes de rendu le modèle sélectionné peut exécuter, au lieu de coder en dur des noms qui deviennent obsolètes dès qu'un nouveau sort.
let cat = mem.list_engrams().await?; // ask, never hard-code
filters
Restreint la recherche à une partie du magasin. Appliqué avant le classement : vous obtenez les meilleures correspondances à l'intérieur du filtre, pas un top-N filtré après coup.
mem.search_with("what did we decide", "alice", 10, json!({"filters": { "categories": ["work"], "event_from": "2026-01-01" // when it HAPPENED }})).await?;
add_idempotent
Rend sûr le fait de répéter une écriture. À utiliser quand la reprise vient de vous - un job interrompu puis relancé, une file qui redélivre.
mem.add_idempotent("she prefers tea", "alice", json!({}), &format!("import:{}", row.id)).await?;
import:row-42), jamais une constante : une clé réutilisée pour deux écritures différentes rejoue la première, et la seconde est perdue en silence. Format : 1 à 128 caractères parmi [A-Za-z0-9._:-].with_timeout / with_retries / with_deadline
Ajustez un seul point d'appel sans toucher au client déjà construit : un clone avec un délai plus long pour une grosse reprise, ou sans reprises à l'intérieur de votre propre boucle de retry.
timeout borne une tentative, donc un appel qui réessaie peut lui survivre : avec les valeurs par défaut, un seul appel peut retenir une connexion 30 s, attendre, réessayer, puis réessayer encore. deadline borne l'appel entier : chaque tentative est plafonnée à ce qu'il reste, et aucune attente ne dort au-delà du budget. Réglez-le quand l'appelant a une vraie limite, comme un gestionnaire de requête disposant de cinq secondes.
mem.with_timeout(120).add_bulk(big_blob, "alice", "general").await?; mem.with_retries(0).add("...", "alice", json!({})).await?; mem.with_deadline(Duration::from_secs(5)).recall("...", "alice").await?; // 5s for the whole call