last ...
This commit is contained in:
+155
-126
@@ -107,7 +107,7 @@ namespace xAiApi.Providers
|
||||
}
|
||||
|
||||
//
|
||||
using var client = GetClient();
|
||||
using var client = Descriptor.GetClient();
|
||||
var history = GetHistory(prompt);
|
||||
var response = await client.GetResponseAsync(
|
||||
messages: history,
|
||||
@@ -152,7 +152,7 @@ namespace xAiApi.Providers
|
||||
}
|
||||
|
||||
//
|
||||
using var client = GetClient();
|
||||
using var client = Descriptor.GetClient();
|
||||
var history = GetHistory(prompt);
|
||||
var enumerable = client.GetStreamingResponseAsync(
|
||||
options: null,
|
||||
@@ -174,106 +174,6 @@ namespace xAiApi.Providers
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
@@ -366,7 +266,7 @@ namespace xAiApi.Providers
|
||||
/// <param name="includeDefaults"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<XAiProjectDto>> LoadProjects(
|
||||
public virtual async Task<IEnumerable<XAiProjectDto>> LoadProjects(
|
||||
string ownerId,
|
||||
string connectionId = null,
|
||||
bool includeDefaults = false,
|
||||
@@ -439,7 +339,7 @@ namespace xAiApi.Providers
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<XAiConversationDto>> LoadConversations(
|
||||
public virtual async Task<IEnumerable<XAiConversationDto>> LoadConversations(
|
||||
Guid projectId,
|
||||
string connectionId = null,
|
||||
CancellationToken cancellationToken = default
|
||||
@@ -470,7 +370,7 @@ namespace xAiApi.Providers
|
||||
/// <param name="conversationId"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<XAiMessageDto>> LoadMessages(
|
||||
public virtual async Task<IEnumerable<XAiMessageDto>> LoadMessages(
|
||||
Guid conversationId,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
@@ -502,7 +402,7 @@ namespace xAiApi.Providers
|
||||
/// <param name="query"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<XQueryResult<XAiProjectDto>> QueryProjects(
|
||||
public virtual async Task<XQueryResult<XAiProjectDto>> QueryProjects(
|
||||
string ownerId,
|
||||
XQuery query,
|
||||
CancellationToken cancellationToken = default
|
||||
@@ -534,7 +434,7 @@ namespace xAiApi.Providers
|
||||
/// <param name="query"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<XQueryResult<XAiConversationDto>> QueryConversations(
|
||||
public virtual async Task<XQueryResult<XAiConversationDto>> QueryConversations(
|
||||
string ownerId,
|
||||
Guid projectId,
|
||||
XQuery query,
|
||||
@@ -589,7 +489,7 @@ namespace xAiApi.Providers
|
||||
/// <param name="query"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<XQueryResult<XAiMessageDto>> QueryMessages(
|
||||
public virtual async Task<XQueryResult<XAiMessageDto>> QueryMessages(
|
||||
string ownerId,
|
||||
Guid conversationId,
|
||||
XQuery query,
|
||||
@@ -646,7 +546,7 @@ namespace xAiApi.Providers
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<XAiProjectDto> CreateProject(
|
||||
public virtual async Task<XAiProjectDto> CreateProject(
|
||||
string ownerId,
|
||||
XAiProjectDto item,
|
||||
string connectionId = null,
|
||||
@@ -674,7 +574,7 @@ namespace xAiApi.Providers
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<XAiConversationDto> CreateConversation(
|
||||
public virtual async Task<XAiConversationDto> CreateConversation(
|
||||
string ownerId,
|
||||
Guid projectId,
|
||||
XAiConversationDto item,
|
||||
@@ -696,6 +596,91 @@ namespace xAiApi.Providers
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Content Extraction ...
|
||||
/// <summary>
|
||||
/// Try to Extract Files Content ...
|
||||
/// </summary>
|
||||
/// <param name="files"></param>
|
||||
/// <param name="extractors"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> ExtractContent(
|
||||
IFormFileCollection files,
|
||||
string[] extractors = null,
|
||||
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, extractors))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
try
|
||||
{
|
||||
//
|
||||
using var stream = file.OpenReadStream();
|
||||
|
||||
//
|
||||
var fr = await fileContentExtractor.ExtractRichAsync(
|
||||
fileStream: stream,
|
||||
mimeType: mimeType,
|
||||
extractors: extractors,
|
||||
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>
|
||||
@@ -710,7 +695,7 @@ namespace xAiApi.Providers
|
||||
/// <param name="userInfo"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<XAiMessageDto> AskAsync(
|
||||
public virtual async Task<XAiMessageDto> AskAsync(
|
||||
string prompt,
|
||||
string ownerId,
|
||||
Guid projectId,
|
||||
@@ -861,25 +846,69 @@ namespace xAiApi.Providers
|
||||
|
||||
//
|
||||
// Since Prompt Message Has Id now, we can Attach Uploaded Files to it ...
|
||||
IList<AIContent> fileContents = null;
|
||||
IList<AIContent> fileContents = [];
|
||||
if (uploadedFiles.HasChild())
|
||||
{
|
||||
//
|
||||
foreach (var uf in uploadedFiles)
|
||||
{
|
||||
//
|
||||
await fileProvider.AddReference(
|
||||
id: uf.Id,
|
||||
userInfo: userInfo,
|
||||
connectionId: connectionId,
|
||||
forProvidedId: promptMessage.Id,
|
||||
cancellationToken: cancellationToken,
|
||||
providedFor: XAiServiceConstants.XAiMessageFilesIdentifier
|
||||
);
|
||||
}
|
||||
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
|
||||
);
|
||||
|
||||
//
|
||||
// TODO: Extract File Contents ...
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -928,7 +957,7 @@ namespace xAiApi.Providers
|
||||
/// <param name="files"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async IAsyncEnumerable<XAiResonseMessage> AskAsEnumerable(
|
||||
public virtual async IAsyncEnumerable<XAiResonseMessage> AskAsEnumerable(
|
||||
string prompt,
|
||||
string ownerId,
|
||||
Guid projectId,
|
||||
@@ -1055,7 +1084,7 @@ namespace xAiApi.Providers
|
||||
|
||||
//
|
||||
// Ask Questions From LLM ...
|
||||
using var client = GetClient();
|
||||
using var client = Descriptor.GetClient();
|
||||
var enumerable = client.GetStreamingResponseAsync(
|
||||
options: null,
|
||||
messages: history
|
||||
@@ -1202,7 +1231,7 @@ namespace xAiApi.Providers
|
||||
|
||||
//
|
||||
history.Add(prompt);
|
||||
using var client = GetClient();
|
||||
using var client = Descriptor.GetClient();
|
||||
var response = await client.GetResponseAsync(
|
||||
messages: history,
|
||||
cancellationToken: cancellationToken
|
||||
|
||||
Reference in New Issue
Block a user