ai-cache
Caches LLM responses by exact or semantic match.
ai-cachePriority 1035What it does
ai-cache keeps the answers a language model gives on a route in Redis and serves a repeated prompt from there, without calling the model again. It acts only on a route that also carries ai-proxy or ai-proxy-multi. The Exact layer matches a prompt word for word, and the Semantic layer adds prompts that mean the same.
The first request reaches the model and gets its answer. The same prompt again is answered from the cache, and the answer carries X-Ai-Cache-Status with HIT.
When to use it
- A help assistant where many customers ask where their order is, answered once by the model and then from Redis for five minutes.
- Prompts that ask the same thing in other words, matched by the semantic layer through embeddings and served the stored answer.
- Answers that differ by caller, kept apart per consumer with Include consumer.
- A client that must always reach the model, for example a test, sending a header listed under Bypass rules.
Not for keeping the answers of an ordinary backend — see proxy-cache (Caches upstream responses on disk or in memory) or holding a route to a budget of model tokens — see ai-rate-limiting (Limits requests to an LLM provider by tokens or count).
Guides
Pages that walk through a job with ai-cache, step by step.
- Put a language model behind the gatewaySend your apps' chat requests to one language model through a route, with the model's key on the gateway, a token budget, a prompt guard and a cache.
Set it up
Answer a repeated prompt from Redis for five minutes
Acts only on a route that also carries ai-proxy or ai-proxy-multi.
Under Basics fill Layers as in the JSON below, fill Exact layer as in the JSON below, leave Policy at redis, set Redis host to cache.example.com, leave Redis port at 6379 and set Redis password to <secret>.
{
"layers": [
"exact"
],
"exact": {
"ttl": 300
},
"policy": "redis",
"redis_host": "cache.example.com",
"redis_port": 6379,
"redis_password": "<secret>"
}
What you will see
- The first time a prompt is seen it is passed to the model and answered
200withX-Ai-Cache-Status: MISS; the answer is kept in Redis. - The same prompt sent again is answered
200with the reply the model gave the first time. - That repeated answer carries
X-Ai-Cache-Status: HIT; the model was not called. - The kept answer is shared with ai-proxy-multi on the same route: a repeated prompt there is answered
200withX-Ai-Cache-Status: HITtoo.
More scenarios
Answer prompts that mean the same thing from the cache with Azure OpenAI embeddings
Acts only on a route that also carries ai-proxy or ai-proxy-multi; the embeddings address is used as written, so it carries the api-version the deployment answers.
Under Basics fill Layers as in the JSON below and set Redis host to cache.example.com. Under Behavior fill Semantic layer as in the JSON below.
{
"layers": [
"exact",
"semantic"
],
"redis_host": "cache.example.com",
"semantic": {
"embedding": {
"azure_openai": {
"api_key": "<secret>",
"endpoint": "https://orders-ai.openai.azure.com/openai/deployments/text-embedding-3-large/embeddings?api-version=2024-02-01"
}
},
"similarity_threshold": 0.95,
"vector_search": {
"redis": {
"index": "prompt-cache"
}
}
}
}
Answer prompts that mean the same thing from the cache with OpenAI embeddings
Acts only on a route that also carries ai-proxy or ai-proxy-multi.
Under Basics fill Layers as in the JSON below and set Redis host to cache.example.com. Under Behavior fill Semantic layer as in the JSON below.
{
"layers": [
"exact",
"semantic"
],
"redis_host": "cache.example.com",
"semantic": {
"embedding": {
"openai": {
"api_key": "<secret>",
"model": "text-embedding-3-small"
}
},
"similarity_threshold": 0.95,
"vector_search": {
"redis": {
"index": "prompt-cache"
}
}
}
}
Things to know
- TTL — With an exact layer in Redis, the first request is answered by the model,
Hello from the mock modelin the example. The same request again is answered200withX-Ai-Cache-Statusset toHIT. - The stored answer does not depend on which proxy plugin called the model. A third request, sent through ai-proxy-multi on the same route, is also a
HIT. - Layers — Only a plain-text OpenAI chat prompt reaches the semantic layer, and anything else is served by the exact layer alone. An image, an audio block or a tool call in the prompt also keeps it out of the semantic layer.
- Semantic layer — Used only when Layers includes semantic; naming semantic there without this block is refused. A block that Layers does not name is ignored and only logged.
- Embedding — An embedding names one provider, and one that names both is refused when you save.
- Endpoint — An Azure OpenAI embedding without an Endpoint is refused when you save.
- Similarity threshold — A threshold above 1, for example
1.5, is refused when you save. - Include variables — Each name is a request variable written without its dollar sign, and a variable the request does not carry joins the key as an empty value.
- Cache status headers — On, every answer carries X-AI-Cache-Status, and a hit also carries X-AI-Cache-Age or, for a semantic hit, X-AI-Cache-Similarity.
- Redis host — A configuration without a Redis host is refused when you save, whatever the layers.
- AdviceCache only answers that every caller of the route may see. For an answer that depends on who asks, turn on Include consumer.
Settings
The fields as the console shows them, section by section. A field under “When … is …” is there only while that value is set.
Basics
layersarray of stringsdefault ["exact"]The cache layers to use, where exact is always needed and semantic adds a similarity match. A list without exact is refused.
exactobjectdefault {}exact.ttlintegerdefault 3600How long an answer stays in the exact layer.
policystringdefault redisRedis is the only store this plugin offers.
When Policy is redis
redis_hoststringRequiredredis_portintegerdefault 6379redis_databaseintegerdefault 0redis_usernamestringThe Redis user name for ACL sign-in, read only when a password is set.
redis_passwordstringStored on the engine and shown only to roles that manage this object.
redis_sslbooleandefault falseredis_ssl_verifybooleandefault falseredis_server_namestringThe server name sent in the TLS handshake and, with verification on, checked against the certificate; unset, the host is sent, and an address never is.
redis_timeoutintegerdefault 1000redis_keepalive_poolintegerdefault 100redis_keepalive_timeoutintegerdefault 10000Behavior
semanticobjectRequired when Layers includes semanticsemantic.embeddingobjectRequiredChoose one:
Embed with OpenAI
semantic.embedding.openaiobjectRequiredsemantic.embedding.openai.api_keystringRequiredStored on the engine and shown only to roles that manage this object.
semantic.embedding.openai.modelstringRequiredsemantic.embedding.openai.dimensionsintegerThe vector length asked of the provider, where the index is built for the length it actually returns.
semantic.embedding.openai.endpointstringThe full embeddings URL, used as written, where empty uses the public OpenAI one.
semantic.embedding.openai.ssl_verifybooleandefault truesemantic.embedding.openai.timeoutintegerdefault 5000How long the embedding call may take, where a slower provider is served as a cache miss.
Embed with Azure OpenAI
semantic.embedding.azure_openaiobjectRequiredsemantic.embedding.azure_openai.api_keystringRequiredStored on the engine and shown only to roles that manage this object.
semantic.embedding.azure_openai.endpointstringRequiredThe whole embeddings URL of the deployment; no path of its own is added to it.
semantic.embedding.azure_openai.dimensionsintegerAsked of the provider; the index is built for the vector length it answers with.
semantic.embedding.azure_openai.ssl_verifybooleandefault truesemantic.embedding.azure_openai.timeoutintegerdefault 5000Connect, send and read alike; a provider slower than this is served as a miss.
semantic.vector_searchobjectRequiredsemantic.vector_search.redisobjectRequireddefault {}Needs the RediSearch module on Redis, because without it every semantic lookup misses and only the log says so.
semantic.vector_search.redis.indexstringdefault ai-cacheThe prefix of the index name and its keys, to which the embedding model and vector length are added.
semantic.matchobjectdefault {}semantic.match.ignore_assistant_promptsbooleandefault truesemantic.match.ignore_system_promptsbooleandefault truesemantic.match.ignore_tool_promptsbooleandefault truesemantic.match.message_countbackintegerdefault 1How many of the latest messages, after the ignore switches are applied, are embedded as the query.
semantic.similarity_thresholdnumberdefault 0.95A stored answer is served when its cosine similarity reaches this.
semantic.top_kintegerdefault 1How many nearest answers the search asks for, of which the nearest decides.
semantic.ttlintegerdefault 86400How long an answer stays in the semantic layer.
cache_keyobjectdefault {}cache_key.include_consumerbooleandefault falseOn, a signed-in consumer is served only what was cached for them.
cache_key.include_varsarray of stringsdefault []cache_key.share_across_routesbooleandefault falseOff, every route keeps a cache of its own.
cache_headersbooleandefault truebypass_onarray of objectsA request carrying one of these headers with that value is served by the upstream and never cached.
bypass_on[].equalsstringRequiredbypass_on[].headerstringRequiredfail_modestringdefault skipWhat a request that did not pass through an AI proxy meets here: skip and warn let it through, error answers 500.
Advanced
max_cache_body_sizeintegerdefault 1048576The largest response body in bytes that is cached, where a larger answer is served but not stored.