From 7b1f16bd180aae8a59f8be9a4746677ddb4e378f Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Sat, 15 Aug 2026 17:25:33 +0330 Subject: [PATCH] last works on AiApi: - Default Ai Service and Controller; - Default Embeding and Controller; - ... --- Constants/XAiApiConstants.cs | 4 + Controllers/DefaultEmbeddingController.cs | 31 +++ .../XAiEmbeddingServiceControllerBase.cs | 110 ++++++++++ Interfaces/IXAiEmbeddingServiceBase.cs | 41 ++++ .../IXAiEmbeddingServiceControllerBase.cs | 34 ++++ Interfaces/IXDefaultEmbeddingService.cs | 5 + Providers/XAiEmbeddingServiceBase.cs | 190 ++++++++++++++++++ Providers/XDefaultAiService.cs | 2 +- Providers/XDefaultEmbeddingService.cs | 21 ++ Startup.cs | 2 +- appsettings.Development.json | 13 ++ nuget.config | 2 +- xAiApi.csproj | 2 +- 13 files changed, 453 insertions(+), 4 deletions(-) create mode 100644 Controllers/DefaultEmbeddingController.cs create mode 100644 Controllers/XAiEmbeddingServiceControllerBase.cs create mode 100644 Interfaces/IXAiEmbeddingServiceBase.cs create mode 100644 Interfaces/IXAiEmbeddingServiceControllerBase.cs create mode 100644 Interfaces/IXDefaultEmbeddingService.cs create mode 100644 Providers/XAiEmbeddingServiceBase.cs create mode 100644 Providers/XDefaultEmbeddingService.cs diff --git a/Constants/XAiApiConstants.cs b/Constants/XAiApiConstants.cs index 419b278..4bd17fe 100644 --- a/Constants/XAiApiConstants.cs +++ b/Constants/XAiApiConstants.cs @@ -10,5 +10,9 @@ namespace xAiApi.Constants // public const string XDILogTag = "XAiApi"; public const string XAiApiIntroductionPromptName = "Introduction"; + + // + public const string XAiEmbeddingModelName = "Embed"; + public const string XAiEmbeddingPromptName = "Embedding"; } } \ No newline at end of file diff --git a/Controllers/DefaultEmbeddingController.cs b/Controllers/DefaultEmbeddingController.cs new file mode 100644 index 0000000..9754b5b --- /dev/null +++ b/Controllers/DefaultEmbeddingController.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xAiApi.Interfaces; +using xCommons.Configurations; +using xCommons.Providers; + +namespace xAiApi.Controllers +{ + [Authorize] + [Route("[controller]")] + public class DefaultEmbeddingController : XAiEmbeddingServiceControllerBase + { + public DefaultEmbeddingController( + ILogger logger, + IXDefaultEmbeddingService aiService, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + aiService, + appConfiguration, + validationProvider + ) + { } + } +} \ No newline at end of file diff --git a/Controllers/XAiEmbeddingServiceControllerBase.cs b/Controllers/XAiEmbeddingServiceControllerBase.cs new file mode 100644 index 0000000..3d8064d --- /dev/null +++ b/Controllers/XAiEmbeddingServiceControllerBase.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xAiApi.Interfaces; +using xCommons.Configurations; +using xCommons.Providers; + +namespace xAiApi.Controllers +{ + [Authorize] + [Route("[controller]")] + public abstract class XAiEmbeddingServiceControllerBase : XAiServiceControllerBase, IXAiEmbeddingServiceControllerBase + where TAiService : IXAiEmbeddingServiceBase + { + private readonly TAiService aiService; + + protected XAiEmbeddingServiceControllerBase( + ILogger logger, + TAiService aiService, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + aiService, + appConfiguration, + validationProvider + ) + { + this.aiService = aiService; + } + + /// + /// Request For Embedding Specified String ... + /// + /// + /// + /// + [AllowAnonymous] + [HttpGet("Embeding")] + public async Task> Embeding( + [FromQuery] string content, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + ValidationProvider.NotEmpty(content); + + // + var result = await aiService.Embeding( + content: content, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Request For Embedding Specified Batch ... + /// + /// + /// + /// + [AllowAnonymous] + [HttpGet("Embedings")] + public async Task>> Embedings( + [FromQuery] string[] batch, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + ValidationProvider.NotZeroChilds(batch); + + // + var result = await aiService.Embedings( + batch: batch, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + } +} \ No newline at end of file diff --git a/Interfaces/IXAiEmbeddingServiceBase.cs b/Interfaces/IXAiEmbeddingServiceBase.cs new file mode 100644 index 0000000..8ef4c54 --- /dev/null +++ b/Interfaces/IXAiEmbeddingServiceBase.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using OllamaSharp; + +namespace xAiApi.Interfaces +{ + /// + /// Base Embedding Provider Service ... + /// + public interface IXAiEmbeddingServiceBase : IXAiServiceBase + { + /// + /// Embedding Specified String ... + /// + /// + /// + /// + Task Embeding( + string content, + CancellationToken cancellationToken = default + ); + + /// + /// Embedding Specified Batch ... + /// + /// + /// + /// + Task> Embedings( + string[] batch, + CancellationToken cancellationToken = default + ); + + /// + /// Access Embedding Client ... + /// + /// + OllamaApiClient GetEmbeddingClient(); + } +} \ No newline at end of file diff --git a/Interfaces/IXAiEmbeddingServiceControllerBase.cs b/Interfaces/IXAiEmbeddingServiceControllerBase.cs new file mode 100644 index 0000000..f9e9294 --- /dev/null +++ b/Interfaces/IXAiEmbeddingServiceControllerBase.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace xAiApi.Interfaces +{ + public interface IXAiEmbeddingServiceControllerBase : IXAiServiceControllerBase + { + /// + /// Request For Embedding Specified String ... + /// + /// + /// + /// + [HttpGet("Embeding")] + Task> Embeding( + [FromQuery] string content, + CancellationToken cancellationToken = default + ); + + /// + /// Request For Embedding Specified Batch ... + /// + /// + /// + /// + [HttpGet("Embedings")] + Task>> Embedings( + [FromQuery] string[] batch, + CancellationToken cancellationToken = default + ); + } +} \ No newline at end of file diff --git a/Interfaces/IXDefaultEmbeddingService.cs b/Interfaces/IXDefaultEmbeddingService.cs new file mode 100644 index 0000000..364273e --- /dev/null +++ b/Interfaces/IXDefaultEmbeddingService.cs @@ -0,0 +1,5 @@ +namespace xAiApi.Interfaces +{ + public interface IXDefaultEmbeddingService : IXAiEmbeddingServiceBase + { } +} \ No newline at end of file diff --git a/Providers/XAiEmbeddingServiceBase.cs b/Providers/XAiEmbeddingServiceBase.cs new file mode 100644 index 0000000..e2a3969 --- /dev/null +++ b/Providers/XAiEmbeddingServiceBase.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using OllamaSharp; +using OllamaSharp.Models; +using xAiApi.Configurations; +using xAiApi.Constants; +using xAiApi.Extensions; +using xAiApi.Interfaces; +using xAiApi.Models; +using xAiModels.Interfaces; +using xAiService.Helpers; +using xCommons.Extensions; +using xExceptions.Constants; + +namespace xAiApi.Providers +{ + /// + /// Base Embedding Provider Service ... + /// + public abstract class XAiEmbeddingServiceBase : XAIServiceBase, IXAiEmbeddingServiceBase + { + private readonly XAiModelDescriptor descriptor; + + protected XAiEmbeddingServiceBase( + IXAiDataProvider dataProvider, + XAiApiConfiguration configuration, + ILogger logger, + string embeddingModelName = XAiApiConstants.XAiEmbeddingModelName + ) : base( + dataProvider, + logger, + configuration + ) + { + // + // Prepare Embedding Model Descriptor ... + descriptor = configuration.GetModel(embeddingModelName); + if (!descriptor.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + } + + /// + /// Embedding Specified String ... + /// + /// + /// + /// + public virtual async Task Embeding( + string content, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (content.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Ollama Client ... + using var client = GetEmbeddingClient(); + + // + // Create Embeding Request .... + var request = new EmbedRequest + { + Input = [content], + Model = descriptor.LLM, + }; + + // + // Make Request and Recieve Response ... + var response = await client.EmbedAsync( + request: request, + cancellationToken: cancellationToken + ); + if (response.IsNullOrDefault()) + { + XException.ActionFailed.Throw(); + } + + // + // Generae Result ... + var result = response? + .Embeddings? + .FirstOrDefault() + ?? []; + if (result.Length == 0) + { + XException.ActionFailed.Throw(); + } + + // + // Normalize ... + VectorHelper.Normalize(result); + + // + return result; + } + + /// + /// Embedding Specified Batch ... + /// + /// + /// + /// + public virtual async Task> Embedings( + string[] batch, + CancellationToken cancellationToken = default + + ) + { + // + // Validate ... + if (!batch.HasChild() || + !batch.All(x => !x.IsNullOrEmpty()) + ) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Ollama Client ... + using var client = GetEmbeddingClient(); + + // + // Create Embeding Request .... + var request = new EmbedRequest + { + Input = [.. batch], + Model = descriptor.LLM, + }; + + // + // Make Request and Recieve Response ... + var response = await client.EmbedAsync( + request: request, + cancellationToken: cancellationToken + ); + if (response.IsNullOrDefault()) + { + XException.ActionFailed.Throw(); + } + + // + // Generae Result ... + var result = response? + .Embeddings + .ToArray() + ?? []; + + // + // Validate Result ... + if (result.Length == 0) + { + XException.ActionFailed.Throw(); + } + + // + // Normalize ... + foreach (var vector in result) + { + VectorHelper.Normalize(vector); + } + + // + return result; + } + + /// + /// Access Embedding Client ... + /// + /// + public OllamaApiClient GetEmbeddingClient() + { + // + var result = new OllamaApiClient(new Uri(descriptor.Url)); + + // + return result; + } + } +} \ No newline at end of file diff --git a/Providers/XDefaultAiService.cs b/Providers/XDefaultAiService.cs index 7adf00c..ad086cc 100644 --- a/Providers/XDefaultAiService.cs +++ b/Providers/XDefaultAiService.cs @@ -9,7 +9,7 @@ namespace xAiApi.Providers { public XDefaultAiService( IXAiDataProvider dataProvider, - ILogger logger, + ILogger logger, XAiApiConfiguration configuration ) : base( dataProvider, diff --git a/Providers/XDefaultEmbeddingService.cs b/Providers/XDefaultEmbeddingService.cs new file mode 100644 index 0000000..66e609a --- /dev/null +++ b/Providers/XDefaultEmbeddingService.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Logging; +using xAiApi.Configurations; +using xAiApi.Interfaces; +using xAiModels.Interfaces; + +namespace xAiApi.Providers +{ + public class XDefaultEmbeddingService : XAiEmbeddingServiceBase, IXDefaultEmbeddingService + { + public XDefaultEmbeddingService( + IXAiDataProvider dataProvider, + XAiApiConfiguration configuration, + ILogger logger + ) : base( + dataProvider, + configuration, + logger + ) + { } + } +} \ No newline at end of file diff --git a/Startup.cs b/Startup.cs index c154949..e58b133 100644 --- a/Startup.cs +++ b/Startup.cs @@ -27,7 +27,6 @@ using Microsoft.Extensions.DependencyInjection; using IdentityModel.AspNetCore.OAuth2Introspection; using xPushService.Helpers; using xAiApi.DI; -using xExceptions.Constants; using xAiApi.Interfaces; using xAiApi.Providers; using xAiModels.DI; @@ -189,6 +188,7 @@ namespace xAiApi // // Register Default Ai Service ... services.AddScoped(); + services.AddScoped(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/appsettings.Development.json b/appsettings.Development.json index 986b937..5032b0c 100644 --- a/appsettings.Development.json +++ b/appsettings.Development.json @@ -43,6 +43,13 @@ "LLM": "gemma3:1b", "Provider": "Ollama", "Url": "http://localhost:11434" + }, + { + "ApiKey": "", + "Name": "Embed", + "LLM": "bge-m3", + "Provider": "Ollama", + "Url": "http://localhost:11434" } ], "Prompts": [ @@ -51,6 +58,12 @@ "Template": "your name is xSaherElmAi, you are a professional ai assistant which develped by SaherElm IT Center", "Description": "", "Params": [] + }, + { + "Name": "Embedding", + "Template": "your name is xSaherElmAi, you are a professional ai assistant which develped by SaherElm IT Center specified for Embedding Documents", + "Description": "", + "Params": [] } ] }, diff --git a/nuget.config b/nuget.config index 632defe..27310a9 100644 --- a/nuget.config +++ b/nuget.config @@ -2,6 +2,6 @@ - + \ No newline at end of file diff --git a/xAiApi.csproj b/xAiApi.csproj index 42f3a9d..42a8999 100644 --- a/xAiApi.csproj +++ b/xAiApi.csproj @@ -35,7 +35,7 @@ - +