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:
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
|
||||||
|
async with page.expect_navigation(timeout=60000):
|
||||||
await page.click('button[name="accept"]')
|
await page.click('button[name="accept"]')
|
||||||
await page.wait_for_load_state("networkidle", timeout=60000)
|
|
||||||
|
|
||||||
logger.info("✓ Keycloak consent granted")
|
logger.info("✓ Keycloak consent granted")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user