From b96d975716698b1353852afeb48caa04c5c61161 Mon Sep 17 00:00:00 2001 From: Kristian Ivanov Date: Mon, 10 Jun 2024 23:18:42 +0300 Subject: [PATCH] Created ChatService --- src/server/CookingApp/Models/Chat.cs | 6 ++ .../Services/ChatService/ChatService.cs | 34 +++++++ .../Services/ChatService/IChatService.cs | 15 ++++ .../OpenAI/Completions/CompletionService.cs | 88 ++++++++++++------- .../ServiceTests/MongoDB/DbTests.cs | 12 +++ 5 files changed, 124 insertions(+), 31 deletions(-) create mode 100644 src/server/CookingApp/Services/ChatService/ChatService.cs create mode 100644 src/server/CookingApp/Services/ChatService/IChatService.cs create mode 100644 test/CookingApp.UnitTests/ServiceTests/MongoDB/DbTests.cs diff --git a/src/server/CookingApp/Models/Chat.cs b/src/server/CookingApp/Models/Chat.cs index 6e5aee77..d5289a50 100644 --- a/src/server/CookingApp/Models/Chat.cs +++ b/src/server/CookingApp/Models/Chat.cs @@ -3,6 +3,12 @@ public class Chat : MongoEntity { + [BsonElement("title")] + public string Title { get; set; } + + [BsonElement("userId")] + public string UserId { get; set; } + [BsonElement("createdTime")] public DateTime CreatedTime { get; set; } diff --git a/src/server/CookingApp/Services/ChatService/ChatService.cs b/src/server/CookingApp/Services/ChatService/ChatService.cs new file mode 100644 index 00000000..82fa9320 --- /dev/null +++ b/src/server/CookingApp/Services/ChatService/ChatService.cs @@ -0,0 +1,34 @@ +namespace CookingApp.Services.ChatHistory +{ + using CookingApp.Infrastructure.Interfaces; + + public class ChatService : IChatService + { + private readonly IRepository _chatRepository; + + public ChatService(IRepository chatRepository) + { + _chatRepository = chatRepository; + } + + public async Task InsertAsync(Chat chat) + { + await _chatRepository.InsertAsync(chat); + } + + public async Task> GetAllChatsAsync() + => await _chatRepository.GetAllAsync(); + + public async Task>> GetAllByUserId(string userId) + { + var result = await _chatRepository.GetAllAsync(c => c.UserId == userId); + return result.OrderBy(c => c.CreatedDateTime).Select(c => Tuple.Create(c.Title, c.Id)).ToList(); + } + + public async Task GetByIdAsync(string id) + => await _chatRepository.GetByIdAsync(id); + + public async Task UpdateAsync(Chat chat) + => await _chatRepository.UpdateAsync(chat); + } +} diff --git a/src/server/CookingApp/Services/ChatService/IChatService.cs b/src/server/CookingApp/Services/ChatService/IChatService.cs new file mode 100644 index 00000000..bad20511 --- /dev/null +++ b/src/server/CookingApp/Services/ChatService/IChatService.cs @@ -0,0 +1,15 @@ +namespace CookingApp.Services.ChatHistory +{ + public interface IChatService + { + Task InsertAsync(Chat chat); + + Task> GetAllChatsAsync(); + + Task>> GetAllByUserId(string userId); + + Task GetByIdAsync(string id); + + Task UpdateAsync(Chat chat); + } +} diff --git a/src/server/CookingApp/Services/OpenAI/Completions/CompletionService.cs b/src/server/CookingApp/Services/OpenAI/Completions/CompletionService.cs index b74d9c8e..af651837 100644 --- a/src/server/CookingApp/Services/OpenAI/Completions/CompletionService.cs +++ b/src/server/CookingApp/Services/OpenAI/Completions/CompletionService.cs @@ -7,6 +7,9 @@ using CookingApp.Common.CompletionConstants; using CookingApp.Infrastructure.Interfaces; using System.Linq; + using CookingApp.Services.ChatHistory; + using MongoDB.Bson.Serialization; + using System.Text.Json; /// /// This class it to assist with the personal needs of the user. @@ -16,23 +19,31 @@ public class CompletionService : ICompletionService { private readonly IRepository _userRepo; - private readonly IRepository _chatRepo; + private readonly ILogger _logger; + private readonly IChatService _chatService; private readonly IOpenAIService _openAIService; - public CompletionService(IOpenAIService openAIService, IRepository userRepo, IRepository chatRepo) + public CompletionService(IOpenAIService openAIService, + IRepository userRepo, + IChatService chatService, + ILogger logger) { _openAIService = openAIService; _userRepo = userRepo; - _chatRepo = chatRepo; + _chatService = chatService; + _logger = logger; } public async Task CreateCompletion(string request) { + _logger.LogInformation("Attempting to find user"); //TODO: get the userId through JWT Bearer - var user = await _userRepo.GetByIdAsync("userId"); + //TODO: insert try-catch + //var user = await _userRepo.GetByIdAsync("userId"); // Get the user allergies - var userAllergies = user.Allergies; + //var userAllergies = user.Allergies; + var userAllergies = new List { "bananas", "oats", "peanuts" }; // Case if the converstaion is new and the chat doesn't exist var completionResult = await _openAIService.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest @@ -43,19 +54,22 @@ public async Task CreateCompletion(string request) ChatMessage.FromSystem(Completions.Instructions + userAllergies + "." + Completions.PromptEngineeringPrevention), - ChatMessage.FromUser(Completions.Suggestion), - ChatMessage.FromAssistant(Completions.ExampleResponse), + //ChatMessage.FromUser(Completions.Suggestion), + //ChatMessage.FromAssistant(Completions.ExampleResponse), ChatMessage.FromUser(request) }, - Model = Models.Gpt_4o + Model = Models.Gpt_3_5_Turbo_0125, + MaxTokens = 5, + N = 1, }); - var userChat = CreateNewChat(completionResult.Id); + // Creates a new Chat where later interaction will be stored + //var userChat = CreateNewChat(completionResult.Id); if (completionResult.Successful) { - var response = completionResult.Choices[0].Message.Content; - UpdateUserChat(userChat, request, response); + //var response = completionResult.Choices[0].Message.Content; + //UpdateUserChat(userChat, request, response); return completionResult; } @@ -63,26 +77,38 @@ public async Task CreateCompletion(string request) } - public async Task UpdateCompletion(string request, string? chatId = null) + public async Task UpdateCompletion(string request, string? chatId) { - var userChat = await _chatRepo.GetByIdAsync(chatId); - - var completionResult = await _openAIService.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest + try { - Messages = new List - { - ChatMessage.FromUser(request) - }, - Model = Models.Gpt_4o - }); + var userChat = await _chatService.GetByIdAsync(chatId); - if (completionResult.Successful) + var completionResult = await _openAIService.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest + { + Messages = new List + { + ChatMessage.FromUser(request) + }, + Model = Models.Gpt_3_5_Turbo_0125 + }); + + if (completionResult.Successful) + { + _logger.LogInformation("Successfully received a response from the ChatGPT API."); + // workout if info is needed inside the logger + _logger.LogInformation($"{JsonSerializer.Serialize(completionResult)}"); + var response = completionResult.Choices[0].Message.Content; + UpdateUserChat(userChat, request, response); + + return completionResult; + } + } + catch (Exception e) { - var response = completionResult.Choices[0].Message.Content; - UpdateUserChat(userChat, request, response); - return completionResult; + _logger.LogInformation("An error occurred while sending a query to ChatGPT API."); + _logger.LogInformation(e.Message); } - + return null; } @@ -104,7 +130,7 @@ private Response CreateNewResponse(string message) }; // Creates a new chat using the ID originating from the ChatGPT API - private Chat CreateNewChat(string id) + private async Task CreateNewChat(string id) { var chat = new Chat() { @@ -113,16 +139,16 @@ private Chat CreateNewChat(string id) Responses = new List() }; - _chatRepo.InsertAsync(chat); + await _chatService.InsertAsync(chat); return chat; } private async void UpdateUserChat(Chat? userChat, string? request, string? response) { - userChat.Requests.Add(CreateNewRequest(request)); - userChat.Responses.Add(CreateNewResponse(response)); - await _chatRepo.UpdateAsync(userChat); + userChat?.Requests.Add(CreateNewRequest(request)); + userChat?.Responses.Add(CreateNewResponse(response)); + await _chatService.UpdateAsync(userChat); } } } diff --git a/test/CookingApp.UnitTests/ServiceTests/MongoDB/DbTests.cs b/test/CookingApp.UnitTests/ServiceTests/MongoDB/DbTests.cs new file mode 100644 index 00000000..5d266171 --- /dev/null +++ b/test/CookingApp.UnitTests/ServiceTests/MongoDB/DbTests.cs @@ -0,0 +1,12 @@ +namespace CookingApp.UnitTests.ServiceTests.MongoDB +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + internal class DbTests + { + } +}