Skip to content

fix(python/redis): restore vector search broken by redisvl>=0.5 API change and KeyError on include_vectors=False - #14278

Open
Patrick Ribbsaeter (patrickswedish) wants to merge 4 commits into
microsoft:mainfrom
patrickswedish:fix/redis-vector-search-redisvl-compat
Open

fix(python/redis): restore vector search broken by redisvl>=0.5 API change and KeyError on include_vectors=False#14278
Patrick Ribbsaeter (patrickswedish) wants to merge 4 commits into
microsoft:mainfrom
patrickswedish:fix/redis-vector-search-redisvl-compat

Conversation

@patrickswedish

@patrickswedish Patrick Ribbsaeter (patrickswedish) commented Aug 6, 2026

Copy link
Copy Markdown

Summary

Fixes #13896.

Vector search (FT.SEARCH) via the Python Redis connector was completely broken due to two independent bugs. Both have been fixed in this PR.


Bug 1 — process_results() API break with redisvl >= 0.5

Root cause

redisvl 0.5.0 changed the signature of process_results():

# redisvl < 0.5 (old API)
process_results(results, query, storage_type: StorageType)

# redisvl >= 0.5 (new API)
process_results(results, query, schema: IndexSchema)

The pyproject.toml pin redisvl ~= 0.4 resolves to >=0.4, <1.0 under PEP 440, so uv.lock silently picks up redisvl 0.15.x where the old API is gone. Every call to collection.search() raised:

VectorSearchExecutionException: An error occurred during the search:
'StorageType' object has no attribute 'index'

Fix

Detect the redisvl API signature at runtime via inspect.signature by introspecting the third parameter name:

  • If third_parameter == "storage_type" (redisvl < 0.5), pass the StorageType enum.
  • If third_parameter == "schema" (redisvl >= 0.5), load the schema via AsyncSearchIndex.from_existing(name=collection_name, redis_client=redis_database) and pass index.schema.
  • If an unsupported third parameter is encountered, raise VectorSearchExecutionException rather than guessing.
parameters = list(inspect.signature(process_results).parameters.values())
if len(parameters) < 3:
    raise VectorSearchExecutionException("Unsupported redisvl process_results() signature.")

third_parameter = parameters[2].name
if third_parameter == "storage_type":
    return process_results(results, query, STORAGE_TYPE_MAP[collection_type])

if third_parameter == "schema":
    index = await AsyncSearchIndex.from_existing(
        name=collection_name,
        redis_client=redis_database,
    )
    return process_results(results, query, index.schema)

raise VectorSearchExecutionException(
    f"Unsupported redisvl process_results() parameter: {third_parameter}."
)

Bug 2 — KeyError in RedisHashsetCollection._deserialize_store_models_to_dicts

Root cause

The deserializer unconditionally decoded every vector field:

case "vector":
    dtype = DATATYPE_MAP_VECTOR[field.type_ or "default"]
    rec[field.name] = buffer_to_array(rec[field.name], dtype)  # KeyError!

When include_vectors=False (the SDK default for search), Redis does not return vector fields in the result dict, so rec[field.name] raised KeyError: 'vector'.

Fix

Guard with an existence check before decoding:

case "vector":
    storage_name = field.storage_name or field.name
    if storage_name in rec:
        dtype = DATATYPE_MAP_VECTOR[field.type_ or "default"]
        rec[field.name] = buffer_to_array(rec[storage_name], dtype)
    else:
        rec[field.name] = None  # vector not requested — leave as None

Impact

Both bugs affected every Python user using the Redis connector for vector search:

  • Bug 1 blocked all search calls unconditionally.
  • Bug 2 blocked hashset search whenever include_vectors=False (the default).

The connector was written against redisvl 0.4.x; the dependency pin allows installation of redisvl 0.15.x where the API changed.


Testing

  • Unit tests in python/tests/unit/connectors/memory/test_redis_store.py:
    • test_process_search_results_with_legacy_redisvl_api: verifies legacy storage_type parameter dispatch.
    • test_process_search_results_with_current_redisvl_api: verifies AsyncSearchIndex.from_existing and schema dispatch.
    • test_hash_deserialization_handles_omitted_vector: verifies hashset deserialization when vector is omitted.
  • The fix is backward-compatible across supported redisvl versions without requiring a version pin change.
  • Existing upsert/get/delete integration tests are unaffected.

…ard KeyError on include_vectors=False

Two independent bugs made vector search completely unusable:

1. **process_results() API break (redisvl >= 0.5)**
   `redisvl` 0.5.0 changed the third argument of `process_results()` from
   `StorageType` (an enum value) to `IndexSchema` (an object). The SK
   `pyproject.toml` pin `redisvl ~= 0.4` resolves via PEP 440 to
   `>=0.4, <1.0`, so `uv.lock` picks up redisvl 0.15.x where the old
   API no longer exists. Every call to `collection.search()` raised:
     AttributeError: 'StorageType' object has no attribute 'index'

   Fix: detect the redisvl API version at runtime using `inspect.signature`
   and call `process_results()` with either the legacy `StorageType` enum
   or the new `IndexSchema` object accordingly, with a fallback swap if
   the initial detection is wrong.

2. **KeyError in RedisHashsetCollection._deserialize_store_models_to_dicts**
   The deserializer unconditionally called `buffer_to_array(rec[field.name], dtype)`
   for every vector field. When `include_vectors=False` (the default for
   search), the vector field is absent from the result dict, producing:
     KeyError: 'vector'

   Fix: check `if storage_name in rec` before decoding. When the field is
   absent (not returned by Redis), set the model attribute to `None` rather
   than raising.

Together these two defects blocked all FT.SEARCH usage regardless of
collection type or include_vectors setting.

Fixes microsoft#13896
Copilot AI lite review requested due to automatic review settings August 6, 2026 19:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@patrickswedish

Copy link
Copy Markdown
Author

Hi team! 👋

This PR fixes two independent bugs that break all Redis vector search for Python users running redisvl >= 0.5:

  1. process_results() API breakredisvl 0.5.0 changed the third argument from StorageType to IndexSchema, but the connector still passes StorageType, crashing every single search call with AttributeError
  2. KeyError on include_vectors=False — the hashset deserializer unconditionally accesses rec[field.name] for vector fields, but Redis omits those fields when include_vectors=False (the SDK default)

Both fixes are backward-compatible — runtime API sniffing handles both old and new redisvl versions. Happy to add tests or address any review feedback!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: Bug: Vector search (FT.SEARCH) via the Python Redis connector is broken

2 participants