This commit is contained in:
Haiping Chen 2023-11-27 21:19:10 -06:00
commit 5bd15f7a6b
16 changed files with 214 additions and 74 deletions

View file

@ -0,0 +1,12 @@
namespace BotSharp.Abstraction.Conversations.Models;
public class LlmCompletionLog
{
public string Id { get; set; } = string.Empty;
public string ConversationId { get; set; } = string.Empty;
public string MessageId { get; set; } = string.Empty;
public string AgentId { get; set; } = string.Empty;
public string Prompt { get; set; } = string.Empty;
public string? Response { get; set; }
public DateTime CreateDateTime { get; set; } = DateTime.UtcNow;
}

View file

@ -0,0 +1,10 @@
namespace BotSharp.Abstraction.Repositories.Filters;
public class AgentFilter
{
public string? AgentName { get; set; }
public bool? Disabled { get; set; }
public bool? AllowRouting { get; set; }
public bool? IsPublic { get; set; }
public List<string>? AgentIds { get; set; }
}

View file

@ -0,0 +1,9 @@
namespace BotSharp.Abstraction.Repositories.Filters;
public class ConversationFilter
{
public string? AgentId { get; set; }
public string? Status { get; set; }
public string? Channel { get; set; }
public string? UserId { get; set; }
}

View file

@ -1,3 +1,4 @@
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Users.Models;
namespace BotSharp.Abstraction.Repositories;
@ -16,8 +17,7 @@ public interface IBotSharpRepository
#region Agent
void UpdateAgent(Agent agent, AgentField field);
Agent? GetAgent(string agentId);
List<Agent> GetAgents(string? name = null, bool? disabled = null, bool? allowRouting = null,
bool? isPublic = null, List<string>? agentIds = null);
List<Agent> GetAgents(AgentFilter filter);
List<Agent> GetAgentsByUser(string userId);
void BulkInsertAgents(List<Agent> agents);
void BulkInsertUserAgents(List<UserAgent> userAgents);
@ -35,10 +35,14 @@ public interface IBotSharpRepository
void UpdateConversationStates(string conversationId, List<StateKeyValue> states);
void UpdateConversationStatus(string conversationId, string status);
Conversation GetConversation(string conversationId);
List<Conversation> GetConversations(string? agentId = null, string? status = null, string? channel = null, string? userId = null);
List<Conversation> GetConversations(ConversationFilter filter);
void UpdateConversationTitle(string conversationId, string title);
List<Conversation> GetLastConversations();
void AddExectionLogs(string conversationId, List<string> logs);
List<string> GetExectionLogs(string conversationId);
#endregion
#region LLM Completion Log
void SaveLlmCompletionLog(LlmCompletionLog log);
#endregion
}

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Repositories.Filters;
namespace BotSharp.Core.Agents.Services;
@ -9,7 +10,8 @@ public partial class AgentService
#endif
public async Task<List<Agent>> GetAgents(bool? allowRouting = null)
{
var agents = _db.GetAgents(allowRouting: allowRouting);
var filter = new AgentFilter { AllowRouting = allowRouting };
var agents = _db.GetAgents(filter);
return await Task.FromResult(agents);
}

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Users.Enums;
namespace BotSharp.Core.Conversations.Services;
@ -57,8 +58,11 @@ public partial class ConversationService : IConversationService
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var user = db.GetUserById(_user.Id);
var targetUserId = user.Role == UserRole.CSR ? null : user?.Id;
var conversations = db.GetConversations(userId: targetUserId);
var filter = new ConversationFilter
{
UserId = user.Role == UserRole.CSR ? string.Empty : user?.Id
};
var conversations = db.GetConversations(filter);
return conversations.OrderByDescending(x => x.CreatedTime).ToList();
}

View file

@ -2,6 +2,7 @@ using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Templating;
@ -72,7 +73,8 @@ public class HFPlanner : IPlaner
if (!string.IsNullOrEmpty(inst.AgentName))
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.GetAgents(inst.AgentName).FirstOrDefault();
var filter = new AgentFilter { AgentName = inst.AgentName };
var agent = db.GetAgents(filter).FirstOrDefault();
var context = _services.GetRequiredService<RoutingContext>();
context.Push(agent.Id);

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Users.Models;
using Microsoft.EntityFrameworkCore.Infrastructure;
@ -72,8 +73,7 @@ public class BotSharpDbContext : Database, IBotSharpRepository
throw new NotImplementedException();
}
public List<Agent> GetAgents(string? name = null, bool? disabled = null, bool? allowRouting = null,
bool? isPublic = null, List<string>? agentIds = null)
public List<Agent> GetAgents(AgentFilter filter)
{
throw new NotImplementedException();
}
@ -131,7 +131,7 @@ public class BotSharpDbContext : Database, IBotSharpRepository
throw new NotImplementedException();
}
public List<Conversation> GetConversations(string? agentId = null, string? status = null, string? channel = null, string? userId = null)
public List<Conversation> GetConversations(ConversationFilter filter)
{
throw new NotImplementedException();
}
@ -197,4 +197,11 @@ public class BotSharpDbContext : Database, IBotSharpRepository
throw new NotImplementedException();
}
#endregion
#region LLM Completion Log
public void SaveLlmCompletionLog(LlmCompletionLog log)
{
throw new NotImplementedException();
}
#endregion
}

View file

@ -5,6 +5,10 @@ using BotSharp.Abstraction.Users.Models;
using BotSharp.Abstraction.Agents.Models;
using MongoDB.Driver;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Utilities;
using BotSharp.Abstraction.Conversations.Models;
namespace BotSharp.Core.Repository;
public class FileRepository : IBotSharpRepository
@ -500,33 +504,32 @@ public class FileRepository : IBotSharpRepository
return null;
}
public List<Agent> GetAgents(string? name = null, bool? disabled = null, bool? allowRouting = null,
bool? isPublic = null, List<string>? agentIds = null)
public List<Agent> GetAgents(AgentFilter filter)
{
var query = Agents;
if (!string.IsNullOrEmpty(name))
if (!string.IsNullOrEmpty(filter.AgentName))
{
query = query.Where(x => x.Name.ToLower() == name.ToLower());
query = query.Where(x => x.Name.ToLower() == filter.AgentName.ToLower());
}
if (disabled.HasValue)
if (filter.Disabled.HasValue)
{
query = query.Where(x => x.Disabled == disabled);
query = query.Where(x => x.Disabled == filter.Disabled);
}
if (allowRouting.HasValue)
if (filter.AllowRouting.HasValue)
{
query = query.Where(x => x.AllowRouting == allowRouting);
query = query.Where(x => x.AllowRouting == filter.AllowRouting);
}
if (isPublic.HasValue)
if (filter.IsPublic.HasValue)
{
query = query.Where(x => x.IsPublic == isPublic);
query = query.Where(x => x.IsPublic == filter.IsPublic);
}
if (agentIds != null)
if (filter.AgentIds != null)
{
query = query.Where(x => agentIds.Contains(x.Id));
query = query.Where(x => filter.AgentIds.Contains(x.Id));
}
return query.ToList();
@ -539,7 +542,12 @@ public class FileRepository : IBotSharpRepository
where ua.UserId == userId || u.ExternalId == userId
select ua.AgentId).ToList();
var agents = GetAgents(isPublic: true, agentIds: agentIds);
var filter = new AgentFilter
{
IsPublic = true,
AgentIds = agentIds
};
var agents = GetAgents(filter);
return agents;
}
@ -552,7 +560,7 @@ public class FileRepository : IBotSharpRepository
foreach (var file in Directory.GetFiles(dir))
{
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
var splits = fileName.ToLower().Split('.');
var splits = ParseFileNameByPath(fileName.ToLower());
var name = splits[0];
var extension = splits[1];
if (name.IsEqualTo(templateName) && extension.IsEqualTo(_agentSettings.TemplateFormat))
@ -610,10 +618,10 @@ public class FileRepository : IBotSharpRepository
{
if (string.IsNullOrEmpty(conversationId)) return false;
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversationId);
if (!Directory.Exists(dir)) return false;
var convDir = FindConversationDirectory(conversationId);
if (string.IsNullOrEmpty(convDir)) return false;
Directory.Delete(dir, true);
Directory.Delete(convDir, true);
return true;
}
@ -734,7 +742,7 @@ public class FileRepository : IBotSharpRepository
return record;
}
public List<Conversation> GetConversations(string? agentId = null, string? status = null, string? channel = null, string? userId = null)
public List<Conversation> GetConversations(ConversationFilter filter)
{
var records = new List<Conversation>();
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
@ -749,10 +757,10 @@ public class FileRepository : IBotSharpRepository
if (record == null) continue;
var matched = true;
if (!string.IsNullOrEmpty(agentId)) matched = matched && record.AgentId == agentId;
if (!string.IsNullOrEmpty(status)) matched = matched && record.Status == status;
if (!string.IsNullOrEmpty(channel)) matched = matched && record.Channel == channel;
if (!string.IsNullOrEmpty(userId)) matched = matched && record.UserId == userId;
if (!string.IsNullOrEmpty(filter.AgentId)) matched = matched && record.AgentId == filter.AgentId;
if (!string.IsNullOrEmpty(filter.Status)) matched = matched && record.Status == filter.Status;
if (!string.IsNullOrEmpty(filter.Channel)) matched = matched && record.Channel == filter.Channel;
if (!string.IsNullOrEmpty(filter.UserId)) matched = matched && record.UserId == filter.UserId;
if (!matched) continue;
records.Add(record);
@ -835,6 +843,25 @@ public class FileRepository : IBotSharpRepository
}
#endregion
#region LLM Completion Log
public void SaveLlmCompletionLog(LlmCompletionLog log)
{
var convDir = FindConversationDirectory(log.ConversationId);
if (!Directory.Exists(convDir)) return;
var logDir = Path.Combine(convDir, "llm_prompt_log");
if (!Directory.Exists(logDir))
{
Directory.CreateDirectory(logDir);
}
var index = GetLlmCompletionLogIndex(logDir, log.MessageId);
var file = Path.Combine(logDir, $"{log.MessageId}.{index}.log");
File.WriteAllText(file, JsonSerializer.Serialize(log, _options));
}
#endregion
#region Private methods
private string GetAgentDataDir(string agentId)
{
@ -927,22 +954,10 @@ public class FileRepository : IBotSharpRepository
private string? FindConversationDirectory(string conversationId)
{
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversationId);
if (!Directory.Exists(dir)) return null;
foreach (var d in Directory.GetDirectories(dir))
{
var path = Path.Combine(d, "conversation.json");
if (!File.Exists(path)) continue;
var json = File.ReadAllText(path);
var conv = JsonSerializer.Deserialize<Conversation>(json, _options);
if (conv != null && conv.Id == conversationId)
{
return d;
}
}
return null;
return dir;
}
private List<DialogElement> CollectDialogElements(string dialogDir)
@ -993,5 +1008,30 @@ public class FileRepository : IBotSharpRepository
}
return states;
}
private int GetLlmCompletionLogIndex(string logDir, string id)
{
var files = Directory.GetFiles(logDir);
if (files.IsNullOrEmpty())
return 0;
var logIndexes = files.Where(file =>
{
var fileName = ParseFileNameByPath(file);
return fileName[0].IsEqualTo(id);
}).Select(file =>
{
var fileName = ParseFileNameByPath(file);
return int.Parse(fileName[1]);
}).ToList();
return logIndexes.IsNullOrEmpty() ? 0 : logIndexes.Max() + 1;
}
private string[] ParseFileNameByPath(string path, string separator = ".")
{
var name = path.Split(Path.DirectorySeparatorChar).Last();
return name.Split(separator);
}
#endregion
}

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.Functions;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Models;
using System.Drawing;
@ -29,7 +30,8 @@ public class RouteToAgentFn : IFunctionCallback
if (!string.IsNullOrEmpty(args.OriginalAgent) && args.OriginalAgent.Length < 32)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var originalAgent = db.GetAgents(name: args.OriginalAgent).FirstOrDefault();
var filter = new AgentFilter { AgentName = args.OriginalAgent };
var originalAgent = db.GetAgents(filter).FirstOrDefault();
if (originalAgent != null)
{
_context.Push(originalAgent.Id);
@ -48,7 +50,8 @@ public class RouteToAgentFn : IFunctionCallback
else
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var targetAgent = db.GetAgents(args.AgentName).FirstOrDefault();
var filter = new AgentFilter { AgentName = args.AgentName };
var targetAgent = db.GetAgents(filter).FirstOrDefault();
if (targetAgent == null)
{
message.Data = JsonSerializer.Deserialize<JsonElement>(message.FunctionArgs);

View file

@ -1,6 +1,7 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Core.Planning;
@ -37,7 +38,8 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase//, IRoutingH
public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var record = db.GetAgents(inst.AgentName).FirstOrDefault();
var filter = new AgentFilter { AgentName = inst.AgentName };
var record = db.GetAgents(filter).FirstOrDefault();
message.FunctionName = inst.Function;
message.CurrentAgentId = record.Id;

View file

@ -2,6 +2,7 @@ using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Settings;
@ -130,7 +131,12 @@ public partial class RoutingService : IRoutingService
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agents = db.GetAgents(disabled: false, allowRouting: true);
var filter = new AgentFilter
{
Disabled = false,
AllowRouting = true
};
var agents = db.GetAgents(filter);
var records = agents.SelectMany(x =>
{
x.RoutingRules.ForEach(r =>
@ -160,7 +166,12 @@ public partial class RoutingService : IRoutingService
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agents = db.GetAgents(disabled: false, allowRouting: true);
var filter = new AgentFilter
{
Disabled = false,
AllowRouting = true
};
var agents = db.GetAgents(filter);
return agents.Select(x => new RoutingItem
{
AgentId = x.Id,

View file

@ -0,0 +1,11 @@
namespace BotSharp.Plugin.MongoStorage.Collections;
public class LlmCompletionLogCollection : MongoBase
{
public string ConversationId { get; set; }
public string MessageId { get; set; }
public string AgentId { get; set; }
public string Prompt { get; set; }
public string? Response { get; set; }
public DateTime CreateDateTime { get; set; }
}

View file

@ -1,5 +1,3 @@
using MongoDB.Bson.Serialization.Attributes;
namespace BotSharp.Plugin.MongoStorage;
[BsonIgnoreExtraElements(Inherited = true)]

View file

@ -45,4 +45,7 @@ public class MongoDbContext
public IMongoCollection<UserAgentCollection> UserAgents
=> Database.GetCollection<UserAgentCollection>($"{_collectionPrefix}_UserAgents");
public IMongoCollection<LlmCompletionLogCollection> LlmCompletionLogs
=> Database.GetCollection<LlmCompletionLogCollection>($"{_collectionPrefix}_Llm_Completion_Logs");
}

View file

@ -1,8 +1,10 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Users.Models;
using BotSharp.Abstraction.Utilities;
using BotSharp.Plugin.MongoStorage.Collections;
using BotSharp.Plugin.MongoStorage.Models;
@ -416,35 +418,34 @@ public class MongoRepository : IBotSharpRepository
};
}
public List<Agent> GetAgents(string? name = null, bool? disabled = null, bool? allowRouting = null,
bool? isPublic = null, List<string>? agentIds = null)
public List<Agent> GetAgents(AgentFilter filter)
{
var agents = new List<Agent>();
IQueryable<AgentCollection> query = _dc.Agents.AsQueryable();
if (!string.IsNullOrEmpty(name))
if (!string.IsNullOrEmpty(filter.AgentName))
{
query = query.Where(x => x.Name.ToLower() == name.ToLower());
query = query.Where(x => x.Name.ToLower() == filter.AgentName.ToLower());
}
if (disabled.HasValue)
if (filter.Disabled.HasValue)
{
query = query.Where(x => x.Disabled == disabled);
query = query.Where(x => x.Disabled == filter.Disabled);
}
if (allowRouting.HasValue)
if (filter.AllowRouting.HasValue)
{
query = query.Where(x => x.AllowRouting == allowRouting);
query = query.Where(x => x.AllowRouting == filter.AllowRouting);
}
if (isPublic.HasValue)
if (filter.IsPublic.HasValue)
{
query = query.Where(x => x.IsPublic == isPublic);
query = query.Where(x => x.IsPublic == filter.IsPublic);
}
if (agentIds != null)
if (filter.AgentIds != null)
{
query = query.Where(x => agentIds.Contains(x.Id));
query = query.Where(x => filter.AgentIds.Contains(x.Id));
}
return query.ToList().Select(x => new Agent
@ -480,7 +481,12 @@ public class MongoRepository : IBotSharpRepository
where ua.UserId == userId || u.ExternalId == userId
select ua.AgentId).ToList();
var agents = GetAgents(isPublic: true, agentIds: agentIds);
var filter = new AgentFilter
{
IsPublic = true,
AgentIds = agentIds
};
var agents = GetAgents(filter);
return agents;
}
@ -726,18 +732,16 @@ public class MongoRepository : IBotSharpRepository
};
}
public List<Conversation> GetConversations(string? agentId = null, string? status = null, string? channel = null, string? userId = null)
public List<Conversation> GetConversations(ConversationFilter filter)
{
var records = new List<Conversation>();
if (string.IsNullOrEmpty(userId)) return records;
var builder = Builders<ConversationCollection>.Filter;
var filters = new List<FilterDefinition<ConversationCollection>>();
if (!string.IsNullOrEmpty(agentId)) filters.Add(builder.Eq(x => x.AgentId, agentId));
if (!string.IsNullOrEmpty(status)) filters.Add(builder.Eq(x => x.Status, status));
if (!string.IsNullOrEmpty(channel)) filters.Add(builder.Eq(x => x.Channel, channel));
if (!string.IsNullOrEmpty(userId)) filters.Add(builder.Eq(x => x.UserId, userId));
if (!string.IsNullOrEmpty(filter.AgentId)) filters.Add(builder.Eq(x => x.AgentId, filter.AgentId));
if (!string.IsNullOrEmpty(filter.Status)) filters.Add(builder.Eq(x => x.Status, filter.Status));
if (!string.IsNullOrEmpty(filter.Channel)) filters.Add(builder.Eq(x => x.Channel, filter.Channel));
if (!string.IsNullOrEmpty(filter.UserId)) filters.Add(builder.Eq(x => x.UserId, filter.UserId));
var conversations = _dc.Conversations.Find(builder.And(filters)).ToList();
@ -858,4 +862,22 @@ public class MongoRepository : IBotSharpRepository
_dc.Users.InsertOne(userCollection);
}
#endregion
#region LLM Completion Log
public void SaveLlmCompletionLog(LlmCompletionLog log)
{
var completiongLog = new LlmCompletionLogCollection
{
Id = string.IsNullOrEmpty(log.Id) ? Guid.NewGuid().ToString() : log.Id,
ConversationId = log.ConversationId,
MessageId = log.MessageId,
AgentId = log.AgentId,
Prompt = log.Prompt,
Response = log.Response,
CreateDateTime = log.CreateDateTime
};
_dc.LlmCompletionLogs.InsertOne(completiongLog);
}
#endregion
}