agno 2.2.11__py3-none-any.whl → 2.2.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. agno/agent/agent.py +62 -47
  2. agno/db/mysql/mysql.py +1 -1
  3. agno/db/singlestore/singlestore.py +1 -1
  4. agno/db/sqlite/async_sqlite.py +1 -1
  5. agno/db/sqlite/sqlite.py +1 -1
  6. agno/filters.py +354 -0
  7. agno/knowledge/knowledge.py +43 -22
  8. agno/os/interfaces/slack/router.py +53 -33
  9. agno/os/interfaces/slack/slack.py +9 -1
  10. agno/os/router.py +25 -1
  11. agno/run/base.py +3 -2
  12. agno/session/agent.py +10 -5
  13. agno/team/team.py +44 -17
  14. agno/utils/agent.py +22 -17
  15. agno/utils/gemini.py +15 -5
  16. agno/utils/knowledge.py +12 -5
  17. agno/utils/log.py +1 -0
  18. agno/utils/print_response/agent.py +5 -4
  19. agno/utils/print_response/team.py +5 -4
  20. agno/vectordb/base.py +2 -4
  21. agno/vectordb/cassandra/cassandra.py +12 -5
  22. agno/vectordb/chroma/chromadb.py +10 -4
  23. agno/vectordb/clickhouse/clickhousedb.py +12 -4
  24. agno/vectordb/couchbase/couchbase.py +12 -3
  25. agno/vectordb/lancedb/lance_db.py +69 -144
  26. agno/vectordb/langchaindb/langchaindb.py +13 -4
  27. agno/vectordb/lightrag/lightrag.py +8 -3
  28. agno/vectordb/llamaindex/llamaindexdb.py +10 -4
  29. agno/vectordb/milvus/milvus.py +16 -5
  30. agno/vectordb/mongodb/mongodb.py +14 -3
  31. agno/vectordb/pgvector/pgvector.py +73 -15
  32. agno/vectordb/pineconedb/pineconedb.py +6 -2
  33. agno/vectordb/qdrant/qdrant.py +25 -13
  34. agno/vectordb/redis/redisdb.py +37 -30
  35. agno/vectordb/singlestore/singlestore.py +9 -4
  36. agno/vectordb/surrealdb/surrealdb.py +13 -3
  37. agno/vectordb/upstashdb/upstashdb.py +8 -5
  38. agno/vectordb/weaviate/weaviate.py +29 -12
  39. agno/workflow/workflow.py +13 -7
  40. {agno-2.2.11.dist-info → agno-2.2.12.dist-info}/METADATA +1 -1
  41. {agno-2.2.11.dist-info → agno-2.2.12.dist-info}/RECORD +44 -43
  42. {agno-2.2.11.dist-info → agno-2.2.12.dist-info}/WHEEL +0 -0
  43. {agno-2.2.11.dist-info → agno-2.2.12.dist-info}/licenses/LICENSE +0 -0
  44. {agno-2.2.11.dist-info → agno-2.2.12.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,10 @@
1
1
  import asyncio
2
2
  import time
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Any, Dict, List, Optional, Union
4
4
 
5
5
  from bson import ObjectId
6
6
 
7
+ from agno.filters import FilterExpr
7
8
  from agno.knowledge.document import Document
8
9
  from agno.knowledge.embedder import Embedder
9
10
  from agno.utils.log import log_debug, log_info, log_warning, logger
@@ -585,9 +586,16 @@ class MongoDb(VectorDb):
585
586
  return True
586
587
 
587
588
  def search(
588
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None, min_score: float = 0.0
589
+ self,
590
+ query: str,
591
+ limit: int = 5,
592
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
593
+ min_score: float = 0.0,
589
594
  ) -> List[Document]:
590
595
  """Search for documents using vector similarity."""
596
+ if isinstance(filters, List):
597
+ log_warning("Filters Expressions are not supported in MongoDB. No filters will be applied.")
598
+ filters = None
591
599
  if self.search_type == SearchType.hybrid:
592
600
  return self.hybrid_search(query, limit=limit, filters=filters)
593
601
 
@@ -1153,9 +1161,12 @@ class MongoDb(VectorDb):
1153
1161
  logger.error(f"Error upserting document '{document.name}' asynchronously: {e}")
1154
1162
 
1155
1163
  async def async_search(
1156
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
1164
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
1157
1165
  ) -> List[Document]:
1158
1166
  """Search for documents asynchronously."""
1167
+ if isinstance(filters, List):
1168
+ log_warning("Filters Expressions are not supported in MongoDB. No filters will be applied.")
1169
+ filters = None
1159
1170
  query_embedding = self.embedder.get_embedding(query)
1160
1171
  if query_embedding is None:
1161
1172
  logger.error(f"Failed to generate embedding for query: {query}")
@@ -6,14 +6,15 @@ from typing import Any, Dict, List, Optional, Union, cast
6
6
  from agno.utils.string import generate_id
7
7
 
8
8
  try:
9
- from sqlalchemy import update
9
+ from sqlalchemy import and_, not_, or_, update
10
10
  from sqlalchemy.dialects import postgresql
11
11
  from sqlalchemy.engine import Engine, create_engine
12
12
  from sqlalchemy.inspection import inspect
13
13
  from sqlalchemy.orm import Session, scoped_session, sessionmaker
14
14
  from sqlalchemy.schema import Column, Index, MetaData, Table
15
+ from sqlalchemy.sql.elements import ColumnElement
15
16
  from sqlalchemy.sql.expression import bindparam, desc, func, select, text
16
- from sqlalchemy.types import DateTime, String
17
+ from sqlalchemy.types import DateTime, Integer, String
17
18
 
18
19
  except ImportError:
19
20
  raise ImportError("`sqlalchemy` not installed. Please install using `pip install sqlalchemy psycopg`")
@@ -23,6 +24,7 @@ try:
23
24
  except ImportError:
24
25
  raise ImportError("`pgvector` not installed. Please install using `pip install pgvector`")
25
26
 
27
+ from agno.filters import FilterExpr
26
28
  from agno.knowledge.document import Document
27
29
  from agno.knowledge.embedder import Embedder
28
30
  from agno.knowledge.reranker.base import Reranker
@@ -680,14 +682,16 @@ class PgVector(VectorDb):
680
682
  logger.error(f"Error updating metadata for document {content_id}: {e}")
681
683
  raise
682
684
 
683
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
685
+ def search(
686
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
687
+ ) -> List[Document]:
684
688
  """
685
689
  Perform a search based on the configured search type.
686
690
 
687
691
  Args:
688
692
  query (str): The search query.
689
693
  limit (int): Maximum number of results to return.
690
- filters (Optional[Dict[str, Any]]): Filters to apply to the search.
694
+ filters (Optional[Union[Dict[str, Any], List[FilterExpr]]]): Filters to apply to the search.
691
695
 
692
696
  Returns:
693
697
  List[Document]: List of matching documents.
@@ -703,19 +707,42 @@ class PgVector(VectorDb):
703
707
  return []
704
708
 
705
709
  async def async_search(
706
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
710
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
707
711
  ) -> List[Document]:
708
712
  """Search asynchronously by running in a thread."""
709
713
  return await asyncio.to_thread(self.search, query, limit, filters)
710
714
 
711
- def vector_search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
715
+ def _dsl_to_sqlalchemy(self, filter_expr, table) -> ColumnElement[bool]:
716
+ op = filter_expr["op"]
717
+
718
+ if op == "EQ":
719
+ return table.c.meta_data[filter_expr["key"]].astext == str(filter_expr["value"])
720
+ elif op == "IN":
721
+ # Postgres JSONB array containment
722
+ return table.c.meta_data[filter_expr["key"]].astext.in_([str(v) for v in filter_expr["values"]])
723
+ elif op == "GT":
724
+ return table.c.meta_data[filter_expr["key"]].astext.cast(Integer) > filter_expr["value"]
725
+ elif op == "LT":
726
+ return table.c.meta_data[filter_expr["key"]].astext.cast(Integer) < filter_expr["value"]
727
+ elif op == "NOT":
728
+ return not_(self._dsl_to_sqlalchemy(filter_expr["condition"], table))
729
+ elif op == "AND":
730
+ return and_(*[self._dsl_to_sqlalchemy(cond, table) for cond in filter_expr["conditions"]])
731
+ elif op == "OR":
732
+ return or_(*[self._dsl_to_sqlalchemy(cond, table) for cond in filter_expr["conditions"]])
733
+ else:
734
+ raise ValueError(f"Unknown filter operator: {op}")
735
+
736
+ def vector_search(
737
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
738
+ ) -> List[Document]:
712
739
  """
713
740
  Perform a vector similarity search.
714
741
 
715
742
  Args:
716
743
  query (str): The search query.
717
744
  limit (int): Maximum number of results to return.
718
- filters (Optional[Dict[str, Any]]): Filters to apply to the search.
745
+ filters (Optional[Union[Dict[str, Any], List[FilterExpr]]]): Filters to apply to the search.
719
746
 
720
747
  Returns:
721
748
  List[Document]: List of matching documents.
@@ -742,7 +769,17 @@ class PgVector(VectorDb):
742
769
 
743
770
  # Apply filters if provided
744
771
  if filters is not None:
745
- stmt = stmt.where(self.table.c.meta_data.contains(filters))
772
+ # Handle dict filters
773
+ if isinstance(filters, dict):
774
+ stmt = stmt.where(self.table.c.meta_data.contains(filters))
775
+ # Handle FilterExpr DSL
776
+ else:
777
+ # Convert each DSL expression to SQLAlchemy and AND them together
778
+ sqlalchemy_conditions = [
779
+ self._dsl_to_sqlalchemy(f.to_dict() if hasattr(f, "to_dict") else f, self.table)
780
+ for f in filters
781
+ ]
782
+ stmt = stmt.where(and_(*sqlalchemy_conditions))
746
783
 
747
784
  # Order the results based on the distance metric
748
785
  if self.distance == Distance.l2:
@@ -815,14 +852,16 @@ class PgVector(VectorDb):
815
852
  processed_words = [word + "*" for word in words]
816
853
  return " ".join(processed_words)
817
854
 
818
- def keyword_search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
855
+ def keyword_search(
856
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
857
+ ) -> List[Document]:
819
858
  """
820
859
  Perform a keyword search on the 'content' column.
821
860
 
822
861
  Args:
823
862
  query (str): The search query.
824
863
  limit (int): Maximum number of results to return.
825
- filters (Optional[Dict[str, Any]]): Filters to apply to the search.
864
+ filters (Optional[Union[Dict[str, Any], List[FilterExpr]]]): Filters to apply to the search.
826
865
 
827
866
  Returns:
828
867
  List[Document]: List of matching documents.
@@ -851,8 +890,17 @@ class PgVector(VectorDb):
851
890
 
852
891
  # Apply filters if provided
853
892
  if filters is not None:
854
- # Use the contains() method for JSONB columns to check if the filters column contains the specified filters
855
- stmt = stmt.where(self.table.c.meta_data.contains(filters))
893
+ # Handle dict filters
894
+ if isinstance(filters, dict):
895
+ stmt = stmt.where(self.table.c.meta_data.contains(filters))
896
+ # Handle FilterExpr DSL
897
+ else:
898
+ # Convert each DSL expression to SQLAlchemy and AND them together
899
+ sqlalchemy_conditions = [
900
+ self._dsl_to_sqlalchemy(f.to_dict() if hasattr(f, "to_dict") else f, self.table)
901
+ for f in filters
902
+ ]
903
+ stmt = stmt.where(and_(*sqlalchemy_conditions))
856
904
 
857
905
  # Order by the relevance rank
858
906
  stmt = stmt.order_by(text_rank.desc())
@@ -898,7 +946,7 @@ class PgVector(VectorDb):
898
946
  self,
899
947
  query: str,
900
948
  limit: int = 5,
901
- filters: Optional[Dict[str, Any]] = None,
949
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
902
950
  ) -> List[Document]:
903
951
  """
904
952
  Perform a hybrid search combining vector similarity and full-text search.
@@ -906,7 +954,7 @@ class PgVector(VectorDb):
906
954
  Args:
907
955
  query (str): The search query.
908
956
  limit (int): Maximum number of results to return.
909
- filters (Optional[Dict[str, Any]]): Filters to apply to the search.
957
+ filters (Optional[Union[Dict[str, Any], List[FilterExpr]]]): Filters to apply to the search.
910
958
 
911
959
  Returns:
912
960
  List[Document]: List of matching documents.
@@ -973,7 +1021,17 @@ class PgVector(VectorDb):
973
1021
 
974
1022
  # Apply filters if provided
975
1023
  if filters is not None:
976
- stmt = stmt.where(self.table.c.meta_data.contains(filters))
1024
+ # Handle dict filters
1025
+ if isinstance(filters, dict):
1026
+ stmt = stmt.where(self.table.c.meta_data.contains(filters))
1027
+ # Handle FilterExpr DSL
1028
+ else:
1029
+ # Convert each DSL expression to SQLAlchemy and AND them together
1030
+ sqlalchemy_conditions = [
1031
+ self._dsl_to_sqlalchemy(f.to_dict() if hasattr(f, "to_dict") else f, self.table)
1032
+ for f in filters
1033
+ ]
1034
+ stmt = stmt.where(and_(*sqlalchemy_conditions))
977
1035
 
978
1036
  # Order the results by the hybrid score in descending order
979
1037
  stmt = stmt.order_by(desc("hybrid_score"))
@@ -22,6 +22,7 @@ except ImportError:
22
22
  raise ImportError("The `pinecone` package is not installed, please install using `pip install pinecone`.")
23
23
 
24
24
 
25
+ from agno.filters import FilterExpr
25
26
  from agno.knowledge.document import Document
26
27
  from agno.knowledge.embedder import Embedder
27
28
  from agno.knowledge.reranker.base import Reranker
@@ -474,7 +475,7 @@ class PineconeDb(VectorDb):
474
475
  self,
475
476
  query: str,
476
477
  limit: int = 5,
477
- filters: Optional[Dict[str, Union[str, float, int, bool, List, dict]]] = None,
478
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
478
479
  namespace: Optional[str] = None,
479
480
  include_values: Optional[bool] = None,
480
481
  ) -> List[Document]:
@@ -492,6 +493,9 @@ class PineconeDb(VectorDb):
492
493
  List[Document]: The list of matching documents.
493
494
 
494
495
  """
496
+ if isinstance(filters, List):
497
+ log_warning("Filters Expressions are not supported in PineconeDB. No filters will be applied.")
498
+ filters = None
495
499
  dense_embedding = self.embedder.get_embedding(query)
496
500
 
497
501
  if self.use_hybrid_search:
@@ -540,7 +544,7 @@ class PineconeDb(VectorDb):
540
544
  self,
541
545
  query: str,
542
546
  limit: int = 5,
543
- filters: Optional[Dict[str, Union[str, float, int, bool, List, dict]]] = None,
547
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
544
548
  namespace: Optional[str] = None,
545
549
  include_values: Optional[bool] = None,
546
550
  ) -> List[Document]:
@@ -1,5 +1,5 @@
1
1
  from hashlib import md5
2
- from typing import Any, Dict, List, Optional
2
+ from typing import Any, Dict, List, Optional, Union
3
3
 
4
4
  try:
5
5
  from qdrant_client import AsyncQdrantClient, QdrantClient # noqa: F401
@@ -9,6 +9,7 @@ except ImportError:
9
9
  "The `qdrant-client` package is not installed. Please install it via `pip install qdrant-client`."
10
10
  )
11
11
 
12
+ from agno.filters import FilterExpr
12
13
  from agno.knowledge.document import Document
13
14
  from agno.knowledge.embedder import Embedder
14
15
  from agno.knowledge.reranker.base import Reranker
@@ -528,7 +529,9 @@ class Qdrant(VectorDb):
528
529
  log_debug("Redirecting the async request to async_insert")
529
530
  await self.async_insert(content_hash=content_hash, documents=documents, filters=filters)
530
531
 
531
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
532
+ def search(
533
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
534
+ ) -> List[Document]:
532
535
  """
533
536
  Search for documents in the collection.
534
537
 
@@ -537,28 +540,37 @@ class Qdrant(VectorDb):
537
540
  limit (int): Number of search results to return
538
541
  filters (Optional[Dict[str, Any]]): Filters to apply while searching
539
542
  """
543
+
544
+ if isinstance(filters, List):
545
+ log_warning("Filters Expressions are not supported in Qdrant. No filters will be applied.")
546
+ filters = None
547
+
540
548
  filters = self._format_filters(filters or {}) # type: ignore
541
549
  if self.search_type == SearchType.vector:
542
- results = self._run_vector_search_sync(query, limit, filters)
550
+ results = self._run_vector_search_sync(query, limit, filters) # type: ignore
543
551
  elif self.search_type == SearchType.keyword:
544
- results = self._run_keyword_search_sync(query, limit, filters)
552
+ results = self._run_keyword_search_sync(query, limit, filters) # type: ignore
545
553
  elif self.search_type == SearchType.hybrid:
546
- results = self._run_hybrid_search_sync(query, limit, filters)
554
+ results = self._run_hybrid_search_sync(query, limit, filters) # type: ignore
547
555
  else:
548
556
  raise ValueError(f"Unsupported search type: {self.search_type}")
549
557
 
550
558
  return self._build_search_results(results, query)
551
559
 
552
560
  async def async_search(
553
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
561
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
554
562
  ) -> List[Document]:
563
+ if isinstance(filters, List):
564
+ log_warning("Filters Expressions are not supported in Qdrant. No filters will be applied.")
565
+ filters = None
566
+
555
567
  filters = self._format_filters(filters or {}) # type: ignore
556
568
  if self.search_type == SearchType.vector:
557
- results = await self._run_vector_search_async(query, limit, filters)
569
+ results = await self._run_vector_search_async(query, limit, filters) # type: ignore
558
570
  elif self.search_type == SearchType.keyword:
559
- results = await self._run_keyword_search_async(query, limit, filters)
571
+ results = await self._run_keyword_search_async(query, limit, filters) # type: ignore
560
572
  elif self.search_type == SearchType.hybrid:
561
- results = await self._run_hybrid_search_async(query, limit, filters)
573
+ results = await self._run_hybrid_search_async(query, limit, filters) # type: ignore
562
574
  else:
563
575
  raise ValueError(f"Unsupported search type: {self.search_type}")
564
576
 
@@ -568,7 +580,7 @@ class Qdrant(VectorDb):
568
580
  self,
569
581
  query: str,
570
582
  limit: int,
571
- filters: Optional[Dict[str, Any]],
583
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
572
584
  ) -> List[models.ScoredPoint]:
573
585
  dense_embedding = self.embedder.get_embedding(query)
574
586
  sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
@@ -594,7 +606,7 @@ class Qdrant(VectorDb):
594
606
  self,
595
607
  query: str,
596
608
  limit: int,
597
- filters: Optional[Dict[str, Any]],
609
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
598
610
  ) -> List[models.ScoredPoint]:
599
611
  dense_embedding = self.embedder.get_embedding(query)
600
612
 
@@ -625,7 +637,7 @@ class Qdrant(VectorDb):
625
637
  self,
626
638
  query: str,
627
639
  limit: int,
628
- filters: Optional[Dict[str, Any]],
640
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
629
641
  ) -> List[models.ScoredPoint]:
630
642
  sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
631
643
  call = self.client.query_points(
@@ -692,7 +704,7 @@ class Qdrant(VectorDb):
692
704
  self,
693
705
  query: str,
694
706
  limit: int,
695
- filters: Optional[Dict[str, Any]],
707
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
696
708
  ) -> List[models.ScoredPoint]:
697
709
  dense_embedding = self.embedder.get_embedding(query)
698
710
  sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
@@ -1,5 +1,5 @@
1
1
  import asyncio
2
- from typing import Any, Dict, List, Optional
2
+ from typing import Any, Dict, List, Optional, Union
3
3
 
4
4
  try:
5
5
  from redis import Redis
@@ -12,9 +12,10 @@ try:
12
12
  except ImportError:
13
13
  raise ImportError("`redis` and `redisvl` not installed. Please install using `pip install redis redisvl`")
14
14
 
15
+ from agno.filters import FilterExpr
15
16
  from agno.knowledge.document import Document
16
17
  from agno.knowledge.embedder import Embedder
17
- from agno.utils.log import log_debug, log_info, logger
18
+ from agno.utils.log import log_debug, log_error, log_info, log_warning
18
19
  from agno.utils.string import hash_string_sha256
19
20
  from agno.vectordb.base import VectorDb
20
21
  from agno.vectordb.distance import Distance
@@ -167,7 +168,7 @@ class RedisDB(VectorDb):
167
168
  else:
168
169
  log_debug(f"Redis index already exists: {self.index_name}")
169
170
  except Exception as e:
170
- logger.error(f"Error creating Redis index: {e}")
171
+ log_error(f"Error creating Redis index: {e}")
171
172
  raise
172
173
 
173
174
  async def async_create(self) -> None:
@@ -180,7 +181,7 @@ class RedisDB(VectorDb):
180
181
  if "already exists" in str(e).lower():
181
182
  log_debug(f"Redis index already exists: {self.index_name}")
182
183
  else:
183
- logger.error(f"Error creating Redis index: {e}")
184
+ log_error(f"Error creating Redis index: {e}")
184
185
  raise
185
186
 
186
187
  def doc_exists(self, document: Document) -> bool:
@@ -189,7 +190,7 @@ class RedisDB(VectorDb):
189
190
  doc_id = document.id or hash_string_sha256(document.content)
190
191
  return self.id_exists(doc_id)
191
192
  except Exception as e:
192
- logger.error(f"Error checking if document exists: {e}")
193
+ log_error(f"Error checking if document exists: {e}")
193
194
  return False
194
195
 
195
196
  async def async_doc_exists(self, document: Document) -> bool:
@@ -206,7 +207,7 @@ class RedisDB(VectorDb):
206
207
  results = await async_index.query(query)
207
208
  return len(results) > 0
208
209
  except Exception as e:
209
- logger.error(f"Error checking if document exists: {e}")
210
+ log_error(f"Error checking if document exists: {e}")
210
211
  return False
211
212
 
212
213
  def name_exists(self, name: str) -> bool:
@@ -221,7 +222,7 @@ class RedisDB(VectorDb):
221
222
  results = self.index.query(query)
222
223
  return len(results) > 0
223
224
  except Exception as e:
224
- logger.error(f"Error checking if name exists: {e}")
225
+ log_error(f"Error checking if name exists: {e}")
225
226
  return False
226
227
 
227
228
  async def async_name_exists(self, name: str) -> bool: # type: ignore[override]
@@ -237,7 +238,7 @@ class RedisDB(VectorDb):
237
238
  results = await async_index.query(query)
238
239
  return len(results) > 0
239
240
  except Exception as e:
240
- logger.error(f"Error checking if name exists: {e}")
241
+ log_error(f"Error checking if name exists: {e}")
241
242
  return False
242
243
 
243
244
  def id_exists(self, id: str) -> bool:
@@ -252,7 +253,7 @@ class RedisDB(VectorDb):
252
253
  results = self.index.query(query)
253
254
  return len(results) > 0
254
255
  except Exception as e:
255
- logger.error(f"Error checking if ID exists: {e}")
256
+ log_error(f"Error checking if ID exists: {e}")
256
257
  return False
257
258
 
258
259
  def content_hash_exists(self, content_hash: str) -> bool:
@@ -267,7 +268,7 @@ class RedisDB(VectorDb):
267
268
  results = self.index.query(query)
268
269
  return len(results) > 0
269
270
  except Exception as e:
270
- logger.error(f"Error checking if content hash exists: {e}")
271
+ log_error(f"Error checking if content hash exists: {e}")
271
272
  return False
272
273
 
273
274
  def _parse_redis_hash(self, doc: Document):
@@ -314,7 +315,7 @@ class RedisDB(VectorDb):
314
315
  self.index.load(parsed_documents, id_field="id")
315
316
  log_debug(f"Inserted {len(documents)} documents with content_hash: {content_hash}")
316
317
  except Exception as e:
317
- logger.error(f"Error inserting documents: {e}")
318
+ log_error(f"Error inserting documents: {e}")
318
319
  raise
319
320
 
320
321
  async def async_insert(
@@ -334,7 +335,7 @@ class RedisDB(VectorDb):
334
335
  await async_index.load(parsed_documents, id_field="id")
335
336
  log_debug(f"Inserted {len(documents)} documents with content_hash: {content_hash}")
336
337
  except Exception as e:
337
- logger.error(f"Error inserting documents: {e}")
338
+ log_error(f"Error inserting documents: {e}")
338
339
  raise
339
340
 
340
341
  def upsert_available(self) -> bool:
@@ -368,7 +369,7 @@ class RedisDB(VectorDb):
368
369
  # Insert new docs
369
370
  self.insert(content_hash, documents, filters)
370
371
  except Exception as e:
371
- logger.error(f"Error upserting documents: {e}")
372
+ log_error(f"Error upserting documents: {e}")
372
373
  raise
373
374
 
374
375
  async def async_upsert(
@@ -400,11 +401,17 @@ class RedisDB(VectorDb):
400
401
  # Insert new docs
401
402
  await self.async_insert(content_hash, documents, filters)
402
403
  except Exception as e:
403
- logger.error(f"Error upserting documents: {e}")
404
+ log_error(f"Error upserting documents: {e}")
404
405
  raise
405
406
 
406
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
407
+ def search(
408
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
409
+ ) -> List[Document]:
407
410
  """Search for documents using the specified search type."""
411
+
412
+ if filters and isinstance(filters, List):
413
+ log_warning("Filters Expressions are not supported in Redis. No filters will be applied.")
414
+ filters = None
408
415
  try:
409
416
  if self.search_type == SearchType.vector:
410
417
  return self.vector_search(query, limit)
@@ -415,11 +422,11 @@ class RedisDB(VectorDb):
415
422
  else:
416
423
  raise ValueError(f"Unsupported search type: {self.search_type}")
417
424
  except Exception as e:
418
- logger.error(f"Error in search: {e}")
425
+ log_error(f"Error in search: {e}")
419
426
  return []
420
427
 
421
428
  async def async_search(
422
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
429
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
423
430
  ) -> List[Document]:
424
431
  """Async version of search method."""
425
432
  return await asyncio.to_thread(self.search, query, limit, filters)
@@ -448,7 +455,7 @@ class RedisDB(VectorDb):
448
455
 
449
456
  return documents
450
457
  except Exception as e:
451
- logger.error(f"Error in vector search: {e}")
458
+ log_error(f"Error in vector search: {e}")
452
459
  return []
453
460
 
454
461
  def keyword_search(self, query: str, limit: int = 5) -> List[Document]:
@@ -471,7 +478,7 @@ class RedisDB(VectorDb):
471
478
 
472
479
  return documents
473
480
  except Exception as e:
474
- logger.error(f"Error in keyword search: {e}")
481
+ log_error(f"Error in keyword search: {e}")
475
482
  return []
476
483
 
477
484
  def hybrid_search(self, query: str, limit: int = 5) -> List[Document]:
@@ -500,7 +507,7 @@ class RedisDB(VectorDb):
500
507
 
501
508
  return documents
502
509
  except Exception as e:
503
- logger.error(f"Error in hybrid search: {e}")
510
+ log_error(f"Error in hybrid search: {e}")
504
511
  return []
505
512
 
506
513
  def drop(self) -> bool: # type: ignore[override]
@@ -510,7 +517,7 @@ class RedisDB(VectorDb):
510
517
  log_debug(f"Deleted Redis index: {self.index_name}")
511
518
  return True
512
519
  except Exception as e:
513
- logger.error(f"Error dropping Redis index: {e}")
520
+ log_error(f"Error dropping Redis index: {e}")
514
521
  return False
515
522
 
516
523
  async def async_drop(self) -> None:
@@ -520,7 +527,7 @@ class RedisDB(VectorDb):
520
527
  await async_index.delete(drop=True)
521
528
  log_debug(f"Deleted Redis index: {self.index_name}")
522
529
  except Exception as e:
523
- logger.error(f"Error dropping Redis index: {e}")
530
+ log_error(f"Error dropping Redis index: {e}")
524
531
  raise
525
532
 
526
533
  def exists(self) -> bool:
@@ -528,7 +535,7 @@ class RedisDB(VectorDb):
528
535
  try:
529
536
  return self.index.exists()
530
537
  except Exception as e:
531
- logger.error(f"Error checking if index exists: {e}")
538
+ log_error(f"Error checking if index exists: {e}")
532
539
  return False
533
540
 
534
541
  async def async_exists(self) -> bool:
@@ -537,7 +544,7 @@ class RedisDB(VectorDb):
537
544
  async_index = await self._get_async_index()
538
545
  return await async_index.exists()
539
546
  except Exception as e:
540
- logger.error(f"Error checking if index exists: {e}")
547
+ log_error(f"Error checking if index exists: {e}")
541
548
  return False
542
549
 
543
550
  def optimize(self) -> None:
@@ -551,7 +558,7 @@ class RedisDB(VectorDb):
551
558
  self.index.clear()
552
559
  return True
553
560
  except Exception as e:
554
- logger.error(f"Error deleting Redis index: {e}")
561
+ log_error(f"Error deleting Redis index: {e}")
555
562
  return False
556
563
 
557
564
  def delete_by_id(self, id: str) -> bool:
@@ -562,7 +569,7 @@ class RedisDB(VectorDb):
562
569
  log_debug(f"Deleted document with id '{id}' from Redis index")
563
570
  return result > 0
564
571
  except Exception as e:
565
- logger.error(f"Error deleting document by ID: {e}")
572
+ log_error(f"Error deleting document by ID: {e}")
566
573
  return False
567
574
 
568
575
  def delete_by_name(self, name: str) -> bool:
@@ -588,7 +595,7 @@ class RedisDB(VectorDb):
588
595
  log_debug(f"Deleted {deleted_count} documents with name '{name}'")
589
596
  return deleted_count > 0
590
597
  except Exception as e:
591
- logger.error(f"Error deleting documents by name: {e}")
598
+ log_error(f"Error deleting documents by name: {e}")
592
599
  return False
593
600
 
594
601
  def delete_by_metadata(self, metadata: Dict[str, Any]) -> bool:
@@ -626,7 +633,7 @@ class RedisDB(VectorDb):
626
633
  log_debug(f"Deleted {deleted_count} documents with metadata {metadata}")
627
634
  return deleted_count > 0
628
635
  except Exception as e:
629
- logger.error(f"Error deleting documents by metadata: {e}")
636
+ log_error(f"Error deleting documents by metadata: {e}")
630
637
  return False
631
638
 
632
639
  def delete_by_content_id(self, content_id: str) -> bool:
@@ -652,7 +659,7 @@ class RedisDB(VectorDb):
652
659
  log_debug(f"Deleted {deleted_count} documents with content_id '{content_id}'")
653
660
  return deleted_count > 0
654
661
  except Exception as e:
655
- logger.error(f"Error deleting documents by content_id: {e}")
662
+ log_error(f"Error deleting documents by content_id: {e}")
656
663
  return False
657
664
 
658
665
  def update_metadata(self, content_id: str, metadata: Dict[str, Any]) -> None:
@@ -679,7 +686,7 @@ class RedisDB(VectorDb):
679
686
 
680
687
  log_debug(f"Updated metadata for documents with content_id '{content_id}'")
681
688
  except Exception as e:
682
- logger.error(f"Error updating metadata: {e}")
689
+ log_error(f"Error updating metadata: {e}")
683
690
  raise
684
691
 
685
692
  def get_supported_search_types(self) -> List[str]: