diff --git a/Documents/Ai Contents.txt b/Documents/Ai Contents.txt new file mode 100644 index 0000000..6548cac --- /dev/null +++ b/Documents/Ai Contents.txt @@ -0,0 +1,4 @@ +bge-m3:latest 790764642607 1.2 GB 4 weeks ago +qwen2.5-coder:14b 9ec8897f747e 9.0 GB 8 weeks ago +nomic-embed-text:latest 0a109f422b47 274 MB 8 weeks ago +gemma3:1b 8648f39daa8f 815 MB 7 months ago diff --git a/Documents/GeneratedContext/xDashboard-xAiApi-20260919_112558.txt b/Documents/GeneratedContext/xDashboard-xAiApi-20260919_112558.txt new file mode 100644 index 0000000..a4edeb2 --- /dev/null +++ b/Documents/GeneratedContext/xDashboard-xAiApi-20260919_112558.txt @@ -0,0 +1,2650 @@ + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Program.cs + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace xAiApi +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Startup.cs + +using System; +using System.IO; +using System.Linq; +using xTagService.DI; +using System.Net.Http; +using xAiApi.Database; +using xHttpService.DI; +using xPushService.DI; +using xFileService.DI; +using xDataService.DI; +using Newtonsoft.Json; +using System.Reflection; +using xAiApi.Extensions; +using xStringService.DI; +using xStorageService.DI; +using xIdentityService.DI; +using xDataService.Models; +using xCommons.Extensions; +using xCommons.Configurations; +using System.Collections.Generic; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json.Serialization; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using IdentityModel.AspNetCore.OAuth2Introspection; +using xPushService.Helpers; +using xAiApi.DI; +using xAiApi.Interfaces; +using xAiApi.Providers; +using xAiModels.DI; + +namespace xAiApi +{ + public class Startup + { + // + #region Props ... + public IConfiguration Configuration { get; } + private readonly List databases; + private readonly XAiApiDatabaseDescriptor apiDatabaseDescriptor; + #endregion + + // + #region Constructor(s) ... + public Startup(IConfiguration configuration) + { + // + Configuration = configuration; + + // + // Create an Api Descriptor Class Instance ... + apiDatabaseDescriptor = new XAiApiDatabaseDescriptor(); + databases = new List + { + apiDatabaseDescriptor + }; + } + #endregion + + public void ConfigureServices(IServiceCollection services) + { + // + // Register Validation Provider and all XCommons Module Services ... + services.AddXCommons(); + + // + // Register App Configuration ... + services.AddXAppConfiguration(Configuration); + var appConfiguration = services.GetRegisteredService(); + + // + // Register Allowed Origins ... + services.AddXCors(appConfiguration.AllowedOrigins); + + // + // Register Swagger ... + var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + services.AddXSwagger(Configuration, xmlFilePath: xmlPath); + + // + // Register HttpService ... + services.AddXHttpService(Configuration); + + // + // Add Api V1 Versioning ... + services.AddXApiV1Versioning(); + + // + // OAuthIntrospectin Http Client Handler ... + // this is for handling SSLErrors ... + services.AddHttpClient(OAuth2IntrospectionDefaults.BackChannelHttpClientName) + .ConfigurePrimaryHttpMessageHandler(() => + { + return new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true + }; + }); + + // + // Register Authorizations ... + services.AddXAuthorization(); + + // + services.AddControllers() + .AddNewtonsoftJson(x => + { + x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + x.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + }); + + // + var lifeTime = ServiceLifetime.Scoped; + var repositoryType = xDataService.Constants.XRepositoryType.EF; + + // + // Register XIdentity Service ... + services.AddXIdentityService( + lifeTime: lifeTime, + configuration: Configuration + ); + + // + // Storage Service ... + services.AddXStorageService(Configuration); + + // + // Push Service ... + services.AddXPushService( + lifeTime: lifeTime, + config: Configuration + ); + + // + // Preparing DataBases ... + if (databases.HasChild()) + { + // + databases + .ToList() + .ForEach(database => + { + // + // Register DataBase ... + services.AddXDatabase( + lifeTime: lifeTime, + descriptor: database, + configuration: Configuration + ); + }); + } + + // + // Register String Service ... + services.AddXStringService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + + // + // Register Tag Service ... + services.AddXTagService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + + // + // Register File Service ... + services.AddXFileService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + + // + // Register AiApi Data Service ... + services.AddXAiDataService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + + // + // Register XAiApi Configuration ... + services.AddXAiApiConfiguration(Configuration); + + // + // Register Default Ai Service ... + services.AddScoped(); + services.AddScoped(); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + // + // Configure the HTTP request pipeline. + var isDevelopmentEnvironment = false; + if (env.IsDevelopment()) + { + isDevelopmentEnvironment = true; + app.UseDeveloperExceptionPage(); + } + + // + // Use Swagger Middleware ... + app.UseXSwagger(); + + // + // Force Https Redirection ... + app.UseHttpsRedirection(); + + // + // Using Cors ... + app.UseXCors(); + + // + // Using Routing ... + app.UseRouting(); + + // + // Use xIdentityService ... + app.UseXIdentityService(); + + // + // Use Authorization ... + app.UseAuthorization(); + + // + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + + // + // Use Storage Service ... + app.UseXStorageService(); + + // + // Use PushService ... + var pushHelper = new XPushServiceHelper(); + app.UseXPushService(pushHelper); + + // + // Using DataBase ... + if (databases.HasChild()) + { + // + databases + .ToList() + .ForEach(database => + { + // + app.UseXDatabase( + descriptor: database, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + }); + } + + // + // Using String Service ... + app.UseXStringService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + + // + // Using XTag Service ... + app.UseXTagService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + + // + // Using File Service ... + app.UseXFileService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Configurations\XAiApiConfiguration.cs + +using xAiApi.Models; + +namespace xAiApi.Configurations +{ + public class XAiApiConfiguration + { + /// + /// Allowed Models to Use ... + /// + public XAiModelDescriptor[] Models { get; set; } = []; + + /// + /// Allowed Prompts to Use ... + /// + public XAiPromptDescriptor[] Prompts { get; set; } = []; + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Constants\ConfigurationNodeNames.cs + +namespace xAiApi.Constants +{ + public partial struct ConfigurationNodeNames + { + public const string AI_API_NODE = "AiAPiConfiguration"; + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Constants\XAiApiConstants.cs + +namespace xAiApi.Constants +{ + public struct XAiApiConstants + { + // + // Database Descriptor ... + public const string XAiApiDbIdentifier = "xAiApiDb"; + public const string XAiApiMigrationsAssemblyIdentifier = "xAiApi"; + + // + public const string XDILogTag = "XAiApi"; + public const string XAiApiIntroductionPromptName = "Introduction"; + + // + public const string XAiEmbeddingModelName = "Embed"; + public const string XAiEmbeddingPromptName = "Embedding"; + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Constants\XAiProvider.cs + +namespace xAiApi.Constants +{ + public enum XAiProvider + { + None, + Ollama, + DeepSeek, + HuggingFace + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Controllers\DefaultAiController.cs + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xAiApi.Interfaces; +using xCommons.Configurations; +using xCommons.Providers; + +namespace xAiApi.Controllers +{ + [Authorize] + [Route ("[controller]")] + public class DefaultAiController : XAiServiceControllerBase + { + public DefaultAiController( + ILogger logger, + IXDefaultAiService aiService, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + aiService, + appConfiguration, + validationProvider + ) + { } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Controllers\DefaultEmbeddingController.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xAiApi.Interfaces; +using xCommons.Configurations; +using xCommons.Providers; + +namespace xAiApi.Controllers +{ + [Authorize] + [Route("[controller]")] + public class DefaultEmbeddingController : XAiEmbeddingServiceControllerBase + { + public DefaultEmbeddingController( + ILogger logger, + IXDefaultEmbeddingService aiService, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + aiService, + appConfiguration, + validationProvider + ) + { } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Controllers\StartupController.cs + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Controllers; +using xCommons.Providers; + +namespace xAiApi.Controllers +{ + /// + /// Runing when application start ... + /// + [Route("")] + [AllowAnonymous] + public class StartupController : XBaseController + { + + public StartupController( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { } + + /// + /// Show Configured Welcome Message + /// + /// + [HttpGet("")] + [AllowAnonymous] + public virtual ActionResult Index() + { + // + var controllerName = GetControllerName(); + var message = $"{AppConfiguration.WelcomeMessage}"; + + // + return Ok(message); + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Controllers\XAiEmbeddingServiceControllerBase.cs + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xAiApi.Interfaces; +using xCommons.Configurations; +using xCommons.Providers; + +namespace xAiApi.Controllers +{ + [Authorize] + [Route("[controller]")] + public abstract class XAiEmbeddingServiceControllerBase : XAiServiceControllerBase, IXAiEmbeddingServiceControllerBase + where TAiService : IXAiEmbeddingServiceBase + { + private readonly TAiService aiService; + + protected XAiEmbeddingServiceControllerBase( + ILogger logger, + TAiService aiService, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + aiService, + appConfiguration, + validationProvider + ) + { + this.aiService = aiService; + } + + /// + /// Request For Embedding Specified String ... + /// + /// + /// + /// + [AllowAnonymous] + [HttpGet("Embeding")] + public async Task> Embeding( + [FromQuery] string content, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + ValidationProvider.NotEmpty(content); + + // + var result = await aiService.Embeding( + content: content, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Request For Embedding Specified Batch ... + /// + /// + /// + /// + [AllowAnonymous] + [HttpGet("Embedings")] + public async Task>> Embedings( + [FromQuery] string[] batch, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + ValidationProvider.NotZeroChilds(batch); + + // + var result = await aiService.Embedings( + batch: batch, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Controllers\XAiServiceControllerBase.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xAiApi.Interfaces; +using xCommons.Configurations; +using xCommons.Controllers; +using xCommons.Providers; + +namespace xAiApi.Controllers +{ + [Authorize] + [Route ("[controller]")] + public abstract class XAiServiceControllerBase : XBaseController, IXAiServiceControllerBase + where TAiService : IXAiServiceBase + { + private readonly TAiService aiService; + + protected XAiServiceControllerBase( + ILogger logger, + TAiService aiService, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { + this.aiService = aiService; + } + + /// + /// Ask Question and Get Response ... + /// + /// + /// + /// + [AllowAnonymous] + [HttpGet("Ask")] + public virtual async Task> Ask( + [FromQuery] string question, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + ValidationProvider.NotEmpty(question); + + // + var result = await aiService.AskAsync( + prompt: question, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Database\XAiApiDatabaseDescriptor.cs + +using xAiApi.Constants; +using xDataService.Interfaces; +using xDataService.Models; + +namespace xAiApi.Database +{ + /// + /// Ai Api Database Descriptor ... + /// + public class XAiApiDatabaseDescriptor : XDatabaseDescriptor, IXDatabaseDescriptor + { + public XAiApiDatabaseDescriptor() : base( + name: XAiApiConstants.XAiApiDbIdentifier, + repositories: [] + ) + { + OptionsBuilder = b => b.MigrationsAssembly(XAiApiConstants.XAiApiMigrationsAssemblyIdentifier); + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Database\XAiApiDbContext.cs + +using Microsoft.EntityFrameworkCore; +using xAiApi.Constants; +using xAiModels.Providers; +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Providers; +using xFileService.Providers; +using xStringService.Providers; +using xTagService.Providers; + +namespace xAiApi.Database +{ + /// + /// Db Context of AiApi ... + /// + public class XAiApiDbContext : XDbContext + { + // + #region DbSets ... + #endregion + + // + #region Navigation Property Entities ... + #endregion + + // + #region Constructor ... + public XAiApiDbContext( + DbContextOptions options, + XDataServiceConfiguration config, + XEntityRegisterarHandlerService dynamicEntityHandlers + ) : base( + config: config, + options: options, + database: XAiApiConstants.XAiApiDbIdentifier, + dynamicEntityHandlers: dynamicEntityHandlers, + dynamicEntityRegisterarsNames: [ + nameof(XTagEntityRegisterar), + nameof(XFileEntityRegisterar), + nameof(XStringEntityRegisterar), + nameof(XAiMessageEntityRegisterar), + nameof(XAiProjectEntityRegisterar), + nameof(XAiConversationEntityRegisterar), + ] + ) + { } + #endregion + + // + #region Overrides ... + // + public override void OnXModelCreating(ModelBuilder modelBuilder) + { } + + // + public override void OnXConfiguring(DbContextOptionsBuilder optionsBuilder) + { } + #endregion + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\DI\XDIHelperExtension.cs + +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xAiApi.Configurations; +using xAiApi.Constants; +using xAiApi.Extensions; +using xCommons.Extensions; +using xExceptions.Constants; + +namespace xAiApi.DI +{ + public static partial class XDIHelperExtension + { + /// + /// Extract AI Configuration ... + /// + /// + /// + public static XAiApiConfiguration GetXAiApiConfiguration(this IConfiguration source) + { + // + XAiApiConfiguration result = null; + + // + var section = source.GetSection(ConfigurationNodeNames.AI_API_NODE); + if (!section.IsNull()) + { + result = section.Get(); + } + + // + return result; + } + + /// + /// Register AiApi Configuration ... + /// + /// + /// + public static void AddXAiApiConfiguration( + this IServiceCollection source, + IConfiguration configuration + ) + { + // + var apiConfig = configuration.GetXAiApiConfiguration(); + source.AddXAiApiConfiguration(apiConfig); + } + + /// + /// Register AiApi Configuration ... + /// + /// + /// + public static void AddXAiApiConfiguration( + this IServiceCollection source, + XAiApiConfiguration configuration + ) + { + // + if (!configuration.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + + // + source.AddSingleton(configuration); + } + + // + #region Private ... + /// + /// a LogTag for Service ... + /// + private static string XLogTag = XAiApiConstants.XDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + #endregion + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Extensions\XModelsExtensions.cs + +using System.Collections.Generic; +using System.Linq; +using xAiApi.Configurations; +using xAiApi.Constants; +using xAiApi.Models; +using xCommons.Extensions; + +namespace xAiApi.Extensions +{ + public static class XModelsExtensions + { + /// + /// Validate a Model Descriptor ... + /// + /// + /// + public static bool IsValid(this XAiModelDescriptor source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Name.IsNullOrEmpty() && + !source.Url.IsNullOrEmpty() && + source.Url.IsValidUrl() && + !source.LLM.IsNullOrEmpty() && + source.Provider != Constants.XAiProvider.None; + + // + return result; + } + + /// + /// Validate Configurations ... + /// + /// + /// + public static bool IsValid(this XAiApiConfiguration source) + { + // + var result = + !source.IsNullOrDefault() && + source.Models.HasChild() && + source.Models.All(m => m.IsValid()) && + source.Prompts.All(m => m.IsValid()) && + source.Prompts.Any(p => p.Name + .Equals( + XAiApiConstants.XAiApiIntroductionPromptName, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Validate Specified Param ... + /// + /// + /// + public static bool IsValid(this XAiPromptParam source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Name.IsNullOrEmpty() && + !source.Param.IsNullOrEmpty(); + + // + return result; + } + + /// + /// Validate Specified Prompt ... + /// + /// + /// + public static bool IsValid(this XAiPromptDescriptor source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Name.IsNullOrEmpty() && + !source.Template.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + if (source.Params.HasChild()) + { + result = + result && + source.Params.All(x => x.IsValid()); + } + + // + return result; + } + + /// + /// Extract Default Model Descriptor ... + /// + /// + /// + public static XAiModelDescriptor GetDefaultModel(this XAiApiConfiguration source) + { + // + XAiModelDescriptor result = null; + + // + if (source.IsValid()) + { + result = source.Models[0]; + } + + // + return result; + } + + /// + /// Retrieve Specified Model from Config ... + /// + /// + /// + /// + public static XAiModelDescriptor GetModel( + this XAiApiConfiguration source, + string name + ) + { + // + XAiModelDescriptor result = null; + + // + if (source.IsValid()) + { + // + if (name.IsNullOrEmpty()) + { + result = source.GetDefaultModel(); + } + else + { + result = source.Models + .FirstOrDefault(m => m.Name + .Equals(name, System.StringComparison.InvariantCultureIgnoreCase)); + } + } + + // + return result; + } + + /// + /// Retrieve Introduction Prompt ... + /// + /// + /// + public static XAiPromptDescriptor GetIntroductionPrompt(this XAiApiConfiguration source) + { + // + XAiPromptDescriptor result = null; + + // + if (source.IsValid()) + { + // + result = source.Prompts + .FirstOrDefault(p => p.Name + .Equals( + XAiApiConstants.XAiApiIntroductionPromptName, + System.StringComparison.InvariantCultureIgnoreCase)); + } + + // + return result; + } + + /// + /// Select Specified Prompts and Parse it's Template + /// and Generate Message ... + /// + /// + /// + /// + /// + public static string GetPrompt( + this XAiApiConfiguration source, + string name = null, + IDictionary @params = null + ) + { + // + var result = string.Empty; + + // + // Normalize ... + if (name.IsNullOrEmpty()) + { + name = XAiApiConstants.XAiApiIntroductionPromptName; + } + + // + if (source.IsValid()) + { + // + var prompt = source.Prompts + .FirstOrDefault(p => p.Name + .Equals( + name, + System.StringComparison.InvariantCultureIgnoreCase + ) + ); + if (!prompt.IsNullOrDefault()) + { + // + result = prompt.Template; + + // + // Handle Params ... + if (!@params.IsNullOrDefault() && @params.HasChild()) + { + // + // Select Params ... + var existsParams = prompt.Params + .Where(p => @params.Any(pr => pr.Key == p.Name)) + .Distinct() + .ToList(); + + // + // Replace Selected Params ... + existsParams + .ForEach(pk => + { + result = result.Replace(pk.Param, @params[pk.Name]); + }); + } + } + } + + // + return result; + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Extensions\XProgramExtensions.cs + +using System.Collections.Generic; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Authorization; +using xCommons.Extensions; +using xIdentityHelper; + +namespace xAiApi.Extensions +{ + public static class XProgramExtensions + { + /// + /// Register Authorization Policies + /// + /// + /// + public static void AddXAuthorization( + this IServiceCollection services, + IDictionary policies = null + ) + { + // + services.AddAuthorization(options => + { + // + if (!policies.IsNull()) + { + // + // Fill Policies ... + using (var policiesEnumerator = policies.GetEnumerator()) + { + while (policiesEnumerator.MoveNext()) + { + var policyDescriptor = policiesEnumerator.Current; + options.AddPolicy(policyDescriptor.Key, policyDescriptor.Value); + } + } + } + + // + var xPolicies = XAuthorizationHelper.GetXAuthorizationPolicies(); + if (!xPolicies.IsNull()) + { + // + // Fill Policies ... + using (var policiesEnumerator = xPolicies.GetEnumerator()) + { + while (policiesEnumerator.MoveNext()) + { + var policyDescriptor = policiesEnumerator.Current; + options.AddPolicy(policyDescriptor.Key, policyDescriptor.Value); + } + } + } + }); + + // + services.AddSingleton(); + } + + /// + /// Use Forward Headers Options for resolving behind a proxy issues ... + /// + /// + /// + public static void UseXForwardOptions(this IApplicationBuilder source) + { + // + var forwardOptions = new ForwardedHeadersOptions + { + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto, + RequireHeaderSymmetry = false + }; + + // + forwardOptions.KnownNetworks.Clear(); + forwardOptions.KnownProxies.Clear(); + + // + // ref: https://github.com/aspnet/Docs/issues/2384 + source.UseForwardedHeaders(forwardOptions); + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\IXAiEmbeddingServiceBase.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using OllamaSharp; + +namespace xAiApi.Interfaces +{ + /// + /// Base Embedding Provider Service ... + /// + public interface IXAiEmbeddingServiceBase : IXAiServiceBase + { + /// + /// Embedding Specified String ... + /// + /// + /// + /// + Task Embeding( + string content, + CancellationToken cancellationToken = default + ); + + /// + /// Embedding Specified Batch ... + /// + /// + /// + /// + Task> Embedings( + string[] batch, + CancellationToken cancellationToken = default + ); + + /// + /// Access Embedding Client ... + /// + /// + OllamaApiClient GetEmbeddingClient(); + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\IXAiEmbeddingServiceControllerBase.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace xAiApi.Interfaces +{ + public interface IXAiEmbeddingServiceControllerBase : IXAiServiceControllerBase + { + /// + /// Request For Embedding Specified String ... + /// + /// + /// + /// + [HttpGet("Embeding")] + Task> Embeding( + [FromQuery] string content, + CancellationToken cancellationToken = default + ); + + /// + /// Request For Embedding Specified Batch ... + /// + /// + /// + /// + [HttpGet("Embedings")] + Task>> Embedings( + [FromQuery] string[] batch, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\IXAiServiceBase.cs + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.AI; + +namespace xAiApi.Interfaces +{ + /// + /// Describe a Base Service Actions of any Ai Based Services ... + /// + public interface IXAiServiceBase : IDisposable + { + /// + /// Base Method for Communicate with LLM ... + /// + /// + /// + /// + Task AskAsync( + string prompt, + CancellationToken cancellationToken = default + ); + + /// + /// Base Method for Communicate with LLM ... + /// + /// + /// + /// + IAsyncEnumerable AskAsEnumerable( + string prompt, + CancellationToken cancellationToken = default + ); + + /// + /// Get LLM Client instance for Communicating with LLM ... + /// + /// + /// + IChatClient GetClient(string llm = null); + + /// + /// Create Custom HttpClient for Communicating with LLM API ... + /// + /// + /// + HttpClient GetHttpClient(string url = null); + + /// + /// Prepare a Message History List for LLM Communication based on Prompt ... + /// + /// + /// + /// + IList GetHistory( + string prompt = null, + bool forcePrompt = true + ); + + /// + /// Prepare a Message History List for LLM Communication based on Prompt ... + /// + /// + /// + /// + IList GetHistory( + ChatMessage message = null, + bool forceMessage = true + ); + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\IXAiServiceControllerBase.cs + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace xAiApi.Interfaces +{ + public interface IXAiServiceControllerBase + { + /// + /// Ask Question and Get Response ... + /// + /// + /// + /// + [HttpGet("Ask")] + Task> Ask( + [FromQuery] string question, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\IXDefaultAiService.cs + +namespace xAiApi.Interfaces +{ + public interface IXDefaultAiService : IXAiServiceBase + { } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\IXDefaultEmbeddingService.cs + +namespace xAiApi.Interfaces +{ + public interface IXDefaultEmbeddingService : IXAiEmbeddingServiceBase + { } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Migrations\20260906032616_InitialMigrationAiApi.cs + +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace xAiApi.Migrations +{ + public partial class InitialMigrationAiApi : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AiConversations", + columns: table => new + { + Id = table.Column(nullable: false), + Deleted = table.Column(nullable: false), + OwnerId = table.Column(maxLength: 255, nullable: false), + Title = table.Column(maxLength: 255, nullable: false), + CreatedOn = table.Column(nullable: false), + UpdatedAt = table.Column(nullable: false), + ProjectId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AiConversations", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AiMessages", + columns: table => new + { + Id = table.Column(nullable: false), + Deleted = table.Column(nullable: false), + OwnerId = table.Column(maxLength: 255, nullable: false), + Content = table.Column(nullable: false), + Role = table.Column(nullable: false), + CreatedOn = table.Column(nullable: false), + UpdatedAt = table.Column(nullable: false), + ConversationId = table.Column(maxLength: 255, nullable: false), + MeatDatas = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AiMessages", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AiProjects", + columns: table => new + { + Id = table.Column(nullable: false), + Deleted = table.Column(nullable: false), + OwnerId = table.Column(maxLength: 255, nullable: false), + Title = table.Column(maxLength: 255, nullable: false), + Description = table.Column(maxLength: 1000, nullable: true), + Prompt = table.Column(nullable: true), + CreatedOn = table.Column(nullable: false), + UpdatedAt = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AiProjects", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Files", + columns: table => new + { + Id = table.Column(nullable: false), + Deleted = table.Column(nullable: false), + Type = table.Column(nullable: false), + OwnerId = table.Column(maxLength: 255, nullable: false), + Name = table.Column(maxLength: 255, nullable: false), + Path = table.Column(nullable: false), + Thumb = table.Column(nullable: true), + ThumbPath = table.Column(nullable: true), + UploadedOn = table.Column(nullable: false), + FileName = table.Column(nullable: true), + References = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Files", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Strings", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Deleted = table.Column(nullable: false), + Language = table.Column(maxLength: 10, nullable: false), + ResourceTitle = table.Column(maxLength: 255, nullable: false), + TranslatedValue = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Strings", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Tags", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Deleted = table.Column(nullable: false), + Tag = table.Column(maxLength: 255, nullable: false), + References = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Tags", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AiConversations"); + + migrationBuilder.DropTable( + name: "AiMessages"); + + migrationBuilder.DropTable( + name: "AiProjects"); + + migrationBuilder.DropTable( + name: "Files"); + + migrationBuilder.DropTable( + name: "Strings"); + + migrationBuilder.DropTable( + name: "Tags"); + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Migrations\20260906032616_InitialMigrationAiApi.Designer.cs + +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using xAiApi.Database; + +namespace xAiApi.Migrations +{ + [DbContext(typeof(XAiApiDbContext))] + [Migration("20260906032616_InitialMigrationAiApi")] + partial class InitialMigrationAiApi + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.14"); + + modelBuilder.Entity("xAiModels.Models.Entities.XAiConversation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("ProjectId") + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AiConversations"); + }); + + modelBuilder.Entity("xAiModels.Models.Entities.XAiMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ConversationId") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("MeatDatas") + .HasColumnType("TEXT"); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Role") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AiMessages"); + }); + + modelBuilder.Entity("xAiModels.Models.Entities.XAiProject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("Description") + .HasColumnType("TEXT") + .HasMaxLength(1000); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Prompt") + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AiProjects"); + }); + + modelBuilder.Entity("xFileService.Models.Entities.XFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("FileName") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("References") + .HasColumnType("TEXT"); + + b.Property("Thumb") + .HasColumnType("TEXT"); + + b.Property("ThumbPath") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UploadedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("xStringService.Models.Entities.XString", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("Language") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(10); + + b.Property("ResourceTitle") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("TranslatedValue") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Strings"); + }); + + modelBuilder.Entity("xTagService.Models.Entities.XTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("References") + .HasColumnType("TEXT"); + + b.Property("Tag") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); +#pragma warning restore 612, 618 + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Migrations\XAiApiDbContextModelSnapshot.cs + +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using xAiApi.Database; + +namespace xAiApi.Migrations +{ + [DbContext(typeof(XAiApiDbContext))] + partial class XAiApiDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.14"); + + modelBuilder.Entity("xAiModels.Models.Entities.XAiConversation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("ProjectId") + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AiConversations"); + }); + + modelBuilder.Entity("xAiModels.Models.Entities.XAiMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ConversationId") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("MeatDatas") + .HasColumnType("TEXT"); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Role") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AiMessages"); + }); + + modelBuilder.Entity("xAiModels.Models.Entities.XAiProject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("Description") + .HasColumnType("TEXT") + .HasMaxLength(1000); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Prompt") + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AiProjects"); + }); + + modelBuilder.Entity("xFileService.Models.Entities.XFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("FileName") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("References") + .HasColumnType("TEXT"); + + b.Property("Thumb") + .HasColumnType("TEXT"); + + b.Property("ThumbPath") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UploadedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("xStringService.Models.Entities.XString", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("Language") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(10); + + b.Property("ResourceTitle") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("TranslatedValue") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Strings"); + }); + + modelBuilder.Entity("xTagService.Models.Entities.XTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("References") + .HasColumnType("TEXT"); + + b.Property("Tag") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); +#pragma warning restore 612, 618 + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Models\XAiModelDescriptor.cs + +using xAiApi.Constants; + +namespace xAiApi.Models +{ + /// + /// Describe an Ai Model ... + /// + public class XAiModelDescriptor + { + /// + /// Provider Name ... + /// + public string Name { get; set; } + + /// + /// LLM Provider Url ... + /// + public string Url { get; set; } + + /// + /// Model Name ... + /// + public string LLM { get; set; } + + /// + /// Api Key for Connection ... + /// + public string ApiKey { get; set; } = null; + + /// + /// Model Provider ... + /// + public XAiProvider Provider { get; set; } = XAiProvider.Ollama; + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Models\XAiPromptDescriptor.cs + +namespace xAiApi.Models +{ + /// + /// Describe Specified Prompt ... + /// + public class XAiPromptDescriptor + { + /// + /// Prompt Name ... + /// + public string Name { get; set; } + + /// + /// Prompt Template ... + /// + public string Template { get; set; } + + /// + /// Prompt Description ... + /// + public string Description { get; set; } + + /// + /// Prompt Template Params ... + /// + public XAiPromptParam[] Params { get; set; } = []; + } + + /// + /// Specified a Prompt Param ... + /// + public class XAiPromptParam + { + /// + /// Param Name ... + /// + public string Name { get; set; } + + /// + /// Param Identifier ... + /// + public string Param { get; set; } + + /// + /// Parma Description ... + /// + public string Description { get; set; } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\XAiEmbeddingServiceBase.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using OllamaSharp; +using OllamaSharp.Models; +using xAiApi.Configurations; +using xAiApi.Constants; +using xAiApi.Extensions; +using xAiApi.Interfaces; +using xAiApi.Models; +using xAiModels.Interfaces; +using xAiService.Helpers; +using xCommons.Extensions; +using xExceptions.Constants; + +namespace xAiApi.Providers +{ + /// + /// Base Embedding Provider Service ... + /// + public abstract class XAiEmbeddingServiceBase : XAIServiceBase, IXAiEmbeddingServiceBase + { + private readonly XAiModelDescriptor descriptor; + + protected XAiEmbeddingServiceBase( + IXAiDataProvider dataProvider, + XAiApiConfiguration configuration, + ILogger logger, + string embeddingModelName = XAiApiConstants.XAiEmbeddingModelName + ) : base( + dataProvider, + logger, + configuration + ) + { + // + // Prepare Embedding Model Descriptor ... + descriptor = configuration.GetModel(embeddingModelName); + if (!descriptor.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + } + + /// + /// Embedding Specified String ... + /// + /// + /// + /// + public virtual async Task Embeding( + string content, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (content.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Ollama Client ... + using var client = GetEmbeddingClient(); + + // + // Create Embeding Request .... + var request = new EmbedRequest + { + Input = [content], + Model = descriptor.LLM, + }; + + // + // Make Request and Recieve Response ... + var response = await client.EmbedAsync( + request: request, + cancellationToken: cancellationToken + ); + if (response.IsNullOrDefault()) + { + XException.ActionFailed.Throw(); + } + + // + // Generae Result ... + var result = response? + .Embeddings? + .FirstOrDefault() + ?? []; + if (result.Length == 0) + { + XException.ActionFailed.Throw(); + } + + // + // Normalize ... + VectorHelper.Normalize(result); + + // + return result; + } + + /// + /// Embedding Specified Batch ... + /// + /// + /// + /// + public virtual async Task> Embedings( + string[] batch, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (!batch.HasChild() || + !batch.All(x => !x.IsNullOrEmpty()) + ) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Ollama Client ... + using var client = GetEmbeddingClient(); + + // + // Create Embeding Request .... + var request = new EmbedRequest + { + Input = [.. batch], + Model = descriptor.LLM, + }; + + // + // Make Request and Recieve Response ... + var response = await client.EmbedAsync( + request: request, + cancellationToken: cancellationToken + ); + if (response.IsNullOrDefault()) + { + XException.ActionFailed.Throw(); + } + + // + // Generae Result ... + var result = response? + .Embeddings + .ToArray() + ?? []; + + // + // Validate Result ... + if (result.Length == 0) + { + XException.ActionFailed.Throw(); + } + + // + // Normalize ... + foreach (var vector in result) + { + VectorHelper.Normalize(vector); + } + + // + return result; + } + + /// + /// Access Embedding Client ... + /// + /// + public OllamaApiClient GetEmbeddingClient() + { + // + var result = new OllamaApiClient(new Uri(descriptor.Url)); + + // + return result; + } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\XAIServiceBase.cs + +using System; +using System.Threading; +using xAiApi.Interfaces; +using xCommons.Extensions; +using xAiModels.Interfaces; +using xExceptions.Constants; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.AI; +using xAiModels.Extensions; +using xAiApi.Configurations; +using xAiApi.Extensions; +using System.Net.Http; +using OllamaSharp; + +namespace xAiApi.Providers +{ + public abstract class XAIServiceBase : IXAiServiceBase + { + private readonly IXAiDataProvider dataProvider; + private readonly ILogger logger; + private readonly XAiApiConfiguration configuration; + + protected XAIServiceBase( + IXAiDataProvider dataProvider, + ILogger logger, + XAiApiConfiguration configuration + ) + { + this.logger = logger; + this.dataProvider = dataProvider; + this.configuration = configuration; + } + + // + #region Actions ... + /// + /// Base Method for Communicate with LLM ... + /// + /// + /// + /// + public virtual async Task AskAsync( + string prompt, + CancellationToken cancellationToken = default + ) + { + // + // Check Prompt Exists ... + if (prompt.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + using var client = GetClient(); + var history = GetHistory(prompt); + var response = await client.GetResponseAsync( + messages: history, + cancellationToken: cancellationToken + ); + + // + // Validate Response ... + if (!response.IsValid()) + { + // + // Dispose Client ... + client.Dispose(); + XException.ActionFailed.Throw(); + } + + // + // Retrieve Response Text ... + var result = response.Text; + + // + // Dispose Client ... + client.Dispose(); + + // + return result; + } + + /// + /// Base Method for Communicate with LLM ... + /// + /// + /// + /// + public virtual async IAsyncEnumerable AskAsEnumerable( + string prompt, + [EnumeratorCancellation] + CancellationToken cancellationToken = default + ) + { + // + // Check Prompt Exists ... + if (prompt.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + using var client = GetClient(); + var history = GetHistory(prompt); + var enumerable = client.GetStreamingResponseAsync( + options: null, + messages: history + ); + + // + var result = ""; + await foreach (var res in enumerable) + { + // + // Cancellation Token ... + if (cancellationToken.IsCancellationRequested) + { + yield break; + } + + // + result += res.Text; + } + + // + // Dispose Client ... + client.Dispose(); + + // + yield return result; + } + + /// + /// Get LLM Client instance for Communicating with LLM ... + /// + /// + /// + public virtual IChatClient GetClient(string llm = null) + { + // + // Retrieve Model Descriptor ... + var modelDescriptor = configuration.GetModel(llm); + if (modelDescriptor.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + // Try to Initialize LLm ... + var model = modelDescriptor.LLM; + var apiKey = modelDescriptor.ApiKey; + var url = new Uri(modelDescriptor.Url); + var httpClient = GetHttpClient(modelDescriptor.Url); + + // + IChatClient result = null; + switch (modelDescriptor.Provider) + { + // + case Constants.XAiProvider.Ollama: + // + // Instance Ollama Client using Custom HttpCLient ... + var ollamaClient = new OllamaApiClient(httpClient, model); + result = + new ChatClientBuilder(ollamaClient) + .Build(); + break; + + // + case Constants.XAiProvider.DeepSeek: + break; + + // + case Constants.XAiProvider.HuggingFace: + break; + + // + default: + break; + } + + // // + // if (isOllamaBased) + // { + // } + // else if (isDeepSeekBased) + // { + // // + // if (apiKey is null || + // string.IsNullOrWhiteSpace(apiKey)) + // { + // throw new Exception("Inavlid Args ..."); + // } + + // // + // var deepseekClient = new DeepSeekChatClient(apiKey: apiKey); + // result = deepseekClient; + // } + // else + // { + // // + // // TODO: Implement this ... + // } + + // + if (result.IsNullOrDefault()) + { + XException.InvalidData.Throw(); + } + + // + return result; + } + + /// + /// Create Custom HttpClient for Communicating with LLM API ... + /// + /// + /// + public virtual HttpClient GetHttpClient(string url = null) + { + // + var result = new HttpClient + { + // + // Disable timeout completely (not recommended for production) + Timeout = Timeout.InfiniteTimeSpan, + + // + BaseAddress = url.IsNullOrEmpty() + ? null + : new Uri(url), + + // + // OR set a very long timeout (e.g., 30 minutes for large file uploads) + // Timeout = TimeSpan.FromMinutes(30) + }; + + // + return result; + } + + /// + /// Prepare a Message History List for LLM Communication based on Prompt ... + /// + /// + /// + /// + public virtual IList GetHistory( + string prompt = null, + bool forcePrompt = true + ) + { + // + // Validate Args ... + if (prompt.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + // User Prompt ... + var promptMessage = new ChatMessage( + ChatRole.User, + prompt + ); + + // + // Preparing Introduction Prompt ... + var introductionPrompt = new ChatMessage( + ChatRole.System, + configuration.GetPrompt() + ); + + // + List result = + [ + introductionPrompt, + ]; + if (forcePrompt && + prompt is not null && + !string.IsNullOrWhiteSpace(prompt)) + { + result.Add(promptMessage); + } + + // + return result; + } + + /// + /// Prepare a Message History List for LLM Communication based on Prompt ... + /// + /// + /// + /// + public virtual IList GetHistory( + ChatMessage message = null, + bool forceMessage = true + ) + { + // + // Preparing Introduction Prompt ... + var introductionPrompt = new ChatMessage( + ChatRole.System, + configuration.GetPrompt() + ); + + // + List result = + [ + introductionPrompt, + ]; + if (forceMessage && + message is not null) + { + result.Add(message); + } + + // + return result; + } + #endregion + + /// + /// Base Dispose Action ... + /// + /// + public virtual void Dispose() + { } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\XDefaultAiService.cs + +using Microsoft.Extensions.Logging; +using xAiApi.Configurations; +using xAiApi.Interfaces; +using xAiModels.Interfaces; + +namespace xAiApi.Providers +{ + public class XDefaultAiService : XAIServiceBase, IXDefaultAiService + { + public XDefaultAiService( + IXAiDataProvider dataProvider, + ILogger logger, + XAiApiConfiguration configuration + ) : base( + dataProvider, + logger, + configuration + ) + { } + } +} + +### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\XDefaultEmbeddingService.cs + +using Microsoft.Extensions.Logging; +using xAiApi.Configurations; +using xAiApi.Interfaces; +using xAiModels.Interfaces; + +namespace xAiApi.Providers +{ + public class XDefaultEmbeddingService : XAiEmbeddingServiceBase, IXDefaultEmbeddingService + { + public XDefaultEmbeddingService( + IXAiDataProvider dataProvider, + XAiApiConfiguration configuration, + ILogger logger + ) : base( + dataProvider, + configuration, + logger + ) + { } + } +} diff --git a/Modules/xAiModels b/Modules/xAiModels index f9b5f1f..490058d 160000 --- a/Modules/xAiModels +++ b/Modules/xAiModels @@ -1 +1 @@ -Subproject commit f9b5f1f6aa839a36ef43b29147cdedca9b463876 +Subproject commit 490058d1a8399d91176e53e1c4d5407bc1ff6d59 diff --git a/xAiApi b/xAiApi index 7435756..ae2713e 160000 --- a/xAiApi +++ b/xAiApi @@ -1 +1 @@ -Subproject commit 74357567eb601cd071ca77678d0d93914ad0f3ba +Subproject commit ae2713edddd99c3166d61504ad9530884677b80f