81 lines
3.4 KiB
C#
81 lines
3.4 KiB
C#
using System.Net.Http.Headers;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using Api.Requests;
|
|
using Api.Responses;
|
|
using Api.Services.Contracts;
|
|
using Api.Settings;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace Api.Services;
|
|
|
|
public sealed class RagApiClient : IRagApiClient
|
|
{
|
|
private readonly HttpClient _http;
|
|
private readonly RagApiSettings _settings;
|
|
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
|
|
|
public RagApiClient(HttpClient http, IOptions<RagApiSettings> options)
|
|
{
|
|
_http = http;
|
|
_settings = options.Value;
|
|
_http.BaseAddress = new Uri(_settings.BaseUrl.TrimEnd('/') + "/");
|
|
if (!string.IsNullOrWhiteSpace(_settings.InternalApiKey))
|
|
{
|
|
_http.DefaultRequestHeaders.Add("X-Internal-Api-Key", _settings.InternalApiKey);
|
|
}
|
|
}
|
|
|
|
public async Task<RagIndexResponse> IndexCvPdfAsync(IFormFile file, CancellationToken ct)
|
|
{
|
|
using var content = new MultipartFormDataContent();
|
|
await using var stream = file.OpenReadStream();
|
|
using var fileContent = new StreamContent(stream);
|
|
fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
|
content.Add(fileContent, "file", file.FileName);
|
|
content.Add(new StringContent("cv"), "documentType");
|
|
content.Add(new StringContent(file.FileName), "title");
|
|
using var response = await _http.PostAsync("api/rag/documents", content, ct);
|
|
return await ReadJsonAsync<RagIndexResponse>(response, ct);
|
|
}
|
|
|
|
public async Task<RagIndexResponse> IndexJobTextAsync(string text, string? url, string? title, CancellationToken ct)
|
|
{
|
|
using var content = new MultipartFormDataContent
|
|
{
|
|
{ new StringContent(text), "text" },
|
|
{ new StringContent("job"), "documentType" },
|
|
{ new StringContent(title ?? "Job description"), "title" }
|
|
};
|
|
if (!string.IsNullOrWhiteSpace(url)) content.Add(new StringContent(url), "sourceUrl");
|
|
using var response = await _http.PostAsync("api/rag/documents", content, ct);
|
|
return await ReadJsonAsync<RagIndexResponse>(response, ct);
|
|
}
|
|
|
|
public async Task<RagDocumentDetails?> GetDocumentAsync(string documentId, CancellationToken ct)
|
|
{
|
|
using var response = await _http.GetAsync($"api/rag/documents/{Uri.EscapeDataString(documentId)}", ct);
|
|
if (response.StatusCode == System.Net.HttpStatusCode.NotFound) return null;
|
|
return await ReadJsonAsync<RagDocumentDetails>(response, ct);
|
|
}
|
|
|
|
public async Task<RagSearchResponse> SearchAsync(RagSearchRequest request, CancellationToken ct)
|
|
{
|
|
using var response = await _http.PostAsync(
|
|
"api/rag/search",
|
|
new StringContent(JsonSerializer.Serialize(request, JsonOptions), Encoding.UTF8, "application/json"),
|
|
ct);
|
|
return await ReadJsonAsync<RagSearchResponse>(response, ct);
|
|
}
|
|
|
|
private static async Task<T> ReadJsonAsync<T>(HttpResponseMessage response, CancellationToken ct)
|
|
{
|
|
var json = await response.Content.ReadAsStringAsync(ct);
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
throw new InvalidOperationException($"RAG API failed: {(int)response.StatusCode} {json}");
|
|
}
|
|
return JsonSerializer.Deserialize<T>(json, JsonOptions) ?? throw new InvalidOperationException("RAG API returned invalid JSON.");
|
|
}
|
|
}
|