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