diff --git a/OpenChat.sln b/OpenChat.sln new file mode 100644 index 0000000..05d0c1d --- /dev/null +++ b/OpenChat.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenChatApi", "OpenChatApi\OpenChatApi.csproj", "{A579CBBF-1C08-4B9B-AD43-8FEC5ECA97D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A579CBBF-1C08-4B9B-AD43-8FEC5ECA97D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A579CBBF-1C08-4B9B-AD43-8FEC5ECA97D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A579CBBF-1C08-4B9B-AD43-8FEC5ECA97D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A579CBBF-1C08-4B9B-AD43-8FEC5ECA97D0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7FA774F6-5265-46B4-8345-9A7469BFFC98} + EndGlobalSection +EndGlobal diff --git a/OpenChatApi/Models/ChatResquest.cs b/OpenChatApi/Models/ChatResquest.cs new file mode 100644 index 0000000..ff162e9 --- /dev/null +++ b/OpenChatApi/Models/ChatResquest.cs @@ -0,0 +1,4 @@ +namespace OpenChatApi.Models +{ + public record ChatRequest(string UserId, string Message); +} diff --git a/OpenChatApi/Models/MessageDocument.cs b/OpenChatApi/Models/MessageDocument.cs new file mode 100644 index 0000000..f5f208e --- /dev/null +++ b/OpenChatApi/Models/MessageDocument.cs @@ -0,0 +1,17 @@ +namespace OpenChatApi.Models +{ + public class MessageDocument + { + public string Id { get; set; } + public string UserId { get; set; } + public string SystemPrompt { get; set; } + public List Messages { get; set; } = new(); + public DateTime LastMessage { get; set; } + } + + public class Message + { + public string Type { get; set; } + public string Content { get; set; } + } +} diff --git a/OpenChatApi/Models/MongoDBSettings.cs b/OpenChatApi/Models/MongoDBSettings.cs new file mode 100644 index 0000000..f7bf447 --- /dev/null +++ b/OpenChatApi/Models/MongoDBSettings.cs @@ -0,0 +1,7 @@ +namespace OpenChatApi.Models +{ + public class MongoDBSettings + { + public string ConnectionString { get; set; } + } +} diff --git a/OpenChatApi/Models/SystemPromptRequest.cs b/OpenChatApi/Models/SystemPromptRequest.cs new file mode 100644 index 0000000..c111160 --- /dev/null +++ b/OpenChatApi/Models/SystemPromptRequest.cs @@ -0,0 +1,4 @@ +namespace OpenChatApi.Models +{ + public record SystemPromptRequest(string UserId, string SystemPrompt); +} diff --git a/OpenChatApi/OpenChatApi.csproj b/OpenChatApi/OpenChatApi.csproj new file mode 100644 index 0000000..d43a35d --- /dev/null +++ b/OpenChatApi/OpenChatApi.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + enable + enable + + + + + + + + + + diff --git a/OpenChatApi/OpenChatApi.http b/OpenChatApi/OpenChatApi.http new file mode 100644 index 0000000..b8f22b7 --- /dev/null +++ b/OpenChatApi/OpenChatApi.http @@ -0,0 +1,6 @@ +@OpenChatApi_HostAddress = http://localhost:5233 + +GET {{OpenChatApi_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/OpenChatApi/Program.cs b/OpenChatApi/Program.cs new file mode 100644 index 0000000..4fb19ad --- /dev/null +++ b/OpenChatApi/Program.cs @@ -0,0 +1,67 @@ +// Program.cs +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.ChatCompletion; +using OpenChatApi.Models; +using OpenChatApi.Services; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +// MongoDB Configuration +builder.Services.Configure( + builder.Configuration.GetSection("MongoDB")); +builder.Services.AddSingleton(); + +// Semantic Kernel Configuration +#pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +builder.Services.AddOpenAIChatCompletion("openchat-3.5-0106", + new Uri("https://gpt.serverspace.com.br/v1/chat/completions"), + "tIAXVf3AkCkkpSX+PjFvktfEeSPyA1ZYam50UO3ye/qmxVZX6PIXstmJsLZXkQ39C33onFD/81mdxvhbGHm7tQ=="); +#pragma warning restore SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + +var app = builder.Build(); + +app.UseSwagger(); +app.UseSwaggerUI(); + +app.MapPost("/api/system-prompt", async (SystemPromptRequest request, IMongoDBService mongoService) => +{ + await mongoService.UpdateSystemPrompt(request.UserId, request.SystemPrompt); + return Results.Ok(); +}) +.WithName("SystemPrompt") +.WithOpenApi(); + +// Endpoint +app.MapPost("/api/chat", async (ChatRequest request, IMongoDBService mongoService, IChatCompletionService kernel) => +{ + var history = await mongoService.GetUserMessages(request.UserId); + + // Create chat history + var chatHistory = new ChatHistory(); + chatHistory.AddSystemMessage(history.SystemPrompt); + foreach (var msg in history.Messages) + { + if (msg.Type == "user") + chatHistory.AddUserMessage(msg.Content); + else + chatHistory.AddAssistantMessage(msg.Content); + } + + // Add current message + chatHistory.AddUserMessage(request.Message); + + // Get response + var response = await kernel.GetChatMessageContentAsync(chatHistory); + + // Update MongoDB + await mongoService.UpdateMessages(request.UserId, request.Message, response.Content); + + return Results.Ok(new { response = response }); +}) +.WithName("Chat") +.WithOpenApi(); + +app.Run(); diff --git a/OpenChatApi/Properties/launchSettings.json b/OpenChatApi/Properties/launchSettings.json new file mode 100644 index 0000000..900ead4 --- /dev/null +++ b/OpenChatApi/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:54894", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5233", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7028;http://localhost:5233", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/OpenChatApi/Services/IMongoDBService.cs b/OpenChatApi/Services/IMongoDBService.cs new file mode 100644 index 0000000..76007ea --- /dev/null +++ b/OpenChatApi/Services/IMongoDBService.cs @@ -0,0 +1,11 @@ +using OpenChatApi.Models; + +namespace OpenChatApi.Services +{ + public interface IMongoDBService + { + Task GetUserMessages(string userId); + Task UpdateMessages(string userId, string userMessage, string assistantResponse); + Task UpdateSystemPrompt(string userId, string systemPrompt); + } +} diff --git a/OpenChatApi/Services/MongoDBService .cs b/OpenChatApi/Services/MongoDBService .cs new file mode 100644 index 0000000..dcf4074 --- /dev/null +++ b/OpenChatApi/Services/MongoDBService .cs @@ -0,0 +1,62 @@ +using Microsoft.Extensions.Options; +using MongoDB.Driver; +using OpenChatApi.Models; + +namespace OpenChatApi.Services +{ + public class MongoDBService : IMongoDBService + { + private readonly IMongoCollection _messagesCollection; + + public MongoDBService(IOptions settings) + { + var client = new MongoClient(settings.Value.ConnectionString); + var database = client.GetDatabase("ChatMessages"); + _messagesCollection = database.GetCollection("Messages"); + } + + public async Task GetUserMessages(string userId) + { + var messages = await _messagesCollection + .Find(x => x.UserId == userId) + .FirstOrDefaultAsync(); + + return messages ?? new MessageDocument { UserId = userId }; + } + public async Task UpdateSystemPrompt(string userId, string systemPrompt) + { + var update = Builders.Update + .Set(x => x.SystemPrompt, systemPrompt); + + await _messagesCollection.UpdateOneAsync( + x => x.UserId == userId, + update, + new UpdateOptions { IsUpsert = true } + ); + } + + public async Task UpdateMessages(string userId, string userMessage, string assistantResponse) + { + var document = await GetUserMessages(userId); + + // Add new messages + document.Messages.Add(new Message { Type = "user", Content = userMessage }); + document.Messages.Add(new Message { Type = "assistant", Content = assistantResponse }); + + // Keep only last 5 message pairs + if (document.Messages.Count > 10) + { + document.Messages = document.Messages.Skip(2).ToList(); + } + + document.LastMessage = DateTime.Now; + + // Upsert document + await _messagesCollection.ReplaceOneAsync( + x => x.UserId == userId, + document, + new ReplaceOptions { IsUpsert = true } + ); + } + } +} diff --git a/OpenChatApi/appsettings.Development.json b/OpenChatApi/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/OpenChatApi/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/OpenChatApi/appsettings.json b/OpenChatApi/appsettings.json new file mode 100644 index 0000000..52d2511 --- /dev/null +++ b/OpenChatApi/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "MongoDB": { + "ConnectionString": "mongodb://localhost:27017" + } +}