Skip to main content
LLM calls are expensive and slow. Cognis has three caching layers, each fitting a different access pattern. Pick the one that matches what you’re trying to skip.

Pick a cache

The three layers compose — you can have all of them on at once. Each catches a different category of “we already did this.”

In-memory cache

with_memory_cache(key_fn) wraps a Runnable with a hash-keyed in-memory cache. The closure produces the cache key from the input.
Identical inputs return immediately. Cache lives for the lifetime of the wrapped value.

SQLite cache

For multi-process or long-running services, use the SQLite-backed cache (feature cognis/cache-sqlite):
The cache survives restarts and is shared across processes that point at the same file. Good for CLI tools, batch jobs, and single-host services.

Provider prompt caching

Anthropic and some others support marking the start of a prompt as cacheable on the provider side. You pay full price the first time, a fraction on subsequent calls. Use the PromptCaching middleware:
The middleware adds the right markers; the provider does the deduplication. Particularly powerful for agents with long, stable system prompts and tool definitions — those rarely change between turns. To wire this inside an AgentBuilder agent, see Middleware → Wiring middleware into an agent.

How it composes

A typical stack:
Mark cacheable prompts for the provider, retry on transient errors. For an in-memory dedup cache on top, use client.with_memory_cache(key_fn) directly when calling the Client outside the pipeline, or layer your own caching middleware in the pipeline.

Embedder cache

For RAG, wrap your embedder with CachedEmbeddings:
Same chunk, embedded twice → second call free. This is hugely valuable when re-running indexing pipelines on slowly-changing corpora.

Cache invalidation

A cache only helps if a hit returns the right answer. Three invalidation knobs:
  • Key carefully. Include everything that influences output: messages, model name, temperature, tool list, system prompt. Two prompts with the same text but different tools are different cache entries.
  • Bypass on demand. with_memory_cache doesn’t have a force-bypass switch by design; if you need one, wrap your own cache backend.
  • TTL. The default in-memory cache is unbounded — explicitly limit with MemoryCache::with_capacity(n) if you’re worried about memory.

How it works

  • Hashing happens in your key_fn. Whatever string the closure returns is the cache key. No automatic introspection.
  • Cache entries are typed. A Cache<R, I, O, K, B> only stores O. Different output types → separate caches.
  • Provider prompt caching is opaque to the client. The middleware adds protocol markers; the provider returns whether the cache hit. Trace via cost tracking to see cache_read_tokens.

See also

Resilience

Combine caching with retries and fallbacks.

Cost tracking

See provider cache hits in your cost numbers.

Embeddings

CachedEmbeddings for RAG.