redis-vl-dotnet

EmbeddingsCache

EmbeddingsCache is the simplest cache surface in the library. It stores one embedding per exact input string and retrieves it later without creating a RediSearch index.

When to use it

Use EmbeddingsCache when:

  • embedding generation is expensive and you want exact-input reuse

  • you do not need semantic lookup or metadata filtering

  • you want a lightweight cache that only depends on basic Redis hash operations

If you need nearest-neighbor lookup against previously stored prompts and responses, use SemanticCache instead.

Options and key layout

EmbeddingsCacheOptions controls three things:

  • Name, which becomes part of the Redis key prefix

  • KeyNamespace, which lets multiple callers isolate entries inside one named cache

  • TimeToLive, which applies a default Redis TTL to each stored entry

Stored values are Redis hashes keyed like embeddings:<name>:<namespace>:<sha256(input)> for legacy input-only entries and embeddings:<name>:<namespace>:<sha256(input + "\n" + model)> for model-aware entries. The library hashes the original identity before writing, but lookup is still an exact string match because the cache re-hashes the caller-provided input and optional model name.

Each hash stores the same core fields exposed by Python RedisVL parity work:

  • input

  • model_name

  • embedding

  • metadata

Metadata is returned as the serialized JSON string stored in Redis so callers can inspect or deserialize it however they prefer.

Core operations

API Behavior

SetAsync(input, modelName, embedding, metadata, timeToLive)

Writes or overwrites the cached entry and returns an EmbeddingsCacheEntry that includes the Redis key, normalized identity, embedding, and serialized metadata.

GetAsync(input, modelName)

Returns the cached EmbeddingsCacheEntry for that exact identity, otherwise null.

GetByKeyAsync(key) / ExistsAsync(…​) / ExistsByKeyAsync(key)

Supports direct Redis-key access plus semantic existence checks for exact input and model identity.

DeleteAsync(…​) / DeleteByKeyAsync(key)

Deletes a single entry by semantic identity or direct Redis key and returns true only when Redis removed an entry.

SetManyAsync(entries) / GetManyAsync(lookups) / GetManyByKeyAsync(keys)

Batch operations preserve the caller’s input order. Read batches return one result per requested lookup or key, using null in-place for misses instead of dropping them.

ExistsManyAsync(lookups) / ExistsManyByKeyAsync(keys) / DeleteManyAsync(lookups) / DeleteManyByKeyAsync(keys)

Batch existence checks return aligned bool results, while batch deletes return the number of deleted entries so mixed hit/miss batches can be handled without re-querying Redis.

StoreAsync(…​) / LookupAsync(…​)

Legacy-compatible aliases for callers that prefer the original method names. StoreAsync still returns bool, while LookupAsync delegates to GetAsync.

Single-entry and batch delete helpers are available for semantic lookups and direct Redis keys. Use short-lived namespaces or TTLs when you want automatic expiration instead of explicit cleanup.

TTL behavior

When TimeToLive is configured, each write resets the Redis expiration for that entry. SetAsync, StoreAsync, and EmbeddingsCacheWriteRequest can also accept a per-call timeToLive, which overrides the cache default for that write only. Once the key expires, GetAsync and LookupAsync return null, ExistsAsync returns false, and delete calls behave like a miss.

Python-parity workflow

The intended translation from Python RedisVL to .NET is:

  1. Write entries with SetAsync when you want the returned EmbeddingsCacheEntry, including the generated Redis key.

  2. Read exact matches with GetAsync(input, modelName) or GetByKeyAsync(key) when a prior write returned the key.

  3. Use Exists* and Delete* for cache lifecycle checks and cleanup.

  4. Use SetManyAsync, GetManyAsync, and the other batch methods when callers already have multiple prompts to process.

  5. Keep Store* and Lookup* for legacy input-only usage or for callers migrating incrementally from older .NET examples.

Python to .NET method mapping

Python RedisVL .NET async .NET sync

set(text, model_name, embedding, metadata=None, ttl=None)

SetAsync(input, modelName, embedding, metadata, timeToLive)

Set(input, modelName, embedding, metadata, timeToLive)

get(text, model_name)

GetAsync(input, modelName)

Get(input, modelName)

mset([{ text, model_name, embedding, metadata, ttl }])

SetManyAsync(entries)

SetMany(entries)

mget([{ text, model_name }])

GetManyAsync(lookups)

GetMany(lookups)

get_by_key(key)

GetByKeyAsync(key)

GetByKey(key)

exists(text, model_name)

ExistsAsync(input, modelName)

Exists(input, modelName)

exists_by_key(key)

ExistsByKeyAsync(key)

ExistsByKey(key)

delete(text, model_name)

DeleteAsync(input, modelName)

Delete(input, modelName)

delete_by_key(key)

DeleteByKeyAsync(key)

DeleteByKey(key)

mexists([{ text, model_name }])

ExistsManyAsync(lookups)

ExistsMany(lookups)

mdelete([{ text, model_name }])

DeleteManyAsync(lookups)

DeleteMany(lookups)

set(text, embedding) legacy input-only usage

StoreAsync(input, embedding) or SetAsync(input, embedding)

Store(input, embedding) or Set(input, embedding)

Example

The runnable example for this API is /examples/EmbeddingsCacheExample.

It demonstrates:

  • creating a namespaced cache with TTL

  • storing an embedding and capturing the returned Redis key

  • reading the same entry by semantic identity and by direct Redis key

  • checking existence before and after deletion

  • reading mixed-hit batches while preserving request order

  • overwriting an existing embedding entry and applying a per-call TTL override

The exact behavior is also covered in tests/RedisVL.Tests/Caches/EmbeddingsCacheTests.cs and tests/RedisVL.Tests/Caches/EmbeddingsCacheIntegrationTests.cs.