
### 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<Startup>();
                });
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Startup.cs

using System;
using System.IO;
using xAiApi.DI;
using System.Linq;
using xAiModels.DI;
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 xAiApi.Providers;
using System.Reflection;
using xAiApi.Extensions;
using xStringService.DI;
using xAiApi.Interfaces;
using xStorageService.DI;
using xIdentityService.DI;
using xDataService.Models;
using xCommons.Extensions;
using xPushService.Helpers;
using xCommons.Configurations;
using System.Collections.Generic;
using xAiApi.Providers.Extractors;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using xAiApi.Interfaces.Extractors;
using Newtonsoft.Json.Serialization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using IdentityModel.AspNetCore.OAuth2Introspection;

namespace xAiApi
{
    public class Startup
    {
        //
        #region Props ...
        public IConfiguration Configuration { get; }
        private readonly List<XDatabaseDescriptor> 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<XDatabaseDescriptor>
            {
                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<XAppConfiguration>();

            //
            // 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<XAiApiDbContext>(
                lifeTime: lifeTime,
                repositoryType: repositoryType
            );

            //
            // Register Tag Service ...
            services.AddXTagService<XAiApiDbContext>(
                lifeTime: lifeTime,
                repositoryType: repositoryType
            );

            //
            // Register File Service ...
            services.AddXFileService<XAiApiDbContext>(
                lifeTime: lifeTime,
                repositoryType: repositoryType
            );

            //
            // Register AiApi Data Service ...
            services.AddXAiDataService<XAiApiDbContext>(
                lifeTime: lifeTime,
                repositoryType: repositoryType
            );

            //
            // Register XAiApi Configuration ...
            services.AddXAiApiConfiguration(Configuration);

            //
            // Register File Content Extractors ...
            services.AddSingleton<IXFileContentExtractor, XFileContentExtractor>();

            //
            // Register Default Ai Service ...
            services.AddScoped<IXDefaultAiService, XDefaultAiService>();
            services.AddScoped<IXDefaultAIOCRService, XDefaultAIOCRService>();
            services.AddScoped<IXDefaultEmbeddingService, XDefaultEmbeddingService>();
            services.AddScoped<IXDefaultThinkingAiService, XDefaultThinkingAiService>();
        }

        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 xAiModels.Models;

namespace xAiApi.Configurations
{
    public class XAiApiConfiguration
    {
        /// <summary>
        /// Allowed Models to Use ...
        /// </summary>
        public XAiModelDescriptor[] Models { get; set; } = [];

        /// <summary>
        /// Allowed Prompts to Use ...
        /// </summary>
        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 XAiApiContentExtractionPromptName = "ContentExtraction";

        //
        public const string XAiDefaultModelName = "Gemma";
        public const string XAiEmbeddingModelName = "Embed";
        public const string XAiDefaultVisionModelName = "Qwen";
        public const string XAiDefaultThinkingModelName = "Qwen";
        public const string XAiEmbeddingPromptName = "Embedding";

        //
        public const string XOpenAINoKey =  "no-key-required";
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Controllers\DefaultAiController.cs

using xAiApi.Interfaces;
using xCommons.Providers;
using xCommons.Configurations;
using Microsoft.AspNetCore.Mvc;
using xIdentityService.Interfaces;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authorization;

namespace xAiApi.Controllers
{
    [Authorize]
    [Route("[controller]")]
    public class DefaultAiController : XAiServiceControllerBase<IXDefaultAiService>
    {
        public DefaultAiController(
            ILogger<DefaultAiController> logger,
            IXDefaultAiService aiService,
            XAppConfiguration appConfiguration,
            IXIdentityProvider identityProvider,
            XValidationProvider validationProvider
        ) : base(
            logger,
            aiService,
            appConfiguration,
            identityProvider,
            validationProvider
        )
        { }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Controllers\DefaultEmbeddingController.cs

using xAiApi.Interfaces;
using xCommons.Providers;
using xCommons.Configurations;
using Microsoft.AspNetCore.Mvc;
using xIdentityService.Interfaces;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authorization;

namespace xAiApi.Controllers
{
    [Authorize]
    [Route("[controller]")]
    public class DefaultEmbeddingController : XAiEmbeddingServiceControllerBase<IXDefaultEmbeddingService>
    {
        public DefaultEmbeddingController(
            ILogger<DefaultEmbeddingController> logger,
            IXDefaultEmbeddingService aiService,
            XAppConfiguration appConfiguration,
            IXIdentityProvider identityProvider,
            XValidationProvider validationProvider
        ) : base(
            logger,
            aiService,
            appConfiguration,
            identityProvider,
            validationProvider
        )
        { }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Controllers\DefaultThinkingAiController.cs

using xAiApi.Interfaces;
using xCommons.Providers;
using xCommons.Configurations;
using xIdentityService.Interfaces;
using Microsoft.Extensions.Logging;

namespace xAiApi.Controllers
{
    public class DefaultThinkingAiController : XAiServiceControllerBase<IXDefaultThinkingAiService>
    {
        public DefaultThinkingAiController(
            ILogger<DefaultThinkingAiController> logger, 
            IXDefaultThinkingAiService aiService, 
            XAppConfiguration appConfiguration, 
            IXIdentityProvider identityProvider,
            XValidationProvider validationProvider
        ) : base(
            logger, 
            aiService, 
            appConfiguration, 
            identityProvider,
            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
{
    /// <summary>
    /// Runing when application start ...
    /// </summary>
    [Route("")]
    [AllowAnonymous]
    public class StartupController : XBaseController
    {

        public StartupController(
            ILogger<StartupController> logger,
            XAppConfiguration appConfiguration,
            XValidationProvider validationProvider
        ) : base(
            logger,
            appConfiguration,
            validationProvider
        )
        { }

        /// <summary>
        /// Show Configured Welcome Message 
        /// </summary>
        /// <returns></returns>
        [HttpGet("")]
        [AllowAnonymous]
        public virtual ActionResult<string> 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.Threading;
using xAiApi.Interfaces;
using xCommons.Providers;
using System.Threading.Tasks;
using xCommons.Configurations;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using xIdentityService.Interfaces;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authorization;

namespace xAiApi.Controllers
{
    [Authorize]
    [Route("[controller]")]
    public abstract class XAiEmbeddingServiceControllerBase<TAiService> : XAiServiceControllerBase<TAiService>, IXAiEmbeddingServiceControllerBase
    where TAiService : IXAiEmbeddingServiceBase
    {
        private readonly TAiService aiService;

        protected XAiEmbeddingServiceControllerBase(
            ILogger logger, 
            TAiService aiService,
            XAppConfiguration appConfiguration, 
            IXIdentityProvider identityProvider,
            XValidationProvider validationProvider
        ) : base(
            logger, 
            aiService,
            appConfiguration, 
            identityProvider,
            validationProvider
        )
        {
            this.aiService = aiService;
        }

        /// <summary>
        /// Request For Embedding Specified String ...
        /// </summary>
        /// <param name="content"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpGet("Embedding")]
        public async Task<ActionResult<float[]>> Embedding(
            [FromQuery] string content, 
            CancellationToken cancellationToken = default    
        )
        {
            //
            try
            {
                //
                // Validate ...
                ValidationProvider.NotEmpty(content);

                //
                var result = await aiService.Embedding(
                    content: content,
                    cancellationToken: cancellationToken
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }

        /// <summary>
        /// Request For Embedding Specified Batch ...
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpGet("Embeddings")]
        public async Task<ActionResult<IReadOnlyList<float[]>>> Embeddings(
            [FromQuery] string[] batch, 
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                // Validate ...
                ValidationProvider.NotZeroChilds(batch);

                //
                var result = await aiService.Embeddings(
                    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 xModels.Dtos;
using System.Text.Json;
using System.Threading;
using xAiModels.Models;
using xAiApi.Interfaces;
using xCommons.Providers;
using xCommons.Extensions;
using xAiModels.Extensions;
using xAiModels.Models.Dtos;
using xExceptions.Constants;
using System.Threading.Tasks;
using xCommons.Configurations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using xIdentityService.Interfaces;
using Microsoft.Extensions.Logging;
using xIdentityService.Controllers;
using Microsoft.AspNetCore.Authorization;

namespace xAiApi.Controllers
{
    [Authorize]
    [Route("[controller]")]
    public abstract class XAiServiceControllerBase<TAiService> : XBaseIdentityApiV1Controller, IXAiServiceControllerBase
    where TAiService : IXAiServiceBase
    {
        private readonly TAiService aiService;

        protected XAiServiceControllerBase(
            ILogger logger,
            TAiService aiService,
            XAppConfiguration appConfiguration,
            IXIdentityProvider identityProvider,
            XValidationProvider validationProvider
        ) : base(
            logger,
            appConfiguration,
            identityProvider,
            validationProvider
        )
        {
            this.aiService = aiService;
        }

        //
        #region Actions ...
        /// <summary>
        /// Ask Question and Get Response ...
        /// </summary>
        /// <param name="question"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpGet("AskText")]
        public virtual async Task<ActionResult<string>> AskText(
            [FromQuery] string question,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                // Validate ...
                ValidationProvider.NotEmpty(question);

                //
                var result = await aiService.AskTextAsync(
                    prompt: question,
                    cancellationToken: cancellationToken
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }

        /// <summary>
        /// Ask Question and Get Response Stream ...
        /// </summary>
        /// <param name="question"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpGet("AskTextStream")]
        public virtual async Task AskTextStream(
            [FromQuery] string question,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                // Validate ...
                ValidationProvider.NotEmpty(question);

                //
                // Configure SSE Response Headers ...
                Response.Headers["X-Accel-Buffering"] = "no";
                Response.Headers["Connection"] = "keep-alive";
                Response.Headers["Content-Type"] = "text/event-stream";
                Response.Headers["Cache-Control"] = "no-cache, no-store";

                //
                // Disable response buffering ...
                var responseBodyFeature = HttpContext.Features
                    .Get<Microsoft.AspNetCore.Http.Features.IHttpResponseBodyFeature>();
                responseBodyFeature?.DisableBuffering();

                //
                // Stream Response ...
                var jsonOptions = new JsonSerializerOptions
                {
                    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
                };

                //
                var enumerable = aiService.AskTextAsEnumerable(
                    prompt: question,
                    cancellationToken: cancellationToken
                );

                //
                await foreach (var chunk in enumerable)
                {
                    //
                    // Skip empty chunks ...
                    if (string.IsNullOrEmpty(chunk))
                    {
                        continue;
                    }

                    //
                    // Build SSE message ...
                    // Format:  data: <json>\n\n
                    // var payload = JsonSerializer.Serialize(
                    //     new { text = chunk },
                    //     jsonOptions
                    // );
                    var payload = chunk;

                    //
                    // Write SSE Event ...
                    await Response.WriteAsync($"{payload}\n\n", cancellationToken);
                    await Response.Body.FlushAsync(cancellationToken);
                }

                //
                await Response.Body.FlushAsync(cancellationToken);
            }
            catch (Exception)
            {
                await Response.Body.FlushAsync();
            }
        }
        #endregion

        //
        #region Loaders ...
        /// <summary>
        /// Retrieve AI Projects ...
        /// </summary>
        /// <param name="includeDefaults"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Projects")]
        public async Task<ActionResult<IEnumerable<XAiProjectDto>>> LoadProjects(
            [FromQuery] bool includeDefaults = false,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                var result = await aiService.LoadProjects(
                    ownerId: userInfo.UserId,
                    connectionId: connectionId,
                    includeDefaults: includeDefaults,
                    cancellationToken: cancellationToken
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }

        /// <summary>
        /// Retrieve Specified AI Project's Conversations ...
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Projects/{projectId}/Conversations")]
        public async Task<ActionResult<IEnumerable<XAiConversationDto>>> LoadConversations(
            [FromRoute] Guid projectId,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                // Validate ...
                if (projectId.IsDefaultGuid())
                {
                    XException.InvalidArgs.Throw();
                }

                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                var result = await aiService.LoadConversations(
                    projectId: projectId,
                    connectionId: connectionId,
                    cancellationToken: cancellationToken
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }

        /// <summary>
        /// Retrieve Specified AI Conversation's Messages ...
        /// </summary>
        /// <param name="conversationId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Conversations/{conversationId}/Messages")]
        public async Task<ActionResult<IEnumerable<XAiMessageDto>>> LoadMessages(
            [FromRoute] Guid conversationId,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                // Validate ...
                if (conversationId.IsDefaultGuid())
                {
                    XException.InvalidArgs.Throw();
                }

                //
                var result = await aiService.LoadMessages(
                    conversationId: conversationId,
                    cancellationToken: cancellationToken
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }
        #endregion

        //
        #region Queries ...
        /// <summary>
        /// Query Projects ...
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Projects/Query")]
        public async Task<ActionResult<XQueryResult<XAiProjectDto>>> QueryProjects(
            [FromQuery] XQuery query,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                var result = await aiService.QueryProjects(
                    query: query,
                    ownerId: userInfo.UserId,
                    cancellationToken: cancellationToken
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }

        /// <summary>
        /// Query Conversations ...
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Projects/{projectId}/Conversations/Query")]
        public async Task<ActionResult<XQueryResult<XAiConversationDto>>> QueryConversations(
            [FromRoute] Guid projectId,
            [FromQuery] XQuery query,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                var result = await aiService.QueryConversations(
                    query: query,
                    projectId: projectId,
                    ownerId: userInfo.UserId,
                    cancellationToken: cancellationToken
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }

        /// <summary>
        /// Query Messages ...
        /// </summary>
        /// <param name="conversationId"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Conversations/{conversationId}/Messages/Query")]
        public async Task<ActionResult<XQueryResult<XAiMessageDto>>> QueryMessages(
            [FromRoute] Guid conversationId,
            [FromQuery] XQuery query,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                var result = await aiService.QueryMessages(
                    query: query,
                    ownerId: userInfo.UserId,
                    conversationId: conversationId,
                    cancellationToken: cancellationToken
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }
        #endregion

        //
        #region Creators ...
        /// <summary>
        /// Create an AI Project ...
        /// </summary>
        /// <param name="item"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPost("Projects")]
        public async Task<ActionResult<XAiProjectDto>> CreateProject(
            [FromBody] XAiProjectDto item,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                // Validate ...
                if (item.IsNullOrDefault())
                {
                    XException.InvalidArgs.Throw();
                }

                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                // Update Required Params ...
                item.OwnerId = userInfo.UserId;
                if (!item.IsValidForAddOrUpdate())
                {
                    XException.InvalidData.Throw();
                }

                //
                var result = await aiService.CreateProject(
                    item: item,
                    ownerId: userInfo.UserId,
                    connectionId: connectionId,
                    cancellationToken: cancellationToken
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }

        /// <summary>
        /// Create an AI Conversation ...
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="item"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPost("Projects/{projectId}/Conversations")]
        public async Task<ActionResult<XAiConversationDto>> CreateConversation(
            [FromRoute] Guid projectId,
            [FromBody] XAiConversationDto item,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                // Validate ...
                if (projectId.IsDefaultGuid() ||
                    item.IsNullOrDefault())
                {
                    XException.InvalidArgs.Throw();
                }

                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                // Update Required Params ...
                item.OwnerId = userInfo.UserId;
                if (!item.IsValidForAddOrUpdate())
                {
                    XException.InvalidData.Throw();
                }

                //
                var result = await aiService.CreateConversation(
                    item: item,
                    projectId: projectId,
                    ownerId: userInfo.UserId,
                    connectionId: connectionId,
                    cancellationToken: cancellationToken
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }
        #endregion
        
        //
        #region Content Extraction ...
        [HttpPost("ExtractContent")]
        [Consumes("multipart/form-data")]
        public async Task<ActionResult<string>> ExtractContent(
            [FromForm] IFormFileCollection files,
            CancellationToken cancellationToken = default            
        ) {
            //
            try
            {
                //
                // Validate ...
                if (!files.HasChild())
                {
                    XException.InvalidArgs.Throw();
                }

                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                var result = string.Empty;

                //


                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }
        #endregion

        //
        #region Data Actions ...
        /// <summary>
        /// Ask Question and Get Response ...
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPost("Ask")]
        public async Task<ActionResult<string>> Ask(
            [FromBody] XAiResponseRequest request,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                // Validate ...
                if (!request.IsValid())
                {
                    XException.InvalidArgs.Throw();
                }

                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                // TODO: Reading Fiels Form Collection and Attach it ...
                var result = await aiService.AskAsync(
                    files: null,
                    prompt: request.Prompt,
                    ownerId: userInfo.UserId,
                    connectionId: connectionId,
                    projectId: request.ProjectId,
                    cancellationToken: cancellationToken,
                    conversationId: request.ConversationId
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                //
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }

        /// <summary>
        /// Ask Question and Get Response Stream ...
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPost("AskStream")]
        public async Task AskStream(
            [FromBody] XAiResponseRequest request,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                // Validate ...
                if (!request.IsValid())
                {
                    XException.InvalidArgs.Throw();
                }

                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                // Configure SSE Response Headers ...
                Response.Headers["X-Accel-Buffering"] = "no";
                Response.Headers["Connection"] = "keep-alive";
                Response.Headers["Content-Type"] = "text/event-stream";
                Response.Headers["Cache-Control"] = "no-cache, no-store";

                //
                // Disable response buffering ...
                var responseBodyFeature = HttpContext.Features
                    .Get<Microsoft.AspNetCore.Http.Features.IHttpResponseBodyFeature>();
                responseBodyFeature?.DisableBuffering();

                //
                // TODO: Reading Fiels Form Collection and Attach it ...
                var enumerable = aiService.AskAsEnumerable(
                    files: null,
                    prompt: request.Prompt,
                    ownerId: userInfo.UserId,
                    connectionId: connectionId,
                    projectId: request.ProjectId,
                    cancellationToken: cancellationToken,
                    conversationId: request.ConversationId
                );

                //
                await foreach (var resp in enumerable)
                {
                    //
                    // Skip Empty Responses ...
                    if (resp.IsNullOrDefault())
                    {
                        continue;
                    }

                    //
                    var payload = resp.ToJSON(camelCase: true);

                    //
                    // Write SSE Event ...
                    await Response.WriteAsync($"{payload}", cancellationToken);
                    await Response.Body.FlushAsync(cancellationToken);
                }

                //
                await Response.Body.FlushAsync(cancellationToken);
            }
            catch (Exception)
            {
                await Response.Body.FlushAsync();
            }
        }

        /// <summary>
        /// Ask Question with Files and Get Response ...
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPost("AskWithFiles")]
        [Consumes("multipart/form-data")]
        public async Task<ActionResult<string>> AskWithFiles(
            [FromForm] XAiResponseRequest request,
            [FromForm] IFormFileCollection files,
            CancellationToken cancellationToken = default
        )
        {
            //
            try
            {
                //
                // Validate ...
                if (!request.IsValid())
                {
                    XException.InvalidArgs.Throw();
                }

                //
                var userInfo = await GetUserInfo();
                var connectionId = GetConnectionId();

                //
                var result = await aiService.AskAsync(
                    files: files,
                    userInfo: userInfo,
                    prompt: request.Prompt,
                    ownerId: userInfo.UserId,
                    connectionId: connectionId,
                    projectId: request.ProjectId,
                    cancellationToken: cancellationToken,
                    conversationId: request.ConversationId
                );

                //
                return Ok(result);
            }
            catch (Exception ex)
            {
                var result = GetExceptionActionResult(ex);
                return result;
            }
        }
        #endregion
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Database\XAiApiDatabaseDescriptor.cs

using xAiApi.Constants;
using xDataService.Interfaces;
using xDataService.Models;

namespace xAiApi.Database
{
    /// <summary>
    /// Ai Api Database Descriptor ... 
    /// </summary>
    public class XAiApiDatabaseDescriptor : XDatabaseDescriptor<XAiApiDbContext>, IXDatabaseDescriptor<XAiApiDbContext>
    {
        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
{
    /// <summary>
    /// Db Context of AiApi ...
    /// </summary>
    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 xAiApi.Constants;
using xAiApi.Extensions;
using xCommons.Extensions;
using xAiApi.Configurations;
using xExceptions.Constants;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace xAiApi.DI
{
    public static partial class XDIHelperExtension
    {
        /// <summary>
        /// Extract AI Configuration ...
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static XAiApiConfiguration GetXAiApiConfiguration(this IConfiguration source)
        {
            //
            XAiApiConfiguration result = null;

            //
            var section = source.GetSection(ConfigurationNodeNames.AI_API_NODE);
            if (!section.IsNull())
            {
                result = section.Get<XAiApiConfiguration>();
            }

            //
            return result;
        }

        /// <summary>
        /// Register AiApi Configuration ...
        /// </summary>
        /// <param name="source"></param>
        /// <param name="configuration"></param>
        public static void AddXAiApiConfiguration(
            this IServiceCollection source,
            IConfiguration configuration
        )
        {
            //
            var apiConfig = configuration.GetXAiApiConfiguration();
            source.AddXAiApiConfiguration(apiConfig);
        }

        /// <summary>
        /// Register AiApi Configuration ...
        /// </summary>
        /// <param name="source"></param>
        /// <param name="configuration"></param>
        public static void AddXAiApiConfiguration(
            this IServiceCollection source,
            XAiApiConfiguration configuration
        )
        {
            //
            if (!configuration.IsValid())
            {
                XException.InvalidConfiguration.Throw();
            }

            //
            source.AddSingleton(configuration);
        }

        //
        #region Private ...
        /// <summary>
        /// a LogTag for Service ...
        /// </summary>
        private static string XLogTag = XAiApiConstants.XDILogTag;

        /// <summary>
        /// print a log in Console ...
        /// </summary>
        /// <param name="message"></param>
        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.Linq;
using xAiApi.Constants;
using xAiModels.Models;
using xCommons.Extensions;
using xAiModels.Extensions;
using xAiApi.Configurations;
using System.Collections.Generic;

namespace xAiApi.Extensions
{
    public static class XModelsExtensions
    {
        /// <summary>
        /// Validate Configurations ...
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        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;
        }

        /// <summary>
        /// Extract Default Model Descriptor ...
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static XAiModelDescriptor GetDefaultModel(this XAiApiConfiguration source)
        {
            //
            XAiModelDescriptor result = null;

            //
            if (source.IsValid())
            {
                result = source.Models[0];
            }

            //
            return result;
        }

        /// <summary>
        /// Retrieve Specified Model from Config ...
        /// </summary>
        /// <param name="source"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        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;
        }

        /// <summary>
        /// Retrieve Introduction Prompt ...
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        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;
        }

        /// <summary>
        /// Select Specified Prompts and Parse it's Template
        /// and Generate Message ...
        /// </summary>
        /// <param name="source"></param>
        /// <param name="name"></param>
        /// <param name="params"></param>
        /// <returns></returns>
        public static string GetPrompt(
            this XAiApiConfiguration source,
            string name = null,
            IDictionary<string, string> @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
    {
        /// <summary>
        /// Register Authorization Policies
        /// </summary>
        /// <param name="services"></param>
        /// <param name="policies"></param>
        public static void AddXAuthorization(
            this IServiceCollection services,
            IDictionary<string, AuthorizationPolicy> 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<IAuthorizationHandler, RequiredRolesHandler>();
        }

        /// <summary>
        /// Use Forward Headers Options for resolving behind a proxy issues ...
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        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 Microsoft.Extensions.AI;
using OllamaSharp;

namespace xAiApi.Interfaces
{
    /// <summary>
    /// Base Embedding Provider Service ...
    /// </summary>
    public interface IXAiEmbeddingServiceBase : IXAiServiceBase
    {
        /// <summary>
        /// Embedding Specified String ...
        /// </summary>
        /// <param name="content"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<float[]> Embedding(
            string content,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Embedding Specified Batch ...
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<IReadOnlyList<float[]>> Embeddings(
            string[] batch,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Embedding Generator ...
        /// </summary>
        /// <returns></returns>
        IEmbeddingGenerator<string, Embedding<float>> GetEmbeddingGenerator();
    }
}

### 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
    {
        /// <summary>
        /// Request For Embedding Specified String ...
        /// </summary>
        /// <param name="content"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Embedding")]
        Task<ActionResult<float[]>> Embedding(
            [FromQuery] string content,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Request For Embedding Specified Batch ...
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Embeddings")]
        Task<ActionResult<IReadOnlyList<float[]>>> Embeddings(
            [FromQuery] string[] batch,
            CancellationToken cancellationToken = default
        );
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\IXAiServiceBase.cs

using System;
using xModels.Dtos;
using System.Net.Http;
using System.Threading;
using xAiModels.Models;
using xAiModels.Models.Dtos;
using System.Threading.Tasks;
using xIdentityModels.Models;
using Microsoft.Extensions.AI;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;

namespace xAiApi.Interfaces
{
    /// <summary>
    /// Describe a Base Service Actions of any Ai Based Services ...
    /// </summary>
    public interface IXAiServiceBase : IDisposable
    {
        /// <summary>
        /// Chat Options ...
        /// </summary>
        ChatOptions Options { get; }

        /// <summary>
        /// Descriptor of Models which used in Service ...
        /// </summary>
        XAiModelDescriptor Descriptor { get; }

        //
        #region Actions ...
        /// <summary>
        /// Base Method for Communicate with LLM ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<string> AskTextAsync(
            string prompt,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Base Method for Communicate with LLM ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        IAsyncEnumerable<string> AskTextAsEnumerable(
            string prompt,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Get LLM Client instance for Communicating with LLM ...
        /// </summary>
        /// <returns></returns>
        IChatClient GetClient();

        /// <summary>
        /// Create Custom HttpClient for Communicating with LLM API ...
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        HttpClient GetHttpClient(string url = null);

        /// <summary>
        /// Prepare a Message History List for LLM Communication based on Prompt ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="forcePrompt"></param>
        /// <returns></returns>
        IList<ChatMessage> GetHistory(
            string prompt = null,
            bool forcePrompt = true
        );

        /// <summary>
        /// Prepare a Message History List for LLM Communication based on Prompt ...
        /// </summary>
        /// <param name="message"></param>
        /// <param name="forceMessage"></param>
        /// <returns></returns>
        IList<ChatMessage> GetHistory(
            ChatMessage message = null,
            bool forceMessage = true
        );
        #endregion

        //
        #region Loaders ...
        /// <summary>
        /// Load AI Projects ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="connectionId"></param>
        /// <param name="includeDefaults"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<IEnumerable<XAiProjectDto>> LoadProjects(
            string ownerId,
            string connectionId = null,
            bool includeDefaults = false,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Load Conversations of Specified AI Project ...
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="connectionId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<IEnumerable<XAiConversationDto>> LoadConversations(
            Guid projectId,
            string connectionId = null,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Load Messages of Specified Conversation ...
        /// </summary>
        /// <param name="conversationId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<IEnumerable<XAiMessageDto>> LoadMessages(
            Guid conversationId,
            CancellationToken cancellationToken = default
        );
        #endregion

        //
        #region Queries ...
        /// <summary>
        /// Query Projects ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<XQueryResult<XAiProjectDto>> QueryProjects(
            string ownerId,
            XQuery query,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Query Conversations ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="projectId"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<XQueryResult<XAiConversationDto>> QueryConversations(
            string ownerId,
            Guid projectId,
            XQuery query,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Query Messages ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="conversationId"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<XQueryResult<XAiMessageDto>> QueryMessages(
            string ownerId,
            Guid conversationId,
            XQuery query,
            CancellationToken cancellationToken = default
        );
        #endregion

        //
        #region Creators ...
        /// <summary>
        /// Create an AI Project ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="item"></param>
        /// <param name="connectionId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<XAiProjectDto> CreateProject(
            string ownerId,
            XAiProjectDto item,
            string connectionId = null,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Create an AI Conversation ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="projectId"></param>
        /// <param name="item"></param>
        /// <param name="connectionId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<XAiConversationDto> CreateConversation(
            string ownerId,
            Guid projectId,
            XAiConversationDto item,
            string connectionId = null,
            CancellationToken cancellationToken = default
        );
        #endregion
        
        //
        #region Content Extraction ...
        /// <summary>
        /// Try to Extract Files Content ...
        /// </summary>
        /// <param name="files"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<string> ExtractContent(
            IFormFileCollection files,
            CancellationToken cancellationToken = default            
        );
        #endregion

        //
        #region Data Actions ...
        /// <summary>
        /// Ask a Question as Prompt and Retrieve Response ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="ownerId"></param>
        /// <param name="projectId"></param>
        /// <param name="conversationId"></param>
        /// <param name="connectionId"></param>
        /// <param name="files"></param>
        /// <param name="userInfo"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<XAiMessageDto> AskAsync(
            string prompt,
            string ownerId,
            Guid projectId,
            Guid conversationId,
            string connectionId = null,
            IFormFileCollection files = null,
            XUserClaimsInfoDto userInfo = null,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Ask a Question as Prompt and Retrieve Response as Async Enumerable ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="ownerId"></param>
        /// <param name="projectId"></param>
        /// <param name="conversationId"></param>
        /// <param name="connectionId"></param>
        /// <param name="files"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        IAsyncEnumerable<XAiResonseMessage> AskAsEnumerable(
            string prompt,
            string ownerId,
            Guid projectId,
            Guid conversationId,
            string connectionId = null,
            IFormFileCollection files = null,
            CancellationToken cancellationToken = default
        );
        #endregion
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\IXAiServiceControllerBase.cs

using System;
using xModels.Dtos;
using System.Threading;
using xAiModels.Models;
using xAiModels.Models.Dtos;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;

namespace xAiApi.Interfaces
{
    public interface IXAiServiceControllerBase
    {
        //
        #region Actions ...
        /// <summary>
        /// Ask Question and Get Response ...
        /// </summary>
        /// <param name="question"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("AskText")]
        Task<ActionResult<string>> AskText(
            [FromQuery] string question,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Ask Question and Get Response Stream ...
        /// </summary>
        /// <param name="question"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("AskTextStream")]
        Task AskTextStream(
            [FromQuery] string question,
            CancellationToken cancellationToken = default
        );
        #endregion

        //
        #region Loaders ...
        /// <summary>
        /// Retrieve AI Projects ...
        /// </summary>
        /// <param name="includeDefaults"></param> 
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Projects")]
        Task<ActionResult<IEnumerable<XAiProjectDto>>> LoadProjects(
            [FromQuery] bool includeDefaults = false,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Retrieve Specified AI Project's Conversations ...
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Projects/{projectId}/Conversations")]
        Task<ActionResult<IEnumerable<XAiConversationDto>>> LoadConversations(
            [FromRoute] Guid projectId,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Retrieve Specified AI Conversation's Messages ...
        /// </summary>
        /// <param name="conversationId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Conversations/{conversationId}/Messages")]
        Task<ActionResult<IEnumerable<XAiMessageDto>>> LoadMessages(
            [FromRoute] Guid conversationId,
            CancellationToken cancellationToken = default
        );
        #endregion

        //
        #region Queries ...
        /// <summary>
        /// Query Projects ...
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Projects/Query")]
        Task<ActionResult<XQueryResult<XAiProjectDto>>> QueryProjects(
            [FromQuery] XQuery query,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Query Conversations ...
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Projects/{projectId}/Conversations/Query")]
        Task<ActionResult<XQueryResult<XAiConversationDto>>> QueryConversations(
            [FromRoute] Guid projectId,
            [FromQuery] XQuery query,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Query Messages ...
        /// </summary>
        /// <param name="conversationId"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("Conversations/{conversationId}/Messages/Query")]
        Task<ActionResult<XQueryResult<XAiMessageDto>>> QueryMessages(
            [FromRoute] Guid conversationId,
            [FromQuery] XQuery query,
            CancellationToken cancellationToken = default
        );
        #endregion

        //
        #region Creators ...
        /// <summary>
        /// Create an AI Project ...
        /// </summary>
        /// <param name="item"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPost("Projects")]
        Task<ActionResult<XAiProjectDto>> CreateProject(
            [FromBody] XAiProjectDto item,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Create an AI Conversation ...
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="item"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPost("Projects/{projectId}/Conversations")]
        Task<ActionResult<XAiConversationDto>> CreateConversation(
            [FromRoute] Guid projectId,
            [FromBody] XAiConversationDto item,
            CancellationToken cancellationToken = default
        );
        #endregion
        
        //
        #region Content Extraction ...
        [HttpPost("ExtractContent")]
        [Consumes("multipart/form-data")]
        Task<ActionResult<string>> ExtractContent(
            [FromForm] IFormFileCollection files,
            CancellationToken cancellationToken = default            
        );
        #endregion

        //
        #region Data Actions ...
        /// <summary>
        /// Ask Question and Get Response ...
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPost("Ask")]
        Task<ActionResult<string>> Ask(
            [FromBody] XAiResponseRequest request,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Ask Question and Get Response Stream ...
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPost("AskStream")]
        Task AskStream(
            [FromBody] XAiResponseRequest request,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Ask Question with Files and Get Response ...
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPost("AskWithFiles")]
        [Consumes("multipart/form-data")]
        Task<ActionResult<string>> AskWithFiles(
            [FromForm] XAiResponseRequest request,
            [FromForm] IFormFileCollection files,
            CancellationToken cancellationToken = default
        );
        #endregion
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\IXDefaultAIOCRService.cs

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using System.Collections.Generic;

namespace xAiApi.Interfaces
{
    public interface IXDefaultAIOCRService : IXAiServiceBase
    {
        /// <summary>
        /// Request for Doing OCR on Given Data Contents ...
        /// </summary>
        /// <param name="messages"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>        
        Task<string> RequestOCRAsync(
            IList<ChatMessage> messages,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Request for Doing OCR on Given Data Contents as Stream ...
        /// </summary>
        /// <param name="messages"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>        
        IAsyncEnumerable<string> RequestOCRAsEnumerable(
            IList<ChatMessage> messages,
            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\Interfaces\IXDefaultThinkingAiService.cs

namespace xAiApi.Interfaces
{
    public interface IXDefaultThinkingAiService: IXAiServiceBase
    { }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\Extractors\IXAudioFileContentExtractor.cs

namespace xAiApi.Interfaces.Extractors
{
    public interface IXAudioFileContentExtractor : IXFileContentExtractor
    { }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\Extractors\IXDocxFileContentExtractor.cs

namespace xAiApi.Interfaces.Extractors
{
    public interface IXDocxFileContentExtractor : IXFileContentExtractor
    { }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\Extractors\IXExcelFileContentExtractor.cs

namespace xAiApi.Interfaces.Extractors
{
    public interface IXExcelFileContentExtractor : IXFileContentExtractor
    { }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\Extractors\IXFileContentExtractor.cs

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using xAiModels.Models;

namespace xAiApi.Interfaces.Extractors
{
    /// <summary>
    /// a Service for Extracting File Content ...
    /// </summary>
    public interface IXFileContentExtractor
    {
        /// <summary>
        /// Check if this extractor supports the specified MIME type ...
        /// </summary>
        bool CanExtract(string mimeType);

        /// <summary>
        /// Extract text content from file stream ...
        /// </summary>
        Task<string> ExtractAsync(
            Stream fileStream,
            string mimeType,
            CancellationToken cancellationToken = default
        );

        /// <summary>
        /// Extract content from stream as Rich Result ...
        /// </summary>
        /// <param name="fileStream"></param>
        /// <param name="fileName"></param>
        /// <param name="mimeType"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<XFileExtractionResult> ExtractRichAsync(
            Stream fileStream,
            string fileName,
            string mimeType,
            CancellationToken cancellationToken = default
        );
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\Extractors\IXImageFileContentExtractor.cs

namespace xAiApi.Interfaces.Extractors
{
    public interface IXImageFileContentExtractor : IXFileContentExtractor
    { }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\Extractors\IXPdfFileContentExtractor.cs

namespace xAiApi.Interfaces.Extractors
{
    public interface IXPdfFileContentExtractor : IXFileContentExtractor
    { }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\Extractors\IXPlainTextFileContentExtractor.cs

namespace xAiApi.Interfaces.Extractors
{
    public interface IXPlainTextFileContentExtractor : IXFileContentExtractor
    { }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Interfaces\Extractors\IXVisionFileContentExtractor.cs

namespace xAiApi.Interfaces.Extractors
{
    public interface IXVisionFileContentExtractor : IXFileContentExtractor
    { }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Migrations\20261003044828_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<Guid>(nullable: false),
                    Deleted = table.Column<bool>(nullable: false),
                    OwnerId = table.Column<string>(maxLength: 255, nullable: false),
                    Title = table.Column<string>(maxLength: 255, nullable: false),
                    CreatedOn = table.Column<DateTime>(nullable: false),
                    UpdatedAt = table.Column<DateTime>(nullable: false),
                    ProjectId = table.Column<Guid>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AiConversations", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "AiMessages",
                columns: table => new
                {
                    Id = table.Column<Guid>(nullable: false),
                    Deleted = table.Column<bool>(nullable: false),
                    OwnerId = table.Column<string>(maxLength: 255, nullable: false),
                    Content = table.Column<string>(nullable: false),
                    Role = table.Column<int>(nullable: false),
                    CreatedOn = table.Column<DateTime>(nullable: false),
                    UpdatedAt = table.Column<DateTime>(nullable: false),
                    ConversationId = table.Column<Guid>(maxLength: 255, nullable: false),
                    MetaDatas = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AiMessages", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "AiProjects",
                columns: table => new
                {
                    Id = table.Column<Guid>(nullable: false),
                    Deleted = table.Column<bool>(nullable: false),
                    OwnerId = table.Column<string>(maxLength: 255, nullable: false),
                    Title = table.Column<string>(maxLength: 255, nullable: false),
                    Description = table.Column<string>(maxLength: 1000, nullable: true),
                    Prompt = table.Column<string>(nullable: true),
                    CreatedOn = table.Column<DateTime>(nullable: false),
                    UpdatedAt = table.Column<DateTime>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AiProjects", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "Files",
                columns: table => new
                {
                    Id = table.Column<Guid>(nullable: false),
                    Deleted = table.Column<bool>(nullable: false),
                    Type = table.Column<int>(nullable: false),
                    OwnerId = table.Column<string>(maxLength: 255, nullable: false),
                    Name = table.Column<string>(maxLength: 255, nullable: false),
                    Path = table.Column<string>(nullable: false),
                    Thumb = table.Column<string>(nullable: true),
                    ThumbPath = table.Column<string>(nullable: true),
                    UploadedOn = table.Column<DateTime>(nullable: false),
                    FileName = table.Column<string>(nullable: true),
                    References = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Files", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "Strings",
                columns: table => new
                {
                    Id = table.Column<int>(nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
                    Deleted = table.Column<bool>(nullable: false),
                    Language = table.Column<string>(maxLength: 10, nullable: false),
                    ResourceTitle = table.Column<string>(maxLength: 255, nullable: false),
                    TranslatedValue = table.Column<string>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Strings", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "Tags",
                columns: table => new
                {
                    Id = table.Column<int>(nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
                    Deleted = table.Column<bool>(nullable: false),
                    Tag = table.Column<string>(maxLength: 255, nullable: false),
                    References = table.Column<string>(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\20261003044828_InitialMigrationAiApi.Designer.cs

// <auto-generated />
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("20261003044828_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<Guid>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("TEXT");

                    b.Property<DateTime>("CreatedOn")
                        .HasColumnType("TEXT");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("OwnerId")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<Guid>("ProjectId")
                        .HasColumnType("TEXT");

                    b.Property<string>("Title")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<DateTime>("UpdatedAt")
                        .HasColumnType("TEXT");

                    b.HasKey("Id");

                    b.ToTable("AiConversations");
                });

            modelBuilder.Entity("xAiModels.Models.Entities.XAiMessage", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("TEXT");

                    b.Property<string>("Content")
                        .IsRequired()
                        .HasColumnType("TEXT");

                    b.Property<Guid>("ConversationId")
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<DateTime>("CreatedOn")
                        .HasColumnType("TEXT");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("MetaDatas")
                        .HasColumnType("TEXT");

                    b.Property<string>("OwnerId")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<int>("Role")
                        .HasColumnType("INTEGER");

                    b.Property<DateTime>("UpdatedAt")
                        .HasColumnType("TEXT");

                    b.HasKey("Id");

                    b.ToTable("AiMessages");
                });

            modelBuilder.Entity("xAiModels.Models.Entities.XAiProject", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("TEXT");

                    b.Property<DateTime>("CreatedOn")
                        .HasColumnType("TEXT");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("Description")
                        .HasColumnType("TEXT")
                        .HasMaxLength(1000);

                    b.Property<string>("OwnerId")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<string>("Prompt")
                        .HasColumnType("TEXT");

                    b.Property<string>("Title")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<DateTime>("UpdatedAt")
                        .HasColumnType("TEXT");

                    b.HasKey("Id");

                    b.ToTable("AiProjects");
                });

            modelBuilder.Entity("xFileService.Models.Entities.XFile", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("TEXT");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("FileName")
                        .HasColumnType("TEXT");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<string>("OwnerId")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<string>("Path")
                        .IsRequired()
                        .HasColumnType("TEXT");

                    b.Property<string>("References")
                        .HasColumnType("TEXT");

                    b.Property<string>("Thumb")
                        .HasColumnType("TEXT");

                    b.Property<string>("ThumbPath")
                        .HasColumnType("TEXT");

                    b.Property<int>("Type")
                        .HasColumnType("INTEGER");

                    b.Property<DateTime>("UploadedOn")
                        .HasColumnType("TEXT");

                    b.HasKey("Id");

                    b.ToTable("Files");
                });

            modelBuilder.Entity("xStringService.Models.Entities.XString", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("INTEGER");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("Language")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(10);

                    b.Property<string>("ResourceTitle")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<string>("TranslatedValue")
                        .IsRequired()
                        .HasColumnType("TEXT");

                    b.HasKey("Id");

                    b.ToTable("Strings");
                });

            modelBuilder.Entity("xTagService.Models.Entities.XTag", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("INTEGER");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("References")
                        .HasColumnType("TEXT");

                    b.Property<string>("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

// <auto-generated />
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<Guid>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("TEXT");

                    b.Property<DateTime>("CreatedOn")
                        .HasColumnType("TEXT");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("OwnerId")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<Guid>("ProjectId")
                        .HasColumnType("TEXT");

                    b.Property<string>("Title")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<DateTime>("UpdatedAt")
                        .HasColumnType("TEXT");

                    b.HasKey("Id");

                    b.ToTable("AiConversations");
                });

            modelBuilder.Entity("xAiModels.Models.Entities.XAiMessage", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("TEXT");

                    b.Property<string>("Content")
                        .IsRequired()
                        .HasColumnType("TEXT");

                    b.Property<Guid>("ConversationId")
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<DateTime>("CreatedOn")
                        .HasColumnType("TEXT");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("MetaDatas")
                        .HasColumnType("TEXT");

                    b.Property<string>("OwnerId")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<int>("Role")
                        .HasColumnType("INTEGER");

                    b.Property<DateTime>("UpdatedAt")
                        .HasColumnType("TEXT");

                    b.HasKey("Id");

                    b.ToTable("AiMessages");
                });

            modelBuilder.Entity("xAiModels.Models.Entities.XAiProject", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("TEXT");

                    b.Property<DateTime>("CreatedOn")
                        .HasColumnType("TEXT");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("Description")
                        .HasColumnType("TEXT")
                        .HasMaxLength(1000);

                    b.Property<string>("OwnerId")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<string>("Prompt")
                        .HasColumnType("TEXT");

                    b.Property<string>("Title")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<DateTime>("UpdatedAt")
                        .HasColumnType("TEXT");

                    b.HasKey("Id");

                    b.ToTable("AiProjects");
                });

            modelBuilder.Entity("xFileService.Models.Entities.XFile", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("TEXT");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("FileName")
                        .HasColumnType("TEXT");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<string>("OwnerId")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<string>("Path")
                        .IsRequired()
                        .HasColumnType("TEXT");

                    b.Property<string>("References")
                        .HasColumnType("TEXT");

                    b.Property<string>("Thumb")
                        .HasColumnType("TEXT");

                    b.Property<string>("ThumbPath")
                        .HasColumnType("TEXT");

                    b.Property<int>("Type")
                        .HasColumnType("INTEGER");

                    b.Property<DateTime>("UploadedOn")
                        .HasColumnType("TEXT");

                    b.HasKey("Id");

                    b.ToTable("Files");
                });

            modelBuilder.Entity("xStringService.Models.Entities.XString", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("INTEGER");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("Language")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(10);

                    b.Property<string>("ResourceTitle")
                        .IsRequired()
                        .HasColumnType("TEXT")
                        .HasMaxLength(255);

                    b.Property<string>("TranslatedValue")
                        .IsRequired()
                        .HasColumnType("TEXT");

                    b.HasKey("Id");

                    b.ToTable("Strings");
                });

            modelBuilder.Entity("xTagService.Models.Entities.XTag", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("INTEGER");

                    b.Property<bool>("Deleted")
                        .HasColumnType("INTEGER");

                    b.Property<string>("References")
                        .HasColumnType("TEXT");

                    b.Property<string>("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\Providers\XAiEmbeddingServiceBase.cs

using System;
using OpenAI;
using System.Linq;
using OllamaSharp;
using System.Threading;
using xAiApi.Constants;
using xAiModels.Models;
using xAiApi.Extensions;
using xAiApi.Interfaces;
using xAiService.Helpers;
using System.ClientModel;
using xCommons.Extensions;
using xAiModels.Constants;
using xAiModels.Extensions;
using xAiModels.Interfaces;
using xAiApi.Configurations;
using xExceptions.Constants;
using System.Threading.Tasks;
using xFileService.Interfaces;
using Microsoft.Extensions.AI;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using xAiApi.Interfaces.Extractors;
using System.ClientModel.Primitives;
using xCommons.Providers;

namespace xAiApi.Providers
{
    /// <summary>
    /// Base Embedding Provider Service ...
    /// </summary>
    public abstract class XAiEmbeddingServiceBase : XAIServiceBase, IXAiEmbeddingServiceBase
    {
        private readonly IXFileProvider fileProvider;
        private readonly XAiModelDescriptor descriptor;
        private readonly ILogger<XAiEmbeddingServiceBase> logger;

        protected XAiEmbeddingServiceBase(
            IXFileProvider fileProvider,
            IXAiDataProvider dataProvider,
            XAiApiConfiguration configuration,
            XValidationProvider validationProvider,
            ILogger<XAiEmbeddingServiceBase> logger,
            IXFileContentExtractor fileContentExtractor,
            string embeddingModelName = XAiApiConstants.XAiEmbeddingModelName
        ) : base(
            fileProvider, 
            dataProvider, 
            logger, 
            configuration, 
            validationProvider, 
            fileContentExtractor, 
            null
        )
        {
            //
            // Prepare Embedding Model Descriptor ...
            descriptor = configuration.GetModel(embeddingModelName);
            if (!descriptor.IsValid())
            {
                XException.InvalidConfiguration.Throw();
            }

            this.fileProvider = fileProvider;

            //
            this.logger = logger;
        }

        /// <summary>
        /// Embedding Specified String ...
        /// </summary>
        /// <param name="content"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<float[]> Embedding(
            string content,
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate ...
            if (content.IsNullOrEmpty())
            {
                XException.InvalidArgs.Throw();
            }

            //
            using var generator = GetEmbeddingGenerator();

            //
            GeneratedEmbeddings<Embedding<float>> response = null;
            response = await generator.GenerateAsync(
                values: [content],
                cancellationToken: cancellationToken
            );

            //
            if (response.IsNullOrDefault())
            {
                XException.ActionFailed.Throw();
            }

            //
            // Generae Result ...
            var result = response
                .First()?
                .Vector
                .ToArray()
               ?? [];
            if (result.Length == 0)
            {
                XException.ActionFailed.Throw();
            }

            //
            // Normalize ...
            VectorHelper.Normalize(result);

            //
            return result;
        }

        /// <summary>
        /// Embedding Specified Batch ...
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<IReadOnlyList<float[]>> Embeddings(
            string[] batch,
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate ...
            if (!batch.HasChild() ||
                !batch.All(x => !x.IsNullOrEmpty())
            )
            {
                XException.InvalidArgs.Throw();
            }

            //
            using var generator = GetEmbeddingGenerator();

            //
            GeneratedEmbeddings<Embedding<float>> response = null;
            response = await generator.GenerateAsync(
                values: [.. batch],
                cancellationToken: cancellationToken
            );

            //
            if (response.IsNullOrDefault())
            {
                XException.ActionFailed.Throw();
            }

            //
            var result = response
                .Select(e => e.Vector.ToArray())
                .ToList();

            //
            // Normalize ...
            foreach (var vector in result)
            {
                VectorHelper.Normalize(vector);
            }

            //
            return result;
        }

        /// <summary>
        /// Embedding Generator ...
        /// </summary>
        /// <returns></returns>
        public IEmbeddingGenerator<string, Embedding<float>> GetEmbeddingGenerator()
        {
            //
            IEmbeddingGenerator<string, Embedding<float>> result = null;

            //
            if (!descriptor.IsValid())
            {
                XException.InvalidData.Throw("Invalid Embedding Model ...");
            }

            //
            switch (descriptor.Provider)
            {
                //
                case XAiModelProviderType.OpenAI:
                    //
                    var openAiClient = new OpenAIClient(
                        new ApiKeyCredential(descriptor.ApiKey.IsNullOrEmpty() ? XAiApiConstants.XOpenAINoKey : descriptor.ApiKey),
                        new OpenAIClientOptions
                        {
                            Endpoint = new Uri(descriptor.Url),
                            Transport = new HttpClientPipelineTransport(GetHttpClient(descriptor.Url))
                        }
                    );
                    result = openAiClient
                        .GetEmbeddingClient(descriptor.LLM)
                        .AsIEmbeddingGenerator();
                    break;

                //
                case XAiModelProviderType.Ollama:
                    //
                    var ollamaClient = new OllamaApiClient(new Uri(descriptor.Url));
                    result = ollamaClient;
                    break;
            }

            //
            if (result.IsNull())
            {
                XException.InvalidData.Throw("Invalid Embedding Model ...");
            }

            //
            return result;
        }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\XAIServiceBase.cs

using System;
using OpenAI;
using OllamaSharp;
using System.Linq;
using xModels.Dtos;
using System.Net.Http;
using System.Threading;
using xAiApi.Constants;
using xAiModels.Models;
using xAiApi.Interfaces;
using xAiApi.Extensions;
using System.ClientModel;
using xCommons.Providers;
using xCommons.Extensions;
using xAiModels.Constants;
using xAiModels.Interfaces;
using xAiModels.Extensions;
using xAiService.Constants;
using xAiApi.Configurations;
using xExceptions.Constants;
using xAiModels.Models.Dtos;
using xAiService.Extensions;
using System.Threading.Tasks;
using xIdentityModels.Models;
using xFileService.Interfaces;
using Microsoft.Extensions.AI;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using xAiApi.Interfaces.Extractors;
using System.ClientModel.Primitives;
using System.Runtime.CompilerServices;

namespace xAiApi.Providers
{
    /// <summary>
    /// Base Ai Service ...
    /// </summary>
    public abstract class XAIServiceBase : IXAiServiceBase
    {
        /// <summary>
        /// Chat Options ...
        /// </summary>
        public ChatOptions Options { get; }

        /// <summary>
        /// Descriptor of Models which used in Service ...
        /// </summary>        
        public XAiModelDescriptor Descriptor { get; }

        private readonly IXFileProvider fileProvider;
        private readonly IXAiDataProvider dataProvider;
        private readonly ILogger<XAIServiceBase> logger;
        private readonly XAiApiConfiguration configuration;
        private readonly XValidationProvider validationProvider;
        private readonly IXFileContentExtractor fileContentExtractor;

        protected XAIServiceBase(
            IXFileProvider fileProvider,
            IXAiDataProvider dataProvider,
            ILogger<XAIServiceBase> logger,
            XAiApiConfiguration configuration,
            XValidationProvider validationProvider,
            IXFileContentExtractor fileContentExtractor,
            string model = null
        )
        {
            //
            this.logger = logger;
            this.fileProvider = fileProvider;
            this.dataProvider = dataProvider;
            this.configuration = configuration;
            this.validationProvider = validationProvider;
            this.fileContentExtractor = fileContentExtractor;

            //
            // Prepare Embedding Model Descriptor ...
            Descriptor = configuration.GetModel(model);
            if (!Descriptor.IsValid())
            {
                XException.InvalidConfiguration.Throw();
            }

            //
            // Prepare Chat Options based On Tools ...
            Options = new ChatOptions();
        }

        //
        #region Actions ...
        /// <summary>
        /// Base Method for Communicate with LLM ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<string> AskTextAsync(
            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;

            //
            return result;
        }

        /// <summary>
        /// Base Method for Communicate with LLM ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async IAsyncEnumerable<string> AskTextAsEnumerable(
            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
            );

            //
            await foreach (var res in enumerable)
            {
                //
                // Cancellation Token ...
                if (cancellationToken.IsCancellationRequested)
                {
                    yield break;
                }

                //
                yield return res.Text;
            }
        }

        /// <summary>
        /// Get LLM Client instance for Communicating with LLM ...
        /// </summary>
        /// <returns></returns>
        public virtual IChatClient GetClient()
        {
            //
            // Try to Initialize LLm ...
            var model = Descriptor.LLM;
            var apiKey = Descriptor.ApiKey;
            var url = new Uri(Descriptor.Url);
            var httpClient = GetHttpClient(Descriptor.Url);

            //
            IChatClient result = null;
            switch (Descriptor.Provider)
            {
                //
                case XAiModelProviderType.Ollama:
                    //
                    // Instance Ollama Client using Custom HttpCLient ...
                    var ollamaClient = new OllamaApiClient(httpClient, model);
                    result =
                        new ChatClientBuilder(ollamaClient)
                        .UseFunctionInvocation()
                        .Build();
                    break;

                //
                case XAiModelProviderType.OpenAI:
                    //
                    // Instance Ollama Client using Custom HttpCLient ...
                    var openAiClient = new OpenAIClient(
                        new ApiKeyCredential(apiKey.IsNullOrEmpty() ? XAiApiConstants.XOpenAINoKey : apiKey),
                        new OpenAIClientOptions
                        {
                            Endpoint = url,
                            Transport = new HttpClientPipelineTransport(httpClient)
                        }
                    );
                    result =
                        new ChatClientBuilder(
                            openAiClient
                            .GetChatClient(model)
                        .AsIChatClient())
                        .UseFunctionInvocation()
                        .Build();
                    break;

                //
                case XAiModelProviderType.DeepSeek:
                    break;

                //
                case XAiModelProviderType.HuggingFace:
                    break;

                //
                default:
                    break;
            }

            //
            if (result.IsNullOrDefault())
            {
                XException.InvalidData.Throw();
            }

            //
            return result;
        }

        /// <summary>
        /// Create Custom HttpClient for Communicating with LLM API ...
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        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;
        }

        /// <summary>
        /// Prepare a Message History List for LLM Communication based on Prompt ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="forcePrompt"></param>
        /// <returns></returns>
        public virtual IList<ChatMessage> 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<ChatMessage> result =
            [
                introductionPrompt,
            ];
            if (forcePrompt &&
                prompt is not null &&
                !string.IsNullOrWhiteSpace(prompt))
            {
                result.Add(promptMessage);
            }

            //
            return result;
        }

        /// <summary>
        /// Prepare a Message History List for LLM Communication based on Prompt ...
        /// </summary>
        /// <param name="message"></param>
        /// <param name="forceMessage"></param>
        /// <returns></returns>
        public virtual IList<ChatMessage> GetHistory(
            ChatMessage message = null,
            bool forceMessage = true
        )
        {
            //
            // Preparing Introduction Prompt ...
            var introductionPrompt = new ChatMessage(
                ChatRole.System,
                configuration.GetPrompt(XAiApiConstants.XAiApiIntroductionPromptName)
            );

            //
            List<ChatMessage> result =
            [
                introductionPrompt,
            ];
            if (forceMessage &&
                !message.IsNullOrDefault())
            {
                result.Add(message);
            }

            //
            return result;
        }
        #endregion

        //
        #region Loaders ...
        /// <summary>
        /// Load AI Projects ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="connectionId"></param>
        /// <param name="includeDefaults"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<IEnumerable<XAiProjectDto>> LoadProjects(
            string ownerId,
            string connectionId = null,
            bool includeDefaults = false,
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate Args ...
            if (ownerId.IsNullOrEmpty())
            {
                XException.InvalidArgs.Throw();
            }

            //
            var result = await dataProvider.LoadProjects(
                ownerId: ownerId,
                fillConversations: false,
                connectionId: connectionId,
                fillConversationMessages: false,
                cancellationToken: cancellationToken
            );

            //
            // Handle Defaults Including ...
            if (includeDefaults)
            {
                //
                // Here we have to fill Conversations of Default Project ...
                var defaultProjectId = result.GetDafualtProject(ownerId);
                var defaultProject = result.FirstOrDefault(x => x.Id == defaultProjectId);
                result = result.Where(x => x.Id != defaultProjectId);

                //
                // Reading Conversations of Specified Default Project ...
                var defaultProjectConversations = await dataProvider.LoadConversations(
                    fillMessages: false,
                    connectionId: connectionId,
                    projectId: defaultProjectId,
                    cancellationToken: cancellationToken
                );
                var defaultConversationId = defaultProjectConversations.GetDefaultConversation(
                    ownerId: ownerId,
                    projectId: defaultProjectId
                );
                var defaultConversation = defaultProjectConversations.First(x => x.Id == defaultConversationId);
                defaultProjectConversations = defaultProjectConversations.Where(x => x.Id != defaultConversationId);

                //
                // Reading Default Conversations Messages ...
                var defaultConversationMessages = await dataProvider.LoadMessages(
                    cancellationToken: cancellationToken,
                    conversationId: defaultConversationId
                );

                //
                defaultConversation.Messages = [.. defaultConversationMessages.OrderByDescending(m => m.CreatedOn)];
                defaultProjectConversations = [defaultConversation, .. defaultProjectConversations];
                defaultProject.Conversations = [.. defaultProjectConversations];
                result = [defaultProject, .. result];
            }

            //
            return result;
        }

        /// <summary>
        /// Load Conversations of Specified AI Project ...
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="connectionId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<IEnumerable<XAiConversationDto>> LoadConversations(
            Guid projectId,
            string connectionId = null,
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate Args ...
            if (projectId.IsDefaultGuid())
            {
                XException.InvalidArgs.Throw();
            }

            //
            var result = await dataProvider.LoadConversations(
                fillMessages: false,
                projectId: projectId,
                connectionId: connectionId,
                cancellationToken: cancellationToken
            );

            //
            return result;
        }

        /// <summary>
        /// Load Messages of Specified Conversation ...
        /// </summary>
        /// <param name="conversationId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<IEnumerable<XAiMessageDto>> LoadMessages(
            Guid conversationId,
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate Args ...
            if (conversationId.IsDefaultGuid())
            {
                XException.InvalidArgs.Throw();
            }

            //
            var result = await dataProvider.LoadMessages(
                conversationId: conversationId,
                cancellationToken: cancellationToken
            );

            //
            return result;
        }
        #endregion

        //
        #region Queries ...
        /// <summary>
        /// Query Projects ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<XQueryResult<XAiProjectDto>> QueryProjects(
            string ownerId,
            XQuery query,
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate ...
            if (ownerId.IsNullOrEmpty())
            {
                XException.InvalidArgs.Throw();
            }

            //
            var result = await dataProvider.QueryProjects(
                query: query,
                ownerId: ownerId,
                cancellationToken: cancellationToken
            );

            //
            return result;
        }

        /// <summary>
        /// Query Conversations ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="projectId"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<XQueryResult<XAiConversationDto>> QueryConversations(
            string ownerId,
            Guid projectId,
            XQuery query,
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate ...
            if (ownerId.IsNullOrEmpty() ||
                projectId.IsDefaultGuid())
            {
                XException.InvalidArgs.Throw();
            }

            //
            // Retrieve Project ...
            var project = await dataProvider.GetProject(
                id: projectId,
                fillConversations: false,
                fillConversationMessages: false,
                cancellationToken: cancellationToken
            );
            if (project.IsNullOrDefault())
            {
                XException.NotFound.Throw();
            }

            //
            // Validate Owner ...
            if (ownerId != project.OwnerId)
            {
                XException.NotAllowed.Throw();
            }

            //
            var result = await dataProvider.QueryConversations(
                query: query,
                ownerId: ownerId,
                projectId: projectId,
                cancellationToken: cancellationToken
            );

            //
            return result;
        }

        /// <summary>
        /// Query Messages ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="conversationId"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<XQueryResult<XAiMessageDto>> QueryMessages(
            string ownerId,
            Guid conversationId,
            XQuery query,
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate ...
            if (ownerId.IsNullOrEmpty() ||
                conversationId.IsDefaultGuid())
            {
                XException.InvalidArgs.Throw();
            }

            //
            // Retrieve Conversation ...
            var conversation = await dataProvider.GetConversation(
                id: conversationId,
                fillMessages: false,
                cancellationToken: cancellationToken
            );
            if (conversation.IsNullOrDefault())
            {
                XException.NotFound.Throw();
            }

            //
            // Validate Owner ...
            if (ownerId != conversation.OwnerId)
            {
                XException.NotAllowed.Throw();
            }

            //
            var result = await dataProvider.QueryMessages(
                query: query,
                ownerId: ownerId,
                conversationId: conversationId,
                cancellationToken: cancellationToken
            );

            //
            return result;
        }
        #endregion

        //
        #region Creators ...
        /// <summary>
        /// Create an AI Project ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="item"></param>
        /// <param name="connectionId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<XAiProjectDto> CreateProject(
            string ownerId,
            XAiProjectDto item,
            string connectionId = null,
            CancellationToken cancellationToken = default
        )
        {
            //
            var result = await dataProvider.CreateProject(
                item: item,
                ownerId: ownerId,
                connectionId: connectionId,
                cancellationToken: cancellationToken
            );

            //
            return result;
        }

        /// <summary>
        /// Create an AI Conversation ...
        /// </summary>
        /// <param name="ownerId"></param>
        /// <param name="projectId"></param>
        /// <param name="item"></param>
        /// <param name="connectionId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<XAiConversationDto> CreateConversation(
            string ownerId,
            Guid projectId,
            XAiConversationDto item,
            string connectionId = null,
            CancellationToken cancellationToken = default
        )
        {
            //
            var result = await dataProvider.CreateConversation(
                item: item,
                ownerId: ownerId,
                projectId: projectId,
                connectionId: connectionId,
                cancellationToken: cancellationToken
            );

            //
            return result;
        }
        #endregion

        //
        #region Content Extraction ...
        /// <summary>
        /// Try to Extract Files Content ...
        /// </summary>
        /// <param name="files"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<string> ExtractContent(
            IFormFileCollection files,
            CancellationToken cancellationToken = default
        )
        {
            //
            if (!files.HasChild())
            {
                XException.InvalidArgs.Throw();
            }

            //
            var extractionResults = new List<XFileExtractionResult>();
            foreach (var file in files)
            {
                //
                if (file.IsNull() || file.Length == 0)
                {
                    continue;
                }

                //
                var mimeType = file.ContentType?.ToLowerInvariant() ?? string.Empty;
                if (!fileContentExtractor.CanExtract(mimeType))
                {
                    continue;
                }

                //
                try
                {
                    //
                    using var stream = file.OpenReadStream();

                    //
                    var fr = await fileContentExtractor.ExtractRichAsync(
                        fileStream: stream,
                        mimeType: mimeType,
                        fileName: file.FileName,
                        cancellationToken: cancellationToken
                    );

                    //
                    if (fr.IsValid())
                    {
                        extractionResults.Add(fr);
                    }
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch
                {
                    continue;
                }
            }

            //
            var result = string.Empty;
            if (extractionResults.HasChild())
            {
                //
                extractionResults.ForEach(er =>
                {
                    result += $"File {er.FileName} Content:{Environment.NewLine}{er.Text ?? er.AudioTranscript}{Environment.NewLine}----";
                });
            }

            //
            return result;
        }
        #endregion

        //
        #region Data Actions ...
        /// <summary>
        /// Ask a Question as Prompt and Retrieve Response ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="ownerId"></param>
        /// <param name="projectId"></param>
        /// <param name="conversationId"></param>
        /// <param name="connectionId"></param>
        /// <param name="files"></param>
        /// <param name="userInfo"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<XAiMessageDto> AskAsync(
            string prompt,
            string ownerId,
            Guid projectId,
            Guid conversationId,
            string connectionId = null,
            IFormFileCollection files = null,
            XUserClaimsInfoDto userInfo = null,
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate Args ...
            validationProvider
                .GroupValidationBuilder()
                .AddNotEmpty(prompt)
                .AddNotEmpty(ownerId)
                .ValidateGroup();

            //
            // Check Project ...
            XAiProjectDto project = null;
            if (projectId.IsDefaultGuid())
            {
                //
                project = await dataProvider.GetDefaultProject(
                    ownerId: ownerId,
                    connectionId: connectionId,
                    cancellationToken: cancellationToken
                );

                //
                projectId = project.Id;
            }
            else
            {
                //
                project = await dataProvider.GetProject(
                    id: projectId,
                    fillConversations: false,
                    fillConversationMessages: false,
                    cancellationToken: cancellationToken
                );
            }
            if (project.IsNullOrDefault())
            {
                XException.NotFound.Throw();
            }

            //
            // Check Conversation ...
            XAiConversationDto conversation = null;
            if (conversationId.IsDefaultGuid())
            {
                //
                conversation = await dataProvider.GetDefaultConversation(
                    ownerId: ownerId,
                    projectId: projectId,
                    connectionId: connectionId,
                    cancellationToken: cancellationToken
                );

                //
                conversationId = conversation.Id;
            }
            else
            {
                //
                conversation = await dataProvider.GetConversation(
                    id: conversationId,
                    fillMessages: false,
                    cancellationToken: cancellationToken
                );
            }
            if (conversation.IsNullOrDefault())
            {
                XException.NotFound.Throw();
            }

            //
            // Reading Messages ...
            var messages = await dataProvider.LoadMessages(
                conversationId: conversationId,
                cancellationToken: cancellationToken
            );

            //
            // Upload Files ...
            IEnumerable<xFileService.Models.Dtos.XFileDto> uploadedFiles = null;
            if (files.HasChild())
            {
                //
                // Validate User Info ...
                if (userInfo.IsNullOrDefault())
                {
                    XException.InvalidArgs.Throw();
                }

                //
                // Upload Files ...
                uploadedFiles = await fileProvider.Upload(
                    files: files,
                    userInfo: userInfo,
                    connectionId: connectionId,
                    cancellationToken: cancellationToken
                );
            }

            //
            // Handle Memory ...
            IList<ChatMessage> history = await PrepareMemory(
                project: project,
                messages: messages,
                cancellationToken: cancellationToken
            );

            //
            // Prepare and Add Prompt Message ...
            var promptMessage = new XAiMessageDto
            {
                Content = prompt,
                OwnerId = ownerId,
                Role = XAiChatRole.User,
                CreatedOn = DateTime.UtcNow,
                ConversationId = conversationId,
                ConversationTitle = conversation.Title
            };

            //
            // Attached Uploaded File Id's as Meta Data ...
            if (uploadedFiles.HasChild())
            {
                //
                var fileIds = uploadedFiles.Select(f => f.Id).ToList();
                promptMessage.MetaDatas = new Dictionary<string, object>
                {
                    { XAiServiceConstants.XMetaDataFileIdsIdentifier, fileIds }
                }.ToJSON();
            }

            //
            // Save Prompt Message ...
            promptMessage = await dataProvider.AddMessage(
                item: promptMessage,
                connectionId: connectionId,
                conversationId: conversationId,
                cancellationToken: cancellationToken
            );

            //
            // Since Prompt Message Has Id now, we can Attach Uploaded Files to it ...
            IList<AIContent> fileContents = [];
            if (uploadedFiles.HasChild())
            {
                //
                foreach (var uf in uploadedFiles)
                {
                    //
                    try
                    {
                        //
                        // Add Reference File to Prompt Message ...
                        await fileProvider.AddReference(
                            id: uf.Id,
                            userInfo: userInfo,
                            connectionId: connectionId,
                            forProvidedId: promptMessage.Id,
                            cancellationToken: cancellationToken,
                            providedFor: XAiServiceConstants.XAiMessageFilesIdentifier
                        );

                        //
                        // Handle File Content Extraction and Attach to Prompt Message ...
                        var fileDescriptor = await fileProvider.GetFileDescriptor(
                            id: uf.Id,
                            cancellationToken: cancellationToken
                        );
                        if (fileDescriptor == null || fileDescriptor.Stream == null)
                        {
                            continue;
                        }

                        //
                        var extractionResult = await fileContentExtractor.ExtractRichAsync(
                            fileStream: fileDescriptor.Stream,
                            fileName: uf.FileName ?? uf.Name,
                            mimeType: fileDescriptor.MIMEType,
                            cancellationToken: cancellationToken
                        );
                        if (extractionResult.IsValid())
                        {
                            // 
                            var contents = extractionResult.ToAIContents();
                            fileContents = [.. fileContents, .. contents];

                            //
                            // ذخیره Metadata برای ردیابی
                            logger.LogInformation(
                                "File {FileName} extracted successfully. Text: {HasText}, Images: {ImageCount}, Fallback: {UsedFallback}",
                                uf.FileName,
                                !string.IsNullOrEmpty(extractionResult.Text),
                                extractionResult.Images.Count,
                                extractionResult.UsedImageFallback
                            );
                        }

                        //
                        fileDescriptor.Stream.Dispose();
                    }
                    catch (Exception ex)
                    {
                        logger.LogError(ex, "Failed to extract content from file: {FileName}", uf.FileName);
                    }
                }
            }

            //
            // Converts to ChatMessage ...
            var promptChatMessage = promptMessage
                .ToChatMessages(fileContents);

            //
            // Ask Questions From LLM ...
            var answer = await AskLLMAsync(
                history: history,
                prompt: promptChatMessage,
                cancellationToken: cancellationToken
            );

            //
            // Prepare Answer AI Message ...
            var result = new XAiMessageDto
            {
                Content = answer,
                OwnerId = ownerId,
                CreatedOn = DateTime.UtcNow,
                Role = XAiChatRole.Assistant,
                ConversationId = conversationId,
                ConversationTitle = conversation.Title
            };
            result = await dataProvider.AddMessage(
                item: result,
                connectionId: connectionId,
                conversationId: conversationId,
                cancellationToken: cancellationToken
            );

            //
            return result;
        }

        /// <summary>
        /// Ask a Question as Prompt and Retrieve Response as Async Enumerable ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="ownerId"></param>
        /// <param name="projectId"></param>
        /// <param name="conversationId"></param>
        /// <param name="connectionId"></param>
        /// <param name="files"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async IAsyncEnumerable<XAiResonseMessage> AskAsEnumerable(
            string prompt,
            string ownerId,
            Guid projectId,
            Guid conversationId,
            string connectionId = null,
            IFormFileCollection files = null,
            [EnumeratorCancellation]
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate Args ...
            validationProvider
                .GroupValidationBuilder()
                .AddNotEmpty(prompt)
                .AddNotEmpty(ownerId)
                .ValidateGroup();

            //
            // Check Project ...
            XAiProjectDto project = null;
            if (projectId.IsDefaultGuid())
            {
                //
                project = await dataProvider.GetDefaultProject(
                    ownerId: ownerId,
                    connectionId: connectionId,
                    cancellationToken: cancellationToken
                );

                //
                projectId = project.Id;
            }
            else
            {
                //
                project = await dataProvider.GetProject(
                    id: projectId,
                    fillConversations: false,
                    fillConversationMessages: false,
                    cancellationToken: cancellationToken
                );
            }
            if (project.IsNullOrDefault())
            {
                XException.NotFound.Throw();
            }

            //
            // Check Conversation ...
            XAiConversationDto conversation = null;
            if (conversationId.IsDefaultGuid())
            {
                //
                conversation = await dataProvider.GetDefaultConversation(
                    ownerId: ownerId,
                    projectId: projectId,
                    connectionId: connectionId,
                    cancellationToken: cancellationToken
                );

                //
                conversationId = conversation.Id;
            }
            else
            {
                //
                conversation = await dataProvider.GetConversation(
                    id: conversationId,
                    fillMessages: false,
                    cancellationToken: cancellationToken
                );
            }
            if (conversation.IsNullOrDefault())
            {
                XException.NotFound.Throw();
            }

            //
            // Reading Messages ...
            var messages = await dataProvider.LoadMessages(
                conversationId: conversationId,
                cancellationToken: cancellationToken
            );

            //
            // TODO: Parse Files ...
            // for Attached them into Prompt Message ...

            //
            // Handle Memory ...
            IList<ChatMessage> history = await PrepareMemory(
                project: project,
                messages: messages,
                cancellationToken: cancellationToken
            );

            //
            // Prepare and Add Prompt Message ...
            var promptMessage = new XAiMessageDto
            {
                Content = prompt,
                OwnerId = ownerId,
                Role = XAiChatRole.User,
                CreatedOn = DateTime.UtcNow,
                ConversationId = conversationId,
            };
            promptMessage = await dataProvider.AddMessage(
                item: promptMessage,
                connectionId: connectionId,
                conversationId: conversationId,
                cancellationToken: cancellationToken
            );
            promptMessage = await dataProvider.Enrich(
                source: promptMessage,
                cancellationToken: cancellationToken
            );

            //
            // Attach Uploaded Files ...
            var promptChatMessage = promptMessage
                .ToChatMessages();
            history.Add(promptChatMessage);

            //
            // Ask Questions From LLM ...
            using var client = GetClient();
            var enumerable = client.GetStreamingResponseAsync(
                options: null,
                messages: history
            );

            //
            long sequence = 1;
            var answer = string.Empty;
            await foreach (var res in enumerable)
            {
                //
                // Cancellation Token ...
                if (cancellationToken.IsCancellationRequested)
                {
                    yield break;
                }

                //
                var resp = new XAiResonseMessage
                {
                    OwnerId = ownerId,
                    Content = res.Text,
                    Sequence = sequence,
                    Role = XAiChatRole.System,
                    RespondTo = promptMessage.Id,
                    RespondedOn = DateTime.UtcNow,
                };

                //
                sequence++;
                answer += res.Text;

                //
                yield return resp;
            }

            //
            // Prepare Answer AI Message ...
            var result = new XAiMessageDto
            {
                Content = answer,
                OwnerId = ownerId,
                Role = XAiChatRole.System,
                CreatedOn = DateTime.UtcNow,
                ConversationId = conversationId
            };
            result = await dataProvider.AddMessage(
                item: result,
                connectionId: connectionId,
                conversationId: conversationId,
                cancellationToken: cancellationToken
            );
        }
        #endregion

        /// <summary>
        /// Base Dispose Action ...
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        public virtual void Dispose()
        { }

        //
        #region Private ...
        /// <summary>
        /// Implementing Memory Strategy here ...
        /// </summary>
        /// <param name="project"></param>
        /// <param name="messages"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task<IList<ChatMessage>> PrepareMemory(
            XAiProjectDto project,
            IEnumerable<XAiMessageDto> messages,
            CancellationToken cancellationToken = default
        )
        {
            //
            var result = new List<ChatMessage>();

            //
            // Retrieve Introduction Prompt ...
            var introductionPrompt = configuration.GetPrompt(XAiApiConstants.XAiApiIntroductionPromptName);
            if (introductionPrompt.IsNullOrEmpty())
            {
                XException.InvalidData.Throw();
            }

            //
            var introductionMessage = new ChatMessage(
                role: ChatRole.System,
                content: introductionPrompt
            );

            //
            result.Add(introductionMessage);

            //
            if (!project.IsNullOrDefault() &&
                !project.Prompt.IsNullOrEmpty())
            {
                //
                // TODO: Handle Project Files Here ...
                var projectMessage = new ChatMessage(
                    role: ChatRole.System,
                    contents: [new TextContent(project.Prompt)]
                );
                result.Add(projectMessage);
            }

            //
            if (messages.HasChild())
            {
                //
                // TODO: Here we can Implement Memory Management based on 
                // Last nth of Messages, or Summarization of Messages, etc ...
                result = [..result, ..messages
                    .OrderBy(m => m.CreatedOn)
                    .Select(m => m.ToChatMessages())];
            }

            //
            return result;
        }

        /// <summary>
        /// Ask Quetion from LLM ...
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="history"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task<string> AskLLMAsync(
            ChatMessage prompt,
            IList<ChatMessage> history,
            CancellationToken cancellationToken = default
        )
        {
            //
            if (prompt.IsNullOrDefault())
            {
                XException.InvalidArgs.Throw();
            }

            //
            history.Add(prompt);
            using var client = GetClient();
            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;

            //
            return result;
        }
        #endregion
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\XDefaultAIOCRService.cs

using System.Threading;
using xAiApi.Interfaces;
using xCommons.Providers;
using xCommons.Extensions;
using xAiModels.Interfaces;
using xAiModels.Extensions;
using xAiApi.Configurations;
using xExceptions.Constants;
using System.Threading.Tasks;
using xFileService.Interfaces;
using Microsoft.Extensions.AI;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using xAiApi.Interfaces.Extractors;
using System.Runtime.CompilerServices;

namespace xAiApi.Providers
{
    public class XDefaultAIOCRService : XAIServiceBase, IXDefaultAIOCRService
    {
        private readonly string prompt;

        public XDefaultAIOCRService(
            IXFileProvider fileProvider,
            IXAiDataProvider dataProvider,
            ILogger<XAIServiceBase> logger,
            XAiApiConfiguration configuration,
            XValidationProvider validationProvider,
            IXFileContentExtractor fileContentExtractor,
            string model = null,
            string prompt = null
        ) : base(
            fileProvider,
            dataProvider,
            logger,
            configuration,
            validationProvider,
            fileContentExtractor,
            model
        )
        {
            //
            this.prompt = prompt;
            if (this.prompt.IsNullOrEmpty())
            {
                XException.InvalidArgs.Throw();
            }
        }

        //
        #region OCR ...
        /// <summary>
        /// Request for Doing OCR on Given Data Contents ...
        /// </summary>
        /// <param name="messages"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>        
        public virtual async Task<string> RequestOCRAsync(
            IList<ChatMessage> messages,
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate ...
            if (!messages.HasChild())
            {
                XException.InvalidArgs.Throw();
            }

            //
            using var client = GetClient();

            //
            // Preparing Extraction Prompt Message ...
            var pMessage = new ChatMessage(
                ChatRole.System,
                prompt
            );

            //
            messages = [pMessage, .. messages];

            //
            var response = await client.GetResponseAsync(
                messages: messages,
                cancellationToken: cancellationToken
            );

            //
            // Validate Response ...
            if (!response.IsValid())
            {
                //
                // Dispose Client ...
                client.Dispose();
                XException.ActionFailed.Throw();
            }

            //
            // Retrieve Response Text ...
            var result = response.Text;

            //
            return result;
        }

        /// <summary>
        /// Request for Doing OCR on Given Data Contents as Stream ...
        /// </summary>
        /// <param name="messages"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>        
        public virtual async IAsyncEnumerable<string> RequestOCRAsEnumerable(
            IList<ChatMessage> messages,
            [EnumeratorCancellation]
            CancellationToken cancellationToken = default
        )
        {
            //
            // Validate ...
            if (!messages.HasChild())
            {
                XException.InvalidArgs.Throw();
            }

            //
            using var client = GetClient();

            //
            // Preparing Extraction Prompt Message ...
            var pMessage = new ChatMessage(
                ChatRole.System,
                prompt
            );

            //
            messages = [pMessage, .. messages];

            //
            var enumerable = client.GetStreamingResponseAsync(
                options: null,
                messages: messages
            );

            //
            await foreach (var res in enumerable)
            {
                //
                // Cancellation Token ...
                if (cancellationToken.IsCancellationRequested)
                {
                    yield break;
                }

                //
                yield return res.Text;
            }
        }
        #endregion
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\XDefaultAiService.cs

using xAiApi.Constants;
using xAiApi.Interfaces;
using xCommons.Providers;
using xAiModels.Interfaces;
using xAiApi.Configurations;
using xFileService.Interfaces;
using Microsoft.Extensions.Logging;
using xAiApi.Interfaces.Extractors;

namespace xAiApi.Providers
{
    public class XDefaultAiService : XAIServiceBase, IXDefaultAiService
    {
        public XDefaultAiService(
            IXFileProvider fileProvider,
            IXAiDataProvider dataProvider,
            ILogger<XDefaultAiService> logger,
            XAiApiConfiguration configuration,
            XValidationProvider validationProvider,
            IXFileContentExtractor fileContentExtractor
        ) : base(
            fileProvider,
            dataProvider,
            logger,
            configuration,
            validationProvider,
            fileContentExtractor,
            XAiApiConstants.XAiDefaultModelName
        )
        { }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\XDefaultEmbeddingService.cs

using xAiApi.Constants;
using xAiApi.Interfaces;
using xAiModels.Interfaces;
using xAiApi.Configurations;
using xFileService.Interfaces;
using Microsoft.Extensions.Logging;
using xAiApi.Interfaces.Extractors;
using xCommons.Providers;

namespace xAiApi.Providers
{
    public class XDefaultEmbeddingService : XAiEmbeddingServiceBase, IXDefaultEmbeddingService
    {
        public XDefaultEmbeddingService(
            IXFileProvider fileProvider,
            IXAiDataProvider dataProvider,
            XAiApiConfiguration configuration,
            XValidationProvider validationProvider,
            ILogger<XDefaultEmbeddingService> logger,
            IXFileContentExtractor fileContentExtractor
        ) : base(
            fileProvider,
            dataProvider,
            configuration,
            validationProvider,
            logger,
            fileContentExtractor,
            XAiApiConstants.XAiEmbeddingModelName
        )
        { }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\XDefaultThinkingAiService.cs

using xAiApi.Constants;
using xAiApi.Interfaces;
using xCommons.Providers;
using xAiModels.Interfaces;
using xAiApi.Configurations;
using Microsoft.Extensions.AI;
using xFileService.Interfaces;
using Microsoft.Extensions.Logging;
using xAiApi.Interfaces.Extractors;

namespace xAiApi.Providers
{
    public class XDefaultThinkingAiService : XAIServiceBase, IXDefaultThinkingAiService
    {
        public XDefaultThinkingAiService(
            IXFileProvider fileProvider,
            IXAiDataProvider dataProvider,
            ILogger<XAIServiceBase> logger,
            XAiApiConfiguration configuration,
            XValidationProvider validationProvider,
            IXFileContentExtractor fileContentExtractor,
            ReasoningEffort effort = ReasoningEffort.Medium,
            ReasoningOutput output = ReasoningOutput.Full
        ) : base(
            fileProvider,
            dataProvider,
            logger,
            configuration,
            validationProvider,
            fileContentExtractor,
            XAiApiConstants.XAiDefaultThinkingModelName
        )
        {
            //
            // Preparing Reasoning ...
            Options.Reasoning = new ReasoningOptions
            {
                Output = output,
                Effort = effort,
            };
        }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\Extractors\XAudioFileContentExtractor.cs

using System;
using System.IO;
using System.Linq;
using Whisper.net;
using System.Threading;
using xAiModels.Models;
using System.Threading.Tasks;
using xAiApi.Interfaces.Extractors;
using DocumentFormat.OpenXml.EMMA;

namespace xAiApi.Providers.Extractors
{
    /// <summary>
    /// Extracts text content from Audio files ...
    /// </summary>    
    public class XAudioFileContentExtractor : IXAudioFileContentExtractor
    {
        /// <summary>
        /// Supported MIME Types ...
        /// </summary>
        private static readonly string[] SupportedMimeTypes =
        [
            "audio/mpeg",
            "audio/mp3",
            "audio/wav",
            "audio/wave",
            "audio/ogg",
            "audio/m4a",
            "audio/mp4",
            "audio/webm"
        ];

        private readonly string language;
        private readonly string whisperModelPath;

        public XAudioFileContentExtractor() : this(
                language: "fa",
                whisperModelPath: "Models/ggml-base.bin"
        )
        { }

        public XAudioFileContentExtractor(
            string whisperModelPath = "Models/ggml-base.bin",
            string language = "fa"
        )
        {
            //
            this.language = language;
            this.whisperModelPath = whisperModelPath;
        }

        /// <summary>
        /// Check if this extractor supports the specified MIME type ...
        /// </summary>
        public bool CanExtract(string mimeType)
        {
            //
            return SupportedMimeTypes.Contains(
                mimeType?.ToLowerInvariant() ?? string.Empty
            );
        }

        /// <summary>
        /// Extract text content from file stream ...
        /// </summary>
        public async Task<string> ExtractAsync(
            Stream fileStream,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var result = await ExtractRichAsync(
                fileStream,
                "audio",
                mimeType,
                cancellationToken
            );

            //
            return result.AudioTranscript;
        }

        /// <summary>
        /// Extract content from stream as Rich Result ...
        /// </summary>
        /// <param name="fileStream"></param>
        /// <param name="fileName"></param>
        /// <param name="mimeType"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<XFileExtractionResult> ExtractRichAsync(
            Stream fileStream,
            string fileName,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var result = new XFileExtractionResult
            {
                FileName = fileName,
                MimeType = mimeType
            };

            //
            try
            {
                //
                using var memoryStream = new MemoryStream();
                await fileStream.CopyToAsync(memoryStream, cancellationToken);
                memoryStream.Position = 0;

                //
                using var wavStream = await ConvertToWavAsync(
                    memoryStream,
                    cancellationToken
                );

                //
                using var factory = WhisperFactory.FromPath(whisperModelPath);
                using var processor = factory.CreateBuilder()
                    .WithLanguage(language)
                    .Build();

                //
                var segments = new System.Text.StringBuilder();
                await foreach (var segment in processor.ProcessAsync(wavStream, cancellationToken))
                {
                    segments.Append(segment.Text);
                }

                //
                result.AudioTranscript = segments.ToString().Trim();
                result.Text = result.AudioTranscript;
            }
            catch (Exception ex)
            {
                result.ErrorMessage = $"Audio extraction failed: {ex.Message}";
            }

            //
            return result;
        }

        /// <summary>
        /// Audio Format Converting ...
        /// </summary>
        private async Task<Stream> ConvertToWavAsync(
            Stream inputStream,
            CancellationToken cancellationToken
        )
        {
            //
            // TODO: Complete this ...
            return await Task.FromResult(inputStream);
        }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\Extractors\XDocxFileContentExtractor.cs

using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using xAiModels.Models;
using System.Threading.Tasks;
using xAiApi.Interfaces.Extractors;
using DocumentFormat.OpenXml.Drawing;
using DocumentFormat.OpenXml.Packaging;

namespace xAiApi.Providers.Extractors
{
    /// <summary>
    /// Extracts text content from DOCX files using DocumentFormat.OpenXml ...
    /// </summary>    
    public class XDocxFileContentExtractor : IXDocxFileContentExtractor
    {
        /// <summary>
        /// Supported MIME Types ...
        /// </summary>
        private static readonly string[] SupportedMimeTypes =
        [
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
        ];

        /// <summary>
        /// Check if this extractor supports the specified MIME type ...
        /// </summary>
        public bool CanExtract(string mimeType)
        {
            //
            return SupportedMimeTypes.Contains(
                mimeType?.ToLowerInvariant() ?? string.Empty
            );
        }

        /// <summary>
        /// Extract text content from file stream ...
        /// </summary>
        public async Task<string> ExtractAsync(
            Stream fileStream,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            using var memoryStream = new MemoryStream();
            await fileStream.CopyToAsync(memoryStream, cancellationToken);
            memoryStream.Position = 0;

            //
            var stringBuilder = new StringBuilder();

            //
            using (var wordDocument = WordprocessingDocument.Open(memoryStream, false))
            {
                //
                var body = wordDocument.MainDocumentPart?.Document?.Body;
                if (body != null)
                {
                    //
                    var paragraphs = body.Elements<Paragraph>();
                    foreach (var paragraph in paragraphs)
                    {
                        //
                        var text = paragraph.InnerText?.Trim();
                        if (!string.IsNullOrEmpty(text))
                        {
                            stringBuilder.AppendLine(text);
                        }
                    }
                }
            }

            //
            return stringBuilder.ToString();
        }

        /// <summary>
        /// Extract content from stream as Rich Result ...
        /// </summary>
        /// <param name="fileStream"></param>
        /// <param name="fileName"></param>
        /// <param name="mimeType"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<XFileExtractionResult> ExtractRichAsync(
            Stream fileStream,
            string fileName,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var content = await ExtractAsync(
                mimeType: mimeType,
                fileStream: fileStream,
                cancellationToken: cancellationToken
            );

            //
            var result = new XFileExtractionResult
            {
                Text = content,
                FileName = fileName,
                MimeType = mimeType
            };

            //
            return result;
        }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\Extractors\XExcelFileContentExtractor.cs

using System;
using System.IO;
using System.Data;
using System.Linq;
using System.Text;
using ExcelDataReader;
using System.Threading;
using xAiModels.Models;
using System.Threading.Tasks;
using xAiApi.Interfaces.Extractors;

namespace xAiApi.Providers.Extractors
{
    public class XExcelFileContentExtractor : IXExcelFileContentExtractor
    {
        /// <summary>
        /// Supported MIME Types ...
        /// </summary>
        private static readonly string[] SupportedMimeTypes =
        [
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // .xlsx
            "application/vnd.ms-excel" // .xls        
        ];

        /// <summary>
        /// Check if this extractor supports the specified MIME type ...
        /// </summary>
        public bool CanExtract(string mimeType)
        {
            //
            return SupportedMimeTypes.Contains(
                mimeType?.ToLowerInvariant() ?? string.Empty
            );
        }

        /// <summary>
        /// Extract text content from file stream ...
        /// </summary>
        public async Task<string> ExtractAsync(
            Stream fileStream,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            //
            using var memoryStream = new MemoryStream();
            await fileStream.CopyToAsync(memoryStream, cancellationToken);
            memoryStream.Position = 0;

            //
            var stringBuilder = new StringBuilder();

            //
            using (var reader = ExcelReaderFactory.CreateReader(memoryStream))
            {
                //
                var result = reader.AsDataSet();
                foreach (DataTable table in result.Tables)
                {
                    //
                    stringBuilder.AppendLine($"--- Sheet: {table.TableName} ---");

                    //
                    var headers = new string[table.Columns.Count];
                    for (int i = 0; i < table.Columns.Count; i++)
                    {
                        headers[i] = table.Columns[i].ColumnName;
                    }
                    stringBuilder.AppendLine(string.Join(" | ", headers));
                    stringBuilder.AppendLine(new string('-', 50));

                    //
                    foreach (DataRow row in table.Rows)
                    {
                        //
                        var rowValues = new string[row.ItemArray.Length];
                        for (int i = 0; i < row.ItemArray.Length; i++)
                        {
                            rowValues[i] = row.ItemArray[i]?.ToString()?.Trim() ?? string.Empty;
                        }
                        stringBuilder.AppendLine(string.Join(" | ", rowValues));
                    }
                    stringBuilder.AppendLine();
                }
            }

            //
            return stringBuilder.ToString();
        }

        /// <summary>
        /// Extract content from stream as Rich Result ...
        /// </summary>
        /// <param name="fileStream"></param>
        /// <param name="fileName"></param>
        /// <param name="mimeType"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<XFileExtractionResult> ExtractRichAsync(
            Stream fileStream,
            string fileName,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var content = await ExtractAsync(
                mimeType: mimeType, 
                fileStream: fileStream, 
                cancellationToken: cancellationToken
            );

            //
            var result = new XFileExtractionResult
            {
                Text = content,
                FileName = fileName,
                MimeType = mimeType
            };

            //
            return result;
        }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\Extractors\XFileContentExtractor.cs

using System;
using System.IO;
using System.Linq;
using System.Threading;
using xAiModels.Models;
using System.Reflection;
using xCommons.Extensions;
using xExceptions.Constants;
using System.Threading.Tasks;
using System.Collections.Generic;
using xAiApi.Interfaces.Extractors;

namespace xAiApi.Providers.Extractors
{
    /// <summary>
    /// Composite extractor that delegates to appropriate extractor 
    /// based on MIME type ...
    /// </summary>    
    public class XFileContentExtractor : IXFileContentExtractor
    {
        //
        private readonly IList<IXFileContentExtractor> extractors = [];

        public XFileContentExtractor()
        {
            //
            var path = Path.Combine(AppContext.BaseDirectory);
            var files = Directory.GetFiles(path, "*.dll");
            foreach (var dll in files)
            {
                //
                // Retrieve DLL Assembly ...
                var assembly = Assembly.LoadFrom(dll);

                //
                // Extract Registerar Types ...
                var extractorTypes = assembly
                    .GetExportedTypes()
                    .Where(t =>
                        !t.IsAbstract &&
                        typeof(IXFileContentExtractor).IsAssignableFrom(t));
                foreach (var extractorType in extractorTypes)
                {
                    //
                    // Ignore XFileContentExtractor ...
                    if (extractorType.FullName.Contains(nameof(XFileContentExtractor)))
                    {
                        continue;
                    }

                    //
                    // Create Instance of Service ...
                    var instance = (IXFileContentExtractor)Activator.CreateInstance(extractorType);
                    if (!instance.IsNull())
                    {
                        //
                        // Add Registerar Instance to Collection ...
                        extractors.Add(instance);
                    }
                }
            }
        }

        /// <summary>
        /// Check if this extractor supports the specified MIME type ...
        /// </summary>
        public bool CanExtract(string mimeType)
        {
            return extractors.Any(e => e.CanExtract(mimeType));
        }

        /// <summary>
        /// Extract text content from file stream ...
        /// </summary>
        public async Task<string> ExtractAsync(
            Stream fileStream,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var extractor = extractors
                .FirstOrDefault(e => e.CanExtract(mimeType));
            if (extractor.IsNull())
            {
                //
                XException.NotAllowed.Throw(
                    $"Unsupported file type: {mimeType}"
                );
            }

            //
            var result = await extractor
                .ExtractAsync(
                    mimeType: mimeType,
                    fileStream: fileStream,
                    cancellationToken: cancellationToken
                );

            //
            return result;
        }

        /// <summary>
        /// Extract content from stream as Rich Result ...
        /// </summary>
        /// <param name="fileStream"></param>
        /// <param name="fileName"></param>
        /// <param name="mimeType"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<XFileExtractionResult> ExtractRichAsync(
            Stream fileStream,
            string fileName,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var extractor = extractors
                .FirstOrDefault(e => e.CanExtract(mimeType));
            if (extractor.IsNull())
            {
                //
                XException.NotAllowed.Throw(
                    $"Unsupported file type: {mimeType}"
                );
            }

            //
            var result = await extractor
                .ExtractRichAsync(
                    mimeType: mimeType,
                    fileName: fileName,
                    fileStream: fileStream,
                    cancellationToken: cancellationToken
                );

            //
            return result;
        }    
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\Extractors\XImageFileContentExtractor.cs

using System;
using System.IO;
using System.Linq;
using System.Threading;
using xAiModels.Models;
using System.Threading.Tasks;
using xAiApi.Interfaces.Extractors;

namespace xAiApi.Providers.Extractors
{
    public class XImageFileContentExtractor : IXImageFileContentExtractor
    {
        /// <summary>
        /// Supported MIME Types ...
        /// </summary>
        private static readonly string[] SupportedMimeTypes =
        [
            "image/png",
            "image/jpeg",
            "image/jpg",
            "image/gif",
            "image/webp",
            "image/bmp"
        ];

        /// <summary>
        /// آیا OCR فعال است (برای مدل‌های غیر Vision) ...
        /// </summary>
        private readonly bool _enableOcrFallback;

        public XImageFileContentExtractor() : this(enableOcrFallback: true)
        { }

        public XImageFileContentExtractor(bool enableOcrFallback = true)
        {
            _enableOcrFallback = enableOcrFallback;
        }

        /// <summary>
        /// Check if this extractor supports the specified MIME type ...
        /// </summary>
        public bool CanExtract(string mimeType)
        {
            //
            return SupportedMimeTypes.Contains(
                mimeType?.ToLowerInvariant() ?? string.Empty
            );
        }

        /// <summary>
        /// Extract text content from file stream ...
        /// </summary>
        public async Task<string> ExtractAsync(
            Stream fileStream,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var result = await ExtractRichAsync(
                fileStream,
                "image",
                mimeType,
                cancellationToken
            );

            //
            return result.Text;
        }

        /// <summary>
        /// Extract content from stream as Rich Result ...
        /// </summary>
        /// <param name="fileStream"></param>
        /// <param name="fileName"></param>
        /// <param name="mimeType"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<XFileExtractionResult> ExtractRichAsync(
            Stream fileStream,
            string fileName,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var result = new XFileExtractionResult
            {
                FileName = fileName,
                MimeType = mimeType
            };

            //
            using var memoryStream = new MemoryStream();
            await fileStream.CopyToAsync(memoryStream, cancellationToken);
            var imageBytes = memoryStream.ToArray();

            //
            result.Images.Add(new XExtractedImage
            {
                Bytes = imageBytes,
                MimeType = mimeType,
                Description = $"Attached image: {fileName}"
            });

            // OCR Fallback برای مدل‌های متنی
            if (_enableOcrFallback)
            {
                //
                try
                {
                    //
                    var ocrText = await PerformOcrAsync(imageBytes, cancellationToken);
                    if (!string.IsNullOrWhiteSpace(ocrText))
                    {
                        result.Text = ocrText;
                    }
                }
                catch
                {
                    // OCR failed, but image is still available for Vision
                }
            }

            //
            return result;
        }

        /// <summary>
        /// Do OCR on Image for Text Extraction ...
        /// </summary>
        private async Task<string> PerformOcrAsync(
            byte[] imageBytes,
            CancellationToken cancellationToken
        )
        {
            //
            // TODO: Complete this ...
            return await Task.FromResult(string.Empty);
        }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\Extractors\XPdfFileContentExtractor.cs

using System;
using System.IO;
using System.Linq;
using System.Text;
using PdfiumViewer;
using System.Threading;
using xAiModels.Models;
using System.Threading.Tasks;
using xAiApi.Interfaces.Extractors;

namespace xAiApi.Providers.Extractors
{
    /// <summary>
    /// Extracts content from PDF files using PdfPig ...
    /// </summary>    
    public class XPdfFileContentExtractor : IXPdfFileContentExtractor
    {
        /// <summary>
        /// Supported MIME Types ...
        /// </summary>
        private static readonly string[] SupportedMimeTypes =
        [
            "application/pdf"
        ];

        /// <summary>
        /// Minimum Length for Fallback Using Images ...
        /// </summary>
        private const int MinTextLengthThreshold = 50;

        /// <summary>
        /// Max Pages for Falling Back Using Images ...
        /// </summary>
        private const int MaxPagesForImageFallback = 10;

        /// <summary>
        /// Default DPI for Rendering Images ...
        /// </summary>
        private const int RenderDpi = 150;

        /// <summary>
        /// Max Rendered Image Width ...
        /// </summary>
        private const int MaxImageWidth = 2000;

        /// <summary>
        /// Check if this extractor supports the specified MIME type ...
        /// </summary>
        public bool CanExtract(string mimeType)
        {
            //
            return SupportedMimeTypes.Contains(
                mimeType?.ToLowerInvariant() ?? string.Empty
            );
        }

        /// <summary>
        /// Extract text content from file stream ...
        /// </summary>
        public async Task<string> ExtractAsync(
            Stream fileStream,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var result = await ExtractRichAsync(
                fileStream,
                "document.pdf",
                mimeType,
                cancellationToken
            );

            //
            return result.Text;
        }

        /// <summary>
        /// Extract content from stream as Rich Result ...
        /// </summary>
        /// <param name="fileStream"></param>
        /// <param name="fileName"></param>
        /// <param name="mimeType"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<XFileExtractionResult> ExtractRichAsync(
            Stream fileStream,
            string fileName,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var result = new XFileExtractionResult
            {
                FileName = fileName,
                MimeType = mimeType
            };

            //
            using var memoryStream = new MemoryStream();
            await fileStream.CopyToAsync(memoryStream, cancellationToken);
            memoryStream.Position = 0;

            //
            var extractedText = await ExtractTextAsync(
                memoryStream,
                cancellationToken
            );
            if (IsTextSufficient(extractedText))
            {
                // PDF متنی است
                result.Text = extractedText;
            }
            else
            {
                // PDF اسکن‌شده است - Fallback به تصویر
                memoryStream.Position = 0;
                result = await FallbackToImageExtractionAsync(
                    memoryStream,
                    fileName,
                    mimeType,
                    cancellationToken
                );
                result.UsedImageFallback = true;
            }

            //
            return result;
        }

        /// <summary>
        /// Extract Text From PDF Files using Common way ...
        /// </summary>
        /// <param name="pdfStream"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task<string> ExtractTextAsync(
            Stream pdfStream,
            CancellationToken cancellationToken
        )
        {
            //
            var result = await Task.Run(() =>
            {
                //
                var sb = new StringBuilder();
                using var document = UglyToad.PdfPig.PdfDocument.Open(pdfStream);
                foreach (var page in document.GetPages())
                {
                    //
                    var text = page.Text?.Trim();
                    if (!string.IsNullOrEmpty(text))
                    {
                        //
                        sb.AppendLine(text);
                        sb.AppendLine();
                    }
                }

                //
                return sb.ToString();
            }, cancellationToken);

            //
            return result;
        }

        /// <summary>
        /// Check Extracted Content is Sufficient or not ...
        /// if not try to Falling Back using Images ...
        /// </summary>
        private bool IsTextSufficient(string text)
        {
            //
            if (string.IsNullOrWhiteSpace(text))
            {
                return false;
            }

            //
            // حذف فاصله‌ها و بررسی طول
            var cleanText = new string(text.Where(c => !char.IsWhiteSpace(c)).ToArray());

            //
            return cleanText.Length >= MinTextLengthThreshold;
        }

        /// <summary>
        /// Fallback Converts PDF Pages to Images ...
        /// </summary>
        private async Task<XFileExtractionResult> FallbackToImageExtractionAsync(
            Stream pdfStream,
            string fileName,
            string mimeType,
            CancellationToken cancellationToken
        )
        {
            //
            var result = new XFileExtractionResult
            {
                FileName = fileName,
                MimeType = mimeType,
                UsedImageFallback = true
            };

            //
            await Task.Run(() =>
            {
                //
                try
                {
                    //
                    using var document = PdfDocument.Load(pdfStream);

                    // 
                    var pageCount = Math.Min(
                        document.PageCount,
                        MaxPagesForImageFallback
                    );

                    //                    
                    for (int pageIndex = 0; pageIndex < pageCount; pageIndex++)
                    {
                        //
                        if (cancellationToken.IsCancellationRequested)
                        {
                            break;
                        }

                        //
                        try
                        {
                            //
                            var pageSize = document.PageSizes[pageIndex];

                            //
                            var scaleFactor = RenderDpi / 72.0;
                            var renderWidth = (int)Math.Ceiling(pageSize.Width * scaleFactor);
                            var renderHeight = (int)Math.Ceiling(pageSize.Height * scaleFactor);

                            //
                            if (renderWidth > MaxImageWidth)
                            {
                                //
                                var ratio = (double)MaxImageWidth / renderWidth;
                                renderWidth = MaxImageWidth;
                                renderHeight = (int)(renderHeight * ratio);
                            }

                            //
                            using var bitmap = document.Render(
                                dpiX: RenderDpi,
                                dpiY: RenderDpi,
                                page: pageIndex,
                                width: renderWidth,
                                forPrinting: false,
                                height: renderHeight
                            );

                            //
                            byte[] imageBytes;
                            using (var memoryStream = new MemoryStream())
                            {
                                //
                                bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
                                imageBytes = memoryStream.ToArray();
                            }

                            //
                            result.Images.Add(new XExtractedImage
                            {
                                Bytes = imageBytes,
                                MimeType = "image/png",
                                PageNumber = pageIndex + 1,
                                Description = $"Page {pageIndex + 1} of {fileName} ({renderWidth}x{renderHeight})"
                            });
                        }
                        catch (Exception)
                        { }
                    }
                }
                catch (Exception ex)
                {
                    result.ErrorMessage = $"PDF image extraction failed: {ex.Message}";
                }
            }, cancellationToken);

            //
            return result;
        }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\Extractors\XPlainTextFileContentExtractor.cs

using System;
using System.IO;
using System.Linq;
using System.Threading;
using xAiModels.Models;
using System.Threading.Tasks;
using xAiApi.Interfaces.Extractors;

namespace xAiApi.Providers.Extractors
{
    /// <summary>
    /// Extracts content from plain text files (txt, md, csv, json, xml) ...
    /// </summary>    
    public class XPlainTextFileContentExtractor : IXPlainTextFileContentExtractor
    {
        /// <summary>
        /// Supported MIME Types ...
        /// </summary>
        private static readonly string[] SupportedMimeTypes =
        [
            "text/plain",
            "text/markdown",
            "text/csv",
            "text/html",
            "text/xml",
            "application/json",
            "application/xml"
        ];

        /// <summary>
        /// Check if this extractor supports the specified MIME type ...
        /// </summary>
        public bool CanExtract(string mimeType)
        {
            //
            return SupportedMimeTypes.Contains(
                mimeType?.ToLowerInvariant() ?? string.Empty
            );
        }

        /// <summary>
        /// Extract text content from file stream ...
        /// </summary>
        public async Task<string> ExtractAsync(
            Stream fileStream,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            using var reader = new StreamReader(fileStream);
            return await reader.ReadToEndAsync();
        }

        /// <summary>
        /// Extract content from stream as Rich Result ...
        /// </summary>
        /// <param name="fileStream"></param>
        /// <param name="fileName"></param>
        /// <param name="mimeType"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<XFileExtractionResult> ExtractRichAsync(
            Stream fileStream,
            string fileName,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var content = await ExtractAsync(
                mimeType: mimeType,
                fileStream: fileStream,
                cancellationToken: cancellationToken
            );

            //
            var result = new XFileExtractionResult
            {
                Text = content,
                FileName = fileName,
                MimeType = mimeType
            };

            //
            return result;
        }
    }
}

### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\xAiApi\Providers\Extractors\XVisionFileContentExtractor.cs

using System;
using System.IO;
using System.Linq;
using System.Threading;
using xAiApi.Constants;
using xAiModels.Models;
using xAiApi.Extensions;
using xAiApi.Interfaces;
using xCommons.Extensions;
using xAiApi.Configurations;
using xExceptions.Constants;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using xAiApi.Interfaces.Extractors;

namespace xAiApi.Providers.Extractors
{
    /// <summary>
    /// Extracts content from files using Vision Model ...
    /// </summary>    
    public class XVisionFileContentExtractor : IXVisionFileContentExtractor
    {
        /// <summary>
        /// Supported MIME Types ...
        /// </summary>
        private static readonly string[] SupportedMimeTypes =
        [
            "image/png",
            "image/jpeg",
            "image/jpg",
            "image/webp",
            "image/bmp",
            "application/pdf"
        ];

        //
        private readonly string prompt;
        private readonly XAiModelDescriptor descriptor;
        private readonly IXDefaultAIOCRService aIOCRService;

        public XVisionFileContentExtractor(
            XAiApiConfiguration configuration,
            IXDefaultAIOCRService aIOCRService,
            string promptName = XAiApiConstants.XAiApiContentExtractionPromptName,
            string modelName = XAiApiConstants.XAiDefaultVisionModelName
        )
        {
            // 
            // Retrieve Descriptoir ...
            if (modelName.IsNullOrEmpty())
            {
                XException.InvalidArgs.Throw();
            }
            descriptor = configuration.GetModel(modelName);

            //
            // Retrieve Extraction Prompt ...
            if (promptName.IsNullOrEmpty())
            {
                XException.InvalidArgs.Throw();
            }
            prompt = configuration.GetPrompt(
                name: promptName,
                @params: null
            );

            //
            // Validate Requirements ...
            if (prompt.IsNullOrEmpty() || descriptor.IsNullOrDefault())
            {
                XException.InvalidData.Throw();
            }

            this.aIOCRService = aIOCRService;
        }

        /// <summary>
        /// Check if this extractor supports the specified MIME type ...
        /// </summary>
        public bool CanExtract(string mimeType)
        {
            //
            return SupportedMimeTypes.Contains(
                mimeType?.ToLowerInvariant() ?? string.Empty
            );
        }

        /// <summary>
        /// Extract text content from file stream ...
        /// </summary>
        public async Task<string> ExtractAsync(
            Stream fileStream,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            var result = await ExtractRichAsync(fileStream, "file", mimeType, cancellationToken);
            return result.Text;
        }

        /// <summary>
        /// Extract content from stream as Rich Result ...
        /// </summary>
        /// <param name="fileStream"></param>
        /// <param name="fileName"></param>
        /// <param name="mimeType"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<XFileExtractionResult> ExtractRichAsync(
            Stream fileStream,
            string fileName,
            string mimeType,
            CancellationToken cancellationToken = default
        )
        {
            //
            var result = new XFileExtractionResult
            {
                FileName = fileName,
                MimeType = mimeType
            };

            //
            try
            {
                //
                using var memoryStream = new MemoryStream();
                await fileStream.CopyToAsync(memoryStream, cancellationToken);
                var imageBytes = memoryStream.ToArray();
                var messages = new[]
                {
                    //
                    new ChatMessage(ChatRole.User, new[]
                    {
                        new DataContent(imageBytes, mimeType)
                    })
                };

                //
                var extractedContent = await aIOCRService.RequestOCRAsync(
                    messages: messages,
                    cancellationToken: cancellationToken
                );
                if (!extractedContent.IsNullOrEmpty())
                {
                    result.Text = extractedContent.Trim();
                }
                else
                {
                    result.ErrorMessage = "there is not any Extracted Content ...";
                }
            }
            catch (Exception ex)
            {
                result.ErrorMessage = $"Error in Vision Extraction: {ex.Message} ...";
            }

            //
            return result;
        }
    }
}
