Merge pull request #93 from hchen2020/master

Adjust conversation history format.
This commit is contained in:
Haiping 2023-08-08 05:52:41 -05:00 committed by GitHub
commit 4c3cb6bf8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 119 additions and 39 deletions

View file

@ -8,7 +8,7 @@ public interface IConversationService
Task<Conversation> GetConversation(string id);
Task<List<Conversation>> GetConversations();
Task DeleteConversation(string id);
Task<bool> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog, Func<RoleDialogModel, Task> onMessageReceived);
Task<bool> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog, Func<RoleDialogModel, Task> onMessageReceived, Func<RoleDialogModel, Task> onFunctionExecuting);
Task<bool> SendMessage(string agentId, string conversationId, List<RoleDialogModel> wholeDialogs, Func<RoleDialogModel, Task> onMessageReceived);
List<RoleDialogModel> GetDialogHistory(string agentId, string conversationId, int lastCount = 20);
Task CleanHistory(string agentId);

View file

@ -6,6 +6,7 @@ public class RoleDialogModel
/// user, system, assistant, function
/// </summary>
public string Role { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public string Content { get; set; }
/// <summary>

View file

@ -1,19 +0,0 @@
using System.Text.Json.Serialization;
namespace BotSharp.Abstraction.Functions.Models;
public class FunctionExecutionResult<T> where T : new()
{
private readonly string _name;
public FunctionExecutionResult(string name)
{
_name = name;
}
[JsonPropertyName("function_name")]
public string Name => _name;
[JsonPropertyName("execution_result")]
public T Result { get; set; } = new T();
}

View file

@ -19,5 +19,6 @@ public class FunctionExecutionValidationResult
public string ValidationStatus { get; set; }
[JsonPropertyName("validation_message")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string ValidationMessage { get; set; }
}

View file

@ -51,6 +51,9 @@ public class ConversationController : ControllerBase, IApiAdapter
await conv.SendMessage(agentId, conversationId, new RoleDialogModel("user", input.Text), async msg =>
{
response.Text += msg.Content;
}, async fn =>
{
});
return response;

View file

@ -67,7 +67,9 @@ public class ConversationService : IConversationService
return record.ToConversation();
}
public async Task<bool> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog, Func<RoleDialogModel, Task> onMessageReceived)
public async Task<bool> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting)
{
_storage.Append(agentId, conversationId, lastDalog);
@ -81,6 +83,8 @@ public class ConversationService : IConversationService
var functions = _services.GetServices<IFunctionCallback>().Where(x => x.Name == msg.FunctionName);
foreach (var fn in functions)
{
await onFunctionExecuting(msg);
msg.ExecutionResult = await fn.Execute(msg.Content);
var result = msg.ExecutionResult.Replace("\r", " ").Replace("\n", " ");

View file

@ -14,20 +14,34 @@ public class ConversationStorage : IConversationStorage
public void Append(string agentId, string conversationId, RoleDialogModel dialog)
{
var conversationFile = GetStorageFile(agentId, conversationId);
File.AppendAllText(conversationFile, $"{dialog.Role}: {dialog.Content}\n");
var sb = new StringBuilder();
sb.AppendLine($"{dialog.Role}|{dialog.CreatedAt}|{dialog.FunctionName}");
sb.AppendLine($" - {dialog.Content}");
var conversation = sb.ToString();
File.AppendAllText(conversationFile, conversation);
}
public List<RoleDialogModel> GetDialogs(string agentId, string conversationId)
{
var conversationFile = GetStorageFile(agentId, conversationId);
var dialogs = File.ReadAllLines(conversationFile);
return dialogs.Select(x =>
var results = new List<RoleDialogModel>();
for (int i = 0; i < dialogs.Length; i += 2)
{
var pos = x.IndexOf(':');
var role = x.Substring(0, pos);
var text = x.Substring(pos + 1);
return new RoleDialogModel(role, text);
}).ToList();
var meta = dialogs[i];
var dialog = dialogs[i + 1];
var role = meta.Split('|')[0];
var createdAt = DateTime.Parse(meta.Split('|')[1]);
var text = dialog.Substring(4);
var funcName = meta.Split('|')[2];
results.Add(new RoleDialogModel(role, text)
{
FunctionName = funcName,
CreatedAt = createdAt
});
}
return results;
}
public void InitStorage(string agentId, string conversationId)

View file

@ -5,8 +5,10 @@ using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Plugin.AzureOpenAI.Settings;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
@ -15,10 +17,12 @@ namespace BotSharp.Plugin.AzureOpenAI.Providers;
public class ChatCompletionProvider : IChatCompletion
{
private readonly AzureOpenAiSettings _settings;
private readonly ILogger _logger;
public ChatCompletionProvider(AzureOpenAiSettings settings)
public ChatCompletionProvider(AzureOpenAiSettings settings, ILogger<ChatCompletionProvider> logger)
{
_settings = settings;
_logger = logger;
}
public string GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
@ -38,6 +42,8 @@ public class ChatCompletionProvider : IChatCompletion
output += message.Content;
}
_logger.LogInformation(output);
return output.Trim();
}
@ -113,9 +119,7 @@ public class ChatCompletionProvider : IChatCompletion
await onMessageReceived(funcContextIn);
// After function is executed, pass the result to LLM
var fnResult = JsonSerializer.Deserialize<FunctionExecutionResult<object>>(funcContextIn.ExecutionResult);
var fnJsonResult = JsonSerializer.Serialize(fnResult.Result);
chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.Function, fnJsonResult)
chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.Function, funcContextIn.ExecutionResult)
{
Name = funcContextIn.FunctionName
});
@ -124,6 +128,9 @@ public class ChatCompletionProvider : IChatCompletion
choice = response.Value.Choices[0];
message = choice.Message;
_logger.LogInformation(message.Content);
await onMessageReceived(new RoleDialogModel(ChatRole.Assistant.ToString(), message.Content));
return true;
@ -161,6 +168,9 @@ public class ChatCompletionProvider : IChatCompletion
continue;
Console.Write(message.Content);
output += message.Content;
_logger.LogInformation(message.Content);
await onMessageReceived(new RoleDialogModel(message.Role.ToString(), message.Content));
}
@ -205,10 +215,9 @@ public class ChatCompletionProvider : IChatCompletion
{
if (message.Role == ChatRole.Function)
{
var funcContext = JsonSerializer.Deserialize<FunctionExecutionResult<object>>(message.Content);
chatCompletionsOptions.Messages.Add(new ChatMessage(message.Role, message.Content)
{
Name = funcContext.Name
Name = message.FunctionName
});
}
else
@ -217,6 +226,7 @@ public class ChatCompletionProvider : IChatCompletion
}
}
_logger.LogInformation(string.Join("\n", chatCompletionsOptions.Messages.Select(x => $"{x.Role}: {x.Content}")));
return chatCompletionsOptions;
}
}

View file

@ -14,6 +14,7 @@ using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Refit;
namespace BotSharp.Plugin.MetaMessenger.Controllers;
@ -58,7 +59,6 @@ public class WebhookController : ControllerBase
// TODO validate request
// https://developers.facebook.com/docs/messenger-platform/webhooks#verification-requests
try
{
// received message
@ -69,10 +69,6 @@ public class WebhookController : ControllerBase
string content = "";
var sessionId = req.Entry[0].Messaging[0].Sender.Id;
var input = req.Entry[0].Messaging[0].Message.Text;
var result = await conv.SendMessage(agentId, sessionId, new RoleDialogModel("user", input), async msg =>
{
content = msg.Content;
});
var setting = _services.GetRequiredService<MetaMessengerSetting>();
var messenger = _services.GetRequiredService<IMessengerGraphAPI>();
@ -80,14 +76,65 @@ public class WebhookController : ControllerBase
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
// Marking seen
/*await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest
{
AccessToken = setting.PageAccessToken,
Recipient = JsonSerializer.Serialize(new { Id = sessionId }, jsonOpt),
SenderAction = SenderActionEnum.MarkSeen
});*/
// Typing on
await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest
{
AccessToken = setting.PageAccessToken,
Recipient = JsonSerializer.Serialize(new { Id = sessionId }, jsonOpt),
SenderAction = SenderActionEnum.TypingOn
});
// Go to LLM
var result = await conv.SendMessage(agentId, sessionId, new RoleDialogModel("user", input), async msg =>
{
content = msg.Content;
}, async fn =>
{
await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest
{
AccessToken = setting.PageAccessToken,
Recipient = JsonSerializer.Serialize(new { Id = sessionId }, jsonOpt),
Message = JsonSerializer.Serialize(new { Text = "I'm pulling the relevent information, please wait a second ..." }, jsonOpt)
});
await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest
{
AccessToken = setting.PageAccessToken,
Recipient = JsonSerializer.Serialize(new { Id = sessionId }, jsonOpt),
SenderAction = SenderActionEnum.TypingOn
});
});
// Response to user
await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest
{
AccessToken = setting.PageAccessToken,
Recipient = JsonSerializer.Serialize(new { Id = sessionId }, jsonOpt),
Message = JsonSerializer.Serialize(new { Text = content }, jsonOpt)
});
// Typing off
await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest
{
AccessToken = setting.PageAccessToken,
Recipient = JsonSerializer.Serialize(new { Id = sessionId }, jsonOpt),
SenderAction = SenderActionEnum.TypingOff
});
}
}
catch (ApiException ex)
{
Console.WriteLine(ex.Content);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());

View file

@ -0,0 +1,13 @@
using System.Runtime.Serialization;
namespace BotSharp.Plugin.MetaMessenger.MessagingModels;
public enum SenderActionEnum
{
[EnumMember(Value = "typing_on")]
TypingOn,
[EnumMember(Value = "typing_off")]
TypingOff,
[EnumMember(Value = "mark_seen")]
MarkSeen
}

View file

@ -24,4 +24,7 @@ public class SendingMessageRequest
[AliasAs("messaging_type")]
public string MessagingType { get; set; } = "RESPONSE";
[AliasAs("sender_action")]
public SenderActionEnum? SenderAction { get; set; }
}

View file

@ -59,6 +59,9 @@ namespace BotSharp.Plugin.WeChat
var result = await conversationService.SendMessage(AgentId, latestConversationId, new RoleDialogModel("user", message), async msg =>
{
await ReplyTextMessageAsync(openid, msg.Content);
}, async fn =>
{
});
}