* Extended functionalities
* Added different UIs
This commit is contained in:
80
MealPlan.Api/Services/AI/OpenAiChatService.cs
Normal file
80
MealPlan.Api/Services/AI/OpenAiChatService.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace MealPlan.Api.Services.AI;
|
||||
|
||||
public class OpenAiChatService : IOpenAiChatService
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
private readonly OpenAiOptions _options;
|
||||
private readonly ILogger<OpenAiChatService> _logger;
|
||||
|
||||
public OpenAiChatService(HttpClient http, IOptions<OpenAiOptions> options, ILogger<OpenAiChatService> logger)
|
||||
{
|
||||
_http = http;
|
||||
_options = options.Value;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool IsConfigured => _options.IsConfigured;
|
||||
|
||||
public async Task<string> CompleteJsonAsync(
|
||||
string systemPrompt,
|
||||
string userPrompt,
|
||||
OpenAiUseCase useCase = OpenAiUseCase.Default,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
if (!IsConfigured)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"OpenAI is not configured. Set OpenAI__ApiKey in appsettings.Local.json.");
|
||||
}
|
||||
|
||||
var model = _options.ResolveModel(useCase);
|
||||
var payload = new
|
||||
{
|
||||
model,
|
||||
max_tokens = _options.MaxTokens,
|
||||
response_format = new { type = "json_object" },
|
||||
messages = new[]
|
||||
{
|
||||
new { role = "system", content = systemPrompt },
|
||||
new { role = "user", content = userPrompt },
|
||||
},
|
||||
};
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.openai.com/v1/chat/completions");
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.ApiKey);
|
||||
request.Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
|
||||
|
||||
using var response = await _http.SendAsync(request, ct);
|
||||
var body = await response.Content.ReadAsStringAsync(ct);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.LogWarning("OpenAI API error {Status}: {Body}", response.StatusCode, body);
|
||||
throw new InvalidOperationException("OpenAI request failed. Check API key and quota.");
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
var content = doc.RootElement
|
||||
.GetProperty("choices")[0]
|
||||
.GetProperty("message")
|
||||
.GetProperty("content")
|
||||
.GetString() ?? "{}";
|
||||
|
||||
return StripMarkdownFence(content);
|
||||
}
|
||||
|
||||
private static string StripMarkdownFence(string content)
|
||||
{
|
||||
var trimmed = content.Trim();
|
||||
if (!trimmed.StartsWith("```", StringComparison.Ordinal)) return trimmed;
|
||||
var firstNewline = trimmed.IndexOf('\n');
|
||||
if (firstNewline < 0) return trimmed;
|
||||
var endFence = trimmed.LastIndexOf("```", StringComparison.Ordinal);
|
||||
if (endFence <= firstNewline) return trimmed;
|
||||
return trimmed[(firstNewline + 1)..endFence].Trim();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user