Skip to content

feat: redis as vector store#450

Merged
Pratham-Mishra04 merged 1 commit into
mainfrom
09-02-feat_redis_as_vector_store
Sep 16, 2025
Merged

feat: redis as vector store#450
Pratham-Mishra04 merged 1 commit into
mainfrom
09-02-feat_redis_as_vector_store

Conversation

@TejasGhatte
Copy link
Copy Markdown
Collaborator

@TejasGhatte TejasGhatte commented Sep 9, 2025

Summary

Add Redis vector store implementation to support Redis as a backend for vector storage and retrieval operations.

Changes

  • Implemented a new RedisStore that implements the VectorStore interface
  • Added support for Redis with RediSearch module for vector similarity search
  • Implemented all required vector store operations (add, get, delete, search)
  • Added comprehensive test suite for Redis vector store
  • Updated the vector store factory to support Redis as a store type

Type of change

  • Feature
  • Bug fix
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (Next.js)
  • Docs

How to test

Requires a Redis instance with RediSearch module enabled:

# Run Redis with RediSearch module
docker run -p 6379:6379 redis/redis-stack

# Set environment variables for tests
export REDIS_ADDR=localhost:6379
export REDIS_USERNAME=
export REDIS_PASSWORD=

# Run tests
go test ./framework/vectorstore -v

Breaking changes

  • Yes
  • No

Related issues

Adds Redis as an alternative to Weaviate for vector storage, providing more deployment options.

Security considerations

  • Redis connection details (username/password) are handled securely
  • Proper error handling for all Redis operations
  • Timeouts implemented for all Redis operations to prevent hanging

Checklist

  • I added/updated tests where appropriate
  • I verified builds succeed (Go)
  • I verified the CI pipeline passes locally

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 9, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added Redis-backed vector store with KNN search, filtering, pagination, and namespace management.
    • Vector store factory now supports Redis.
    • Semantic Cache supports configurable namespace and embedding dimension.
    • UI adds “Dimension” field to cache settings.
  • Refactor

    • Semantic Cache switched from class name to namespace usage.
    • More robust parsing of streamed chunks in cache responses.
  • Tests

    • Comprehensive Redis unit/integration tests; Weaviate tests for namespace-dimension handling; config tests updated.
  • Documentation

    • New unified Vector Store guide (Weaviate + Redis), updated navigation, and Dimension field documented.
  • Chores

    • Added Redis client dependencies.

Walkthrough

Adds a Redis-backed vector store with KNN, CRUD, batching, namespace management and tests; extends vectorstore factory and CreateNamespace to accept dimension; makes semanticcache namespace and dimension configurable; improves stream_chunks parsing; consolidates vector-store docs and adds a UI Dimension field.

Changes

Cohort / File(s) Summary
Dependencies
framework/go.mod
Added github.com/redis/go-redis/v9 v9.12.1 and indirect deps github.com/cespare/xxhash/v2, github.com/dgryski/go-rendezvous.
VectorStore — Redis implementation
framework/vectorstore/redis.go
New Redis-backed store: RedisConfig, RedisStore, CreateNamespace (RediSearch HNSW vector field), Add/GetChunk/GetChunks/GetAll/GetNearest, Delete/DeleteAll/DeleteNamespace/Close, query builders, binary embedding encoding, batching/SCAN, and newRedisStore.
VectorStore helpers
framework/vectorstore/utils.go
Added withTimeout(ctx, timeout) helper for per-operation deadlines.
VectorStore factory / types
framework/vectorstore/store.go
Added VectorStoreTypeRedis; updated VectorStore.CreateNamespace signature to include dimension int; extended Unmarshal/NewVectorStore to construct RedisStore from config.
VectorStore tests & test utilities
framework/vectorstore/redis_test.go, framework/vectorstore/test_utils.go, framework/vectorstore/weaviate_test.go
Added comprehensive Redis test suite (env-driven), shared test helpers, and updated Weaviate tests to validate dimension handling; removed duplicate helpers.
Semantic Cache — namespace, dimension & parsing
plugins/semanticcache/main.go, plugins/semanticcache/search.go, plugins/semanticcache/stream.go, plugins/semanticcache/utils.go, plugins/semanticcache/test_utils.go
Added Dimension and VectorStoreNamespace config fields with defaults/timeouts; replaced hard-coded class name usages with namespace; added parseStreamChunks and updated call sites; adjusted test embedding model and added Redis test config helper.
Docs navigation
docs/docs.json
Replaced nested Vector Store subgroup with direct reference to unified vector-store page.
Docs — Vector Store reference
docs/architecture/framework/vector-store.mdx
New unified Vector Store guide covering interface, Weaviate and Redis backends, setup, usage, and production notes.
Docs — Weaviate page removal
docs/architecture/framework/vector-store/weaviate.mdx
Removed standalone Weaviate page (content consolidated into the new unified page).
UI — semantic cache config
ui/app/config/views/pluginsForm.tsx, ui/lib/types/config.ts
Added numeric dimension field to cache config type and UI form; defaulted to 0 and persisted via existing update flow.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App
  participant Factory as NewVectorStore
  participant Redis as Redis Server
  participant RedisStore as RedisStore

  App->>Factory: NewVectorStore(ctx, {Type: "redis", Config: RedisConfig})
  Factory->>Factory: unmarshal -> RedisConfig
  Factory->>Redis: Dial + Ping (newRedisStore)
  Redis-->>Factory: Pong
  Factory-->>App: RedisStore instance

  App->>RedisStore: CreateNamespace(namespace, dimension, properties)
  RedisStore->>Redis: FT.CREATE (HASH + VECTOR field HNSW)
  Redis-->>RedisStore: OK
Loading
sequenceDiagram
  autonumber
  participant Client
  participant Plugin as SemanticCache
  participant Store as VectorStore

  Client->>Plugin: performSemanticSearch(req)
  Plugin->>Store: GetNearest(plugin.config.VectorStoreNamespace, vector, filters, threshold, limit)
  Store-->>Plugin: []SearchResult
  Plugin->>Plugin: parseStreamChunks(stream_chunks)
  Plugin-->>Client: semantic response (streaming if valid)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

A hop to Redis fields I go,
Keys and vectors in tidy row.
Namespaces bloom and searches sing,
Stream chunks parsed — the cache takes wing.
I twitch my nose — tests pass bright 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description explains the feature, core changes, testing for the Redis backend, and includes a checklist, but it is incomplete: it does not mark or describe UI and docs changes present in the diff, it lacks UI testing/build instructions and screenshots for UI edits, and it incorrectly declares "No" for Breaking changes despite public API/signature updates (e.g., VectorStore.CreateNamespace and related method signatures were changed to include a dimension parameter). Please update the PR description to (1) include UI and Docs in "Affected areas" and document what changed in those areas, (2) add UI build/test steps and before/after screenshots for the UI change, and (3) set "Breaking changes" to Yes and explicitly document migration notes (for example, callers must adapt to VectorStore.CreateNamespace now accepting a dimension int — show example callsites, recommended default behavior such as passing 0 to retain prior semantics, and any other public API changes), plus instructions to run the full test matrix (Go and UI) and validate the docs updates.
Docstring Coverage ⚠️ Warning Docstring coverage is 64.86% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat: redis as vector store" is concise, follows conventional commit-style prefix, and accurately summarizes the primary change in this PR (adding Redis as a vector store backend), so a reviewer scanning history can quickly understand the main purpose.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 09-02-feat_redis_as_vector_store

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Collaborator Author

TejasGhatte commented Sep 9, 2025

@TejasGhatte TejasGhatte marked this pull request as ready for review September 9, 2025 19:14
@coderabbitai coderabbitai Bot requested review from akshaydeo and danpiths September 9, 2025 19:16
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugins/semanticcache/test_helpers.go (1)

198-201: Bug: “RedisCluster” setup still uses Weaviate

Function name and comment say Redis Cluster, but it constructs a Weaviate store. Switch to Redis (and consider adding cluster-specific env later).

-	store, err := vectorstore.NewVectorStore(ctx, &vectorstore.Config{
-		Type:   vectorstore.VectorStoreTypeWeaviate,
-		Config: getWeaviateConfigFromEnv(),
-	}, logger)
+	store, err := vectorstore.NewVectorStore(ctx, &vectorstore.Config{
+		Type:   vectorstore.VectorStoreTypeRedis,
+		Config: getRedisConfigFromEnv(),
+	}, logger)
🧹 Nitpick comments (14)
framework/vectorstore/test_helpers..go (4)

30-36: Make embeddings deterministic to avoid test flakes

Use a dedicated, seeded RNG instead of the global rand. This stabilizes vector-similarity expectations in tests.

Apply:

@@
-import (
+import (
 	"math/rand"
 	"os"
 	"strconv"
 
 	"github.com/google/uuid"
 )
 
+// Seeded RNG for deterministic tests
+var testRand = rand.New(rand.NewSource(42))
+
@@
 func generateTestEmbedding(dim int) []float32 {
 	embedding := make([]float32, dim)
 	for i := range embedding {
-		embedding[i] = rand.Float32()*2 - 1 // Random values between -1 and 1
+		embedding[i] = testRand.Float32()*2 - 1 // Random values between -1 and 1
 	}
 	return embedding
 }

38-46: Clamp similarity and use deterministic noise

Prevent out-of-range inputs from increasing noise and reuse the seeded RNG for determinism.

 func generateSimilarEmbedding(original []float32, similarity float32) []float32 {
+	if similarity < 0 {
+		similarity = 0
+	} else if similarity > 1 {
+		similarity = 1
+	}
 	similar := make([]float32, len(original))
 	for i := range similar {
 		// Add small random noise to create similar but not identical embedding
-		noise := (rand.Float32()*2 - 1) * (1 - similarity) * 0.1
+		noise := (testRand.Float32()*2 - 1) * (1 - similarity) * 0.1
 		similar[i] = original[i] + noise
 	}
 	return similar
 }

19-24: Consider soft-fail on parse error

Returning an error for a bad env int forces callers to plumb/fail. For test helpers, falling back to defaultValue (with a log) typically yields more resilient tests.


1-1: Nit: filename has a double dot

Rename to test_helpers.go for consistency.

framework/go.mod (1)

68-68: go-redis should likely be a direct dependency

github.com/redis/go-redis/v9 is used by framework/vectorstore/redis.go; mark it direct and run go mod tidy to drop unused deps (e.g., Weaviate if fully migrated).

framework/vectorstore/store.go (2)

157-166: Accept both value and pointer configs in factory

Type assertions will fail if callers pass *RedisConfig. Handle both to reduce footguns.

-	case VectorStoreTypeRedis:
-		if config.Config == nil {
-			return nil, fmt.Errorf("redis config is required")
-		}
-		redisConfig, ok := config.Config.(RedisConfig)
-		if !ok {
-			return nil, fmt.Errorf("invalid redis config")
-		}
-		return newRedisStore(ctx, redisConfig, logger)
+	case VectorStoreTypeRedis:
+		if config.Config == nil {
+			return nil, fmt.Errorf("redis config is required")
+		}
+		var redisConfig RedisConfig
+		switch v := config.Config.(type) {
+		case RedisConfig:
+			redisConfig = v
+		case *RedisConfig:
+			redisConfig = *v
+		default:
+			return nil, fmt.Errorf("invalid redis config")
+		}
+		return newRedisStore(ctx, redisConfig, logger)

148-156: Mirror pointer/value handling for Weaviate path

For parity and future-proofing, accept both WeaviateConfig and *WeaviateConfig.

-	case VectorStoreTypeWeaviate:
-		if config.Config == nil {
-			return nil, fmt.Errorf("weaviate config is required")
-		}
-		weaviateConfig, ok := config.Config.(WeaviateConfig)
-		if !ok {
-			return nil, fmt.Errorf("invalid weaviate config")
-		}
-		return newWeaviateStore(ctx, &weaviateConfig, logger)
+	case VectorStoreTypeWeaviate:
+		if config.Config == nil {
+			return nil, fmt.Errorf("weaviate config is required")
+		}
+		var weaviateConfig WeaviateConfig
+		switch v := config.Config.(type) {
+		case WeaviateConfig:
+			weaviateConfig = v
+		case *WeaviateConfig:
+			weaviateConfig = *v
+		default:
+			return nil, fmt.Errorf("invalid weaviate config")
+		}
+		return newWeaviateStore(ctx, &weaviateConfig, logger)
plugins/semanticcache/test_helpers.go (1)

134-145: Use the created context instead of context.Background()

Minor consistency/readability improvement.

-	store, err := vectorstore.NewVectorStore(context.Background(), &vectorstore.Config{
+	store, err := vectorstore.NewVectorStore(ctx, &vectorstore.Config{
 		Type:    vectorstore.VectorStoreTypeRedis,
 		Config:  getRedisConfigFromEnv(),
 		Enabled: true,
 	}, logger)
framework/vectorstore/redis_test.go (3)

228-233: Close store in validation test to avoid leaks

Ensure resources are released for the “valid config” branch.

 			} else {
 				// For valid configs, store creation should succeed
 				// (connection will fail later when actually using Redis)
 				assert.NoError(t, err)
 				assert.NotNil(t, store)
+				if store != nil {
+					_ = store.Close(ctx, TestNamespace)
+				}
 			}

329-336: Drop heavy JSON logging in hot paths

Pretty-printing results in tests is noisy and slows test runs. Keep only on failures or behind t.Logf guarded by testing.Verbose().


380-387: Replace fixed sleeps with eventual polling

Use polling with a timeout to reduce flakiness and speed up tests on fast environments.

I can propose a small waitUntil helper if you want it added.

Also applies to: 457-466, 653-663, 719-726, 801-816

framework/vectorstore/redis.go (3)

580-605: TTL logic should handle all numeric types safely

The TTL extraction code handles multiple numeric types but could be simplified and made more robust using type assertion with ok pattern consistently.

 	// Set TTL if expires_at is provided in metadata
 	if expiresAtRaw, exists := metadata["expires_at"]; exists && expiresAtRaw != nil {
-		var expiresAt int64
-		var validType bool
-		switch v := expiresAtRaw.(type) {
-		case float64:
-			expiresAt = int64(v)
-			validType = true
-		case int64:
-			expiresAt = v
-			validType = true
-		case int:
-			expiresAt = int64(v)
-			validType = true
-		}
-		if validType {
+		expiresAt := extractInt64(expiresAtRaw)
+		if expiresAt > 0 {
 			currentTime := time.Now().Unix()
 			ttlSeconds := expiresAt - currentTime
 			if ttlSeconds > 0 {
 				// Set Redis TTL
 				if err := s.client.Expire(ctx, key, time.Duration(ttlSeconds)*time.Second).Err(); err != nil {
 					s.logger.Debug(fmt.Sprintf("Failed to set TTL for key %s: %v", key, err))
 				}
 			}
 		}
 	}

Add a helper function:

func extractInt64(v interface{}) int64 {
	switch val := v.(type) {
	case float64:
		return int64(val)
	case int64:
		return val
	case int:
		return int64(val)
	case float32:
		return int64(val)
	case int32:
		return int64(val)
	default:
		return 0
	}
}

516-516: Unnecessary RETURN clause in FT.SEARCH

The RETURN clause with "3", "id", "score", "*" appears to be ignored based on the comment on line 531 about fetching properties separately.

Since the properties are fetched separately anyway, consider simplifying:

-		"RETURN", "3", "id", "score", "*",
+		"RETURN", "1", "score",

846-863: Document and standardize score field handling: Add a comment in framework/vectorstore/redis.go explaining that __embedding_score is a legacy field (RediSearch dialect 1) and score is used in dialect 2, then remove the fallback or update tests/docs if the legacy key is no longer needed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 78341ef and 12a4848.

⛔ Files ignored due to path filters (1)
  • framework/go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • framework/go.mod (2 hunks)
  • framework/vectorstore/redis.go (1 hunks)
  • framework/vectorstore/redis_test.go (1 hunks)
  • framework/vectorstore/store.go (3 hunks)
  • framework/vectorstore/test_helpers..go (1 hunks)
  • framework/vectorstore/weaviate_test.go (0 hunks)
  • plugins/semanticcache/test_helpers.go (2 hunks)
💤 Files with no reviewable changes (1)
  • framework/vectorstore/weaviate_test.go
🧰 Additional context used
🧬 Code graph analysis (4)
framework/vectorstore/redis_test.go (5)
framework/vectorstore/redis.go (2)
  • RedisStore (37-41)
  • RedisConfig (17-34)
core/schemas/logger.go (2)
  • Logger (28-55)
  • LogLevelInfo (11-11)
framework/vectorstore/store.go (14)
  • Config (92-96)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeString (71-71)
  • VectorStorePropertyTypeInteger (72-72)
  • VectorStorePropertyTypeBoolean (73-73)
  • Query (20-24)
  • QueryOperatorEqual (29-29)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorLike (35-35)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • VectorStore (78-89)
  • VectorStoreTypeRedis (16-16)
  • NewVectorStore (138-168)
core/logger.go (1)
  • NewDefaultLogger (40-49)
framework/vectorstore/weaviate_test.go (1)
  • TestEmbeddingDim (21-21)
framework/vectorstore/redis.go (3)
core/schemas/logger.go (1)
  • Logger (28-55)
framework/vectorstore/store.go (16)
  • VectorStoreProperties (63-66)
  • SearchResult (43-47)
  • Query (20-24)
  • QueryOperatorIsNull (38-38)
  • QueryOperatorEqual (29-29)
  • QueryOperatorNotEqual (30-30)
  • QueryOperatorLike (35-35)
  • QueryOperatorIsNotNull (39-39)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorLessThanOrEqual (34-34)
  • QueryOperator (26-26)
  • DeleteResult (50-54)
  • DeleteStatusError (60-60)
  • DeleteStatusSuccess (59-59)
plugins/semanticcache/main.go (1)
  • VectorStoreProperties (150-187)
plugins/semanticcache/test_helpers.go (2)
framework/vectorstore/redis.go (1)
  • RedisConfig (17-34)
framework/vectorstore/store.go (2)
  • VectorStoreTypeRedis (16-16)
  • Config (92-96)
framework/vectorstore/store.go (1)
framework/vectorstore/redis.go (1)
  • RedisConfig (17-34)
🔇 Additional comments (3)
plugins/semanticcache/test_helpers.go (1)

138-142: LGTM: switch tests to Redis via factory

Using VectorStoreTypeRedis with getRedisConfigFromEnv() aligns semantic-cache tests with the new backend.

framework/vectorstore/redis.go (2)

358-363: IsNull operator logic may be incorrect for missing fields

When a field doesn't exist, the IsNull operator continues (line 360), but line 362 returns false for other operators. This means IsNull returns true for missing fields, which might be the intended behavior, but it's worth confirming.

Can you confirm that treating missing fields as null (returning true for IsNull) is the intended behavior? This differs from some databases where NULL and missing are distinct concepts.


1-991: LGTM! Comprehensive Redis vector store implementation

The implementation provides a well-structured Redis-backed vector store with proper error handling, context management, and batch operations for efficiency. The code integrates cleanly with the existing VectorStore interface and includes thoughtful features like TTL support and pipeline-based operations.

Comment thread framework/vectorstore/redis_test.go
Comment thread framework/vectorstore/redis_test.go Outdated
Comment thread framework/vectorstore/redis_test.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
@TejasGhatte TejasGhatte force-pushed the 09-02-feat_redis_as_vector_store branch from 12a4848 to e625742 Compare September 10, 2025 05:38
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (6)
framework/vectorstore/redis_test.go (1)

144-150: Good: skip when RediSearch/schema creation fails.

Switching to t.Skipf prevents cascading failures on environments without RediSearch.

framework/vectorstore/redis.go (5)

24-31: Consider removing or deprecating MaxActiveConns from RedisConfig.

Since go-redis doesn’t support it, keeping it is misleading. Either drop it or document it as ignored.


56-105: Index numeric fields as NUMERIC (not TEXT) for efficient comparisons.

All metadata fields are added as TEXT; numeric filters then require SCAN+client filtering. If properties include integers, map those to NUMERIC in FT schema.

   // Add all metadata fields as TEXT with exact matching
   // All values are converted to strings for consistent searching
   for _, field := range metadataFields {
-    args = append(args, field, "TEXT", "NOSTEM")
+    if prop, ok := properties[field]; ok && prop.DataType == VectorStorePropertyTypeInteger {
+      args = append(args, field, "NUMERIC")
+    } else if ok && prop.DataType == VectorStorePropertyTypeBoolean {
+      // Store booleans as TAGs for exact match
+      args = append(args, field, "TAG")
+    } else {
+      args = append(args, field, "TEXT", "NOSTEM")
+    }
   }

778-792: Close duplicates cleanup; delegate to DeleteNamespace.

Close currently only drops the index, leaving data behind. Delegate to DeleteNamespace for consistent teardown.

 func (s *RedisStore) Close(ctx context.Context, namespace string) error {
-  ctx, cancel := s.withTimeout(ctx)
-  defer cancel()
-
-  // Drop the index using FT.DROPINDEX
-  if err := s.client.Do(ctx, "FT.DROPINDEX", namespace).Err(); err != nil {
-    // Check if error is "Unknown Index name" - that's OK, index doesn't exist
-    if strings.Contains(err.Error(), "Unknown Index name") {
-      return nil // Index doesn't exist, nothing to drop
-    }
-    return fmt.Errorf("failed to drop semantic index %s: %w", namespace, err)
-  }
-
-  return nil
+  return s.DeleteNamespace(ctx, namespace)
 }

972-982: Compile error: redis.Options has no MaxActiveConns field.

go-redis/v9’s Options doesn’t define MaxActiveConns. This line won’t compile.

   client := redis.NewClient(&redis.Options{
     Addr:            config.Addr,
     Username:        config.Username,
     Password:        config.Password,
     DB:              config.DB,
     PoolSize:        config.PoolSize,
-    MaxActiveConns:  config.MaxActiveConns,
     MinIdleConns:    config.MinIdleConns,
     MaxIdleConns:    config.MaxIdleConns,
     ConnMaxLifetime: config.ConnMaxLifetime,
     ConnMaxIdleTime: config.ConnMaxIdleTime,
     DialTimeout:     config.DialTimeout,
     ReadTimeout:     config.ReadTimeout,
     WriteTimeout:    config.WriteTimeout,
   })

425-469: Fix incorrect fallback for numeric comparisons.

Falling back to string equality for >, >=, <, <= is wrong when parsing fails. Return false for ordering operators when values aren’t numeric.

   if err1 != nil || err2 != nil {
     // If parsing as float fails, try as int64
@@
-    if err1 != nil || err2 != nil {
-      // If both fail, fall back to string comparison
-      return propStr == queryStr
-    }
+    if err1 != nil || err2 != nil {
+      switch operator {
+      case QueryOperatorGreaterThan, QueryOperatorGreaterThanOrEqual,
+        QueryOperatorLessThan, QueryOperatorLessThanOrEqual:
+        return false
+      default:
+        return propStr == queryStr
+      }
+    }
🧹 Nitpick comments (7)
framework/vectorstore/store.go (1)

157-165: Accept both value and pointer Redis configs in NewVectorStore.

Today only the value-typed RedisConfig works. Supporting pointers avoids surprises when callers construct configs programmatically.

 case VectorStoreTypeRedis:
   if config.Config == nil {
     return nil, fmt.Errorf("redis config is required")
   }
-  redisConfig, ok := config.Config.(RedisConfig)
-  if !ok {
-    return nil, fmt.Errorf("invalid redis config")
-  }
-  return newRedisStore(ctx, redisConfig, logger)
+  var redisConfig RedisConfig
+  switch v := config.Config.(type) {
+  case RedisConfig:
+    redisConfig = v
+  case *RedisConfig:
+    if v == nil {
+      return nil, fmt.Errorf("invalid redis config: nil pointer")
+    }
+    redisConfig = *v
+  default:
+    return nil, fmt.Errorf("invalid redis config")
+  }
+  return newRedisStore(ctx, redisConfig, logger)
framework/vectorstore/redis_test.go (1)

175-237: Unit test currently requires a live Redis; clarify or gate.

newRedisStore pings Redis; “valid config” will fail without a running server. Either (a) mark this test as integration and Skip in short mode, or (b) refactor to validate config without connecting.

 func TestRedisConfig_Validation(t *testing.T) {
+  if testing.Short() {
+    t.Skip("Skipping config-connection validation in short mode")
+  }

If you prefer a pure unit test, extract a validateRedisConfig(config) error that doesn’t ping and test that instead.

framework/vectorstore/redis.go (5)

581-584: Use HSET instead of deprecated HMSet and pass fields variadically.

HMSet is a compatibility alias; prefer HSet for clarity.

-  if err := s.client.HMSet(ctx, key, fields).Err(); err != nil {
+  if err := s.client.HSet(ctx, key, fields).Err(); err != nil {
     return fmt.Errorf("failed to store semantic cache entry: %w", err)
   }

518-526: Add LIMIT to FT.SEARCH to align returned row count with KNN topK.

Without LIMIT, RediSearch defaults can cap results independently of KNN. Append LIMIT 0 .

   args := []interface{}{
     "FT.SEARCH", namespace,
     fmt.Sprintf("%s=>[KNN %d @embedding $vec AS score]", hybridQuery, limit),
     "PARAMS", "2", "vec", queryBytes,
     "SORTBY", "score",
     "RETURN", "3", "id", "score", "*",
     "DIALECT", "2",
+    "LIMIT", "0", fmt.Sprint(limit),
   }

360-423: Implement missing operators (ContainsAny/ContainsAll) or reject explicitly.

The VectorStore API defines ContainsAny/ContainsAll but matchesQueries doesn’t handle them; current default path devolves to string equality, which is wrong for these.

Minimal approach: explicitly return false with a TODO for these operators to avoid silent misbehavior. Alternatively, implement them for []string and comma-delimited string values.


271-277: Return nil cursor when no more pages.

Returning a non-nil pointer to an empty string forces callers to check string content. Prefer nil to signal completion.

-  // Set next cursor if there are more results
-  if nextCursor == "" && cursorValue != 0 {
-    nextCursor = strconv.FormatUint(cursorValue, 10)
-  }
-
-  return allResults, &nextCursor, nil
+  // Set next cursor if there are more results
+  if nextCursor == "" && cursorValue != 0 {
+    v := strconv.FormatUint(cursorValue, 10)
+    return allResults, &v, nil
+  }
+  return allResults, nil, nil

795-884: Be defensive in FT.SEARCH result parsing.

Relying on map[interface{}]interface{} with total_results/results may not hold across Redis/RediSearch versions and clients (many return []interface{} rows). Consider supporting the array-shaped response as a fallback.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 12a4848 and e625742.

⛔ Files ignored due to path filters (1)
  • framework/go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • framework/go.mod (2 hunks)
  • framework/vectorstore/redis.go (1 hunks)
  • framework/vectorstore/redis_test.go (1 hunks)
  • framework/vectorstore/store.go (3 hunks)
  • framework/vectorstore/test_helpers..go (1 hunks)
  • framework/vectorstore/weaviate_test.go (0 hunks)
  • plugins/semanticcache/test_helpers.go (2 hunks)
💤 Files with no reviewable changes (1)
  • framework/vectorstore/weaviate_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • framework/vectorstore/test_helpers..go
  • framework/go.mod
  • plugins/semanticcache/test_helpers.go
🧰 Additional context used
🧬 Code graph analysis (3)
framework/vectorstore/store.go (1)
framework/vectorstore/redis.go (1)
  • RedisConfig (17-38)
framework/vectorstore/redis_test.go (3)
framework/vectorstore/redis.go (2)
  • RedisStore (41-45)
  • RedisConfig (17-38)
core/schemas/logger.go (2)
  • Logger (28-55)
  • LogLevelInfo (11-11)
framework/vectorstore/store.go (14)
  • Config (92-96)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeString (71-71)
  • VectorStorePropertyTypeInteger (72-72)
  • VectorStorePropertyTypeBoolean (73-73)
  • Query (20-24)
  • QueryOperatorEqual (29-29)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorLike (35-35)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • VectorStore (78-89)
  • VectorStoreTypeRedis (16-16)
  • NewVectorStore (138-168)
framework/vectorstore/redis.go (3)
core/schemas/logger.go (1)
  • Logger (28-55)
framework/vectorstore/store.go (16)
  • VectorStoreProperties (63-66)
  • SearchResult (43-47)
  • Query (20-24)
  • QueryOperatorIsNull (38-38)
  • QueryOperatorEqual (29-29)
  • QueryOperatorNotEqual (30-30)
  • QueryOperatorLike (35-35)
  • QueryOperatorIsNotNull (39-39)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorLessThanOrEqual (34-34)
  • QueryOperator (26-26)
  • DeleteResult (50-54)
  • DeleteStatusError (60-60)
  • DeleteStatusSuccess (59-59)
plugins/semanticcache/main.go (1)
  • VectorStoreProperties (150-187)
🔇 Additional comments (3)
framework/vectorstore/store.go (2)

16-16: LGTM: Added Redis store type constant.

Constant definition is clear and consistent with existing types.


124-129: LGTM: UnmarshalJSON supports Redis config.

Happy with the pattern mirroring Weaviate. Error messages are precise.

framework/vectorstore/redis_test.go (1)

571-579: Relax flakiness risk acknowledgment.

Assertion targets only the top hit with a reasonable delta. Looks good.

Comment thread framework/vectorstore/redis_test.go
@TejasGhatte TejasGhatte force-pushed the 09-02-feat_redis_as_vector_store branch from e625742 to 04cca26 Compare September 10, 2025 07:12
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (4)
framework/vectorstore/redis_test.go (1)

144-149: Proper test resilience for missing RediSearch.

The updated error handling correctly skips tests when RediSearch is unavailable rather than continuing with cascading failures. This makes the tests more robust in different deployment environments.

framework/vectorstore/redis.go (3)

93-97: Consider using Redis schema types for better performance.

All metadata fields are currently stored as TEXT with NOSTEM, but this prevents efficient numeric operations. Consider detecting field types from the VectorStoreProperties and using appropriate Redis field types (NUMERIC for integers, etc.) to improve query performance.


426-434: Fix incorrect fallback for numeric comparison operators.

When numeric parsing fails, the code falls back to string equality comparison even for inequality operators like GreaterThan/LessThan, which is incorrect. Numeric ordering operations should return false when values can't be parsed as numbers.


773-787: Close method should delegate to DeleteNamespace.

The Close method only drops the FT index but doesn't clean up the associated data, unlike DeleteNamespace. For consistency and complete cleanup, Close should delegate to DeleteNamespace or clearly document why it only performs partial cleanup.

🧹 Nitpick comments (1)
framework/vectorstore/redis.go (1)

25-27: Consider consolidating pool configuration fields.

Both PoolSize and MaxActiveConns fields serve similar purposes in connection pool management. According to the Redis client documentation, PoolSize is the primary field for controlling pool size, while MaxActiveConns provides additional limiting. Consider documenting the distinction or consolidating if they serve overlapping purposes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e625742 and 04cca26.

⛔ Files ignored due to path filters (1)
  • framework/go.sum is excluded by !**/*.sum
📒 Files selected for processing (8)
  • framework/go.mod (2 hunks)
  • framework/vectorstore/redis.go (1 hunks)
  • framework/vectorstore/redis_test.go (1 hunks)
  • framework/vectorstore/store.go (3 hunks)
  • framework/vectorstore/test_helpers..go (1 hunks)
  • framework/vectorstore/weaviate_test.go (0 hunks)
  • plugins/semanticcache/search.go (3 hunks)
  • plugins/semanticcache/test_helpers.go (2 hunks)
💤 Files with no reviewable changes (1)
  • framework/vectorstore/weaviate_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • framework/go.mod
  • framework/vectorstore/store.go
🧰 Additional context used
🧬 Code graph analysis (4)
plugins/semanticcache/search.go (1)
plugins/semanticcache/main.go (1)
  • Plugin (128-134)
framework/vectorstore/redis.go (2)
core/schemas/logger.go (1)
  • Logger (28-55)
framework/vectorstore/store.go (16)
  • VectorStoreProperties (63-66)
  • SearchResult (43-47)
  • Query (20-24)
  • QueryOperatorIsNull (38-38)
  • QueryOperatorEqual (29-29)
  • QueryOperatorNotEqual (30-30)
  • QueryOperatorLike (35-35)
  • QueryOperatorIsNotNull (39-39)
  • QueryOperator (26-26)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorLessThanOrEqual (34-34)
  • DeleteResult (50-54)
  • DeleteStatusError (60-60)
  • DeleteStatusSuccess (59-59)
plugins/semanticcache/test_helpers.go (2)
framework/vectorstore/redis.go (1)
  • RedisConfig (17-38)
framework/vectorstore/store.go (2)
  • VectorStoreTypeRedis (16-16)
  • Config (92-96)
framework/vectorstore/redis_test.go (4)
framework/vectorstore/redis.go (2)
  • RedisStore (41-45)
  • RedisConfig (17-38)
core/schemas/logger.go (2)
  • Logger (28-55)
  • LogLevelInfo (11-11)
framework/vectorstore/store.go (14)
  • Config (92-96)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeString (71-71)
  • VectorStorePropertyTypeInteger (72-72)
  • VectorStorePropertyTypeBoolean (73-73)
  • Query (20-24)
  • QueryOperatorEqual (29-29)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorLike (35-35)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • VectorStore (78-89)
  • VectorStoreTypeRedis (16-16)
  • NewVectorStore (138-168)
core/logger.go (1)
  • NewDefaultLogger (40-49)
🔇 Additional comments (11)
plugins/semanticcache/search.go (3)

205-210: LGTM! Improved stream_chunks parsing logic.

The refactored parsing logic using the new parseStreamChunks helper method improves robustness by handling different data formats and properly validates the parsed result before determining streaming validity.


272-276: Good error handling improvement.

The error message now clearly indicates that parsing failed rather than using the generic type assertion error, which makes debugging easier.


346-378: Well-designed helper function for stream data normalization.

The parseStreamChunks function effectively handles the different formats that stream data can arrive in from various vector store backends (Redis JSON strings, native arrays, etc.). The type switch covers all expected scenarios and provides clear error messages.

framework/vectorstore/test_helpers..go (1)

1-47: Well-structured test helper utilities.

The helper functions provide clean environment variable handling and embedding generation utilities that are essential for testing vector store functionality. The functions follow Go conventions with unexported names and proper error handling.

plugins/semanticcache/test_helpers.go (2)

45-88: Clean Redis configuration helper.

The getRedisConfigFromEnv function properly handles environment variable parsing with sensible defaults and error handling for invalid values. The timeout parsing using time.ParseDuration is particularly well done.


148-151: Proper migration to Redis backend.

The switch from Weaviate to Redis in the test setup is correctly implemented, using the new Redis configuration helper and the appropriate vector store type constant.

framework/vectorstore/redis_test.go (3)

16-23: Good test constants definition.

The constants provide clear default values and proper timeout settings for Redis testing. The dimension constant aligns with common embedding model outputs.


365-378: Fixed array indexing issue.

The embedding array now properly matches the number of keys, preventing the out-of-range panic that was previously possible. Good fix!


578-580: Improved score assertion reliability.

Only checking the top result's score for exact match (~1.0) is much more reliable than asserting all results have perfect scores, which was prone to flakiness.

framework/vectorstore/redis.go (2)

76-79: Good configurable embedding dimension.

The implementation now properly uses the configured dimension from RedisConfig with a sensible fallback to 1536. This addresses the previous hard-coded dimension issue and supports different embedding model dimensions.


963-977: Redis client configuration looks correct.

Based on the latest go-redis documentation, both PoolSize and MaxActiveConns are valid fields in redis.Options. The configuration properly maps all the Redis config fields to the client options.

@TejasGhatte TejasGhatte force-pushed the 09-02-feat_redis_as_vector_store branch 2 times, most recently from 7644ea4 to 6f0396e Compare September 10, 2025 07:34
@TejasGhatte TejasGhatte force-pushed the 09-02-feat_redis_as_vector_store branch from 6f0396e to 6ce25c3 Compare September 10, 2025 09:35
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (9)
docs/architecture/framework/vector-store/redis.mdx (2)

273-287: Connection pool configuration example exceeds Redis client field limits.

The Redis client's redis.Options struct doesn't have a MaxActiveConns field based on the code implementation. Only PoolSize is used.

 config := vectorstore.RedisConfig{
     Addr:            "localhost:6379",
     PoolSize:        20,                    // Max socket connections
-    MaxActiveConns:  20,                    // Max active connections
     MinIdleConns:    5,                     // Min idle connections
     MaxIdleConns:    10,                    // Max idle connections
     ConnMaxLifetime: 30 * time.Minute,      // Connection lifetime
     ConnMaxIdleTime: 5 * time.Minute,       // Idle connection timeout
     DialTimeout:     5 * time.Second,       // Connection timeout
     ReadTimeout:     3 * time.Second,       // Read timeout
     WriteTimeout:    3 * time.Second,       // Write timeout
     ContextTimeout:  10 * time.Second,      // Operation timeout
 }

295-308: Remove MaxActiveConns from JSON configuration examples.

The JSON configuration example still references max_active_conns which isn't mapped correctly in the Redis client.

 {
   "vector_store": {
     "config": {
       "pool_size": 50,           // Increase for high concurrency
-      "max_active_conns": 50,    // Match pool_size
       "min_idle_conns": 10,      // Keep connections warm
       "max_idle_conns": 20,      // Allow some idle connections
       "conn_max_lifetime": "1h", // Refresh connections periodically
       "conn_max_idle_time": "10m" // Close idle connections
     }
   }
 }
framework/vectorstore/redis.go (3)

781-785: LGTM! Close method properly delegates to DeleteNamespace.

The Close method now correctly delegates to DeleteNamespace, ensuring both index and data cleanup as previously suggested.


17-38: Remove MaxActiveConns field or document it clearly.

The MaxActiveConns field in RedisConfig doesn't map to any field in redis.Options. The Redis Go client only uses PoolSize for connection pooling.

You have two options:

Option 1: Remove the field entirely (recommended)

 type RedisConfig struct {
 	// Connection settings
 	Addr     string `json:"addr"`               // Redis server address (host:port) - REQUIRED
 	Username string `json:"username,omitempty"` // Username for Redis AUTH (optional)
 	Password string `json:"password,omitempty"` // Password for Redis AUTH (optional)
 	DB       int    `json:"db,omitempty"`       // Redis database number (default: 0)
 
 	// Connection pool and timeout settings (passed directly to Redis client)
 	PoolSize        int           `json:"pool_size,omitempty"`          // Maximum number of socket connections (optional)
-	MaxActiveConns  int           `json:"max_active_conns,omitempty"`   // Maximum number of active connections (optional)
 	MinIdleConns    int           `json:"min_idle_conns,omitempty"`     // Minimum number of idle connections (optional)
 	MaxIdleConns    int           `json:"max_idle_conns,omitempty"`     // Maximum number of idle connections (optional)

Option 2: Map both fields to PoolSize for backward compatibility
If you need to keep the field for backward compatibility, update the comment to clarify it's an alias for PoolSize.


961-975: Remove MaxActiveConns from Redis client options.

The redis.Options struct doesn't have a MaxActiveConns field. This will cause a compilation error.

 	client := redis.NewClient(&redis.Options{
 		Addr:            config.Addr,
 		Username:        config.Username,
 		Password:        config.Password,
 		DB:              config.DB,
 		PoolSize:        config.PoolSize,
-		MaxActiveConns:  config.MaxActiveConns,
 		MinIdleConns:    config.MinIdleConns,
 		MaxIdleConns:    config.MaxIdleConns,
 		ConnMaxLifetime: config.ConnMaxLifetime,
 		ConnMaxIdleTime: config.ConnMaxIdleTime,
 		DialTimeout:     config.DialTimeout,
 		ReadTimeout:     config.ReadTimeout,
 		WriteTimeout:    config.WriteTimeout,
 	})
framework/vectorstore/redis_test.go (4)

16-22: LGTM! Test constants are well-defined.

The test constants including RedisTestDimension are properly defined, addressing the previous compilation issue.


143-148: LGTM! Proper test skipping when RediSearch is unavailable.

The test now correctly uses t.Skipf when namespace creation fails, avoiding cascading failures.


358-375: LGTM! Batch retrieval test properly handles embedding array.

The test correctly creates an embeddings array with the same length as keys (including a nil entry for metadata-only items), fixing the out-of-range panic issue.


563-574: LGTM! Vector search score assertions are appropriately relaxed.

The test now only asserts that the top result has a score close to 1.0, which is more realistic for similarity search results.

🧹 Nitpick comments (4)
docs/architecture/framework/vector-store/redis.mdx (1)

38-40: Consider using Go type aliases for clarity.

Instead of importing both packages with the same name, consider using type aliases or renaming one of the imports for clarity.

-import (
-    "context"
-    "github.com/maximhq/bifrost/framework/vectorstore"
-    "github.com/maximhq/bifrost/core/schemas"
-)
+import (
+    "context"
+    bifrostVectorStore "github.com/maximhq/bifrost/framework/vectorstore"
+    "github.com/maximhq/bifrost/core/schemas"
+)
framework/vectorstore/redis.go (2)

417-430: Numeric comparison implementation has incomplete operator support.

The compareNumbers function is called for numeric operators but doesn't handle QueryOperatorContainsAny and QueryOperatorContainsAll that are defined in the interface. Consider documenting which operators are supported or implementing all of them.

Add a comment documenting supported operators:

 // matchesQueries checks if the properties match the given queries
+// Supported operators: Equal, NotEqual, Like, IsNull, IsNotNull, 
+// GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual
+// Unsupported operators will fall back to string equality comparison
 func (s *RedisStore) matchesQueries(properties map[string]interface{}, queries []Query) bool {

433-472: Consider simplifying numeric comparison logic.

The compareNumbers function has nested parsing with redundant comparison logic. The float parsing already handles integers, so the integer parsing fallback may be unnecessary.

 func (s *RedisStore) compareNumbers(propStr, queryStr string, operator QueryOperator) bool {
 	// Try to parse as float64 first (handles both integers and floats)
 	propVal, err1 := strconv.ParseFloat(propStr, 64)
 	queryVal, err2 := strconv.ParseFloat(queryStr, 64)
 	if err1 != nil || err2 != nil {
-		// If parsing as float fails, try as int64
-		propInt, err1 := strconv.ParseInt(propStr, 10, 64)
-		queryInt, err2 := strconv.ParseInt(queryStr, 10, 64)
-		if err1 != nil || err2 != nil {
-			// If both fail, fall back to string comparison
-			return propStr == queryStr
-		}
-		// Use integer comparison
-		switch operator {
-		case QueryOperatorGreaterThan:
-			return propInt > queryInt
-		case QueryOperatorGreaterThanOrEqual:
-			return propInt >= queryInt
-		case QueryOperatorLessThan:
-			return propInt < queryInt
-		case QueryOperatorLessThanOrEqual:
-			return propInt <= queryInt
-		default:
-			return propInt == queryInt
-		}
+		// If numeric parsing fails, return false for numeric operators
+		return false
 	}
 	// Use float comparison
 	switch operator {
framework/vectorstore/redis_test.go (1)

830-836: Consider adding a dimension validation test.

The test creates a RedisConfig with Dimension but doesn't verify that the dimension is properly used when creating the namespace/index.

Add a test to verify dimension handling:

t.Run("Dimension configuration", func(t *testing.T) {
    // Create a store with a specific dimension
    customConfig := RedisConfig{
        Addr:      getEnvWithDefault("REDIS_ADDR", DefaultTestAddr),
        Dimension: 768, // Different from default
    }
    
    customStore, err := newRedisStore(context.Background(), customConfig, logger)
    require.NoError(t, err)
    defer customStore.Close(context.Background(), "TestDimension")
    
    // Create namespace and verify it accepts embeddings of the configured dimension
    err = customStore.CreateNamespace(context.Background(), "TestDimension", map[string]VectorStoreProperties{})
    require.NoError(t, err)
    
    // Add with correct dimension should succeed
    embedding768 := generateTestEmbedding(768)
    err = customStore.Add(context.Background(), "TestDimension", "test1", embedding768, nil)
    assert.NoError(t, err)
})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6f0396e and 6ce25c3.

⛔ Files ignored due to path filters (1)
  • framework/go.sum is excluded by !**/*.sum
📒 Files selected for processing (9)
  • docs/architecture/framework/vector-store/redis.mdx (1 hunks)
  • framework/go.mod (2 hunks)
  • framework/vectorstore/redis.go (1 hunks)
  • framework/vectorstore/redis_test.go (1 hunks)
  • framework/vectorstore/store.go (3 hunks)
  • framework/vectorstore/test_helpers..go (1 hunks)
  • framework/vectorstore/weaviate_test.go (0 hunks)
  • plugins/semanticcache/search.go (3 hunks)
  • plugins/semanticcache/test_helpers.go (2 hunks)
💤 Files with no reviewable changes (1)
  • framework/vectorstore/weaviate_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • plugins/semanticcache/search.go
  • framework/go.mod
  • plugins/semanticcache/test_helpers.go
  • framework/vectorstore/test_helpers..go
🧰 Additional context used
🧬 Code graph analysis (3)
framework/vectorstore/store.go (1)
framework/vectorstore/redis.go (1)
  • RedisConfig (17-38)
framework/vectorstore/redis_test.go (4)
framework/vectorstore/redis.go (2)
  • RedisStore (41-45)
  • RedisConfig (17-38)
core/schemas/logger.go (2)
  • Logger (28-55)
  • LogLevelInfo (11-11)
framework/vectorstore/store.go (14)
  • Config (92-96)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeString (71-71)
  • VectorStorePropertyTypeInteger (72-72)
  • VectorStorePropertyTypeBoolean (73-73)
  • Query (20-24)
  • QueryOperatorEqual (29-29)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorLike (35-35)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • VectorStore (78-89)
  • VectorStoreTypeRedis (16-16)
  • NewVectorStore (138-168)
core/logger.go (1)
  • NewDefaultLogger (40-49)
framework/vectorstore/redis.go (3)
core/schemas/logger.go (1)
  • Logger (28-55)
framework/vectorstore/store.go (17)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeInteger (72-72)
  • SearchResult (43-47)
  • Query (20-24)
  • QueryOperatorIsNull (38-38)
  • QueryOperatorEqual (29-29)
  • QueryOperatorNotEqual (30-30)
  • QueryOperatorLike (35-35)
  • QueryOperatorIsNotNull (39-39)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorLessThanOrEqual (34-34)
  • QueryOperator (26-26)
  • DeleteResult (50-54)
  • DeleteStatusError (60-60)
  • DeleteStatusSuccess (59-59)
plugins/semanticcache/main.go (1)
  • VectorStoreProperties (150-187)
🔇 Additional comments (5)
framework/vectorstore/store.go (3)

16-16: LGTM! Clean integration of Redis as a new vector store type.

The addition of VectorStoreTypeRedis constant follows the existing pattern and properly extends the vector store types enum.


124-129: LGTM! Redis config unmarshaling follows the established pattern.

The Redis configuration unmarshaling is correctly implemented with proper error handling, consistent with the existing Weaviate pattern.


157-165: LGTM! Redis store factory integration is properly implemented.

The Redis store creation follows the same validation and error handling pattern as Weaviate, maintaining consistency in the codebase.

framework/vectorstore/redis.go (2)

56-112: LGTM! Namespace creation with proper schema handling.

The implementation correctly handles both string and integer field types when creating the Redis FT index, with sensible defaults for the embedding dimension.


590-614: TTL handling implementation looks good.

The TTL implementation correctly extracts expires_at from metadata and sets Redis expiration time. Good defensive programming with type checking.

@akshaydeo
Copy link
Copy Markdown
Contributor

Lets get namespace as a parameter here to help users manage the vector store data properly

@TejasGhatte TejasGhatte force-pushed the 09-02-feat_redis_as_vector_store branch from 6ce25c3 to a85a794 Compare September 10, 2025 14:37
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (12)
plugins/semanticcache/main.go (4)

34-34: Expose VectorStoreNamespace but validate/sanitize it

Add lightweight validation (allowed chars) to avoid backend-specific name errors (e.g., Redis index/prefix constraints) and document accepted format.

You can validate during Init:

@@
 type Config struct {
@@
- VectorStoreNamespace string `json:"vector_store_namespace,omitempty"` // Namespace for vector store (optional)
+ VectorStoreNamespace string `json:"vector_store_namespace,omitempty"` // Namespace for vector store (optional; [A-Za-z0-9:_-])

And in Init (see lines 260-262 comment below) perform a regex check before use.


53-53: Support a JSON alias 'namespace' for ergonomics

Many users expect "namespace" in config. Accept an alias to reduce friction.

 type TempConfig struct {
@@
-  VectorStoreNamespace         string        `json:"vector_store_namespace,omitempty"`
+  VectorStoreNamespace         string        `json:"vector_store_namespace,omitempty"`
+  Namespace                    string        `json:"namespace,omitempty"`

71-71: Prefer alias fallback when vector_store_namespace is unset

If only "namespace" is provided, use it.

- c.VectorStoreNamespace = temp.VectorStoreNamespace
+ if temp.VectorStoreNamespace != "" {
+   c.VectorStoreNamespace = temp.VectorStoreNamespace
+ } else if temp.Namespace != "" {
+   c.VectorStoreNamespace = temp.Namespace
+ }

260-262: Trim and default namespace; optionally enforce format

Small hardening to avoid accidental spaces and invalid names.

@@ func Init(ctx context.Context, config Config, logger schemas.Logger, store vectorstore.VectorStore) (schemas.Plugin, error) {
- if config.VectorStoreNamespace == "" {
-   config.VectorStoreNamespace = DefaultVectorStoreNamespace
- }
+ // Normalize before defaulting
+ config.VectorStoreNamespace = strings.TrimSpace(config.VectorStoreNamespace)
+ if config.VectorStoreNamespace == "" {
+   config.VectorStoreNamespace = DefaultVectorStoreNamespace
+ }
+ // Optional: enforce allowed chars
+ // if ok := regexp.MustCompile(`^[A-Za-z0-9:_-]+$`).MatchString(config.VectorStoreNamespace); !ok {
+ //   return nil, fmt.Errorf("invalid vector_store_namespace: %q (allowed: [A-Za-z0-9:_-])", config.VectorStoreNamespace)
+ // }

Add imports:

@@
-import (
+import (
   "context"
   "encoding/json"
   "fmt"
   "strconv"
   "sync"
   "time"
+  "strings"
+  // "regexp" // if you enable format enforcement
 )
framework/vectorstore/store.go (2)

124-129: Minor: normalize pointer vs value semantics across backends

Here c.Config stores a value RedisConfig, while Weaviate’s path later passes a pointer. Consider standardizing (either both values or both pointers) to reduce surprises and copies. Not urgent.


157-166: Make factory robust to programmatic (map) configs

If Config is constructed in code (not via JSON), config.Config may be a map[string]any or *RedisConfig. Add a small normalization to support these without forcing callers to pre-cast.

Apply:

 case VectorStoreTypeRedis:
   if config.Config == nil {
     return nil, fmt.Errorf("redis config is required")
   }
-  redisConfig, ok := config.Config.(RedisConfig)
-  if !ok {
-    return nil, fmt.Errorf("invalid redis config")
-  }
-  return newRedisStore(ctx, redisConfig, logger)
+  switch v := config.Config.(type) {
+  case RedisConfig:
+    return newRedisStore(ctx, v, logger)
+  case *RedisConfig:
+    return newRedisStore(ctx, *v, logger)
+  case map[string]any, json.RawMessage:
+    b, _ := json.Marshal(v)
+    var rc RedisConfig
+    if err := json.Unmarshal(b, &rc); err != nil {
+      return nil, fmt.Errorf("invalid redis config: %w", err)
+    }
+    return newRedisStore(ctx, rc, logger)
+  default:
+    return nil, fmt.Errorf("invalid redis config")
+  }
framework/vectorstore/redis_test.go (6)

34-34: Mark helper for clearer failure locations

Add t.Helper() to NewRedisTestSetup.

 func NewRedisTestSetup(t *testing.T) *RedisTestSetup {
+  t.Helper()

151-168: Cleanup should page through all results (avoid 1000-item cap)

If >1000 objects accumulate, current cleanup leaves leftovers. Iterate with cursor.

 func (ts *RedisTestSetup) cleanupTestData(t *testing.T) {
-  // Delete all objects in the test namespace
-  allTestKeys, _, err := ts.Store.GetAll(ts.ctx, TestNamespace, []Query{}, []string{}, nil, 1000)
-  if err != nil {
-    t.Logf("Warning: Failed to get all test keys: %v", err)
-    return
-  }
-
-  for _, key := range allTestKeys {
-    err := ts.Store.Delete(ts.ctx, TestNamespace, key.ID)
-    if err != nil {
-      t.Logf("Warning: Failed to delete test key %s: %v", key.ID, err)
-    }
-  }
+  var cursor *string
+  for {
+    page, next, err := ts.Store.GetAll(ts.ctx, TestNamespace, nil, nil, cursor, 500)
+    if err != nil {
+      t.Logf("Warning: Failed to get test keys: %v", err)
+      break
+    }
+    for _, key := range page {
+      if err := ts.Store.Delete(ts.ctx, TestNamespace, key.ID); err != nil {
+        t.Logf("Warning: Failed to delete test key %s: %v", key.ID, err)
+      }
+    }
+    if next == nil {
+      break
+    }
+    cursor = next
+  }

385-388: Avoid assuming GetChunks result order

Unless the contract guarantees order, assert by ID.

-    // Verify each result
-    for i, result := range results {
-      assert.Equal(t, keys[i], result.ID)
-      assert.Equal(t, metadata[i]["type"], result.Properties["type"])
-    }
+    // Verify by ID (order-agnostic)
+    resByID := make(map[string]SearchResult, len(results))
+    for _, r := range results {
+      resByID[r.ID] = r
+    }
+    for i, key := range keys {
+      r, ok := resByID[key]
+      require.True(t, ok, "missing result for key %s", key)
+      assert.Equal(t, metadata[i]["type"], r.Properties["type"])
+    }

329-331: Reduce flakiness: replace fixed sleeps with Eventually

Indexing can be async; poll instead of sleeping.

require.Eventually(t, func() bool {
  _, err := setup.Store.GetChunk(setup.ctx, TestNamespace, testKey)
  return err == nil
}, 5*time.Second, 50*time.Millisecond)

571-574: Assert top hit identity; relax score delta

Validates correctness without overfitting to exact similarity value.

   require.NotEmpty(t, results)
   require.NotNil(t, results[0].Score)
-  assert.InDelta(t, 1.0, *results[0].Score, 1e-4)
+  assert.Equal(t, testDocs[0].key, results[0].ID)
+  assert.InDelta(t, 1.0, *results[0].Score, 1e-3)

596-600: Add minimal assertions for high-threshold search

Ensure at least the self-match returns.

   results, err := setup.Store.GetNearest(setup.ctx, TestNamespace, queryEmbedding, nil, []string{"type", "category", "content"}, 0.99, 10)
   require.NoError(t, err)
-  // Should return fewer results due to high threshold
-  t.Logf("High threshold search returned %d results", len(results))
+  require.NotEmpty(t, results)
+  assert.Equal(t, testDocs[0].key, results[0].ID)
+  t.Logf("High threshold search returned %d results", len(results))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6ce25c3 and a85a794.

⛔ Files ignored due to path filters (1)
  • framework/go.sum is excluded by !**/*.sum
📒 Files selected for processing (12)
  • docs/architecture/framework/vector-store/redis.mdx (1 hunks)
  • framework/go.mod (2 hunks)
  • framework/vectorstore/redis.go (1 hunks)
  • framework/vectorstore/redis_test.go (1 hunks)
  • framework/vectorstore/store.go (3 hunks)
  • framework/vectorstore/test_helpers..go (1 hunks)
  • framework/vectorstore/weaviate_test.go (0 hunks)
  • plugins/semanticcache/main.go (10 hunks)
  • plugins/semanticcache/search.go (6 hunks)
  • plugins/semanticcache/stream.go (1 hunks)
  • plugins/semanticcache/test_helpers.go (2 hunks)
  • plugins/semanticcache/utils.go (1 hunks)
💤 Files with no reviewable changes (1)
  • framework/vectorstore/weaviate_test.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • framework/go.mod
  • docs/architecture/framework/vector-store/redis.mdx
  • plugins/semanticcache/test_helpers.go
  • framework/vectorstore/redis.go
  • plugins/semanticcache/search.go
  • framework/vectorstore/test_helpers..go
🧰 Additional context used
🧬 Code graph analysis (3)
framework/vectorstore/store.go (1)
framework/vectorstore/redis.go (1)
  • RedisConfig (17-38)
plugins/semanticcache/main.go (1)
framework/vectorstore/store.go (1)
  • VectorStoreProperties (63-66)
framework/vectorstore/redis_test.go (4)
framework/vectorstore/redis.go (2)
  • RedisStore (41-45)
  • RedisConfig (17-38)
core/schemas/logger.go (2)
  • Logger (28-55)
  • LogLevelInfo (11-11)
framework/vectorstore/store.go (6)
  • Config (92-96)
  • VectorStoreProperties (63-66)
  • Query (20-24)
  • VectorStore (78-89)
  • VectorStoreTypeRedis (16-16)
  • NewVectorStore (138-168)
core/logger.go (1)
  • NewDefaultLogger (40-49)
🔇 Additional comments (8)
plugins/semanticcache/utils.go (1)

308-308: Switching to configurable namespace is correct

Using plugin.config.VectorStoreNamespace here aligns storage with the new config-driven namespace. No action needed.

plugins/semanticcache/stream.go (1)

123-123: Consistent namespace usage for streaming final Add

Good alignment with the configurable namespace on the final store.Add call.

plugins/semanticcache/main.go (3)

307-307: Ensure CreateNamespace is idempotent

If the namespace already exists, this should not fail Init. Confirm RedisStore.CreateNamespace returns a typed ErrNamespaceExists (or equivalent) that you can ignore; otherwise, make the call idempotent.

Would you like me to wire a guard to ignore a known “already exists” error once you confirm the error type?


616-616: Cleanup DeleteAll scoped by plugin flag looks good

Filtering by from_bifrost_semantic_cache_plugin avoids cross-tenant deletion. LGTM.


34-34: VectorStoreClassName removal verified
Ran searches for VectorStoreClassName|vector_store_class_name|vectorStoreClassName and found no matches; VectorStoreNamespace is consistently referenced in the plugins/semanticcache plugin.

framework/vectorstore/store.go (1)

16-16: Redis store type wiring looks good

Constant addition is straightforward and consistent with existing types.

framework/vectorstore/redis_test.go (2)

827-849: Factory smoke test looks good

Covers the type path and Close. Fine as-is.


868-871: Verify Delete semantics for non-existent keys across stores

  • Check that RedisStore.Delete (framework/vectorstore/redis.go) and WeaviateStore.Delete (framework/vectorstore/weaviate.go) both return an error when the target ID doesn’t exist.
  • Align implementations or tests so callers see the same behavior.

Comment thread framework/vectorstore/redis_test.go
Comment thread plugins/semanticcache/main.go
Comment thread plugins/semanticcache/main.go Outdated
Comment thread plugins/semanticcache/main.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/test_helpers..go
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go Outdated
Comment thread plugins/semanticcache/main.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
plugins/semanticcache/stream.go (1)

89-95: Fix sort comparator: nil chunks are currently moved to the front (breaks ordering).

The comparator returns true when i.Response is nil and j.Response is non‑nil, pushing nils to the front, contrary to the comment and expected behavior.

Apply this diff to push nils to the end deterministically:

-  sort.SliceStable(accumulator.Chunks, func(i, j int) bool {
-    if accumulator.Chunks[i].Response == nil || accumulator.Chunks[j].Response == nil {
-      // Push nils to the end deterministically
-      return accumulator.Chunks[j].Response != nil
-    }
-    return accumulator.Chunks[i].Response.ExtraFields.ChunkIndex < accumulator.Chunks[j].Response.ExtraFields.ChunkIndex
-  })
+  sort.SliceStable(accumulator.Chunks, func(i, j int) bool {
+    ri := accumulator.Chunks[i].Response
+    rj := accumulator.Chunks[j].Response
+    // Push nils to the end deterministically
+    if ri == nil && rj == nil {
+      return false
+    }
+    if ri == nil {
+      return false
+    }
+    if rj == nil {
+      return true
+    }
+    return ri.ExtraFields.ChunkIndex < rj.ExtraFields.ChunkIndex
+  })
plugins/semanticcache/test_utils.go (1)

185-212: Function name vs. implementation mismatch (Weaviate used in “RedisCluster” setup).

NewRedisClusterTestSetup initializes a Weaviate store, not Redis. Either rename the function or switch to VectorStoreTypeRedis and use getRedisConfigFromEnv().

Apply:

- store, err := vectorstore.NewVectorStore(ctx, &vectorstore.Config{
-   Type:   vectorstore.VectorStoreTypeWeaviate,
-   Config: getWeaviateConfigFromEnv(),
- }, logger)
+ store, err := vectorstore.NewVectorStore(ctx, &vectorstore.Config{
+   Type:   vectorstore.VectorStoreTypeRedis,
+   Config: getRedisConfigFromEnv(),
+ }, logger)
♻️ Duplicate comments (1)
framework/vectorstore/redis.go (1)

872-881: Remove MaxActiveConns from redis.Options; map legacy MaxActiveConns → PoolSize

go-redis/v9's redis.Options has no MaxActiveConns field. Replace the Options entry with a compatibility mapping that prefers config.PoolSize and falls back to config.MaxActiveConns, and update docs/examples.

- client := redis.NewClient(&redis.Options{
+ // Optional compatibility mapping: prefer PoolSize, fall back to MaxActiveConns for legacy config
+ poolSize := config.PoolSize
+ if poolSize == 0 && config.MaxActiveConns > 0 {
+   poolSize = config.MaxActiveConns
+ }
+ client := redis.NewClient(&redis.Options{
    Addr:            config.Addr,
    Username:        config.Username,
    Password:        config.Password,
    DB:              config.DB,
    Protocol:        3, // Explicitly use RESP3 protocol
-   PoolSize:        config.PoolSize,
-   MaxActiveConns:  config.MaxActiveConns,
+   PoolSize:        poolSize,
    MinIdleConns:    config.MinIdleConns,
    MaxIdleConns:    config.MaxIdleConns,
    ConnMaxLifetime: config.ConnMaxLifetime,
    ConnMaxIdleTime: config.ConnMaxIdleTime,
    DialTimeout:     config.DialTimeout,
    ReadTimeout:     config.ReadTimeout,
    WriteTimeout:    config.WriteTimeout,
  })

Files to update: framework/vectorstore/redis.go (options literal around lines ~871–874; config struct field remains at ~line 31 if you want backward compatibility) and docs/architecture/framework/vector-store.mdx (remove or deprecate example uses of MaxActiveConns).

🧹 Nitpick comments (10)
plugins/semanticcache/stream.go (1)

115-121: Config‑driven namespace is good; guard empty, propagate expiry, and log the namespace.

Validate namespace isn’t empty, include TTL/expiry in metadata (for stores without native TTL), and log ns for traceability. Also confirm Add is upsert‑safe for repeated requestIDs.

Apply this diff:

   finalMetadata["stream_chunks"] = streamResponses
+  // Carry expiry info for stores that don't support native TTL
+  if accumulator.TTL > 0 {
+    finalMetadata["expires_at"] = time.Now().Add(accumulator.TTL).UTC().Format(time.RFC3339)
+  }
 
   // Store complete unified entry using original requestID - this is the final .Add() call
-  if err := plugin.store.Add(ctx, plugin.config.VectorStoreNamespace, requestID, accumulator.Embedding, finalMetadata); err != nil {
+  ns := plugin.config.VectorStoreNamespace
+  if ns == "" {
+    return fmt.Errorf("VectorStoreNamespace cannot be empty")
+  }
+  if err := plugin.store.Add(ctx, ns, requestID, accumulator.Embedding, finalMetadata); err != nil {
     return fmt.Errorf("failed to store complete streaming cache entry: %w", err)
   }
 
-  plugin.logger.Debug(fmt.Sprintf("%s Successfully cached complete stream with %d ordered chunks, ID: %s", PluginLoggerPrefix, len(streamResponses), requestID))
+  plugin.logger.Debug(fmt.Sprintf("%s Successfully cached complete stream with %d ordered chunks, ns=%s, ID=%s", PluginLoggerPrefix, len(streamResponses), ns, requestID))

Also applies to: 123-123, 127-127

plugins/semanticcache/test_utils.go (2)

45-88: Unused helper: consider moving or using getRedisConfigFromEnv.

This function isn’t referenced in this package. Either wire it into Redis-backed tests or move it to framework/vectorstore/test_utils.go to avoid dead code.


314-325: Duplicate success log.

“Response correctly served from cache” is logged twice. Remove one to reduce noise.

Apply:

-    t.Log("✅ Response correctly served from cache")
-  }
-
-  t.Log("✅ Response correctly served from cache")
+    t.Log("✅ Response correctly served from cache")
+  }
framework/vectorstore/test_utils.go (3)

19-24: Simplify API: unnecessary error return.

getEnvWithDefaultInt never returns a non-nil error on the default path. Consider returning just int and documenting that invalid values fall back to default.


30-36: Determinism for tests.

generateTestEmbedding uses math/rand without a seed; results vary run-to-run. For stable tests, accept a *rand.Rand or seed locally with a fixed source.

Apply (one option):

-func generateTestEmbedding(dim int) []float32 {
-  embedding := make([]float32, dim)
-  for i := range embedding {
-    embedding[i] = rand.Float32()*2 - 1
-  }
-  return embedding
-}
+func generateTestEmbedding(dim int) []float32 {
+  r := rand.New(rand.NewSource(1))
+  embedding := make([]float32, dim)
+  for i := range embedding {
+    embedding[i] = r.Float32()*2 - 1
+  }
+  return embedding
+}

38-46: Clarify parameter semantics.

Add a brief comment that similarity is in [0,1], where 1 means nearly identical (minimal noise) and 0 allows maximal noise.

framework/vectorstore/redis.go (4)

45-50: Add Redis Cluster support with UniversalClient.

Using *redis.Client precludes Cluster. Switch to redis.UniversalClient and instantiate ClusterClient when multiple addresses are provided.

Apply:

 type RedisStore struct {
-  client *redis.Client
+  client redis.UniversalClient
   config RedisConfig
   logger schemas.Logger
 }
@@
-func newRedisStore(ctx context.Context, config RedisConfig, logger schemas.Logger) (*RedisStore, error) {
+func newRedisStore(ctx context.Context, config RedisConfig, logger schemas.Logger) (*RedisStore, error) {
   if config.Addr == "" {
     return nil, fmt.Errorf("redis addr is required")
   }
-
-  client := redis.NewClient(&redis.Options{
-    Addr:            config.Addr,
-    Username:        config.Username,
-    Password:        config.Password,
-    DB:              config.DB,
-    Protocol:        3,
-    PoolSize:        poolSize,
-    MinIdleConns:    config.MinIdleConns,
-    MaxIdleConns:    config.MaxIdleConns,
-    ConnMaxLifetime: config.ConnMaxLifetime,
-    ConnMaxIdleTime: config.ConnMaxIdleTime,
-    DialTimeout:     config.DialTimeout,
-    ReadTimeout:     config.ReadTimeout,
-    WriteTimeout:    config.WriteTimeout,
-  })
+  addrs := strings.Split(config.Addr, ",")
+  var client redis.UniversalClient
+  if len(addrs) > 1 {
+    client = redis.NewClusterClient(&redis.ClusterOptions{
+      Addrs:           addrs,
+      Username:        config.Username,
+      Password:        config.Password,
+      Protocol:        3,
+      PoolSize:        poolSize,
+      MinIdleConns:    config.MinIdleConns,
+      MaxIdleConns:    config.MaxIdleConns,
+      ConnMaxLifetime: config.ConnMaxLifetime,
+      ConnMaxIdleTime: config.ConnMaxIdleTime,
+      DialTimeout:     config.DialTimeout,
+      ReadTimeout:     config.ReadTimeout,
+      WriteTimeout:    config.WriteTimeout,
+    })
+  } else {
+    client = redis.NewClient(&redis.Options{
+      Addr:            config.Addr,
+      Username:        config.Username,
+      Password:        config.Password,
+      DB:              config.DB,
+      Protocol:        3,
+      PoolSize:        poolSize,
+      MinIdleConns:    config.MinIdleConns,
+      MaxIdleConns:    config.MaxIdleConns,
+      ConnMaxLifetime: config.ConnMaxLifetime,
+      ConnMaxIdleTime: config.ConnMaxIdleTime,
+      DialTimeout:     config.DialTimeout,
+      ReadTimeout:     config.ReadTimeout,
+      WriteTimeout:    config.WriteTimeout,
+    })
+  }

Also applies to: 859-897


111-145: Avoid returning raw binary embedding in GetChunk(s).

HGetAll returns the binary “embedding” field; propagating it into Properties bloats responses and logs. Skip it unless explicitly requested.

Apply (both methods):

- for k, v := range fields {
-   searchResult.Properties[k] = v
- }
+ for k, v := range fields {
+   if k == "embedding" { // omit large binary blob
+     continue
+   }
+   searchResult.Properties[k] = v
+ }

Also applies to: 147-208


287-373: RESP3-only parsing: acceptable with Protocol:3, add a note.

parseSearchResults assumes RESP3 map shape; since you pin Protocol:3, that’s fine. Add a brief comment here referencing the client Protocol setting.


814-843: Document intentional comma→OR behavior in TAG escaping.

Since commas are rewritten to “|” to match any comma-separated token, add a comment explaining this nonstandard equality semantics.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cadf64 and f8f10ca.

⛔ Files ignored due to path filters (1)
  • framework/go.sum is excluded by !**/*.sum
📒 Files selected for processing (15)
  • docs/architecture/framework/vector-store.mdx (1 hunks)
  • docs/architecture/framework/vector-store/weaviate.mdx (0 hunks)
  • docs/docs.json (1 hunks)
  • framework/go.mod (2 hunks)
  • framework/vectorstore/redis.go (1 hunks)
  • framework/vectorstore/redis_test.go (1 hunks)
  • framework/vectorstore/store.go (3 hunks)
  • framework/vectorstore/test_utils.go (1 hunks)
  • framework/vectorstore/utils.go (1 hunks)
  • framework/vectorstore/weaviate_test.go (0 hunks)
  • plugins/semanticcache/main.go (10 hunks)
  • plugins/semanticcache/search.go (8 hunks)
  • plugins/semanticcache/stream.go (1 hunks)
  • plugins/semanticcache/test_utils.go (6 hunks)
  • plugins/semanticcache/utils.go (1 hunks)
💤 Files with no reviewable changes (2)
  • framework/vectorstore/weaviate_test.go
  • docs/architecture/framework/vector-store/weaviate.mdx
🚧 Files skipped from review as they are similar to previous changes (9)
  • plugins/semanticcache/utils.go
  • framework/go.mod
  • docs/docs.json
  • framework/vectorstore/utils.go
  • plugins/semanticcache/main.go
  • docs/architecture/framework/vector-store.mdx
  • framework/vectorstore/store.go
  • plugins/semanticcache/search.go
  • framework/vectorstore/redis_test.go
🧰 Additional context used
🧬 Code graph analysis (2)
plugins/semanticcache/test_utils.go (1)
framework/vectorstore/redis.go (1)
  • RedisConfig (22-43)
framework/vectorstore/redis.go (2)
core/schemas/logger.go (1)
  • Logger (28-55)
framework/vectorstore/store.go (18)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeInteger (72-72)
  • SearchResult (43-47)
  • Query (20-24)
  • QueryOperatorEqual (29-29)
  • QueryOperatorNotEqual (30-30)
  • QueryOperatorLike (35-35)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorLessThanOrEqual (34-34)
  • QueryOperatorIsNull (38-38)
  • QueryOperatorIsNotNull (39-39)
  • QueryOperatorContainsAny (36-36)
  • QueryOperatorContainsAll (37-37)
  • DeleteResult (50-54)
  • DeleteStatusError (60-60)
  • DeleteStatusSuccess (59-59)
🔇 Additional comments (7)
plugins/semanticcache/test_utils.go (1)

129-141: Switch to text-embedding-3-small looks good.

Lower cost model for tests is appropriate and should reduce flakiness due to rate limits.

Also applies to: 193-205, 396-408, 419-431, 442-455

framework/vectorstore/test_utils.go (1)

26-28: LGTM.

UUID generation is straightforward and appropriate for test IDs.

framework/vectorstore/redis.go (5)

71-109: Index creation and RediSearch presence checks look solid.

Dimension validation and FT.INFO unknown-command handling are good safeguards.


210-285: Good: LIMIT, RETURN handling, and cursor pagination.

The LIMIT clause and offset-based cursor resolve earlier paging issues and make DeleteAll robust.


466-573: KNN flow and similarity conversion look correct.

Including AS score, sorting, thresholding, and returning similarity via 1-distance reads well.


586-624: Metadata encoding is safe for arrays/objects.

JSON-encoding for []interface{} avoids CSV pitfalls and preserves structure for round-trips.


755-812: Consistent cleanup via Close → DeleteNamespace.

Good consolidation; this prevents orphaned data or indexes.

Comment thread framework/vectorstore/redis.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
plugins/semanticcache/main.go (1)

264-271: Add debug log for dimension defaulting.

The namespace defaulting includes a debug log, but the dimension defaulting is missing one. Consider adding a debug log for consistency.

 if config.Dimension == 0 {
-	logger.Debug(PluginLoggerPrefix + " Dimension is not set, using default of 0")
+	logger.Debug(PluginLoggerPrefix + " Dimension is not set, will be determined by vector store")
 	config.Dimension = 0
 }

Note: Setting dimension to 0 might not be the best default. Consider either requiring this field or defaulting to a common value like 1536 for OpenAI embeddings.

framework/vectorstore/redis.go (2)

22-40: Remove or consolidate MaxActiveConns field.

The MaxActiveConns field duplicates the functionality of PoolSize. The Redis Go client uses PoolSize to control the maximum number of connections.

 	// Connection pool and timeout settings (passed directly to Redis client)
 	PoolSize        int           `json:"pool_size,omitempty"`          // Maximum number of socket connections (optional)
-	MaxActiveConns  int           `json:"max_active_conns,omitempty"`   // Maximum number of active connections (optional)
 	MinIdleConns    int           `json:"min_idle_conns,omitempty"`     // Minimum number of idle connections (optional)

Also remove the corresponding field from line 863 in the Redis client options.


830-830: Document the intentional comma-to-OR conversion.

The conversion of commas to OR operators in TAG queries is intentional for supporting comma-separated values. Consider adding a comment to clarify this design choice.

 		"~", "\\~",
 		"`", "\\`",
 		"\"", "\\\"",
 		"'", "\\'",
 		" ", "\\ ",
 		"-", "\\-",
+		// Intentionally convert commas to OR for "contains any" semantics on comma-separated values
 		",", "|",
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8f10ca and 6237854.

⛔ Files ignored due to path filters (1)
  • framework/go.sum is excluded by !**/*.sum
📒 Files selected for processing (17)
  • docs/architecture/framework/vector-store.mdx (1 hunks)
  • docs/architecture/framework/vector-store/weaviate.mdx (0 hunks)
  • docs/docs.json (1 hunks)
  • docs/features/semantic-caching.mdx (1 hunks)
  • framework/go.mod (2 hunks)
  • framework/vectorstore/redis.go (1 hunks)
  • framework/vectorstore/redis_test.go (1 hunks)
  • framework/vectorstore/store.go (4 hunks)
  • framework/vectorstore/test_utils.go (1 hunks)
  • framework/vectorstore/utils.go (1 hunks)
  • framework/vectorstore/weaviate.go (2 hunks)
  • framework/vectorstore/weaviate_test.go (1 hunks)
  • plugins/semanticcache/main.go (10 hunks)
  • plugins/semanticcache/search.go (8 hunks)
  • plugins/semanticcache/stream.go (1 hunks)
  • plugins/semanticcache/test_utils.go (6 hunks)
  • plugins/semanticcache/utils.go (1 hunks)
💤 Files with no reviewable changes (1)
  • docs/architecture/framework/vector-store/weaviate.mdx
🚧 Files skipped from review as they are similar to previous changes (8)
  • plugins/semanticcache/utils.go
  • framework/vectorstore/test_utils.go
  • framework/go.mod
  • docs/architecture/framework/vector-store.mdx
  • docs/docs.json
  • plugins/semanticcache/stream.go
  • framework/vectorstore/utils.go
  • plugins/semanticcache/test_utils.go
🧰 Additional context used
🧬 Code graph analysis (7)
framework/vectorstore/weaviate.go (2)
framework/vectorstore/store.go (1)
  • VectorStoreProperties (63-66)
plugins/semanticcache/main.go (1)
  • VectorStoreProperties (157-194)
framework/vectorstore/store.go (2)
plugins/semanticcache/main.go (1)
  • Config (25-42)
framework/vectorstore/redis.go (1)
  • RedisConfig (22-40)
plugins/semanticcache/main.go (1)
framework/vectorstore/store.go (1)
  • VectorStoreProperties (63-66)
framework/vectorstore/redis_test.go (4)
framework/vectorstore/redis.go (2)
  • RedisStore (43-47)
  • RedisConfig (22-40)
core/schemas/logger.go (1)
  • Logger (28-55)
framework/vectorstore/store.go (12)
  • Config (92-96)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeString (71-71)
  • VectorStorePropertyTypeInteger (72-72)
  • VectorStorePropertyTypeBoolean (73-73)
  • Query (20-24)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorEqual (29-29)
  • QueryOperatorLessThan (32-32)
  • VectorStore (78-89)
  • VectorStoreTypeRedis (16-16)
  • NewVectorStore (138-168)
core/logger.go (1)
  • NewDefaultLogger (40-49)
plugins/semanticcache/search.go (1)
plugins/semanticcache/main.go (1)
  • Plugin (134-140)
framework/vectorstore/weaviate_test.go (2)
plugins/semanticcache/test_utils.go (1)
  • NewTestSetup (115-132)
framework/vectorstore/store.go (2)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeString (71-71)
framework/vectorstore/redis.go (3)
core/schemas/logger.go (1)
  • Logger (28-55)
framework/vectorstore/store.go (18)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeInteger (72-72)
  • SearchResult (43-47)
  • Query (20-24)
  • QueryOperatorEqual (29-29)
  • QueryOperatorNotEqual (30-30)
  • QueryOperatorLike (35-35)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorLessThanOrEqual (34-34)
  • QueryOperatorIsNull (38-38)
  • QueryOperatorIsNotNull (39-39)
  • QueryOperatorContainsAny (36-36)
  • QueryOperatorContainsAll (37-37)
  • DeleteResult (50-54)
  • DeleteStatusError (60-60)
  • DeleteStatusSuccess (59-59)
plugins/semanticcache/main.go (1)
  • VectorStoreProperties (157-194)
🔇 Additional comments (33)
docs/features/semantic-caching.mdx (1)

114-114: LGTM!

The addition of the Dimension field to the semantic cache configuration is well-integrated and properly documented. The example value of 1536 is appropriate for the OpenAI text-embedding-3-small model mentioned in the configuration.

framework/vectorstore/weaviate.go (1)

451-508: LGTM! Dimension-aware namespace creation.

The implementation correctly handles the dimension parameter for vector indexing. When dimension > 0, it properly configures the VectorIndexConfig with the specified dimension, allowing Weaviate to optimize the vector index for the expected embedding size.

framework/vectorstore/store.go (4)

16-16: LGTM! Redis vector store type constant added.

The new constant follows the existing naming convention and correctly identifies the Redis store type.


79-79: LGTM! Interface updated to support dimension-aware namespaces.

The addition of the dimension parameter to CreateNamespace is a necessary change that enables both Redis and Weaviate backends to properly configure their vector indices with the correct embedding dimension.


124-129: LGTM! Redis configuration unmarshalling added.

The JSON unmarshalling logic correctly handles the Redis configuration case, following the same pattern as Weaviate.


157-165: LGTM! Redis store factory integration.

The Redis store creation logic properly validates the configuration and delegates to newRedisStore, maintaining consistency with the Weaviate pattern.

framework/vectorstore/weaviate_test.go (1)

742-814: LGTM! Comprehensive dimension handling test.

The test thoroughly validates that namespaces can be recreated with different embedding dimensions without crashes. It properly tests:

  1. Creating a namespace with dimension 512 and adding/verifying data
  2. Deleting the namespace
  3. Recreating with dimension 1024 and verifying vector search functionality

This ensures dimension changes are handled gracefully.

plugins/semanticcache/main.go (6)

30-30: LGTM! Dimension field added to config.

The addition of the Dimension field enables proper configuration of vector store namespaces with the expected embedding dimension.


33-35: LGTM! VectorStoreNamespace field added for namespace management.

The addition of VectorStoreNamespace allows users to configure custom namespaces for their vector stores, providing better isolation and organization of cache data.


145-148: LGTM! Constants updated for namespace management.

The replacement of VectorStoreClassName with DefaultVectorStoreNamespace and addition of CreateNamespaceTimeout provide better defaults and timeout management for namespace operations.


316-320: LGTM! Namespace creation with proper timeout.

The namespace creation correctly uses the configured namespace name, dimension, and a dedicated timeout for this potentially long-running operation.


627-641: LGTM! Proper namespace cleanup with timeout.

The cleanup properly deletes all cache entries and the namespace itself, with appropriate timeout handling. The documentation clearly states that the namespace is temporary and should not be used for external data.


671-674: LGTM! Cache clearing methods use proper timeout.

Both ClearCacheForKey and ClearCacheForRequestID correctly use timeouts and the configured namespace for their operations.

plugins/semanticcache/search.go (6)

8-8: LGTM! Added strconv import for parsing.

The import is necessary for the new string-to-int parsing logic for expires_at values.


62-62: LGTM! Namespace configuration properly applied.

Both performDirectSearch and performSemanticSearch now correctly use the configured namespace from plugin.config.VectorStoreNamespace instead of a hardcoded value.

Also applies to: 139-139


170-177: LGTM! Robust expires_at parsing.

The code now handles expires_at values that might be stored as strings (from Redis TAG fields), properly parsing them to int64 before comparing with the current time.


195-195: LGTM! Delete operation uses configured namespace.

The expired entry deletion correctly uses the configured namespace.


214-218: Stream chunks parsing handles Redis storage format.

The new approach using parseStreamChunks properly handles the Redis TAG storage format where arrays might be stored as JSON strings.


355-387: LGTM! Comprehensive stream chunks parsing.

The parseStreamChunks function handles multiple formats gracefully:

  • Direct []interface{} (from Weaviate)
  • []string arrays
  • JSON-encoded string arrays (from Redis)

This ensures compatibility across different vector store backends.

framework/vectorstore/redis_test.go (4)

143-147: LGTM! Proper test skipping for missing RediSearch.

The test correctly skips when RediSearch is unavailable rather than continuing with cascading failures.


293-297: LGTM! Fixed embedding array bounds issue.

The test now correctly provides three embeddings (including a nil for metadata-only) matching the three keys, preventing index out of bounds errors.


504-506: LGTM! Relaxed score assertion for vector search.

The test now correctly asserts only that the top result has a score close to 1.0, which is more robust than asserting all results have perfect scores.


820-890: LGTM! Comprehensive namespace dimension handling test.

The test thoroughly validates that Redis namespaces can be recreated with different dimensions, properly testing both 512 and 1024-dimensional embeddings.

framework/vectorstore/redis.go (10)

18-20: LGTM! Well-defined batch limit constant.

The constant provides a reasonable default for batch operations and pagination.


58-60: LGTM! Good error handling for missing RediSearch.

The code provides a clear, actionable error message when RediSearch is not available.


71-73: LGTM! Proper dimension validation.

The code validates that dimension > 0 before creating the index, preventing FT.CREATE failures.


91-97: LGTM! Proper field type detection.

The code correctly maps VectorStorePropertyTypeInteger to NUMERIC fields in Redis for proper numeric comparisons, while defaulting to TAG for text fields.


233-237: LGTM! Proper handling of limit=0 for "get all".

The code correctly interprets limit=0 as "get all results" using MaxInt32, and applies proper limits in both GetAll and GetNearest methods.

Also applies to: 511-515


285-368: LGTM! RESP3 protocol parsing.

The parseSearchResults function correctly handles RESP3 map format from FT.SEARCH. The code explicitly sets Protocol: 3 in the client options (line 861) ensuring consistent response format.


597-602: LGTM! Arrays preserved as JSON.

The code correctly preserves array structures by JSON-encoding them, which is essential for round-trip compatibility with fields like stream_chunks.


646-743: LGTM! Efficient cursor-based batch deletion.

The DeleteAll implementation efficiently handles large datasets using cursor-based pagination and batch processing with pipelines.


798-801: LGTM! Close delegates to DeleteNamespace.

The Close method now properly delegates to DeleteNamespace, ensuring consistent cleanup of both index and data.


861-861: LGTM! RESP3 protocol explicitly set.

Setting Protocol: 3 ensures consistent RESP3 response format, which is required by the parseSearchResults implementation.

@TejasGhatte TejasGhatte force-pushed the 09-02-feat_redis_as_vector_store branch from 6237854 to 6f87f7c Compare September 15, 2025 13:46
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/docs.json (1)

149-156: vector-store page exists; fix broken nav targets in docs/docs.json

vector-store.mdx found; no stale weaviate references. The nav sanity check reports these missing files referenced from docs/docs.json:

  • docs/Gateway.mdx
  • docs/server.mdx
  • docs/Use as Go SDK.mdx
  • docs/code.mdx
  • docs/Plugins.mdx
  • docs/puzzle-piece.mdx
  • docs/Core Architecture.mdx
  • docs/sitemap.mdx
  • docs/Framework.mdx
  • docs/screwdriver-wrench.mdx

Add the missing files under docs/ or update docs/docs.json to point to the correct paths so the nav entries resolve.

♻️ Duplicate comments (1)
framework/vectorstore/redis_test.go (1)

142-147: Don’t skip when namespace already exists

Treat “already exists” as success; only skip for real failures (e.g., missing RediSearch). This avoids cascading skips. (This was flagged earlier; appears still unresolved.)

-err := ts.Store.CreateNamespace(ts.ctx, TestNamespace, RedisTestDimension, properties)
-if err != nil {
-  t.Skipf("RediSearch not available or schema creation failed for namespace %q: %v", TestNamespace, err)
-} else {
-  t.Logf("Created test namespace: %s", TestNamespace)
-}
+err := ts.Store.CreateNamespace(ts.ctx, TestNamespace, RedisTestDimension, properties)
+if err != nil {
+  if strings.Contains(strings.ToLower(err.Error()), "already exists") {
+    t.Logf("Namespace %q already exists; continuing", TestNamespace)
+    return
+  }
+  t.Skipf("RediSearch not available or schema creation failed for namespace %q: %v", TestNamespace, err)
+}
+t.Logf("Created test namespace: %s", TestNamespace)
🧹 Nitpick comments (10)
docs/docs.json (2)

149-156: Add redirects for moved/removed docs to avoid 404s.

If the old Weaviate-specific page was public, add a redirect to preserve inbound links.

Proposed addition to docs/docs.json (root-level “redirects” key):

{
  "redirects": [
    { "from": "/architecture/framework/vector-store/weaviate", "to": "/architecture/framework/vector-store" }
  ]
}

If an existing “redirects” array is present, append to it instead.


149-156: Ensure the new page documents the Redis namespace parameter.

A reviewer requested adding a namespace param for RedisStore; confirm the unified page covers how to set and use it so users don’t mix indices across environments.

If needed, I can draft a short “Namespaces” subsection for the page—want me to?

ui/lib/types/config.ts (1)

227-227: Dimension sentinel semantics need to be explicit (or make it optional).

You’ve added dimension: number as required, while the UI defaults to 0 and allows sending 0. If 0 means “unspecified/let backend infer,” please document this in the type to avoid misuse; if some stores (e.g., Redis) require a positive dimension, consider marking this field optional and letting defaults be handled backend-side.

Apply this small inline doc to clarify intent:

 export interface CacheConfig {
   provider: ModelProviderName;
   keys: ModelProviderKey[];
   embedding_model: string;
-  dimension: number;
+  /**
+   * Vector dimension.
+   * 0 = let backend infer / ignore (where supported).
+   * Positive integer required for stores that mandate dimensions (e.g., Redis).
+   */
+  dimension: number; // consider `?: number` if backend treats it as optional
   ttl_seconds: number;
ui/app/config/views/pluginsForm.tsx (2)

21-21: Defaulting to 0 is fine, but call it out to reduce misconfiguration.

If 0 is a sentinel for “infer,” add a brief comment here (and/or helper text next to the input) so users don’t enable semantic cache with an invalid dimension for backends that require it.

 const defaultCacheConfig: CacheConfig = {
   provider: "openai" as ModelProviderName,
   keys: [],
   embedding_model: "text-embedding-3-small",
-  dimension: 0,
+  // 0 = backend infer/ignore. For Redis, set an explicit positive dimension.
+  dimension: 0,
   ttl_seconds: 300,

282-291: Align input constraints with the sentinel value and guard negatives.

The input uses min="1" but the code falls back to 0. This mismatch can push an invalid value for stores that accept only positive dimensions. Either allow 0 explicitly or clamp to >=1. Given current semantics, prefer allowing 0 and guarding negatives.

   <div className="space-y-2">
     <Label htmlFor="dimension">Dimension</Label>
     <Input
       id="dimension"
       type="number"
-      min="1"
+      min="0"
+      step="1"
       value={cacheConfig.dimension}
-      onChange={(e) => debouncedUpdateCacheConfig({ dimension: parseInt(e.target.value) || 0 })}
+      onChange={(e) => {
+        const parsed = parseInt(e.target.value, 10);
+        const dimension = Number.isFinite(parsed) ? Math.max(0, parsed) : 0;
+        debouncedUpdateCacheConfig({ dimension });
+      }}
     />
+    <p className="text-muted-foreground text-xs">
+      Use 0 to let the backend infer the dimension (if supported). For Redis, set the exact dimension.
+    </p>
   </div>
plugins/semanticcache/test_utils.go (1)

45-79: Unused Redis test helper — remove or move to shared test utils

getRedisConfigFromEnv is package‑private and not referenced in this file. Either delete it or relocate/export it in a shared test utils module (e.g., framework/vectorstore/test_utils.go) to avoid dead code and duplication.

framework/vectorstore/weaviate_test.go (1)

742-814: Reduce flakiness: wait briefly after Add before reads

Weaviate can be eventually consistent for fresh writes. Add a short sleep before GetChunk and GetNearest in this test.

@@
-    err = setup.Store.Add(setup.ctx, testClassName, testKey512, embedding512, metadata)
+    err = setup.Store.Add(setup.ctx, testClassName, testKey512, embedding512, metadata)
     require.NoError(t, err)
 
-    // Verify it was added
+    // Small delay for consistency
+    time.Sleep(100 * time.Millisecond)
+    // Verify it was added
@@
-    err = setup.Store.Add(setup.ctx, testClassName, testKey1024, embedding1024, metadata1024)
+    err = setup.Store.Add(setup.ctx, testClassName, testKey1024, embedding1024, metadata1024)
     require.NoError(t, err)
 
-    // Verify new document exists
+    // Small delay for consistency
+    time.Sleep(100 * time.Millisecond)
+    // Verify new document exists
framework/vectorstore/redis_test.go (3)

3-13: Import strings to detect “already exists” without skipping

Needed for the CreateNamespace error check below.

 import (
   "context"
   "os"
   "testing"
   "time"
+  "strings"
 
   bifrost "github.com/maximhq/bifrost/core"

317-321: Avoid relying on GetChunks result ordering

Order may not be guaranteed. Validate by ID map instead.

-    for i, result := range results {
-      assert.Equal(t, keys[i], result.ID)
-      assert.Equal(t, metadata[i]["type"], result.Properties["type"])
-    }
+    byID := map[string]map[string]interface{}{}
+    for i, k := range keys {
+      byID[k] = metadata[i]
+    }
+    for _, result := range results {
+      assert.Contains(t, byID, result.ID)
+      assert.Equal(t, byID[result.ID]["type"], result.Properties["type"])
+    }

850-854: Reduce flakiness: add short sleeps after Add before reads

Align with other tests that wait briefly after writes.

@@
-    // Verify it was added
+    // Small delay to ensure consistency
+    time.Sleep(100 * time.Millisecond)
+    // Verify it was added
@@
-    // Verify new document exists
+    // Small delay to ensure consistency
+    time.Sleep(100 * time.Millisecond)
+    // Verify new document exists

Also applies to: 874-877

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6237854 and 6f87f7c.

⛔ Files ignored due to path filters (1)
  • framework/go.sum is excluded by !**/*.sum
📒 Files selected for processing (19)
  • docs/architecture/framework/vector-store.mdx (1 hunks)
  • docs/architecture/framework/vector-store/weaviate.mdx (0 hunks)
  • docs/docs.json (1 hunks)
  • docs/features/semantic-caching.mdx (1 hunks)
  • framework/go.mod (2 hunks)
  • framework/vectorstore/redis.go (1 hunks)
  • framework/vectorstore/redis_test.go (1 hunks)
  • framework/vectorstore/store.go (4 hunks)
  • framework/vectorstore/test_utils.go (1 hunks)
  • framework/vectorstore/utils.go (1 hunks)
  • framework/vectorstore/weaviate.go (2 hunks)
  • framework/vectorstore/weaviate_test.go (1 hunks)
  • plugins/semanticcache/main.go (10 hunks)
  • plugins/semanticcache/search.go (8 hunks)
  • plugins/semanticcache/stream.go (1 hunks)
  • plugins/semanticcache/test_utils.go (6 hunks)
  • plugins/semanticcache/utils.go (1 hunks)
  • ui/app/config/views/pluginsForm.tsx (2 hunks)
  • ui/lib/types/config.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • docs/architecture/framework/vector-store/weaviate.mdx
🚧 Files skipped from review as they are similar to previous changes (12)
  • framework/vectorstore/test_utils.go
  • framework/vectorstore/store.go
  • plugins/semanticcache/utils.go
  • framework/vectorstore/utils.go
  • plugins/semanticcache/main.go
  • plugins/semanticcache/stream.go
  • framework/go.mod
  • docs/features/semantic-caching.mdx
  • plugins/semanticcache/search.go
  • framework/vectorstore/redis.go
  • docs/architecture/framework/vector-store.mdx
  • framework/vectorstore/weaviate.go
🧰 Additional context used
🧬 Code graph analysis (4)
framework/vectorstore/weaviate_test.go (2)
plugins/semanticcache/test_utils.go (1)
  • NewTestSetup (115-132)
framework/vectorstore/store.go (2)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeString (71-71)
ui/app/config/views/pluginsForm.tsx (2)
ui/components/ui/label.tsx (1)
  • Label (21-21)
ui/components/ui/input.tsx (1)
  • Input (7-21)
plugins/semanticcache/test_utils.go (1)
framework/vectorstore/redis.go (1)
  • RedisConfig (22-40)
framework/vectorstore/redis_test.go (4)
framework/vectorstore/redis.go (2)
  • RedisStore (43-47)
  • RedisConfig (22-40)
core/schemas/logger.go (2)
  • Logger (28-55)
  • LogLevelInfo (11-11)
framework/vectorstore/store.go (12)
  • Config (92-96)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeString (71-71)
  • VectorStorePropertyTypeInteger (72-72)
  • VectorStorePropertyTypeBoolean (73-73)
  • Query (20-24)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorEqual (29-29)
  • QueryOperatorLessThan (32-32)
  • VectorStore (78-89)
  • VectorStoreTypeRedis (16-16)
  • NewVectorStore (138-168)
core/logger.go (1)
  • NewDefaultLogger (40-49)

Comment thread framework/vectorstore/redis_test.go
Comment thread plugins/semanticcache/test_utils.go
@TejasGhatte TejasGhatte force-pushed the 09-02-feat_redis_as_vector_store branch 2 times, most recently from 32580c0 to 637b4d1 Compare September 15, 2025 14:05
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
ui/app/config/views/pluginsForm.tsx (2)

271-280: Threshold handler blocks setting 0 due to “|| 0.8”. Clamp instead.

parseFloat(...) || 0.8 treats 0 as falsy, preventing users from choosing a valid 0. Use valueAsNumber and clamp to [0,1].

-  onChange={(e) => debouncedUpdateCacheConfig({ threshold: parseFloat(e.target.value) || 0.8 })}
+  onChange={(e) => {
+    const v = e.currentTarget.valueAsNumber;
+    debouncedUpdateCacheConfig({
+      threshold: Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : 0.8,
+    });
+  }}

128-159: Debounced revert uses stale cacheConfig from closure; capture prev state.

On backend error, setCacheConfig(cacheConfig) may revert to a newer or unrelated state. Capture prevConfig before optimistic update and revert to that.

 const debouncedUpdateCacheConfig = useCallback(
   (updates: Partial<CacheConfig>) => {
-    // Update local state immediately for responsive UI
-    const newConfig = { ...cacheConfig, ...updates };
+    // Update local state immediately for responsive UI
+    const prevConfig = cacheConfig;
+    const newConfig = { ...prevConfig, ...updates };
     setCacheConfig(newConfig);
@@
-            .catch((error) => {
+            .catch((error) => {
               toast.error("Failed to update cache configuration");
               // Revert on error
-              setCacheConfig(cacheConfig);
+              setCacheConfig(prevConfig);
             });
♻️ Duplicate comments (2)
framework/vectorstore/redis.go (2)

29-36: Compile error: redis.Options has no MaxActiveConns/MaxIdleConns.

Drop unsupported fields here to match go-redis v9.

 type RedisConfig struct {
@@
-  PoolSize        int           `json:"pool_size,omitempty"`          // Maximum number of socket connections (optional)
-  MaxActiveConns  int           `json:"max_active_conns,omitempty"`   // Maximum number of active connections (optional)
-  MinIdleConns    int           `json:"min_idle_conns,omitempty"`     // Minimum number of idle connections (optional)
-  MaxIdleConns    int           `json:"max_idle_conns,omitempty"`     // Maximum number of idle connections (optional)
+  PoolSize        int           `json:"pool_size,omitempty"`          // Maximum number of socket connections (optional)
+  MinIdleConns    int           `json:"min_idle_conns,omitempty"`     // Minimum number of idle connections (optional)

856-871: Compile error: remove unsupported Options fields.

Match go-redis v9 Options by removing MaxActiveConns/MaxIdleConns.

 client := redis.NewClient(&redis.Options{
   Addr:            config.Addr,
   Username:        config.Username,
   Password:        config.Password,
   DB:              config.DB,
   Protocol:        3, // Explicitly use RESP3 protocol
-  PoolSize:        config.PoolSize,
-  MaxActiveConns:  config.MaxActiveConns,
-  MinIdleConns:    config.MinIdleConns,
-  MaxIdleConns:    config.MaxIdleConns,
+  PoolSize:        config.PoolSize,
+  MinIdleConns:    config.MinIdleConns,
   ConnMaxLifetime: config.ConnMaxLifetime,
   ConnMaxIdleTime: config.ConnMaxIdleTime,
   DialTimeout:     config.DialTimeout,
   ReadTimeout:     config.ReadTimeout,
   WriteTimeout:    config.WriteTimeout,
 })
🧹 Nitpick comments (6)
ui/app/config/views/pluginsForm.tsx (1)

282-291: Dimension input: allow 0=auto (if intended), clamp and avoid parseInt pitfalls.

Fix UX and validation by permitting 0, adding step, and using valueAsNumber with clamping and helper text.

   <div className="space-y-2">
     <Label htmlFor="dimension">Dimension</Label>
     <Input
       id="dimension"
       type="number"
-      min="1"
-      value={cacheConfig.dimension}
-      onChange={(e) => debouncedUpdateCacheConfig({ dimension: parseInt(e.target.value) || 0 })}
+      min="0"
+      step="1"
+      value={cacheConfig.dimension}
+      onChange={(e) => {
+        const v = e.currentTarget.valueAsNumber;
+        debouncedUpdateCacheConfig({
+          dimension: Number.isFinite(v) ? Math.max(0, Math.floor(v)) : 0,
+        });
+      }}
     />
+    <p className="text-muted-foreground text-xs">0 uses the embedding model&apos;s default dimension.</p>
   </div>
plugins/semanticcache/main.go (2)

45-46: Docstring claims “1hr”, but time.ParseDuration doesn’t accept it.

Either change the example to “1h” or add custom parsing for “hr”.

-// It supports TTL parsing from both string durations ("1m", "1hr") and numeric seconds for configurable cache behavior.
+// It supports TTL parsing from both string durations ("1m", "1h") and numeric seconds for configurable cache behavior.

302-314: Avoid shadowing the imported package name “bifrost”.

Minor readability nit; rename the local variable.

-   bifrost, err := bifrost.Init(ctx, schemas.BifrostConfig{
+   bfClient, err := bifrost.Init(ctx, schemas.BifrostConfig{
       Logger: logger,
       Account: &PluginAccount{
         provider: config.Provider,
         keys:     config.Keys,
       },
     })
     if err != nil {
       return nil, fmt.Errorf("failed to initialize bifrost for semantic cache: %w", err)
     }
-
-    plugin.client = bifrost
+    plugin.client = bfClient
framework/vectorstore/test_utils.go (1)

30-46: Seed randomness to reduce test flakiness.

Deterministic embeddings make assertions stable across runs.

+func init() {
+  rand.Seed(1)
+}
framework/vectorstore/redis_test.go (1)

16-22: Use a unique test namespace per run to avoid collisions.

Parallel runs or leaked data can interfere with fixed “TestRedis”.

-const (
-  RedisTestTimeout        = 30 * time.Second
-  TestNamespace           = "TestRedis"
+var (
+  RedisTestTimeout        = 30 * time.Second
+  TestNamespace           = "TestRedis_" + generateUUID()
framework/vectorstore/redis.go (1)

284-369: RESP3-only parsing: okay since Protocol=3 is set. Add a brief comment.

Clarify the coupling to avoid future regressions.

 func (s *RedisStore) parseSearchResults(result interface{}, selectFields []string) ([]SearchResult, error) {
-  // FT.SEARCH returns a map with results array
+  // RESP3: FT.SEARCH returns a map with "results"/"extra_attributes".
+  // newRedisStore pins Protocol=3 to guarantee this shape.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f87f7c and 637b4d1.

⛔ Files ignored due to path filters (1)
  • framework/go.sum is excluded by !**/*.sum
📒 Files selected for processing (20)
  • docs/architecture/framework/vector-store.mdx (1 hunks)
  • docs/architecture/framework/vector-store/weaviate.mdx (0 hunks)
  • docs/docs.json (1 hunks)
  • docs/features/semantic-caching.mdx (1 hunks)
  • framework/go.mod (2 hunks)
  • framework/vectorstore/redis.go (1 hunks)
  • framework/vectorstore/redis_test.go (1 hunks)
  • framework/vectorstore/store.go (4 hunks)
  • framework/vectorstore/test_utils.go (1 hunks)
  • framework/vectorstore/utils.go (1 hunks)
  • framework/vectorstore/weaviate.go (2 hunks)
  • framework/vectorstore/weaviate_test.go (1 hunks)
  • plugins/semanticcache/main.go (10 hunks)
  • plugins/semanticcache/plugin_core_test.go (3 hunks)
  • plugins/semanticcache/search.go (8 hunks)
  • plugins/semanticcache/stream.go (1 hunks)
  • plugins/semanticcache/test_utils.go (6 hunks)
  • plugins/semanticcache/utils.go (1 hunks)
  • ui/app/config/views/pluginsForm.tsx (2 hunks)
  • ui/lib/types/config.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • docs/architecture/framework/vector-store/weaviate.mdx
✅ Files skipped from review due to trivial changes (1)
  • docs/architecture/framework/vector-store.mdx
🚧 Files skipped from review as they are similar to previous changes (11)
  • plugins/semanticcache/stream.go
  • ui/lib/types/config.ts
  • framework/vectorstore/store.go
  • docs/features/semantic-caching.mdx
  • framework/go.mod
  • docs/docs.json
  • framework/vectorstore/utils.go
  • plugins/semanticcache/search.go
  • plugins/semanticcache/test_utils.go
  • framework/vectorstore/weaviate_test.go
  • plugins/semanticcache/utils.go
🧰 Additional context used
🧬 Code graph analysis (4)
framework/vectorstore/weaviate.go (2)
framework/vectorstore/store.go (1)
  • VectorStoreProperties (63-66)
plugins/semanticcache/main.go (1)
  • VectorStoreProperties (157-194)
ui/app/config/views/pluginsForm.tsx (2)
ui/components/ui/label.tsx (1)
  • Label (21-21)
ui/components/ui/input.tsx (1)
  • Input (7-21)
framework/vectorstore/redis.go (3)
core/schemas/logger.go (1)
  • Logger (28-55)
framework/vectorstore/store.go (18)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeInteger (72-72)
  • SearchResult (43-47)
  • Query (20-24)
  • QueryOperatorEqual (29-29)
  • QueryOperatorNotEqual (30-30)
  • QueryOperatorLike (35-35)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorLessThanOrEqual (34-34)
  • QueryOperatorIsNull (38-38)
  • QueryOperatorIsNotNull (39-39)
  • QueryOperatorContainsAny (36-36)
  • QueryOperatorContainsAll (37-37)
  • DeleteResult (50-54)
  • DeleteStatusError (60-60)
  • DeleteStatusSuccess (59-59)
plugins/semanticcache/main.go (1)
  • VectorStoreProperties (157-194)
plugins/semanticcache/main.go (1)
framework/vectorstore/store.go (1)
  • VectorStoreProperties (63-66)
🔇 Additional comments (2)
plugins/semanticcache/plugin_core_test.go (1)

336-363: LGTM: model name updates are consistent.

framework/vectorstore/weaviate.go (1)

451-499: Dimension-aware CreateNamespace looks good.

Applying vectorDimensions only when >0 preserves backward compatibility.

If you’ve validated “vectorDimensions” against your current Weaviate version in CI, we’re good. Otherwise, confirm the exact key in the version you target.

Comment thread plugins/semanticcache/main.go Outdated
Comment thread plugins/semanticcache/main.go
@TejasGhatte TejasGhatte force-pushed the 09-02-feat_redis_as_vector_store branch 2 times, most recently from 16484ca to 3e8863d Compare September 15, 2025 14:24
Comment thread framework/vectorstore/redis.go
Comment thread framework/vectorstore/redis.go Outdated
Comment thread framework/vectorstore/redis.go
Comment thread plugins/semanticcache/main.go Outdated
Copy link
Copy Markdown
Collaborator

@Pratham-Mishra04 Pratham-Mishra04 left a comment

Choose a reason for hiding this comment

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

some comments

@TejasGhatte TejasGhatte force-pushed the 09-02-feat_redis_as_vector_store branch from 3e8863d to 1b4a949 Compare September 16, 2025 05:16
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
plugins/semanticcache/main.go (1)

623-637: Cleanup order is sane; destructive namespace deletion is intentional per product choice

DeleteAll (scoped by from_bifrost_semantic_cache_plugin) followed by DeleteNamespace is consistent with earlier discussion. Ensure docs clearly mark the namespace as plugin-owned/ephemeral, which you already mentioned in prior comments.

🧹 Nitpick comments (7)
plugins/semanticcache/main.go (2)

32-36: Dimension in Config: consider omitempty to reduce noisy serialization

dimension is required for Redis but optional (auto-inferred) for Weaviate. Marking it omitempty avoids emitting 0 in JSON when not set.

- Dimension            int             `json:"dimension"`
+ Dimension            int             `json:"dimension,omitempty"`

312-315: CreateNamespace timeout + dimension threading — good; add one debug for DX

We’re calling CreateNamespace with a bounded context and passing dimension through — great. Add a debug log that prints the namespace and dimension to aid troubleshooting (esp. when Redis requires >0).

 createCtx, cancel := context.WithTimeout(ctx, CreateNamespaceTimeout)
 defer cancel()
+logger.Debug(fmt.Sprintf("%s Creating vector namespace %q (dimension=%d)", PluginLoggerPrefix, config.VectorStoreNamespace, config.Dimension))
 if err := store.CreateNamespace(createCtx, config.VectorStoreNamespace, config.Dimension, VectorStoreProperties); err != nil {
framework/vectorstore/redis_test.go (2)

142-148: Treat “already exists” as success instead of skipping the whole suite

CreateNamespace may return an “already exists” error; don’t skip in that case or subsequent tests will be silently dropped.

- err := ts.Store.CreateNamespace(ts.ctx, TestNamespace, RedisTestDimension, properties)
- if err != nil {
-     t.Skipf("RediSearch not available or schema creation failed for namespace %q: %v", TestNamespace, err)
- } else {
-     t.Logf("Created test namespace: %s", TestNamespace)
- }
+ err := ts.Store.CreateNamespace(ts.ctx, TestNamespace, RedisTestDimension, properties)
+ if err != nil {
+     if strings.Contains(strings.ToLower(err.Error()), "already exists") {
+         t.Logf("Namespace %q already exists; continuing", TestNamespace)
+     } else {
+         t.Skipf("RediSearch not available or schema creation failed for namespace %q: %v", TestNamespace, err)
+     }
+ } else {
+     t.Logf("Created test namespace: %s", TestNamespace)
+ }

Remember to import strings.


83-95: Tests leave the index around; optional: drop test namespace on Cleanup

You already delete keys; optionally call DeleteNamespace to keep Redis clean across runs.

 func (ts *RedisTestSetup) Cleanup(t *testing.T) {
   defer ts.cancel()
   if !testing.Short() {
     ts.cleanupTestData(t)
+    if err := ts.Store.DeleteNamespace(ts.ctx, TestNamespace); err != nil {
+      t.Logf("Warning: Failed to drop test namespace: %v", err)
+    }
   }
   if err := ts.Store.Close(ts.ctx, TestNamespace); err != nil {
     t.Logf("Warning: Failed to close store: %v", err)
   }
 }
framework/vectorstore/redis.go (3)

463-516: KNN query honors LIMIT and returns score; small safeguard recommended

Add an early namespace check to surface clearer errors and optionally validate vector length if you decide to persist per-namespace dimension later.

 func (s *RedisStore) GetNearest(...){
   ctx, cancel := withTimeout(ctx, s.config.ContextTimeout)
   defer cancel()
+  if strings.TrimSpace(namespace) == "" {
+    return nil, fmt.Errorf("namespace is required")
+  }
   // (Optionally) if you later track dimension: compare len(vector) with index dim and error early.

582-611: Add-time dimensionality check (optional, if you track index dim)

If you store/know the index dimension, validate embedding length on Add to prevent KNN failures later.

 if len(embedding) > 0 {
+  // if known, validate embedding dimensionality to match index
+  // e.g., if s.indexDims[namespace] > 0 && len(embedding) != s.indexDims[namespace] { return fmt.Errorf(...) }
   embeddingBytes := float32SliceToBytes(embedding)
   fields["embedding"] = embeddingBytes
 }

766-795: Comma-to-OR escaping is non-standard — add a brief comment

Since commas are intentionally treated as OR for TAG queries, document it inline to avoid future confusion.

- ",", "|",
+ // Intentional: treat comma-delimited values as OR in TAG queries
+ ",", "|",
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16484ca and 1b4a949.

⛔ Files ignored due to path filters (1)
  • framework/go.sum is excluded by !**/*.sum
📒 Files selected for processing (20)
  • docs/architecture/framework/vector-store.mdx (1 hunks)
  • docs/architecture/framework/vector-store/weaviate.mdx (0 hunks)
  • docs/docs.json (1 hunks)
  • docs/features/semantic-caching.mdx (1 hunks)
  • framework/go.mod (2 hunks)
  • framework/vectorstore/redis.go (1 hunks)
  • framework/vectorstore/redis_test.go (1 hunks)
  • framework/vectorstore/store.go (4 hunks)
  • framework/vectorstore/test_utils.go (1 hunks)
  • framework/vectorstore/utils.go (1 hunks)
  • framework/vectorstore/weaviate.go (2 hunks)
  • framework/vectorstore/weaviate_test.go (1 hunks)
  • plugins/semanticcache/main.go (10 hunks)
  • plugins/semanticcache/plugin_core_test.go (3 hunks)
  • plugins/semanticcache/search.go (8 hunks)
  • plugins/semanticcache/stream.go (1 hunks)
  • plugins/semanticcache/test_utils.go (6 hunks)
  • plugins/semanticcache/utils.go (1 hunks)
  • ui/app/config/views/pluginsForm.tsx (2 hunks)
  • ui/lib/types/config.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • docs/architecture/framework/vector-store/weaviate.mdx
🚧 Files skipped from review as they are similar to previous changes (14)
  • docs/features/semantic-caching.mdx
  • framework/vectorstore/test_utils.go
  • framework/vectorstore/utils.go
  • ui/lib/types/config.ts
  • ui/app/config/views/pluginsForm.tsx
  • plugins/semanticcache/plugin_core_test.go
  • plugins/semanticcache/test_utils.go
  • framework/vectorstore/weaviate.go
  • docs/docs.json
  • framework/go.mod
  • framework/vectorstore/weaviate_test.go
  • docs/architecture/framework/vector-store.mdx
  • framework/vectorstore/store.go
  • plugins/semanticcache/search.go
🧰 Additional context used
🧬 Code graph analysis (3)
framework/vectorstore/redis_test.go (4)
framework/vectorstore/redis.go (2)
  • RedisStore (43-47)
  • RedisConfig (22-40)
core/schemas/logger.go (2)
  • Logger (28-55)
  • LogLevelInfo (11-11)
framework/vectorstore/store.go (12)
  • Config (92-96)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeString (71-71)
  • VectorStorePropertyTypeInteger (72-72)
  • VectorStorePropertyTypeBoolean (73-73)
  • Query (20-24)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorEqual (29-29)
  • QueryOperatorLessThan (32-32)
  • VectorStore (78-89)
  • VectorStoreTypeRedis (16-16)
  • NewVectorStore (138-168)
core/logger.go (1)
  • NewDefaultLogger (40-49)
plugins/semanticcache/main.go (1)
framework/vectorstore/store.go (1)
  • VectorStoreProperties (63-66)
framework/vectorstore/redis.go (2)
core/schemas/logger.go (1)
  • Logger (28-55)
framework/vectorstore/store.go (18)
  • VectorStoreProperties (63-66)
  • VectorStorePropertyTypeInteger (72-72)
  • SearchResult (43-47)
  • Query (20-24)
  • QueryOperatorEqual (29-29)
  • QueryOperatorNotEqual (30-30)
  • QueryOperatorLike (35-35)
  • QueryOperatorGreaterThan (31-31)
  • QueryOperatorGreaterThanOrEqual (33-33)
  • QueryOperatorLessThan (32-32)
  • QueryOperatorLessThanOrEqual (34-34)
  • QueryOperatorIsNull (38-38)
  • QueryOperatorIsNotNull (39-39)
  • QueryOperatorContainsAny (36-36)
  • QueryOperatorContainsAll (37-37)
  • DeleteResult (50-54)
  • DeleteStatusError (60-60)
  • DeleteStatusSuccess (59-59)
🔇 Additional comments (8)
plugins/semanticcache/utils.go (1)

308-308: Namespace switch to configured value — looks good

Using plugin.config.VectorStoreNamespace here aligns with the new pluggable namespace behavior across the plugin.

plugins/semanticcache/stream.go (1)

123-123: Consistent use of configured namespace in streaming finalization

The final Add now targets plugin.config.VectorStoreNamespace, matching single-response logic. Good consistency.

plugins/semanticcache/main.go (2)

264-267: Good defaulting for VectorStoreNamespace

Defaulting and logging help trace config. Thanks for the explicit debug.


667-670: Timeouts added for DeleteAll/Delete — nice

This prevents request-thread hangs on external calls.

Also applies to: 696-699

framework/vectorstore/redis.go (4)

71-86: Fail-fast on invalid dimension — good

Rejecting DIM<=0 at FT.CREATE avoids opaque Redis errors.


225-281: GetAll: LIMIT/OFFSET + DIALECT 2 implemented correctly

Honors limit=0 as “all” via MaxInt32 and supports cursor-based paging. Solid.


284-369: RESP3-only parsing is acceptable given Protocol:3

parseSearchResults expects RESP3 maps and newRedisStore pins Protocol:3. This coupling is fine; just ensure we don’t downgrade Protocol elsewhere.


818-833: Compilation blocker: redis.Options has no MaxActiveConns/MaxIdleConns

go-redis v9 uses PoolSize and MinIdleConns; MaxActiveConns/MaxIdleConns aren’t valid fields and will fail to compile.

 client := redis.NewClient(&redis.Options{
   Addr:            config.Addr,
   Username:        config.Username,
   Password:        config.Password,
   DB:              config.DB,
   Protocol:        3, // Explicitly use RESP3 protocol
   PoolSize:        config.PoolSize,
-  MaxActiveConns:  config.MaxActiveConns,
   MinIdleConns:    config.MinIdleConns,
-  MaxIdleConns:    config.MaxIdleConns,
   ConnMaxLifetime: config.ConnMaxLifetime,
   ConnMaxIdleTime: config.ConnMaxIdleTime,
   DialTimeout:     config.DialTimeout,
   ReadTimeout:     config.ReadTimeout,
   WriteTimeout:    config.WriteTimeout,
 })

Optional follow-up: deprecate MaxActiveConns/MaxIdleConns in RedisConfig or map legacy MaxActiveConns to PoolSize when PoolSize==0.

⛔ Skipped due to learnings
Learnt from: TejasGhatte
PR: maximhq/bifrost#450
File: docs/architecture/framework/vector-store/redis.mdx:59-63
Timestamp: 2025-09-12T14:02:20.389Z
Learning: go-redis v9 (specifically v9.2.0+) supports MaxActiveConns field in redis.Options struct for connection pool management. This field is valid and should not be flagged as unsupported in go-redis configurations.

Copy link
Copy Markdown
Collaborator

Pratham-Mishra04 commented Sep 16, 2025

Merge activity

  • Sep 16, 5:53 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Sep 16, 5:54 AM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@Pratham-Mishra04 Pratham-Mishra04 merged commit 6df11c5 into main Sep 16, 2025
4 checks passed
@Pratham-Mishra04 Pratham-Mishra04 deleted the 09-02-feat_redis_as_vector_store branch September 16, 2025 05:54
@TejasGhatte TejasGhatte mentioned this pull request Sep 16, 2025
18 tasks
akshaydeo pushed a commit that referenced this pull request Nov 17, 2025
## Summary

Add Redis vector store implementation to support Redis as a backend for vector storage and retrieval operations.

## Changes

- Implemented a new `RedisStore` that implements the `VectorStore` interface
- Added support for Redis with RediSearch module for vector similarity search
- Implemented all required vector store operations (add, get, delete, search)
- Added comprehensive test suite for Redis vector store
- Updated the vector store factory to support Redis as a store type

## Type of change

- [x] Feature
- [ ] Bug fix
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [x] Plugins
- [ ] UI (Next.js)
- [ ] Docs

## How to test

Requires a Redis instance with RediSearch module enabled:

```sh
# Run Redis with RediSearch module
docker run -p 6379:6379 redis/redis-stack

# Set environment variables for tests
export REDIS_ADDR=localhost:6379
export REDIS_USERNAME=
export REDIS_PASSWORD=

# Run tests
go test ./framework/vectorstore -v
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

Adds Redis as an alternative to Weaviate for vector storage, providing more deployment options.

## Security considerations

- Redis connection details (username/password) are handled securely
- Proper error handling for all Redis operations
- Timeouts implemented for all Redis operations to prevent hanging

## Checklist

- [x] I added/updated tests where appropriate
- [x] I verified builds succeed (Go)
- [x] I verified the CI pipeline passes locally
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.

3 participants