Merge branch 'SciSharp:master' into master

This commit is contained in:
hchen2020 2025-03-12 11:51:22 -05:00 committed by GitHub
commit 253be7c676
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 160 additions and 93 deletions

View file

@ -6,4 +6,5 @@ public class WebBrowsingSettings
public bool Headless { get; set; }
// Default timeout in milliseconds
public float DefaultTimeout { get; set; } = 30000;
public bool IsEnableScreenshot { get; set; }
}

View file

@ -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

View file

@ -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();
}
}

View file

@ -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

View file

@ -0,0 +1,6 @@
namespace BotSharp.Abstraction.Utilities;
public class DateTimePagination<T> : PagedItems<T>
{
public DateTime? NextTime { get; set; }
}

View file

@ -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);
}
}

View file

@ -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

View file

@ -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>();

View file

@ -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

View file

@ -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 { }
}
}

View file

@ -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

View file

@ -1,3 +1,5 @@
using BotSharp.Abstraction.Browsing.Settings;
namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
public partial class PlaywrightWebDriver
@ -5,19 +7,21 @@ public partial class PlaywrightWebDriver
public async Task<BrowserActionResult> ScreenshotAsync(MessageInfo message, string path)
{
var result = new BrowserActionResult();
await _instance.Wait(message.ContextId, waitNetworkIdle: false);
var page = _instance.GetPage(message.ContextId);
await Task.Delay(300);
var bytes = await page.ScreenshotAsync(new PageScreenshotOptions
var _webDriver = _services.GetRequiredService<WebBrowsingSettings>();
if (_webDriver.IsEnableScreenshot)
{
Path = path,
FullPage = true
});
await _instance.Wait(message.ContextId, waitNetworkIdle: false);
var page = _instance.GetPage(message.ContextId);
await Task.Delay(300);
var bytes = await page.ScreenshotAsync(new PageScreenshotOptions
{
Path = path,
FullPage = true
});
result.Body = "data:image/png;base64," + Convert.ToBase64String(bytes);
}
result.IsSuccess = true;
result.Body = "data:image/png;base64," + Convert.ToBase64String(bytes);
return result;
}
}