Add onMessageReceived to support streaming.
This commit is contained in:
parent
a87ec531b5
commit
842880dbbf
|
|
@ -46,8 +46,8 @@ public abstract class ConversationCompletionHookBase : IConversationCompletionHo
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task<string> AfterCompletion(string response)
|
||||
public virtual Task AfterCompletion(RoleDialogModel message)
|
||||
{
|
||||
return Task.FromResult(response);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,5 +18,5 @@ public interface IConversationCompletionHook
|
|||
IConversationCompletionHook SetChatCompletion(IChatCompletion chatCompletion);
|
||||
|
||||
Task BeforeCompletion();
|
||||
Task<string> AfterCompletion(string response);
|
||||
Task AfterCompletion(RoleDialogModel message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ public interface IConversationService
|
|||
Task<Conversation> GetConversation(string id);
|
||||
Task<List<Conversation>> GetConversations();
|
||||
Task DeleteConversation(string id);
|
||||
Task<string> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog);
|
||||
Task<string> SendMessage(string agentId, string conversationId, List<RoleDialogModel> wholeDialogs);
|
||||
Task<bool> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog, Func<RoleDialogModel, Task> onMessageReceived);
|
||||
Task<bool> SendMessage(string agentId, string conversationId, List<RoleDialogModel> wholeDialogs, Func<RoleDialogModel, Task> onMessageReceived);
|
||||
List<RoleDialogModel> GetDialogHistory(string agentId, string conversationId);
|
||||
Task CleanHistory(string agentId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,16 @@ namespace BotSharp.Abstraction.Conversations.Models;
|
|||
public class RoleDialogModel
|
||||
{
|
||||
/// <summary>
|
||||
/// user, system, assistant
|
||||
/// user, system, assistant, function
|
||||
/// </summary>
|
||||
public string Role { get; set; }
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Function name if LLM response function call
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
public RoleDialogModel(string role, string text)
|
||||
{
|
||||
Role = role;
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@ namespace BotSharp.Abstraction.MLTasks;
|
|||
public interface IChatCompletion
|
||||
{
|
||||
string GetChatCompletions(Agent agent, List<RoleDialogModel> conversations);
|
||||
Task<string> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations);
|
||||
Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,11 +46,13 @@ public class ConversationController : ControllerBase, IApiAdapter
|
|||
{
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
|
||||
var result = await conv.SendMessage(agentId, conversationId, new RoleDialogModel("user", input.Text));
|
||||
var response = new MessageResponseModel();
|
||||
|
||||
return new MessageResponseModel
|
||||
await conv.SendMessage(agentId, conversationId, new RoleDialogModel("user", input.Text), async msg =>
|
||||
{
|
||||
Text = result
|
||||
};
|
||||
response.Text += msg.Content;
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,20 +67,22 @@ public class ConversationService : IConversationService
|
|||
return record.ToConversation();
|
||||
}
|
||||
|
||||
public async Task<string> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog)
|
||||
public async Task<bool> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
_storage.Append(agentId, conversationId, lastDalog);
|
||||
|
||||
var wholeDialogs = GetDialogHistory(agentId, conversationId);
|
||||
|
||||
var response = await SendMessage(agentId, conversationId, wholeDialogs);
|
||||
|
||||
_storage.Append(agentId, conversationId, new RoleDialogModel("assistant", response));
|
||||
var response = await SendMessage(agentId, conversationId, wholeDialogs, async msg =>
|
||||
{
|
||||
await onMessageReceived(msg);
|
||||
_storage.Append(agentId, conversationId, new RoleDialogModel(msg.Role, msg.Content));
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<string> SendMessage(string agentId, string conversationId, List<RoleDialogModel> wholeDialogs)
|
||||
public async Task<bool> SendMessage(string agentId, string conversationId, List<RoleDialogModel> wholeDialogs, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
var agent = await _services.GetRequiredService<IAgentService>().GetAgent(agentId);
|
||||
var converation = await GetConversation(conversationId);
|
||||
|
|
@ -110,15 +112,14 @@ public class ConversationService : IConversationService
|
|||
.BeforeCompletion();
|
||||
});
|
||||
|
||||
var response = await chatCompletion.GetChatCompletionsStreamingAsync(agent, wholeDialogs);
|
||||
|
||||
// After chat completion hook
|
||||
hooks.ForEach(async hook =>
|
||||
var result = await chatCompletion.GetChatCompletionsStreamingAsync(agent, wholeDialogs, async msg =>
|
||||
{
|
||||
response = await hook.AfterCompletion(response);
|
||||
// After chat completion hook
|
||||
hooks.ForEach(async hook => await hook.AfterCompletion(msg));
|
||||
await onMessageReceived(msg);
|
||||
});
|
||||
|
||||
return response;
|
||||
return result;
|
||||
}
|
||||
|
||||
public IChatCompletion GetChatCompletion()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations)
|
||||
public async Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
string totalResponse = "";
|
||||
var content = string.Join("\n", conversations.Select(x => $"{x.Role}: {x.Content.Replace("user:", "")}")).Trim();
|
||||
|
|
@ -36,6 +36,6 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
totalResponse += response;
|
||||
}
|
||||
|
||||
return Task.FromResult(totalResponse.Trim());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
return functions;
|
||||
}
|
||||
|
||||
public async Task<string> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations)
|
||||
public async Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
var client = new OpenAIClient(new Uri(_settings.Endpoint), new AzureKeyCredential(_settings.ApiKey));
|
||||
var chatCompletionsOptions = PrepareOptions(agent, conversations);
|
||||
|
|
@ -97,16 +97,23 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
string output = "";
|
||||
await foreach (var choice in streaming.GetChoicesStreaming())
|
||||
{
|
||||
if (choice.FinishReason == CompletionsFinishReason.FunctionCall)
|
||||
{
|
||||
}
|
||||
|
||||
await foreach (var message in choice.GetMessageStreaming())
|
||||
{
|
||||
if (message.Content == null)
|
||||
continue;
|
||||
Console.Write(message.Content);
|
||||
output += message.Content;
|
||||
await onMessageReceived(new RoleDialogModel(message.Role.ToString(), message.Content));
|
||||
}
|
||||
|
||||
output = "";
|
||||
}
|
||||
|
||||
return output.Trim();
|
||||
return true;
|
||||
}
|
||||
|
||||
private ChatCompletionsOptions PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
|
||||
|
|
|
|||
|
|
@ -78,22 +78,24 @@ public class ChatbotUiController : ControllerBase, IApiAdapter
|
|||
converation = await conversationService.NewConversation(sess);
|
||||
}
|
||||
|
||||
var result = await conversationService.SendMessage(input.AgentId, input.ConversationId, conversations);
|
||||
var result = await conversationService.SendMessage(input.AgentId,
|
||||
input.ConversationId,
|
||||
conversations,
|
||||
async msg =>
|
||||
await OnChunkReceived(outputStream, msg));
|
||||
|
||||
await OnChunkReceived(outputStream, result);
|
||||
await OnEventCompleted(outputStream);
|
||||
}
|
||||
|
||||
private async Task OnChunkReceived(Stream outputStream, string content)
|
||||
private async Task OnChunkReceived(Stream outputStream, RoleDialogModel message)
|
||||
{
|
||||
|
||||
var response = new OpenAiChatOutput
|
||||
{
|
||||
Choices = new List<OpenAiChoice>
|
||||
{
|
||||
new OpenAiChoice
|
||||
{
|
||||
Delta = new RoleDialogModel("assistant", content)
|
||||
Delta = new RoleDialogModel(message.Role, message.Content)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -105,7 +107,7 @@ public class ChatbotUiController : ControllerBase, IApiAdapter
|
|||
|
||||
var buffer = Encoding.UTF8.GetBytes($"data:{json}\n");
|
||||
await outputStream.WriteAsync(buffer, 0, buffer.Length);
|
||||
await Task.Delay(100);
|
||||
await Task.Delay(10);
|
||||
|
||||
buffer = Encoding.UTF8.GetBytes("\n");
|
||||
await outputStream.WriteAsync(buffer, 0, buffer.Length);
|
||||
|
|
|
|||
Loading…
Reference in a new issue