mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 13:29:52 +08:00
fix: disable chroma server-side embedding functions
This commit is contained in:
@ -37,6 +37,7 @@ from lfx.base.knowledge_bases.ingestion_sources import (
|
||||
KBIngestionSource,
|
||||
)
|
||||
from lfx.base.knowledge_bases.ingestion_sources.base import IngestionItemStatus, IngestionRunStatus
|
||||
from lfx.base.vectorstores.chroma_security import chroma_langchain_collection_kwargs
|
||||
from lfx.components.models_and_agents.embedding_model import EmbeddingModelComponent
|
||||
from lfx.log import logger
|
||||
|
||||
@ -192,7 +193,7 @@ class KBStorageHelper:
|
||||
has_data = any((kb_path / m).exists() for m in ["chroma", "chroma.sqlite3", "index"])
|
||||
if has_data:
|
||||
client = KBStorageHelper.get_fresh_chroma_client(kb_path)
|
||||
chroma = Chroma(client=client, collection_name=kb_name)
|
||||
chroma = Chroma(client=client, collection_name=kb_name, **chroma_langchain_collection_kwargs())
|
||||
with contextlib.suppress(Exception):
|
||||
chroma.delete_collection()
|
||||
chroma = None
|
||||
@ -419,7 +420,7 @@ class KBAnalysisHelper:
|
||||
try:
|
||||
if created_locally:
|
||||
client = KBStorageHelper.get_fresh_chroma_client(kb_path)
|
||||
chroma = Chroma(client=client, collection_name=kb_path.name)
|
||||
chroma = Chroma(client=client, collection_name=kb_path.name, **chroma_langchain_collection_kwargs())
|
||||
|
||||
if chroma is None:
|
||||
return
|
||||
|
||||
@ -21,6 +21,7 @@ from lfx.base.knowledge_bases.ingestion_sources import (
|
||||
get_source_class,
|
||||
registered_sources,
|
||||
)
|
||||
from lfx.base.vectorstores.chroma_security import chroma_client_create_collection_kwargs
|
||||
from lfx.log import logger
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
@ -588,7 +589,7 @@ async def create_knowledge_base(
|
||||
# This ensures files exist for read operations and avoids 'readonly' errors later
|
||||
try:
|
||||
client = KBStorageHelper.get_fresh_chroma_client(kb_path)
|
||||
client.create_collection(name=kb_name)
|
||||
client.create_collection(name=kb_name, **chroma_client_create_collection_kwargs())
|
||||
except (OSError, ValueError, chromadb.errors.ChromaError) as e:
|
||||
logger.warning("Initial Chroma setup for %s failed: %s", kb_name, e)
|
||||
finally:
|
||||
|
||||
@ -13,6 +13,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
import chromadb.errors
|
||||
from langchain_chroma import Chroma
|
||||
from lfx.base.vectorstores.chroma_security import chroma_langchain_collection_kwargs
|
||||
from lfx.log.logger import logger
|
||||
from sqlmodel import col, func, select
|
||||
|
||||
@ -431,7 +432,12 @@ async def _delete_chunks_for_session(
|
||||
|
||||
client = KBStorageHelper.get_fresh_chroma_client(kb_path)
|
||||
try:
|
||||
chroma = Chroma(client=client, embedding_function=embeddings, collection_name=kb_name)
|
||||
chroma = Chroma(
|
||||
client=client,
|
||||
embedding_function=embeddings,
|
||||
collection_name=kb_name,
|
||||
**chroma_langchain_collection_kwargs(),
|
||||
)
|
||||
await chroma.adelete(where={"session_id": {"$eq": session_id}})
|
||||
# Refresh on-disk metrics so the UI reflects the post-purge state.
|
||||
try:
|
||||
|
||||
@ -13,6 +13,7 @@ import uuid
|
||||
from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from lfx.base.vectorstores.chroma_security import chroma_client_create_collection_kwargs
|
||||
from lfx.log.logger import logger
|
||||
from sqlmodel import select
|
||||
|
||||
@ -108,7 +109,7 @@ async def initialize_kb(
|
||||
# Initialize Chroma collection so the directory is non-empty and readable
|
||||
try:
|
||||
client = KBStorageHelper.get_fresh_chroma_client(kb_path)
|
||||
client.create_collection(name=kb_name)
|
||||
client.create_collection(name=kb_name, **chroma_client_create_collection_kwargs())
|
||||
except (OSError, ValueError, chromadb.errors.ChromaError) as exc:
|
||||
await logger.awarning("Initial Chroma setup for %s failed: %s", kb_name, exc)
|
||||
finally:
|
||||
|
||||
@ -29,6 +29,7 @@ from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from langchain_chroma import Chroma
|
||||
from lfx.base.vectorstores.chroma_security import chroma_langchain_collection_kwargs
|
||||
from lfx.log.logger import logger
|
||||
from sqlalchemy import text
|
||||
from sqlmodel import Session, col, select
|
||||
@ -386,7 +387,12 @@ async def ingest_memory_task(*, request: IngestionRequest) -> dict:
|
||||
client = KBStorageHelper.get_fresh_chroma_client(kb_path)
|
||||
written = 0
|
||||
try:
|
||||
chroma = Chroma(client=client, embedding_function=embeddings, collection_name=kb_name)
|
||||
chroma = Chroma(
|
||||
client=client,
|
||||
embedding_function=embeddings,
|
||||
collection_name=kb_name,
|
||||
**chroma_langchain_collection_kwargs(),
|
||||
)
|
||||
|
||||
written = await KBIngestionHelper.write_documents_to_chroma(
|
||||
documents=documents,
|
||||
|
||||
@ -318,11 +318,18 @@ class TestChromaCloudMode:
|
||||
with (
|
||||
patch("chromadb.CloudClient", return_value=mock_client) as mock_cloud,
|
||||
patch("chromadb.PersistentClient") as mock_local,
|
||||
patch("lfx.base.knowledge_bases.backends.chroma.Chroma", return_value=MagicMock()) as mock_chroma,
|
||||
):
|
||||
bk._build_vector_store()
|
||||
|
||||
mock_cloud.assert_called_once()
|
||||
mock_local.assert_not_called()
|
||||
mock_chroma.assert_called_once_with(
|
||||
client=mock_client,
|
||||
collection_name="cloud_test_kb",
|
||||
embedding_function=bk.embedding_function,
|
||||
collection_configuration={"embedding_function": None},
|
||||
)
|
||||
|
||||
def test_get_cloud_client_passes_optional_host_port(self, tmp_path: Path):
|
||||
from unittest.mock import patch
|
||||
|
||||
@ -3,6 +3,10 @@ from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from lfx.base.vectorstores.chroma_security import (
|
||||
chroma_client_create_collection_kwargs,
|
||||
chroma_langchain_collection_kwargs,
|
||||
)
|
||||
from lfx.components.chroma import ChromaVectorStoreComponent
|
||||
from lfx.schema.data import Data
|
||||
|
||||
@ -37,9 +41,29 @@ def test_remote_chroma_server_uses_http_client() -> None:
|
||||
client=mock_client,
|
||||
embedding_function=None,
|
||||
collection_name="remote_collection",
|
||||
collection_configuration={"embedding_function": None},
|
||||
)
|
||||
|
||||
|
||||
def test_chroma_collection_security_kwargs_disable_server_side_embedding_functions() -> None:
|
||||
assert chroma_langchain_collection_kwargs() == {
|
||||
"collection_configuration": {"embedding_function": None},
|
||||
}
|
||||
assert chroma_client_create_collection_kwargs() == {
|
||||
"configuration": {"embedding_function": None},
|
||||
"embedding_function": None,
|
||||
}
|
||||
|
||||
|
||||
def test_chroma_collection_security_kwargs_are_fresh_dicts() -> None:
|
||||
first = chroma_langchain_collection_kwargs()
|
||||
first["collection_configuration"]["embedding_function"] = "unsafe"
|
||||
|
||||
assert chroma_langchain_collection_kwargs() == {
|
||||
"collection_configuration": {"embedding_function": None},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.api_key_required
|
||||
class TestChromaVectorStoreComponent(ComponentTestBaseWithoutClient):
|
||||
@pytest.fixture
|
||||
|
||||
@ -223,6 +223,11 @@ class TestKnowledgeBaseAPI:
|
||||
assert data["name"] == "New KB"
|
||||
assert data["backend_type"] == "opensearch"
|
||||
assert data["backend_config"] == {"index_name": "new_kb_index"}
|
||||
mock_fresh_client.return_value.create_collection.assert_called_once_with(
|
||||
name=kb_name,
|
||||
configuration={"embedding_function": None},
|
||||
embedding_function=None,
|
||||
)
|
||||
record = await knowledge_base_service.get_by_user_and_name(active_user.id, kb_name)
|
||||
assert record is not None
|
||||
assert record.model_selection == model_selection
|
||||
|
||||
@ -36,6 +36,7 @@ from lfx.base.knowledge_bases.backends.base import (
|
||||
IngestedDocument,
|
||||
TestConnectionResult,
|
||||
)
|
||||
from lfx.base.vectorstores.chroma_security import chroma_langchain_collection_kwargs
|
||||
from lfx.log.logger import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -110,6 +111,7 @@ class ChromaLocalBackend(BaseVectorStoreBackend):
|
||||
client=self._client,
|
||||
collection_name=self.kb_name,
|
||||
embedding_function=self.embedding_function,
|
||||
**chroma_langchain_collection_kwargs(),
|
||||
)
|
||||
|
||||
# ---- overrides --------------------------------------------------------
|
||||
@ -304,6 +306,7 @@ class ChromaCloudBackend(BaseVectorStoreBackend):
|
||||
client=self._client,
|
||||
collection_name=self.kb_name,
|
||||
embedding_function=self.embedding_function,
|
||||
**chroma_langchain_collection_kwargs(),
|
||||
)
|
||||
|
||||
# ---- overrides --------------------------------------------------------
|
||||
|
||||
28
src/lfx/src/lfx/base/vectorstores/chroma_security.py
Normal file
28
src/lfx/src/lfx/base/vectorstores/chroma_security.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""Safe defaults for Langflow-created Chroma collections.
|
||||
|
||||
Chroma collection configuration can persist server-side embedding functions.
|
||||
Some embedding functions accept model-loading kwargs such as trust_remote_code.
|
||||
Langflow embeds client-side, so built-in collection creation should not register
|
||||
server-side embedding code.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
def chroma_langchain_collection_kwargs() -> dict[str, Any]:
|
||||
"""Return LangChain Chroma kwargs that disable Chroma server-side embedding code."""
|
||||
return {"collection_configuration": _chroma_collection_configuration_without_embedding_function()}
|
||||
|
||||
|
||||
def chroma_client_create_collection_kwargs() -> dict[str, Any]:
|
||||
"""Return chromadb client kwargs that create collections without server-side embedding code."""
|
||||
return {
|
||||
"configuration": _chroma_collection_configuration_without_embedding_function(),
|
||||
"embedding_function": None,
|
||||
}
|
||||
|
||||
|
||||
def _chroma_collection_configuration_without_embedding_function() -> dict[str, Any]:
|
||||
return {"embedding_function": None}
|
||||
@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
|
||||
from langchain_chroma import Chroma
|
||||
from typing_extensions import override
|
||||
|
||||
from lfx.base.vectorstores.chroma_security import chroma_langchain_collection_kwargs
|
||||
from lfx.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store
|
||||
from lfx.base.vectorstores.utils import chroma_collection_to_data
|
||||
from lfx.inputs.inputs import BoolInput, DropdownInput, HandleInput, IntInput, StrInput
|
||||
@ -119,6 +120,7 @@ class ChromaVectorStoreComponent(LCVectorStoreComponent):
|
||||
client=client,
|
||||
embedding_function=self.embedding,
|
||||
collection_name=self.collection_name,
|
||||
**chroma_langchain_collection_kwargs(),
|
||||
)
|
||||
except Exception as e:
|
||||
if isinstance(e, ChromaError):
|
||||
|
||||
@ -39,6 +39,7 @@ from lfx.base.knowledge_bases.ingestion_sources.base import (
|
||||
from lfx.base.knowledge_bases.ingestion_sources.flow_component import FlowComponentSource
|
||||
from lfx.base.knowledge_bases.knowledge_base_utils import get_knowledge_bases
|
||||
from lfx.base.models.unified_models import get_embedding_model_options, get_embeddings
|
||||
from lfx.base.vectorstores.chroma_security import chroma_langchain_collection_kwargs
|
||||
from lfx.components.files_and_knowledge._kb_paths import (
|
||||
get_knowledge_bases_root_path as _get_knowledge_bases_root_path,
|
||||
)
|
||||
@ -1030,6 +1031,7 @@ class KnowledgeComponent(Component):
|
||||
chroma = Chroma(
|
||||
persist_directory=str(kb_path),
|
||||
collection_name=self.knowledge_base,
|
||||
**chroma_langchain_collection_kwargs(),
|
||||
)
|
||||
|
||||
all_docs = chroma.get()
|
||||
|
||||
@ -20,6 +20,7 @@ from langflow.services.database.models.user.crud import get_user_by_id
|
||||
from langflow.services.memory_base.kb_path_helpers import hash_session_id, validate_kb_path
|
||||
from sqlmodel import select
|
||||
|
||||
from lfx.base.vectorstores.chroma_security import chroma_langchain_collection_kwargs
|
||||
from lfx.components.files_and_knowledge._kb_paths import (
|
||||
get_knowledge_bases_root_path,
|
||||
load_kb_metadata,
|
||||
@ -234,6 +235,7 @@ class MemoryBaseComponent(Component):
|
||||
persist_directory=str(kb_path),
|
||||
embedding_function=embedding_function,
|
||||
collection_name=kb_name,
|
||||
**chroma_langchain_collection_kwargs(),
|
||||
)
|
||||
|
||||
def _format_results(self, results: list[tuple]) -> DataFrame:
|
||||
|
||||
@ -4,6 +4,7 @@ from pathlib import Path
|
||||
from langchain_chroma import Chroma
|
||||
from typing_extensions import override
|
||||
|
||||
from lfx.base.vectorstores.chroma_security import chroma_langchain_collection_kwargs
|
||||
from lfx.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store
|
||||
from lfx.base.vectorstores.utils import chroma_collection_to_data
|
||||
from lfx.inputs.inputs import MultilineInput
|
||||
@ -226,6 +227,7 @@ class LocalDBComponent(LCVectorStoreComponent):
|
||||
client=None,
|
||||
embedding_function=self.embedding,
|
||||
collection_name=self.collection_name,
|
||||
**chroma_langchain_collection_kwargs(),
|
||||
)
|
||||
|
||||
self._add_documents_to_vector_store(chroma)
|
||||
|
||||
Reference in New Issue
Block a user