refine json format in log

This commit is contained in:
Jicheng Lu 2024-07-23 17:44:39 -05:00
parent aee41a0ac7
commit 75f8d5b3ff
2 changed files with 147 additions and 43 deletions

View file

@ -9,8 +9,18 @@ public class ChatHubConversationHook : ConversationHookBase
private readonly IServiceProvider _services;
private readonly IHubContext<SignalRHub> _chatHub;
private readonly IUserIdentity _user;
private readonly BotSharpOptions _options;
public ChatHubConversationHook(IServiceProvider services,
private readonly BotSharpOptions _options;
#region Event
private const string INIT_CLIENT_CONVERSATION = "OnConversationInitFromClient";
private const string RECEIVE_CLIENT_MESSAGE = "OnMessageReceivedFromClient";
private const string RECEIVE_ASSISTANT_MESSAGE = "OnMessageReceivedFromAssistant";
private const string GENERATE_SENDER_ACTION = "OnSenderActionGenerated";
private const string DELETE_MESSAGE = "OnMessageDeleted";
#endregion
public ChatHubConversationHook(
IServiceProvider services,
IHubContext<SignalRHub> chatHub,
BotSharpOptions options,
IUserIdentity user)
@ -29,8 +39,7 @@ public class ChatHubConversationHook : ConversationHookBase
var user = await userService.GetUser(conv.User.Id);
conv.User = UserViewModel.FromUser(user);
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationInitFromClient", conv);
await InitClientConversation(conv);
await base.OnConversationInitialized(conversation);
}
@ -41,35 +50,36 @@ public class ChatHubConversationHook : ConversationHookBase
var sender = await userService.GetMyProfile();
// Update console conversation UI for CSR
await _chatHub.Clients.User(_user.Id).SendAsync("OnMessageReceivedFromClient", new ChatResponseModel()
var model = new ChatResponseModel()
{
ConversationId = conv.ConversationId,
MessageId = message.MessageId,
Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
Sender = UserViewModel.FromUser(sender)
});
};
await ReceiveClientMessage(model);
// Send typing-on to client
await _chatHub.Clients.User(_user.Id).SendAsync("OnSenderActionGenerated", new ConversationSenderActionModel
var action = new ConversationSenderActionModel
{
ConversationId = conv.ConversationId,
SenderAction = SenderActionEnum.TypingOn
});
};
await GenerateSenderAction(action);
await base.OnMessageReceived(message);
}
public override async Task OnFunctionExecuting(RoleDialogModel message)
{
var conv = _services.GetRequiredService<IConversationService>();
await _chatHub.Clients.User(_user.Id).SendAsync("OnSenderActionGenerated", new ConversationSenderActionModel
var action = new ConversationSenderActionModel
{
ConversationId = conv.ConversationId,
SenderAction = SenderActionEnum.TypingOn,
Indication = message.Indication
});
};
await GenerateSenderAction(action);
await base.OnFunctionExecuting(message);
}
@ -98,23 +108,52 @@ public class ChatHubConversationHook : ConversationHookBase
}, _options.JsonSerializerOptions);
// Send typing-off to client
await _chatHub.Clients.User(_user.Id).SendAsync("OnSenderActionGenerated", new ConversationSenderActionModel
var action = new ConversationSenderActionModel
{
ConversationId = conv.ConversationId,
SenderAction = SenderActionEnum.TypingOff
});
await _chatHub.Clients.User(_user.Id).SendAsync("OnMessageReceivedFromAssistant", json);
};
await GenerateSenderAction(action);
await ReceiveAssistantMessage(json);
await base.OnResponseGenerated(message);
}
public override async Task OnMessageDeleted(string conversationId, string messageId)
{
await _chatHub.Clients.User(_user.Id).SendAsync("OnMessageDeleted", new ChatResponseModel
var model = new ChatResponseModel
{
ConversationId = conversationId,
MessageId = messageId
});
};
await DeleteMessage(model);
await base.OnMessageDeleted(conversationId, messageId);
}
#region Private methods
private async Task InitClientConversation(ConversationViewModel conversation)
{
await _chatHub.Clients.User(_user.Id).SendAsync(INIT_CLIENT_CONVERSATION, conversation);
}
private async Task ReceiveClientMessage(ChatResponseModel model)
{
await _chatHub.Clients.User(_user.Id).SendAsync(RECEIVE_CLIENT_MESSAGE, model);
}
private async Task ReceiveAssistantMessage(string? json)
{
await _chatHub.Clients.User(_user.Id).SendAsync(RECEIVE_ASSISTANT_MESSAGE, json);
}
private async Task GenerateSenderAction(ConversationSenderActionModel action)
{
await _chatHub.Clients.User(_user.Id).SendAsync(GENERATE_SENDER_ACTION, action);
}
private async Task DeleteMessage(ChatResponseModel model)
{
await _chatHub.Clients.User(_user.Id).SendAsync(DELETE_MESSAGE, model);
}
#endregion
}

View file

@ -1,5 +1,4 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Loggers;
using BotSharp.Abstraction.Loggers.Enums;
@ -10,6 +9,9 @@ using BotSharp.Abstraction.Routing;
using Microsoft.AspNetCore.SignalR;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using Newtonsoft.Json.Linq;
using JsonConvert = Newtonsoft.Json.JsonConvert;
using JsonSerializerSettings = Newtonsoft.Json.JsonSerializerSettings;
namespace BotSharp.Plugin.ChatHub.Hooks;
@ -25,6 +27,13 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
private readonly IAgentService _agentService;
private readonly IRoutingContext _routingCtx;
#region Event
private const string CONTENT_LOG_GENERATED = "OnConversationContentLogGenerated";
private const string STATE_LOG_GENERATED = "OnConversateStateLogGenerated";
private const string AGENT_QUEUE_CHANGED = "OnAgentQueueChanged";
private const string STATE_CHANGED = "OnStateChangeGenerated";
#endregion
public StreamingLogHook(
ConversationSetting convSettings,
BotSharpOptions options,
@ -60,7 +69,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.UserInput,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public override async Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg)
@ -78,7 +87,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.UserInput,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public async Task OnRenderingTemplate(Agent agent, string name, string content)
@ -100,7 +109,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public async Task BeforeGenerating(Agent agent, List<RoleDialogModel> conversations)
@ -117,7 +126,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
var agent = await _agentService.LoadAgent(message.CurrentAgentId);
message.FunctionArgs = message.FunctionArgs ?? "{}";
var args = JsonSerializer.Serialize(JsonDocument.Parse(message.FunctionArgs), _options.JsonSerializerOptions);
var args = FormatJson(message.FunctionArgs);
var log = $"{message.FunctionName} <u>executing</u>\r\n```json\r\n{args}\r\n```";
var input = new ContentLogInputModel(conversationId, message)
@ -127,7 +136,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public override async Task OnFunctionExecuted(RoleDialogModel message)
@ -139,7 +148,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
var agent = await _agentService.LoadAgent(message.CurrentAgentId);
message.FunctionArgs = message.FunctionArgs ?? "{}";
// var args = JsonSerializer.Serialize(JsonDocument.Parse(message.FunctionArgs), _options.JsonSerializerOptions);
var log = $"{message.FunctionName} =>\r\n*{message.Content?.Trim()}*";
var input = new ContentLogInputModel(conversationId, message)
@ -149,7 +157,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
/// <summary>
@ -176,7 +184,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.Prompt,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
/// <summary>
@ -190,7 +198,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
if (string.IsNullOrEmpty(conversationId)) return;
var conv = _services.GetRequiredService<IConversationService>();
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversateStateLogGenerated", BuildStateLog(conv.ConversationId, _state.GetStates(), message));
await SendStateLog(conv.ConversationId, _state.GetStates(), message);
if (message.Role == AgentRole.Assistant)
{
@ -209,7 +217,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.AgentResponse,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
}
@ -227,7 +235,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public override async Task OnConversationEnding(RoleDialogModel message)
@ -244,7 +252,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public override async Task OnBreakpointUpdated(string conversationId, bool resetStates)
@ -272,7 +280,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
},
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public override async Task OnStateChanged(StateChangeModel stateChange)
@ -282,7 +290,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
if (stateChange == null) return;
await _chatHub.Clients.User(_user.Id).SendAsync("OnStateChangeGenerated", BuildStateChangeLog(stateChange));
await SendStateChange(stateChange);
}
#endregion
@ -296,7 +304,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"{agent.Name} is enqueued";
await _chatHub.Clients.User(_user.Id).SendAsync("OnAgentQueueChanged", BuildAgentQueueChangedLog(conversationId, log));
await SendAgentQueueLog(conversationId, log);
// Content log
log = $"{agent.Name} is enqueued{(reason != null ? $" ({reason})" : "")}";
@ -311,7 +319,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public async Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null)
@ -324,7 +332,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"{agent.Name} is dequeued";
await _chatHub.Clients.User(_user.Id).SendAsync("OnAgentQueueChanged", BuildAgentQueueChangedLog(conversationId, log));
await SendAgentQueueLog(conversationId, log);
// Content log
log = $"{agent.Name} is dequeued{(reason != null ? $" ({reason})" : "")}, current agent is {currentAgent?.Name}";
@ -339,7 +347,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public async Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null)
@ -352,7 +360,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"Agent queue is replaced from {fromAgent.Name} to {toAgent.Name}";
await _chatHub.Clients.User(_user.Id).SendAsync("OnAgentQueueChanged", BuildAgentQueueChangedLog(conversationId, log));
await SendAgentQueueLog(conversationId, log);
// Content log
log = $"{fromAgent.Name} is replaced to {toAgent.Name}{(reason != null ? $" ({reason})" : "")}";
@ -367,7 +375,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public async Task OnAgentQueueEmptied(string agentId, string? reason = null)
@ -377,7 +385,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"Agent queue is empty";
await _chatHub.Clients.User(_user.Id).SendAsync("OnAgentQueueChanged", BuildAgentQueueChangedLog(conversationId, log));
await SendAgentQueueLog(conversationId, log);
// Content log
log = reason ?? "Agent queue is cleared";
@ -392,7 +400,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public async Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message)
@ -411,7 +419,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.AgentResponse,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
public async Task OnRoutingInstructionRevised(FunctionCallFromLlm instruct, RoleDialogModel message)
@ -429,11 +437,32 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
await SendContentLog(input);
}
#endregion
#region Private methods
private async Task SendContentLog(ContentLogInputModel input)
{
await _chatHub.Clients.User(_user.Id).SendAsync(CONTENT_LOG_GENERATED, BuildContentLog(input));
}
private async Task SendStateLog(string conversationId, Dictionary<string, string> states, RoleDialogModel message)
{
await _chatHub.Clients.User(_user.Id).SendAsync(STATE_LOG_GENERATED, BuildStateLog(conversationId, states, message));
}
private async Task SendAgentQueueLog(string conversationId, string log)
{
await _chatHub.Clients.User(_user.Id).SendAsync(AGENT_QUEUE_CHANGED, BuildAgentQueueChangedLog(conversationId, log));
}
private async Task SendStateChange(StateChangeModel stateChange)
{
await _chatHub.Clients.User(_user.Id).SendAsync(STATE_CHANGED, BuildStateChangeLog(stateChange));
}
private string BuildContentLog(ContentLogInputModel input)
{
var output = new ContentLogOutputModel
@ -538,4 +567,40 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
return localOptions;
}
private string FormatJson(string? json)
{
var defaultJson = "{}";
if (string.IsNullOrWhiteSpace(json))
{
return defaultJson;
}
try
{
var parsedJson = JObject.Parse(json);
foreach (var item in parsedJson)
{
try
{
var key = item.Key;
var value = parsedJson[key].ToString();
var parsedValue = JObject.Parse(value);
parsedJson[key] = parsedValue;
}
catch { continue; }
}
var jsonSettings = new JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.Indented
};
return JsonConvert.SerializeObject(parsedJson, jsonSettings) ?? defaultJson;
}
catch
{
return defaultJson;
}
}
#endregion
}