diff --git a/Controllers/XAiServiceControllerBase.cs b/Controllers/XAiServiceControllerBase.cs
index 9eebff1..0b03124 100644
--- a/Controllers/XAiServiceControllerBase.cs
+++ b/Controllers/XAiServiceControllerBase.cs
@@ -652,6 +652,56 @@ namespace xAiApi.Controllers
await Response.Body.FlushAsync();
}
}
+
+ ///
+ /// Ask Question with Files and Get Response ...
+ ///
+ ///
+ ///
+ ///
+ [HttpPost("AskWithFiles")]
+ [Consumes("multipart/form-data")]
+ public async Task> 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
}
}
\ No newline at end of file
diff --git a/Interfaces/IXAiServiceBase.cs b/Interfaces/IXAiServiceBase.cs
index 582b6aa..69009ff 100644
--- a/Interfaces/IXAiServiceBase.cs
+++ b/Interfaces/IXAiServiceBase.cs
@@ -5,6 +5,7 @@ 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;
@@ -87,7 +88,7 @@ namespace xAiApi.Interfaces
#endregion
//
- #region Data Provider Based Actions ...
+ #region Loaders ...
///
/// Load AI Projects ...
///
@@ -126,7 +127,10 @@ namespace xAiApi.Interfaces
Guid conversationId,
CancellationToken cancellationToken = default
);
-
+ #endregion
+
+ //
+ #region Queries ...
///
/// Query Projects ...
///
@@ -139,7 +143,7 @@ namespace xAiApi.Interfaces
XQuery query,
CancellationToken cancellationToken = default
);
-
+
///
/// Query Conversations ...
///
@@ -154,7 +158,7 @@ namespace xAiApi.Interfaces
XQuery query,
CancellationToken cancellationToken = default
);
-
+
///
/// Query Messages ...
///
@@ -169,7 +173,10 @@ namespace xAiApi.Interfaces
XQuery query,
CancellationToken cancellationToken = default
);
+ #endregion
+ //
+ #region Creators ...
///
/// Create an AI Project ...
///
@@ -201,7 +208,10 @@ namespace xAiApi.Interfaces
string connectionId = null,
CancellationToken cancellationToken = default
);
+ #endregion
+ //
+ #region Data Actions ...
///
/// Ask a Question as Prompt and Retrieve Response ...
///
@@ -211,6 +221,7 @@ namespace xAiApi.Interfaces
///
///
///
+ ///
///
///
Task AskAsync(
@@ -220,6 +231,7 @@ namespace xAiApi.Interfaces
Guid conversationId,
string connectionId = null,
IFormFileCollection files = null,
+ XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
);
diff --git a/Interfaces/IXAiServiceControllerBase.cs b/Interfaces/IXAiServiceControllerBase.cs
index e073f63..3dc95e4 100644
--- a/Interfaces/IXAiServiceControllerBase.cs
+++ b/Interfaces/IXAiServiceControllerBase.cs
@@ -6,6 +6,7 @@ using xAiModels.Models.Dtos;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
+using Microsoft.AspNetCore.Http;
namespace xAiApi.Interfaces
{
@@ -176,6 +177,20 @@ namespace xAiApi.Interfaces
[FromBody] XAiResponseRequest request,
CancellationToken cancellationToken = default
);
+
+ ///
+ /// Ask Question with Files and Get Response ...
+ ///
+ ///
+ ///
+ ///
+ [HttpPost("AskWithFiles")]
+ [Consumes("multipart/form-data")]
+ Task> AskWithFiles(
+ [FromForm] XAiResponseRequest request,
+ [FromForm] IFormFileCollection files,
+ CancellationToken cancellationToken = default
+ );
#endregion
}
}
\ No newline at end of file
diff --git a/Providers/XAIServiceBase.cs b/Providers/XAIServiceBase.cs
index 30092fe..06eb38b 100644
--- a/Providers/XAIServiceBase.cs
+++ b/Providers/XAIServiceBase.cs
@@ -15,11 +15,14 @@ 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;
@@ -44,12 +47,14 @@ namespace xAiApi.Providers
///
public XAiModelDescriptor Descriptor { get; }
+ private readonly IXFileProvider fileProvider;
private readonly IXAiDataProvider dataProvider;
private readonly ILogger logger;
private readonly XAiApiConfiguration configuration;
private readonly XValidationProvider validationProvider;
protected XAIServiceBase(
+ IXFileProvider fileProvider,
IXAiDataProvider dataProvider,
ILogger logger,
XAiApiConfiguration configuration,
@@ -59,6 +64,7 @@ namespace xAiApi.Providers
{
//
this.logger = logger;
+ this.fileProvider = fileProvider;
this.dataProvider = dataProvider;
this.configuration = configuration;
this.validationProvider = validationProvider;
@@ -347,7 +353,7 @@ namespace xAiApi.Providers
#endregion
//
- #region Data Provider Based Actions ...
+ #region Loaders ...
///
/// Load AI Projects ...
///
@@ -422,8 +428,6 @@ namespace xAiApi.Providers
return result;
}
-
-
///
/// Load Conversations of Specified AI Project ...
///
@@ -483,7 +487,153 @@ namespace xAiApi.Providers
//
return result;
}
+ #endregion
+ //
+ #region Queries ...
+ ///
+ /// Query Projects ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> 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;
+ }
+
+ ///
+ /// Query Conversations ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> 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;
+ }
+
+ ///
+ /// Query Messages ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> 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 ...
///
/// Create an AI Project ...
///
@@ -540,7 +690,10 @@ namespace xAiApi.Providers
//
return result;
}
+ #endregion
+ //
+ #region Data Actions ...
///
/// Ask a Question as Prompt and Retrieve Response ...
///
@@ -550,6 +703,7 @@ namespace xAiApi.Providers
///
///
///
+ ///
///
///
public async Task AskAsync(
@@ -559,6 +713,7 @@ namespace xAiApi.Providers
Guid conversationId,
string connectionId = null,
IFormFileCollection files = null,
+ XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
)
{
@@ -638,8 +793,26 @@ namespace xAiApi.Providers
);
//
- // TODO: Parse Files ...
- // for Attached them into Prompt Message ...
+ // Upload Files ...
+ IEnumerable 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 ...
@@ -660,21 +833,55 @@ namespace xAiApi.Providers
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
+ {
+ { XAiServiceConstants.XMetaDataFileIdsIdentifier, fileIds }
+ }.ToJSON();
+ }
+
+ //
+ // Save Prompt Message ...
promptMessage = await dataProvider.AddMessage(
item: promptMessage,
connectionId: connectionId,
conversationId: conversationId,
cancellationToken: cancellationToken
);
- promptMessage = await dataProvider.Enrich(
- source: promptMessage,
- cancellationToken: cancellationToken
- );
//
- // Attach Uploaded Files ...
+ // Since Prompt Message Has Id now, we can Attach Uploaded Files to it ...
+ IList fileContents = null;
+ 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
+ );
+ }
+
+ //
+ // TODO: Extract File Contents ...
+ }
+
+ //
+ // Converts to ChatMessage ...
var promptChatMessage = promptMessage
- .ToChatMessages();
+ .ToChatMessages(fileContents);
//
// Ask Questions From LLM ...
@@ -701,10 +908,6 @@ namespace xAiApi.Providers
conversationId: conversationId,
cancellationToken: cancellationToken
);
- result = await dataProvider.Enrich(
- source: result,
- cancellationToken: cancellationToken
- );
//
return result;
@@ -904,149 +1107,6 @@ namespace xAiApi.Providers
}
#endregion
- //
- #region Query ...
- ///
- /// Query Projects ...
- ///
- ///
- ///
- ///
- ///
- public async Task> 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;
- }
-
- ///
- /// Query Conversations ...
- ///
- ///
- ///
- ///
- ///
- ///
- public async Task> 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;
- }
-
- ///
- /// Query Messages ...
- ///
- ///
- ///
- ///
- ///
- ///
- public async Task> 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
-
///
/// Base Dispose Action ...
///
diff --git a/Providers/XAiEmbeddingServiceBase.cs b/Providers/XAiEmbeddingServiceBase.cs
index 0a1cda3..cd96a26 100644
--- a/Providers/XAiEmbeddingServiceBase.cs
+++ b/Providers/XAiEmbeddingServiceBase.cs
@@ -16,6 +16,7 @@ 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;
@@ -28,15 +29,17 @@ namespace xAiApi.Providers
///
public abstract class XAiEmbeddingServiceBase : XAIServiceBase, IXAiEmbeddingServiceBase
{
+ private readonly IXFileProvider fileProvider;
private readonly XAiModelDescriptor descriptor;
private readonly ILogger logger;
protected XAiEmbeddingServiceBase(
+ IXFileProvider fileProvider,
IXAiDataProvider dataProvider,
XAiApiConfiguration configuration,
ILogger logger,
string embeddingModelName = XAiApiConstants.XAiEmbeddingModelName
- ) : base(dataProvider, logger, configuration, null)
+ ) : base(fileProvider, dataProvider, logger, configuration, null)
{
//
// Prepare Embedding Model Descriptor ...
@@ -46,6 +49,8 @@ namespace xAiApi.Providers
XException.InvalidConfiguration.Throw();
}
+ this.fileProvider = fileProvider;
+
//
this.logger = logger;
}
diff --git a/Providers/XDefaultAiService.cs b/Providers/XDefaultAiService.cs
index 29852d4..7ae7a5e 100644
--- a/Providers/XDefaultAiService.cs
+++ b/Providers/XDefaultAiService.cs
@@ -3,6 +3,7 @@ using xAiApi.Interfaces;
using xCommons.Providers;
using xAiModels.Interfaces;
using xAiApi.Configurations;
+using xFileService.Interfaces;
using Microsoft.Extensions.Logging;
namespace xAiApi.Providers
@@ -10,11 +11,13 @@ namespace xAiApi.Providers
public class XDefaultAiService : XAIServiceBase, IXDefaultAiService
{
public XDefaultAiService(
+ IXFileProvider fileProvider,
IXAiDataProvider dataProvider,
ILogger logger,
XAiApiConfiguration configuration,
XValidationProvider validationProvider
) : base(
+ fileProvider,
dataProvider,
logger,
configuration,
diff --git a/Providers/XDefaultEmbeddingService.cs b/Providers/XDefaultEmbeddingService.cs
index ced9f0d..2ca0d70 100644
--- a/Providers/XDefaultEmbeddingService.cs
+++ b/Providers/XDefaultEmbeddingService.cs
@@ -2,6 +2,7 @@ using xAiApi.Constants;
using xAiApi.Interfaces;
using xAiModels.Interfaces;
using xAiApi.Configurations;
+using xFileService.Interfaces;
using Microsoft.Extensions.Logging;
namespace xAiApi.Providers
@@ -9,10 +10,12 @@ namespace xAiApi.Providers
public class XDefaultEmbeddingService : XAiEmbeddingServiceBase, IXDefaultEmbeddingService
{
public XDefaultEmbeddingService(
+ IXFileProvider fileProvider,
IXAiDataProvider dataProvider,
XAiApiConfiguration configuration,
ILogger logger
) : base(
+ fileProvider,
dataProvider,
configuration,
logger,
diff --git a/Providers/XDefaultThinkingAiService.cs b/Providers/XDefaultThinkingAiService.cs
index e42a207..6b83d04 100644
--- a/Providers/XDefaultThinkingAiService.cs
+++ b/Providers/XDefaultThinkingAiService.cs
@@ -4,6 +4,7 @@ using xCommons.Providers;
using xAiModels.Interfaces;
using xAiApi.Configurations;
using Microsoft.Extensions.AI;
+using xFileService.Interfaces;
using Microsoft.Extensions.Logging;
namespace xAiApi.Providers
@@ -11,6 +12,7 @@ namespace xAiApi.Providers
public class XDefaultThinkingAiService : XAIServiceBase, IXDefaultThinkingAiService
{
public XDefaultThinkingAiService(
+ IXFileProvider fileProvider,
IXAiDataProvider dataProvider,
ILogger logger,
XAiApiConfiguration configuration,
@@ -18,6 +20,7 @@ namespace xAiApi.Providers
ReasoningEffort effort = ReasoningEffort.Medium,
ReasoningOutput output = ReasoningOutput.Full
) : base(
+ fileProvider,
dataProvider,
logger,
configuration,