2024-03-26 21:57:13 +00:00
|
|
|
using BotSharp.Abstraction.Routing;
|
2024-04-30 21:25:58 +00:00
|
|
|
using Newtonsoft.Json.Serialization;
|
|
|
|
|
using Newtonsoft.Json;
|
2024-05-03 22:24:18 +00:00
|
|
|
using BotSharp.Abstraction.Files.Models;
|
2024-05-06 06:51:59 +00:00
|
|
|
using BotSharp.Abstraction.Files;
|
2024-01-31 19:57:54 +00:00
|
|
|
|
2023-08-19 00:15:02 +00:00
|
|
|
namespace BotSharp.OpenAPI.Controllers;
|
|
|
|
|
|
|
|
|
|
[Authorize]
|
|
|
|
|
[ApiController]
|
2023-12-27 15:53:54 +00:00
|
|
|
public class ConversationController : ControllerBase
|
2023-08-19 00:15:02 +00:00
|
|
|
{
|
|
|
|
|
private readonly IServiceProvider _services;
|
|
|
|
|
private readonly IUserIdentity _user;
|
|
|
|
|
|
2023-10-27 22:16:53 +00:00
|
|
|
public ConversationController(IServiceProvider services,
|
2023-08-19 00:15:02 +00:00
|
|
|
IUserIdentity user)
|
|
|
|
|
{
|
|
|
|
|
_services = services;
|
|
|
|
|
_user = user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost("/conversation/{agentId}")]
|
2023-10-27 15:18:48 +00:00
|
|
|
public async Task<ConversationViewModel> NewConversation([FromRoute] string agentId, [FromBody] MessageConfig config)
|
2023-08-19 00:15:02 +00:00
|
|
|
{
|
|
|
|
|
var service = _services.GetRequiredService<IConversationService>();
|
2023-10-27 15:18:48 +00:00
|
|
|
var conv = new Conversation
|
2023-08-19 00:15:02 +00:00
|
|
|
{
|
2023-11-14 01:25:25 +00:00
|
|
|
AgentId = agentId,
|
2023-11-27 03:04:48 +00:00
|
|
|
Channel = ConversationChannel.OpenAPI,
|
2024-02-12 15:14:33 +00:00
|
|
|
UserId = _user.Id,
|
|
|
|
|
TaskId = config.TaskId
|
2023-08-19 00:15:02 +00:00
|
|
|
};
|
2023-10-27 15:18:48 +00:00
|
|
|
conv = await service.NewConversation(conv);
|
2023-11-20 17:08:05 +00:00
|
|
|
service.SetConversationId(conv.Id, config.States);
|
2023-10-27 15:18:48 +00:00
|
|
|
|
|
|
|
|
return ConversationViewModel.FromSession(conv);
|
2023-08-19 00:15:02 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-08 22:02:08 +00:00
|
|
|
[HttpPost("/conversations")]
|
|
|
|
|
public async Task<PagedItems<ConversationViewModel>> GetConversations([FromBody] ConversationFilter filter)
|
2023-11-14 01:25:25 +00:00
|
|
|
{
|
|
|
|
|
var service = _services.GetRequiredService<IConversationService>();
|
2023-12-04 23:42:46 +00:00
|
|
|
var conversations = await service.GetConversations(filter);
|
|
|
|
|
|
2023-11-14 01:25:25 +00:00
|
|
|
var userService = _services.GetRequiredService<IUserService>();
|
2024-01-28 01:02:33 +00:00
|
|
|
var agentService = _services.GetRequiredService<IAgentService>();
|
2023-12-04 23:42:46 +00:00
|
|
|
var list = conversations.Items
|
|
|
|
|
.Select(x => ConversationViewModel.FromSession(x))
|
|
|
|
|
.ToList();
|
2023-11-14 01:25:25 +00:00
|
|
|
|
|
|
|
|
foreach (var item in list)
|
|
|
|
|
{
|
|
|
|
|
var user = await userService.GetUser(item.User.Id);
|
|
|
|
|
item.User = UserViewModel.FromUser(user);
|
2024-01-28 01:02:33 +00:00
|
|
|
|
|
|
|
|
var agent = await agentService.GetAgent(item.AgentId);
|
2024-02-12 15:14:33 +00:00
|
|
|
item.AgentName = agent?.Name;
|
2023-11-14 01:25:25 +00:00
|
|
|
}
|
|
|
|
|
|
2023-12-04 23:42:46 +00:00
|
|
|
return new PagedItems<ConversationViewModel>
|
|
|
|
|
{
|
|
|
|
|
Count = conversations.Count,
|
|
|
|
|
Items = list
|
|
|
|
|
};
|
2023-11-14 01:25:25 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-14 14:13:54 +00:00
|
|
|
[HttpGet("/conversation/{conversationId}/dialogs")]
|
|
|
|
|
public async Task<IEnumerable<ChatResponseModel>> GetDialogs([FromRoute] string conversationId)
|
|
|
|
|
{
|
|
|
|
|
var conv = _services.GetRequiredService<IConversationService>();
|
2024-03-27 18:50:27 +00:00
|
|
|
conv.SetConversationId(conversationId, new List<MessageState>());
|
2024-03-24 01:31:15 +00:00
|
|
|
var history = conv.GetDialogHistory(fromBreakpoint: false);
|
2023-11-14 14:13:54 +00:00
|
|
|
|
|
|
|
|
var userService = _services.GetRequiredService<IUserService>();
|
2024-01-31 20:01:59 +00:00
|
|
|
var agentService = _services.GetRequiredService<IAgentService>();
|
2023-11-14 14:13:54 +00:00
|
|
|
|
|
|
|
|
var dialogs = new List<ChatResponseModel>();
|
|
|
|
|
foreach (var message in history)
|
|
|
|
|
{
|
2024-01-31 19:57:54 +00:00
|
|
|
if (message.Role == AgentRole.User)
|
|
|
|
|
{
|
|
|
|
|
var user = await userService.GetUser(message.SenderId);
|
2023-11-14 14:13:54 +00:00
|
|
|
|
2024-01-31 19:57:54 +00:00
|
|
|
dialogs.Add(new ChatResponseModel
|
|
|
|
|
{
|
|
|
|
|
ConversationId = conversationId,
|
|
|
|
|
MessageId = message.MessageId,
|
|
|
|
|
CreatedAt = message.CreatedAt,
|
2024-04-23 21:27:09 +00:00
|
|
|
Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
|
2024-02-06 20:42:35 +00:00
|
|
|
Data = message.Data,
|
2024-01-31 19:57:54 +00:00
|
|
|
Sender = UserViewModel.FromUser(user)
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-02-28 04:36:33 +00:00
|
|
|
else if (message.Role == AgentRole.Assistant)
|
2023-11-14 14:13:54 +00:00
|
|
|
{
|
2024-01-31 20:01:59 +00:00
|
|
|
var agent = await agentService.GetAgent(message.CurrentAgentId);
|
2024-01-31 19:57:54 +00:00
|
|
|
dialogs.Add(new ChatResponseModel
|
|
|
|
|
{
|
|
|
|
|
ConversationId = conversationId,
|
|
|
|
|
MessageId = message.MessageId,
|
|
|
|
|
CreatedAt = message.CreatedAt,
|
2024-04-23 21:27:09 +00:00
|
|
|
Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
|
2024-02-28 16:16:46 +00:00
|
|
|
Function = message.FunctionName,
|
2024-02-06 20:42:35 +00:00
|
|
|
Data = message.Data,
|
2024-01-31 20:01:59 +00:00
|
|
|
Sender = new UserViewModel
|
|
|
|
|
{
|
|
|
|
|
FirstName = agent.Name,
|
|
|
|
|
Role = message.Role,
|
2024-02-22 16:02:11 +00:00
|
|
|
},
|
2024-04-23 21:27:09 +00:00
|
|
|
RichContent = message.SecondaryRichContent ?? message.RichContent
|
2024-01-31 19:57:54 +00:00
|
|
|
});
|
|
|
|
|
}
|
2023-11-14 14:13:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dialogs;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-26 03:16:41 +00:00
|
|
|
[HttpGet("/conversation/{conversationId}")]
|
|
|
|
|
public async Task<ConversationViewModel> GetConversation([FromRoute] string conversationId)
|
|
|
|
|
{
|
|
|
|
|
var service = _services.GetRequiredService<IConversationService>();
|
|
|
|
|
var conversations = await service.GetConversations(new ConversationFilter
|
|
|
|
|
{
|
|
|
|
|
Id = conversationId
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var userService = _services.GetRequiredService<IUserService>();
|
|
|
|
|
var result = ConversationViewModel.FromSession(conversations.Items.First());
|
|
|
|
|
|
2023-12-27 19:27:52 +00:00
|
|
|
var state = _services.GetRequiredService<IConversationStateService>();
|
2024-04-10 17:45:35 +00:00
|
|
|
result.States = state.Load(conversationId, isReadOnly: true);
|
2023-12-27 19:27:52 +00:00
|
|
|
|
2023-12-26 03:16:41 +00:00
|
|
|
var user = await userService.GetUser(result.User.Id);
|
|
|
|
|
result.User = UserViewModel.FromUser(user);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-14 14:13:54 +00:00
|
|
|
[HttpDelete("/conversation/{conversationId}")]
|
2023-11-26 06:29:06 +00:00
|
|
|
public async Task<bool> DeleteConversation([FromRoute] string conversationId)
|
2023-08-19 00:15:02 +00:00
|
|
|
{
|
2023-11-26 06:29:06 +00:00
|
|
|
var conversationService = _services.GetRequiredService<IConversationService>();
|
2024-03-07 07:34:52 +00:00
|
|
|
var response = await conversationService.DeleteConversations(new List<string> { conversationId });
|
2023-11-26 06:29:06 +00:00
|
|
|
return response;
|
2023-08-19 00:15:02 +00:00
|
|
|
}
|
|
|
|
|
|
2024-01-29 01:33:40 +00:00
|
|
|
[HttpDelete("/conversation/{conversationId}/message/{messageId}")]
|
|
|
|
|
public async Task<bool> DeleteConversationMessage([FromRoute] string conversationId, [FromRoute] string messageId)
|
|
|
|
|
{
|
|
|
|
|
var conversationService = _services.GetRequiredService<IConversationService>();
|
|
|
|
|
var response = await conversationService.TruncateConversation(conversationId, messageId);
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-30 21:25:58 +00:00
|
|
|
private void SetStates(IConversationService conv, NewMessageModel input)
|
|
|
|
|
{
|
|
|
|
|
conv.States.SetState("channel", input.Channel, source: StateSource.External)
|
|
|
|
|
.SetState("provider", input.Provider, source: StateSource.External)
|
|
|
|
|
.SetState("model", input.Model, source: StateSource.External)
|
|
|
|
|
.SetState("temperature", input.Temperature, source: StateSource.External)
|
|
|
|
|
.SetState("sampling_factor", input.SamplingFactor, source: StateSource.External);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-19 00:15:02 +00:00
|
|
|
[HttpPost("/conversation/{agentId}/{conversationId}")]
|
2023-11-14 04:56:06 +00:00
|
|
|
public async Task<ChatResponseModel> SendMessage([FromRoute] string agentId,
|
2023-10-27 22:16:53 +00:00
|
|
|
[FromRoute] string conversationId,
|
2023-09-09 15:37:38 +00:00
|
|
|
[FromBody] NewMessageModel input)
|
2023-08-19 00:15:02 +00:00
|
|
|
{
|
|
|
|
|
var conv = _services.GetRequiredService<IConversationService>();
|
2024-01-29 01:33:40 +00:00
|
|
|
if (!string.IsNullOrEmpty(input.TruncateMessageId))
|
|
|
|
|
{
|
|
|
|
|
await conv.TruncateConversation(conversationId, input.TruncateMessageId);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-03 19:57:44 +00:00
|
|
|
var inputMsg = new RoleDialogModel(AgentRole.User, input.Text)
|
|
|
|
|
{
|
|
|
|
|
Files = input.Files
|
|
|
|
|
};
|
2024-03-26 21:57:13 +00:00
|
|
|
var routing = _services.GetRequiredService<IRoutingService>();
|
|
|
|
|
routing.Context.SetMessageId(conversationId, inputMsg.MessageId);
|
|
|
|
|
|
2024-03-26 23:11:05 +00:00
|
|
|
conv.SetConversationId(conversationId, input.States);
|
2024-04-30 21:25:58 +00:00
|
|
|
SetStates(conv, input);
|
2023-09-14 01:41:51 +00:00
|
|
|
|
2023-11-14 04:56:06 +00:00
|
|
|
var response = new ChatResponseModel();
|
2024-01-13 21:17:40 +00:00
|
|
|
|
2023-11-27 03:44:17 +00:00
|
|
|
await conv.SendMessage(agentId, inputMsg,
|
2024-03-16 14:43:00 +00:00
|
|
|
replyMessage: input.Postback,
|
2023-08-19 00:15:02 +00:00
|
|
|
async msg =>
|
2023-08-20 19:02:59 +00:00
|
|
|
{
|
2024-04-23 21:27:09 +00:00
|
|
|
response.Text = !string.IsNullOrEmpty(msg.SecondaryContent) ? msg.SecondaryContent : msg.Content;
|
2023-10-30 16:48:18 +00:00
|
|
|
response.Function = msg.FunctionName;
|
2024-04-23 21:27:09 +00:00
|
|
|
response.RichContent = msg.SecondaryRichContent ?? msg.RichContent;
|
2023-10-30 16:48:18 +00:00
|
|
|
response.Instruction = msg.Instruction;
|
|
|
|
|
response.Data = msg.Data;
|
2023-08-20 19:02:59 +00:00
|
|
|
},
|
2024-03-16 14:43:00 +00:00
|
|
|
_ => Task.CompletedTask,
|
|
|
|
|
_ => Task.CompletedTask);
|
2023-08-19 00:15:02 +00:00
|
|
|
|
2023-11-03 14:16:16 +00:00
|
|
|
var state = _services.GetRequiredService<IConversationStateService>();
|
|
|
|
|
response.States = state.GetStates();
|
2023-10-27 15:18:48 +00:00
|
|
|
response.MessageId = inputMsg.MessageId;
|
2023-11-14 14:13:54 +00:00
|
|
|
response.ConversationId = conversationId;
|
2023-09-22 20:38:58 +00:00
|
|
|
|
2023-08-19 00:15:02 +00:00
|
|
|
return response;
|
|
|
|
|
}
|
2023-11-09 21:11:36 +00:00
|
|
|
|
2024-04-30 21:25:58 +00:00
|
|
|
[HttpPost("/conversation/{agentId}/{conversationId}/sse")]
|
|
|
|
|
public async Task SendMessageSse([FromRoute] string agentId,
|
|
|
|
|
[FromRoute] string conversationId,
|
|
|
|
|
[FromBody] NewMessageModel input)
|
|
|
|
|
{
|
|
|
|
|
var conv = _services.GetRequiredService<IConversationService>();
|
|
|
|
|
if (!string.IsNullOrEmpty(input.TruncateMessageId))
|
|
|
|
|
{
|
|
|
|
|
await conv.TruncateConversation(conversationId, input.TruncateMessageId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var inputMsg = new RoleDialogModel(AgentRole.User, input.Text);
|
|
|
|
|
var routing = _services.GetRequiredService<IRoutingService>();
|
|
|
|
|
routing.Context.SetMessageId(conversationId, inputMsg.MessageId);
|
|
|
|
|
|
|
|
|
|
conv.SetConversationId(conversationId, input.States);
|
|
|
|
|
SetStates(conv, input);
|
|
|
|
|
|
|
|
|
|
var response = new ChatResponseModel();
|
|
|
|
|
|
|
|
|
|
Response.StatusCode = 200;
|
|
|
|
|
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.ContentType, "text/event-stream");
|
|
|
|
|
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.CacheControl, "no-cache");
|
|
|
|
|
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.Connection, "keep-alive");
|
|
|
|
|
|
|
|
|
|
await conv.SendMessage(agentId, inputMsg,
|
|
|
|
|
replyMessage: input.Postback,
|
|
|
|
|
async msg =>
|
|
|
|
|
{
|
|
|
|
|
response.Text = !string.IsNullOrEmpty(msg.SecondaryContent) ? msg.SecondaryContent : msg.Content;
|
|
|
|
|
response.Function = msg.FunctionName;
|
|
|
|
|
response.RichContent = msg.SecondaryRichContent ?? msg.RichContent;
|
|
|
|
|
response.Instruction = msg.Instruction;
|
|
|
|
|
response.Data = msg.Data;
|
|
|
|
|
|
|
|
|
|
await OnChunkReceived(Response, msg);
|
|
|
|
|
},
|
|
|
|
|
async msg =>
|
|
|
|
|
{
|
|
|
|
|
var message = new RoleDialogModel(AgentRole.Function, msg.Content)
|
|
|
|
|
{
|
|
|
|
|
FunctionArgs = msg.FunctionArgs,
|
|
|
|
|
FunctionName = msg.FunctionName,
|
|
|
|
|
Indication = msg.Indication
|
|
|
|
|
};
|
|
|
|
|
await OnChunkReceived(Response, message);
|
|
|
|
|
},
|
|
|
|
|
async msg =>
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var state = _services.GetRequiredService<IConversationStateService>();
|
|
|
|
|
response.States = state.GetStates();
|
|
|
|
|
response.MessageId = inputMsg.MessageId;
|
|
|
|
|
response.ConversationId = conversationId;
|
|
|
|
|
|
|
|
|
|
// await OnEventCompleted(Response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task OnChunkReceived(HttpResponse response, RoleDialogModel message)
|
|
|
|
|
{
|
|
|
|
|
var json = JsonConvert.SerializeObject(message, new JsonSerializerSettings
|
|
|
|
|
{
|
|
|
|
|
Formatting = Formatting.None,
|
|
|
|
|
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
|
|
|
|
NullValueHandling = NullValueHandling.Ignore,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var buffer = Encoding.UTF8.GetBytes($"data:{json}\n");
|
|
|
|
|
await response.Body.WriteAsync(buffer, 0, buffer.Length);
|
|
|
|
|
await Task.Delay(10);
|
|
|
|
|
|
|
|
|
|
buffer = Encoding.UTF8.GetBytes("\n");
|
|
|
|
|
await response.Body.WriteAsync(buffer, 0, buffer.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task OnEventCompleted(HttpResponse response)
|
|
|
|
|
{
|
|
|
|
|
var buffer = Encoding.UTF8.GetBytes("data:[DONE]\n");
|
|
|
|
|
await response.Body.WriteAsync(buffer, 0, buffer.Length);
|
|
|
|
|
|
|
|
|
|
buffer = Encoding.UTF8.GetBytes("\n");
|
|
|
|
|
await response.Body.WriteAsync(buffer, 0, buffer.Length);
|
|
|
|
|
}
|
2023-08-19 00:15:02 +00:00
|
|
|
}
|