Skip to content

Python: fix(python/redis): handle redisvl 0.5 API change in process_results and guard vector buffer decode#13899

Open
Doondi-Ashlesh wants to merge 2 commits intomicrosoft:mainfrom
Doondi-Ashlesh:fix/redis-search-process-results-compat
Open

Python: fix(python/redis): handle redisvl 0.5 API change in process_results and guard vector buffer decode#13899
Doondi-Ashlesh wants to merge 2 commits intomicrosoft:mainfrom
Doondi-Ashlesh:fix/redis-search-process-results-compat

Conversation

@Doondi-Ashlesh
Copy link
Copy Markdown

Summary

Fixes #13896

Two bugs in the Python Redis connector cause FT.SEARCH (collection.search(...)) to fail completely:

Bug 1 - redisvl 0.5.0 broke process_results signature

redisvl 0.5.0 changed the third parameter of process_results() from StorageType to IndexSchema. Because pyproject.toml pins redisvl ~= 0.4 (which allows 0.5.x per PEP 440), users who upgrade redisvl to 0.5.x get:

AttributeError: 'StorageType' object has no attribute 'index'

Fix: detect the active API at import time via inspect.signature. When IndexSchema is required, construct a minimal schema from the collection name and storage type before calling process_results.

Bug 2 - KeyError on missing vector field during deserialization

RedisHashsetCollection._deserialize_store_models_to_dicts calls buffer_to_array() for every vector field unconditionally. When include_vectors=False (the default for search), the vector field is absent from the result dict, raising:

KeyError: '<vector_field_name>'

Fix: skip the vector buffer decode when the field is absent from the record.

Test plan

  • Run existing Redis unit tests: cd python && make test TEST_FILTERS="connectors/memory/test_redis" (no new failures)
  • Manual smoke test with Redis Stack: create_collection, upsert, search with include_vectors=False (default) completes without error
  • Manual smoke test with include_vectors=True still decodes vectors correctly
  • Tested with both redisvl 0.4.x and 0.5.x

Signed-off-by: Doondi-Ashlesh doondiashlesh@gmail.com

…nd guard vector buffer decode

Two bugs prevent FT.SEARCH from working in the Python Redis connector:

1. redisvl 0.5.0 changed the signature of process_results() to accept
   an IndexSchema instead of StorageType. Detect the active API at import
   time via inspect.signature and pass the appropriate argument. When
   IndexSchema is required, construct a minimal schema from the collection
   name and storage type so no search-path code needs to know the redisvl
   version.

2. RedisHashsetCollection._deserialize_store_models_to_dicts calls
   buffer_to_array() unconditionally for every vector field. When
   include_vectors=False (the default for search), the vector field is
   absent from the result dict and a KeyError is raised. Add a guard so
   vector decoding is skipped for absent fields.

Fixes microsoft#13896

Signed-off-by: Doondi-Ashlesh <doondiashlesh@gmail.com>
@Doondi-Ashlesh Doondi-Ashlesh requested a review from a team as a code owner April 21, 2026 03:22
@moonbox3 moonbox3 added the python Pull requests for the Python Semantic Kernel label Apr 21, 2026
@github-actions github-actions bot changed the title fix(python/redis): handle redisvl 0.5 API change in process_results and guard vector buffer decode Python: fix(python/redis): handle redisvl 0.5 API change in process_results and guard vector buffer decode Apr 21, 2026
@Doondi-Ashlesh
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 4 | Confidence: 89%

✓ Correctness

This PR adds backward-compatible support for the redisvl API change where process_results switched its third parameter from StorageType to IndexSchema. It uses runtime introspection to detect which API is available and constructs a minimal IndexSchema when needed. It also adds a guard for missing vector fields during deserialization. The changes are logically correct for the current usage patterns. The minimal IndexSchema (with empty fields) would cause a KeyError if normalize_vector_distance=True were ever passed to VectorQuery, but the SK code never sets that flag, so this is not a current bug — just a latent fragility.

✓ Security Reliability

This diff introduces two changes: (1) a module-level compatibility shim that uses inspect.signature to detect whether the redisvl process_results function accepts a schema parameter (new API) or a StorageType (old API), and branches accordingly at search time; (2) a defensive guard in RedisHashsetCollection._deserialize_store_models_to_dicts that checks if a vector field exists in the record before attempting buffer_to_array deserialization, preventing a KeyError when vectors are excluded from search results (include_vectors=False). Both changes are straightforward reliability improvements with no security concerns. The try/except ImportError at module level correctly handles older redisvl versions that lack IndexSchema, and the IndexSchema.from_dict call uses only trusted internal values (collection_name and storage type enum value).

✗ Test Coverage

This PR makes two defensive changes to the Redis connector: (1) a compatibility shim for the redisvl process_results API change (new versions require an IndexSchema instead of StorageType), and (2) a guard against KeyError when vector fields are absent from search results (e.g., when include_vectors=False). Neither change has any corresponding unit tests. The existing test file (test_redis_store.py) has no tests for _inner_search or for _deserialize_store_models_to_dicts with missing vector fields. Both behaviors are easily unit-testable with mocks already present in the test file.

✗ Design Approach

The diff has two changes. The vector-field presence guard (change 2) is a reasonable defensive fix. The first change — using runtime inspect.signature to detect whether process_results accepts a schema or StorageType argument — is a fragile, symptom-level workaround for an API-breaking change in redisvl. The project already pins redisvl ~= 0.4 in pyproject.toml, so the right fix is to either tighten the version constraint to the sub-version that introduced the new signature, or migrate to the new API exclusively and drop the old branch. Additionally, the try/except ImportError guard around IndexSchema is redundant: StorageType is already imported from the same redisvl.schema module unconditionally at line 24, so if redisvl is importable, IndexSchema will always be importable too, making the fallback path unreachable.

Flagged Issues

  • Runtime signature introspection (inspect.signature(process_results).parameters) is used to branch between two incompatible process_results call sites. This couples SK permanently to detecting a transient redisvl API break instead of expressing the version requirement explicitly. The proper fix is to tighten the redisvl ~= 0.4 pin to the sub-version where process_results gained the schema parameter (and use only that API path), or cap the upper bound and keep only the old path. Signature sniffing will silently do the wrong thing if redisvl's signature changes again.

Suggestions

  • Add unit tests for both new code paths: (1) a test for _deserialize_store_models_to_dicts with a record missing the vector field key, verifying no KeyError is raised; (2) a parametrized test for _inner_search that patches _PROCESS_RESULTS_USES_SCHEMA to True/False and asserts the correct process_results call signature is used.

Automated review by Doondi-Ashlesh's agents

…tests

Address review feedback on the earlier compatibility shim approach:

- Drop the inspect.signature dual-path. redisvl ~= 0.4 allowed 0.5.x per
  PEP 440, but the right fix is to express the requirement explicitly.
  Update pyproject.toml to redisvl >= 0.5 and use only the IndexSchema
  API path in _inner_search. The redundant try/except ImportError guard is
  removed since StorageType is already imported from the same module.

- Add test_deserialize_hashset_skips_missing_vector_field: calls
  _deserialize_store_models_to_dicts with a record that has no vector key
  (as returned by search with include_vectors=False) and asserts no
  KeyError is raised and other fields are intact.

- Add test_inner_search_passes_index_schema_to_process_results:
  parametrized over hashset and json, patches process_results and
  AsyncSearch.search, calls _inner_search with a direct vector, and
  asserts that the third argument to process_results is an IndexSchema
  with the correct storage_type - not a bare StorageType enum.

Fixes microsoft#13896

Signed-off-by: Doondi-Ashlesh <doondiashlesh@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Pull requests for the Python Semantic Kernel

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