feat: implement multi-tenancy support across knowledge services and normalize TenantId to string type.

This commit is contained in:
2026-05-03 17:52:12 +02:00
parent eac0e9057e
commit e21c24b66d
16 changed files with 334 additions and 94 deletions
@@ -1,51 +1,22 @@
using FluentResults;
using MediatR;
using Microsoft.Extensions.AI;
using NexusReader.Application.Abstractions.Services;
namespace NexusReader.Application.Commands.AI;
public record VerifyGroundednessCommand(string Answer, string Context) : IRequest<Result<GroundednessResult>>;
public record GroundednessResult(float Score, string Rationale, bool IsGrounded);
public record VerifyGroundednessCommand(string Answer, string Context, string TenantId) : IRequest<Result<GroundednessResult>>;
public class VerifyGroundednessCommandHandler : IRequestHandler<VerifyGroundednessCommand, Result<GroundednessResult>>
{
private readonly IChatClient _chatClient;
private readonly IKnowledgeService _knowledgeService;
public VerifyGroundednessCommandHandler(IChatClient chatClient)
public VerifyGroundednessCommandHandler(IKnowledgeService knowledgeService)
{
_chatClient = chatClient;
_knowledgeService = knowledgeService;
}
public async Task<Result<GroundednessResult>> Handle(VerifyGroundednessCommand request, CancellationToken cancellationToken)
{
var systemPrompt = @"
You are a Fact-Checking AI. Evaluate if the 'Answer' is supported by the 'Context'.
Rate the groundedness from 0.0 to 1.0.
Return ONLY a JSON object: { ""score"": 0.9, ""rationale"": ""string"", ""isGrounded"": true }
";
var userPrompt = $"Context: {request.Context}\n\nAnswer: {request.Answer}";
try
{
var response = await _chatClient.GetResponseAsync(new List<ChatMessage>
{
new ChatMessage(ChatRole.System, systemPrompt),
new ChatMessage(ChatRole.User, userPrompt)
}, cancellationToken: cancellationToken);
var rawJson = response.Text?.Trim() ?? "{}";
// Simple cleanup if needed
rawJson = rawJson.Replace("```json", "").Replace("```", "").Trim();
var result = System.Text.Json.JsonSerializer.Deserialize<GroundednessResult>(rawJson, new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true });
return result != null ? Result.Ok(result) : Result.Fail("Failed to parse groundedness result");
}
catch (Exception ex)
{
return Result.Fail(new Error("Failed to verify groundedness").CausedBy(ex));
}
return await _knowledgeService.VerifyGroundednessAsync(request.Answer, request.Context, request.TenantId, cancellationToken);
}
}