refactor: update to Qdrant query_points API and fix Playwright Keycloak login

- Replace deprecated qdrant_client.search() with query_points() API
- Update semantic search implementation in notes.py
- Update all integration tests to use query_points()
- Fix Keycloak login in test_keycloak_dcr.py to use form.submit() instead of button click
- Remove unnecessary popup handler code
- Simplify consent screen logging
This commit is contained in:
Chris Coutinho
2025-11-08 22:41:14 +01:00
parent e96c02e4d4
commit 1a57f97d3a
3 changed files with 33 additions and 31 deletions
+3 -3
View File
@@ -414,9 +414,9 @@ def configure_notes_tools(mcp: FastMCP):
# Search Qdrant with user filtering # Search Qdrant with user filtering
qdrant_client = await get_qdrant_client() qdrant_client = await get_qdrant_client()
search_results = await qdrant_client.search( search_response = await qdrant_client.query_points(
collection_name=settings.qdrant_collection, collection_name=settings.qdrant_collection,
query_vector=query_embedding, query=query_embedding,
query_filter=Filter( query_filter=Filter(
must=[ must=[
FieldCondition( FieldCondition(
@@ -439,7 +439,7 @@ def configure_notes_tools(mcp: FastMCP):
seen_note_ids = set() seen_note_ids = set()
results = [] results = []
for result in search_results: for result in search_response.points:
note_id = int(result.payload["doc_id"]) note_id = int(result.payload["doc_id"])
# Skip if we've already seen this note # Skip if we've already seen this note
+20 -20
View File
@@ -207,32 +207,32 @@ async def test_semantic_search_with_qdrant(
query = "async programming patterns in Python" query = "async programming patterns in Python"
query_embedding = await simple_embedding_provider.embed(query) query_embedding = await simple_embedding_provider.embed(query)
results = await qdrant_test_client.search( response = await qdrant_test_client.query_points(
collection_name=test_collection, collection_name=test_collection,
query_vector=query_embedding, query=query_embedding,
limit=3, limit=3,
score_threshold=0.0, score_threshold=0.0,
) )
# Should find Python note as top result # Should find Python note as top result
assert len(results) > 0 assert len(response.points) > 0
assert results[0].payload["note_id"] == 1 assert response.points[0].payload["note_id"] == 1
assert "Python" in results[0].payload["title"] assert "Python" in response.points[0].payload["title"]
# Test Query 2: Search for books # Test Query 2: Search for books
query = "good books to read recommendations" query = "good books to read recommendations"
query_embedding = await simple_embedding_provider.embed(query) query_embedding = await simple_embedding_provider.embed(query)
results = await qdrant_test_client.search( response = await qdrant_test_client.query_points(
collection_name=test_collection, collection_name=test_collection,
query_vector=query_embedding, query=query_embedding,
limit=3, limit=3,
score_threshold=0.0, score_threshold=0.0,
) )
# Should find book recommendations note # Should find book recommendations note
assert len(results) > 0 assert len(response.points) > 0
top_result = results[0] top_result = response.points[0]
assert top_result.payload["note_id"] == 2 assert top_result.payload["note_id"] == 2
assert "Book" in top_result.payload["title"] assert "Book" in top_result.payload["title"]
@@ -240,17 +240,17 @@ async def test_semantic_search_with_qdrant(
query = "how to bake cookies dessert" query = "how to bake cookies dessert"
query_embedding = await simple_embedding_provider.embed(query) query_embedding = await simple_embedding_provider.embed(query)
results = await qdrant_test_client.search( response = await qdrant_test_client.query_points(
collection_name=test_collection, collection_name=test_collection,
query_vector=query_embedding, query=query_embedding,
limit=3, limit=3,
score_threshold=0.0, score_threshold=0.0,
) )
# Should find recipe note # Should find recipe note
assert len(results) > 0 assert len(response.points) > 0
# Recipe should be in top 2 results # Recipe should be in top 2 results
top_note_ids = [r.payload["note_id"] for r in results[:2]] top_note_ids = [r.payload["note_id"] for r in response.points[:2]]
assert 3 in top_note_ids assert 3 in top_note_ids
@@ -289,9 +289,9 @@ async def test_semantic_search_with_filters(
query = "books reading" query = "books reading"
query_embedding = await simple_embedding_provider.embed(query) query_embedding = await simple_embedding_provider.embed(query)
results = await qdrant_test_client.search( response = await qdrant_test_client.query_points(
collection_name=test_collection, collection_name=test_collection,
query_vector=query_embedding, query=query_embedding,
query_filter=Filter( query_filter=Filter(
must=[FieldCondition(key="category", match=MatchValue(value="Personal"))] must=[FieldCondition(key="category", match=MatchValue(value="Personal"))]
), ),
@@ -299,8 +299,8 @@ async def test_semantic_search_with_filters(
) )
# Should only return Personal category notes # Should only return Personal category notes
assert len(results) > 0 assert len(response.points) > 0
for result in results: for result in response.points:
assert result.payload["category"] == "Personal" assert result.payload["category"] == "Personal"
@@ -314,13 +314,13 @@ async def test_semantic_search_empty_results(
query = "test query" query = "test query"
query_embedding = await simple_embedding_provider.embed(query) query_embedding = await simple_embedding_provider.embed(query)
results = await qdrant_test_client.search( response = await qdrant_test_client.query_points(
collection_name=test_collection, collection_name=test_collection,
query_vector=query_embedding, query=query_embedding,
limit=10, limit=10,
) )
assert len(results) == 0 assert len(response.points) == 0
async def test_batch_embedding(simple_embedding_provider: SimpleEmbeddingProvider): async def test_batch_embedding(simple_embedding_provider: SimpleEmbeddingProvider):
+10 -8
View File
@@ -46,9 +46,10 @@ async def handle_keycloak_login(page, username: str, password: str):
Keycloak uses: Keycloak uses:
- input#username for username field - input#username for username field
- input#password for password field - input#password for password field
- input[type="submit"] for submit button - Form submission via JavaScript (more reliable than clicking button)
""" """
logger.info(f"Handling Keycloak login for user: {username}") logger.info(f"Handling Keycloak login for user: {username}")
logger.info(f"Current URL before login: {page.url}")
# Wait for username field and fill it # Wait for username field and fill it
await page.wait_for_selector("input#username", timeout=10000) await page.wait_for_selector("input#username", timeout=10000)
@@ -58,11 +59,12 @@ async def handle_keycloak_login(page, username: str, password: str):
await page.wait_for_selector("input#password", timeout=10000) await page.wait_for_selector("input#password", timeout=10000)
await page.fill("input#password", password) await page.fill("input#password", password)
# Click submit button # Submit form using JavaScript (more reliable than clicking button)
await page.click('input[type="submit"]') logger.info("Submitting Keycloak login form...")
await page.wait_for_load_state("networkidle", timeout=60000) async with page.expect_navigation(timeout=60000):
await page.evaluate("document.querySelector('form').submit()")
logger.info("✓ Keycloak login completed") logger.info(f"✓ Keycloak login completed, redirected to: {page.url}")
async def handle_keycloak_consent(page, client_name: str): async def handle_keycloak_consent(page, client_name: str):
@@ -80,9 +82,9 @@ async def handle_keycloak_consent(page, client_name: str):
# Wait for consent screen (button with name="accept") # Wait for consent screen (button with name="accept")
await page.wait_for_selector('button[name="accept"]', timeout=5000) await page.wait_for_selector('button[name="accept"]', timeout=5000)
# Click accept button # Click accept button and wait for navigation
await page.click('button[name="accept"]') async with page.expect_navigation(timeout=60000):
await page.wait_for_load_state("networkidle", timeout=60000) await page.click('button[name="accept"]')
logger.info("✓ Keycloak consent granted") logger.info("✓ Keycloak consent granted")
except Exception as e: except Exception as e: