Hypha Serving Cache is a lightweight middleware around ModelProvider.generate().
It caches exact LLM API responses and records prompt prefix metadata without
changing ReAct, FSM, DomainPack, Session, Run, or Event semantics.
It also records provider-side prefix cache shape. Hypha does not own the physical provider prefix cache; it keeps the rendered prefix stable, canonicalizes tool schemas, and records provider-reported cached/missed prompt tokens when the provider exposes them.
Agent / Workflow / Domain Pack
|
Hypha Runtime
|
ModelProvider / LLMClient
|
ServingCache
|
ProviderAdapter
|
OpenAI / Anthropic / DeepSeek / Qwen / Gemini
The cache is provider-call middleware. Domain Packs do not need to know it exists, and agent run/step/action interfaces are unchanged.
Exact response keys are deterministic hashes over:
| Field | Description |
|---|---|
provider |
Resolved provider id, such as openai or deepseek. |
model |
Resolved model alias/id used by the request. |
system |
System prompt plus cache prefix content when supplied. |
messages |
Normalized request messages or structured input. |
tools |
Canonicalized tool/function schemas. |
params |
Generation params such as temperature and max tokens. |
cacheScope |
Optional tenant, user, project, session, DomainPack ids. |
undefined, timestamps, request ids, and random values are excluded. Object
keys are sorted, and tool schemas are sorted by stable id/name before hashing.
OpenAI-compatible provider requests also export tool schemas in sorted provider
name order so the byte shape sent to the model is stable.
PrefixCacheShapeTracker compares the current stable prefix with the previous
request for the same provider/model/scope. Trace payloads may include:
| Field | Description |
|---|---|
prefixHash |
Hash of stable prompt blocks. |
toolSchemaHash |
Hash of canonicalized tool schemas. |
domainPackHash |
Hash of DomainPack scope when present. |
dynamicSuffixHash |
Hash of current turn messages/input. |
stablePrefixChanged |
Whether provider-side prefix reuse is likely reset. |
dynamicSuffixChanged |
Whether only the dynamic suffix changed. |
changedReasons |
first_request, prefix_changed, tool_schema_changed, domain_pack_changed, dynamic_suffix_changed, or unchanged. |
Provider usage is normalized into cacheHitTokens and cacheMissTokens.
DeepSeek-style prompt_cache_hit_tokens and prompt_cache_miss_tokens, plus
OpenAI-style prompt_tokens_details.cached_tokens, are mapped into Hypha usage
fields and surfaced under servingCache.providerPrefixCache.
CachePolicy fields:
| Field | Default | Description |
|---|---|---|
enabled |
false |
Enables the middleware. |
mode |
readwrite |
One of off, read, write, or readwrite. |
ttlMs |
86400000 |
Entry TTL. Omit for no expiration. |
respectNoCache |
true |
Honors per-request no-cache metadata. |
failureMode |
bypass |
Bypasses an unavailable cache or throws in strict mode. |
scopeRequirement |
user |
Requires no scope, a user, or both a user and session. |
operationTimeoutMs |
250 |
Bounds each store operation before bypass or failure. |
singleflight |
true |
Coalesces concurrent exact misses for the same scoped key. |
maxEntryBytes |
1048576 |
Rejects oversized value plus metadata before store mutation. |
circuitBreaker |
see config | Opens after repeated store failures and later probes recovery. |
Configure the server with:
HYPHA_SERVING_CACHE=off
HYPHA_SERVING_CACHE=memory
HYPHA_SERVING_CACHE=sqlite
HYPHA_SERVING_CACHE=redisstore is the single server enable switch. SQLite entries use
HYPHA_SERVING_CACHE_SQLITE_PATH; Redis uses the shared Redis deployment and
HYPHA_SERVING_CACHE_REDIS_PREFIX. Streaming requests and provider errors are
never cached.
@hypha/serving-cache exposes CacheStore, NoopCacheStore,
MemoryCacheStore, SQLiteCacheStore, and RedisCacheStore. Store
implementations persist versioned, runtime-validated CacheEntry records.
Every store verifies that the physical lookup key matches CacheEntry.key;
malformed or mismatched records are removed and treated as misses. Cache policy
values are runtime-validated before a manager or middleware is created.
Only CachedModelResponseProjection is persisted: content, tool calls, and
usage. Provider raw payloads, old response ids, and arbitrary response
metadata are excluded; every hit receives a new runtime response id. Memory,
SQLite, and prefix-shape indexes are bounded.
Runtime traces may include:
| Event | When emitted |
|---|---|
llm.cache.lookup |
A cache-enabled non-streaming request is checked. |
llm.cache.hit |
A fresh exact cache entry is reused. |
llm.cache.miss |
No entry exists, entry expired, or read disabled. |
llm.cache.write |
A provider response is stored. |
llm.cache.bypass |
Cache is disabled, unscoped, unavailable, no-cache, mode off, or stream. |
These events are regular Hypha events and can be replayed, audited, and evaluated like other runtime facts.
This layer does not implement semantic cache, fuzzy matching, cache trees, WorkCache graph scheduling, tool result cache, provider KV cache management, CPU/GPU cache migration, or streaming chunk cache.