-
Notifications
You must be signed in to change notification settings - Fork 8.5k
feat: make knowledge basis settings accessible by global variables #9981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
lucaseduoli
wants to merge
54
commits into
main
Choose a base branch
from
global-llm
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 1 commit
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
b453a72
feat: add category column to variable model and validation logic
ogabrielluiz dcf8d0c
feat: add LLM loading and utility functions
ogabrielluiz ee37cc8
feat: enhance variable management with category support and LLM settings
ogabrielluiz b1b862a
feat: implement async LLM generation and loading in Component
ogabrielluiz 5699903
test: add unit and integration tests for LLM loading functionality
ogabrielluiz a9b0d26
feat: add hook to retrieve variables by category and enhance global v…
ogabrielluiz 4890a45
feat: add LLM settings page and integrate into settings navigation
ogabrielluiz f183436
feat: enhance validation for LLM settings in DatabaseVariableService
ogabrielluiz 2dda874
feat: add async method to retrieve global LLM instance
ogabrielluiz 205f9b1
feat: add nullable category column to variable table in migration script
ogabrielluiz 0fcb3d8
fix: update down_revision in migration script for category column add…
ogabrielluiz 9fc688c
Merge branch 'main' into global-llm
ogabrielluiz 7408646
refactor: remove unused LLM attribute from Component class
ogabrielluiz 0dddf96
feat: add abstract method for retrieving variables by category
ogabrielluiz 320f8ad
feat: add CATEGORY_KB to valid categories in constants
ogabrielluiz 30effce
feat: add non-nullable category column to variable table
ogabrielluiz 9ff9226
chore: clean up package-lock.json by removing deprecated and unused d…
ogabrielluiz a9a9e02
feat: replace LLMSettingsPage with KBSettingsPage for Knowledge Base …
ogabrielluiz 7825f52
feat: implement vector store factory for Knowledge Bases
ogabrielluiz 6c19fa1
feat: add provider-aware metadata adapters for Knowledge Bases
ogabrielluiz 45a6cfa
feat: enhance metadata extraction for Knowledge Bases with user-aware…
ogabrielluiz 6a42bc5
feat: refactor vector store integration for Knowledge Bases
ogabrielluiz ba9a795
feat: add unit tests for knowledge bases functionality
ogabrielluiz 51da64d
feat: add unit tests for knowledge bases functionality
ogabrielluiz 4c4e15e
Merge branch 'main' into global-llm
ogabrielluiz d33858b
feat: enhance OpenSearch vector store integration with new adapter an…
ogabrielluiz dd95e47
fix: improve OpenSearch metadata adapter functionality and error hand…
ogabrielluiz 07866f5
refactor: enhance OpenSearch with classmethods
ogabrielluiz 74bf0d2
chore: add blank line for improved readability in test file
ogabrielluiz de4cdf2
refactor: update OpenSearch vector store tests to use new adapter
ogabrielluiz 0227bd8
refactor: update OpenSearch vector store tests to use real adapter
ogabrielluiz d62f78e
Merge branch 'main' of https://github.com/langflow-ai/langflow into g…
deon-sanchez 4a658ca
chore: update component index
github-actions[bot] a8ee772
Merge branch 'main' into global-llm
deon-sanchez 0e605b4
chore: update component index
github-actions[bot] 7739c56
Update src/backend/base/langflow/alembic/versions/e5fc330efa7c_add_ca…
erichare 1bb4bb0
Update src/frontend/src/pages/SettingsPage/KBSettingsPage.tsx
erichare b65362b
Update src/backend/tests/unit/base/knowledge_bases/test_database_fact…
erichare 7d3fcb2
Update src/frontend/src/pages/SettingsPage/KBSettingsPage.tsx
erichare 11d58c2
chore: update component index
github-actions[bot] ebaa39d
Merge branch 'main' into global-llm
erichare c8062cc
Update src/backend/tests/unit/base/knowledge_bases/test_metadata_adap…
erichare 763880d
Update src/backend/tests/unit/base/knowledge_bases/test_database_fact…
erichare 2dcc710
chore: update component index
github-actions[bot] 5b224c7
Update vector_store_factory.py
erichare db4fd7c
chore: update component index
github-actions[bot] 8b8f97c
Update KBSettingsPage.tsx
erichare d328c49
[autofix.ci] apply automated fixes
autofix-ci[bot] 069d157
Update e5fc330efa7c_add_category_column_in_variable.py
erichare d6422c5
chore: update component index
github-actions[bot] 99fce7f
Update e5fc330efa7c_add_category_column_in_variable.py
erichare 86c7b8b
chore: update component index
github-actions[bot] 54f8f19
Merge branch 'main' into global-llm
erichare 0734ced
chore: update component index
github-actions[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat: add unit tests for knowledge bases functionality
Introduced new unit tests for knowledge bases, including tests for database-driven vector store factory, enhanced metadata adapters, and KB variable service integration. These tests improve coverage and ensure robust functionality across various components, enhancing the reliability of knowledge base management. The additions include tests for creating and retrieving metadata, handling different vector store providers, and validating KB variable structures.
- Loading branch information
commit ba9a795d595f0f7ed58b476f6ded2562d8cae947
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| """Tests for knowledge bases functionality.""" |
340 changes: 340 additions & 0 deletions
340
src/backend/tests/unit/base/knowledge_bases/test_database_factory.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,340 @@ | ||
| """Tests for database-driven vector store factory.""" | ||
|
|
||
| import tempfile | ||
| from pathlib import Path | ||
| from unittest.mock import AsyncMock, Mock, patch | ||
| from uuid import uuid4 | ||
|
|
||
| import pytest | ||
| from langflow.base.knowledge_bases.vector_store_factory import ( | ||
| ChromaVectorStoreAdapter, | ||
| MockOpenSearchVectorStore, | ||
| build_kb_vector_store, | ||
| ) | ||
|
|
||
|
|
||
| class TestVectorStoreFactory: | ||
| """Test the database-driven vector store factory.""" | ||
|
|
||
| @pytest.fixture | ||
| def mock_session(self): | ||
| """Mock database session.""" | ||
| return AsyncMock() | ||
|
|
||
| @pytest.fixture | ||
| def user_id(self): | ||
| """Test user ID.""" | ||
| return uuid4() | ||
|
|
||
| @pytest.fixture | ||
| def kb_path(self): | ||
| """Test knowledge base path.""" | ||
| with tempfile.TemporaryDirectory() as temp_dir: | ||
| yield Path(temp_dir) | ||
|
|
||
| @pytest.fixture | ||
| def mock_variable_service(self): | ||
| """Mock variable service.""" | ||
| return AsyncMock() | ||
|
|
||
| @pytest.mark.asyncio | ||
| @patch("langflow.base.knowledge_bases.vector_store_factory.get_variable_service") | ||
| async def test_build_chroma_store_default( | ||
| self, mock_get_service, mock_variable_service, mock_session, user_id, kb_path | ||
| ): | ||
| """Test building Chroma store with default configuration (no variables).""" | ||
| # Setup mocks | ||
| mock_get_service.return_value = mock_variable_service | ||
| mock_variable_service.get_by_category.return_value = [] # No KB variables | ||
|
|
||
| with patch("langchain_chroma.Chroma") as mock_chroma: | ||
| mock_chroma_instance = Mock() | ||
| mock_chroma.return_value = mock_chroma_instance | ||
|
|
||
| # Call factory | ||
| result = await build_kb_vector_store( | ||
| kb_path=kb_path, | ||
| collection_name="test_collection", | ||
| embedding_function=None, | ||
| user_id=user_id, | ||
| session=mock_session, | ||
| ) | ||
|
|
||
| # Verify result | ||
| assert isinstance(result, ChromaVectorStoreAdapter) | ||
| assert result._store == mock_chroma_instance | ||
|
|
||
| # Verify Chroma was called with correct parameters | ||
| mock_chroma.assert_called_once_with( | ||
| persist_directory=str(kb_path), | ||
| collection_name="test_collection", | ||
| embedding_function=None, | ||
| client_settings=None, | ||
| ) | ||
|
|
||
| @pytest.mark.asyncio | ||
| @patch("langflow.base.knowledge_bases.vector_store_factory.get_variable_service") | ||
| async def test_build_opensearch_store( | ||
| self, mock_get_service, mock_variable_service, mock_session, user_id, kb_path | ||
| ): | ||
| """Test building OpenSearch store with configuration.""" | ||
| # Setup KB variables for OpenSearch | ||
| mock_var1 = Mock() | ||
| mock_var1.name = "kb_provider" | ||
| mock_var1.value = "opensearch" | ||
|
|
||
| mock_var2 = Mock() | ||
| mock_var2.name = "kb_opensearch_url" | ||
| mock_var2.value = "https://localhost:9200" | ||
|
|
||
| mock_var3 = Mock() | ||
| mock_var3.name = "kb_opensearch_index_prefix" | ||
| mock_var3.value = "test-" | ||
|
|
||
| mock_variables = [mock_var1, mock_var2, mock_var3] | ||
|
|
||
| mock_get_service.return_value = mock_variable_service | ||
| mock_variable_service.get_by_category.return_value = mock_variables | ||
|
|
||
| # Call factory | ||
| result = await build_kb_vector_store( | ||
| kb_path=kb_path, | ||
| collection_name="test_collection", | ||
| embedding_function=None, | ||
| user_id=user_id, | ||
| session=mock_session, | ||
| ) | ||
|
|
||
| # Verify result | ||
| assert isinstance(result, MockOpenSearchVectorStore) | ||
| assert result.opensearch_url == "https://localhost:9200" | ||
| assert result.index_name == "test-test_collection" | ||
|
|
||
| @pytest.mark.asyncio | ||
| @patch("langflow.base.knowledge_bases.vector_store_factory.get_variable_service") | ||
| async def test_build_chroma_with_server_config( | ||
| self, mock_get_service, mock_variable_service, mock_session, user_id, kb_path | ||
| ): | ||
| """Test building Chroma store with server configuration.""" | ||
| # Setup KB variables for Chroma server | ||
| mock_var1 = Mock() | ||
| mock_var1.name = "kb_provider" | ||
| mock_var1.value = "chroma" | ||
|
|
||
| mock_var2 = Mock() | ||
| mock_var2.name = "kb_chroma_server_host" | ||
| mock_var2.value = "localhost" | ||
|
|
||
| mock_var3 = Mock() | ||
| mock_var3.name = "kb_chroma_server_http_port" | ||
| mock_var3.value = "8000" | ||
|
|
||
| mock_var4 = Mock() | ||
| mock_var4.name = "kb_chroma_server_ssl_enabled" | ||
| mock_var4.value = "true" | ||
|
|
||
| mock_variables = [mock_var1, mock_var2, mock_var3, mock_var4] | ||
|
|
||
| mock_get_service.return_value = mock_variable_service | ||
| mock_variable_service.get_by_category.return_value = mock_variables | ||
|
|
||
| with ( | ||
| patch("langchain_chroma.Chroma") as mock_chroma, | ||
| patch("chromadb.HttpClient") as mock_http_client, | ||
| ): | ||
| mock_chroma_instance = Mock() | ||
| mock_chroma.return_value = mock_chroma_instance | ||
| mock_client_instance = Mock() | ||
| mock_http_client.return_value = mock_client_instance | ||
erichare marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # Call factory | ||
| result = await build_kb_vector_store( | ||
| kb_path=kb_path, | ||
| collection_name="test_collection", | ||
| embedding_function=None, | ||
| user_id=user_id, | ||
| session=mock_session, | ||
| ) | ||
|
|
||
| # Verify result | ||
| assert isinstance(result, ChromaVectorStoreAdapter) | ||
|
|
||
| # Verify HttpClient was created with correct parameters | ||
| mock_http_client.assert_called_once_with( | ||
| host="localhost", | ||
| port=8000, | ||
| ssl=True, | ||
| ) | ||
|
|
||
| # Verify Chroma was called with client | ||
| mock_chroma.assert_called_once_with( | ||
| client=mock_client_instance, | ||
| collection_name="test_collection", | ||
| embedding_function=None, | ||
| ) | ||
|
|
||
| @pytest.mark.asyncio | ||
| @patch("langflow.base.knowledge_bases.vector_store_factory.get_variable_service") | ||
| async def test_unsupported_provider_raises_error( | ||
| self, mock_get_service, mock_variable_service, mock_session, user_id, kb_path | ||
| ): | ||
| """Test that unsupported provider raises ValueError.""" | ||
| # Setup KB variables with unsupported provider | ||
| mock_var1 = Mock() | ||
| mock_var1.name = "kb_provider" | ||
| mock_var1.value = "unsupported_provider" | ||
|
|
||
| mock_variables = [mock_var1] | ||
|
|
||
| mock_get_service.return_value = mock_variable_service | ||
| mock_variable_service.get_by_category.return_value = mock_variables | ||
|
|
||
| # Call factory and expect error | ||
| with pytest.raises(ValueError, match="Unsupported vector store provider: unsupported_provider"): | ||
| await build_kb_vector_store( | ||
| kb_path=kb_path, | ||
| collection_name="test_collection", | ||
| embedding_function=None, | ||
| user_id=user_id, | ||
| session=mock_session, | ||
| ) | ||
|
|
||
| @pytest.mark.asyncio | ||
| @patch("langflow.base.knowledge_bases.vector_store_factory.get_variable_service") | ||
| async def test_variable_service_called_correctly( | ||
| self, mock_get_service, mock_variable_service, mock_session, user_id, kb_path | ||
| ): | ||
| """Test that variable service is called with correct parameters.""" | ||
| mock_get_service.return_value = mock_variable_service | ||
| mock_variable_service.get_by_category.return_value = [] | ||
|
|
||
| with patch("langchain_chroma.Chroma"): | ||
| await build_kb_vector_store( | ||
erichare marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| kb_path=kb_path, | ||
| collection_name="test_collection", | ||
| embedding_function=None, | ||
| user_id=user_id, | ||
| session=mock_session, | ||
| ) | ||
|
|
||
| # Verify variable service was called correctly | ||
| mock_get_service.assert_called_once() | ||
| mock_variable_service.get_by_category.assert_called_once_with(user_id, "KB", mock_session) | ||
|
|
||
|
|
||
| class TestChromaVectorStoreAdapter: | ||
| """Test ChromaVectorStoreAdapter.""" | ||
|
|
||
| @pytest.fixture | ||
| def mock_chroma_store(self): | ||
| """Mock LangChain Chroma store.""" | ||
| return Mock() | ||
|
|
||
| @pytest.fixture | ||
| def adapter(self, mock_chroma_store): | ||
| """ChromaVectorStoreAdapter instance.""" | ||
| return ChromaVectorStoreAdapter(mock_chroma_store) | ||
|
|
||
| def test_adapter_delegates_get(self, adapter, mock_chroma_store): | ||
| """Test that adapter delegates get() to underlying store.""" | ||
| mock_chroma_store.get.return_value = {"documents": ["test"]} | ||
|
|
||
| result = adapter.get(include=["documents"]) | ||
|
|
||
| assert result == {"documents": ["test"]} | ||
| mock_chroma_store.get.assert_called_once_with(include=["documents"]) | ||
|
|
||
| def test_adapter_delegates_add_documents(self, adapter, mock_chroma_store): | ||
| """Test that adapter delegates add_documents() to underlying store.""" | ||
| mock_documents = [Mock(), Mock()] | ||
| mock_chroma_store.add_documents.return_value = ["id1", "id2"] | ||
|
|
||
| result = adapter.add_documents(mock_documents) | ||
|
|
||
| assert result == ["id1", "id2"] | ||
| mock_chroma_store.add_documents.assert_called_once_with(mock_documents) | ||
|
|
||
| def test_adapter_delegates_similarity_search(self, adapter, mock_chroma_store): | ||
| """Test that adapter delegates similarity_search() to underlying store.""" | ||
| mock_results = [Mock(), Mock()] | ||
| mock_chroma_store.similarity_search.return_value = mock_results | ||
|
|
||
| result = adapter.similarity_search("test query", k=5) | ||
|
|
||
| assert result == mock_results | ||
| mock_chroma_store.similarity_search.assert_called_once_with("test query", k=5) | ||
|
|
||
| def test_adapter_exposes_chroma_attributes(self, adapter, mock_chroma_store): | ||
| """Test that adapter exposes Chroma-specific attributes.""" | ||
| mock_collection = Mock() | ||
| mock_client = Mock() | ||
| mock_chroma_store._collection = mock_collection | ||
| mock_chroma_store._client = mock_client | ||
|
|
||
| assert adapter._collection == mock_collection | ||
| assert adapter._client == mock_client | ||
|
|
||
|
|
||
| class TestMockOpenSearchVectorStore: | ||
| """Test MockOpenSearchVectorStore.""" | ||
|
|
||
| @pytest.fixture | ||
| def mock_store(self): | ||
| """MockOpenSearchVectorStore instance.""" | ||
| return MockOpenSearchVectorStore(opensearch_url="https://localhost:9200", index_name="test-index") | ||
|
|
||
| def test_mock_store_initialization(self, mock_store): | ||
| """Test mock store initializes correctly.""" | ||
| assert mock_store.opensearch_url == "https://localhost:9200" | ||
| assert mock_store.index_name == "test-index" | ||
| assert mock_store._documents == [] | ||
|
|
||
| def test_mock_store_add_documents(self, mock_store): | ||
| """Test adding documents to mock store.""" | ||
| mock_doc1 = Mock() | ||
| mock_doc1.page_content = "Document 1 content" | ||
| mock_doc1.metadata = {"source": "doc1"} | ||
|
|
||
| mock_doc2 = Mock() | ||
| mock_doc2.page_content = "Document 2 content" | ||
| mock_doc2.metadata = {"source": "doc2"} | ||
|
|
||
| doc_ids = mock_store.add_documents([mock_doc1, mock_doc2]) | ||
|
|
||
| assert len(doc_ids) == 2 | ||
| assert doc_ids == ["doc_0", "doc_1"] | ||
| assert len(mock_store._documents) == 2 | ||
| assert mock_store._documents[0]["content"] == "Document 1 content" | ||
| assert mock_store._documents[1]["content"] == "Document 2 content" | ||
|
|
||
| def test_mock_store_similarity_search(self, mock_store): | ||
| """Test similarity search on mock store.""" | ||
| # Add some documents first | ||
| mock_doc = Mock() | ||
| mock_doc.page_content = "Test content" | ||
| mock_doc.metadata = {"source": "test"} | ||
| mock_store.add_documents([mock_doc]) | ||
|
|
||
| results = mock_store.similarity_search("query", k=1) | ||
|
|
||
| assert len(results) == 1 | ||
| # Results should be Data objects | ||
| assert hasattr(results[0], "data") | ||
|
|
||
| def test_mock_store_get_method(self, mock_store): | ||
| """Test get method on mock store.""" | ||
| # Add a document first | ||
| mock_doc = Mock() | ||
| mock_doc.page_content = "Test content" | ||
| mock_doc.metadata = {"key": "value"} | ||
| mock_store.add_documents([mock_doc]) | ||
|
|
||
| result = mock_store.get(include=["documents", "metadatas", "ids"]) | ||
|
|
||
| assert "documents" in result | ||
| assert "metadatas" in result | ||
| assert "ids" in result | ||
| assert len(result["documents"]) == 1 | ||
| assert result["documents"][0] == "Test content" | ||
| assert result["metadatas"][0] == {"key": "value"} | ||
| assert result["ids"][0] == "doc_0" | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Patch the symbol at the import site (factory), not the library module.
Patching
langchain_chroma.Chromawon’t affect the symbol already imported in the factory. PatchChromawhere it’s used:langflow.base.knowledge_bases.vector_store_factory.Chroma.Apply this diff:
📝 Committable suggestion
🤖 Prompt for AI Agents