using OpenAI.Chat; using OpenAI.Models; using PerfReviewSummarizer.Api.Models; namespace PerfReviewSummarizer.Api.Services; public class OpenAiService : IOpenAiService, IAiService { private readonly IConfiguration _configuration; private readonly HttpClient _httpClient; private bool _initialized = false; public OpenAiService(IConfiguration configuration, HttpClient httpClient) { _configuration = configuration; _httpClient = httpClient; } private void EnsureInitialized() { if (_initialized) return; var apiKey = _configuration["OpenAI:ApiKey"]; if (string.IsNullOrEmpty(apiKey)) { throw new InvalidOperationException("OpenAI API ключ не найден в конфигурации. Добавьте 'OpenAI:ApiKey' в appsettings.json"); } _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", apiKey); _initialized = true; } public async Task GenerateSummaryFromCommitsAsync(List commits) { var commitMessages = commits.Select(c => c.Message).ToList(); return await GenerateSummaryFromCommitMessagesAsync(commitMessages); } public async Task GenerateSummaryFromCommitMessagesAsync(List commitMessages) { EnsureInitialized(); if (!commitMessages.Any()) { return "Нет коммитов для анализа"; } var commitListText = string.Join("\n", commitMessages.Select((msg, idx) => $"{idx + 1}. {msg}")); var prompt = $@"Проанализируй следующие сообщения коммитов из Git репозитория и создай краткое, информативное резюме (summary). Сообщения коммитов: {commitListText} Требования: 1. Резюме должно быть на русском языке 2. Будь лаконичен, но информативен (2-4 абзаца) 3. Выделите основные темы изменений (новые функции, исправления, рефакторинг и т.д.) 4. Укажите общую направленность разработки 5. Отметьте любые важные или критичные изменения Формат ответа - просто текст резюме без дополнительных комментариев."; try { var request = new ChatCompletionRequest { Model = "gpt-3.5-turbo", Messages = new List { new ChatMessage { Role = ChatMessageRole.User, Content = prompt } }, Temperature = 0.7, MaxTokens = 1000 }; var json = System.Text.Json.JsonSerializer.Serialize(request); var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync("https://api.openai.com/v1/chat/completions", content); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"OpenAI Error ({response.StatusCode}): {errorContent}"); throw new InvalidOperationException($"Ошибка OpenAI API: {response.StatusCode} - {errorContent}"); } var responseContent = await response.Content.ReadAsStringAsync(); var result = System.Text.Json.JsonDocument.Parse(responseContent); var message = result.RootElement .GetProperty("choices")[0] .GetProperty("message") .GetProperty("content") .GetString(); return message ?? "Ошибка при генерировании резюме"; } catch (Exception ex) { throw new InvalidOperationException($"Ошибка при обращении к OpenAI API: {ex.Message}", ex); } } } // Вспомогательные классы для сериализации public class ChatCompletionRequest { [System.Text.Json.Serialization.JsonPropertyName("model")] public string Model { get; set; } = "gpt-3.5-turbo"; [System.Text.Json.Serialization.JsonPropertyName("messages")] public List Messages { get; set; } = new(); [System.Text.Json.Serialization.JsonPropertyName("temperature")] public double Temperature { get; set; } = 0.7; [System.Text.Json.Serialization.JsonPropertyName("max_tokens")] public int MaxTokens { get; set; } = 1000; } public class ChatMessage { [System.Text.Json.Serialization.JsonPropertyName("role")] public string Role { get; set; } = "user"; [System.Text.Json.Serialization.JsonPropertyName("content")] public string Content { get; set; } = string.Empty; } public static class ChatMessageRole { public const string User = "user"; public const string Assistant = "assistant"; public const string System = "system"; }