Files
myAi/api/Services/Rag/OpenAiRagClient.cs
T
claude 2dce2ab0ff
Build and Push Docker Images / build (push) Successful in 42s
Changes
2026-05-04 15:56:15 +03:00

105 lines
3.6 KiB
C#

using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Api.Settings;
using Microsoft.Extensions.Options;
namespace Api.Services.Rag;
public interface IOpenAiRagClient
{
Task<float[]> CreateEmbeddingAsync(string input, CancellationToken ct);
Task<string> CreateChatCompletionAsync(string systemPrompt, string userPrompt, CancellationToken ct);
}
public sealed class OpenAiRagClient : IOpenAiRagClient
{
private readonly HttpClient _httpClient;
private readonly OpenAiSettings _settings;
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
public OpenAiRagClient(HttpClient httpClient, IOptions<OpenAiSettings> options)
{
_httpClient = httpClient;
_settings = options.Value;
if (!string.IsNullOrWhiteSpace(_settings.ApiKey))
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _settings.ApiKey);
}
_httpClient.Timeout = TimeSpan.FromSeconds(Math.Max(15, _settings.TimeoutSeconds));
_httpClient.BaseAddress = new Uri("https://api.openai.com/v1/");
}
public async Task<float[]> CreateEmbeddingAsync(string input, CancellationToken ct)
{
EnsureConfigured();
var payload = new { model = _settings.EmbeddingModel, input };
using var response = await _httpClient.PostAsync("embeddings", ToJson(payload), ct);
var json = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException($"OpenAI embeddings request failed: {(int)response.StatusCode} {json}");
}
using var document = JsonDocument.Parse(json);
var embedding = document.RootElement.GetProperty("data")[0].GetProperty("embedding");
var result = new float[embedding.GetArrayLength()];
var i = 0;
foreach (var value in embedding.EnumerateArray())
{
result[i++] = value.GetSingle();
}
return result;
}
public async Task<string> CreateChatCompletionAsync(string systemPrompt, string userPrompt, CancellationToken ct)
{
EnsureConfigured();
var payload = new
{
model = _settings.ChatModel,
temperature = 0.2,
response_format = new { type = "json_object" },
messages = new[]
{
new { role = "system", content = systemPrompt },
new { role = "user", content = userPrompt }
}
};
using var response = await _httpClient.PostAsync("chat/completions", ToJson(payload), ct);
var json = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException($"OpenAI chat request failed: {(int)response.StatusCode} {json}");
}
using var document = JsonDocument.Parse(json);
return document.RootElement
.GetProperty("choices")[0]
.GetProperty("message")
.GetProperty("content")
.GetString() ?? "{}";
}
private void EnsureConfigured()
{
if (string.IsNullOrWhiteSpace(_settings.ApiKey))
{
throw new InvalidOperationException("OpenAI API key is not configured. Set OpenAI__ApiKey.");
}
}
private static StringContent ToJson<T>(T payload) => new(
JsonSerializer.Serialize(payload, JsonOptions),
Encoding.UTF8,
"application/json"
);
}