Skip to content

std.cache provides memoization as a decorator. Apply @memoize to any function and its result is cached by (callee, arguments) for the duration of the run. It is the everyday way to avoid recomputing an expensive step — a model call, an embedding, a fetch — that you know is pure for its inputs.

from std.cache import memoize, memoize_disk

Caching an expensive AI step is universal, and hand-rolled caches drift into bespoke pickle paths and ad-hoc keys (a typical hand-rolled cache_or_load pickle layer). std.cache standardizes it, and because it is written in Sema as an ordinary decorator, you can write your own variants (TTL, namespacing) the same way — no framework hooks required.

memoize keys on the callee and its arguments and stores results in the ambient memory store, so its effect row is !{memory.read, memory.write}:

@memoize
def embed(text: str) -> list[f64] !{model.invoke}:
return semantic.embed(text)
a = embed("hello") # computes
b = embed("hello") # cache hit — no second model call

The decorated function’s own effects still apply the first time it runs; the cache only short-circuits repeats within the same process.

memoize_disk JSON-encodes each result under .sema/cache/<hash>.json, keyed by (callee, arguments), so the cache survives process restarts. Its effect row is !{fs.read, fs.write}:

@memoize_disk
def fetch_report(url: str) -> str !{net.connect}:
return http.get(url)
# First run writes .sema/cache/<hash>.json; later runs read it back.

See the exact signatures and effect rows in the generated std.cache API reference.