Merge pull request #315 from iceljc/features/save-rich-content-in-dialog
Features/save rich content in dialog
This commit is contained in:
commit
3513eabb67
|
|
@ -30,22 +30,24 @@ public class Conversation
|
|||
|
||||
public class DialogElement
|
||||
{
|
||||
public DialogMeta MetaData { get; set; }
|
||||
public DialogMetaData MetaData { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string? RichContent { get; set; }
|
||||
|
||||
public DialogElement()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DialogElement(DialogMeta meta, string content)
|
||||
public DialogElement(DialogMetaData meta, string content, string? richContent = null)
|
||||
{
|
||||
MetaData = meta;
|
||||
Content = content;
|
||||
RichContent = richContent;
|
||||
}
|
||||
}
|
||||
|
||||
public class DialogMeta
|
||||
public class DialogMetaData
|
||||
{
|
||||
public string Role { get; set; }
|
||||
public string AgentId { get; set; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
namespace BotSharp.Abstraction.Loggers.Enums;
|
||||
|
||||
public static class ContentLogSource
|
||||
{
|
||||
public const string UserInput = "user input";
|
||||
public const string Prompt = "prompt";
|
||||
public const string FunctionCall = "function call";
|
||||
public const string AgentResponse = "agent response";
|
||||
}
|
||||
|
|
@ -4,13 +4,19 @@ public class ConversationContentLogModel
|
|||
{
|
||||
[JsonPropertyName("conversation_id")]
|
||||
public string ConversationId { get; set; }
|
||||
|
||||
[JsonPropertyName("message_id")]
|
||||
public string MessageId { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[JsonPropertyName("role")]
|
||||
public string Role { get; set; }
|
||||
|
||||
[JsonPropertyName("source")]
|
||||
public string Source { get; set; }
|
||||
|
||||
[JsonPropertyName("content")]
|
||||
public string Content { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace BotSharp.Abstraction.Messaging.JsonConverters;
|
||||
|
|
@ -6,7 +7,22 @@ public class RichContentJsonConverter : JsonConverter<IRichMessage>
|
|||
{
|
||||
public override IRichMessage? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using var jsonDoc = JsonDocument.ParseValue(ref reader);
|
||||
var root = jsonDoc.RootElement;
|
||||
var jsonText = root.GetRawText();
|
||||
JsonElement element;
|
||||
object? res = null;
|
||||
|
||||
if (root.TryGetProperty("buttons", out element))
|
||||
{
|
||||
res = JsonSerializer.Deserialize<ButtonTemplateMessage>(jsonText, options);
|
||||
}
|
||||
else if (root.TryGetProperty("options", out element))
|
||||
{
|
||||
res = JsonSerializer.Deserialize<MultiSelectTemplateMessage>(jsonText, options);
|
||||
}
|
||||
|
||||
return res as IRichMessage;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, IRichMessage value, JsonSerializerOptions options)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace BotSharp.Abstraction.Messaging.JsonConverters;
|
||||
|
|
@ -6,7 +7,22 @@ public class TemplateMessageJsonConverter : JsonConverter<ITemplateMessage>
|
|||
{
|
||||
public override ITemplateMessage? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using var jsonDoc = JsonDocument.ParseValue(ref reader);
|
||||
var root = jsonDoc.RootElement;
|
||||
var jsonText = root.GetRawText();
|
||||
JsonElement element;
|
||||
object? res = null;
|
||||
|
||||
if (root.TryGetProperty("buttons", out element))
|
||||
{
|
||||
res = JsonSerializer.Deserialize<ButtonTemplateMessage>(jsonText, options);
|
||||
}
|
||||
else if (root.TryGetProperty("options", out element))
|
||||
{
|
||||
res = JsonSerializer.Deserialize<MultiSelectTemplateMessage>(jsonText, options);
|
||||
}
|
||||
|
||||
return res as ITemplateMessage;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ITemplateMessage value, JsonSerializerOptions options)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
using BotSharp.Abstraction.Messaging;
|
||||
using BotSharp.Abstraction.Messaging.JsonConverters;
|
||||
using BotSharp.Abstraction.Messaging.Models.RichContent;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
|
@ -8,12 +11,25 @@ public class ConversationStorage : IConversationStorage
|
|||
{
|
||||
private readonly BotSharpDatabaseSettings _dbSettings;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly JsonSerializerOptions _options;
|
||||
|
||||
public ConversationStorage(
|
||||
BotSharpDatabaseSettings dbSettings,
|
||||
IServiceProvider services)
|
||||
{
|
||||
_dbSettings = dbSettings;
|
||||
_services = services;
|
||||
_options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
AllowTrailingCommas = true,
|
||||
Converters =
|
||||
{
|
||||
new RichContentJsonConverter(),
|
||||
new TemplateMessageJsonConverter(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void Append(string conversationId, RoleDialogModel dialog)
|
||||
|
|
@ -24,7 +40,7 @@ public class ConversationStorage : IConversationStorage
|
|||
|
||||
if (dialog.Role == AgentRole.Function)
|
||||
{
|
||||
var meta = new DialogMeta
|
||||
var meta = new DialogMetaData
|
||||
{
|
||||
Role = dialog.Role,
|
||||
AgentId = agentId,
|
||||
|
|
@ -42,7 +58,7 @@ public class ConversationStorage : IConversationStorage
|
|||
}
|
||||
else
|
||||
{
|
||||
var meta = new DialogMeta
|
||||
var meta = new DialogMetaData
|
||||
{
|
||||
Role = dialog.Role,
|
||||
AgentId = agentId,
|
||||
|
|
@ -56,8 +72,8 @@ public class ConversationStorage : IConversationStorage
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dialogElements.Add(new DialogElement(meta, content));
|
||||
var richContent = dialog.RichContent != null ? JsonSerializer.Serialize(dialog.RichContent, _options) : null;
|
||||
dialogElements.Add(new DialogElement(meta, content, richContent));
|
||||
}
|
||||
|
||||
db.AppendConversationDialogs(conversationId, dialogElements);
|
||||
|
|
@ -80,6 +96,8 @@ public class ConversationStorage : IConversationStorage
|
|||
var function = role == AgentRole.Function ? meta.FunctionName : null;
|
||||
var senderId = role == AgentRole.Function ? currentAgentId : meta.SenderId;
|
||||
var createdAt = meta.CreateTime;
|
||||
var richContent = !string.IsNullOrEmpty(dialog.RichContent) ?
|
||||
JsonSerializer.Deserialize<RichContent<IRichMessage>>(dialog.RichContent, _options) : null;
|
||||
|
||||
var record = new RoleDialogModel(role, content)
|
||||
{
|
||||
|
|
@ -87,7 +105,8 @@ public class ConversationStorage : IConversationStorage
|
|||
MessageId = messageId,
|
||||
CreatedAt = createdAt,
|
||||
SenderId = senderId,
|
||||
FunctionName = function
|
||||
FunctionName = function,
|
||||
RichContent = richContent
|
||||
};
|
||||
results.Add(record);
|
||||
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ namespace BotSharp.Core.Repository
|
|||
var blocks = rawDialogs[i].Split("|");
|
||||
var content = rawDialogs[i + 1];
|
||||
var trimmed = content.Substring(4);
|
||||
var meta = new DialogMeta
|
||||
var meta = new DialogMetaData
|
||||
{
|
||||
Role = blocks[1],
|
||||
AgentId = blocks[2],
|
||||
|
|
@ -326,7 +326,13 @@ namespace BotSharp.Core.Repository
|
|||
SenderId = blocks[1] == AgentRole.Function ? null : blocks[4],
|
||||
CreateTime = DateTime.Parse(blocks[0])
|
||||
};
|
||||
dialogs.Add(new DialogElement(meta, trimmed));
|
||||
|
||||
string? richContent = null;
|
||||
if (blocks.Count() > 5)
|
||||
{
|
||||
richContent = blocks[5];
|
||||
}
|
||||
dialogs.Add(new DialogElement(meta, trimmed, richContent));
|
||||
}
|
||||
}
|
||||
return dialogs;
|
||||
|
|
@ -342,7 +348,7 @@ namespace BotSharp.Core.Repository
|
|||
var meta = element.MetaData;
|
||||
var createTime = meta.CreateTime.ToString("MM/dd/yyyy hh:mm:ss.fff tt", CultureInfo.InvariantCulture);
|
||||
var source = meta.FunctionName ?? meta.SenderId;
|
||||
var metaStr = $"{createTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{source}";
|
||||
var metaStr = $"{createTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{source}|{element.RichContent}";
|
||||
dialogTexts.Add(metaStr);
|
||||
var content = $" - {element.Content}";
|
||||
dialogTexts.Add(content);
|
||||
|
|
|
|||
|
|
@ -102,7 +102,8 @@ public class ConversationController : ControllerBase
|
|||
{
|
||||
FirstName = agent.Name,
|
||||
Role = message.Role,
|
||||
}
|
||||
},
|
||||
RichContent = message.RichContent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Loggers;
|
||||
using BotSharp.Abstraction.Loggers.Enums;
|
||||
using BotSharp.Abstraction.Loggers.Models;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Serilog;
|
||||
|
||||
namespace BotSharp.Plugin.ChatHub.Hooks;
|
||||
|
||||
|
|
@ -37,11 +37,13 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook
|
|||
AllowTrailingCommas = true
|
||||
};
|
||||
}
|
||||
|
||||
public override async Task OnMessageReceived(RoleDialogModel message)
|
||||
{
|
||||
var conversationId = _state.GetConversationId();
|
||||
var log = $"MessageId: {message.MessageId} ==>\r\n{message.Role}: {message.Content}";
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(conversationId, _user.UserName, log, message));
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated",
|
||||
BuildContentLog(conversationId, _user.UserName, log, ContentLogSource.UserInput, message));
|
||||
}
|
||||
|
||||
public async Task BeforeGenerating(Agent agent, List<RoleDialogModel> conversations)
|
||||
|
|
@ -62,7 +64,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook
|
|||
var agent = await agentService.LoadAgent(message.CurrentAgentId);
|
||||
var log = $"[{agent?.Name}]: {message.FunctionName}({message.FunctionArgs}) => {message.Content}";
|
||||
log += $"\r\n<== MessageId: {message.MessageId}";
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(conversationId, agent?.Name, log, message));
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated",
|
||||
BuildContentLog(conversationId, agent?.Name, log, ContentLogSource.FunctionCall, message));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -78,30 +81,25 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook
|
|||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var conversationId = _state.GetConversationId();
|
||||
var agent = await agentService.LoadAgent(message.CurrentAgentId);
|
||||
var logSource = string.Empty;
|
||||
|
||||
// Log routing output
|
||||
try
|
||||
{
|
||||
var inst = message.Content.JsonContent<FunctionCallFromLlm>();
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(conversationId, agent?.Name, message.Content, message));
|
||||
logSource = ContentLogSource.AgentResponse;
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated",
|
||||
BuildContentLog(conversationId, agent?.Name, message.Content, logSource, message));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
string log;
|
||||
if (message.Role == AgentRole.Function)
|
||||
{
|
||||
log = $"[{agent?.Name}]: {message.FunctionName}({message.FunctionArgs}) => {message.Content}";
|
||||
log += $"\r\n<== MessageId: {message.MessageId}";
|
||||
}
|
||||
else
|
||||
{
|
||||
log = tokenStats.Prompt;
|
||||
}
|
||||
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(conversationId, agent?.Name, log, message));
|
||||
var log = tokenStats.Prompt;
|
||||
logSource = ContentLogSource.Prompt;
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated",
|
||||
BuildContentLog(conversationId, agent?.Name, log, logSource, message));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -127,11 +125,12 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook
|
|||
log += $"\r\n{richContent}";
|
||||
}
|
||||
log += $"\r\n<== MessageId: {message.MessageId}";
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(conv.ConversationId, agent?.Name, log, message));
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated",
|
||||
BuildContentLog(conv.ConversationId, agent?.Name, log, ContentLogSource.AgentResponse, message));
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildContentLog(string conversationId, string? name, string content, RoleDialogModel message)
|
||||
private string BuildContentLog(string conversationId, string? name, string logContent, string logSource, RoleDialogModel message)
|
||||
{
|
||||
var log = new ConversationContentLogModel
|
||||
{
|
||||
|
|
@ -139,7 +138,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook
|
|||
MessageId = message.MessageId,
|
||||
Name = name,
|
||||
Role = message.Role,
|
||||
Content = content,
|
||||
Content = logContent,
|
||||
Source = logSource,
|
||||
CreateTime = DateTime.UtcNow
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ public class ConversationContentLogDocument : MongoBase
|
|||
public string MessageId { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string Role { get; set; }
|
||||
public string Source { get; set; }
|
||||
public string Content { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ namespace BotSharp.Plugin.MongoStorage.Models;
|
|||
|
||||
public class DialogMongoElement
|
||||
{
|
||||
public DialogMetaMongoElement MetaData { get; set; }
|
||||
public DialogMetaDataMongoElement MetaData { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string? RichContent { get; set; }
|
||||
|
||||
public DialogMongoElement()
|
||||
{
|
||||
|
|
@ -16,8 +17,9 @@ public class DialogMongoElement
|
|||
{
|
||||
return new DialogMongoElement
|
||||
{
|
||||
MetaData = DialogMetaMongoElement.ToMongoElement(dialog.MetaData),
|
||||
Content = dialog.Content
|
||||
MetaData = DialogMetaDataMongoElement.ToMongoElement(dialog.MetaData),
|
||||
Content = dialog.Content,
|
||||
RichContent = dialog.RichContent
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -25,13 +27,14 @@ public class DialogMongoElement
|
|||
{
|
||||
return new DialogElement
|
||||
{
|
||||
MetaData = DialogMetaMongoElement.ToDomainElement(dialog.MetaData),
|
||||
Content = dialog.Content
|
||||
MetaData = DialogMetaDataMongoElement.ToDomainElement(dialog.MetaData),
|
||||
Content = dialog.Content,
|
||||
RichContent = dialog.RichContent
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class DialogMetaMongoElement
|
||||
public class DialogMetaDataMongoElement
|
||||
{
|
||||
public string Role { get; set; }
|
||||
public string AgentId { get; set; }
|
||||
|
|
@ -40,14 +43,14 @@ public class DialogMetaMongoElement
|
|||
public string? SenderId { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
public DialogMetaMongoElement()
|
||||
public DialogMetaDataMongoElement()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static DialogMeta ToDomainElement(DialogMetaMongoElement meta)
|
||||
public static DialogMetaData ToDomainElement(DialogMetaDataMongoElement meta)
|
||||
{
|
||||
return new DialogMeta
|
||||
return new DialogMetaData
|
||||
{
|
||||
Role = meta.Role,
|
||||
AgentId = meta.AgentId,
|
||||
|
|
@ -58,9 +61,9 @@ public class DialogMetaMongoElement
|
|||
};
|
||||
}
|
||||
|
||||
public static DialogMetaMongoElement ToMongoElement(DialogMeta meta)
|
||||
public static DialogMetaDataMongoElement ToMongoElement(DialogMetaData meta)
|
||||
{
|
||||
return new DialogMetaMongoElement
|
||||
return new DialogMetaDataMongoElement
|
||||
{
|
||||
Role = meta.Role,
|
||||
AgentId = meta.AgentId,
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ public partial class MongoRepository
|
|||
MessageId = messageId,
|
||||
Name = log.Name,
|
||||
Role = log.Role,
|
||||
Source = log.Source,
|
||||
Content = log.Content,
|
||||
CreateTime = log.CreateTime
|
||||
};
|
||||
|
|
@ -91,6 +92,7 @@ public partial class MongoRepository
|
|||
MessageId = x.MessageId,
|
||||
Name = x.Name,
|
||||
Role = x.Role,
|
||||
Source = x.Source,
|
||||
Content = x.Content,
|
||||
CreateTime = x.CreateTime
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue