Skip to main content

Semantic Cache

The Semantic Cache middleware reduces LLM response times and API costs by avoiding redundant computations. It uses semantic similarity (not only text matching) to determine whether a request has been previously answered—and reuses the cached result when appropriate.

By default, the cache is private: each caller gets its own partition, so one consumer never receives another consumer's cached response. Set cacheControl.public: true to share the cache across all callers on a route.

A single semantic-cache plugin handles every client format through the clientRequestFormat field:

clientRequestFormatBest forKey options
unset, with contentTemplateAny JSON payload / REST requestcontentTemplate
ccrOpenAI-compatible chat completionsignoreSystem, ignoreAssistant, ignoreTool, messageHistory
responsesAPIOpenAI Responses APIignoreInstructions, ignoreTool, messageHistory
messagesAPIAnthropic Messages APIignoreSystem, ignoreTool
note

Available from v3.21.0-ea.1:

  • ignoreTool for responsesAPI and messagesAPI
  • messageHistory for responsesAPI
Deprecation Notice

The chat-completion-semantic-cache plugin name is deprecated and will be removed in a future release. Use the semantic-cache plugin with clientRequestFormat: ccr set explicitly instead. See Client Request Format for the full mapping. Existing configurations using the legacy name continue to work but should be migrated.

Key Features and Benefits​

  • Faster Responses: Resolve repeated requests in milliseconds instead of waiting for LLM inference.
  • Lower API Costs: Avoid paying for redundant token usage across identical or similar prompts.
  • Semantic Matching: Works even when input phrasing changes, thanks to vector-based similarity.
  • Private by default: Cache responses are partitioned per caller identity — no cross-tenant data leaks.
  • Safe Caching: readOnly mode allows staging and production separation to prevent cache pollution
  • Multiple Vectorizeres & DB support: Support for OpenAI, Ollama, Mistral, redis-stack, Milvus, and Weaviate etc.

Requirements​

  • You must have AI Gateway enabled:

    helm upgrade traefik traefik/traefik -n traefik --wait \
    --reset-then-reuse-values \
    --set hub.aigateway.enabled=true
  • You need a vectorizer that can produce text embeddings. We currently support:

    • OpenAI
    • Gemini
    • Ollama
    • Mistral
    • Azure OpenAI
    • Bedrock
    • Cohere

If your chosen vectorizer requires a credential, store it in a Kubernetes Secret and reference it in the middleware configuration with a urn reference.

Credential field names vary by vectorizer:

  • OpenAI and Cohere use token.
  • Azure OpenAI, Gemini, and Mistral use apiKey.
  • Ollama and Bedrock don't take a credential field here: Ollama typically runs without authentication, and Bedrock authenticates through AWS credentials.

Using the wrong field name for a given provider doesn't raise a configuration error. The value is silently ignored, an empty credential is sent with each request, and the provider's API rejects it. See Configuration Options for the exact field per vectorizer.

How It Works​

When an AI request arrives, the semantic cache middleware processes it through the following steps:

Prerequisites for Caching:

  • Only requests with a request body are cached. GET requests cannot be cached since they typically don't have bodies to extract content from.
  • Only 200 OK responses are cached. Other status codes like 201 Created, 400 Bad Request, etc. are not cached.
  • The contentTemplate must successfully extract text from the request body for caching to occur.
  1. Resolve the cache partition: When cacheControl.public is false (the default), the middleware looks for the caller's identity in the request context — set by the auth middleware upstream (JWT, OIDC, LDAP, API Key, or OAuth Client Credentials) or by the Hub application ID for API Management routes. If no identity is found, the cache is bypassed and X-Cache-Status: Bypass is returned.

  2. Extract & Prepare Text : The middleware extracts text from the request body and formats it according to a content template. By default, it takes the last user message in a typical chat completion request.

Automatic format detection

When a Chat Completion, Responses API, Messages API, Bedrock Mantle, or Google Agent Platform middleware precedes Semantic Cache in the chain, it marks the client's request format in the request context. Semantic Cache then extracts the conversation content from relevant fields in that format automatically, and no clientRequestFormat value is required.

  1. Compute Embeddings : The text is converted into a vector using a vectorizer. Each vector can represent the semantic meaning of the request text.

  2. Similarity Search in Vector Database: The middleware queries a vector database (redis-stack, weaviate, or Milvus) to see if there is a cached response with a sufficiently close vector (based on a similarity threshold, for example, maxDistance).

  • Cache Hit: If a similar vector is found, the cached answer is returned.
  • Cache Miss: The request proceeds to the AI service, and the resulting response is stored as a new entry in the cache (unless readOnly is true).
  1. Response Headers : The user sees the following headers in the response:

    • X-Cache-Status: Hit, Miss, or Bypass.
    • X-Cache-Distance : useful for tuning your similarity threshold.
  2. Separate caches - stream ("stream": true) and non-stream answers live in different buckets; a hit in one does not populate the other.

Service Response Requirements

The semantic cache only stores responses with 200 OK status codes. If your API returns 201 Created, 202 Accepted, or other success codes, consider configuring your service to return 200 OK for cached endpoints.

Configuration Examples​

Choose the plugin variant that matches your use case, then configure it with your preferred vector database.

Semantic Cache Plugin​

Use the semantic-cache plugin for general REST APIs and custom JSON payloads. You must define a contentTemplate to extract text from your specific JSON structure.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
namespace: traefik
name: semantic-cache
spec:
plugin:
semantic-cache:
vectorizer:
ollama:
baseUrl: http://ollama.default.svc.cluster.local:11434 # Protocol HTTP/HTTPS must be declared
model: nomic-embed-text
vectorDB:
redis:
endpoints:
- redis.default.svc.cluster.local:6379
collectionName: demo_doc
maxDistance: 0.6
ttl: 3600
readOnly: false
allowBypass: true # Allow clients to bypass cache with Cache-Control headers
contentTemplate: '{{ .messages }}' # Extract 'messages' field from JSON body like {"messages": "text"}

Chat Completion + Semantic Cache​

Use the semantic-cache plugin with clientRequestFormat: ccr for OpenAI-compatible chat completion endpoints. It has built-in understanding of the messages array and offers chat-specific filtering options.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: semantic-cache-chat
spec:
plugin:
semantic-cache:
clientRequestFormat: ccr
vectorizer:
openai:
model: text-embedding-3-small
token: urn:k8s:secret:ai-keys:openai-token
vectorDB:
redis:
endpoints:
- redis.default.svc.cluster.local:6379
collectionName: chat_cache
maxDistance: 0.4
ttl: 1800
# Chat-specific options
ignoreAssistant: true
messageHistory: 4
readOnly: false
allowBypass: true

Responses API + Semantic Cache​

Use the semantic-cache plugin for OpenAI Responses API endpoints. clientRequestFormat: responsesAPI is auto-detected when a Responses API middleware precedes Semantic Cache in the chain; set it explicitly here to be unambiguous, or if Semantic Cache is used standalone. The cache key is built from the request input field (both plain-string and message-array shapes are supported). By default, the top-level instructions field is also included in the cache key.

semantic-cache-responses-api.yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: semantic-cache-responses
spec:
plugin:
semantic-cache:
clientRequestFormat: responsesAPI
vectorizer:
openai:
model: text-embedding-3-small
token: urn:k8s:secret:ai-keys:openai-token
vectorDB:
redis:
endpoints: ["redis.default.svc.cluster.local:6379"]
collectionName: responses_cache
maxDistance: 0.4
ttl: 1800

Set ignoreInstructions: true to exclude the instructions field from the cache key. Two requests with the same input but different instructions will then share a cache entry:

semantic-cache-responses-api-ignore-instructions.yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: semantic-cache-responses
spec:
plugin:
semantic-cache:
clientRequestFormat: responsesAPI
ignoreInstructions: true
vectorizer:
openai:
model: text-embedding-3-small
token: urn:k8s:secret:ai-keys:openai-token
vectorDB:
redis:
endpoints: ["redis.default.svc.cluster.local:6379"]
collectionName: responses_cache
maxDistance: 0.4
ttl: 1800

Both streaming and non-streaming Responses API requests are cached and replayed correctly.

IngressRoute Configuration​

All plugin variants work with the same IngressRoute configuration:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: ai
namespace: traefik
spec:
routes:
- kind: Rule
match: Host(`ai.localhost`)
middlewares:
- name: semantic-cache # or semantic-cache-chat
services:
- name: chatgpt-external # ExternalName to api.openai.com
port: 443
scheme: https
passHostHeader: false
Vectorizer ≠ Service

vectorizer.* tells Semantic Cache where to fetch embeddings for similarity checks. services: in the IngressRoute tells Traefik Hub where to send the actual request for inference.

They can point to the same provider (all-OpenAI, all-Ollama) or to different ones (local embeddings + cloud LLM). Pick whatever best balances cost, performance, and availability for your use case.

Weaviate collection names

The AI gateway upper-cases the first letter of the collection name internally (chat_cache → Chat_cache). Submit names in any case; they are normalised automatically to Weaviate's expected format.

Configuration Options​

FieldDescriptionRequiredDefault
vectorizerConfigures which embedding provider to use (OpenAI, Azure OpenAI, Cohere, Gemini, Mistral, Ollama, Bedrock.)Yes
vectorizer.<embedding-provider>.baseURLConfigures the base URL of the embedding provider. Must include the scheme and, for OpenAI-compatible endpoints, the /v1 path, for example http://ollama.default.svc.cluster.local:11434/v1.No
vectorizer.<embedding-provider>.modelConfigures the embedding model.Yes
vectorizer.openai.token / vectorizer.cohere.tokenURN of a Kubernetes Secret that holds the embedding provider's API key (for example, urn:k8s:secret:<secretname>:<key>).No
vectorizer.azureopenai.apiKey / vectorizer.gemini.apiKey / vectorizer.mistral.apiKeyURN of a Kubernetes Secret that holds the embedding provider's API key (for example, urn:k8s:secret:<secretname>:<key>).No
vectorizer.<embedding-provider>.schemeConfigures the scheme for the embedding provider.No
vectorizer.clientConfigConfigures the HTTP client settings for the embedding provider.No
vectorizer.clientConfig.timeoutSecondsConfigures the timeout in seconds for the HTTP client (integer value).No5
vectorizer.clientConfig.proxyURLConfigures the proxy URL for the HTTP client.No
vectorizer.clientConfig.insecureSkipVerifyConfigures the insecure skip verify for the HTTP client.Nofalse
vectorDBConfigures which vector database to use (redis-stack, weaviate, or milvus).Yes
vectorDB.redis.endpointsConfigures the Redis host and port (for example, redis.default.svc.cluster.local:6379).Yes
vectorDB.redis.collectionNameConfigures the collection name in Redis.Yes
vectorDB.redis.maxDistanceThreshold for semantic similarity in Redis. The lower the value, the more exact the match must be.No
vectorDB.redis.ttlConfigures the time to live for cached entries in Redis (0 = forever).No0
vectorDB.redis.*Redis configuration inherits from the Redis client configuration and supports additional Redis-specific options.No
vectorDB.weaviate.hostConfigures the Weaviate host and port (for example, weaviate.default.svc.cluster.local:80).Yes
vectorDB.weaviate.collectionNameConfigures the collection name in Weaviate.Yes
vectorDB.weaviate.maxDistanceThreshold for semantic similarity in Weaviate. The lower the value, the more exact the match must be.No
vectorDB.weaviate.ttlConfigures the time to live for cached entries in Weaviate (0 = forever).No0
vectorDB.weaviate.schemeConfigures the scheme for Weaviate (http or https).Yes
vectorDB.weaviate.apiKeyURN of a Kubernetes Secret that holds the Weaviate API key (for example, urn:k8s:secret:<secretname>:<key>).No
vectorDB.weaviate.clientConfigConfigures the HTTP client settings for Weaviate.No
vectorDB.milvus.clientConfigConfigures the Milvus client settings including address, authentication, and connection options.Yes
vectorDB.milvus.clientConfig.addressThe Milvus server address (for example, http://milvus.default.svc.cluster.local:19530).Yes
vectorDB.milvus.clientConfig.usernameUsername for Milvus authentication.No
vectorDB.milvus.clientConfig.passwordPassword for Milvus authentication. (for example, urn:k8s:secret:<secretname>:<key>)No
vectorDB.milvus.clientConfig.dbNameDatabase name for Milvus connection.No
vectorDB.milvus.clientConfig.identifierClient identifier for Milvus connection.No
vectorDB.milvus.clientConfig.enableTLSAuthEnable TLS authentication for Milvus connection.Nofalse
vectorDB.milvus.clientConfig.apiKeyAPI key for Milvus authentication.No
vectorDB.milvus.clientConfig.serverVersionMilvus server version for compatibility.No
vectorDB.milvus.clientConfig.disableConnDisable connection for testing purposes.Nofalse
vectorDB.milvus.clientConfig.retryRateLimitConfigure retry rate limiting options for Milvus client.No
vectorDB.milvus.clientConfig.retryRateLimit.maxRetryMaximum number of retries for rate limited requests.No
vectorDB.milvus.clientConfig.retryRateLimit.maxBackoffMaximum backoff duration for rate limited retries.No
vectorDB.milvus.collectionNameConfigures the collection name in Milvus.Yes
vectorDB.milvus.maxDistanceThreshold for semantic similarity in Milvus. The lower the value, the more exact the match must be.No
vectorDB.milvus.ttlConfigures the time to live for cached entries in Milvus (0 = forever).No0
allowBypassWhen true, the middleware looks at the client's Cache-Control header. If the request includes no-cache or no-store, Semantic Cache skips both read and write operations, forwards the call directly to the model, and returns X-Cache-Status: Bypass. Use this when you want callers to opt-out of caching on demand.Nofalse
cacheControl.publicWhen true, all callers share a single cache partition. When false (the default), the cache is partitioned per caller identity; requests with no resolvable identity bypass the cache.Nofalse
ignoreInstructionsWhen clientRequestFormat: responsesAPI, drops the top-level instructions field from the cache key. Two requests with the same input but different instructions will share a cache entry.Nofalse
contentTemplateA Go template that determines how to extract text from the request. The default template is for chat completion. For REST APIs, you must specify a template matching your JSON structure.No{{ $last := "" }}{{ range .messages}}{{ $last = .content }}{{ end }}{{ $last }}
readOnlyWhen true, the cache is not updated after a miss. Existing entries can still be retrieved.Nofalse
clientRequestFormatFormat of the client request, used to extract text for embedding. One of custom, ccr, responsesAPI, messagesAPI. See Client Request Format. Only applies to the semantic-cache plugin; use clientRequestFormat: ccr instead of the deprecated chat-completion-semantic-cache plugin.Nocustom

Chat completion (clientRequestFormat: ccr) specific configuration options

Applicability by format:

  • ignoreSystem, ignoreAssistant: ccr only.
  • ignoreTool: ccr (all versions); responsesAPI and messagesAPI from v3.21.0-ea.1.
  • messageHistory: ccr (all versions); responsesAPI from v3.21.0-ea.1.

See Client Request Format for the full per-format mapping.

FieldDescriptionRequiredDefault
ignoreSystemWhen true, the system messages are not considered for cache lookup.Nofalse
ignoreAssistantWhen true, the assistant messages are not considered for cache lookup.Nofalse
ignoreToolWhen true, the tool messages are not considered for cache lookup.Nofalse
messageHistoryConfigures the number of messages to consider for cache lookup.No0

Client Request Format​

The clientRequestFormat field controls how the middleware extracts text from the request body to compute the cache key. It only applies to the semantic-cache plugin.

clientRequestFormatBest forText extracted from
custom (default)Generic JSON / REST payloadscontentTemplate, or the raw request body if none is set
ccrOpenAI Chat Completionsmessages[].content
responsesAPIOpenAI Responses APIinput (string or input[].content), plus instructions unless ignoreInstructions is set. Respects ignoreTool and messageHistory from v3.21.0-ea.1.
messagesAPIAnthropic Messages APImessages[].content, plus the top-level system field unless ignoreSystem is set. Respects ignoreTool from v3.21.0-ea.1.

See the Deprecation Notice near the top of this page for chat-completion-semantic-cache migration guidance.

When left unset, Semantic Cache auto-detects ccr, responsesAPI, or messagesAPI from the request context if a Chat Completion, Responses API, Messages API, Bedrock Mantle, or Google Agent Platform middleware precedes it in the chain. Otherwise it falls back to custom.

readOnly​

By default, the readOnly option is set to false, meaning that on a cache miss, the middleware actively adds a new entry to the cache. Setting readOnly to true is useful when you want to test or freeze a pre-populated vector database, preventing new requests from modifying its contents.

For example, in a production deployment, you can configure one route with readOnly: false to serve as an internal endpoint that actively warms up the cache—new entries are added when there is a cache miss. In contrast, a second route with readOnly: true can serve as the production endpoint, ensuring that only pre-validated entries are returned and protecting against cache poisoning. This separation of responsibilities helps maintain a robust and reliable caching layer.

cacheControl​

The cacheControl block controls how cache records are shared between callers.

cacheControl.public: false (the default) enables per-caller partitioning. The middleware derives the partition key from the caller's identity — either the Hub application ID or the identity set by an upstream auth middleware. Requests that arrive without a resolvable identity bypass the cache entirely (X-Cache-Status: Bypass).

cacheControl.public: true places all requests into a single shared partition. Use this for routes where all callers are trusted to see the same cached content, or where no authentication middleware is in the chain.

spec:
plugin:
semantic-cache:
# ... vectorizer and vectorDB config ...
cacheControl:
public: false # default: private, partitioned per caller identity

contentTemplate​

This field is a Go text template that receives the JSON body as input. By default, the middleware picks the last message's content from an array of messages. You can customize it to combine roles or multiple messages, for instance:

"{{ range .messages }}Role: {{ .role }} - Content: {{ .content }}\n{{ end }}"

Vectorizer credential fields​

openai and cohere take their credential under token:

vectorizer:
openai:
model: text-embedding-3-large
token: urn:k8s:secret:ai-keys:openai

azureopenai, gemini, and mistral take theirs under apiKey:

vectorizer:
azureopenai:
baseUrl: https://<resource>.services.ai.azure.com/openai/v1
model: text-embedding-3-large
apiKey: urn:k8s:secret:ai-keys:azureopenai

Setting token under azureopenai, gemini, or mistral doesn't fail configuration validation. It's ignored, the outgoing embeddings request carries no credential, and the provider rejects it. Match the field name to the vectorizer you're using.

vectorizer.clientConfig​

All vectorizers accept an optional clientConfig block for custom HTTP settings:

vectorizer:
openai:
model: text-embedding-3-large
token: urn:k8s:secret:ai-keys:openai
clientConfig:
timeoutSeconds: 30
proxyURL: http://squid.default.svc:3128
insecureSkipVerify: true

Use this when you need a proxy, custom CA bundle, or tighter time-outs.

Vector Database Configuration​

Each vector database has specific configuration options:

Redis Configuration: The Redis vector database configuration inherits from the standard Redis client configuration, supporting additional options like authentication, database selection, and connection pooling. Common Redis-specific fields include:

vectorDB:
redis:
endpoints:
- redis.default.svc.cluster.local:6379
password: urn:k8s:secret:redis-auth:password
collectionName: my_cache
maxDistance: 0.6
ttl: 3600

Partitioning the cache​

By default, the semantic cache is private: every caller gets a separate partition, so one consumer's cached LLM response is never returned to a different consumer.

Behavior change from previous versions

From v3.19.11 and v3.20.6, the semantic cache is private by default (cacheControl.public: false). In previous versions, the semantic cache used a shared collection and any consumer on a route could receive another consumer's cached response. Routes that relied on a shared cache must add cacheControl: { public: true } to preserve that behavior.

Private cache (default)​

When cacheControl.public is false, the middleware partitions the cache by caller identity. Identity is resolved in this order:

  1. Hub application ID — available automatically on API Management routes.
  2. Auth middleware identity — populated by an upstream JWT, OIDC, LDAP, API Key, or OAuth Client Credentials middleware.

If neither is available, the request bypasses the cache and X-Cache-Status: Bypass is returned.

Each auth middleware has an identity field that controls which claim or attribute identifies the caller:

Middlewareidentity fieldDefault value
JWTJWT claim namesub
OIDCOIDC claim namesub
LDAPLDAP attribute namecn
API Key(automatic — SHA-256 hash of the matched secret)—
OAuth Client CredentialsOAuth access token claim namesub

To use a custom claim for cache partitioning on a JWT middleware:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: jwt-auth
spec:
plugin:
jwt:
signingSecret: urn:k8s:secret:jwt-keys:secret
identity: grp # use the 'grp' claim as the cache partition key

See the JWT, OIDC, LDAP, API Key, and OAuth Client Credentials reference pages for the full identity field documentation.

Public cache​

Set cacheControl.public: true to share a single cache partition across all callers on a route. Use this for routes where all callers are trusted to receive the same cached responses, or when no auth middleware is in the chain.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: semantic-cache
spec:
plugin:
semantic-cache:
vectorizer:
ollama:
baseUrl: http://ollama.default.svc.cluster.local:11434
model: nomic-embed-text
vectorDB:
redis:
endpoints:
- redis.default.svc.cluster.local:6379
collectionName: demo_doc
maxDistance: 0.6
ttl: 3600
cacheControl:
public: true # all callers share this cache
contentTemplate: '{{ .messages }}'

Example: shared FAQ cache for a support agent​

Consider this example: A support team wants a chatbot to give the exact same, pre-approved answer every time someone asks a common question, like "What are your business hours?" They don't want unreviewed AI answers ever entering that shared pool.

This works by running two separate Middleware resources that point at the same vectorDB collection, but with different write permissions:

  • One warms the cache: it's allowed to add new entries.
  • One serves customers: it can only read existing entries, never add to them.

Step 1: Warm the cache with reviewed answers​

Create a Middleware with cacheControl.public: true and readOnly: false. Send the support team's approved question-and-answer pairs through this route, for example with a script or an internal admin tool. Each pair gets converted to a vector and stored in the shared collection.

Warming route
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: faq-cache-warm
spec:
plugin:
semantic-cache:
vectorizer:
ollama:
baseUrl: http://ollama.default.svc.cluster.local:11434
model: nomic-embed-text
vectorDB:
redis:
endpoints:
- redis.default.svc.cluster.local:6379
collectionName: faq_cache
maxDistance: 0.6
ttl: 0
cacheControl:
public: true
readOnly: false
contentTemplate: '{{ .messages }}'
Protect the warming route

Anyone who can reach this route can add entries to the shared cache. Add an auth middleware, such as JWT or API Key, to the warming route's middleware chain, and restrict access to your support team or internal tooling only.

After this step, the faq_cache collection holds only the answers the support team put there on purpose.

Step 2: Serve customers from the same cache, read-only​

Create a second Middleware for the customer-facing route. Point it at the same collectionName (faq_cache), but set readOnly: true.

Public route
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: faq-cache-serve
spec:
plugin:
semantic-cache:
vectorizer:
ollama:
baseUrl: http://ollama.default.svc.cluster.local:11434
model: nomic-embed-text
vectorDB:
redis:
endpoints:
- redis.default.svc.cluster.local:6379
collectionName: faq_cache
maxDistance: 0.6
ttl: 0
cacheControl:
public: true
readOnly: true
contentTemplate: '{{ .messages }}'

Now, when a customer's question is close enough to one of the reviewed questions, they receive that exact reviewed answer. When it isn't close enough, the request still reaches the AI service and the customer still gets an answer, but readOnly blocks that answer from being saved. The customer-facing cache never grows on its own; it only ever contains what the warming route puts there.

Troubleshooting​

Cache Status: Bypass (always)

When cacheControl.public is false (the default), the cache requires a caller identity. Requests without one are bypassed.

  • Confirm that an auth middleware (JWT, OIDC, LDAP, or API Key) is chained before the semantic cache middleware in the same IngressRoute.
  • Check that the auth middleware successfully validates the request (no 401 or 403 responses).
  • To disable partitioning temporarily for debugging, set cacheControl.public: true.
Cache Status: Bypass (Large Conversations with Tool Calls)

Vectorizers enforce an input size limit (for example, OpenAI's embeddings API rejects input over 8192 tokens). A conversation with tool-call payloads or long history can exceed that limit; the middleware fails open and returns X-Cache-Status: Bypass on every request instead of blocking.

  • Set ignoreTool: true to drop tool-call content from the cache key. This works with ccr. responsesAPI and messagesAPI support is available from v3.21.0-ea.1.
  • Set messageHistory to a small number to limit the cache key to the most recent messages. This works with ccr. responsesAPI support is available from v3.21.0-ea.1.
  • Check your vectorizer's error logs for a 400 (or similar) response indicating the input was rejected as too large.
Cache Status: Bypass (Vectorizer Misconfigured)

If neither a missing caller identity nor an oversized input explains a permanent X-Cache-Status: Bypass, check the gateway log for an error-level Creating collection line. This means the gateway can't reach your embedding provider at all, usually because vectorizer.<embedding-provider>.baseURL is misconfigured. This produces the same permanent Bypass as the other causes on this page, with no other client-visible difference.

Cache Status: Miss (Always)
  • Verify your contentTemplate matches your JSON structure exactly
  • Use POST or PUT methods with a request body; GET requests are not supported for caching.
  • Confirm your service responds with 200 OK, as only successful responses are cached.
  • For REST APIs, define a contentTemplate that matches your JSON structure exactly.
Common Template Issues
  • Missing quotes: Use contentTemplate: "{{ .field }}" not contentTemplate: {{ .field }}
  • Wrong field names: Ensure the field name in your template matches your JSON exactly (case-sensitive)
  • Nested fields: Use dot notation for nested objects: {{ .data.text }}
  • Array access: For arrays, you may need {{ index .items 0 }} or range over them
Cache Distance Too High

If X-Cache-Distance is always above your maxDistance threshold:

  • Your prompts may be too different semantically
  • Try lowering the maxDistance value (for example, from 0.5 to 0.3)
  • Consider if your use case actually benefits from semantic similarity vs exact matching
Timeout Errors and Retries

If you see timeout errors and automatic retries in the logs when calling your vectorizer:

  • The field name is timeoutSeconds (not timeout)
  • The value must be an integer in seconds: timeoutSeconds: 30 (not 30s)
  • The default timeout is 5 seconds, which may be too short for some embedding models
  • Increase the timeout for slow vectorizers (for example, local Ollama instances): timeoutSeconds: 60

Example configuration:

vectorizer:
ollama:
baseUrl: http://ollama.default.svc.cluster.local:11434
model: nomic-embed-text
clientConfig:
timeoutSeconds: 60 # Increase timeout to 60 seconds