refine chat log
This commit is contained in:
parent
db3ed51875
commit
85cf382adc
|
|
@ -6,8 +6,8 @@ namespace BotSharp.Abstraction.Loggers.Services;
|
|||
public interface ILoggerService
|
||||
{
|
||||
#region Conversation
|
||||
Task<List<ContentLogOutputModel>> GetConversationContentLogs(string conversationId);
|
||||
Task<List<ConversationStateLogModel>> GetConversationStateLogs(string conversationId);
|
||||
Task<DateTimePagination<ContentLogOutputModel>> GetConversationContentLogs(string conversationId, ConversationLogFilter filter);
|
||||
Task<DateTimePagination<ConversationStateLogModel>> GetConversationStateLogs(string conversationId, ConversationLogFilter filter);
|
||||
#endregion
|
||||
|
||||
#region Instruction
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
namespace BotSharp.Abstraction.Repositories.Filters;
|
||||
|
||||
public class ConversationLogFilter
|
||||
{
|
||||
public int Size { get; set; } = 20;
|
||||
public DateTime StartTime { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public ConversationLogFilter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static ConversationLogFilter Empty()
|
||||
{
|
||||
return new();
|
||||
}
|
||||
}
|
||||
|
|
@ -164,14 +164,14 @@ public interface IBotSharpRepository : IHaveServiceProvider
|
|||
#region Conversation Content Log
|
||||
void SaveConversationContentLog(ContentLogOutputModel log)
|
||||
=> throw new NotImplementedException();
|
||||
List<ContentLogOutputModel> GetConversationContentLogs(string conversationId)
|
||||
DateTimePagination<ContentLogOutputModel> GetConversationContentLogs(string conversationId, ConversationLogFilter filter)
|
||||
=> throw new NotImplementedException();
|
||||
#endregion
|
||||
|
||||
#region Conversation State Log
|
||||
void SaveConversationStateLog(ConversationStateLogModel log)
|
||||
=> throw new NotImplementedException();
|
||||
List<ConversationStateLogModel> GetConversationStateLogs(string conversationId)
|
||||
DateTimePagination<ConversationStateLogModel> GetConversationStateLogs(string conversationId, ConversationLogFilter filter)
|
||||
=> throw new NotImplementedException();
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
namespace BotSharp.Abstraction.Utilities;
|
||||
|
||||
public class DateTimePagination<T> : PagedItems<T>
|
||||
{
|
||||
public DateTime? NextTime { get; set; }
|
||||
}
|
||||
|
|
@ -4,18 +4,28 @@ namespace BotSharp.Core.Loggers.Services;
|
|||
|
||||
public partial class LoggerService
|
||||
{
|
||||
public async Task<List<ContentLogOutputModel>> GetConversationContentLogs(string conversationId)
|
||||
public async Task<DateTimePagination<ContentLogOutputModel>> GetConversationContentLogs(string conversationId, ConversationLogFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = ConversationLogFilter.Empty();
|
||||
}
|
||||
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var logs = db.GetConversationContentLogs(conversationId);
|
||||
var logs = db.GetConversationContentLogs(conversationId, filter);
|
||||
return await Task.FromResult(logs);
|
||||
}
|
||||
|
||||
|
||||
public async Task<List<ConversationStateLogModel>> GetConversationStateLogs(string conversationId)
|
||||
public async Task<DateTimePagination<ConversationStateLogModel>> GetConversationStateLogs(string conversationId, ConversationLogFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = ConversationLogFilter.Empty();
|
||||
}
|
||||
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var logs = db.GetConversationStateLogs(conversationId);
|
||||
var logs = db.GetConversationStateLogs(conversationId, filter);
|
||||
return await Task.FromResult(logs);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using BotSharp.Abstraction.Loggers.Models;
|
||||
using Microsoft.IdentityModel.Logging;
|
||||
using System.IO;
|
||||
|
||||
namespace BotSharp.Core.Repository
|
||||
|
|
@ -54,26 +55,34 @@ namespace BotSharp.Core.Repository
|
|||
File.WriteAllText(file, JsonSerializer.Serialize(log, _options));
|
||||
}
|
||||
|
||||
public List<ContentLogOutputModel> GetConversationContentLogs(string conversationId)
|
||||
public DateTimePagination<ContentLogOutputModel> GetConversationContentLogs(string conversationId, ConversationLogFilter filter)
|
||||
{
|
||||
var logs = new List<ContentLogOutputModel>();
|
||||
if (string.IsNullOrEmpty(conversationId)) return logs;
|
||||
if (string.IsNullOrEmpty(conversationId)) return new();
|
||||
|
||||
var convDir = FindConversationDirectory(conversationId);
|
||||
if (string.IsNullOrEmpty(convDir)) return logs;
|
||||
if (string.IsNullOrEmpty(convDir)) return new();
|
||||
|
||||
var logDir = Path.Combine(convDir, "content_log");
|
||||
if (!Directory.Exists(logDir)) return logs;
|
||||
if (!Directory.Exists(logDir)) return new();
|
||||
|
||||
var logs = new List<ContentLogOutputModel>();
|
||||
foreach (var file in Directory.GetFiles(logDir))
|
||||
{
|
||||
var text = File.ReadAllText(file);
|
||||
var log = JsonSerializer.Deserialize<ContentLogOutputModel>(text);
|
||||
if (log == null) continue;
|
||||
if (log == null || log.CreatedTime >= filter.StartTime) continue;
|
||||
|
||||
logs.Add(log);
|
||||
}
|
||||
return logs.OrderBy(x => x.CreatedTime).ToList();
|
||||
|
||||
logs = logs.OrderByDescending(x => x.CreatedTime).Take(filter.Size).ToList();
|
||||
logs.Reverse();
|
||||
return new DateTimePagination<ContentLogOutputModel>
|
||||
{
|
||||
Items = logs,
|
||||
Count = logs.Count,
|
||||
NextTime = logs.FirstOrDefault()?.CreatedTime
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
@ -99,26 +108,34 @@ namespace BotSharp.Core.Repository
|
|||
File.WriteAllText(file, JsonSerializer.Serialize(log, _options));
|
||||
}
|
||||
|
||||
public List<ConversationStateLogModel> GetConversationStateLogs(string conversationId)
|
||||
public DateTimePagination<ConversationStateLogModel> GetConversationStateLogs(string conversationId, ConversationLogFilter filter)
|
||||
{
|
||||
var logs = new List<ConversationStateLogModel>();
|
||||
if (string.IsNullOrEmpty(conversationId)) return logs;
|
||||
if (string.IsNullOrEmpty(conversationId)) return new();
|
||||
|
||||
var convDir = FindConversationDirectory(conversationId);
|
||||
if (string.IsNullOrEmpty(convDir)) return logs;
|
||||
if (string.IsNullOrEmpty(convDir)) return new();
|
||||
|
||||
var logDir = Path.Combine(convDir, "state_log");
|
||||
if (!Directory.Exists(logDir)) return logs;
|
||||
if (!Directory.Exists(logDir)) return new();
|
||||
|
||||
var logs = new List<ConversationStateLogModel>();
|
||||
foreach (var file in Directory.GetFiles(logDir))
|
||||
{
|
||||
var text = File.ReadAllText(file);
|
||||
var log = JsonSerializer.Deserialize<ConversationStateLogModel>(text);
|
||||
if (log == null) continue;
|
||||
if (log == null || log.CreatedTime >= filter.StartTime) continue;
|
||||
|
||||
logs.Add(log);
|
||||
}
|
||||
return logs.OrderBy(x => x.CreatedTime).ToList();
|
||||
|
||||
logs = logs.OrderByDescending(x => x.CreatedTime).Take(filter.Size).ToList();
|
||||
logs.Reverse();
|
||||
return new DateTimePagination<ConversationStateLogModel>
|
||||
{
|
||||
Items = logs,
|
||||
Count = logs.Count,
|
||||
NextTime = logs.FirstOrDefault()?.CreatedTime
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -81,11 +81,11 @@ public class ConversationController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpGet("/conversation/{conversationId}/dialogs")]
|
||||
public async Task<IEnumerable<ChatResponseModel>> GetDialogs([FromRoute] string conversationId)
|
||||
public async Task<IEnumerable<ChatResponseModel>> GetDialogs([FromRoute] string conversationId, [FromQuery] int count = 100)
|
||||
{
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
conv.SetConversationId(conversationId, [], isReadOnly: true);
|
||||
var history = conv.GetDialogHistory(fromBreakpoint: false);
|
||||
var history = conv.GetDialogHistory(lastCount: count, fromBreakpoint: false);
|
||||
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
|
|
|
|||
|
|
@ -10,14 +10,11 @@ namespace BotSharp.OpenAPI.Controllers;
|
|||
public class LoggerController : ControllerBase
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IUserIdentity _user;
|
||||
|
||||
public LoggerController(
|
||||
IServiceProvider services,
|
||||
IUserIdentity user)
|
||||
IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
_user = user;
|
||||
}
|
||||
|
||||
[HttpGet("/logger/full-log")]
|
||||
|
|
@ -40,17 +37,21 @@ public class LoggerController : ControllerBase
|
|||
|
||||
#region Conversation log
|
||||
[HttpGet("/logger/conversation/{conversationId}/content-log")]
|
||||
public async Task<List<ContentLogOutputModel>> GetConversationContentLogs([FromRoute] string conversationId)
|
||||
public async Task<DateTimePagination<ContentLogOutputModel>> GetConversationContentLogs(
|
||||
[FromRoute] string conversationId,
|
||||
[FromQuery] ConversationLogFilter request)
|
||||
{
|
||||
var logging = _services.GetRequiredService<ILoggerService>();
|
||||
return await logging.GetConversationContentLogs(conversationId);
|
||||
return await logging.GetConversationContentLogs(conversationId, request);
|
||||
}
|
||||
|
||||
[HttpGet("/logger/conversation/{conversationId}/state-log")]
|
||||
public async Task<List<ConversationStateLogModel>> GetConversationStateLogs([FromRoute] string conversationId)
|
||||
public async Task<DateTimePagination<ConversationStateLogModel>> GetConversationStateLogs(
|
||||
[FromRoute] string conversationId,
|
||||
[FromQuery] ConversationLogFilter request)
|
||||
{
|
||||
var logging = _services.GetRequiredService<ILoggerService>();
|
||||
return await logging.GetConversationStateLogs(conversationId);
|
||||
return await logging.GetConversationStateLogs(conversationId, request);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ public class ChatHubCrontabHook : ICrontabHook
|
|||
private readonly IHubContext<SignalRHub> _chatHub;
|
||||
private readonly ILogger<ChatHubCrontabHook> _logger;
|
||||
private readonly IUserIdentity _user;
|
||||
private readonly IConversationStorage _storage;
|
||||
private readonly BotSharpOptions _options;
|
||||
private readonly ChatHubSettings _settings;
|
||||
|
||||
|
|
@ -22,7 +21,6 @@ public class ChatHubCrontabHook : ICrontabHook
|
|||
IHubContext<SignalRHub> chatHub,
|
||||
ILogger<ChatHubCrontabHook> logger,
|
||||
IUserIdentity user,
|
||||
IConversationStorage storage,
|
||||
BotSharpOptions options,
|
||||
ChatHubSettings settings)
|
||||
{
|
||||
|
|
@ -30,7 +28,6 @@ public class ChatHubCrontabHook : ICrontabHook
|
|||
_chatHub = chatHub;
|
||||
_logger = logger;
|
||||
_user = user;
|
||||
_storage = storage;
|
||||
_options = options;
|
||||
_settings = settings;
|
||||
}
|
||||
|
|
@ -58,19 +55,8 @@ public class ChatHubCrontabHook : ICrontabHook
|
|||
{
|
||||
try
|
||||
{
|
||||
if (_settings.EventDispatchBy == EventDispatchType.Group)
|
||||
{
|
||||
await _chatHub.Clients.Group(item.ConversationId).SendAsync(GENERATE_NOTIFICATION, json);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _chatHub.Clients.User(item.UserId).SendAsync(GENERATE_NOTIFICATION, json);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"Failed to send event in {nameof(ChatHubCrontabHook)} (conversation id: {item.ConversationId})." +
|
||||
$"\r\n{ex.Message}\r\n{ex.InnerException}");
|
||||
await _chatHub.Clients.User(item.UserId).SendAsync(GENERATE_NOTIFICATION, json);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using BotSharp.Abstraction.Loggers.Models;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using MongoDB.Driver;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||
|
|
@ -34,7 +35,8 @@ public partial class MongoRepository
|
|||
{
|
||||
if (log == null) return;
|
||||
|
||||
var found = _dc.Conversations.AsQueryable().FirstOrDefault(x => x.Id == log.ConversationId);
|
||||
var filter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, log.ConversationId);
|
||||
var found = _dc.Conversations.Find(filter).FirstOrDefault();
|
||||
if (found == null) return;
|
||||
|
||||
var logDoc = new ConversationContentLogDocument
|
||||
|
|
@ -52,25 +54,36 @@ public partial class MongoRepository
|
|||
_dc.ContentLogs.InsertOne(logDoc);
|
||||
}
|
||||
|
||||
public List<ContentLogOutputModel> GetConversationContentLogs(string conversationId)
|
||||
public DateTimePagination<ContentLogOutputModel> GetConversationContentLogs(string conversationId, ConversationLogFilter filter)
|
||||
{
|
||||
var logs = _dc.ContentLogs
|
||||
.AsQueryable()
|
||||
.Where(x => x.ConversationId == conversationId)
|
||||
.Select(x => new ContentLogOutputModel
|
||||
{
|
||||
ConversationId = x.ConversationId,
|
||||
MessageId = x.MessageId,
|
||||
Name = x.Name,
|
||||
AgentId = x.AgentId,
|
||||
Role = x.Role,
|
||||
Source = x.Source,
|
||||
Content = x.Content,
|
||||
CreatedTime = x.CreatedTime
|
||||
})
|
||||
.OrderBy(x => x.CreatedTime)
|
||||
.ToList();
|
||||
return logs;
|
||||
var builder = Builders<ConversationContentLogDocument>.Filter;
|
||||
var logFilters = new List<FilterDefinition<ConversationContentLogDocument>>
|
||||
{
|
||||
builder.Eq(x => x.ConversationId, conversationId),
|
||||
builder.Lt(x => x.CreatedTime, filter.StartTime)
|
||||
};
|
||||
var logSortDef = Builders<ConversationContentLogDocument>.Sort.Descending(x => x.CreatedTime);
|
||||
|
||||
var docs = _dc.ContentLogs.Find(builder.And(logFilters)).Sort(logSortDef).Limit(filter.Size).ToList();
|
||||
var logs = docs.Select(x => new ContentLogOutputModel
|
||||
{
|
||||
ConversationId = x.ConversationId,
|
||||
MessageId = x.MessageId,
|
||||
Name = x.Name,
|
||||
AgentId = x.AgentId,
|
||||
Role = x.Role,
|
||||
Source = x.Source,
|
||||
Content = x.Content,
|
||||
CreatedTime = x.CreatedTime
|
||||
}).ToList();
|
||||
|
||||
logs.Reverse();
|
||||
return new DateTimePagination<ContentLogOutputModel>
|
||||
{
|
||||
Items = logs,
|
||||
Count = logs.Count,
|
||||
NextTime = logs.FirstOrDefault()?.CreatedTime
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
@ -79,7 +92,8 @@ public partial class MongoRepository
|
|||
{
|
||||
if (log == null) return;
|
||||
|
||||
var found = _dc.Conversations.AsQueryable().FirstOrDefault(x => x.Id == log.ConversationId);
|
||||
var filter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, log.ConversationId);
|
||||
var found = _dc.Conversations.Find(filter).FirstOrDefault();
|
||||
if (found == null) return;
|
||||
|
||||
var logDoc = new ConversationStateLogDocument
|
||||
|
|
@ -94,22 +108,33 @@ public partial class MongoRepository
|
|||
_dc.StateLogs.InsertOne(logDoc);
|
||||
}
|
||||
|
||||
public List<ConversationStateLogModel> GetConversationStateLogs(string conversationId)
|
||||
public DateTimePagination<ConversationStateLogModel> GetConversationStateLogs(string conversationId, ConversationLogFilter filter)
|
||||
{
|
||||
var logs = _dc.StateLogs
|
||||
.AsQueryable()
|
||||
.Where(x => x.ConversationId == conversationId)
|
||||
.Select(x => new ConversationStateLogModel
|
||||
{
|
||||
ConversationId = x.ConversationId,
|
||||
AgentId = x.AgentId,
|
||||
MessageId = x.MessageId,
|
||||
States = x.States,
|
||||
CreatedTime = x.CreatedTime
|
||||
})
|
||||
.OrderBy(x => x.CreatedTime)
|
||||
.ToList();
|
||||
return logs;
|
||||
var builder = Builders<ConversationStateLogDocument>.Filter;
|
||||
var logFilters = new List<FilterDefinition<ConversationStateLogDocument>>
|
||||
{
|
||||
builder.Eq(x => x.ConversationId, conversationId),
|
||||
builder.Lt(x => x.CreatedTime, filter.StartTime)
|
||||
};
|
||||
var logSortDef = Builders<ConversationStateLogDocument>.Sort.Descending(x => x.CreatedTime);
|
||||
|
||||
var docs = _dc.StateLogs.Find(builder.And(logFilters)).Sort(logSortDef).Limit(filter.Size).ToList();
|
||||
var logs = docs.Select(x => new ConversationStateLogModel
|
||||
{
|
||||
ConversationId = x.ConversationId,
|
||||
AgentId = x.AgentId,
|
||||
MessageId = x.MessageId,
|
||||
States = x.States,
|
||||
CreatedTime = x.CreatedTime
|
||||
}).ToList();
|
||||
|
||||
logs.Reverse();
|
||||
return new DateTimePagination<ConversationStateLogModel>
|
||||
{
|
||||
Items = logs,
|
||||
Count = logs.Count,
|
||||
NextTime = logs.FirstOrDefault()?.CreatedTime
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue