Skip to content

Commit d4e534d

Browse files
authored
test(qdrant): add regression test for issue #32283
Signed-off-by: NDV Hareesh <89001360+ndvHareesh@users.noreply.github.com>
1 parent e495651 commit d4e534d

1 file changed

Lines changed: 191 additions & 0 deletions

File tree

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
"""
2+
Regression tests for GitHub issue #32283:
3+
aadd_texts() got multiple values for keyword argument 'ids'
4+
5+
When aadd_documents() is called with custom ids as a keyword argument,
6+
it should NOT raise:
7+
TypeError: aadd_texts() got multiple values for keyword argument 'ids'
8+
9+
Root cause: aadd_documents was adding ids into **kwargs AND also passing
10+
ids=ids explicitly to aadd_texts, causing Python to reject the duplicate.
11+
12+
These tests guard against regression in:
13+
- QdrantVectorStore (new API)
14+
- Deprecated Qdrant class (sync-fallback path via @sync_call_fallback)
15+
16+
Run with:
17+
source venv/bin/activate
18+
pytest tests/test_aadd_documents_ids.py -v
19+
"""
20+
21+
import asyncio
22+
import uuid
23+
import warnings
24+
import pytest
25+
26+
from langchain_core.documents import Document
27+
from langchain_core.embeddings import Embeddings
28+
from typing import List
29+
30+
31+
class FakeEmbeddings(Embeddings):
32+
"""Minimal embeddings stub — returns fixed 3D vectors."""
33+
def embed_documents(self, texts: List[str]) -> List[List[float]]:
34+
return [[0.1, 0.2, 0.3] for _ in texts]
35+
36+
def embed_query(self, text: str) -> List[float]:
37+
return [0.1, 0.2, 0.3]
38+
39+
40+
def make_uuid_ids(n: int) -> List[str]:
41+
return [str(uuid.uuid4()) for _ in range(n)]
42+
43+
44+
# ---------------------------------------------------------------------------
45+
# Fixtures
46+
# ---------------------------------------------------------------------------
47+
48+
@pytest.fixture
49+
def qdrant_client():
50+
from qdrant_client import QdrantClient
51+
from qdrant_client.models import Distance, VectorParams
52+
client = QdrantClient(":memory:")
53+
client.create_collection(
54+
collection_name="test",
55+
vectors_config=VectorParams(size=3, distance=Distance.COSINE),
56+
)
57+
return client
58+
59+
60+
@pytest.fixture
61+
def qdrant_vector_store(qdrant_client):
62+
from langchain_qdrant import QdrantVectorStore
63+
return QdrantVectorStore(
64+
client=qdrant_client,
65+
collection_name="test",
66+
embedding=FakeEmbeddings(),
67+
)
68+
69+
70+
@pytest.fixture
71+
def deprecated_qdrant_sync(qdrant_client):
72+
"""Deprecated Qdrant class with sync-only client (no async_client)."""
73+
with warnings.catch_warnings():
74+
warnings.simplefilter("ignore")
75+
from langchain_qdrant.vectorstores import Qdrant
76+
return Qdrant(
77+
client=qdrant_client,
78+
collection_name="test",
79+
embeddings=FakeEmbeddings(),
80+
)
81+
82+
83+
# ---------------------------------------------------------------------------
84+
# Tests — QdrantVectorStore (new API)
85+
# ---------------------------------------------------------------------------
86+
87+
class TestQdrantVectorStoreAaddDocuments:
88+
"""
89+
Regression: issue #32283 — ids passed twice to aadd_texts.
90+
"""
91+
92+
@pytest.mark.asyncio
93+
async def test_aadd_documents_with_custom_ids_does_not_raise(self, qdrant_vector_store):
94+
"""
95+
Calling aadd_documents(docs, ids=ids) must not raise TypeError.
96+
This was the exact call pattern from issue #32283.
97+
"""
98+
docs = [
99+
Document(page_content="Hello world"),
100+
Document(page_content="Goodbye world"),
101+
]
102+
ids = make_uuid_ids(2)
103+
104+
# Must not raise: TypeError: aadd_texts() got multiple values for keyword argument 'ids'
105+
result = await qdrant_vector_store.aadd_documents(docs, ids=ids)
106+
assert len(result) == 2
107+
108+
@pytest.mark.asyncio
109+
async def test_aadd_documents_returns_provided_ids(self, qdrant_vector_store):
110+
"""The IDs returned should match the ones passed in."""
111+
docs = [Document(page_content="Test doc")]
112+
ids = make_uuid_ids(1)
113+
114+
result = await qdrant_vector_store.aadd_documents(docs, ids=ids)
115+
assert result == ids
116+
117+
@pytest.mark.asyncio
118+
async def test_aadd_documents_without_ids_generates_ids(self, qdrant_vector_store):
119+
"""aadd_documents without explicit ids should auto-generate valid UUIDs."""
120+
docs = [
121+
Document(page_content="First doc"),
122+
Document(page_content="Second doc"),
123+
]
124+
result = await qdrant_vector_store.aadd_documents(docs)
125+
assert len(result) == 2
126+
# All returned values should be valid UUIDs
127+
for id_ in result:
128+
uuid.UUID(str(id_)) # raises ValueError if not a valid UUID
129+
130+
@pytest.mark.asyncio
131+
async def test_aadd_documents_multiple_calls_accumulate(self, qdrant_vector_store):
132+
"""Multiple aadd_documents calls should accumulate documents, not overwrite."""
133+
docs_a = [Document(page_content="Batch A")]
134+
docs_b = [Document(page_content="Batch B")]
135+
ids_a = make_uuid_ids(1)
136+
ids_b = make_uuid_ids(1)
137+
138+
result_a = await qdrant_vector_store.aadd_documents(docs_a, ids=ids_a)
139+
result_b = await qdrant_vector_store.aadd_documents(docs_b, ids=ids_b)
140+
141+
assert result_a == ids_a
142+
assert result_b == ids_b
143+
assert result_a != result_b
144+
145+
@pytest.mark.asyncio
146+
async def test_aadd_documents_upsert_same_id_updates_content(self, qdrant_vector_store):
147+
"""Adding a document with an existing ID should upsert (update) it."""
148+
shared_id = make_uuid_ids(1)
149+
doc_v1 = [Document(page_content="Version 1")]
150+
doc_v2 = [Document(page_content="Version 2")]
151+
152+
r1 = await qdrant_vector_store.aadd_documents(doc_v1, ids=shared_id)
153+
r2 = await qdrant_vector_store.aadd_documents(doc_v2, ids=shared_id)
154+
155+
assert r1 == shared_id
156+
assert r2 == shared_id # same ID, upserted
157+
158+
159+
# ---------------------------------------------------------------------------
160+
# Tests — Deprecated Qdrant class (sync fallback path)
161+
# ---------------------------------------------------------------------------
162+
163+
class TestDeprecatedQdrantAaddDocuments:
164+
"""
165+
The deprecated Qdrant class overrides aadd_texts with @sync_call_fallback.
166+
Verify aadd_documents still works correctly through that path.
167+
"""
168+
169+
@pytest.mark.asyncio
170+
async def test_aadd_documents_with_custom_ids_does_not_raise(self, deprecated_qdrant_sync):
171+
docs = [
172+
Document(page_content="Hello world"),
173+
Document(page_content="Goodbye world"),
174+
]
175+
ids = make_uuid_ids(2)
176+
# Must not raise: TypeError: aadd_texts() got multiple values for keyword argument 'ids'
177+
result = await deprecated_qdrant_sync.aadd_documents(docs, ids=ids)
178+
assert len(result) == 2
179+
180+
@pytest.mark.asyncio
181+
async def test_aadd_documents_returns_provided_ids(self, deprecated_qdrant_sync):
182+
docs = [Document(page_content="Test doc")]
183+
ids = make_uuid_ids(1)
184+
result = await deprecated_qdrant_sync.aadd_documents(docs, ids=ids)
185+
assert result == ids
186+
187+
@pytest.mark.asyncio
188+
async def test_aadd_documents_without_ids(self, deprecated_qdrant_sync):
189+
docs = [Document(page_content="Auto ID doc")]
190+
result = await deprecated_qdrant_sync.aadd_documents(docs)
191+
assert len(result) == 1

0 commit comments

Comments
 (0)