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; public class WasmKnowledgeService : IKnowledgeService { private readonly HttpClient _httpClient; public WasmKnowledgeService(HttpClient httpClient) { _httpClient = httpClient; } public async Task> GetKnowledgeAsync(string text, string tenantId, Guid? ebookId = null, CancellationToken cancellationToken = default) { return await CallKnowledgeApiAsync("/api/knowledge", text, ebookId, cancellationToken); } public async Task> GetGraphDataAsync(string text, string tenantId, Guid? ebookId = null, CancellationToken cancellationToken = default) { return await CallKnowledgeApiAsync("/api/knowledge/graph", text, ebookId, cancellationToken); } public async Task> GetKnowledgeMapAsync(string text, string tenantId, Guid? ebookId = null, CancellationToken cancellationToken = default) { return await CallKnowledgeApiAsync("/api/knowledge/map", text, ebookId, cancellationToken); } public async Task> GetSummaryAndQuizAsync(string text, string tenantId, Guid? ebookId = null, CancellationToken cancellationToken = default) { return await CallKnowledgeApiAsync("/api/knowledge/summary", text, ebookId, cancellationToken); } public async Task>> GetRelevantContextAsync(string query, string tenantId, CancellationToken cancellationToken = default) { try { var response = await _httpClient.PostAsJsonAsync("/api/knowledge/relevant", new { query, tenantId }, cancellationToken); if (response.IsSuccessStatusCode) { var context = await response.Content.ReadFromJsonAsync>(cancellationToken: cancellationToken); return context != null ? Result.Ok(context) : Result.Fail("Failed to deserialize relevant context."); } 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)); } } public async Task> VerifyGroundednessAsync(string answer, string context, string tenantId, CancellationToken cancellationToken = default) { try { var response = await _httpClient.PostAsJsonAsync("/api/knowledge/verify-groundedness", new { answer, context, tenantId }, cancellationToken); if (response.IsSuccessStatusCode) { var result = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); return result != null ? Result.Ok(result) : Result.Fail("Failed to deserialize groundedness result."); } 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)); } } public async Task>> SearchLibrarySemanticallyAsync(string queryText, string tenantId, int limit, CancellationToken cancellationToken = default) { try { var response = await _httpClient.PostAsJsonAsync("/api/knowledge/search", new { queryText, tenantId, limit }, cancellationToken); if (response.IsSuccessStatusCode) { var searchResults = await response.Content.ReadFromJsonAsync>(cancellationToken: cancellationToken); return searchResults != null ? Result.Ok(searchResults) : Result.Ok(new List()); } 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)); } } public async Task> AskQuestionAsync(string question, string tenantId, Guid? ebookId = null, int limit = 5, CancellationToken cancellationToken = default) { try { var response = await _httpClient.PostAsJsonAsync("/api/knowledge/ask", new { question, tenantId, ebookId, limit }, cancellationToken); if (response.IsSuccessStatusCode) { var result = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); return result != null ? Result.Ok(result) : Result.Fail("Failed to deserialize grounded 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)); } } public async Task> 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( 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> CallKnowledgeApiAsync(string endpoint, string text, Guid? ebookId, CancellationToken cancellationToken) { try { var response = await _httpClient.PostAsJsonAsync(endpoint, new { text, ebookId }, cancellationToken); if (response.IsSuccessStatusCode) { var packet = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); return packet != null ? Result.Ok(packet) : Result.Fail("Failed to deserialize knowledge packet."); } var errorBody = await response.Content.ReadAsStringAsync(cancellationToken); return Result.Fail($"Server error ({response.StatusCode}): {errorBody}"); } catch (Exception ex) { return Result.Fail(new Error($"Network or parsing error: {ex.Message}").CausedBy(ex)); } } public async Task ClearCacheAsync(CancellationToken cancellationToken = default) { try { Console.WriteLine("[WasmKnowledgeService] Requesting cache clear..."); var response = await _httpClient.DeleteAsync("/api/knowledge", cancellationToken); if (response.IsSuccessStatusCode) { Console.WriteLine("[WasmKnowledgeService] Cache cleared successfully."); return Result.Ok(); } 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)); } } }