feat(recommendations): implement contextual recommendation engine #76
Reference in New Issue
Block a user
Delete Branch "feature/global-intelligence-qa"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Resolves #75
Description
This pull request implements a smart, Native AOT-compliant contextual recommendation engine for the desktop dashboard to drive user retention and cross-book monetization.
Key Changes
IUserReadingStateStoreinterface to decouple reading state discovery.IVectorSearchStore.SearchGlobalExcludeAsync(...)to abstract semantic similarity searches with exclusions.GetContextualRecommendationsQueryand response DTOs (ContextualRecommendationResponse,RecommendationDto).UserReadingStateStoreusing EF Core with DbContext pooling.SearchGlobalExcludeAsyncinVectorSearchStoreto construct gRPC Qdrant filters (excluding the active book ID) and fetchbookTitleandchapterTitlefrom point payloads.GetContextualRecommendationsQueryHandlerusing clean abstractions.GET /api/recommendationsendpoint.AppJsonContext.csfor AOT-compliant JSON serialization.GetContextualRecommendationsQueryTests.cs. All 30 unit tests pass.Review Findings
Recommendations:
Overall, the PR is solid and ready for merge after addressing the above items.
@@ -0,0 +30,4 @@IUserReadingStateStore readingStateStore,IUserLibraryStore libraryStore,IVectorSearchStore vectorSearchStore,ILogger<GetContextualRecommendationsQueryHandler> logger)Consider injecting IHttpClientFactory if future HttpClient usage is added; currently no HttpClient present.
@@ -0,0 +81,4 @@limit: 2,cancellationToken: cancellationToken);Add registration for IVectorSearchStore in DI container (Program.cs).
@@ -0,0 +9,4 @@@inject NavigationManager NavigationManager@inject ILogger<AiResponseRenderer> Logger<div class="message-row @(Message.Sender == "User" ? "user-row" : "ai-row")">Add tabindex="0" to the root div for keyboard focus and ensure focus is set after rendering.
@@ -0,0 +50,4 @@}</div><div class="upsell-card" role="alert" aria-live="polite">Verify color contrast for premium upsell badge meets WCAG AA; consider using CSS variables for theme colors.
@@ -0,0 +58,4 @@<p class="upsell-text">Twoje zasoby odpowiadają na to pytanie w <strong>@_localScore%</strong>. W materiale <strong>'@_lockedBookTitle'</strong> znaleźliśmy odpowiedź dopasowaną w <strong>@_globalScore%</strong>.</p>Ensure component CSS uses custom properties to respect dark mode.
Overall review: Good architectural compliance, but please address DI registration for IVectorSearchStore and add unit tests. See inline comments for UI accessibility and logging improvements.
@@ -0,0 +30,4 @@IUserReadingStateStore readingStateStore,IUserLibraryStore libraryStore,IVectorSearchStore vectorSearchStore,ILogger<GetContextualRecommendationsQueryHandler> logger)Consider injecting IHttpClientFactory if future HttpClient usage is added; currently no HttpClient present.
@@ -0,0 +81,4 @@limit: 2,cancellationToken: cancellationToken);Add registration for IVectorSearchStore in DI container (Program.cs).
@@ -0,0 +9,4 @@@inject NavigationManager NavigationManager@inject ILogger<AiResponseRenderer> Logger<div class="message-row @(Message.Sender == "User" ? "user-row" : "ai-row")">Add tabindex="0" to the root div for keyboard focus and ensure focus is set after rendering.
@@ -0,0 +50,4 @@}</div><div class="upsell-card" role="alert" aria-live="polite">Verify color contrast for premium upsell badge meets WCAG AA; consider using CSS variables for theme colors.
@@ -0,0 +58,4 @@<p class="upsell-text">Twoje zasoby odpowiadają na to pytanie w <strong>@_localScore%</strong>. W materiale <strong>'@_lockedBookTitle'</strong> znaleźliśmy odpowiedź dopasowaną w <strong>@_globalScore%</strong>.</p>Ensure component CSS uses custom properties to respect dark mode.