Skip to content

v0.32.0

Latest

Choose a tag to compare

@applied-ai-release-bot applied-ai-release-bot released this 11 Nov 10:21

RedisVL 0.32.0 adds OpenTelemetry metrics and spans for SemanticCache and vectorizers, drops Python 3.10, adds async methods to MessageHistory, and makes SearchIndex work with RESP3 clients. Before upgrading, read the behaviour change to exists() below if your code creates an index when exists() returns False.

Highlights

OpenTelemetry support for SemanticCache and vectorizers

RedisVL now emits OpenTelemetry metrics and spans, so you can see how often SemanticCache hits, how long each operation takes, and how close every miss came to matching.

  • Metrics: redisvl.semantic_cache.operation.duration covers check, store, update and drop, split by hit and miss, which gives you the hit rate. redisvl.semantic_cache.distance records the nearest candidate's distance on every lookup, hit or miss. That distance is what tells you whether distance_threshold is right: if most misses land just above it, loosening it slightly turns them into hits.
  • Spans: each cache operation produces a span tree that separates embedding the prompt from the vector search, so a slow lookup shows where its time went. Every vectorizer also emits embeddings spans and the gen_ai.client.operation.duration metric.

To turn it on, install redisvl[otel] and configure an OpenTelemetry SDK in your application. RedisVL depends on the OpenTelemetry API only and records nothing until you configure an SDK, so applications that don't use OpenTelemetry are unaffected. Prompts are recorded only when OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT is SPAN_ONLY or SPAN_AND_EVENT, and REDISVL_OTEL_DISABLED=true switches all of it off. The new guide, "Observe the Semantic Cache with OpenTelemetry", walks through the setup. (#755, #776, #777, @vishal-bala)

Python 3.10 is no longer supported

RedisVL now requires Python 3.11 or later. On Python 3.10, pip install redisvl keeps installing 0.31.0, so move to 3.11 or later to get this release and later ones. (#810, @vishal-bala)

Async methods on MessageHistory

MessageHistory now has async versions of its methods: aget_recent, astore, aadd_messages, aadd_message, acount, adrop, aclear and adisconnect. If your agent runs on asyncio, reading and writing chat history no longer blocks the event loop. The async index is created on the first async call from the history's existing connection settings, the same way SemanticRouter.aroute works. A sync Sentinel or RedisCluster client can't be converted, so pass redis_url instead if you use one. (#816, @Yarmoluk)

SemanticCache TTL refresh controls

By default, every cache hit resets the entry's TTL, so an entry that keeps getting hit never expires. You can now turn that off with refresh_ttl_on_hit=False on the constructor, or for a single lookup with refresh_ttl=False on check() or acheck(). Use it when answers should expire on a fixed schedule however popular they are. With refresh off, a hit sends no EXPIRE, so a lookup makes no writes and works under an ACL user that can only read. (#618, @omribz156, @vishal-bala)

RESP3 client support in SearchIndex

query(), paginate(), search(), clear() and delete(drop=True) now work on SearchIndex and AsyncSearchIndex with a client created with protocol=3; before, they failed on such a client. On a RESP3 client, search(), cluster_search() and async_cluster_search() now return a redis-py Result rather than the raw reply dict, so update any code that reads that dict. HybridQuery and AggregateHybridQuery are still unsupported on RESP3. (#830, @hyeonsang010716)

Behaviour change: exists() raises when Redis can't answer

SearchIndex.exists() and AsyncSearchIndex.exists() now recognise a missing index by the reply type rather than by the wording of Redis Search's error, so create() keeps working if a future Redis release rewords it. As part of that change, two errors that used to read as "index missing" now raise RedisSearchError: BUSY, while a script or function is running on the server, and ERRCLUSTER, while a cluster's Redis Search coordinator is not yet initialised. Previously exists() returned False in both cases, so code that creates the index when exists() returns False would try to create one that was already there. If you call exists() at startup, catch RedisSearchError and retry. (#818, @Yarmoluk)

Other fixes

  • Vector fields declared with index_missing: true can now be created, because INDEXMISSING is emitted after the vector attribute block. You can then use ismissing(@field) to find documents that don't have an embedding yet. (#771, @RaphaelFakhri)
  • Hybrid queries with an explicit KNN or RANGE vector clause no longer send default tuning parameters you didn't set, which the server rejected, and hybrid RANGE now accepts a radius of 0. (#828, @gezerui888)
  • SVS-VAMANA compression values are no longer case-sensitive: lvq4x8 in a schema now means the same as LVQ4x8 instead of failing validation, and the migration wizard accepts any case too. (#809, @Bhavy-Ranka, @servis)
  • CohereReranker.arank now creates Cohere's AsyncClient on first use and recreates it when the event loop changes, so it keeps working in tests or scripts that call asyncio.run() more than once; before, every call after the first loop closed failed with "Event loop is closed". If you pass your own httpx_client, also pass async_httpx_client; with only a sync client, arank falls back to the blocking rank and logs a warning. (#827, @Yarmoluk)

Contributors

Thanks to @Yarmoluk, @hyeonsang010716, @RaphaelFakhri, @gezerui888, @omribz156, @Bhavy-Ranka and @servis for their contributions to this release, including first contributions from @RaphaelFakhri, @gezerui888, @omribz156, @Bhavy-Ranka and @servis.

Full changelog: v0.31.0...v0.32.0