last works ...

This commit is contained in:
2026-04-30 12:01:56 +03:30
parent 2b5e09912f
commit f7184c7b79
3 changed files with 210 additions and 10 deletions
+31
View File
@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Mvc;
using xModels.Base;
namespace xAiApi.AI.Models.Dtos
{
/// <summary>
/// Ai Ask Request Data Transfer Object ...
/// </summary>
public class XAiAskRequestDto : XBaseDto
{
/// <summary>
/// Provided Prompt for Ask ...
/// </summary>
[FromQuery]
public string Prompt { get; set; }
/// <summary>
/// Ai Selected Project Id, Optional ...
/// if null, Use Default Project for Authenticated User ...
/// </summary>
[FromQuery]
public string ProjectId { get; set; } = null;
/// <summary>
/// Ai Selected Conversation of Project, Optional ...
/// if null, Create new Conversation in Selected Project ...
/// </summary>
[FromQuery]
public string ConversationId { get; set; } = null;
}
}
+26 -10
View File
@@ -509,12 +509,17 @@ namespace xAiApi.AI.Services
//
// Getting Response Message from LLM ...
var chatResponseEnumerable = chatClient
var chatStream = chatClient
.GetStreamingResponseAsync(
options: chatOptions,
messages: requirements.ChatHistory,
cancellationToken: cancellationToken
);
isValid = !chatStream.IsNull();
if (!isValid)
{
yield break;
}
//
// Create an Empty Message for ID ...
@@ -531,31 +536,31 @@ namespace xAiApi.AI.Services
saveChanges: true,
item: responseMessage
);
//
// Collecting Data through Enumeration ...
// Loop through Async Enumerable ...
string content = string.Empty;
var sequence = DateTime.UtcNow.ToTimestamp();
await foreach (var data in chatResponseEnumerable)
await foreach(var msg in chatStream)
{
//
// Crucially, check the cancellation token before continuing
// Throwing OperationCanceledException is the standard way to signal cancellation.
if (cancellationToken.CanBeCanceled)
if (cancellationToken.IsCancellationRequested)
{
yield break;
}
//
// Append recieved data to Content ...
content += data;
content += msg.Text;
//
// Crate Message Update Model ...
var item = new XAiMessageUpdateDto
{
Sequense = sequence,
Content = data.Text,
Content = msg.Text,
Id = responseMessage.Id,
OwnerId = userInfo.UserId,
UpdatedAt = DateTime.UtcNow,
@@ -586,6 +591,7 @@ namespace xAiApi.AI.Services
}
//
// End Async Enumerable ...
yield break;
}
#endregion
@@ -976,7 +982,10 @@ namespace xAiApi.AI.Services
{
//
// Complete Chat History ...
foreach (var msg in conversation.Messages)
var orderedByTimeMessage = conversation.Messages
.OrderBy(x => x.CreatedOn)
.AsEnumerable();
foreach (var msg in orderedByTimeMessage)
{
//
result.ChatHistory.Add(
@@ -1025,11 +1034,18 @@ namespace xAiApi.AI.Services
XException.InvalidData.Throw();
}
//
// Update Conversation ...
var conversationEntity = await aiConversationRepository
.GetAsync(id: result.AiConversation.Id);
conversation = mapper.Map<XAiConversationDto>(conversationEntity);
result.AiConversation = conversation;
//
result.ChatHistory.Add(
new ChatMessage(
content: prompt,
role: XAiChatRole.User.ToChatRole()
content: promptAiMessage.Content,
role: promptAiMessage.Role.ToChatRole()
)
);
+153
View File
@@ -8,11 +8,13 @@ using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using OpenAI.Realtime;
using xAiApi.AI.Interfaces;
using xAiApi.AI.Models.Dtos;
using xAiApi.Base;
using xAiModels.Models;
using xCommons.Configurations;
using xCommons.Extensions;
using xCommons.Providers;
using xExceptions.Constants;
using xIdentityHelper;
using xIdentityService.Constants;
using xIdentityService.Interfaces;
@@ -51,6 +53,62 @@ namespace xAiApi.Controllers.V1
//
#region Actions ...
/// <summary>
/// As a Message from Ai ...
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("Ask")]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XAiMessageDto>> Ask(
[FromQuery] XAiAskRequestDto request,
CancellationToken cancellationToken = default
)
{
//
// Do ...
try
{
//
// Validate Model ...
var isValid =
ModelState.IsValid &&
!request.IsNull() &&
!request.Prompt.IsNullOrEmpty();
if (!isValid)
{
XException.InvalidArgs.Throw();
}
//
// Retrieve User Info ...
var userInfo = await GetUserInfo();
//
// Retrieve Result ...
var result = await aiService
.Ask(
userInfo: userInfo,
prompt: request.Prompt,
projectId: request.ProjectId,
cancellationToken: cancellationToken,
conversationId: request.ConversationId
);
//
return Ok(result.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// As a Message from Ai ...
/// </summary>
@@ -96,6 +154,98 @@ namespace xAiApi.Controllers.V1
}
}
/// <summary>
/// Ask a Message from Ai using Streamin Pattern ...
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("AskStream")]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task AskStream(
[FromQuery] XAiAskRequestDto request,
CancellationToken cancellationToken = default
)
{
//
// Do ...
try
{
//
// Validate Model ...
var isValid =
ModelState.IsValid &&
!request.IsNull() &&
!request.Prompt.IsNullOrEmpty();
if (!isValid)
{
XException.InvalidArgs.Throw();
}
//
// Adding Content Type ...
Response.Headers
.Append("Content-Type", "text/event-stream");
//
// Retrieve User Info ...
var userInfo = await GetUserInfo();
//
// Access Async Enumerable ...
var stream = aiService
.AskStream(
userInfo: userInfo,
prompt: request.Prompt,
projectId: request.ProjectId,
cancellationToken: cancellationToken,
conversationId: request.ConversationId
);
//
// Loop through Enumerable ...
await foreach (var message in stream)
{
//
// Converts Model to Json String ...
var json = message.ToJSON();
//
// Generates Json byte[] ...
var bytes = json.ToBytes();
//
// Write Bytes to Stream ...
await Response.Body.WriteAsync(
bytes,
0,
bytes.Length,
cancellationToken
);
//
// Flushing Stream ...
await Response.Body.FlushAsync(cancellationToken);
}
//
// Closing Connection ...
await Response.Body.FlushAsync();
}
catch (Exception)
{ }
}
/// <summary>
/// Ask a Message from Ai using Streamin Pattern ...
/// </summary>
/// <param name="prompt"></param>
/// <param name="projectId"></param>
/// <param name="conversationId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("AskAIStream")]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task AskAIStream(
@@ -124,6 +274,9 @@ namespace xAiApi.Controllers.V1
conversationId: conversationId,
cancellationToken: cancellationToken
);
//
// Loop through Enumerable ...
await foreach (var message in stream)
{
//