feat(ui): implement premium NexusSearchBox component and integrate semantic search navigation
This commit is contained in:
@@ -1,18 +1,7 @@
|
||||
using FluentResults;
|
||||
using MediatR;
|
||||
using Pgvector;
|
||||
using Pgvector.EntityFrameworkCore;
|
||||
using NexusReader.Application.Abstractions.Services;
|
||||
using NexusReader.Application.DTOs.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Resilience;
|
||||
using Polly;
|
||||
using Polly.Registry;
|
||||
using Mapster;
|
||||
using MapsterMapper;
|
||||
|
||||
using NexusReader.Data.Persistence;
|
||||
|
||||
namespace NexusReader.Application.Queries.Library;
|
||||
|
||||
@@ -21,21 +10,11 @@ public record SearchLibrarySemanticallyQuery(string QueryText, string TenantId,
|
||||
|
||||
public class SearchLibrarySemanticallyQueryHandler : IRequestHandler<SearchLibrarySemanticallyQuery, Result<List<SemanticSearchResultDto>>>
|
||||
{
|
||||
private readonly IEmbeddingGenerator<string, Embedding<float>> _embeddingGenerator;
|
||||
private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
|
||||
private readonly ResiliencePipeline _retryPipeline;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly IKnowledgeService _knowledgeService;
|
||||
|
||||
public SearchLibrarySemanticallyQueryHandler(
|
||||
IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator,
|
||||
IDbContextFactory<AppDbContext> dbContextFactory,
|
||||
ResiliencePipelineProvider<string> pipelineProvider,
|
||||
IMapper mapper)
|
||||
public SearchLibrarySemanticallyQueryHandler(IKnowledgeService knowledgeService)
|
||||
{
|
||||
_embeddingGenerator = embeddingGenerator;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_retryPipeline = pipelineProvider.GetPipeline("ai-retry");
|
||||
_mapper = mapper;
|
||||
_knowledgeService = knowledgeService;
|
||||
}
|
||||
|
||||
public async Task<Result<List<SemanticSearchResultDto>>> Handle(SearchLibrarySemanticallyQuery request, CancellationToken cancellationToken)
|
||||
@@ -45,19 +24,10 @@ public class SearchLibrarySemanticallyQueryHandler : IRequestHandler<SearchLibra
|
||||
return Result.Fail("Query text cannot be empty.");
|
||||
}
|
||||
|
||||
// Generate embedding with retry
|
||||
var embeddingResponse = await _retryPipeline.ExecuteAsync(async ct =>
|
||||
await _embeddingGenerator.GenerateAsync(new[] { request.QueryText }, cancellationToken: ct), cancellationToken);
|
||||
var queryVector = new Vector(embeddingResponse.First().Vector.ToArray());
|
||||
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
var cacheEntries = await dbContext.SemanticKnowledgeCache
|
||||
.Where(c => c.TenantId == request.TenantId && c.Embedding != null)
|
||||
.OrderBy(c => c.Embedding!.CosineDistance(queryVector))
|
||||
.Take(request.Limit)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var dtos = _mapper.Map<List<SemanticSearchResultDto>>(cacheEntries);
|
||||
return Result.Ok(dtos);
|
||||
return await _knowledgeService.SearchLibrarySemanticallyAsync(
|
||||
request.QueryText,
|
||||
request.TenantId,
|
||||
request.Limit,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user