From 523ad16b3ca45c8647d222743e7cb0d284e23655 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Thu, 9 Apr 2026 17:02:13 +0330 Subject: [PATCH] last ... --- AI/DI/AIExtensions.cs | 27 +++++ AI/Interfaces/IXAIService.cs | 28 +++++ AI/Services/XAIService.cs | 66 ++++++++++++ Controllers/OSController.cs | 84 +++++++++++++++ Controllers/V1/AIController.cs | 62 +++++++++++ Controllers/V1/OllamaController.cs | 115 ++++++++++++++++++++ Helpers/XOllamaHelper.cs | 145 +++++++++++++++++++++++++ Helpers/XOsHelper.cs | 168 +++++++++++++++++++++++++++++ Startup.cs | 9 ++ xAiApi.csproj | 2 + 10 files changed, 706 insertions(+) create mode 100644 AI/DI/AIExtensions.cs create mode 100644 AI/Interfaces/IXAIService.cs create mode 100644 AI/Services/XAIService.cs create mode 100644 Controllers/OSController.cs create mode 100644 Controllers/V1/AIController.cs create mode 100644 Controllers/V1/OllamaController.cs create mode 100644 Helpers/XOllamaHelper.cs create mode 100644 Helpers/XOsHelper.cs diff --git a/AI/DI/AIExtensions.cs b/AI/DI/AIExtensions.cs new file mode 100644 index 0000000..7ab2e5e --- /dev/null +++ b/AI/DI/AIExtensions.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using xAiApi.AI.Interfaces; +using xAiApi.AI.Services; + +namespace xAiApi.AI.DI +{ + public static class AIExtensions + { + /// + /// Register AI Services ... + /// + /// + public static void AddXAIServices(this IServiceCollection services) + { + // + services.AddSingleton(); + } + + /// + /// Use AI Service Middlewares ... + /// + /// + public static void UseXAIServices(this IApplicationBuilder builder) + { } + } +} \ No newline at end of file diff --git a/AI/Interfaces/IXAIService.cs b/AI/Interfaces/IXAIService.cs new file mode 100644 index 0000000..cbade33 --- /dev/null +++ b/AI/Interfaces/IXAIService.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.AI; + +namespace xAiApi.AI.Interfaces +{ + public interface IXAIService + { + IChatClient TextGenreatorClient { get; } + IChatClient ImageGenreatorClient { get; } + + // + #region Actions ... + /// + /// Generated Text ... + /// + /// + /// + Task GetTextResponseAsync(string prompt); + + /// + /// Generated Response ... + /// + /// + /// + Task GetResponseAsync(string prompt); + #endregion + } +} \ No newline at end of file diff --git a/AI/Services/XAIService.cs b/AI/Services/XAIService.cs new file mode 100644 index 0000000..1e4a593 --- /dev/null +++ b/AI/Services/XAIService.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.AI; +using OllamaSharp; +using xAiApi.AI.Interfaces; +using xCommons.Extensions; +using xExceptions.Constants; + +namespace xAiApi.AI.Services +{ + public class XAIService : IXAIService + { + // + public IChatClient TextGenreatorClient { get; } + public IChatClient ImageGenreatorClient { get; } + + public XAIService() + { + // + // here we are Initialize Text Generator Model ... + TextGenreatorClient = new OllamaApiClient( + new Uri("http://localhost:11434"), + "gemma3:1b" + ); + } + + // + #region Actions ... + /// + /// Generated Response ... + /// + /// + /// + public async Task GetResponseAsync(string prompt) + { + // + // Validate ... + var isValid = !prompt.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Generate Response ... + var result = await TextGenreatorClient + .GetResponseAsync(prompt); + + // + return result; + } + + /// + /// Generated Text ... + /// + /// + /// + public async Task GetTextResponseAsync(string prompt) + { + // + var response = await GetResponseAsync(prompt); + return response.Text; + } + #endregion + } +} \ No newline at end of file diff --git a/Controllers/OSController.cs b/Controllers/OSController.cs new file mode 100644 index 0000000..ed9acc7 --- /dev/null +++ b/Controllers/OSController.cs @@ -0,0 +1,84 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xAiApi.Helpers; +using xCommons.Configurations; +using xCommons.Controllers; +using xCommons.Extensions; +using xCommons.Providers; +using xIdentityHelper; + +namespace xAiApi.Controllers +{ + public class OSController : XBaseController + { + public OSController( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { } + + // + #region Actions ... + /// + /// Get OS Type ... + /// + /// + [HttpGet("OSType")] + [Authorize(Policy = XPolicies.Admin)] + public ActionResult GetOSType() + { + // + // Do ... + try + { + // + var osType = XOsHelper.GetOSType(); + var result = osType.GetStringValue(); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get OS Description ... + /// + /// + [HttpGet("OSDescription")] + [Authorize(Policy = XPolicies.Admin)] + public ActionResult GetOSDescription() + { + // + // Do ... + try + { + // + var result = XOsHelper.GetOSDescription(); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} \ No newline at end of file diff --git a/Controllers/V1/AIController.cs b/Controllers/V1/AIController.cs new file mode 100644 index 0000000..e666888 --- /dev/null +++ b/Controllers/V1/AIController.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xAiApi.AI.Interfaces; +using xAiApi.Base; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; + +namespace xAiApi.Controllers.V1 +{ + public class AIController : XIBaseV1Controller + { + private readonly IXAIService aiService; + + public AIController( + IXAIService aiService, + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { + this.aiService = aiService; + } + + // + #region Actions ... + [AllowAnonymous] + [HttpGet("AskAI")] + public async Task> AskAI( + [FromQuery] string prompt + ) + { + // + // Do ... + try + { + // + var result = await aiService + .GetTextResponseAsync(prompt); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} \ No newline at end of file diff --git a/Controllers/V1/OllamaController.cs b/Controllers/V1/OllamaController.cs new file mode 100644 index 0000000..0810ab8 --- /dev/null +++ b/Controllers/V1/OllamaController.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xAiApi.Base; +using xAiApi.Helpers; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityHelper; +using xIdentityService.Interfaces; + +namespace xAiApi.Controllers.V1 +{ + public class OllamaController : XIBaseV1Controller + { + public OllamaController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { } + + // + #region Actions ... + /// + /// Retrieve Ollama Version ... + /// + /// + [HttpGet("Version")] + [Authorize(Policy = XPolicies.Admin)] + public async Task> GetVersion() + { + // + // Do ... + try + { + // + var result = await XOllamaHelper.GetVersion(); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve a List of Available models ... + /// + /// + [HttpGet("Models")] + [Authorize(Policy = XPolicies.Admin)] + public async Task>> GetModels() + { + // + // Do ... + try + { + // + var result = await XOllamaHelper.GetModels(); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find a model ... + /// + /// + [HttpGet("FindModel")] + [Authorize(Policy = XPolicies.Admin)] + public async Task>> FindModel( + [FromQuery] + string model + ) + { + // + // Do ... + try + { + // + var result = await XOllamaHelper.FindModel(model); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} \ No newline at end of file diff --git a/Helpers/XOllamaHelper.cs b/Helpers/XOllamaHelper.cs new file mode 100644 index 0000000..c4da791 --- /dev/null +++ b/Helpers/XOllamaHelper.cs @@ -0,0 +1,145 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using xCommons.Extensions; + +namespace xAiApi.Helpers +{ + public class XOllamaHelper + { + /// + /// Get Version ... + /// + /// + public static async Task GetVersion() + { + // + var cmd = "ollama"; + var args = "--version"; + var cmdResult = await XOsHelper.Execute(cmd, args); + + // + var result = + cmdResult.Error.IsNullOrEmpty() + ? cmdResult.Result + : cmdResult.Error; + + // + result = result.Replace("\n", ""); + + // + return result; + } + + /// + /// Get Exists Models ... + /// + /// + public static async Task> GetModels() + { + // + var cmd = "ollama"; + var args = "list"; + var cmdResult = await XOsHelper.Execute(cmd, args); + + // + var cmdResultText = + cmdResult.Error.IsNullOrEmpty() + ? cmdResult.Result + : cmdResult.Error; + + // + var cmdResultList = cmdResultText + .Split("\n") + .ToList(); + if (cmdResultList.Count > 0) + { + cmdResultList.RemoveAt(0); + } + + // + var result = new List(); + if (cmdResultList.Count > 0) + { + // + cmdResultList + .ForEach(i => + { + // + if (!i.IsNullOrEmpty()) + { + // + var d = i.Split(" "); + if (d.Length > 0) + { + result.Add(d[0]); + } + } + }); + } + + // + return result; + } + + /// + /// Check Specified Model Exists ... + /// + /// + /// + public static async Task HasModel(string model) + { + // + var result = !model.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + var models = await GetModels(); + result = models.HasChild(); + if (!result) + { + return result; + } + + // + result = models + .Any(n => n == model || n.ToNormalString().Contains(model.ToNormalString())); + return result; + } + + /// + /// Search Available Models ... + /// + /// + /// + public static async Task> FindModel(string model) + { + // + var result = new List(); + + // + if (model.IsNullOrEmpty()) + { + return result; + } + + // + var models = await GetModels(); + if (!models.HasChild()) + { + return result; + } + + // + result = models + .Where(n => n == model || n.ToNormalString().Contains(model.ToNormalString())) + .ToList(); + + // + return result; + } + } +} \ No newline at end of file diff --git a/Helpers/XOsHelper.cs b/Helpers/XOsHelper.cs new file mode 100644 index 0000000..900903c --- /dev/null +++ b/Helpers/XOsHelper.cs @@ -0,0 +1,168 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Attributes; +using xExceptions.Constants; + +namespace xAiApi.Helpers +{ + /// + /// Several OS Type ... + /// + public enum XOSType + { + [StringValue("Unknown")] + XUnknown, + + [StringValue("Mac")] + XMacOs, + + [StringValue("Linux")] + XLinux, + + [StringValue("Windows")] + XWindows, + + [StringValue("FreeBSD")] + XFreeBSD, + } + + /// + /// OS Command Execution Result ... + /// + public class XOSCMDResult + { + /// + /// Execution Result ... + /// + /// + public string Result { get; set; } + + /// + /// Execution Error ... + /// + /// + public string Error { get; set; } + } + + public static class XOsHelper + { + /// + /// Retrieve OS Type ... + /// + /// + public static XOSType GetOSType() + { + // + XOSType result = XOSType.XUnknown; + + // + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + result = XOSType.XMacOs; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + result = XOSType.XLinux; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + result = XOSType.XWindows; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + { + result = XOSType.XFreeBSD; + } + + // + return result; + } + + /// + /// Retrieve OS Description ... + /// + /// + public static string GetOSDescription() + { + // + var result = RuntimeInformation.OSDescription; + return result; + } + + /// + /// Execute an Specified Command ... + /// + /// Specified Command + /// Command Arguments + /// + public static async Task Execute( + string cmd, + string args + ) + { + // + var result = new XOSCMDResult + { + Error = "Unknown", + }; + + // + try + { + // + // Validate CMD ... + var isValid = !cmd.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Validate OS Type ... + XOSType osType = GetOSType(); + isValid = osType != XOSType.XUnknown; + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + ProcessStartInfo psi = new ProcessStartInfo(); + + // + psi.FileName = cmd; + psi.Arguments = args; + psi.UseShellExecute = false; + psi.RedirectStandardError = true; + psi.RedirectStandardOutput = true; + + // + Process process = Process.Start(psi); + + // + // Read the output + string output = process.StandardOutput.ReadToEnd(); + string error = process.StandardError.ReadToEnd(); + + // + await process.WaitForExitAsync(); + + // + result = new XOSCMDResult + { + Error = error, + Result = output, + }; + } + catch (Exception ex) + { + result.Error = ex.Message; + } + + // + return result; + } + } +} \ No newline at end of file diff --git a/Startup.cs b/Startup.cs index 294ce8f..4b46c20 100644 --- a/Startup.cs +++ b/Startup.cs @@ -27,6 +27,7 @@ using xAiApi.DI; using xAiApi.Data.Helpers; using xAiApi.Data; using xAiApi.Data.Seeder; +using xAiApi.AI.DI; // using xApi.Extensions; // using xDataHelper; // using xDataHelper.DbSeeder; @@ -201,6 +202,10 @@ namespace xAiApi }; }); #endregion + + // + // Register AI Service ... + services.AddXAIServices(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) @@ -263,6 +268,10 @@ namespace xAiApi // // Use xGraphQL Middleware ... app.UseXGraphQL(withPlayground: withPlayground); + + // + // Use AI Services ... + app.UseXAIServices(); } } } \ No newline at end of file diff --git a/xAiApi.csproj b/xAiApi.csproj index 6d917b9..da9bf91 100644 --- a/xAiApi.csproj +++ b/xAiApi.csproj @@ -29,6 +29,8 @@ + +