fix(ui/security): Enforce idempotent AI fetching, secure auth handler, and memory leak guards #45
@@ -31,7 +31,7 @@ public class KnowledgeService : IKnowledgeService
|
|||||||
private readonly Tokenizer _tokenizer;
|
private readonly Tokenizer _tokenizer;
|
||||||
private readonly ILogger<KnowledgeService> _logger;
|
private readonly ILogger<KnowledgeService> _logger;
|
||||||
private const string PromptVersion = "1.3";
|
private const string PromptVersion = "1.3";
|
||||||
private static readonly ConcurrentDictionary<string, Task<Result<KnowledgePacket>>> _activeRequests = new();
|
private static readonly ConcurrentDictionary<string, Lazy<Task<Result<KnowledgePacket>>>> _activeRequests = new();
|
||||||
|
|
||||||
public KnowledgeService(
|
public KnowledgeService(
|
||||||
IChatClient chatClient,
|
IChatClient chatClient,
|
||||||
@@ -100,10 +100,38 @@ public class KnowledgeService : IKnowledgeService
|
|||||||
|
|
||||||
// Deduplicate concurrent active requests for the exact same hash
|
// Deduplicate concurrent active requests for the exact same hash
|
||||||
var requestKey = $"{tenantId}:{hash}:{traceType}";
|
var requestKey = $"{tenantId}:{hash}:{traceType}";
|
||||||
var task = _activeRequests.GetOrAdd(requestKey, _ =>
|
|
||||||
ExecuteAiRequestAndCacheAsync(normalizedText, tenantId, systemPrompt, traceType, ebookId, hash));
|
var lazyTask = _activeRequests.GetOrAdd(requestKey, k =>
|
||||||
|
new Lazy<Task<Result<KnowledgePacket>>>(
|
||||||
|
() => ExecuteAiRequestAndCacheAsync(normalizedText, tenantId, systemPrompt, traceType, ebookId, hash),
|
||||||
|
System.Threading.LazyThreadSafetyMode.ExecutionAndPublication
|
||||||
|
));
|
||||||
|
|
||||||
return await task;
|
try
|
||||||
|
{
|
||||||
|
var result = await lazyTask.Value;
|
||||||
|
|
||||||
|
// If the AI call returned a failure, remove it from the active dictionary
|
||||||
|
// so subsequent retries have a chance to request the AI again.
|
||||||
|
if (result.IsFailed)
|
||||||
|
{
|
||||||
|
_activeRequests.TryRemove(requestKey, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Evict from active dictionary on hard exceptions to ensure system resiliency
|
||||||
|
_activeRequests.TryRemove(requestKey, out _);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Once a task successfully finishes and persists to the Persistent Database Cache,
|
||||||
|
// we evict it from RAM (_activeRequests) since future hits will leverage the DB cache.
|
||||||
|
_activeRequests.TryRemove(requestKey, out _);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Result<KnowledgePacket>> ExecuteAiRequestAndCacheAsync(
|
private async Task<Result<KnowledgePacket>> ExecuteAiRequestAndCacheAsync(
|
||||||
|
|||||||
Reference in New Issue
Block a user