using System; using OpenAI; using OllamaSharp; using System.Net.Http; using System.Threading; using xAiApi.Constants; using xAiModels.Models; using xAiApi.Interfaces; using xAiApi.Extensions; using System.ClientModel; using xCommons.Extensions; using xAiModels.Constants; using xAiModels.Interfaces; 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 abstract class XAIServiceBase : IXAiServiceBase { /// /// Chat Options ... /// public ChatOptions Options { get; } /// /// Descriptor of Models which used in Service ... /// public XAiModelDescriptor Descriptor { get; } private readonly IXAiDataProvider dataProvider; private readonly ILogger logger; private readonly XAiApiConfiguration configuration; protected XAIServiceBase( IXAiDataProvider dataProvider, ILogger logger, XAiApiConfiguration configuration, string model = null ) { this.logger = logger; this.dataProvider = dataProvider; this.configuration = configuration; // // 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 ... /// /// Base Method for Communicate with LLM ... /// /// /// /// public virtual async Task 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; } /// /// Base Method for Communicate with LLM ... /// /// /// /// public virtual async IAsyncEnumerable 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; } } /// /// Get LLM Client instance for Communicating with LLM ... /// /// 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; } /// /// Create Custom HttpClient for Communicating with LLM API ... /// /// /// public virtual HttpClient GetHttpClient(string url = null) { // var result = new HttpClient { // // Disable timeout completely (not recommended for production) Timeout = Timeout.InfiniteTimeSpan, // BaseAddress = url.IsNullOrEmpty() ? null : new Uri(url), // // OR set a very long timeout (e.g., 30 minutes for large file uploads) // Timeout = TimeSpan.FromMinutes(30) }; // return result; } /// /// Prepare a Message History List for LLM Communication based on Prompt ... /// /// /// /// public virtual IList GetHistory( string prompt = null, bool forcePrompt = true ) { // // Validate Args ... if (prompt.IsNullOrEmpty()) { XException.InvalidArgs.Throw(); } // // User Prompt ... var promptMessage = new ChatMessage( ChatRole.User, prompt ); // // Preparing Introduction Prompt ... var introductionPrompt = new ChatMessage( ChatRole.System, configuration.GetPrompt() ); // List result = [ introductionPrompt, ]; if (forcePrompt && prompt is not null && !string.IsNullOrWhiteSpace(prompt)) { result.Add(promptMessage); } // return result; } /// /// Prepare a Message History List for LLM Communication based on Prompt ... /// /// /// /// public virtual IList GetHistory( ChatMessage message = null, bool forceMessage = true ) { // // Preparing Introduction Prompt ... var introductionPrompt = new ChatMessage( ChatRole.System, configuration.GetPrompt(XAiApiConstants.XAiApiIntroductionPromptName) ); // List result = [ introductionPrompt, ]; if (forceMessage && !message.IsNullOrDefault()) { result.Add(message); } // return result; } #endregion /// /// Base Dispose Action ... /// /// public virtual void Dispose() { } } }