From 44361e73ce620651586466ba5ea1d8299abc6d90 Mon Sep 17 00:00:00 2001 From: "Amir.H Khademi" Date: Mon, 12 May 2025 16:18:01 +0330 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Update=20project=20configuration=20?= =?UTF-8?q?and=20add=20AI=20chat=20functionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 🐳 Update `.dockerignore` to include `README.md` Exclude additional Docker-related files for cleaner builds. - 🔧 Modify `HealthController.cs` to use `IRestApiWrapper` Enhance health check functionality with REST API integration. - 🐋 Change exposed port in `Dockerfile` to `5245` Update build process to use configuration arguments. - 📦 Upgrade `Refit` package to version `7.2.1` Add `Refit.Newtonsoft.Json` for improved JSON handling. - 📜 Add using directives in `Brizco.Infrastructure.csproj` Include necessary namespaces for new functionality. - 🔗 Update `IRestApiWrapper` to include `IMetisRestApi` Enhance API wrapper with new Metis API integration. - 🛠️ Create `launch.json` for .NET Core debugging Simplify debugging process with Docker settings. - 📝 Add `tasks.json` for build and publish tasks Define tasks for building, publishing, and Docker operations. - 🤖 Introduce `AiController.cs` for AI chat bot endpoint Implement endpoint to handle chat requests using REST API. - 📂 Update `AiCommands.cs` with correct namespace Ensure proper organization and access to commands. - 🆕 Add new data classes for AI functionality Introduce `CreateSessionRequestDto`, `MetisMessageRequest`, etc. - 📡 Create `IMetisRestApi` interface for chat sessions Define API endpoints for managing chat interactions. --- Changes made by Amir.h Khademi --- .dockerignore | 4 +- .vscode/launch.json | 44 ++++++++ .vscode/tasks.json | 101 ++++++++++++++++++ Brizco.Api/Controllers/AiController.cs | 31 ++++++ Brizco.Api/Controllers/HealthController.cs | 7 +- Brizco.Api/Dockerfile | 20 ++-- .../CommandQueries/Commands/AiCommands.cs | 1 + .../Brizco.Infrastructure.csproj | 6 +- .../Models/Metis/CreateSessionRequestDto.cs | 9 ++ .../Models/Metis/MetisMessageRequest.cs | 12 +++ .../Models/Metis/MetisMessageResponse.cs | 13 +++ .../Models/Metis/MetisUser.cs | 7 ++ .../Models/RestApi/Metris/IMetisRestApi.cs | 13 +++ .../RestServices/IRestApiWrapper.cs | 7 ++ 14 files changed, 260 insertions(+), 15 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Brizco.Api/Controllers/AiController.cs create mode 100644 Brizco.Domain/CommandQueries/Commands/AiCommands.cs create mode 100644 Brizco.Infrastructure/Models/Metis/CreateSessionRequestDto.cs create mode 100644 Brizco.Infrastructure/Models/Metis/MetisMessageRequest.cs create mode 100644 Brizco.Infrastructure/Models/Metis/MetisMessageResponse.cs create mode 100644 Brizco.Infrastructure/Models/Metis/MetisUser.cs create mode 100644 Brizco.Infrastructure/Models/RestApi/Metris/IMetisRestApi.cs diff --git a/.dockerignore b/.dockerignore index 3729ff0..3dbbcf3 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,10 +11,10 @@ **/*.*proj.user **/*.dbmdl **/*.jfm -**/azds.yaml **/bin **/charts **/docker-compose* +**/compose* **/Dockerfile* **/node_modules **/npm-debug.log @@ -22,4 +22,4 @@ **/secrets.dev.yaml **/values.dev.yaml LICENSE -README.md \ No newline at end of file +README.md diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b0efed2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,44 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/Brizco.Api/bin/Debug/net8.0/Brizco.Api.dll", + "args": [], + "cwd": "${workspaceFolder}/Brizco.Api", + "stopAtEntry": false, + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + }, + { + "name": "Docker .NET Launch", + "type": "docker", + "env": { + "ASPNETCORE_URLS": "http://+:80", + "TZ": "Asia/Tehran" + }, + "request": "launch", + "DockerfileRunArguments": " --network=mother -p 32767:80", + "preLaunchTask": "docker-run: debug", + "netCore": { + "appProject": "${workspaceFolder}/Brizco.Api/Brizco.Api.csproj" + } + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..c4c02fe --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,101 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Brizco.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/Brizco.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/Brizco.sln" + ], + "problemMatcher": "$msCompile" + }, + { + "type": "docker-build", + "label": "docker-build: debug", + "dependsOn": [ + "build" + ], + "dockerBuild": { + "tag": "brizco:dev", + "target": "base", + "dockerfile": "${workspaceFolder}/Brizco.Api/Dockerfile", + "context": "${workspaceFolder}", + "pull": true + }, + "netCore": { + "appProject": "${workspaceFolder}/Brizco.Api/Brizco.Api.csproj" + } + }, + { + "type": "docker-build", + "label": "docker-build: release", + "dependsOn": [ + "build" + ], + "dockerBuild": { + "tag": "brizco:latest", + "dockerfile": "${workspaceFolder}/Brizco.Api/Dockerfile", + "context": "${workspaceFolder}", + "platform": { + "os": "linux", + "architecture": "amd64" + }, + "pull": true + }, + "netCore": { + "appProject": "${workspaceFolder}/Brizco.Api/Brizco.Api.csproj" + } + }, + { + "type": "docker-run", + "label": "docker-run: debug", + "dependsOn": [ + "docker-build: debug" + ], + "dockerRun": {}, + "netCore": { + "appProject": "${workspaceFolder}/Brizco.Api/Brizco.Api.csproj", + "enableDebugging": true + } + }, + { + "type": "docker-run", + "label": "docker-run: release", + "dependsOn": [ + "docker-build: release" + ], + "dockerRun": {}, + "netCore": { + "appProject": "${workspaceFolder}/Brizco.Api/Brizco.Api.csproj" + } + } + ] +} \ No newline at end of file diff --git a/Brizco.Api/Controllers/AiController.cs b/Brizco.Api/Controllers/AiController.cs new file mode 100644 index 0000000..05dc3ab --- /dev/null +++ b/Brizco.Api/Controllers/AiController.cs @@ -0,0 +1,31 @@ +using Brizco.Infrastructure.Models.Metis; +using Brizco.Infrastructure.RestServices; + +namespace Brizco.Api.Controllers; + +public class AiController : ICarterModule +{ + public void AddRoutes(IEndpointRouteBuilder app) + { + var group = app.NewVersionedApi("Ai").MapGroup("api/ai"); + group.MapGet("chat", ChatAsync) + .WithDisplayName("AiChatBot") + .HasApiVersion(1.0); + } + + private async Task ChatAsync([FromQuery]string message, [FromServices] IRestApiWrapper apiWrapper, CancellationToken cancellationToken) + { + + var messageRequest = new MetisMessageRequest + { + message = new MetisMessage + { + content = message + } + }; + var response = await apiWrapper.MetisRestApi.SendMessage("6519455b-9e7f-4faf-8b87-8f0073c730b0", messageRequest, "tpsg-pzDUto3eFhQE9oNzPlSsLZHTkeSvYf5"); + //var json = response.Content.Replace("```json", "").Replace("```", "").Trim(); + //var complex = JsonConvert.DeserializeObject(json); + return TypedResults.Ok(response.Content); + } +} \ No newline at end of file diff --git a/Brizco.Api/Controllers/HealthController.cs b/Brizco.Api/Controllers/HealthController.cs index 5d4f54c..ff0ec65 100644 --- a/Brizco.Api/Controllers/HealthController.cs +++ b/Brizco.Api/Controllers/HealthController.cs @@ -1,5 +1,6 @@ using MD.PersianDateTime.Standard; using System.Diagnostics; +using Brizco.Infrastructure.RestServices; namespace Brizco.Api.Controllers; @@ -14,15 +15,15 @@ public class HealthController : ICarterModule .WithDisplayName("CheckHealth") .HasApiVersion(1.0); } - public IResult GetHealth() + public async Task GetHealth([FromServices] IRestApiWrapper apiWrapper) { var version = typeof(Program)?.Assembly.GetName()?.Version?.ToString(); var check = new HealthCheck { Health = true, Version = version ?? string.Empty, - StartAt = System.Diagnostics.Process.GetCurrentProcess().StartTime.ToString("F"), - StartAtPersian = new PersianDateTime(System.Diagnostics.Process.GetCurrentProcess().StartTime).ToLongDateTimeString(), + StartAt = Process.GetCurrentProcess().StartTime.ToString("F"), + StartAtPersian = new PersianDateTime(Process.GetCurrentProcess().StartTime).ToLongDateTimeString(), MachineName = Environment.MachineName }; var process = Process.GetCurrentProcess(); diff --git a/Brizco.Api/Dockerfile b/Brizco.Api/Dockerfile index 0b30706..5d5f274 100644 --- a/Brizco.Api/Dockerfile +++ b/Brizco.Api/Dockerfile @@ -1,22 +1,24 @@ -#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. - FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base -ENV ASPNETCORE_URLS=https://0.0.0.0:8010 WORKDIR /app -EXPOSE 8010 +EXPOSE 5245 -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ENV ASPNETCORE_URLS=http://+:5245 + +USER app +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG configuration=Release WORKDIR /src -COPY ["Brizco.Api.csproj", "Brizco.Api/"] +COPY ["Brizco.Api/Brizco.Api.csproj", "Brizco.Api/"] RUN dotnet restore "Brizco.Api/Brizco.Api.csproj" COPY . . WORKDIR "/src/Brizco.Api" -RUN dotnet build "Brizco.Api.csproj" -c Release -o /app/build +RUN dotnet build "Brizco.Api.csproj" -c $configuration -o /app/build FROM build AS publish -RUN dotnet publish "Brizco.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false +ARG configuration=Release +RUN dotnet publish "Brizco.Api.csproj" -c $configuration -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "Berizco.Api.dll"] \ No newline at end of file +ENTRYPOINT ["dotnet", "Brizco.Api.dll"] diff --git a/Brizco.Domain/CommandQueries/Commands/AiCommands.cs b/Brizco.Domain/CommandQueries/Commands/AiCommands.cs new file mode 100644 index 0000000..d3cd91b --- /dev/null +++ b/Brizco.Domain/CommandQueries/Commands/AiCommands.cs @@ -0,0 +1 @@ +namespace Brizco.Domain.CommandQueries.Commands; diff --git a/Brizco.Infrastructure/Brizco.Infrastructure.csproj b/Brizco.Infrastructure/Brizco.Infrastructure.csproj index 6074445..c7bfd31 100644 --- a/Brizco.Infrastructure/Brizco.Infrastructure.csproj +++ b/Brizco.Infrastructure/Brizco.Infrastructure.csproj @@ -7,8 +7,9 @@ - + + @@ -29,10 +30,13 @@ + + + diff --git a/Brizco.Infrastructure/Models/Metis/CreateSessionRequestDto.cs b/Brizco.Infrastructure/Models/Metis/CreateSessionRequestDto.cs new file mode 100644 index 0000000..f276917 --- /dev/null +++ b/Brizco.Infrastructure/Models/Metis/CreateSessionRequestDto.cs @@ -0,0 +1,9 @@ +namespace Brizco.Infrastructure.Models.Metis; + +public class CreateSessionRequestDto +{ + public string BotId { get; set; } = string.Empty; + public MetisUser? User { get; set; } + public MetisMessageRequest? InitializeMessage { get; set; } +} + diff --git a/Brizco.Infrastructure/Models/Metis/MetisMessageRequest.cs b/Brizco.Infrastructure/Models/Metis/MetisMessageRequest.cs new file mode 100644 index 0000000..8bfdadd --- /dev/null +++ b/Brizco.Infrastructure/Models/Metis/MetisMessageRequest.cs @@ -0,0 +1,12 @@ +namespace Brizco.Infrastructure.Models.Metis; + +public class MetisMessageRequest +{ + public MetisMessage message { get; set; } = new(); +} + +public class MetisMessage +{ + public string content { get; set; } = string.Empty; + public string type { get; set; } = "USER"; +} \ No newline at end of file diff --git a/Brizco.Infrastructure/Models/Metis/MetisMessageResponse.cs b/Brizco.Infrastructure/Models/Metis/MetisMessageResponse.cs new file mode 100644 index 0000000..c72940c --- /dev/null +++ b/Brizco.Infrastructure/Models/Metis/MetisMessageResponse.cs @@ -0,0 +1,13 @@ +namespace Brizco.Infrastructure.Models.Metis; + +public class MetisMessageResponse +{ + public string Id { get; set; } = string.Empty; + public string Type { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; + public object Attachments { get; set; } = string.Empty; + public long Timestamp { get; set; } + public string FinishReason { get; set; } = string.Empty; + public object Citations { get; set; } = string.Empty; + public object ToolCalls { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Brizco.Infrastructure/Models/Metis/MetisUser.cs b/Brizco.Infrastructure/Models/Metis/MetisUser.cs new file mode 100644 index 0000000..20403c0 --- /dev/null +++ b/Brizco.Infrastructure/Models/Metis/MetisUser.cs @@ -0,0 +1,7 @@ +namespace Brizco.Infrastructure.Models.Metis; + +public class MetisUser +{ + public string Name { get; set; } = string.Empty; + public string Id { get; set; } = Guid.NewGuid().ToString("N").Substring(0, 8); +} \ No newline at end of file diff --git a/Brizco.Infrastructure/Models/RestApi/Metris/IMetisRestApi.cs b/Brizco.Infrastructure/Models/RestApi/Metris/IMetisRestApi.cs new file mode 100644 index 0000000..8b91448 --- /dev/null +++ b/Brizco.Infrastructure/Models/RestApi/Metris/IMetisRestApi.cs @@ -0,0 +1,13 @@ +namespace Brizco.Infrastructure.Models.RestApi.Metris; + +public interface IMetisRestApi +{ + [Post("/chat/session")] + public Task CreateSession(string sessionId, [Body]CreateSessionRequestDto request, [Header("x-api-key")] string metisToken); + + [Post("/chat/session/{sessionId}/message")] + public Task SendMessage(string sessionId, [Body]MetisMessageRequest request, [Header("x-api-key")] string metisToken); + + [Post("/chat/session/{sessionId}/message/stream")] + public Task SendStreamMessage(string sessionId, [Body]MetisMessageRequest request, [Header("x-api-key")] string metisToken); +} \ No newline at end of file diff --git a/Brizco.Infrastructure/RestServices/IRestApiWrapper.cs b/Brizco.Infrastructure/RestServices/IRestApiWrapper.cs index d30099b..d33965a 100644 --- a/Brizco.Infrastructure/RestServices/IRestApiWrapper.cs +++ b/Brizco.Infrastructure/RestServices/IRestApiWrapper.cs @@ -3,9 +3,16 @@ public interface IRestApiWrapper : IScopedDependency { IKaveNegarRestApi KaveNegarRestApi { get; } + IMetisRestApi MetisRestApi { get; } } public class RestApiWrapper : IRestApiWrapper { + private static readonly RefitSettings setting = new RefitSettings(new NewtonsoftJsonContentSerializer(new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + + })); public IKaveNegarRestApi KaveNegarRestApi => RestService.For(RestAddress.BaseKaveNegar); + public IMetisRestApi MetisRestApi => RestService.For("https://api.metisai.ir/api/v1", setting); } \ No newline at end of file