-
Notifications
You must be signed in to change notification settings - Fork 8.2k
fix(pinecone): issue #10512 Pinecone vector store returns zero results #10602
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
base: main
Are you sure you want to change the base?
Conversation
…o results in Langflow 1.6.5 when using namespaces. **Root Cause:** `similarity_search()` method was not properly passing the namespace parameter to the underlying Pinecone query, resulting in searches against the wrong namespace and returning zero results. **Solution:** - Modified `search_documents()` to query Pinecone directly using the native Pinecone client - Explicitly pass namespace parameter to `index.query()` when set - Maintain compatibility with existing Float32Embeddings wrapper - Preserve all existing functionality while ensuring namespace is respected **Testing:** - Added comprehensive unit tests for namespace handling - Tests verify namespace is passed correctly in queries - Tests cover edge cases: with/without namespace, custom text_key, empty queries
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis PR addresses a Pinecone vector store regression by replacing the langchain-pinecone PineconeVectorStore integration with direct Pinecone client queries. The implementation wraps embeddings with Float32Embeddings, initializes the Pinecone client directly, embeds the query, and manually constructs Documents from results. Comprehensive unit tests validate initialization, namespace handling, custom metadata keys, and error scenarios. Changes
Sequence DiagramsequenceDiagram
participant User
participant Component as PineconeComponent
participant Float32 as Float32Embeddings
participant Pinecone as Pinecone Client
participant Index as Pinecone Index
User->>Component: search_documents(query)
Component->>Float32: wrap embedding
Component->>Pinecone: initialize with API key
Component->>Float32: embed_query(query)
Float32-->>Component: query_vector
Component->>Index: query(vector, top_k, namespace, include_metadata)
Index-->>Component: matches with metadata
rect rgb(200, 220, 250)
Note over Component: Convert matches to Documents
Component->>Component: extract text from metadata[text_key]
Component->>Component: create Document(page_content, metadata)
end
Component-->>User: list[Data]
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (8 passed)
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. Comment |
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.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/lfx/src/lfx/components/pinecone/pinecone.py (2)
94-138: search_documents logic looks correct; adjust inner imports and consider status on empty queries
- The new flow (Float32Embeddings → Pinecone client → conditional
namespaceinquery_params) correctly addresses the namespace regression and keeps tests’ expectations for error handling intact.- The inner import block is triggering Ruff I001; swap the two lines so they are alphabetically ordered by module path:
- from pinecone import Pinecone - from langchain_core.documents import Document + from langchain_core.documents import Document + from pinecone import Pinecone
- Minor behavioral note: when
search_queryis empty you return[]early, soself.statusis left unchanged. If callers rely onself.statusreflecting “last search”, you may want to explicitly reset it to[]before returning; otherwise current behavior is acceptable.
145-165: Float32Embeddings implementation is fine; add newline at EOF
_force_float32correctly normalizes any numeric input to a Pythonfloatvianp.float32, which is consistent with the wrapper’s intent.- Ruff reports
W292 No newline at end of file; add a trailing newline after thisreturnto satisfy the linter:- return float(np.float32(value)) + return float(np.float32(value)) +src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py (2)
1-41: Minor cleanup: redundant OPENAI API key handling in default_kwargs
get_openai_api_key()already callspytest.skipwhenOPENAI_API_KEYis not set, so the surroundingtry/except ValueErrorand extrapytest.skipare effectively dead code:try: api_key = get_openai_api_key() except ValueError: pytest.skip("OPENAI_API_KEY is not set")You can simplify this fixture to just:
api_key = get_openai_api_key()Behavior is unchanged, and the intent is clearer.
352-352: Fix Ruff W292: add newline at end of test fileRuff reports
W292 No newline at end of filehere. Add a trailing newline after the last assertion to satisfy the linter and unblock the style check.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py(1 hunks)src/lfx/src/lfx/components/pinecone/pinecone.py(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-05T22:51:27.961Z
Learnt from: edwinjosechittilappilly
Repo: langflow-ai/langflow PR: 0
File: :0-0
Timestamp: 2025-08-05T22:51:27.961Z
Learning: The TestComposioComponentAuth test in src/backend/tests/unit/components/bundles/composio/test_base_composio.py demonstrates proper integration testing patterns for external API components, including real API calls with mocking for OAuth completion, comprehensive resource cleanup, and proper environment variable handling with pytest.skip() fallbacks.
Applied to files:
src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py
🧬 Code graph analysis (2)
src/lfx/src/lfx/components/pinecone/pinecone.py (1)
src/lfx/src/lfx/field_typing/constants.py (1)
Document(43-44)
src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py (4)
src/lfx/src/lfx/components/pinecone/pinecone.py (3)
PineconeVectorStoreComponent(10-142)embed_query(157-161)search_documents(94-142)src/lfx/src/lfx/schema/data.py (1)
Data(26-288)src/backend/tests/base.py (2)
ComponentTestBaseWithoutClient(166-167)VersionComponentMapping(15-18)src/backend/tests/api_keys.py (1)
get_openai_api_key(33-37)
🪛 GitHub Actions: Ruff Style Check
src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py
[error] 352-352: ruff check failed: W292 No newline at end of file.
🪛 GitHub Check: Ruff Style Check (3.13)
src/lfx/src/lfx/components/pinecone/pinecone.py
[failure] 100-101: Ruff (I001)
src/lfx/src/lfx/components/pinecone/pinecone.py:100:13: I001 Import block is un-sorted or un-formatted
[failure] 165-165: Ruff (W292)
src/lfx/src/lfx/components/pinecone/pinecone.py:165:40: W292 No newline at end of file
src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py
[failure] 352-352: Ruff (W292)
src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py:352:41: W292 No newline at end of file
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
- GitHub Check: Lint Backend / Run Mypy (3.10)
- GitHub Check: Lint Backend / Run Mypy (3.12)
- GitHub Check: Lint Backend / Run Mypy (3.11)
- GitHub Check: Test Docker Images / Test docker images
- GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 4
- GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 5
- GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 3
- GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 2
- GitHub Check: Run Backend Tests / Integration Tests - Python 3.10
- GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 1
- GitHub Check: Run Backend Tests / LFX Tests - Python 3.10
- GitHub Check: Test Starter Templates
- GitHub Check: Update Component Index
| def test_search_documents_with_namespace( | ||
| self, component_class: type[PineconeVectorStoreComponent], default_kwargs: dict[str, Any] | ||
| ) -> None: | ||
| """Test that search_documents properly passes namespace parameter. | ||
|
|
||
| This test verifies the fix for issue #10512 where namespace wasn't being | ||
| properly passed to Pinecone queries, resulting in zero results. | ||
| """ | ||
| component: PineconeVectorStoreComponent = component_class().set(**default_kwargs) | ||
|
|
||
| # Mock the Pinecone client and index | ||
| mock_pinecone = MagicMock() | ||
| mock_index = MagicMock() | ||
| mock_pinecone.Index.return_value = mock_index | ||
|
|
||
| # Mock query results | ||
| mock_match = Mock() | ||
| mock_match.metadata = {"text": "test result", "source": "test"} | ||
| mock_results = Mock() | ||
| mock_results.matches = [mock_match] | ||
| mock_index.query.return_value = mock_results | ||
|
|
||
| with ( | ||
| patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone), | ||
| patch("lfx.components.pinecone.pinecone.Float32Embeddings") as mock_float32, | ||
| ): | ||
| mock_embeddings_instance = MagicMock() | ||
| mock_embeddings_instance.embed_query.return_value = [0.1] * 3072 | ||
| mock_float32.return_value = mock_embeddings_instance | ||
|
|
||
| component.set(search_query="test query") | ||
| results = component.search_documents() | ||
|
|
||
| # Verify Pinecone was called correctly | ||
| mock_pinecone.Index.assert_called_once_with("test-index") | ||
| mock_index.query.assert_called_once() | ||
|
|
||
| # Verify namespace was passed | ||
| call_kwargs = mock_index.query.call_args[1] | ||
| assert "namespace" in call_kwargs | ||
| assert call_kwargs["namespace"] == "test-namespace" | ||
| assert call_kwargs["top_k"] == 4 | ||
| assert call_kwargs["include_metadata"] is True | ||
|
|
||
| # Verify results are returned | ||
| assert len(results) == 1 | ||
| assert isinstance(results[0], Data) | ||
| assert results[0].text == "test result" | ||
|
|
||
| def test_search_documents_without_namespace( | ||
| self, component_class: type[PineconeVectorStoreComponent], default_kwargs: dict[str, Any] | ||
| ) -> None: | ||
| """Test that search_documents works without namespace.""" | ||
| # Remove namespace from kwargs | ||
| default_kwargs.pop("namespace", None) | ||
| component: PineconeVectorStoreComponent = component_class().set(**default_kwargs) | ||
|
|
||
| # Mock the Pinecone client and index | ||
| mock_pinecone = MagicMock() | ||
| mock_index = MagicMock() | ||
| mock_pinecone.Index.return_value = mock_index | ||
|
|
||
| # Mock query results | ||
| mock_match = Mock() | ||
| mock_match.metadata = {"text": "test result"} | ||
| mock_results = Mock() | ||
| mock_results.matches = [mock_match] | ||
| mock_index.query.return_value = mock_results | ||
|
|
||
| with ( | ||
| patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone), | ||
| patch("lfx.components.pinecone.pinecone.Float32Embeddings") as mock_float32, | ||
| ): | ||
| mock_embeddings_instance = MagicMock() | ||
| mock_embeddings_instance.embed_query.return_value = [0.1] * 3072 | ||
| mock_float32.return_value = mock_embeddings_instance | ||
|
|
||
| component.set(search_query="test query") | ||
| results = component.search_documents() | ||
|
|
||
| # Verify namespace was NOT passed when not set | ||
| call_kwargs = mock_index.query.call_args[1] | ||
| assert "namespace" not in call_kwargs | ||
|
|
||
| # Verify results are still returned | ||
| assert len(results) == 1 | ||
|
|
||
| def test_search_documents_empty_query( | ||
| self, component_class: type[PineconeVectorStoreComponent], default_kwargs: dict[str, Any] | ||
| ) -> None: | ||
| """Test that empty query returns empty results.""" | ||
| component: PineconeVectorStoreComponent = component_class().set(**default_kwargs) | ||
| component.set(search_query="") | ||
| results = component.search_documents() | ||
| assert results == [] | ||
|
|
||
| def test_search_documents_with_custom_text_key( | ||
| self, component_class: type[PineconeVectorStoreComponent], default_kwargs: dict[str, Any] | ||
| ) -> None: | ||
| """Test that custom text_key is properly used to extract content.""" | ||
| default_kwargs["text_key"] = "chunk_text" | ||
| component: PineconeVectorStoreComponent = component_class().set(**default_kwargs) | ||
|
|
||
| # Mock the Pinecone client and index | ||
| mock_pinecone = MagicMock() | ||
| mock_index = MagicMock() | ||
| mock_pinecone.Index.return_value = mock_index | ||
|
|
||
| # Mock query results with custom text key | ||
| mock_match = Mock() | ||
| mock_match.metadata = {"chunk_text": "custom text content", "source": "test"} | ||
| mock_results = Mock() | ||
| mock_results.matches = [mock_match] | ||
| mock_index.query.return_value = mock_results | ||
|
|
||
| # Mock Float32Embeddings | ||
| with ( | ||
| patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone), | ||
| patch("lfx.components.pinecone.pinecone.Float32Embeddings") as mock_float32, | ||
| ): | ||
| mock_embeddings_instance = MagicMock() | ||
| mock_embeddings_instance.embed_query.return_value = [0.1] * 3072 | ||
| mock_float32.return_value = mock_embeddings_instance | ||
|
|
||
| component.set(search_query="test query") | ||
| results = component.search_documents() | ||
|
|
||
| # Verify the custom text_key was used | ||
| assert len(results) == 1 | ||
| assert results[0].text == "custom text content" | ||
|
|
||
| def test_search_documents_pinecone_api_error( | ||
| self, component_class: type[PineconeVectorStoreComponent], default_kwargs: dict[str, Any] | ||
| ) -> None: | ||
| """Test that Pinecone API errors are properly handled.""" | ||
| component: PineconeVectorStoreComponent = component_class().set(**default_kwargs) | ||
|
|
||
| # Mock the Pinecone client to raise an error | ||
| mock_pinecone = MagicMock() | ||
| mock_index = MagicMock() | ||
| mock_index.query.side_effect = Exception("Pinecone API error") | ||
| mock_pinecone.Index.return_value = mock_index | ||
|
|
||
| with ( | ||
| patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone), | ||
| patch("lfx.components.pinecone.pinecone.Float32Embeddings") as mock_float32, | ||
| ): | ||
| mock_embeddings_instance = MagicMock() | ||
| mock_embeddings_instance.embed_query.return_value = [0.1] * 3072 | ||
| mock_float32.return_value = mock_embeddings_instance | ||
|
|
||
| component.set(search_query="test query") | ||
|
|
||
| # Verify error is raised | ||
| with pytest.raises(ValueError, match="Error searching documents"): | ||
| component.search_documents() | ||
|
|
||
| def test_search_documents_embedding_error( | ||
| self, component_class: type[PineconeVectorStoreComponent], default_kwargs: dict[str, Any] | ||
| ) -> None: | ||
| """Test that embedding errors are properly handled.""" | ||
| component: PineconeVectorStoreComponent = component_class().set(**default_kwargs) | ||
|
|
||
| # Mock Float32Embeddings to raise an error | ||
| with patch("lfx.components.pinecone.pinecone.Float32Embeddings") as mock_float32: | ||
| mock_embeddings_instance = MagicMock() | ||
| mock_embeddings_instance.embed_query.side_effect = Exception("Embedding error") | ||
| mock_float32.return_value = mock_embeddings_instance | ||
|
|
||
| component.set(search_query="test query") | ||
|
|
||
| # Verify error is raised | ||
| with pytest.raises(ValueError, match="Error searching documents"): | ||
| component.search_documents() | ||
|
|
||
| def test_float32_embeddings_wrapper_usage( | ||
| self, component_class: type[PineconeVectorStoreComponent], default_kwargs: dict[str, Any] | ||
| ) -> None: | ||
| """Test that Float32Embeddings wrapper is correctly instantiated and used.""" | ||
| component: PineconeVectorStoreComponent = component_class().set(**default_kwargs) | ||
|
|
||
| # Mock the Pinecone client and index | ||
| mock_pinecone = MagicMock() | ||
| mock_index = MagicMock() | ||
| mock_pinecone.Index.return_value = mock_index | ||
|
|
||
| # Mock query results | ||
| mock_match = Mock() | ||
| mock_match.metadata = {"text": "test result"} | ||
| mock_results = Mock() | ||
| mock_results.matches = [mock_match] | ||
| mock_index.query.return_value = mock_results | ||
|
|
||
| with ( | ||
| patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone), | ||
| patch("lfx.components.pinecone.pinecone.Float32Embeddings") as mock_float32, | ||
| ): | ||
| mock_embeddings_instance = MagicMock() | ||
| mock_embeddings_instance.embed_query.return_value = [0.1] * 3072 | ||
| mock_float32.return_value = mock_embeddings_instance | ||
|
|
||
| component.set(search_query="test query") | ||
| component.search_documents() | ||
|
|
||
| # Verify Float32Embeddings was instantiated with the correct embeddings | ||
| mock_float32.assert_called_once_with(default_kwargs["embedding"]) | ||
|
|
||
| # Verify embed_query was called with the search query | ||
| mock_embeddings_instance.embed_query.assert_called_once_with("test query") | ||
|
|
||
| def test_pinecone_client_initialization( | ||
| self, component_class: type[PineconeVectorStoreComponent], default_kwargs: dict[str, Any] | ||
| ) -> None: | ||
| """Test that Pinecone client is initialized with correct parameters.""" | ||
| component: PineconeVectorStoreComponent = component_class().set(**default_kwargs) | ||
|
|
||
| # Mock the Pinecone client and index | ||
| mock_pinecone = MagicMock() | ||
| mock_index = MagicMock() | ||
| mock_pinecone.Index.return_value = mock_index | ||
|
|
||
| # Mock query results | ||
| mock_match = Mock() | ||
| mock_match.metadata = {"text": "test result"} | ||
| mock_results = Mock() | ||
| mock_results.matches = [mock_match] | ||
| mock_index.query.return_value = mock_results | ||
|
|
||
| with ( | ||
| patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone) as mock_pc_class, | ||
| patch("lfx.components.pinecone.pinecone.Float32Embeddings") as mock_float32, | ||
| ): | ||
| mock_embeddings_instance = MagicMock() | ||
| mock_embeddings_instance.embed_query.return_value = [0.1] * 3072 | ||
| mock_float32.return_value = mock_embeddings_instance | ||
|
|
||
| component.set(search_query="test query") | ||
| component.search_documents() | ||
|
|
||
| # Verify Pinecone was initialized with correct API key | ||
| mock_pc_class.assert_called_once_with(api_key="test-pinecone-key") | ||
|
|
||
| # Verify Index was called with correct index name | ||
| mock_pinecone.Index.assert_called_once_with("test-index") | ||
|
|
||
| def test_search_documents_with_missing_text_key_in_metadata( | ||
| self, component_class: type[PineconeVectorStoreComponent], default_kwargs: dict[str, Any] | ||
| ) -> None: | ||
| """Test that documents with missing text_key in metadata are handled gracefully.""" | ||
| component: PineconeVectorStoreComponent = component_class().set(**default_kwargs) | ||
|
|
||
| # Mock the Pinecone client and index | ||
| mock_pinecone = MagicMock() | ||
| mock_index = MagicMock() | ||
| mock_pinecone.Index.return_value = mock_index | ||
|
|
||
| # Mock query results with metadata missing the text key | ||
| mock_match = Mock() | ||
| mock_match.metadata = {"source": "test"} # Missing "text" key | ||
| mock_results = Mock() | ||
| mock_results.matches = [mock_match] | ||
| mock_index.query.return_value = mock_results | ||
|
|
||
| with ( | ||
| patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone), | ||
| patch("lfx.components.pinecone.pinecone.Float32Embeddings") as mock_float32, | ||
| ): | ||
| mock_embeddings_instance = MagicMock() | ||
| mock_embeddings_instance.embed_query.return_value = [0.1] * 3072 | ||
| mock_float32.return_value = mock_embeddings_instance | ||
|
|
||
| component.set(search_query="test query") | ||
| results = component.search_documents() | ||
|
|
||
| # Verify results are returned with empty text | ||
| assert len(results) == 1 | ||
| assert results[0].text == "" | ||
|
|
||
| def test_search_documents_with_none_metadata( | ||
| self, component_class: type[PineconeVectorStoreComponent], default_kwargs: dict[str, Any] | ||
| ) -> None: | ||
| """Test that documents with None metadata are handled gracefully.""" | ||
| component: PineconeVectorStoreComponent = component_class().set(**default_kwargs) | ||
|
|
||
| # Mock the Pinecone client and index | ||
| mock_pinecone = MagicMock() | ||
| mock_index = MagicMock() | ||
| mock_pinecone.Index.return_value = mock_index | ||
|
|
||
| # Mock query results with None metadata | ||
| mock_match = Mock() | ||
| mock_match.metadata = None | ||
| mock_results = Mock() | ||
| mock_results.matches = [mock_match] | ||
| mock_index.query.return_value = mock_results | ||
|
|
||
| with ( | ||
| patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone), | ||
| patch("lfx.components.pinecone.pinecone.Float32Embeddings") as mock_float32, | ||
| ): | ||
| mock_embeddings_instance = MagicMock() | ||
| mock_embeddings_instance.embed_query.return_value = [0.1] * 3072 | ||
| mock_float32.return_value = mock_embeddings_instance | ||
|
|
||
| component.set(search_query="test query") | ||
| results = component.search_documents() | ||
|
|
||
| # Verify results are returned with empty text and metadata | ||
| assert len(results) == 1 |
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.
🧩 Analysis chain
Pinecone mocking target doesn’t match implementation import; tests may call the real client
In search_documents the implementation does a local import:
from pinecone import Pinecone
pc = Pinecone(api_key=self.pinecone_api_key)This binds Pinecone as a local name from the external pinecone module. The tests, however, patch lfx.components.pinecone.pinecone.Pinecone, which is a different symbol and won’t intercept that local import. As a result, these tests can end up using the real Pinecone client (or failing with ImportError) instead of the mocked one.
To ensure the tests actually mock what’s used at runtime, you have two main options:
- Option A (change tests, keep implementation as-is – simplest):
Update all contexts like:
patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone)to patch the external module instead:
- patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone),
+ patch("pinecone.Pinecone", return_value=mock_pinecone),This way from pinecone import Pinecone pulls in the mocked class.
- Option B (change implementation to use module-level import), then patch the component module’s symbol.
If you prefer to keep patching lfx.components.pinecone.pinecone.Pinecone, move the import to module scope with a guarded pattern and have search_documents reference the global Pinecone. That’s a broader behavioral change (and may affect optional-dependency semantics), so Option A is safer for this PR.
Also note that test_search_documents_embedding_error doesn’t patch Pinecone at all; if pinecone isn’t installed, the resulting ValueError("Error searching documents") would be due to an import error, not the embedding error. The assertion still passes, but the failure mode is different from what the test name suggests.
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Find PineconeVectorStoreComponent implementation file
find . -name "*.py" -type f | xargs grep -l "class PineconeVectorStoreComponent" | grep -v test | head -5Length of output: 49
🏁 Script executed:
#!/bin/bash
# Inspect the search_documents method in PineconeVectorStoreComponent
ast-grep --pattern 'def search_documents($$$) {
$$$
}'Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check import statements at module level and inside search_documents
rg -n "from pinecone import|import pinecone" --type=py -g "!test*" | head -20Length of output: 93
🏁 Script executed:
#!/bin/bash
# Check for EOF newline in the test file (W292 - no newline at EOF)
tail -c 1 "src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py" | od -A x -t x1zLength of output: 40
🏁 Script executed:
#!/bin/bash
# Count lines to confirm file structure
wc -l "src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py"Length of output: 90
🏁 Script executed:
#!/bin/bash
# Show last 10 lines of test file to check EOF
tail -10 "src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py"Length of output: 447
🏁 Script executed:
#!/bin/bash
# Find all patch targets in the test file to understand mocking strategy
rg -n 'patch\("' "src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py"Length of output: 1620
🏁 Script executed:
#!/bin/bash
# Get the actual implementation of search_documents
ast-grep --pattern 'def search_documents($_) {
$$$
}' | head -100Length of output: 0
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# View the implementation file header and imports
head -20 "src/lfx/src/lfx/components/pinecone/pinecone.py"Length of output: 817
🏁 Script executed:
#!/bin/bash
# View context around line 100 to see search_documents and import
sed -n '90,120p' "src/lfx/src/lfx/components/pinecone/pinecone.py"Length of output: 1125
🏁 Script executed:
#!/bin/bash
# View the search_documents method more completely
sed -n '90,150p' "src/lfx/src/lfx/components/pinecone/pinecone.py"Length of output: 2154
🏁 Script executed:
#!/bin/bash
# Check if there's a module-level Pinecone import
grep -n "^from pinecone\|^import pinecone" "src/lfx/src/lfx/components/pinecone/pinecone.py"Length of output: 0
🏁 Script executed:
#!/bin/bash
# Show last line of test file with hex dump alternative to confirm no newline
tail -1 "src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py" | wc -cLength of output: 2
🏁 Script executed:
#!/bin/bash
# Check full context: what's the actual module path structure?
find . -path "*/lfx/components/pinecone/pinecone.py" -o -path "*/lfx/components/pinecone/__init__.py" | head -5Length of output: 99
Pinecone mocking target doesn't match implementation import; tests may call the real client
In search_documents() (line 100), the implementation does a local import:
from pinecone import Pinecone
pc = Pinecone(api_key=self.pinecone_api_key)This creates a local binding to the external pinecone module. The tests, however, patch lfx.components.pinecone.pinecone.Pinecone, which doesn't exist at module scope (Pinecone is never imported there). As a result, these tests won't intercept the local import and can end up using the real Pinecone client instead of the mocked one.
To fix, update all patch calls from:
patch("lfx.components.pinecone.pinecone.Pinecone", return_value=mock_pinecone)to:
patch("pinecone.Pinecone", return_value=mock_pinecone)This ensures from pinecone import Pinecone pulls in the mocked class.
Also, the test file is missing a trailing newline at EOF (W292).
🤖 Prompt for AI Agents
src/backend/tests/unit/components/vectorstores/test_pinecone_vector_store_component.py
lines 43-351: the tests patch "lfx.components.pinecone.pinecone.Pinecone" but
the implementation does a local import "from pinecone import Pinecone", so
update every patch(...) call that targets Pinecone to patch "pinecone.Pinecone"
(e.g. patch("pinecone.Pinecone", return_value=mock_pinecone)) so the local
import is mocked, and add a trailing newline at EOF to fix W292.
fixes #10512
Root Cause:
similarity_search()method was not properly passing the namespace parameter to the underlying Pinecone query, resulting in searches against the wrong namespace and returning zero results.Solution:
search_documents()to query Pinecone directly using the native Pinecone clientindex.query()when setTesting:
implemented coderabbits suggestions
Summary by CodeRabbit
Tests
Refactor