mirror of
https://github.com/open-webui/open-webui.git
synced 2026-09-08 22:21:38 +00:00
add BM25 hybrid search support for Qdrant (multitenancy mode)
This commit is contained in:
parent
9bd84258d0
commit
a0e595c1c5
21 changed files with 219 additions and 40 deletions
|
|
@ -2276,6 +2276,12 @@ QDRANT_TIMEOUT = int(os.environ.get('QDRANT_TIMEOUT', '5'))
|
|||
QDRANT_HNSW_M = int(os.environ.get('QDRANT_HNSW_M', '16'))
|
||||
ENABLE_QDRANT_MULTITENANCY_MODE = os.environ.get('ENABLE_QDRANT_MULTITENANCY_MODE', 'true').lower() == 'true'
|
||||
QDRANT_COLLECTION_PREFIX = os.environ.get('QDRANT_COLLECTION_PREFIX', 'open-webui')
|
||||
QDRANT_HYBRID_SEARCH_ENABLED = os.environ.get('QDRANT_HYBRID_SEARCH_ENABLED', 'true').lower() == 'true'
|
||||
QDRANT_SPARSE_EMBEDDING_MODEL = os.environ.get('QDRANT_SPARSE_EMBEDDING_MODEL', 'Qdrant/bm25')
|
||||
QDRANT_DENSE_VECTOR_NAME = os.environ.get('QDRANT_DENSE_VECTOR_NAME', 'dense')
|
||||
QDRANT_SPARSE_VECTOR_NAME = os.environ.get('QDRANT_SPARSE_VECTOR_NAME', 'sparse')
|
||||
QDRANT_HYBRID_SEARCH_RRF_K = int(os.environ.get('QDRANT_HYBRID_SEARCH_RRF_K', '60'))
|
||||
QDRANT_SPARSE_ON_DISK = os.environ.get('QDRANT_SPARSE_ON_DISK', 'false').lower() == 'true'
|
||||
|
||||
WEAVIATE_HTTP_HOST = os.environ.get('WEAVIATE_HTTP_HOST', '')
|
||||
WEAVIATE_GRPC_HOST = os.environ.get('WEAVIATE_GRPC_HOST', '')
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ class VectorSearchRetriever(BaseRetriever):
|
|||
collection_name=self.collection_name,
|
||||
vectors=[embedding],
|
||||
limit=self.top_k,
|
||||
query=query,
|
||||
)
|
||||
|
||||
ids = result.ids[0]
|
||||
|
|
@ -144,13 +145,14 @@ class VectorSearchRetriever(BaseRetriever):
|
|||
return results
|
||||
|
||||
|
||||
def query_doc(collection_name: str, query_embedding: list[float], k: int, user: UserModel = None):
|
||||
def query_doc(collection_name: str, query_embedding: list[float], k: int, user: UserModel = None, query_text: Optional[str] = None):
|
||||
try:
|
||||
log.debug(f'query_doc:doc {collection_name}')
|
||||
result = VECTOR_DB_CLIENT.search(
|
||||
collection_name=collection_name,
|
||||
vectors=[query_embedding],
|
||||
limit=k,
|
||||
query=query_text,
|
||||
)
|
||||
|
||||
if result:
|
||||
|
|
@ -436,13 +438,14 @@ async def query_collection(
|
|||
results = []
|
||||
error = False
|
||||
|
||||
def process_query_collection(collection_name, query_embedding):
|
||||
def process_query_collection(collection_name, query_embedding, query_text : Optional[str] = None):
|
||||
try:
|
||||
if collection_name:
|
||||
result = query_doc(
|
||||
collection_name=collection_name,
|
||||
k=k,
|
||||
query_embedding=query_embedding,
|
||||
query_text=query_text,
|
||||
)
|
||||
if result is not None:
|
||||
return result.model_dump(), None
|
||||
|
|
@ -457,9 +460,9 @@ async def query_collection(
|
|||
|
||||
with ThreadPoolExecutor() as executor:
|
||||
future_results = []
|
||||
for query_embedding in query_embeddings:
|
||||
for query_text, query_embedding in zip(queries, query_embeddings):
|
||||
for collection_name in collection_names:
|
||||
result = executor.submit(process_query_collection, collection_name, query_embedding)
|
||||
result = executor.submit(process_query_collection, collection_name, query_embedding, query_text)
|
||||
future_results.append(result)
|
||||
task_results = [future.result() for future in future_results]
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ class ChromaClient(VectorDBBase):
|
|||
vectors: list[list[float | int]],
|
||||
filter: Optional[dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
# Search for the nearest neighbor items based on the vectors and return 'limit' number of results.
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ class ElasticsearchClient(VectorDBBase):
|
|||
vectors: list[list[float]],
|
||||
filter: Optional[dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
query = {
|
||||
'size': limit,
|
||||
|
|
|
|||
|
|
@ -384,6 +384,7 @@ class MariaDBVectorClient(VectorDBBase):
|
|||
vectors: List[List[float]],
|
||||
filter: Optional[Dict[str, Any]] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
"""
|
||||
Perform a vector similarity search.
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ class MilvusClient(VectorDBBase):
|
|||
vectors: list[list[float | int]],
|
||||
filter: Optional[dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
# Search for the nearest neighbor items based on the vectors and return 'limit' number of results.
|
||||
collection_name = collection_name.replace('-', '_')
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ class MilvusClient(VectorDBBase):
|
|||
vectors: List[List[float]],
|
||||
filter: Optional[Dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
if not vectors:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ class OpenGaussClient(VectorDBBase):
|
|||
vectors: List[List[float]],
|
||||
filter: Optional[Dict[str, Any]] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
try:
|
||||
if not vectors:
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ class OpenSearchClient(VectorDBBase):
|
|||
vectors: list[list[float | int]],
|
||||
filter: Optional[dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
try:
|
||||
if not self.has_collection(collection_name):
|
||||
|
|
|
|||
|
|
@ -518,6 +518,7 @@ class Oracle23aiClient(VectorDBBase):
|
|||
vectors: List[List[Union[float, int]]],
|
||||
filter: Optional[dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
"""
|
||||
Search for similar vectors in the database.
|
||||
|
|
|
|||
|
|
@ -396,6 +396,7 @@ class PgvectorClient(VectorDBBase):
|
|||
vectors: List[List[float]],
|
||||
filter: Optional[Dict[str, Any]] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
try:
|
||||
if not vectors:
|
||||
|
|
|
|||
|
|
@ -354,6 +354,7 @@ class PineconeClient(VectorDBBase):
|
|||
vectors: List[List[Union[float, int]]],
|
||||
filter: Optional[dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
"""Search for similar vectors in a collection."""
|
||||
if not vectors or not vectors[0]:
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ class QdrantClient(VectorDBBase):
|
|||
vectors: list[list[float | int]],
|
||||
filter: Optional[dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
# Search for the nearest neighbor items based on the vectors and return 'limit' number of results.
|
||||
if limit is None:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,14 @@ from open_webui.config import (
|
|||
QDRANT_COLLECTION_PREFIX,
|
||||
QDRANT_TIMEOUT,
|
||||
QDRANT_HNSW_M,
|
||||
QDRANT_HYBRID_SEARCH_ENABLED,
|
||||
QDRANT_SPARSE_EMBEDDING_MODEL,
|
||||
QDRANT_DENSE_VECTOR_NAME,
|
||||
QDRANT_SPARSE_VECTOR_NAME,
|
||||
QDRANT_HYBRID_SEARCH_RRF_K,
|
||||
QDRANT_SPARSE_ON_DISK,
|
||||
)
|
||||
|
||||
from open_webui.retrieval.vector.main import (
|
||||
GetResult,
|
||||
SearchResult,
|
||||
|
|
@ -25,9 +32,16 @@ from open_webui.retrieval.vector.main import (
|
|||
)
|
||||
from qdrant_client import QdrantClient as Qclient
|
||||
from qdrant_client.http.exceptions import UnexpectedResponse
|
||||
from qdrant_client.http.models import PointStruct
|
||||
from qdrant_client.http.models import PointStruct, SparseVector
|
||||
from qdrant_client.models import models
|
||||
|
||||
try:
|
||||
from fastembed.sparse import SparseTextEmbedding
|
||||
|
||||
FASTEMBED_AVAILABLE = True
|
||||
except ImportError:
|
||||
FASTEMBED_AVAILABLE = False
|
||||
|
||||
NO_LIMIT = 999999999
|
||||
TENANT_ID_FIELD = 'tenant_id'
|
||||
DEFAULT_DIMENSION = 384
|
||||
|
|
@ -53,6 +67,12 @@ class QdrantClient(VectorDBBase):
|
|||
self.GRPC_PORT = QDRANT_GRPC_PORT
|
||||
self.QDRANT_TIMEOUT = QDRANT_TIMEOUT
|
||||
self.QDRANT_HNSW_M = QDRANT_HNSW_M
|
||||
self.QDRANT_HYBRID_SEARCH_ENABLED = QDRANT_HYBRID_SEARCH_ENABLED
|
||||
self.QDRANT_SPARSE_EMBEDDING_MODEL = QDRANT_SPARSE_EMBEDDING_MODEL
|
||||
self.QDRANT_DENSE_VECTOR_NAME = QDRANT_DENSE_VECTOR_NAME
|
||||
self.QDRANT_SPARSE_VECTOR_NAME = QDRANT_SPARSE_VECTOR_NAME
|
||||
self.QDRANT_HYBRID_SEARCH_RRF_K = QDRANT_HYBRID_SEARCH_RRF_K
|
||||
self.QDRANT_SPARSE_ON_DISK = QDRANT_SPARSE_ON_DISK
|
||||
|
||||
if not self.QDRANT_URI:
|
||||
raise ValueError('QDRANT_URI is not set. Please configure it in the environment variables.')
|
||||
|
|
@ -86,6 +106,42 @@ class QdrantClient(VectorDBBase):
|
|||
self.WEB_SEARCH_COLLECTION = f'{self.collection_prefix}_web-search'
|
||||
self.HASH_BASED_COLLECTION = f'{self.collection_prefix}_hash-based'
|
||||
|
||||
# Initialize sparse encoder if hybrid search is enabled
|
||||
self.sparse_encoder = None
|
||||
if self.QDRANT_HYBRID_SEARCH_ENABLED:
|
||||
if FASTEMBED_AVAILABLE:
|
||||
try:
|
||||
self.sparse_encoder = SparseTextEmbedding(model_name=self.QDRANT_SPARSE_EMBEDDING_MODEL)
|
||||
log.info(f'Hybrid search enabled with sparse model: {self.QDRANT_SPARSE_EMBEDDING_MODEL}')
|
||||
except Exception as e:
|
||||
log.warning(f'Failed to load sparse encoder, hybrid search disabled: {e}')
|
||||
else:
|
||||
log.warning(
|
||||
"fastembed not installed, hybrid search disabled. Install with: pip install 'fastembed>=0.6.1'"
|
||||
)
|
||||
@property
|
||||
def _hybrid_enabled(self) -> bool:
|
||||
return self.QDRANT_HYBRID_SEARCH_ENABLED and self.sparse_encoder is not None
|
||||
|
||||
def _is_hybrid_collection(self, mt_collection_name: str) -> bool:
|
||||
"""Check if an existing collection has sparse vectors (i.e. was created as hybrid)."""
|
||||
try:
|
||||
info = self.client.get_collection(mt_collection_name)
|
||||
return bool(info.config.params.sparse_vectors)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _encode_sparse(self, texts: list[str]) -> list[SparseVector]:
|
||||
"""Encode texts into sparse vectors using fastembed."""
|
||||
embeddings = list(self.sparse_encoder.embed(texts))
|
||||
return [SparseVector(indices=e.indices.tolist(), values=e.values.tolist()) for e in embeddings]
|
||||
|
||||
def _encode_sparse_query(self, text: str) -> SparseVector:
|
||||
"""Encode a query text into a sparse vector."""
|
||||
embeddings = list(self.sparse_encoder.query_embed(text))
|
||||
e = embeddings[0]
|
||||
return SparseVector(indices=e.indices.tolist(), values=e.values.tolist())
|
||||
|
||||
def _result_to_get_result(self, points) -> GetResult:
|
||||
ids, documents, metadatas = [], [], []
|
||||
for point in points:
|
||||
|
|
@ -134,21 +190,46 @@ class QdrantClient(VectorDBBase):
|
|||
"""
|
||||
Creates a collection with multi-tenancy configuration and payload indexes for tenant_id and metadata fields.
|
||||
"""
|
||||
self.client.create_collection(
|
||||
collection_name=mt_collection_name,
|
||||
vectors_config=models.VectorParams(
|
||||
size=dimension,
|
||||
distance=models.Distance.COSINE,
|
||||
on_disk=self.QDRANT_ON_DISK,
|
||||
),
|
||||
# Disable global index building due to multitenancy
|
||||
# For more details https://qdrant.tech/documentation/guides/multiple-partitions/#calibrate-performance
|
||||
hnsw_config=models.HnswConfigDiff(
|
||||
payload_m=self.QDRANT_HNSW_M,
|
||||
m=0,
|
||||
),
|
||||
)
|
||||
log.info(f'Multi-tenant collection {mt_collection_name} created with dimension {dimension}!')
|
||||
if self._hybrid_enabled:
|
||||
self.client.create_collection(
|
||||
collection_name=mt_collection_name,
|
||||
vectors_config={
|
||||
self.QDRANT_DENSE_VECTOR_NAME: models.VectorParams(
|
||||
size=dimension,
|
||||
distance=models.Distance.COSINE,
|
||||
on_disk=self.QDRANT_ON_DISK,
|
||||
)
|
||||
},
|
||||
sparse_vectors_config={
|
||||
self.QDRANT_SPARSE_VECTOR_NAME: models.SparseVectorParams(
|
||||
index=models.SparseIndexParams(on_disk=self.QDRANT_SPARSE_ON_DISK)
|
||||
)
|
||||
},
|
||||
hnsw_config=models.HnswConfigDiff(
|
||||
payload_m=self.QDRANT_HNSW_M,
|
||||
m=0,
|
||||
),
|
||||
)
|
||||
log.info(
|
||||
f'Multi-tenant hybrid collection {mt_collection_name} created with '
|
||||
f'dense ({dimension} dims) + sparse vectors'
|
||||
)
|
||||
else:
|
||||
self.client.create_collection(
|
||||
collection_name=mt_collection_name,
|
||||
vectors_config=models.VectorParams(
|
||||
size=dimension,
|
||||
distance=models.Distance.COSINE,
|
||||
on_disk=self.QDRANT_ON_DISK,
|
||||
),
|
||||
# Disable global index building due to multitenancy
|
||||
# For more details https://qdrant.tech/documentation/guides/multiple-partitions/#calibrate-performance
|
||||
hnsw_config=models.HnswConfigDiff(
|
||||
payload_m=self.QDRANT_HNSW_M,
|
||||
m=0,
|
||||
),
|
||||
)
|
||||
log.info(f'Multi-tenant collection {mt_collection_name} created with dimension {dimension}')
|
||||
|
||||
self.client.create_payload_index(
|
||||
collection_name=mt_collection_name,
|
||||
|
|
@ -170,22 +251,60 @@ class QdrantClient(VectorDBBase):
|
|||
),
|
||||
)
|
||||
|
||||
def _create_points(self, items: List[VectorItem], tenant_id: str) -> List[PointStruct]:
|
||||
def _create_points(
|
||||
self, items: list[VectorItem], tenant_id: str, collection_is_hybrid: bool = False
|
||||
) -> list[PointStruct]:
|
||||
"""
|
||||
Create point structs from vector items with tenant ID.
|
||||
Uses named vectors when collection_is_hybrid is True.
|
||||
If hybrid is also enabled in config, adds sparse vectors; otherwise dense-only named vector.
|
||||
"""
|
||||
return [
|
||||
PointStruct(
|
||||
id=item['id'],
|
||||
vector=item['vector'],
|
||||
payload={
|
||||
'text': item['text'],
|
||||
'metadata': item['metadata'],
|
||||
TENANT_ID_FIELD: tenant_id,
|
||||
},
|
||||
)
|
||||
for item in items
|
||||
]
|
||||
if collection_is_hybrid:
|
||||
if self._hybrid_enabled:
|
||||
texts = [item['text'] for item in items]
|
||||
sparse_vectors = self._encode_sparse(texts)
|
||||
return [
|
||||
PointStruct(
|
||||
id=item['id'],
|
||||
vector={
|
||||
self.QDRANT_DENSE_VECTOR_NAME: item['vector'],
|
||||
self.QDRANT_SPARSE_VECTOR_NAME: sparse_vec,
|
||||
},
|
||||
payload={
|
||||
'text': item['text'],
|
||||
'metadata': item['metadata'],
|
||||
TENANT_ID_FIELD: tenant_id,
|
||||
},
|
||||
)
|
||||
for item, sparse_vec in zip(items, sparse_vectors)
|
||||
]
|
||||
else:
|
||||
# Collection is hybrid but encoder unavailable — store only dense named vector
|
||||
return [
|
||||
PointStruct(
|
||||
id=item['id'],
|
||||
vector={self.QDRANT_DENSE_VECTOR_NAME: item['vector']},
|
||||
payload={
|
||||
'text': item['text'],
|
||||
'metadata': item['metadata'],
|
||||
TENANT_ID_FIELD: tenant_id,
|
||||
},
|
||||
)
|
||||
for item in items
|
||||
]
|
||||
else:
|
||||
return [
|
||||
PointStruct(
|
||||
id=item['id'],
|
||||
vector=item['vector'],
|
||||
payload={
|
||||
'text': item['text'],
|
||||
'metadata': item['metadata'],
|
||||
TENANT_ID_FIELD: tenant_id,
|
||||
},
|
||||
)
|
||||
for item in items
|
||||
]
|
||||
|
||||
def _ensure_collection(self, mt_collection_name: str, dimension: int = DEFAULT_DIMENSION):
|
||||
"""
|
||||
|
|
@ -245,9 +364,11 @@ class QdrantClient(VectorDBBase):
|
|||
vectors: List[List[float | int]],
|
||||
filter: Optional[Dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
"""
|
||||
Search for the nearest neighbor items based on the vectors with tenant isolation.
|
||||
Uses hybrid search (dense + sparse RRF) when enabled and query is provided.
|
||||
"""
|
||||
if not self.client or not vectors:
|
||||
return None
|
||||
|
|
@ -257,12 +378,42 @@ class QdrantClient(VectorDBBase):
|
|||
return None
|
||||
|
||||
tenant_filter = _tenant_filter(tenant_id)
|
||||
query_response = self.client.query_points(
|
||||
collection_name=mt_collection,
|
||||
query=vectors[0],
|
||||
limit=limit,
|
||||
query_filter=models.Filter(must=[tenant_filter]),
|
||||
)
|
||||
combined_filter = models.Filter(must=[tenant_filter])
|
||||
|
||||
collection_is_hybrid = self._is_hybrid_collection(mt_collection)
|
||||
|
||||
# Hybrid search with RRF fusion
|
||||
if collection_is_hybrid and self._hybrid_enabled and query:
|
||||
sparse_query = self._encode_sparse_query(query)
|
||||
query_response = self.client.query_points(
|
||||
collection_name=mt_collection,
|
||||
query=models.FusionQuery(fusion=models.Fusion.RRF),
|
||||
prefetch=[
|
||||
models.Prefetch(
|
||||
query=vectors[0],
|
||||
using=self.QDRANT_DENSE_VECTOR_NAME,
|
||||
filter=combined_filter,
|
||||
limit=limit * 2,
|
||||
),
|
||||
models.Prefetch(
|
||||
query=sparse_query,
|
||||
using=self.QDRANT_SPARSE_VECTOR_NAME,
|
||||
filter=combined_filter,
|
||||
limit=limit * 2,
|
||||
),
|
||||
],
|
||||
limit=limit,
|
||||
)
|
||||
else:
|
||||
# Dense-only search — use named vector if collection schema requires it
|
||||
query_vector = (self.QDRANT_DENSE_VECTOR_NAME, vectors[0]) if collection_is_hybrid else vectors[0]
|
||||
query_response = self.client.query_points(
|
||||
collection_name=mt_collection,
|
||||
query=query_vector,
|
||||
limit=limit,
|
||||
query_filter=combined_filter,
|
||||
)
|
||||
|
||||
get_result = self._result_to_get_result(query_response.points)
|
||||
return SearchResult(
|
||||
ids=get_result.ids,
|
||||
|
|
@ -320,7 +471,8 @@ class QdrantClient(VectorDBBase):
|
|||
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
|
||||
dimension = len(items[0]['vector'])
|
||||
self._ensure_collection(mt_collection, dimension)
|
||||
points = self._create_points(items, tenant_id)
|
||||
collection_is_hybrid = self._is_hybrid_collection(mt_collection)
|
||||
points = self._create_points(items, tenant_id, collection_is_hybrid)
|
||||
self.client.upload_points(mt_collection, points)
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@ class S3VectorClient(VectorDBBase):
|
|||
vectors: List[List[Union[float, int]]],
|
||||
filter: Optional[dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
"""
|
||||
Search for similar vectors in a collection using multiple query vectors.
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ class WeaviateClient(VectorDBBase):
|
|||
vectors: List[List[Union[float, int]]],
|
||||
filter: Optional[dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
sane_collection_name = self._sanitize_collection_name(collection_name)
|
||||
if not self.client.collections.exists(sane_collection_name):
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ class VectorDBBase(ABC):
|
|||
vectors: List[List[Union[float, int]]],
|
||||
filter: Optional[Dict] = None,
|
||||
limit: int = 10,
|
||||
query: Optional[str] = None,
|
||||
) -> Optional[SearchResult]:
|
||||
"""Search for similar vectors in a collection."""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ async def query_memory(
|
|||
collection_name=f'user-memory-{user.id}',
|
||||
vectors=[vector],
|
||||
limit=form_data.k,
|
||||
query=form_data.content,
|
||||
)
|
||||
|
||||
return results
|
||||
|
|
|
|||
|
|
@ -2409,6 +2409,7 @@ async def query_doc_handler(
|
|||
query_embedding=query_embedding,
|
||||
k=form_data.k if form_data.k else request.app.state.config.TOP_K,
|
||||
user=user,
|
||||
query_text=form_data.query,
|
||||
)
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
|
|
|
|||
|
|
@ -2221,6 +2221,7 @@ async def query_knowledge_bases(
|
|||
vectors=[query_embedding],
|
||||
filter={'knowledge_base_id': {'$in': accessible_ids}},
|
||||
limit=count,
|
||||
query=query,
|
||||
)
|
||||
|
||||
if search_results and search_results.ids and search_results.ids[0]:
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ boto3==1.42.62
|
|||
|
||||
pymilvus==2.6.9
|
||||
qdrant-client==1.17.0
|
||||
fastembed==0.8.0 # fastembed for qdrant SparseTextEmbedding
|
||||
playwright==1.58.0 # Caution: version must match docker-compose.playwright.yaml - Update the docker-compose.yaml if necessary
|
||||
elasticsearch==9.3.0
|
||||
pinecone==6.0.2
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue