RAG cache¶
The cache sub-package eliminates redundant work in the RAG pipeline: re-parsing the same document, re-embedding the same chunks, and especially re-summarising the same cluster of text with the same LLM call. The most consequential consumer is RAPTOR (which summarises overlapping clusters across runs) and GraphRAG (which extracts entities from documents that often recur).
All implementations satisfy:
type Store interface {
Get(ctx, hash) (*CacheEntry, bool)
Put(ctx, hash, *CacheEntry) error
Delete(ctx, hash) error
Size() int
Clear()
}
A TransformationCache namespaces keys by a transform name so two
caches can share a store without colliding.
Stores¶
in-memory — process-local LRU¶
| Field | Type | Default | Description |
|---|---|---|---|
max_size |
int | 10000 | Eviction trigger — oldest entry by CreatedAt evicted on overflow. |
Concurrency-safe via sync.RWMutex. Use for single-process runs,
tests, and as the default fallback. Source:
pkg/rag/cache/cache.go (InMemoryStore).
redis — Redis-backed shared cache¶
rag:
cache:
type: redis
addr: localhost:6379
password: ${REDIS_PASSWORD}
db: 0
prefix: "helix:cache:"
ttl: 24h
| Field | Type | Default | Description |
|---|---|---|---|
addr |
string | — | Required. Redis address. |
password |
string | — | Optional. |
db |
int | 0 | Redis DB number. |
prefix |
string | helix:cache: |
Prefix for all keys. |
ttl |
duration | 0 (no expiry) | Per-entry TTL set via Redis SET EX. |
Native TTL (uses SET EX per key). Size() uses SCAN (so it scans
the keyspace each call — call sparingly). Use across multiple
processes or to share cached LLM responses with other tools. Source:
pkg/rag/cache/redis.go.
Processors¶
CacheProcessor — mark a hit, don't compute¶
Sets HelixCacheHit and HelixCacheKey on the exchange but does
not run the inner pipeline. Use as a probe followed by a choice
that routes hits around the rest of the pipeline. Source:
pkg/rag/cache/cache.go (CacheProcessor).
SubPipelineCacheProcessor — full wrap-and-replay¶
rag:
cache:
type: wrap
store: ${CACHE_REF}
transform: summarise
inner: ${INNER_PROCESSOR_REF}
key_header: "" # default: hash full body
ttl: 1h
| Field | Type | Default | Description |
|---|---|---|---|
store |
ref | — | Required. |
transform |
string | default |
Cache-key namespace prefix. |
inner |
ref | — | Processor whose output should be cached. |
key_header |
string | — | Optional: hash this header value instead of the full body. |
ttl |
duration | 0 | Eviction after this age. Zero = no expiry. |
Wraps an inner processor so identical inputs replay cached outputs.
On hit, the inner pipeline is skipped, the cached body replaces
ex.In().Body(), and HelixCacheHit=true plus
HelixCacheCreatedAt are set. On miss, the inner runs and the
result is cached under the computed key. TTL is enforced at Get
time (entries past TTL are deleted in-line). Source:
pkg/rag/cache/cache.go (SubPipelineCacheProcessor).
CachingChatModel — memoise LLM Generate calls¶
Wraps any llm.ChatModel so identical Generate calls (same
provider + model + messages + resolved options) replay from cache
instead of hitting the API again. Cache keys are namespaced by
provider:model so swapping models doesn't return another model's
cached answers. Only Generate is memoised — Stream is delegated
unchanged (streaming responses aren't cached). Tracks Hits() and
Misses() for metrics.
This is the cache wrapper that makes RAPTOR cluster summarisation
practical: the same cluster (with the same prompt) hits the cache on
re-builds, dropping LLM cost to near-zero. Source:
pkg/rag/cache/chatmodel.go.
Helpers¶
| Function | Purpose |
|---|---|
HashDocument(*rag.Document) |
SHA-256 of content + metadata. |
HashChunks([]*rag.Chunk) |
SHA-256 of chunk contents. |
DeduplicateDocuments(ctx, store, docs) |
Skip docs already in cache; record new ones. |
GetStats(store) *Stats |
Returns Size (and Hits / Misses if tracked separately). |
Source: pkg/rag/cache/cache.go.
Choosing between them¶
| Use case | Pick |
|---|---|
| Tests / single-box runs | in-memory |
| Multi-process or persistent | redis (with TTL set per workload) |
| Cache an expensive sub-pipeline | SubPipelineCacheProcessor |
Cache all LLM Generate calls |
CachingChatModel (especially for RAPTOR / GraphRAG) |
| Skip already-ingested docs | DeduplicateDocuments |
| Probe before an expensive branch | CacheProcessor + choice routing |
In production RAPTOR + GraphRAG pipelines, the typical wiring is:
This single change can drop a re-run's LLM cost by 90%+.