Files
xSaherelmWorkspace/Documents/GeneratedContext/xDashboard-xAiApi-20261003_145153.txt
T
2026-10-03 18:04:36 +03:30

6456 lines
199 KiB
Plaintext

### 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, XPdfFileContentExtractor>();
services.AddSingleton<IXFileContentExtractor, XDocxFileContentExtractor>();
services.AddSingleton<IXFileContentExtractor, XImageFileContentExtractor>();
services.AddSingleton<IXFileContentExtractor, XAudioFileContentExtractor>();
services.AddSingleton<IXFileContentExtractor, XExcelFileContentExtractor>();
services.AddSingleton<IXFileContentExtractor, XVisionFileContentExtractor>();
services.AddSingleton<IXFileContentExtractor, XPlainTextFileContentExtractor>();
//
services.AddScoped<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 Microsoft.AspNetCore.Http;
using System.Collections.Generic;
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;
using xAiModels.Models;
using System.Net.Http;
namespace xAiApi.Interfaces
{
public interface IXDefaultAIOCRService
{
/// <summary>
/// Chat Options ...
/// </summary>
ChatOptions Options { get; }
/// <summary>
/// Prompt ...
/// </summary>
string Prompt { get; }
/// <summary>
/// Descriptor of Models which used in Service ...
/// </summary>
XAiModelDescriptor Descriptor { get; }
/// <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
);
/// <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);
}
}
### 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;
using OpenAI;
using OllamaSharp;
using System.Net.Http;
using System.Threading;
using xAiModels.Models;
using xAiApi.Constants;
using xAiApi.Interfaces;
using xAiApi.Extensions;
using System.ClientModel;
using xCommons.Extensions;
using xAiModels.Constants;
using xAiModels.Extensions;
using xAiApi.Configurations;
using xExceptions.Constants;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using System.ClientModel.Primitives;
using System.Runtime.CompilerServices;
namespace xAiApi.Providers
{
public class XDefaultAIOCRService : IXDefaultAIOCRService
{
private readonly ILogger<XDefaultAIOCRService> logger;
/// <summary>
/// Chat Options ...
/// </summary>
public ChatOptions Options { get; }
/// <summary>
/// Prompt ...
/// </summary>
public string Prompt { get; }
/// <summary>
/// Descriptor of Models which used in Service ...
/// </summary>
public XAiModelDescriptor Descriptor { get; }
public XDefaultAIOCRService(
XAiApiConfiguration configuration,
ILogger<XDefaultAIOCRService> logger,
string model = null,
string prompt = null
)
{
//
// Prepare Embedding Model Descriptor ...
Descriptor = configuration.GetModel(model);
if (!Descriptor.IsValid())
{
XException.InvalidConfiguration.Throw();
}
this.logger = logger;
//
Prompt = prompt;
if (Prompt.IsNullOrEmpty())
{
XException.InvalidArgs.Throw();
}
//
// Prepare Chat Options based On Tools ...
Options = new ChatOptions();
}
//
#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
//
#region Preaprations ...
/// <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;
}
#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(
IEnumerable<IXFileContentExtractor> availableExtractors
)
{
//
extractors = [.. availableExtractors
.Where(e => e.GetType() != typeof(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;
}
}
}