diff --git a/AI/Configuration/XAiApiConfiguration.cs b/AI/Configuration/XAiApiConfiguration.cs
new file mode 100644
index 0000000..22d70ae
--- /dev/null
+++ b/AI/Configuration/XAiApiConfiguration.cs
@@ -0,0 +1,29 @@
+namespace xAiApi.AI.Configuration
+{
+ public class XAiApiConfiguration
+ {
+ ///
+ /// AI Provider Client URL ...
+ ///
+ ///
+ public string Url { get; set; }
+
+ ///
+ /// Text Generator Model Name ...
+ ///
+ ///
+ public string TextModel { get; set; }
+
+ ///
+ /// Code Generator Model Name ...
+ ///
+ ///
+ public string CodeModel { get; set; }
+
+ ///
+ /// Image Generator Model Name ...
+ ///
+ ///
+ public string ImageModel { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AI/Constants/ConfigurationNodeNames.cs b/AI/Constants/ConfigurationNodeNames.cs
new file mode 100644
index 0000000..d7bbf91
--- /dev/null
+++ b/AI/Constants/ConfigurationNodeNames.cs
@@ -0,0 +1,7 @@
+namespace xAiApi.AI.Constants
+{
+ public partial struct ConfigurationNodeNames
+ {
+ public const string AI_API_SERVICE_NODE = "AiApiServiceConfiguration";
+ }
+}
\ No newline at end of file
diff --git a/AI/DI/AIExtensions.cs b/AI/DI/AIExtensions.cs
index 7ab2e5e..95b3e62 100644
--- a/AI/DI/AIExtensions.cs
+++ b/AI/DI/AIExtensions.cs
@@ -11,9 +11,10 @@ namespace xAiApi.AI.DI
/// Register AI Services ...
///
///
- public static void AddXAIServices(this IServiceCollection services)
+ public static void AddXAIServices(
+ this IServiceCollection services
+ )
{
- //
services.AddSingleton();
}
@@ -23,5 +24,9 @@ namespace xAiApi.AI.DI
///
public static void UseXAIServices(this IApplicationBuilder builder)
{ }
+
+ //
+ #region Private ...
+ #endregion
}
}
\ No newline at end of file
diff --git a/AI/Extensions/XAiApiConfigurationExtension.cs b/AI/Extensions/XAiApiConfigurationExtension.cs
new file mode 100644
index 0000000..d9da12f
--- /dev/null
+++ b/AI/Extensions/XAiApiConfigurationExtension.cs
@@ -0,0 +1,70 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using xAiApi.AI.Configuration;
+using xAiApi.AI.Constants;
+using xCommons.Extensions;
+
+namespace xAiApi.AI.Extensions
+{
+ public static class XAiApiConfigurationExtension
+ {
+ ///
+ /// Extract Configuration ...
+ ///
+ ///
+ ///
+ public static XAiApiConfiguration GetXAiApiConfiguration(this IConfiguration source)
+ {
+ //
+ var section = source.GetSection(ConfigurationNodeNames.AI_API_SERVICE_NODE);
+ var result = section.Get();
+
+ //
+ return result;
+ }
+
+ ///
+ /// Add XAiApiConfiguration ...
+ ///
+ ///
+ ///
+ public static void AddXAiApiConfiguration(
+ this IServiceCollection source,
+ XAiApiConfiguration configuration
+ )
+ {
+ //
+ if (!configuration.IsValid())
+ {
+ return;
+ }
+
+ //
+ source.AddSingleton(configuration);
+ }
+
+ ///
+ /// Validate XAiApiConfiguration ...
+ ///
+ ///
+ ///
+ public static bool IsValid(this XAiApiConfiguration configuration)
+ {
+ //
+ var result =
+ !configuration.IsNull() &&
+ !configuration.Url.IsNullOrEmpty() &&
+ configuration.Url.IsValidUrl() &&
+ !configuration.TextModel.IsNullOrEmpty();
+
+ //
+ if (!result)
+ {
+ return result;
+ }
+
+ //
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/AI/Interfaces/IXAIService.cs b/AI/Interfaces/IXAIService.cs
index cbade33..0e37b82 100644
--- a/AI/Interfaces/IXAIService.cs
+++ b/AI/Interfaces/IXAIService.cs
@@ -1,28 +1,84 @@
+using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
+using Microsoft.SemanticKernel.ChatCompletion;
+using xAiApi.AI.Configuration;
namespace xAiApi.AI.Interfaces
{
public interface IXAIService
{
+ ///
+ /// Provides Options for Chat ...
+ ///
+ ///
+ ChatOptions ChatOptions { get; }
+
+ ///
+ /// Provides History for Chat ...
+ ///
+ ///
+ ChatHistory ChatHistory { get; }
+
+ ///
+ /// a Client for Text Chat ...
+ ///
+ ///
IChatClient TextGenreatorClient { get; }
+
+ ///
+ /// a Client for Code Chat ...
+ ///
+ ///
+ IChatClient CodeGenreatorClient { get; }
+
+ ///
+ /// a Client for Image Chats ...
+ ///
+ ///
IChatClient ImageGenreatorClient { get; }
+ ///
+ /// Configuration ...
+ ///
+ ///
+ XAiApiConfiguration Configuration { get; }
+
//
#region Actions ...
///
/// Generated Text ...
///
///
+ ///
///
- Task GetTextResponseAsync(string prompt);
+ Task GetTextResponseAsync(
+ string prompt,
+ CancellationToken cancellationToken = default
+ );
///
/// Generated Response ...
///
///
+ ///
///
- Task GetResponseAsync(string prompt);
+ Task GetResponseAsync(
+ string prompt,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Generate Text Response Stream ...
+ ///
+ ///
+ ///
+ ///
+ IAsyncEnumerable GetTextReponseStreamAsync(
+ string prompt,
+ CancellationToken cancellationToken = default
+ );
#endregion
}
}
\ No newline at end of file
diff --git a/AI/Services/XAIService.cs b/AI/Services/XAIService.cs
index 1e4a593..42fe3db 100644
--- a/AI/Services/XAIService.cs
+++ b/AI/Services/XAIService.cs
@@ -1,37 +1,140 @@
using System;
+using OllamaSharp;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
-using OllamaSharp;
using xAiApi.AI.Interfaces;
using xCommons.Extensions;
using xExceptions.Constants;
+using xAiApi.AI.Configuration;
+using System.Collections.Generic;
+using Microsoft.SemanticKernel.ChatCompletion;
+using System.Threading;
namespace xAiApi.AI.Services
{
public class XAIService : IXAIService
{
- //
+ ///
+ /// Provides Options for Chat ...
+ ///
+ ///
+ public ChatOptions ChatOptions { get; }
+
+ ///
+ /// Provides History for Chat ...
+ ///
+ ///
+ public ChatHistory ChatHistory { get; }
+
+ ///
+ /// a Client for Text Chat ...
+ ///
+ ///
public IChatClient TextGenreatorClient { get; }
+
+ ///
+ /// a Client for Code Chat ...
+ ///
+ ///
+ public IChatClient CodeGenreatorClient { get; }
+
+ ///
+ /// a Client for Image Chats ...
+ ///
+ ///
public IChatClient ImageGenreatorClient { get; }
- public XAIService()
+ ///
+ /// Configuration ...
+ ///
+ ///
+ public XAiApiConfiguration Configuration { get; }
+
+ ///
+ /// Constructing Service ...
+ ///
+ /// configuration ...
+ public XAIService(
+ XAiApiConfiguration configuration
+ )
{
+ //
+ Configuration = configuration;
+
//
// here we are Initialize Text Generator Model ...
- TextGenreatorClient = new OllamaApiClient(
- new Uri("http://localhost:11434"),
- "gemma3:1b"
- );
+ TextGenreatorClient = new ChatClientBuilder(new OllamaApiClient(
+ new Uri(configuration.Url),
+ configuration.TextModel
+ ))
+ // .UseFunctionInvocation()
+ .Build();
+
+ //
+ // Code Generator Client ...
+ if (!configuration.CodeModel.IsNullOrEmpty())
+ {
+ //
+ CodeGenreatorClient = new ChatClientBuilder(new OllamaApiClient(
+ new Uri(configuration.Url),
+ configuration.CodeModel
+ ))
+ // .UseFunctionInvocation()
+ .Build();
+ }
+
+ //
+ // Image Generator Client ...
+ if (!configuration.ImageModel.IsNullOrEmpty())
+ {
+ //
+ ImageGenreatorClient = new ChatClientBuilder(new OllamaApiClient(
+ new Uri(configuration.Url),
+ configuration.ImageModel
+ ))
+ // .UseFunctionInvocation()
+ .Build();
+ }
+
+ //
+ // Configuring Chant Options ...
+ ChatOptions = new ChatOptions()
+ {
+ //
+ // Preparing Tools ...
+ // Tools = [
+ // //
+ // // Tempreature Tool ...
+ // AIFunctionFactory.Create((string location, string unit) => {
+ // //
+ // var temp = Random.Shared.Next(5, 20);
+ // var cond = Random.Shared.Next(0, 1) == 0 ? "sunny" : "rainy";
+
+ // //
+ // var result = $"The weather is {temp} degrees C and {cond}.";
+
+ // //
+ // return result;
+ // },
+ // "get_current_weather",
+ // "Get the current weather in given location"
+ // )
+ // ],
+ };
}
//
- #region Actions ...
+ #region Text Actions ...
///
/// Generated Response ...
///
///
+ ///
///
- public async Task GetResponseAsync(string prompt)
+ public async Task GetResponseAsync(
+ string prompt,
+ CancellationToken cancellationToken = default
+ )
{
//
// Validate ...
@@ -41,10 +144,21 @@ namespace xAiApi.AI.Services
XException.InvalidArgs.Throw();
}
+ //
+ // Preparing Prompt ...
+ prompt = await PreparePrompt(
+ prompt: prompt,
+ cancellationToken: cancellationToken
+ );
+
//
// Generate Response ...
var result = await TextGenreatorClient
- .GetResponseAsync(prompt);
+ .GetResponseAsync(
+ chatMessage: prompt,
+ options: ChatOptions,
+ cancellationToken: cancellationToken
+ );
//
return result;
@@ -54,13 +168,88 @@ namespace xAiApi.AI.Services
/// Generated Text ...
///
///
+ ///
///
- public async Task GetTextResponseAsync(string prompt)
+ public async Task GetTextResponseAsync(
+ string prompt,
+ CancellationToken cancellationToken = default
+ )
{
//
- var response = await GetResponseAsync(prompt);
+ var response = await GetResponseAsync(
+ prompt: prompt,
+ cancellationToken: cancellationToken
+ );
+
+ //
return response.Text;
}
+
+ ///
+ /// Generate Text Response Stream ...
+ ///
+ ///
+ ///
+ ///
+ public IAsyncEnumerable GetTextReponseStreamAsync(
+ string prompt,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ // Validate ...
+ var isValid = !prompt.IsNullOrEmpty();
+ if (!isValid)
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Preparing Prompt ...
+ prompt = PreparePrompt(
+ prompt: prompt,
+ cancellationToken: cancellationToken
+ )
+ .RunTask();
+
+ //
+ // Generate Response ...
+ var result = TextGenreatorClient
+ .GetStreamingResponseAsync(
+ chatMessage: prompt,
+ options: ChatOptions,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ return result;
+ }
+ #endregion
+
+ //
+ #region Code Actions ...
+ #endregion
+
+ //
+ #region Image Actions ...
+ #endregion
+
+ //
+ #region Private ...
+ ///
+ /// Preparing Prompt for AI ...
+ ///
+ ///
+ ///
+ ///
+ private async Task PreparePrompt(
+ string prompt,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ return await Task.FromResult(prompt);
+ }
#endregion
}
}
\ No newline at end of file
diff --git a/Controllers/V1/AIController.cs b/Controllers/V1/AIController.cs
index e666888..fd0bc21 100644
--- a/Controllers/V1/AIController.cs
+++ b/Controllers/V1/AIController.cs
@@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging;
using xAiApi.AI.Interfaces;
using xAiApi.Base;
using xCommons.Configurations;
+using xCommons.Extensions;
using xCommons.Providers;
using xIdentityService.Interfaces;
@@ -57,6 +58,37 @@ namespace xAiApi.Controllers.V1
return result;
}
}
+
+ [AllowAnonymous]
+ [HttpGet("AskAIStream")]
+ public async Task AskAIStream(
+ [FromQuery] string prompt
+ )
+ {
+ //
+ // Do ...
+ try
+ {
+ //
+ var stream = aiService
+ .GetTextReponseStreamAsync(prompt);
+ await foreach (var message in stream)
+ {
+ //
+ // Do What we Have to Do with Message ...
+
+ //
+ var msgBytes = message.Text.ToBytes();
+ await Response.Body.WriteAsync(msgBytes, 0, msgBytes.Length);
+ await Response.Body.FlushAsync();
+ }
+
+ //
+ await Response.Body.FlushAsync();
+ }
+ catch
+ { }
+ }
#endregion
}
}
\ No newline at end of file
diff --git a/DI/.gitkeep b/DI/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/DI/XPushDIExtensions.cs b/DI/XPushDIExtensions.cs
deleted file mode 100644
index 38c1175..0000000
--- a/DI/XPushDIExtensions.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace xAiApi.DI
-{
- public static class XPushDIExtensions
- {
- ///
- /// Register XPushProvider Services
- ///
- ///
- public static void AddXPushHubProvider(
- this IServiceCollection services,
- ServiceLifetime lifeTime = ServiceLifetime.Transient
- )
- {
- AddPushHubProvider(services, lifeTime);
- }
-
- //
- #region Private ...
- private static void AddPushHubProvider(
- this IServiceCollection services,
- ServiceLifetime lifeTime = ServiceLifetime.Transient
- )
- { }
- #endregion
- }
-}
\ No newline at end of file
diff --git a/Startup.cs b/Startup.cs
index 4b46c20..13ac009 100644
--- a/Startup.cs
+++ b/Startup.cs
@@ -23,11 +23,11 @@ using xDataService.DI;
using xDataService.Interfaces;
using xPushService.Helpers;
using xAiApi.Extensions;
-using xAiApi.DI;
using xAiApi.Data.Helpers;
using xAiApi.Data;
using xAiApi.Data.Seeder;
using xAiApi.AI.DI;
+using xAiApi.AI.Extensions;
// using xApi.Extensions;
// using xDataHelper;
// using xDataHelper.DbSeeder;
@@ -122,7 +122,7 @@ namespace xAiApi
//
// Register XPushService ...
services.AddXPushService(Configuration);
- services.AddXPushHubProvider(lifeTime);
+ // services.AddXPushHubProvider(lifeTime);
//
// Register XIdentityService ...
@@ -203,6 +203,12 @@ namespace xAiApi
});
#endregion
+ //
+ // Registering Configurations ...
+ var xAiApiConfiguration = Configuration
+ .GetXAiApiConfiguration();
+ services.AddXAiApiConfiguration(xAiApiConfiguration);
+
//
// Register AI Service ...
services.AddXAIServices();
diff --git a/appsettings.Development.json b/appsettings.Development.json
index 9af7537..03590fd 100644
--- a/appsettings.Development.json
+++ b/appsettings.Development.json
@@ -7,6 +7,10 @@
"http://api.saherelmhub.ir",
"https://api.saherelmhub.ir"
],
+ "AiApiServiceConfiguration": {
+ "Url":"http://localhost:11434",
+ "TextModel": "gemma3:1b"
+ },
"IdentityServiceConfiguration": {
"Authority": "https://localhost:4001",
"ApiName": "xSaherElmAPI",