Recall caching

Repeated recall, at a tenth of the price.

Opt in per request and WOS caches the search result under its query text, with the same prefix rules as LLM prompt caching. While the cache is warm, a repeated or extended query reuses the previous result, and the cached part is billed at 10% of the normal token rate.

Tablet and Scroll only. Caching works on every Tablet and Scroll model, current and future. Book does not support it: Book reasons over your memories and learns between calls, so the same question can legitimately come back with a different answer, and a cached result would be wrong by design. Sending cache_control to Book returns a clear 403.

One conversation, three turns

Here is what actually happens when an agent keeps talking to its memory. Each turn sends the conversation so far as the query, with cache_control on.

writeTurn 1 - "Alice: I moved to Lisbon last spring."

The full query is searched and cached: input at 2x (5-minute TTL).

extendTurn 2 - the same text plus "Bob: How is the weather there?"

Only Bob’s sentence is indexed and searched. The old part costs 0.1x, the new sentence 2x, and the cache now ends at it.

hitTurn 3 - exactly the same query again (a retry, a refresh)

No engine call at all. Everything at 0.1x: the 90% discount.

The rates

OperationToken billingWhat it means
Cache write - TTL 5 minutesThe first request. Its result is kept for 5 minutes, and every read slides the window forward.
Cache write - TTL 1 hourThe first request, kept for a full hour.
Cache read - hit or prefix hit0.1×Every request after the write: the cached part costs a tenth of the normal token rate.

What it saves

A concrete example: your agent sends a 3,000-token conversation as its query and repeats or continues it 10 times within five minutes. Without caching, that is 30,000 input tokens at full price. With a 5-minute cache it is 6,000 for the first write (2x) plus about 2,700 for the nine cached reads: 8,700 billed tokens, 71% less. The longer the conversation runs, the bigger the save.

The prefix rule

Matching is on the front of the query. If the front stays identical and new text is only appended, the cached part is reused and only the new part is searched. If anything before the end of the cached text changes, nothing can be reused.

prefix match
cached    [ A B C D E F G ]

○   [ A B C D E F G ] E
✗   [ B C D E F G ] E

hit - the front is unchanged, E is the only new part
miss - the front changed, so the whole query is searched and cached again

Three rules to remember

  • Extending re-caches through the new tail. After [A B C D E F G] + E, the cache now ends at E: the tail is billed once at the write rate, and the next turn can match all of A..E as its prefix again.
  • One contiguous prefix per request. A query cannot be split into two cached segments; only its front can match.
  • Writes invalidate instantly. Any store, store-turn, bulk-store, forget, supersede, or store deletion drops that store’s cache, so a cached answer can never be stale.

Turning it on

hits = mem.search(
    "...the conversation so far...", user_id="alice",
    cache_control={"ttl": "5m"},   # or "1h"
)
const hits = await mem.search(
  "...the conversation so far...", "alice", 10,
  { cache_control: { ttl: "5m" } },   // or "1h"
);
let hits = mem.search_with(
    "...the conversation so far...", "alice", 10,
    serde_json::json!({"cache_control": {"ttl": "5m"}}),   // or "1h"
).await?;
curl -X POST https://api.wontopos.com/api/v1/memory/search \
  -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \
  -d '{"user_id":"alice",
       "query":"...the conversation so far...",
       "cache_control":{"ttl":"5m"}}'   # or "1h"
Response - the cache object reports what happened
{ "memories": [ ... ],
  "cache": { "status": "hit",              // "write" | "hit" | "extend"
             "ttl": "5m",
             "cache_read_input_tokens": 412,
             "cache_creation_input_tokens": 0 } }

You do not need an SDK for any of this. Caching is one field on one HTTP call, so it works from every programming language. The curl tab is the universal recipe, and the Python, TypeScript, and Rust SDKs are convenience wrappers around the exact same call.

Caching is isolated per store and per model inside your workspace, and it is off by default: without cache_control, nothing about your requests changes.