feat(intelligence): implement global hybrid search engine and monetization logic

- Created IUserLibraryStore and IVectorSearchStore abstractions to decouple relational DB and Qdrant gRPC logic from Application Layer
- Implemented MediatR GetGlobalIntelligenceQuery with value-first teaser RAG monetization logic
- Registered new request and response DTOs in AppJsonContext for Native AOT source-generated serialization
- Bound RagMonetizationOptions via IOptions pattern in appsettings.json configuration
- Added POST /api/intelligence endpoint on server and implemented GetGlobalIntelligenceAsync in WASM client service
- Refactored Intelligence.razor to consume the backend-driven global hybrid search Q&A engine
This commit is contained in:
2026-06-06 10:55:58 +02:00
parent faf6ec826e
commit 93133a49b6
15 changed files with 692 additions and 43 deletions
@@ -2,6 +2,8 @@ using System.Net.Http.Json;
using FluentResults;
using NexusReader.Application.Abstractions.Services;
using NexusReader.Application.DTOs.AI;
using NexusReader.Application.Common;
using NexusReader.Application.Queries.Intelligence;
namespace NexusReader.Web.Client.Services;
@@ -113,6 +115,34 @@ public class WasmKnowledgeService : IKnowledgeService
}
}
public async Task<Result<IntelligenceResponse>> GetGlobalIntelligenceAsync(string queryText, string userId, string tenantId, CancellationToken cancellationToken = default)
{
try
{
var response = await _httpClient.PostAsJsonAsync(
"/api/intelligence",
new GetGlobalIntelligenceRequest(queryText),
AppJsonContext.Default.GetGlobalIntelligenceRequest,
cancellationToken);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<IntelligenceResponse>(
AppJsonContext.Default.IntelligenceResponse,
cancellationToken: cancellationToken);
return result != null ? Result.Ok(result) : Result.Fail("Failed to deserialize global intelligence response.");
}
var errorBody = await response.Content.ReadAsStringAsync(cancellationToken);
return Result.Fail($"Server error ({response.StatusCode}): {errorBody}");
}
catch (Exception ex)
{
return Result.Fail(new Error($"Network error: {ex.Message}").CausedBy(ex));
}
}
private async Task<Result<KnowledgePacket>> CallKnowledgeApiAsync(string endpoint, string text, Guid? ebookId, CancellationToken cancellationToken)
{
try