This commit is contained in:
2026-09-30 15:27:39 +03:30
parent 383e42a8d2
commit efaf71d962
4 changed files with 459 additions and 75 deletions
+136 -4
View File
@@ -1,4 +1,5 @@
using System; using System;
using xModels.Dtos;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using xAiModels.Models; using xAiModels.Models;
@@ -164,14 +165,16 @@ namespace xAiApi.Controllers
#endregion #endregion
// //
#region Data Provided Actions ... #region Loaders ...
/// <summary> /// <summary>
/// Retrieve AI Projects ... /// Retrieve AI Projects ...
/// </summary> /// </summary>
/// <param name="includeDefaults"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("Projects")] [HttpGet("Projects")]
public async Task<ActionResult<IEnumerable<XAiProjectDto>>> LoadProjects( public async Task<ActionResult<IEnumerable<XAiProjectDto>>> LoadProjects(
[FromQuery] bool includeDefaults = false,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
) )
{ {
@@ -186,6 +189,7 @@ namespace xAiApi.Controllers
var result = await aiService.LoadProjects( var result = await aiService.LoadProjects(
ownerId: userInfo.UserId, ownerId: userInfo.UserId,
connectionId: connectionId, connectionId: connectionId,
includeDefaults: includeDefaults,
cancellationToken: cancellationToken cancellationToken: cancellationToken
); );
@@ -282,7 +286,132 @@ namespace xAiApi.Controllers
return result; return result;
} }
} }
#endregion
//
#region Queries ...
/// <summary>
/// Query Projects ...
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("Projects/Query")]
public async Task<ActionResult<XQueryResult<XAiProjectDto>>> QueryProjects(
[FromQuery] XQuery query,
CancellationToken cancellationToken = default
)
{
//
try
{
//
var userInfo = await GetUserInfo();
var connectionId = GetConnectionId();
//
var result = await aiService.QueryProjects(
query: query,
ownerId: userInfo.UserId,
cancellationToken: cancellationToken
);
//
return Ok(result);
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// Query Conversations ...
/// </summary>
/// <param name="projectId"></param>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("Projects/{projectId}/Conversations/Query")]
public async Task<ActionResult<XQueryResult<XAiConversationDto>>> QueryConversations(
[FromRoute] Guid projectId,
[FromQuery] XQuery query,
CancellationToken cancellationToken = default
)
{
//
try
{
//
var userInfo = await GetUserInfo();
var connectionId = GetConnectionId();
//
var result = await aiService.QueryConversations(
query: query,
projectId: projectId,
ownerId: userInfo.UserId,
cancellationToken: cancellationToken
);
//
return Ok(result);
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// Query Messages ...
/// </summary>
/// <param name="conversationId"></param>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("Conversations/{conversationId}/Messages/Query")]
public async Task<ActionResult<XQueryResult<XAiMessageDto>>> QueryMessages(
[FromRoute] Guid conversationId,
[FromQuery] XQuery query,
CancellationToken cancellationToken = default
)
{
//
try
{
//
var userInfo = await GetUserInfo();
var connectionId = GetConnectionId();
//
var result = await aiService.QueryMessages(
query: query,
ownerId: userInfo.UserId,
conversationId: conversationId,
cancellationToken: cancellationToken
);
//
return Ok(result);
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
#endregion
//
#region Creators ...
/// <summary> /// <summary>
/// Create an AI Project ... /// Create an AI Project ...
/// </summary> /// </summary>
@@ -333,7 +462,7 @@ namespace xAiApi.Controllers
// //
var result = GetExceptionActionResult(ex); var result = GetExceptionActionResult(ex);
return result; return result;
} }
} }
/// <summary> /// <summary>
@@ -355,7 +484,7 @@ namespace xAiApi.Controllers
{ {
// //
// Validate ... // Validate ...
if (projectId.IsDefaultGuid() || if (projectId.IsDefaultGuid() ||
item.IsNullOrDefault()) item.IsNullOrDefault())
{ {
XException.InvalidArgs.Throw(); XException.InvalidArgs.Throw();
@@ -390,9 +519,12 @@ namespace xAiApi.Controllers
// //
var result = GetExceptionActionResult(ex); var result = GetExceptionActionResult(ex);
return result; return result;
} }
} }
#endregion
//
#region Data Actions ...
/// <summary> /// <summary>
/// Ask Question and Get Response ... /// Ask Question and Get Response ...
/// </summary> /// </summary>
+46
View File
@@ -1,4 +1,5 @@
using System; using System;
using xModels.Dtos;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using xAiModels.Models; using xAiModels.Models;
@@ -92,11 +93,13 @@ namespace xAiApi.Interfaces
/// </summary> /// </summary>
/// <param name="ownerId"></param> /// <param name="ownerId"></param>
/// <param name="connectionId"></param> /// <param name="connectionId"></param>
/// <param name="includeDefaults"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
Task<IEnumerable<XAiProjectDto>> LoadProjects( Task<IEnumerable<XAiProjectDto>> LoadProjects(
string ownerId, string ownerId,
string connectionId = null, string connectionId = null,
bool includeDefaults = false,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
); );
@@ -123,6 +126,49 @@ namespace xAiApi.Interfaces
Guid conversationId, Guid conversationId,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
); );
/// <summary>
/// Query Projects ...
/// </summary>
/// <param name="ownerId"></param>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XQueryResult<XAiProjectDto>> QueryProjects(
string ownerId,
XQuery query,
CancellationToken cancellationToken = default
);
/// <summary>
/// Query Conversations ...
/// </summary>
/// <param name="ownerId"></param>
/// <param name="projectId"></param>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XQueryResult<XAiConversationDto>> QueryConversations(
string ownerId,
Guid projectId,
XQuery query,
CancellationToken cancellationToken = default
);
/// <summary>
/// Query Messages ...
/// </summary>
/// <param name="ownerId"></param>
/// <param name="conversationId"></param>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XQueryResult<XAiMessageDto>> QueryMessages(
string ownerId,
Guid conversationId,
XQuery query,
CancellationToken cancellationToken = default
);
/// <summary> /// <summary>
/// Create an AI Project ... /// Create an AI Project ...
+55 -1
View File
@@ -1,4 +1,5 @@
using System; using System;
using xModels.Dtos;
using System.Threading; using System.Threading;
using xAiModels.Models; using xAiModels.Models;
using xAiModels.Models.Dtos; using xAiModels.Models.Dtos;
@@ -38,14 +39,16 @@ namespace xAiApi.Interfaces
#endregion #endregion
// //
#region Data Provided Actions ... #region Loaders ...
/// <summary> /// <summary>
/// Retrieve AI Projects ... /// Retrieve AI Projects ...
/// </summary> /// </summary>
/// <param name="includeDefaults"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("Projects")] [HttpGet("Projects")]
Task<ActionResult<IEnumerable<XAiProjectDto>>> LoadProjects( Task<ActionResult<IEnumerable<XAiProjectDto>>> LoadProjects(
[FromQuery] bool includeDefaults = false,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
); );
@@ -72,7 +75,55 @@ namespace xAiApi.Interfaces
[FromRoute] Guid conversationId, [FromRoute] Guid conversationId,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
); );
#endregion
//
#region Queries ...
/// <summary>
/// Query Projects ...
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("Projects/Query")]
Task<ActionResult<XQueryResult<XAiProjectDto>>> QueryProjects(
[FromQuery] XQuery query,
CancellationToken cancellationToken = default
);
/// <summary>
/// Query Conversations ...
/// </summary>
/// <param name="projectId"></param>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("Projects/{projectId}/Conversations/Query")]
Task<ActionResult<XQueryResult<XAiConversationDto>>> QueryConversations(
[FromRoute] Guid projectId,
[FromQuery] XQuery query,
CancellationToken cancellationToken = default
);
/// <summary>
/// Query Messages ...
/// </summary>
/// <param name="conversationId"></param>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("Conversations/{conversationId}/Messages/Query")]
Task<ActionResult<XQueryResult<XAiMessageDto>>> QueryMessages(
[FromRoute] Guid conversationId,
[FromQuery] XQuery query,
CancellationToken cancellationToken = default
);
#endregion
//
#region Creators ...
/// <summary> /// <summary>
/// Create an AI Project ... /// Create an AI Project ...
/// </summary> /// </summary>
@@ -98,7 +149,10 @@ namespace xAiApi.Interfaces
[FromBody] XAiConversationDto item, [FromBody] XAiConversationDto item,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
); );
#endregion
//
#region Data Actions ...
/// <summary> /// <summary>
/// Ask Question and Get Response ... /// Ask Question and Get Response ...
/// </summary> /// </summary>
+222 -70
View File
@@ -2,6 +2,7 @@ using System;
using OpenAI; using OpenAI;
using OllamaSharp; using OllamaSharp;
using System.Linq; using System.Linq;
using xModels.Dtos;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using xAiApi.Constants; using xAiApi.Constants;
@@ -352,11 +353,13 @@ namespace xAiApi.Providers
/// </summary> /// </summary>
/// <param name="ownerId"></param> /// <param name="ownerId"></param>
/// <param name="connectionId"></param> /// <param name="connectionId"></param>
/// <param name="includeDefaults"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
public async Task<IEnumerable<XAiProjectDto>> LoadProjects( public async Task<IEnumerable<XAiProjectDto>> LoadProjects(
string ownerId, string ownerId,
string connectionId = null, string connectionId = null,
bool includeDefaults = false,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
) )
{ {
@@ -377,42 +380,50 @@ namespace xAiApi.Providers
); );
// //
// Here we have to fill Conversations of Default Project ... // Handle Defaults Including ...
var defaultProjectId = result.GetDafualtProject(ownerId); if (includeDefaults)
var defaultProject = result.FirstOrDefault(x => x.Id == defaultProjectId); {
result = result.Where(x => x.Id != defaultProjectId); //
// Here we have to fill Conversations of Default Project ...
var defaultProjectId = result.GetDafualtProject(ownerId);
var defaultProject = result.FirstOrDefault(x => x.Id == defaultProjectId);
result = result.Where(x => x.Id != defaultProjectId);
// //
// Reading Conversations of Specified Default Project ... // Reading Conversations of Specified Default Project ...
var defaultProjectConversations = await dataProvider.LoadConversations( var defaultProjectConversations = await dataProvider.LoadConversations(
fillMessages: false, fillMessages: false,
connectionId: connectionId, connectionId: connectionId,
projectId: defaultProjectId, projectId: defaultProjectId,
cancellationToken: cancellationToken cancellationToken: cancellationToken
); );
var defaultConversationId = defaultProjectConversations.GetDefaultConversation( var defaultConversationId = defaultProjectConversations.GetDefaultConversation(
ownerId: ownerId, ownerId: ownerId,
projectId: defaultProjectId projectId: defaultProjectId
); );
var defaultConversation = defaultProjectConversations.First(x => x.Id == defaultConversationId); var defaultConversation = defaultProjectConversations.First(x => x.Id == defaultConversationId);
defaultProjectConversations = defaultProjectConversations.Where(x => x.Id != defaultConversationId); defaultProjectConversations = defaultProjectConversations.Where(x => x.Id != defaultConversationId);
// //
// Reading Default Conversations Messages ... // Reading Default Conversations Messages ...
var defaultConversationMessages = await dataProvider.LoadMessages( var defaultConversationMessages = await dataProvider.LoadMessages(
cancellationToken: cancellationToken, cancellationToken: cancellationToken,
conversationId: defaultConversationId conversationId: defaultConversationId
); );
// //
defaultConversation.Messages = [.. defaultConversationMessages]; defaultConversation.Messages = [.. defaultConversationMessages.OrderByDescending(m => m.CreatedOn)];
defaultProjectConversations = [defaultConversation, .. defaultProjectConversations]; defaultProjectConversations = [defaultConversation, .. defaultProjectConversations];
result = [defaultProject, .. result]; defaultProject.Conversations = [.. defaultProjectConversations];
result = [defaultProject, .. result];
}
// //
return result; return result;
} }
/// <summary> /// <summary>
/// Load Conversations of Specified AI Project ... /// Load Conversations of Specified AI Project ...
/// </summary> /// </summary>
@@ -470,7 +481,7 @@ namespace xAiApi.Providers
); );
// //
return result; return result;
} }
/// <summary> /// <summary>
@@ -647,6 +658,7 @@ namespace xAiApi.Providers
Role = XAiChatRole.User, Role = XAiChatRole.User,
CreatedOn = DateTime.UtcNow, CreatedOn = DateTime.UtcNow,
ConversationId = conversationId, ConversationId = conversationId,
ConversationTitle = conversation.Title
}; };
promptMessage = await dataProvider.AddMessage( promptMessage = await dataProvider.AddMessage(
item: promptMessage, item: promptMessage,
@@ -678,9 +690,10 @@ namespace xAiApi.Providers
{ {
Content = answer, Content = answer,
OwnerId = ownerId, OwnerId = ownerId,
Role = XAiChatRole.System,
CreatedOn = DateTime.UtcNow, CreatedOn = DateTime.UtcNow,
ConversationId = conversationId Role = XAiChatRole.Assistant,
ConversationId = conversationId,
ConversationTitle = conversation.Title
}; };
result = await dataProvider.AddMessage( result = await dataProvider.AddMessage(
item: result, item: result,
@@ -891,6 +904,149 @@ namespace xAiApi.Providers
} }
#endregion #endregion
//
#region Query ...
/// <summary>
/// Query Projects ...
/// </summary>
/// <param name="ownerId"></param>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XQueryResult<XAiProjectDto>> 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;
}
/// <summary>
/// Query Conversations ...
/// </summary>
/// <param name="ownerId"></param>
/// <param name="projectId"></param>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XQueryResult<XAiConversationDto>> 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;
}
/// <summary>
/// Query Messages ...
/// </summary>
/// <param name="ownerId"></param>
/// <param name="conversationId"></param>
/// <param name="query"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XQueryResult<XAiMessageDto>> 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
/// <summary> /// <summary>
/// Base Dispose Action ... /// Base Dispose Action ...
/// </summary> /// </summary>
@@ -916,49 +1072,45 @@ namespace xAiApi.Providers
// //
var result = new List<ChatMessage>(); var result = new List<ChatMessage>();
//
// Retrieve Introduction Prompt ...
var introductionPrompt = configuration.GetPrompt(XAiApiConstants.XAiApiIntroductionPromptName);
if (introductionPrompt.IsNullOrEmpty())
{
XException.InvalidData.Throw();
}
//
var introductionMessage = new ChatMessage(
role: ChatRole.System,
content: introductionPrompt
);
//
result.Add(introductionMessage);
//
if (!project.IsNullOrDefault() &&
!project.Prompt.IsNullOrEmpty())
{
//
// TODO: Handle Project Files Here ...
var projectMessage = new ChatMessage(
role: ChatRole.System,
contents: [new TextContent(project.Prompt)]
);
result.Add(projectMessage);
}
// //
if (messages.HasChild()) if (messages.HasChild())
{ {
// //
// if Messages Has Child, this means // TODO: Here we can Implement Memory Management based on
// Default Messages Handled before ... // Last nth of Messages, or Summarization of Messages, etc ...
result = [.. messages.Select(m => m.ToChatMessages())]; result = [..result, ..messages
} .OrderBy(m => m.CreatedOn)
else .Select(m => m.ToChatMessages())];
{
//
// If Message Has not any Childs, this means
// First Asking Questions ...
//
// Retrieve Introduction Prompt ...
var introductionPrompt = configuration.GetPrompt(XAiApiConstants.XAiApiIntroductionPromptName);
if (introductionPrompt.IsNullOrEmpty())
{
XException.InvalidData.Throw();
}
//
var introductionMessage = new ChatMessage(
role: ChatRole.System,
content: introductionPrompt
);
//
result.Add(introductionMessage);
//
if (!project.IsNullOrDefault() &&
!project.Prompt.IsNullOrEmpty())
{
//
// TODO: Handle Project Files Here ...
var projectMessage = new ChatMessage(
role: ChatRole.System,
contents: [new TextContent(project.Prompt)]
);
result.Add(projectMessage);
}
} }
// //