refine instruction log

This commit is contained in:
Jicheng Lu 2025-03-06 11:11:45 -06:00
parent 4dadd5c6fe
commit ae84088b5f
12 changed files with 130 additions and 25 deletions

View file

@ -1,5 +1,3 @@
using System.Collections.Generic;
namespace BotSharp.Abstraction.Instructs.Models;
public class InstructLogFilter : Pagination
@ -8,6 +6,7 @@ public class InstructLogFilter : Pagination
public List<string>? Providers { get; set; }
public List<string>? Models { get; set; }
public List<string>? TemplateNames { get; set; }
public List<string>? UserIds { get; set; }
public static InstructLogFilter Empty()
{

View file

@ -39,6 +39,9 @@ public class InstructionLogModel
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? UserId { get; set; }
[JsonIgnore]
public string? UserName { get; set; }
[JsonIgnore]
public Dictionary<string, string> States { get; set; } = [];

View file

@ -0,0 +1,9 @@
using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Abstraction.Loggers.Models;
namespace BotSharp.Abstraction.Loggers.Services;
public interface ILoggerService
{
Task<PagedItems<InstructionLogModel>> GetInstructionLogs(InstructLogFilter filter);
}

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(TargetFramework)</TargetFramework>

View file

@ -1,5 +1,4 @@
using BotSharp.Abstraction.Plugins.Models;
using BotSharp.Abstraction.Users.Enums;
using Microsoft.Extensions.Configuration;
namespace BotSharp.Core.Instructs;
@ -22,8 +21,8 @@ public class InsturctionPlugin : IBotSharpPlugin
{
SubMenu = new List<PluginMenuDef>
{
new PluginMenuDef("Instruction", link: "page/instruction"),
new PluginMenuDef("Log", link: "page/instruction/log") { Roles = [UserRole.Root, UserRole.Admin] }
new PluginMenuDef("Testing", link: "page/instruction/testing"),
new PluginMenuDef("Log", link: "page/instruction/log")
}
});

View file

@ -0,0 +1,15 @@
using Microsoft.Extensions.Configuration;
namespace BotSharp.Core.Loggers;
public class LoggerPlugin : IBotSharpPlugin
{
public string Id => "ea1aade7-7e29-4f13-a78b-2b1835aa4fea";
public string Name => "Logger";
public string Description => "Provide log service";
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddScoped<ILoggerService, LoggerService>();
}
}

View file

@ -0,0 +1,78 @@
using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Abstraction.Loggers.Models;
using BotSharp.Abstraction.Users.Enums;
using BotSharp.Abstraction.Users.Models;
namespace BotSharp.Core.Loggers.Services;
public class LoggerService : ILoggerService
{
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
private readonly ILogger<LoggerService> _logger;
public LoggerService(
IServiceProvider services,
IUserIdentity user,
ILogger<LoggerService> logger)
{
_services = services;
_user = user;
_logger = logger;
}
public async Task<PagedItems<InstructionLogModel>> GetInstructionLogs(InstructLogFilter filter)
{
if (filter == null)
{
filter = InstructLogFilter.Empty();
}
var userService = _services.GetRequiredService<IUserService>();
var user = await userService.GetUser(_user.Id);
var isAdmin = UserConstant.AdminRoles.Contains(user?.Role);
if (!isAdmin && user?.Id == null) return new();
filter.UserIds = isAdmin ? [] : user?.Id != null ? [user.Id] : [];
var agents = new List<Agent>();
var users = new List<User>();
var db = _services.GetRequiredService<IBotSharpRepository>();
var logs = db.GetInstructionLogs(filter);
var agentIds = logs.Items.Where(x => !string.IsNullOrEmpty(x.AgentId)).Select(x => x.AgentId).ToList();
var userIds = logs.Items.Where(x => !string.IsNullOrEmpty(x.UserId)).Select(x => x.UserId).ToList();
agents = db.GetAgents(new AgentFilter
{
AgentIds = agentIds,
Pager = new Pagination { Size = filter.Size }
});
if (isAdmin)
{
users = db.GetUserByIds(userIds);
}
var items = logs.Items.Select(x =>
{
x.AgentId = !string.IsNullOrEmpty(x.AgentId) ? agents.FirstOrDefault(a => a.Id == x.AgentId)?.Name : null;
if (!isAdmin)
{
x.UserName = user != null ? $"{user.FirstName} {user.LastName}" : null;
}
else
{
var found = !string.IsNullOrEmpty(x.UserId) ? users.FirstOrDefault(u => u.Id == x.UserId) : null;
x.UserName = found != null ? $"{found.FirstName} {found.LastName}" : null;
}
return x;
}).ToList();
return new PagedItems<InstructionLogModel>
{
Items = items,
Count = logs.Count
};
}
}

View file

@ -182,6 +182,10 @@ namespace BotSharp.Core.Repository
{
matched = matched && filter.TemplateNames.Contains(log.TemplateName);
}
if (!filter.UserIds.IsNullOrEmpty())
{
matched = matched && filter.UserIds.Contains(log.UserId);
}
if (!matched) continue;
@ -190,12 +194,6 @@ namespace BotSharp.Core.Repository
}
var records = logs.OrderByDescending(x => x.CreatedTime).Skip(filter.Offset).Take(filter.Size);
var agentIds = records.Where(x => !string.IsNullOrEmpty(x.AgentId)).Select(x => x.AgentId).ToList();
var agents = GetAgents(new AgentFilter
{
AgentIds = agentIds
});
records = records.Select(x =>
{
var states = x.InnerStates.ToDictionary(p => p.Key, p =>
@ -203,7 +201,6 @@ namespace BotSharp.Core.Repository
var data = p.Value.RootElement.GetProperty("data");
return data.ValueKind != JsonValueKind.Null ? data.ToString() : null;
});
x.AgentName = !string.IsNullOrEmpty(x.AgentId) ? agents.FirstOrDefault(a => a.Id == x.AgentId)?.Name : null;
x.States = states ?? [];
return x;
}).ToList();

View file

@ -39,6 +39,8 @@ global using BotSharp.Abstraction.SideCar.Attributes;
global using BotSharp.Abstraction.Statistics.Models;
global using BotSharp.Abstraction.Statistics.Enums;
global using BotSharp.Abstraction.Statistics.Services;
global using BotSharp.Abstraction.Loggers.Services;
global using BotSharp.Abstraction.Infrastructures.Events;
global using BotSharp.Core.Repository;
global using BotSharp.Core.Routing;
global using BotSharp.Core.Agents.Services;
@ -46,4 +48,4 @@ global using BotSharp.Core.Conversations.Services;
global using BotSharp.Core.Infrastructures;
global using BotSharp.Core.Users.Services;
global using BotSharp.Core.Statistics.Services;
global using BotSharp.Abstraction.Infrastructures.Events;
global using BotSharp.Core.Loggers.Services;

View file

@ -1,6 +1,6 @@
using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Abstraction.Loggers.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Loggers.Services;
using BotSharp.OpenAPI.ViewModels.Instructs;
using Microsoft.AspNetCore.Hosting;
@ -11,10 +11,14 @@ namespace BotSharp.OpenAPI.Controllers;
public class LoggerController : ControllerBase
{
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
public LoggerController(IServiceProvider services)
public LoggerController(
IServiceProvider services,
IUserIdentity user)
{
_services = services;
_user = user;
}
[HttpGet("/logger/full-log")]
@ -52,8 +56,9 @@ public class LoggerController : ControllerBase
[HttpGet("/logger/instruction/log")]
public async Task<PagedItems<InstructionLogViewModel>> GetInstructionLogs([FromQuery] InstructLogFilter request)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var logs = db.GetInstructionLogs(request);
var logging = _services.GetRequiredService<ILoggerService>();
var logs = await logging.GetInstructionLogs(request);
return new PagedItems<InstructionLogViewModel>
{
Items = logs.Items.Select(x => InstructionLogViewModel.From(x)),

View file

@ -40,6 +40,10 @@ public class InstructionLogViewModel
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? UserId { get; set; }
[JsonPropertyName("user_name")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? UserName { get; set; }
[JsonPropertyName("states")]
public Dictionary<string, string> States { get; set; } = [];
@ -60,6 +64,7 @@ public class InstructionLogViewModel
SystemInstruction = log.SystemInstruction,
CompletionText = log.CompletionText,
UserId = log.UserId,
UserName = log.UserName,
States = log.States,
CreatedTime = log.CreatedTime
};

View file

@ -178,16 +178,9 @@ public partial class MongoRepository
var docs = _dc.InstructionLogs.Find(filterDef).Sort(sortDef).Skip(filter.Offset).Limit(filter.Size).ToList();
var count = _dc.InstructionLogs.CountDocuments(filterDef);
var agentIds = docs.Where(x => !string.IsNullOrEmpty(x.AgentId)).Select(x => x.AgentId).ToList();
var agents = GetAgents(new AgentFilter
{
AgentIds = agentIds
});
var logs = docs.Select(x =>
{
var log = InstructionLogBetaDocument.ToDomainModel(x);
log.AgentName = !string.IsNullOrEmpty(x.AgentId) ? agents.FirstOrDefault(a => a.Id == x.AgentId)?.Name : null;
log.States = x.States.ToDictionary(p => p.Key, p =>
{
var jsonStr = p.Value.ToJson();