BotSharp/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs

307 lines
11 KiB
C#
Raw Normal View History

2024-01-10 14:18:59 +00:00
using BotSharp.Abstraction.Agents.Models;
2024-02-20 23:13:44 +00:00
using BotSharp.Abstraction.Functions.Models;
2024-01-10 14:18:59 +00:00
using BotSharp.Abstraction.Loggers;
2024-02-22 17:25:13 +00:00
using BotSharp.Abstraction.Loggers.Enums;
2024-01-19 04:38:44 +00:00
using BotSharp.Abstraction.Loggers.Models;
2024-02-15 20:43:36 +00:00
using BotSharp.Abstraction.Repositories;
2024-02-28 16:21:14 +00:00
using BotSharp.Abstraction.Routing;
2024-01-10 14:18:59 +00:00
using Microsoft.AspNetCore.SignalR;
namespace BotSharp.Plugin.ChatHub.Hooks;
2024-02-28 16:21:14 +00:00
public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IRoutingHook
2024-01-10 14:18:59 +00:00
{
private readonly ConversationSetting _convSettings;
2024-01-24 23:47:57 +00:00
private readonly JsonSerializerOptions _serializerOptions;
2024-01-10 14:18:59 +00:00
private readonly IServiceProvider _services;
private readonly IHubContext<SignalRHub> _chatHub;
2024-01-24 23:47:57 +00:00
private readonly IConversationStateService _state;
private readonly IUserIdentity _user;
2024-02-23 01:53:00 +00:00
private readonly IAgentService _agentService;
2024-02-28 20:40:59 +00:00
private readonly IRoutingContext _routingCtx;
2024-01-10 14:18:59 +00:00
public StreamingLogHook(
ConversationSetting convSettings,
IServiceProvider serivces,
2024-01-24 23:47:57 +00:00
IHubContext<SignalRHub> chatHub,
IConversationStateService state,
2024-02-23 01:53:00 +00:00
IUserIdentity user,
2024-02-28 20:40:59 +00:00
IAgentService agentService,
IRoutingContext routingCtx)
2024-01-10 14:18:59 +00:00
{
_convSettings = convSettings;
_services = serivces;
_chatHub = chatHub;
2024-01-24 23:47:57 +00:00
_state = state;
_user = user;
2024-02-23 01:53:00 +00:00
_agentService = agentService;
2024-02-28 20:40:59 +00:00
_routingCtx = routingCtx;
2024-01-19 04:38:44 +00:00
_serializerOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
2024-02-23 01:53:00 +00:00
AllowTrailingCommas = true,
WriteIndented = true
2024-01-19 04:38:44 +00:00
};
2024-01-10 14:18:59 +00:00
}
2024-02-22 17:25:13 +00:00
2024-01-24 23:47:57 +00:00
public override async Task OnMessageReceived(RoleDialogModel message)
{
var conversationId = _state.GetConversationId();
2024-02-28 16:21:14 +00:00
var log = $"{message.Content}";
2024-02-29 23:17:43 +00:00
var input = new ContentLogInput(conversationId, message)
{
Name = _user.UserName,
Source = ContentLogSource.UserInput,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
2024-01-24 23:47:57 +00:00
}
2024-01-10 14:18:59 +00:00
public async Task BeforeGenerating(Agent agent, List<RoleDialogModel> conversations)
{
if (!_convSettings.ShowVerboseLog) return;
2024-01-24 23:47:57 +00:00
}
public override async Task OnFunctionExecuted(RoleDialogModel message)
{
2024-02-20 20:37:32 +00:00
var conversationId = _state.GetConversationId();
2024-02-23 01:53:00 +00:00
var agent = await _agentService.LoadAgent(message.CurrentAgentId);
var log = $"{message.FunctionName}({message.FunctionArgs})\r\n => {message.Content}";
2024-02-29 23:17:43 +00:00
var input = new ContentLogInput(conversationId, message)
{
Name = agent?.Name,
AgentId = agent?.Id,
Source = ContentLogSource.FunctionCall,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
2024-01-10 14:18:59 +00:00
}
2024-02-20 20:37:32 +00:00
/// <summary>
/// Used to log prompt
/// </summary>
/// <param name="message"></param>
/// <param name="tokenStats"></param>
/// <returns></returns>
2024-01-10 14:18:59 +00:00
public async Task AfterGenerated(RoleDialogModel message, TokenStatsModel tokenStats)
{
if (!_convSettings.ShowVerboseLog) return;
2024-01-24 23:47:57 +00:00
var conversationId = _state.GetConversationId();
2024-02-23 01:53:00 +00:00
var agent = await _agentService.LoadAgent(message.CurrentAgentId);
2024-01-10 14:18:59 +00:00
2024-02-22 21:14:13 +00:00
var log = tokenStats.Prompt;
2024-02-29 23:17:43 +00:00
var input = new ContentLogInput(conversationId, message)
{
Name = agent?.Name,
AgentId = agent?.Id,
Source = ContentLogSource.Prompt,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
2024-01-19 04:38:44 +00:00
}
2024-02-20 20:37:32 +00:00
/// <summary>
/// Used to log final response
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
2024-02-16 21:58:39 +00:00
public override async Task OnResponseGenerated(RoleDialogModel message)
{
var conv = _services.GetRequiredService<IConversationService>();
2024-02-23 01:53:00 +00:00
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversateStateLogGenerated", BuildStateLog(conv.ConversationId, _state.GetStates(), message));
2024-02-20 20:37:32 +00:00
if (message.Role == AgentRole.Assistant)
{
2024-02-23 01:53:00 +00:00
var agent = await _agentService.LoadAgent(message.CurrentAgentId);
2024-02-22 21:14:13 +00:00
var log = $"{message.Content}";
2024-02-20 20:38:49 +00:00
if (message.RichContent != null && message.RichContent.Message.RichType != "text")
2024-02-20 20:37:32 +00:00
{
2024-02-20 20:38:49 +00:00
var richContent = JsonSerializer.Serialize(message.RichContent, _serializerOptions);
log += $"\r\n{richContent}";
2024-02-20 20:37:32 +00:00
}
2024-02-15 20:43:36 +00:00
2024-02-29 23:17:43 +00:00
var input = new ContentLogInput(conv.ConversationId, message)
{
Name = agent?.Name,
AgentId = agent?.Id,
Source = ContentLogSource.AgentResponse,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
2024-02-16 21:58:39 +00:00
}
}
2024-02-28 16:21:14 +00:00
#region IRoutingHook
2024-02-28 20:40:59 +00:00
public async Task OnAgentEnqueued(string agentId, string preAgentId, string? reason = null)
2024-02-28 16:21:14 +00:00
{
var conversationId = _state.GetConversationId();
var agent = await _agentService.LoadAgent(agentId);
var preAgent = await _agentService.LoadAgent(preAgentId);
2024-02-28 20:40:59 +00:00
var log = $"{agent.Name} is enqueued{(reason != null ? $" ({reason})" : "")}";
2024-02-29 23:17:43 +00:00
var message = new RoleDialogModel(AgentRole.System, log)
{
MessageId = _routingCtx.MessageId
};
var input = new ContentLogInput(conversationId, message)
{
Name = "Router",
Source = ContentLogSource.HardRule,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
2024-02-28 16:21:14 +00:00
}
2024-02-28 20:40:59 +00:00
public async Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null)
2024-02-28 16:21:14 +00:00
{
var conversationId = _state.GetConversationId();
var agent = await _agentService.LoadAgent(agentId);
var currentAgent = await _agentService.LoadAgent(currentAgentId);
2024-02-29 02:45:01 +00:00
var log = $"{agent.Name} is dequeued{(reason != null ? $" ({reason})" : "")}, current agent is {currentAgent?.Name}";
2024-02-29 23:17:43 +00:00
var message = new RoleDialogModel(AgentRole.System, log)
{
MessageId = _routingCtx.MessageId
};
var input = new ContentLogInput(conversationId, message)
{
Name = "Router",
Source = ContentLogSource.HardRule,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
2024-02-28 16:21:14 +00:00
}
2024-02-28 20:40:59 +00:00
public async Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null)
2024-02-28 16:21:14 +00:00
{
var conversationId = _state.GetConversationId();
var fromAgent = await _agentService.LoadAgent(fromAgentId);
var toAgent = await _agentService.LoadAgent(toAgentId);
2024-02-28 20:40:59 +00:00
var log = $"{fromAgent.Name} is replaced to {toAgent.Name}{(reason != null ? $" ({reason})" : "")}";
2024-02-29 23:17:43 +00:00
var message = new RoleDialogModel(AgentRole.System, log)
{
MessageId = _routingCtx.MessageId
};
var input = new ContentLogInput(conversationId, message)
{
Name = "Router",
Source = ContentLogSource.HardRule,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
2024-02-28 16:21:14 +00:00
}
2024-02-28 20:40:59 +00:00
public async Task OnAgentQueueEmptied(string agentId, string? reason = null)
2024-02-28 16:21:14 +00:00
{
var conversationId = _state.GetConversationId();
var agent = await _agentService.LoadAgent(agentId);
2024-02-28 20:40:59 +00:00
var log = reason ?? "Agent queue is cleared";
2024-02-29 23:17:43 +00:00
var message = new RoleDialogModel(AgentRole.System, log)
{
MessageId = _routingCtx.MessageId
};
var input = new ContentLogInput(conversationId, message)
{
Name = "Router",
Source = ContentLogSource.HardRule,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
2024-02-28 16:21:14 +00:00
}
2024-02-28 20:40:59 +00:00
public async Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message)
2024-02-28 16:21:14 +00:00
{
var conversationId = _state.GetConversationId();
var agent = await _agentService.LoadAgent(message.CurrentAgentId);
var log = JsonSerializer.Serialize(instruct, _serializerOptions);
2024-02-29 23:17:43 +00:00
var input = new ContentLogInput(conversationId, message)
{
Name = agent?.Name,
AgentId = agent?.Id,
Source = ContentLogSource.AgentResponse,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
2024-02-28 16:21:14 +00:00
}
#endregion
2024-02-29 23:17:43 +00:00
private string BuildContentLog(ContentLogInput input)
{
var log = new ConversationContentLogModel
{
ConversationId = input.ConversationId,
MessageId = input.Message.MessageId,
Name = input.Name,
Role = input.Message.Role,
Content = input.Log,
Source = input.Source,
CreateTime = DateTime.UtcNow
};
var json = JsonSerializer.Serialize(log, _serializerOptions);
var convSettings = _services.GetRequiredService<ConversationSetting>();
if (convSettings.EnableContentLog)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
db.SaveConversationContentLog(log);
}
return json;
}
private string BuildStateLog(string conversationId, Dictionary<string, string> states, RoleDialogModel message)
{
var log = new ConversationStateLogModel
{
ConversationId = conversationId,
MessageId = message.MessageId,
States = states,
CreateTime = DateTime.UtcNow
};
var convSettings = _services.GetRequiredService<ConversationSetting>();
if (convSettings.EnableStateLog)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
db.SaveConversationStateLog(log);
}
return JsonSerializer.Serialize(log, _serializerOptions);
}
2024-01-10 14:18:59 +00:00
}
2024-02-29 23:17:43 +00:00
internal class ContentLogInput
{
public string ConversationId { get; set; }
public string? Name { get; set; }
public string? AgentId { get; set; }
public string Log { get; set; }
public string Source { get; set; }
public RoleDialogModel Message { get; set; }
public ContentLogInput()
{
}
public ContentLogInput(string conversationId, RoleDialogModel message)
{
ConversationId = conversationId;
Message = message;
}
}