Merge pull request #250 from iceljc/features/add-mongo-set-plugins
add mongo plugin actions
This commit is contained in:
commit
0bacd32ed5
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,422 @@
|
||||||
|
using BotSharp.Abstraction.Agents.Models;
|
||||||
|
using BotSharp.Abstraction.Evaluations.Settings;
|
||||||
|
using BotSharp.Abstraction.Functions.Models;
|
||||||
|
using BotSharp.Abstraction.Repositories.Filters;
|
||||||
|
using BotSharp.Abstraction.Routing.Models;
|
||||||
|
using BotSharp.Abstraction.Routing.Settings;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BotSharp.Core.Repository
|
||||||
|
{
|
||||||
|
public partial class FileRepository
|
||||||
|
{
|
||||||
|
public void UpdateAgent(Agent agent, AgentField field)
|
||||||
|
{
|
||||||
|
if (agent == null || string.IsNullOrEmpty(agent.Id)) return;
|
||||||
|
|
||||||
|
switch (field)
|
||||||
|
{
|
||||||
|
case AgentField.Name:
|
||||||
|
UpdateAgentName(agent.Id, agent.Name);
|
||||||
|
break;
|
||||||
|
case AgentField.Description:
|
||||||
|
UpdateAgentDescription(agent.Id, agent.Description);
|
||||||
|
break;
|
||||||
|
case AgentField.IsPublic:
|
||||||
|
UpdateAgentIsPublic(agent.Id, agent.IsPublic);
|
||||||
|
break;
|
||||||
|
case AgentField.Disabled:
|
||||||
|
UpdateAgentDisabled(agent.Id, agent.Disabled);
|
||||||
|
break;
|
||||||
|
case AgentField.AllowRouting:
|
||||||
|
UpdateAgentAllowRouting(agent.Id, agent.AllowRouting);
|
||||||
|
break;
|
||||||
|
case AgentField.Profiles:
|
||||||
|
UpdateAgentProfiles(agent.Id, agent.Profiles);
|
||||||
|
break;
|
||||||
|
case AgentField.RoutingRule:
|
||||||
|
UpdateAgentRoutingRules(agent.Id, agent.RoutingRules);
|
||||||
|
break;
|
||||||
|
case AgentField.Instruction:
|
||||||
|
UpdateAgentInstruction(agent.Id, agent.Instruction);
|
||||||
|
break;
|
||||||
|
case AgentField.Function:
|
||||||
|
UpdateAgentFunctions(agent.Id, agent.Functions);
|
||||||
|
break;
|
||||||
|
case AgentField.Template:
|
||||||
|
UpdateAgentTemplates(agent.Id, agent.Templates);
|
||||||
|
break;
|
||||||
|
case AgentField.Response:
|
||||||
|
UpdateAgentResponses(agent.Id, agent.Responses);
|
||||||
|
break;
|
||||||
|
case AgentField.Sample:
|
||||||
|
UpdateAgentSamples(agent.Id, agent.Samples);
|
||||||
|
break;
|
||||||
|
case AgentField.LlmConfig:
|
||||||
|
UpdateAgentLlmConfig(agent.Id, agent.LlmConfig);
|
||||||
|
break;
|
||||||
|
case AgentField.All:
|
||||||
|
UpdateAgentAllFields(agent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Update Agent Fields
|
||||||
|
private void UpdateAgentName(string agentId, string name)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name)) return;
|
||||||
|
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
agent.Name = name;
|
||||||
|
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||||
|
var json = JsonSerializer.Serialize(agent, _options);
|
||||||
|
File.WriteAllText(agentFile, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentDescription(string agentId, string description)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(description)) return;
|
||||||
|
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
agent.Description = description;
|
||||||
|
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||||
|
var json = JsonSerializer.Serialize(agent, _options);
|
||||||
|
File.WriteAllText(agentFile, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentIsPublic(string agentId, bool isPublic)
|
||||||
|
{
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
agent.IsPublic = isPublic;
|
||||||
|
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||||
|
var json = JsonSerializer.Serialize(agent, _options);
|
||||||
|
File.WriteAllText(agentFile, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentDisabled(string agentId, bool disabled)
|
||||||
|
{
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
agent.Disabled = disabled;
|
||||||
|
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||||
|
var json = JsonSerializer.Serialize(agent, _options);
|
||||||
|
File.WriteAllText(agentFile, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentAllowRouting(string agentId, bool allowRouting)
|
||||||
|
{
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
agent.AllowRouting = allowRouting;
|
||||||
|
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||||
|
var json = JsonSerializer.Serialize(agent, _options);
|
||||||
|
File.WriteAllText(agentFile, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentProfiles(string agentId, List<string> profiles)
|
||||||
|
{
|
||||||
|
if (profiles.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
agent.Profiles = profiles;
|
||||||
|
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||||
|
var json = JsonSerializer.Serialize(agent, _options);
|
||||||
|
File.WriteAllText(agentFile, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentRoutingRules(string agentId, List<RoutingRule> rules)
|
||||||
|
{
|
||||||
|
if (rules.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
agent.RoutingRules = rules;
|
||||||
|
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||||
|
var json = JsonSerializer.Serialize(agent, _options);
|
||||||
|
File.WriteAllText(agentFile, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentInstruction(string agentId, string instruction)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(instruction)) return;
|
||||||
|
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
var instructionFile = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir,
|
||||||
|
agentId, $"{AGENT_INSTRUCTION_FILE}.{_agentSettings.TemplateFormat}");
|
||||||
|
|
||||||
|
File.WriteAllText(instructionFile, instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentFunctions(string agentId, List<FunctionDef> inputFunctions)
|
||||||
|
{
|
||||||
|
if (inputFunctions.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
var functionFile = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir,
|
||||||
|
agentId, AGENT_FUNCTIONS_FILE);
|
||||||
|
|
||||||
|
var functionText = JsonSerializer.Serialize(inputFunctions, _options);
|
||||||
|
File.WriteAllText(functionFile, functionText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentTemplates(string agentId, List<AgentTemplate> templates)
|
||||||
|
{
|
||||||
|
if (templates.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
var templateDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "templates");
|
||||||
|
|
||||||
|
if (!Directory.Exists(templateDir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(templateDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var file in Directory.GetFiles(templateDir))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var template in templates)
|
||||||
|
{
|
||||||
|
var file = Path.Combine(templateDir, $"{template.Name}.{_agentSettings.TemplateFormat}");
|
||||||
|
File.WriteAllText(file, template.Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentResponses(string agentId, List<AgentResponse> responses)
|
||||||
|
{
|
||||||
|
if (responses.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
var responseDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "responses");
|
||||||
|
if (!Directory.Exists(responseDir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(responseDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var file in Directory.GetFiles(responseDir))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < responses.Count; i++)
|
||||||
|
{
|
||||||
|
var response = responses[i];
|
||||||
|
var fileName = $"{response.Prefix}.{response.Intent}.{i}.{_agentSettings.TemplateFormat}";
|
||||||
|
var file = Path.Combine(responseDir, fileName);
|
||||||
|
File.WriteAllText(file, response.Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentSamples(string agentId, List<string> samples)
|
||||||
|
{
|
||||||
|
if (samples.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
var file = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_SAMPLES_FILE);
|
||||||
|
File.WriteAllLines(file, samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentLlmConfig(string agentId, AgentLlmConfig? config)
|
||||||
|
{
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
agent.LlmConfig = config;
|
||||||
|
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||||
|
var json = JsonSerializer.Serialize(agent, _options);
|
||||||
|
File.WriteAllText(agentFile, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentAllFields(Agent inputAgent)
|
||||||
|
{
|
||||||
|
var (agent, agentFile) = GetAgentFromFile(inputAgent.Id);
|
||||||
|
if (agent == null) return;
|
||||||
|
|
||||||
|
agent.Name = inputAgent.Name;
|
||||||
|
agent.Description = inputAgent.Description;
|
||||||
|
agent.IsPublic = inputAgent.IsPublic;
|
||||||
|
agent.Disabled = inputAgent.Disabled;
|
||||||
|
agent.AllowRouting = inputAgent.AllowRouting;
|
||||||
|
agent.Profiles = inputAgent.Profiles;
|
||||||
|
agent.RoutingRules = inputAgent.RoutingRules;
|
||||||
|
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||||
|
var json = JsonSerializer.Serialize(agent, _options);
|
||||||
|
File.WriteAllText(agentFile, json);
|
||||||
|
|
||||||
|
UpdateAgentInstruction(inputAgent.Id, inputAgent.Instruction);
|
||||||
|
UpdateAgentResponses(inputAgent.Id, inputAgent.Responses);
|
||||||
|
UpdateAgentTemplates(inputAgent.Id, inputAgent.Templates);
|
||||||
|
UpdateAgentFunctions(inputAgent.Id, inputAgent.Functions);
|
||||||
|
UpdateAgentSamples(inputAgent.Id, inputAgent.Samples);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public List<string> GetAgentResponses(string agentId, string prefix, string intent)
|
||||||
|
{
|
||||||
|
var responses = new List<string>();
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "responses");
|
||||||
|
if (!Directory.Exists(dir)) return responses;
|
||||||
|
|
||||||
|
foreach (var file in Directory.GetFiles(dir))
|
||||||
|
{
|
||||||
|
if (file.Split(Path.DirectorySeparatorChar)
|
||||||
|
.Last()
|
||||||
|
.StartsWith(prefix + "." + intent))
|
||||||
|
{
|
||||||
|
responses.Add(File.ReadAllText(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Agent? GetAgent(string agentId)
|
||||||
|
{
|
||||||
|
var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir);
|
||||||
|
var dir = Directory.GetDirectories(agentDir).FirstOrDefault(x => x.Split(Path.DirectorySeparatorChar).Last() == agentId);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(dir))
|
||||||
|
{
|
||||||
|
var json = File.ReadAllText(Path.Combine(dir, AGENT_FILE));
|
||||||
|
if (string.IsNullOrEmpty(json)) return null;
|
||||||
|
|
||||||
|
var record = JsonSerializer.Deserialize<Agent>(json, _options);
|
||||||
|
if (record == null) return null;
|
||||||
|
|
||||||
|
var instruction = FetchInstruction(dir);
|
||||||
|
var functions = FetchFunctions(dir);
|
||||||
|
var samples = FetchSamples(dir);
|
||||||
|
var templates = FetchTemplates(dir);
|
||||||
|
var responses = FetchResponses(dir);
|
||||||
|
return record.SetInstruction(instruction)
|
||||||
|
.SetFunctions(functions)
|
||||||
|
.SetSamples(samples)
|
||||||
|
.SetTemplates(templates)
|
||||||
|
.SetResponses(responses);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Agent> GetAgents(AgentFilter filter)
|
||||||
|
{
|
||||||
|
var query = Agents;
|
||||||
|
if (!string.IsNullOrEmpty(filter.AgentName))
|
||||||
|
{
|
||||||
|
query = query.Where(x => x.Name.ToLower() == filter.AgentName.ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.Disabled.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(x => x.Disabled == filter.Disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.AllowRouting.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(x => x.AllowRouting == filter.AllowRouting);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.IsPublic.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(x => x.IsPublic == filter.IsPublic);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.IsRouter.HasValue)
|
||||||
|
{
|
||||||
|
var route = _services.GetRequiredService<RoutingSettings>();
|
||||||
|
query = filter.IsRouter.Value ?
|
||||||
|
query.Where(x => x.Id == route.AgentId) :
|
||||||
|
query.Where(x => x.Id != route.AgentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.IsEvaluator.HasValue)
|
||||||
|
{
|
||||||
|
var evaluate = _services.GetRequiredService<EvaluatorSetting>();
|
||||||
|
query = filter.IsEvaluator.Value ?
|
||||||
|
query.Where(x => x.Id == evaluate.AgentId) :
|
||||||
|
query.Where(x => x.Id != evaluate.AgentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.AgentIds != null)
|
||||||
|
{
|
||||||
|
query = query.Where(x => filter.AgentIds.Contains(x.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Agent> GetAgentsByUser(string userId)
|
||||||
|
{
|
||||||
|
var agentIds = (from ua in UserAgents
|
||||||
|
join u in Users on ua.UserId equals u.Id
|
||||||
|
where ua.UserId == userId || u.ExternalId == userId
|
||||||
|
select ua.AgentId).ToList();
|
||||||
|
|
||||||
|
var filter = new AgentFilter
|
||||||
|
{
|
||||||
|
IsPublic = true,
|
||||||
|
AgentIds = agentIds
|
||||||
|
};
|
||||||
|
var agents = GetAgents(filter);
|
||||||
|
return agents;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string GetAgentTemplate(string agentId, string templateName)
|
||||||
|
{
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "templates");
|
||||||
|
if (!Directory.Exists(dir)) return string.Empty;
|
||||||
|
|
||||||
|
foreach (var file in Directory.GetFiles(dir))
|
||||||
|
{
|
||||||
|
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
|
||||||
|
var splits = ParseFileNameByPath(fileName.ToLower());
|
||||||
|
var name = splits[0];
|
||||||
|
var extension = splits[1];
|
||||||
|
if (name.IsEqualTo(templateName) && extension.IsEqualTo(_agentSettings.TemplateFormat))
|
||||||
|
{
|
||||||
|
return File.ReadAllText(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BulkInsertAgents(List<Agent> agents)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BulkInsertUserAgents(List<UserAgent> userAgents)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DeleteAgents()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,324 @@
|
||||||
|
using BotSharp.Abstraction.Repositories.Filters;
|
||||||
|
using BotSharp.Abstraction.Repositories.Models;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BotSharp.Core.Repository
|
||||||
|
{
|
||||||
|
public partial class FileRepository
|
||||||
|
{
|
||||||
|
public void CreateNewConversation(Conversation conversation)
|
||||||
|
{
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversation.Id);
|
||||||
|
if (!Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
var convFile = Path.Combine(dir, CONVERSATION_FILE);
|
||||||
|
if (!File.Exists(convFile))
|
||||||
|
{
|
||||||
|
File.WriteAllText(convFile, JsonSerializer.Serialize(conversation, _options));
|
||||||
|
}
|
||||||
|
|
||||||
|
var dialogFile = Path.Combine(dir, DIALOG_FILE);
|
||||||
|
if (!File.Exists(dialogFile))
|
||||||
|
{
|
||||||
|
File.WriteAllText(dialogFile, string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
var stateFile = Path.Combine(dir, STATE_FILE);
|
||||||
|
if (!File.Exists(stateFile))
|
||||||
|
{
|
||||||
|
var states = conversation.States ?? new Dictionary<string, string>();
|
||||||
|
var initialStates = states.Select(x => new StateKeyValue
|
||||||
|
{
|
||||||
|
Key = x.Key,
|
||||||
|
Values = new List<StateValue>
|
||||||
|
{
|
||||||
|
new StateValue { Data = x.Value, UpdateTime = DateTime.UtcNow }
|
||||||
|
}
|
||||||
|
}).ToList();
|
||||||
|
File.WriteAllText(stateFile, JsonSerializer.Serialize(initialStates, _options));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DeleteConversation(string conversationId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId)) return false;
|
||||||
|
|
||||||
|
var convDir = FindConversationDirectory(conversationId);
|
||||||
|
if (string.IsNullOrEmpty(convDir)) return false;
|
||||||
|
|
||||||
|
Directory.Delete(convDir, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DialogElement> GetConversationDialogs(string conversationId)
|
||||||
|
{
|
||||||
|
var dialogs = new List<DialogElement>();
|
||||||
|
var convDir = FindConversationDirectory(conversationId);
|
||||||
|
if (!string.IsNullOrEmpty(convDir))
|
||||||
|
{
|
||||||
|
var dialogDir = Path.Combine(convDir, DIALOG_FILE);
|
||||||
|
dialogs = CollectDialogElements(dialogDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConversationDialogElements(string conversationId, List<DialogContentUpdateModel> updateElements)
|
||||||
|
{
|
||||||
|
var dialogElements = GetConversationDialogs(conversationId);
|
||||||
|
if (dialogElements.IsNullOrEmpty() || updateElements.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var convDir = FindConversationDirectory(conversationId);
|
||||||
|
if (!string.IsNullOrEmpty(convDir))
|
||||||
|
{
|
||||||
|
var dialogDir = Path.Combine(convDir, DIALOG_FILE);
|
||||||
|
if (File.Exists(dialogDir))
|
||||||
|
{
|
||||||
|
var updated = dialogElements.Select((x, idx) =>
|
||||||
|
{
|
||||||
|
var found = updateElements.FirstOrDefault(e => e.Index == idx);
|
||||||
|
if (found != null)
|
||||||
|
{
|
||||||
|
x.Content = found.UpdateContent;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var texts = ParseDialogElements(updated);
|
||||||
|
File.WriteAllLines(dialogDir, texts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AppendConversationDialogs(string conversationId, List<DialogElement> dialogs)
|
||||||
|
{
|
||||||
|
var convDir = FindConversationDirectory(conversationId);
|
||||||
|
if (!string.IsNullOrEmpty(convDir))
|
||||||
|
{
|
||||||
|
var dialogDir = Path.Combine(convDir, DIALOG_FILE);
|
||||||
|
if (File.Exists(dialogDir))
|
||||||
|
{
|
||||||
|
var texts = ParseDialogElements(dialogs);
|
||||||
|
File.AppendAllLines(dialogDir, texts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConversationTitle(string conversationId, string title)
|
||||||
|
{
|
||||||
|
var convDir = FindConversationDirectory(conversationId);
|
||||||
|
if (!string.IsNullOrEmpty(convDir))
|
||||||
|
{
|
||||||
|
var convFile = Path.Combine(convDir, CONVERSATION_FILE);
|
||||||
|
var content = File.ReadAllText(convFile);
|
||||||
|
var record = JsonSerializer.Deserialize<Conversation>(content, _options);
|
||||||
|
if (record != null)
|
||||||
|
{
|
||||||
|
record.Title = title;
|
||||||
|
record.UpdatedTime = DateTime.UtcNow;
|
||||||
|
File.WriteAllText(convFile, JsonSerializer.Serialize(record, _options));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConversationState GetConversationStates(string conversationId)
|
||||||
|
{
|
||||||
|
var states = new List<StateKeyValue>();
|
||||||
|
var convDir = FindConversationDirectory(conversationId);
|
||||||
|
if (!string.IsNullOrEmpty(convDir))
|
||||||
|
{
|
||||||
|
var stateFile = Path.Combine(convDir, STATE_FILE);
|
||||||
|
states = CollectConversationStates(stateFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConversationState(states);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
||||||
|
{
|
||||||
|
if (states.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var convDir = FindConversationDirectory(conversationId);
|
||||||
|
if (!string.IsNullOrEmpty(convDir))
|
||||||
|
{
|
||||||
|
var stateFile = Path.Combine(convDir, STATE_FILE);
|
||||||
|
if (File.Exists(stateFile))
|
||||||
|
{
|
||||||
|
var stateStr = JsonSerializer.Serialize(states, _options);
|
||||||
|
File.WriteAllText(stateFile, stateStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConversationStatus(string conversationId, string status)
|
||||||
|
{
|
||||||
|
var convDir = FindConversationDirectory(conversationId);
|
||||||
|
if (!string.IsNullOrEmpty(convDir))
|
||||||
|
{
|
||||||
|
var convFile = Path.Combine(convDir, CONVERSATION_FILE);
|
||||||
|
if (File.Exists(convFile))
|
||||||
|
{
|
||||||
|
var json = File.ReadAllText(convFile);
|
||||||
|
var conv = JsonSerializer.Deserialize<Conversation>(json, _options);
|
||||||
|
conv.Status = status;
|
||||||
|
conv.UpdatedTime = DateTime.UtcNow;
|
||||||
|
File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Conversation GetConversation(string conversationId)
|
||||||
|
{
|
||||||
|
var convDir = FindConversationDirectory(conversationId);
|
||||||
|
if (string.IsNullOrEmpty(convDir)) return null;
|
||||||
|
|
||||||
|
var convFile = Path.Combine(convDir, CONVERSATION_FILE);
|
||||||
|
var content = File.ReadAllText(convFile);
|
||||||
|
var record = JsonSerializer.Deserialize<Conversation>(content, _options);
|
||||||
|
|
||||||
|
var dialogFile = Path.Combine(convDir, DIALOG_FILE);
|
||||||
|
if (record != null)
|
||||||
|
{
|
||||||
|
record.Dialogs = CollectDialogElements(dialogFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
var stateFile = Path.Combine(convDir, STATE_FILE);
|
||||||
|
if (record != null)
|
||||||
|
{
|
||||||
|
var states = CollectConversationStates(stateFile);
|
||||||
|
var curStates = new Dictionary<string, string>();
|
||||||
|
states.ForEach(x =>
|
||||||
|
{
|
||||||
|
curStates[x.Key] = x.Values?.LastOrDefault()?.Data ?? string.Empty;
|
||||||
|
});
|
||||||
|
record.States = curStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Conversation> GetConversations(ConversationFilter filter)
|
||||||
|
{
|
||||||
|
var records = new List<Conversation>();
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
|
||||||
|
|
||||||
|
foreach (var d in Directory.GetDirectories(dir))
|
||||||
|
{
|
||||||
|
var path = Path.Combine(d, CONVERSATION_FILE);
|
||||||
|
if (!File.Exists(path)) continue;
|
||||||
|
|
||||||
|
var json = File.ReadAllText(path);
|
||||||
|
var record = JsonSerializer.Deserialize<Conversation>(json, _options);
|
||||||
|
if (record == null) continue;
|
||||||
|
|
||||||
|
var matched = true;
|
||||||
|
if (filter.Id != null) matched = matched && record.Id == filter.Id;
|
||||||
|
if (filter.AgentId != null) matched = matched && record.AgentId == filter.AgentId;
|
||||||
|
if (filter.Status != null) matched = matched && record.Status == filter.Status;
|
||||||
|
if (filter.Channel != null) matched = matched && record.Channel == filter.Channel;
|
||||||
|
if (filter.UserId != null) matched = matched && record.UserId == filter.UserId;
|
||||||
|
|
||||||
|
if (!matched) continue;
|
||||||
|
records.Add(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Conversation> GetLastConversations()
|
||||||
|
{
|
||||||
|
var records = new List<Conversation>();
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
|
||||||
|
|
||||||
|
foreach (var d in Directory.GetDirectories(dir))
|
||||||
|
{
|
||||||
|
var path = Path.Combine(d, CONVERSATION_FILE);
|
||||||
|
if (!File.Exists(path)) continue;
|
||||||
|
|
||||||
|
var json = File.ReadAllText(path);
|
||||||
|
var record = JsonSerializer.Deserialize<Conversation>(json, _options);
|
||||||
|
if (record == null) continue;
|
||||||
|
|
||||||
|
records.Add(record);
|
||||||
|
}
|
||||||
|
return records.GroupBy(r => r.UserId)
|
||||||
|
.Select(g => g.OrderByDescending(x => x.CreatedTime).First())
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
private string? FindConversationDirectory(string conversationId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId)) return null;
|
||||||
|
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversationId);
|
||||||
|
if (!Directory.Exists(dir)) return null;
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DialogElement> CollectDialogElements(string dialogDir)
|
||||||
|
{
|
||||||
|
var dialogs = new List<DialogElement>();
|
||||||
|
|
||||||
|
if (!File.Exists(dialogDir)) return dialogs;
|
||||||
|
|
||||||
|
var rawDialogs = File.ReadAllLines(dialogDir);
|
||||||
|
if (!rawDialogs.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
for (int i = 0; i < rawDialogs.Count(); i += 2)
|
||||||
|
{
|
||||||
|
var blocks = rawDialogs[i].Split("|");
|
||||||
|
var content = rawDialogs[i + 1];
|
||||||
|
var trimmed = content.Substring(4);
|
||||||
|
var meta = new DialogMeta
|
||||||
|
{
|
||||||
|
Role = blocks[1],
|
||||||
|
AgentId = blocks[2],
|
||||||
|
MessageId = blocks[3],
|
||||||
|
FunctionName = blocks[1] == AgentRole.Function ? blocks[4] : null,
|
||||||
|
SenderId = blocks[1] == AgentRole.Function ? null : blocks[4],
|
||||||
|
CreateTime = DateTime.Parse(blocks[0])
|
||||||
|
};
|
||||||
|
dialogs.Add(new DialogElement(meta, trimmed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dialogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> ParseDialogElements(List<DialogElement> dialogs)
|
||||||
|
{
|
||||||
|
var dialogTexts = new List<string>();
|
||||||
|
if (dialogs.IsNullOrEmpty()) return dialogTexts;
|
||||||
|
|
||||||
|
foreach (var element in dialogs)
|
||||||
|
{
|
||||||
|
var meta = element.MetaData;
|
||||||
|
var source = meta.FunctionName ?? meta.SenderId;
|
||||||
|
var metaStr = $"{meta.CreateTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{source}";
|
||||||
|
dialogTexts.Add(metaStr);
|
||||||
|
var content = $" - {element.Content}";
|
||||||
|
dialogTexts.Add(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialogTexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<StateKeyValue> CollectConversationStates(string stateFile)
|
||||||
|
{
|
||||||
|
var states = new List<StateKeyValue>();
|
||||||
|
if (!File.Exists(stateFile)) return states;
|
||||||
|
|
||||||
|
var stateStr = File.ReadAllText(stateFile);
|
||||||
|
if (string.IsNullOrEmpty(stateStr)) return states;
|
||||||
|
|
||||||
|
states = JsonSerializer.Deserialize<List<StateKeyValue>>(stateStr, _options);
|
||||||
|
return states ?? new List<StateKeyValue>();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BotSharp.Core.Repository
|
||||||
|
{
|
||||||
|
public partial class FileRepository
|
||||||
|
{
|
||||||
|
#region Execution Log
|
||||||
|
public void AddExecutionLogs(string conversationId, List<string> logs)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId) || logs.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, "conversations", conversationId);
|
||||||
|
if (!Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = Path.Combine(dir, EXECUTION_LOG_FILE);
|
||||||
|
File.AppendAllLines(file, logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetExecutionLogs(string conversationId)
|
||||||
|
{
|
||||||
|
var logs = new List<string>();
|
||||||
|
if (string.IsNullOrEmpty(conversationId)) return logs;
|
||||||
|
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, "conversations", conversationId);
|
||||||
|
if (!Directory.Exists(dir)) return logs;
|
||||||
|
|
||||||
|
var file = Path.Combine(dir, EXECUTION_LOG_FILE);
|
||||||
|
logs = File.ReadAllLines(file)?.ToList() ?? new List<string>();
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region LLM Completion Log
|
||||||
|
public void SaveLlmCompletionLog(LlmCompletionLog log)
|
||||||
|
{
|
||||||
|
if (log == null) return;
|
||||||
|
|
||||||
|
log.ConversationId = log.ConversationId.IfNullOrEmptyAs(Guid.NewGuid().ToString());
|
||||||
|
log.MessageId = log.MessageId.IfNullOrEmptyAs(Guid.NewGuid().ToString());
|
||||||
|
|
||||||
|
var convDir = FindConversationDirectory(log.ConversationId);
|
||||||
|
if (string.IsNullOrEmpty(convDir))
|
||||||
|
{
|
||||||
|
convDir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, log.ConversationId);
|
||||||
|
Directory.CreateDirectory(convDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
var logDir = Path.Combine(convDir, "llm_prompt_log");
|
||||||
|
if (!Directory.Exists(logDir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(logDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = GetNextLlmCompletionLogIndex(logDir, log.MessageId);
|
||||||
|
var file = Path.Combine(logDir, $"{log.MessageId}.{index}.log");
|
||||||
|
File.WriteAllText(file, JsonSerializer.Serialize(log, _options));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
private int GetNextLlmCompletionLogIndex(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;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -34,5 +34,6 @@ public partial class FileRepository
|
||||||
{
|
{
|
||||||
var configFile = Path.Combine(_dbSettings.FileRepository, "plugins", "config.json");
|
var configFile = Path.Combine(_dbSettings.FileRepository, "plugins", "config.json");
|
||||||
File.WriteAllText(configFile, JsonSerializer.Serialize(config, _options));
|
File.WriteAllText(configFile, JsonSerializer.Serialize(config, _options));
|
||||||
|
_pluginConfig = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
using BotSharp.Abstraction.Users.Models;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BotSharp.Core.Repository
|
||||||
|
{
|
||||||
|
public partial class FileRepository
|
||||||
|
{
|
||||||
|
public User? GetUserByEmail(string email)
|
||||||
|
{
|
||||||
|
return Users.FirstOrDefault(x => x.Email == email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User? GetUserById(string id = null)
|
||||||
|
{
|
||||||
|
return Users.FirstOrDefault(x => x.ExternalId == id || x.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateUser(User user)
|
||||||
|
{
|
||||||
|
var userId = Guid.NewGuid().ToString();
|
||||||
|
user.Id = userId;
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, "users", userId);
|
||||||
|
if (!Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
var path = Path.Combine(dir, "user.json");
|
||||||
|
File.WriteAllText(path, JsonSerializer.Serialize(user, _options));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,253 @@
|
||||||
|
using BotSharp.Abstraction.Repositories;
|
||||||
|
using System.IO;
|
||||||
|
using FunctionDef = BotSharp.Abstraction.Functions.Models.FunctionDef;
|
||||||
|
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.Repositories.Models;
|
||||||
|
using BotSharp.Abstraction.Routing.Settings;
|
||||||
|
using BotSharp.Abstraction.Evaluations.Settings;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using BotSharp.Abstraction.Plugins.Models;
|
||||||
|
|
||||||
|
namespace BotSharp.Core.Repository;
|
||||||
|
|
||||||
|
public partial class FileRepository : IBotSharpRepository
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _services;
|
||||||
|
private readonly BotSharpDatabaseSettings _dbSettings;
|
||||||
|
private readonly AgentSettings _agentSettings;
|
||||||
|
private readonly ConversationSetting _conversationSettings;
|
||||||
|
private JsonSerializerOptions _options;
|
||||||
|
|
||||||
|
private const string AGENT_FILE = "agent.json";
|
||||||
|
private const string AGENT_INSTRUCTION_FILE = "instruction";
|
||||||
|
private const string AGENT_FUNCTIONS_FILE = "functions.json";
|
||||||
|
private const string AGENT_SAMPLES_FILE = "samples.txt";
|
||||||
|
private const string USER_FILE = "user.json";
|
||||||
|
private const string USER_AGENT_FILE = "agents.json";
|
||||||
|
private const string CONVERSATION_FILE = "conversation.json";
|
||||||
|
private const string DIALOG_FILE = "dialogs.txt";
|
||||||
|
private const string STATE_FILE = "state.json";
|
||||||
|
private const string EXECUTION_LOG_FILE = "execution.log";
|
||||||
|
private const string PLUGIN_CONFIG_FILE = "config.json";
|
||||||
|
|
||||||
|
public FileRepository(
|
||||||
|
IServiceProvider services,
|
||||||
|
BotSharpDatabaseSettings dbSettings,
|
||||||
|
AgentSettings agentSettings,
|
||||||
|
ConversationSetting conversationSettings)
|
||||||
|
{
|
||||||
|
_services = services;
|
||||||
|
_dbSettings = dbSettings;
|
||||||
|
_agentSettings = agentSettings;
|
||||||
|
_conversationSettings = conversationSettings;
|
||||||
|
|
||||||
|
_options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
WriteIndented = true,
|
||||||
|
AllowTrailingCommas = true,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
};
|
||||||
|
|
||||||
|
_dbSettings.FileRepository = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _dbSettings.FileRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<User> _users = new List<User>();
|
||||||
|
private List<Agent> _agents = new List<Agent>();
|
||||||
|
private List<UserAgent> _userAgents = new List<UserAgent>();
|
||||||
|
private List<Conversation> _conversations = new List<Conversation>();
|
||||||
|
private PluginConfig? _pluginConfig = null;
|
||||||
|
|
||||||
|
private IQueryable<User> Users
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_users.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return _users.AsQueryable();
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, "users");
|
||||||
|
_users = new List<User>();
|
||||||
|
if (Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
foreach (var d in Directory.GetDirectories(dir))
|
||||||
|
{
|
||||||
|
var userFile = Path.Combine(d, USER_FILE);
|
||||||
|
if (!Directory.Exists(d) || !File.Exists(userFile))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var json = File.ReadAllText(userFile);
|
||||||
|
_users.Add(JsonSerializer.Deserialize<User>(json, _options));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _users.AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<Agent> Agents
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_agents.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return _agents.AsQueryable();
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir);
|
||||||
|
_agents = new List<Agent>();
|
||||||
|
if (Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
foreach (var d in Directory.GetDirectories(dir))
|
||||||
|
{
|
||||||
|
var file = Path.Combine(d, AGENT_FILE);
|
||||||
|
if (!Directory.Exists(d) || !File.Exists(file))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var json = File.ReadAllText(file);
|
||||||
|
var agent = JsonSerializer.Deserialize<Agent>(json, _options);
|
||||||
|
if (agent != null)
|
||||||
|
{
|
||||||
|
agent = agent.SetInstruction(FetchInstruction(d))
|
||||||
|
.SetTemplates(FetchTemplates(d))
|
||||||
|
.SetFunctions(FetchFunctions(d))
|
||||||
|
.SetResponses(FetchResponses(d))
|
||||||
|
.SetSamples(FetchSamples(d));
|
||||||
|
_agents.Add(agent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _agents.AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<UserAgent> UserAgents
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_userAgents.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return _userAgents.AsQueryable();
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, "users");
|
||||||
|
_userAgents = new List<UserAgent>();
|
||||||
|
if (Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
foreach (var d in Directory.GetDirectories(dir))
|
||||||
|
{
|
||||||
|
var file = Path.Combine(d, USER_AGENT_FILE);
|
||||||
|
if (!Directory.Exists(d) || !File.Exists(file))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var json = File.ReadAllText(file);
|
||||||
|
_userAgents.AddRange(JsonSerializer.Deserialize<List<UserAgent>>(json, _options));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _userAgents.AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
private string GetAgentDataDir(string agentId)
|
||||||
|
{
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId);
|
||||||
|
if (!Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
dir = string.Empty;
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private (Agent?, string) GetAgentFromFile(string agentId)
|
||||||
|
{
|
||||||
|
var dir = GetAgentDataDir(agentId);
|
||||||
|
var agentFile = Path.Combine(dir, AGENT_FILE);
|
||||||
|
if (!File.Exists(agentFile)) return (null, string.Empty);
|
||||||
|
|
||||||
|
var json = File.ReadAllText(agentFile);
|
||||||
|
var agent = JsonSerializer.Deserialize<Agent>(json, _options);
|
||||||
|
return (agent, agentFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FetchInstruction(string fileDir)
|
||||||
|
{
|
||||||
|
var file = Path.Combine(fileDir, $"{AGENT_INSTRUCTION_FILE}.{_agentSettings.TemplateFormat}");
|
||||||
|
if (!File.Exists(file)) return string.Empty;
|
||||||
|
|
||||||
|
var instruction = File.ReadAllText(file);
|
||||||
|
return instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<FunctionDef> FetchFunctions(string fileDir)
|
||||||
|
{
|
||||||
|
var file = Path.Combine(fileDir, AGENT_FUNCTIONS_FILE);
|
||||||
|
if (!File.Exists(file)) return new List<FunctionDef>();
|
||||||
|
|
||||||
|
var functionsJson = File.ReadAllText(file);
|
||||||
|
var functions = JsonSerializer.Deserialize<List<FunctionDef>>(functionsJson, _options);
|
||||||
|
return functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> FetchSamples(string fileDir)
|
||||||
|
{
|
||||||
|
var file = Path.Combine(fileDir, AGENT_SAMPLES_FILE);
|
||||||
|
if (!File.Exists(file)) return new List<string>();
|
||||||
|
|
||||||
|
return File.ReadAllLines(file)?.ToList() ?? new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AgentTemplate> FetchTemplates(string fileDir)
|
||||||
|
{
|
||||||
|
var templates = new List<AgentTemplate>();
|
||||||
|
var templateDir = Path.Combine(fileDir, "templates");
|
||||||
|
if (!Directory.Exists(templateDir)) return templates;
|
||||||
|
|
||||||
|
foreach (var file in Directory.GetFiles(templateDir))
|
||||||
|
{
|
||||||
|
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
|
||||||
|
var splits = fileName.ToLower().Split('.');
|
||||||
|
var name = string.Join('.', splits.Take(splits.Length - 1));
|
||||||
|
var extension = splits.Last();
|
||||||
|
if (extension.Equals(_agentSettings.TemplateFormat, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var content = File.ReadAllText(file);
|
||||||
|
templates.Add(new AgentTemplate(name, content));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return templates;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AgentResponse> FetchResponses(string fileDir)
|
||||||
|
{
|
||||||
|
var responses = new List<AgentResponse>();
|
||||||
|
var responseDir = Path.Combine(fileDir, "responses");
|
||||||
|
if (!Directory.Exists(responseDir)) return responses;
|
||||||
|
|
||||||
|
foreach (var file in Directory.GetFiles(responseDir))
|
||||||
|
{
|
||||||
|
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
|
||||||
|
var splits = fileName.ToLower().Split('.');
|
||||||
|
var prefix = splits[0];
|
||||||
|
var intent = splits[1];
|
||||||
|
var content = File.ReadAllText(file);
|
||||||
|
responses.Add(new AgentResponse(prefix, intent, content));
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] ParseFileNameByPath(string path, string separator = ".")
|
||||||
|
{
|
||||||
|
var name = path.Split(Path.DirectorySeparatorChar).Last();
|
||||||
|
return name.Split(separator);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
namespace BotSharp.Plugin.MongoStorage.Collections;
|
||||||
|
|
||||||
|
public class PluginDocument : MongoBase
|
||||||
|
{
|
||||||
|
public List<string> EnabledPlugins { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -51,4 +51,7 @@ public class MongoDbContext
|
||||||
|
|
||||||
public IMongoCollection<LlmCompletionLogDocument> LlmCompletionLogs
|
public IMongoCollection<LlmCompletionLogDocument> LlmCompletionLogs
|
||||||
=> Database.GetCollection<LlmCompletionLogDocument>($"{_collectionPrefix}_Llm_Completion_Logs");
|
=> Database.GetCollection<LlmCompletionLogDocument>($"{_collectionPrefix}_Llm_Completion_Logs");
|
||||||
|
|
||||||
|
public IMongoCollection<PluginDocument> Plugins
|
||||||
|
=> Database.GetCollection<PluginDocument>($"{_collectionPrefix}_Plugins");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,450 @@
|
||||||
|
using BotSharp.Abstraction.Agents.Models;
|
||||||
|
using BotSharp.Abstraction.Evaluations.Settings;
|
||||||
|
using BotSharp.Abstraction.Functions.Models;
|
||||||
|
using BotSharp.Abstraction.Repositories.Filters;
|
||||||
|
using BotSharp.Abstraction.Routing.Models;
|
||||||
|
using BotSharp.Abstraction.Routing.Settings;
|
||||||
|
using BotSharp.Plugin.MongoStorage.Collections;
|
||||||
|
using BotSharp.Plugin.MongoStorage.Models;
|
||||||
|
|
||||||
|
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||||
|
|
||||||
|
public partial class MongoRepository
|
||||||
|
{
|
||||||
|
public void UpdateAgent(Agent agent, AgentField field)
|
||||||
|
{
|
||||||
|
if (agent == null || string.IsNullOrEmpty(agent.Id)) return;
|
||||||
|
|
||||||
|
switch (field)
|
||||||
|
{
|
||||||
|
case AgentField.Name:
|
||||||
|
UpdateAgentName(agent.Id, agent.Name);
|
||||||
|
break;
|
||||||
|
case AgentField.Description:
|
||||||
|
UpdateAgentDescription(agent.Id, agent.Description);
|
||||||
|
break;
|
||||||
|
case AgentField.IsPublic:
|
||||||
|
UpdateAgentIsPublic(agent.Id, agent.IsPublic);
|
||||||
|
break;
|
||||||
|
case AgentField.Disabled:
|
||||||
|
UpdateAgentDisabled(agent.Id, agent.Disabled);
|
||||||
|
break;
|
||||||
|
case AgentField.AllowRouting:
|
||||||
|
UpdateAgentAllowRouting(agent.Id, agent.AllowRouting);
|
||||||
|
break;
|
||||||
|
case AgentField.Profiles:
|
||||||
|
UpdateAgentProfiles(agent.Id, agent.Profiles);
|
||||||
|
break;
|
||||||
|
case AgentField.RoutingRule:
|
||||||
|
UpdateAgentRoutingRules(agent.Id, agent.RoutingRules);
|
||||||
|
break;
|
||||||
|
case AgentField.Instruction:
|
||||||
|
UpdateAgentInstruction(agent.Id, agent.Instruction);
|
||||||
|
break;
|
||||||
|
case AgentField.Function:
|
||||||
|
UpdateAgentFunctions(agent.Id, agent.Functions);
|
||||||
|
break;
|
||||||
|
case AgentField.Template:
|
||||||
|
UpdateAgentTemplates(agent.Id, agent.Templates);
|
||||||
|
break;
|
||||||
|
case AgentField.Response:
|
||||||
|
UpdateAgentResponses(agent.Id, agent.Responses);
|
||||||
|
break;
|
||||||
|
case AgentField.Sample:
|
||||||
|
UpdateAgentSamples(agent.Id, agent.Samples);
|
||||||
|
break;
|
||||||
|
case AgentField.LlmConfig:
|
||||||
|
UpdateAgentLlmConfig(agent.Id, agent.LlmConfig);
|
||||||
|
break;
|
||||||
|
case AgentField.All:
|
||||||
|
UpdateAgentAllFields(agent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Update Agent Fields
|
||||||
|
private void UpdateAgentName(string agentId, string name)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name)) return;
|
||||||
|
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Name, name)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentDescription(string agentId, string description)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(description)) return;
|
||||||
|
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Description, description)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentIsPublic(string agentId, bool isPublic)
|
||||||
|
{
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.IsPublic, isPublic)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentDisabled(string agentId, bool disabled)
|
||||||
|
{
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Disabled, disabled)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentAllowRouting(string agentId, bool allowRouting)
|
||||||
|
{
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.AllowRouting, allowRouting)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentProfiles(string agentId, List<string> profiles)
|
||||||
|
{
|
||||||
|
if (profiles.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Profiles, profiles)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentRoutingRules(string agentId, List<RoutingRule> rules)
|
||||||
|
{
|
||||||
|
if (rules.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var ruleElements = rules.Select(x => RoutingRuleMongoElement.ToMongoElement(x)).ToList();
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.RoutingRules, ruleElements)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentInstruction(string agentId, string instruction)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(instruction)) return;
|
||||||
|
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Instruction, instruction)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentFunctions(string agentId, List<FunctionDef> functions)
|
||||||
|
{
|
||||||
|
if (functions.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var functionsToUpdate = functions.Select(f => FunctionDefMongoElement.ToMongoElement(f)).ToList();
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Functions, functionsToUpdate)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentTemplates(string agentId, List<AgentTemplate> templates)
|
||||||
|
{
|
||||||
|
if (templates.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var templatesToUpdate = templates.Select(t => AgentTemplateMongoElement.ToMongoElement(t)).ToList();
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Templates, templatesToUpdate)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentResponses(string agentId, List<AgentResponse> responses)
|
||||||
|
{
|
||||||
|
if (responses.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var responsesToUpdate = responses.Select(r => AgentResponseMongoElement.ToMongoElement(r)).ToList();
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Responses, responsesToUpdate)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentSamples(string agentId, List<string> samples)
|
||||||
|
{
|
||||||
|
if (samples.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Samples, samples)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentLlmConfig(string agentId, AgentLlmConfig? config)
|
||||||
|
{
|
||||||
|
var llmConfig = AgentLlmConfigMongoElement.ToMongoElement(config);
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.LlmConfig, llmConfig)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Agents.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentAllFields(Agent agent)
|
||||||
|
{
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agent.Id);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Name, agent.Name)
|
||||||
|
.Set(x => x.Description, agent.Description)
|
||||||
|
.Set(x => x.Disabled, agent.Disabled)
|
||||||
|
.Set(x => x.AllowRouting, agent.AllowRouting)
|
||||||
|
.Set(x => x.Profiles, agent.Profiles)
|
||||||
|
.Set(x => x.RoutingRules, agent.RoutingRules.Select(r => RoutingRuleMongoElement.ToMongoElement(r)).ToList())
|
||||||
|
.Set(x => x.Instruction, agent.Instruction)
|
||||||
|
.Set(x => x.Templates, agent.Templates.Select(t => AgentTemplateMongoElement.ToMongoElement(t)).ToList())
|
||||||
|
.Set(x => x.Functions, agent.Functions.Select(f => FunctionDefMongoElement.ToMongoElement(f)).ToList())
|
||||||
|
.Set(x => x.Responses, agent.Responses.Select(r => AgentResponseMongoElement.ToMongoElement(r)).ToList())
|
||||||
|
.Set(x => x.Samples, agent.Samples)
|
||||||
|
.Set(x => x.LlmConfig, AgentLlmConfigMongoElement.ToMongoElement(agent.LlmConfig))
|
||||||
|
.Set(x => x.IsPublic, agent.IsPublic)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
var res = _dc.Agents.UpdateOne(filter, update);
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
public Agent? GetAgent(string agentId)
|
||||||
|
{
|
||||||
|
var agent = _dc.Agents.AsQueryable().FirstOrDefault(x => x.Id == agentId);
|
||||||
|
if (agent == null) return null;
|
||||||
|
|
||||||
|
return new Agent
|
||||||
|
{
|
||||||
|
Id = agent.Id,
|
||||||
|
Name = agent.Name,
|
||||||
|
Description = agent.Description,
|
||||||
|
Instruction = agent.Instruction,
|
||||||
|
Templates = !agent.Templates.IsNullOrEmpty() ? agent.Templates
|
||||||
|
.Select(t => AgentTemplateMongoElement.ToDomainElement(t))
|
||||||
|
.ToList() : new List<AgentTemplate>(),
|
||||||
|
Functions = !agent.Functions.IsNullOrEmpty() ? agent.Functions
|
||||||
|
.Select(f => FunctionDefMongoElement.ToDomainElement(f))
|
||||||
|
.ToList() : new List<FunctionDef>(),
|
||||||
|
Responses = !agent.Responses.IsNullOrEmpty() ? agent.Responses
|
||||||
|
.Select(r => AgentResponseMongoElement.ToDomainElement(r))
|
||||||
|
.ToList() : new List<AgentResponse>(),
|
||||||
|
Samples = agent.Samples ?? new List<string>(),
|
||||||
|
IsPublic = agent.IsPublic,
|
||||||
|
Disabled = agent.Disabled,
|
||||||
|
AllowRouting = agent.AllowRouting,
|
||||||
|
Profiles = agent.Profiles,
|
||||||
|
RoutingRules = !agent.RoutingRules.IsNullOrEmpty() ? agent.RoutingRules
|
||||||
|
.Select(r => RoutingRuleMongoElement.ToDomainElement(agent.Id, agent.Name, r))
|
||||||
|
.ToList() : new List<RoutingRule>(),
|
||||||
|
LlmConfig = AgentLlmConfigMongoElement.ToDomainElement(agent.LlmConfig)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Agent> GetAgents(AgentFilter filter)
|
||||||
|
{
|
||||||
|
var agents = new List<Agent>();
|
||||||
|
IQueryable<AgentDocument> query = _dc.Agents.AsQueryable();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(filter.AgentName))
|
||||||
|
{
|
||||||
|
query = query.Where(x => x.Name.ToLower() == filter.AgentName.ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.Disabled.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(x => x.Disabled == filter.Disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.AllowRouting.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(x => x.AllowRouting == filter.AllowRouting);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.IsPublic.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(x => x.IsPublic == filter.IsPublic);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.IsRouter.HasValue)
|
||||||
|
{
|
||||||
|
var route = _services.GetRequiredService<RoutingSettings>();
|
||||||
|
query = filter.IsRouter.Value ?
|
||||||
|
query.Where(x => x.Id == route.AgentId) :
|
||||||
|
query.Where(x => x.Id != route.AgentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.IsEvaluator.HasValue)
|
||||||
|
{
|
||||||
|
var evaluate = _services.GetRequiredService<EvaluatorSetting>();
|
||||||
|
query = filter.IsEvaluator.Value ?
|
||||||
|
query.Where(x => x.Id == evaluate.AgentId) :
|
||||||
|
query.Where(x => x.Id != evaluate.AgentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.AgentIds != null)
|
||||||
|
{
|
||||||
|
query = query.Where(x => filter.AgentIds.Contains(x.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.ToList().Select(x => new Agent
|
||||||
|
{
|
||||||
|
Id = x.Id,
|
||||||
|
Name = x.Name,
|
||||||
|
Description = x.Description,
|
||||||
|
Instruction = x.Instruction,
|
||||||
|
Templates = !x.Templates.IsNullOrEmpty() ? x.Templates
|
||||||
|
.Select(t => AgentTemplateMongoElement.ToDomainElement(t))
|
||||||
|
.ToList() : new List<AgentTemplate>(),
|
||||||
|
Functions = !x.Functions.IsNullOrEmpty() ? x.Functions
|
||||||
|
.Select(f => FunctionDefMongoElement.ToDomainElement(f))
|
||||||
|
.ToList() : new List<FunctionDef>(),
|
||||||
|
Responses = !x.Responses.IsNullOrEmpty() ? x.Responses
|
||||||
|
.Select(r => AgentResponseMongoElement.ToDomainElement(r))
|
||||||
|
.ToList() : new List<AgentResponse>(),
|
||||||
|
Samples = x.Samples ?? new List<string>(),
|
||||||
|
IsPublic = x.IsPublic,
|
||||||
|
Disabled = x.Disabled,
|
||||||
|
AllowRouting = x.AllowRouting,
|
||||||
|
Profiles = x.Profiles,
|
||||||
|
RoutingRules = !x.RoutingRules.IsNullOrEmpty() ? x.RoutingRules
|
||||||
|
.Select(r => RoutingRuleMongoElement.ToDomainElement(x.Id, x.Name, r))
|
||||||
|
.ToList() : new List<RoutingRule>(),
|
||||||
|
LlmConfig = AgentLlmConfigMongoElement.ToDomainElement(x.LlmConfig)
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Agent> GetAgentsByUser(string userId)
|
||||||
|
{
|
||||||
|
var agentIds = (from ua in _dc.UserAgents.AsQueryable()
|
||||||
|
join u in _dc.Users.AsQueryable() on ua.UserId equals u.Id
|
||||||
|
where ua.UserId == userId || u.ExternalId == userId
|
||||||
|
select ua.AgentId).ToList();
|
||||||
|
|
||||||
|
var filter = new AgentFilter
|
||||||
|
{
|
||||||
|
AgentIds = agentIds,
|
||||||
|
IsPublic = true
|
||||||
|
};
|
||||||
|
var agents = GetAgents(filter);
|
||||||
|
return agents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetAgentResponses(string agentId, string prefix, string intent)
|
||||||
|
{
|
||||||
|
var responses = new List<string>();
|
||||||
|
var agent = _dc.Agents.AsQueryable().FirstOrDefault(x => x.Id == agentId);
|
||||||
|
if (agent == null) return responses;
|
||||||
|
|
||||||
|
return agent.Responses.Where(x => x.Prefix == prefix && x.Intent == intent).Select(x => x.Content).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetAgentTemplate(string agentId, string templateName)
|
||||||
|
{
|
||||||
|
var agent = _dc.Agents.AsQueryable().FirstOrDefault(x => x.Id == agentId);
|
||||||
|
if (agent == null) return string.Empty;
|
||||||
|
|
||||||
|
return agent.Templates?.FirstOrDefault(x => x.Name == templateName.ToLower())?.Content ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BulkInsertAgents(List<Agent> agents)
|
||||||
|
{
|
||||||
|
if (agents.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var agentDocs = agents.Select(x => new AgentDocument
|
||||||
|
{
|
||||||
|
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
||||||
|
Name = x.Name,
|
||||||
|
Description = x.Description,
|
||||||
|
Instruction = x.Instruction,
|
||||||
|
Templates = x.Templates?
|
||||||
|
.Select(t => AgentTemplateMongoElement.ToMongoElement(t))?
|
||||||
|
.ToList() ?? new List<AgentTemplateMongoElement>(),
|
||||||
|
Functions = x.Functions?
|
||||||
|
.Select(f => FunctionDefMongoElement.ToMongoElement(f))?
|
||||||
|
.ToList() ?? new List<FunctionDefMongoElement>(),
|
||||||
|
Responses = x.Responses?
|
||||||
|
.Select(r => AgentResponseMongoElement.ToMongoElement(r))?
|
||||||
|
.ToList() ?? new List<AgentResponseMongoElement>(),
|
||||||
|
Samples = x.Samples ?? new List<string>(),
|
||||||
|
IsPublic = x.IsPublic,
|
||||||
|
AllowRouting = x.AllowRouting,
|
||||||
|
Disabled = x.Disabled,
|
||||||
|
Profiles = x.Profiles,
|
||||||
|
RoutingRules = x.RoutingRules?
|
||||||
|
.Select(r => RoutingRuleMongoElement.ToMongoElement(r))?
|
||||||
|
.ToList() ?? new List<RoutingRuleMongoElement>(),
|
||||||
|
LlmConfig = AgentLlmConfigMongoElement.ToMongoElement(x.LlmConfig),
|
||||||
|
CreatedTime = x.CreatedDateTime,
|
||||||
|
UpdatedTime = x.UpdatedDateTime
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
_dc.Agents.InsertMany(agentDocs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BulkInsertUserAgents(List<UserAgent> userAgents)
|
||||||
|
{
|
||||||
|
if (userAgents.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var userAgentDocs = userAgents.Select(x => new UserAgentDocument
|
||||||
|
{
|
||||||
|
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
||||||
|
AgentId = x.AgentId,
|
||||||
|
UserId = !string.IsNullOrEmpty(x.UserId) ? x.UserId : string.Empty,
|
||||||
|
Editable = x.Editable,
|
||||||
|
CreatedTime = x.CreatedTime,
|
||||||
|
UpdatedTime = x.UpdatedTime
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
_dc.UserAgents.InsertMany(userAgentDocs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DeleteAgents()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_dc.UserAgents.DeleteMany(Builders<UserAgentDocument>.Filter.Empty);
|
||||||
|
_dc.Agents.DeleteMany(Builders<AgentDocument>.Filter.Empty);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,257 @@
|
||||||
|
using BotSharp.Abstraction.Conversations.Models;
|
||||||
|
using BotSharp.Abstraction.Repositories.Filters;
|
||||||
|
using BotSharp.Abstraction.Repositories.Models;
|
||||||
|
using BotSharp.Plugin.MongoStorage.Collections;
|
||||||
|
using BotSharp.Plugin.MongoStorage.Models;
|
||||||
|
|
||||||
|
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||||
|
|
||||||
|
public partial class MongoRepository
|
||||||
|
{
|
||||||
|
public void CreateNewConversation(Conversation conversation)
|
||||||
|
{
|
||||||
|
if (conversation == null) return;
|
||||||
|
|
||||||
|
var convDoc = new ConversationDocument
|
||||||
|
{
|
||||||
|
Id = !string.IsNullOrEmpty(conversation.Id) ? conversation.Id : Guid.NewGuid().ToString(),
|
||||||
|
AgentId = conversation.AgentId,
|
||||||
|
UserId = !string.IsNullOrEmpty(conversation.UserId) ? conversation.UserId : string.Empty,
|
||||||
|
Title = conversation.Title,
|
||||||
|
Channel = conversation.Channel,
|
||||||
|
Status = conversation.Status,
|
||||||
|
CreatedTime = DateTime.UtcNow,
|
||||||
|
UpdatedTime = DateTime.UtcNow,
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialogDoc = new ConversationDialogDocument
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid().ToString(),
|
||||||
|
ConversationId = convDoc.Id,
|
||||||
|
Dialogs = new List<DialogMongoElement>()
|
||||||
|
};
|
||||||
|
|
||||||
|
var states = conversation.States ?? new Dictionary<string, string>();
|
||||||
|
var initialStates = states.Select(x => new StateMongoElement
|
||||||
|
{
|
||||||
|
Key = x.Key,
|
||||||
|
Values = new List<StateValueMongoElement>
|
||||||
|
{
|
||||||
|
new StateValueMongoElement { Data = x.Value, UpdateTime = DateTime.UtcNow }
|
||||||
|
}
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var stateDoc = new ConversationStateDocument
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid().ToString(),
|
||||||
|
ConversationId = convDoc.Id,
|
||||||
|
States = initialStates
|
||||||
|
};
|
||||||
|
|
||||||
|
_dc.Conversations.InsertOne(convDoc);
|
||||||
|
_dc.ConversationDialogs.InsertOne(dialogDoc);
|
||||||
|
_dc.ConversationStates.InsertOne(stateDoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DeleteConversation(string conversationId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId)) return false;
|
||||||
|
|
||||||
|
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
||||||
|
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var filterSates = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var filterExeLog = Builders<ExecutionLogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var filterPromptLog = Builders<LlmCompletionLogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
|
||||||
|
var exeLogDeleted = _dc.ExectionLogs.DeleteMany(filterExeLog);
|
||||||
|
var promptLogDeleted = _dc.LlmCompletionLogs.DeleteMany(filterPromptLog);
|
||||||
|
var statesDeleted = _dc.ConversationStates.DeleteMany(filterSates);
|
||||||
|
var dialogDeleted = _dc.ConversationDialogs.DeleteMany(filterDialog);
|
||||||
|
var convDeleted = _dc.Conversations.DeleteMany(filterConv);
|
||||||
|
return convDeleted.DeletedCount > 0 || dialogDeleted.DeletedCount > 0 || statesDeleted.DeletedCount > 0
|
||||||
|
|| exeLogDeleted.DeletedCount > 0 || promptLogDeleted.DeletedCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DialogElement> GetConversationDialogs(string conversationId)
|
||||||
|
{
|
||||||
|
var dialogs = new List<DialogElement>();
|
||||||
|
if (string.IsNullOrEmpty(conversationId)) return dialogs;
|
||||||
|
|
||||||
|
var filter = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var foundDialog = _dc.ConversationDialogs.Find(filter).FirstOrDefault();
|
||||||
|
if (foundDialog == null) return dialogs;
|
||||||
|
|
||||||
|
var formattedDialog = foundDialog.Dialogs?.Select(x => DialogMongoElement.ToDomainElement(x))?.ToList();
|
||||||
|
return formattedDialog ?? new List<DialogElement>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConversationDialogElements(string conversationId, List<DialogContentUpdateModel> updateElements)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId) || updateElements.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var foundDialog = _dc.ConversationDialogs.Find(filterDialog).FirstOrDefault();
|
||||||
|
if (foundDialog == null || foundDialog.Dialogs.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
foundDialog.Dialogs = foundDialog.Dialogs.Select((x, idx) =>
|
||||||
|
{
|
||||||
|
var found = updateElements.FirstOrDefault(e => e.Index == idx);
|
||||||
|
if (found != null)
|
||||||
|
{
|
||||||
|
x.Content = found.UpdateContent;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
_dc.ConversationDialogs.ReplaceOne(filterDialog, foundDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AppendConversationDialogs(string conversationId, List<DialogElement> dialogs)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId)) return;
|
||||||
|
|
||||||
|
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
||||||
|
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var dialogElements = dialogs.Select(x => DialogMongoElement.ToMongoElement(x)).ToList();
|
||||||
|
var updateDialog = Builders<ConversationDialogDocument>.Update.PushEach(x => x.Dialogs, dialogElements);
|
||||||
|
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.ConversationDialogs.UpdateOne(filterDialog, updateDialog);
|
||||||
|
_dc.Conversations.UpdateOne(filterConv, updateConv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConversationTitle(string conversationId, string title)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId)) return;
|
||||||
|
|
||||||
|
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
||||||
|
var updateConv = Builders<ConversationDocument>.Update
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow)
|
||||||
|
.Set(x => x.Title, title);
|
||||||
|
|
||||||
|
_dc.Conversations.UpdateOne(filterConv, updateConv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConversationState GetConversationStates(string conversationId)
|
||||||
|
{
|
||||||
|
var states = new ConversationState();
|
||||||
|
if (string.IsNullOrEmpty(conversationId)) return states;
|
||||||
|
|
||||||
|
var filter = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var foundStates = _dc.ConversationStates.Find(filter).FirstOrDefault();
|
||||||
|
if (foundStates == null || foundStates.States.IsNullOrEmpty()) return states;
|
||||||
|
|
||||||
|
var savedStates = foundStates.States.Select(x => StateMongoElement.ToDomainElement(x)).ToList();
|
||||||
|
return new ConversationState(savedStates);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId) || states.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var filterStates = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var saveStates = states.Select(x => StateMongoElement.ToMongoElement(x)).ToList();
|
||||||
|
var updateStates = Builders<ConversationStateDocument>.Update.Set(x => x.States, saveStates);
|
||||||
|
|
||||||
|
_dc.ConversationStates.UpdateOne(filterStates, updateStates);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConversationStatus(string conversationId, string status)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(status)) return;
|
||||||
|
|
||||||
|
var filter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
||||||
|
var update = Builders<ConversationDocument>.Update
|
||||||
|
.Set(x => x.Status, status)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
|
_dc.Conversations.UpdateOne(filter, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Conversation GetConversation(string conversationId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId)) return null;
|
||||||
|
|
||||||
|
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
||||||
|
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var filterState = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
|
||||||
|
var conv = _dc.Conversations.Find(filterConv).FirstOrDefault();
|
||||||
|
var dialog = _dc.ConversationDialogs.Find(filterDialog).FirstOrDefault();
|
||||||
|
var states = _dc.ConversationStates.Find(filterState).FirstOrDefault();
|
||||||
|
|
||||||
|
if (conv == null) return null;
|
||||||
|
|
||||||
|
var dialogElements = dialog?.Dialogs?.Select(x => DialogMongoElement.ToDomainElement(x))?.ToList() ?? new List<DialogElement>();
|
||||||
|
var curStates = new Dictionary<string, string>();
|
||||||
|
states.States.ForEach(x =>
|
||||||
|
{
|
||||||
|
curStates[x.Key] = x.Values?.LastOrDefault()?.Data ?? string.Empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Conversation
|
||||||
|
{
|
||||||
|
Id = conv.Id.ToString(),
|
||||||
|
AgentId = conv.AgentId.ToString(),
|
||||||
|
UserId = conv.UserId.ToString(),
|
||||||
|
Title = conv.Title,
|
||||||
|
Channel = conv.Channel,
|
||||||
|
Status = conv.Status,
|
||||||
|
Dialogs = dialogElements,
|
||||||
|
States = curStates,
|
||||||
|
CreatedTime = conv.CreatedTime,
|
||||||
|
UpdatedTime = conv.UpdatedTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Conversation> GetConversations(ConversationFilter filter)
|
||||||
|
{
|
||||||
|
var records = new List<Conversation>();
|
||||||
|
var builder = Builders<ConversationDocument>.Filter;
|
||||||
|
var filters = new List<FilterDefinition<ConversationDocument>>();
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
foreach (var conv in conversations)
|
||||||
|
{
|
||||||
|
var convId = conv.Id.ToString();
|
||||||
|
records.Add(new Conversation
|
||||||
|
{
|
||||||
|
Id = convId,
|
||||||
|
AgentId = conv.AgentId.ToString(),
|
||||||
|
UserId = conv.UserId.ToString(),
|
||||||
|
Title = conv.Title,
|
||||||
|
Channel = conv.Channel,
|
||||||
|
Status = conv.Status,
|
||||||
|
CreatedTime = conv.CreatedTime,
|
||||||
|
UpdatedTime = conv.UpdatedTime
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Conversation> GetLastConversations()
|
||||||
|
{
|
||||||
|
var records = new List<Conversation>();
|
||||||
|
var conversations = _dc.Conversations.Aggregate()
|
||||||
|
.Group(c => c.UserId, g => g.OrderByDescending(x => x.CreatedTime).First())
|
||||||
|
.ToList();
|
||||||
|
return conversations.Select(c => new Conversation()
|
||||||
|
{
|
||||||
|
Id = c.Id.ToString(),
|
||||||
|
AgentId = c.AgentId.ToString(),
|
||||||
|
UserId = c.UserId.ToString(),
|
||||||
|
Title = c.Title,
|
||||||
|
Channel = c.Channel,
|
||||||
|
Status = c.Status,
|
||||||
|
CreatedTime = c.CreatedTime,
|
||||||
|
UpdatedTime = c.UpdatedTime
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
using BotSharp.Abstraction.Conversations.Models;
|
||||||
|
using BotSharp.Plugin.MongoStorage.Collections;
|
||||||
|
using BotSharp.Plugin.MongoStorage.Models;
|
||||||
|
|
||||||
|
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||||
|
|
||||||
|
public partial class MongoRepository
|
||||||
|
{
|
||||||
|
#region Execution Log
|
||||||
|
public void AddExecutionLogs(string conversationId, List<string> logs)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conversationId) || logs.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
var filter = Builders<ExecutionLogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var update = Builders<ExecutionLogDocument>.Update
|
||||||
|
.SetOnInsert(x => x.Id, Guid.NewGuid().ToString())
|
||||||
|
.PushEach(x => x.Logs, logs);
|
||||||
|
|
||||||
|
_dc.ExectionLogs.UpdateOne(filter, update, _options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetExecutionLogs(string conversationId)
|
||||||
|
{
|
||||||
|
var logs = new List<string>();
|
||||||
|
if (string.IsNullOrEmpty(conversationId)) return logs;
|
||||||
|
|
||||||
|
var filter = Builders<ExecutionLogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var logCollection = _dc.ExectionLogs.Find(filter).FirstOrDefault();
|
||||||
|
|
||||||
|
logs = logCollection?.Logs ?? new List<string>();
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region LLM Completion Log
|
||||||
|
public void SaveLlmCompletionLog(LlmCompletionLog log)
|
||||||
|
{
|
||||||
|
if (log == null) return;
|
||||||
|
|
||||||
|
var conversationId = log.ConversationId.IfNullOrEmptyAs(Guid.NewGuid().ToString());
|
||||||
|
var messageId = log.MessageId.IfNullOrEmptyAs(Guid.NewGuid().ToString());
|
||||||
|
|
||||||
|
var logElement = new PromptLogMongoElement
|
||||||
|
{
|
||||||
|
MessageId = messageId,
|
||||||
|
AgentId = log.AgentId,
|
||||||
|
Prompt = log.Prompt,
|
||||||
|
Response = log.Response,
|
||||||
|
CreateDateTime = log.CreateDateTime
|
||||||
|
};
|
||||||
|
|
||||||
|
var filter = Builders<LlmCompletionLogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
|
var update = Builders<LlmCompletionLogDocument>.Update
|
||||||
|
.SetOnInsert(x => x.Id, Guid.NewGuid().ToString())
|
||||||
|
.Push(x => x.Logs, logElement);
|
||||||
|
|
||||||
|
_dc.LlmCompletionLogs.UpdateOne(filter, update, _options);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
using BotSharp.Abstraction.Plugins.Models;
|
||||||
|
using BotSharp.Plugin.MongoStorage.Collections;
|
||||||
|
|
||||||
|
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||||
|
|
||||||
|
public partial class MongoRepository
|
||||||
|
{
|
||||||
|
#region Plugin
|
||||||
|
public PluginConfig GetPluginConfig()
|
||||||
|
{
|
||||||
|
var config = new PluginConfig();
|
||||||
|
var found = _dc.Plugins.AsQueryable().FirstOrDefault();
|
||||||
|
if (found != null)
|
||||||
|
{
|
||||||
|
config = new PluginConfig()
|
||||||
|
{
|
||||||
|
EnabledPlugins = found.EnabledPlugins
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SavePluginConfig(PluginConfig config)
|
||||||
|
{
|
||||||
|
if (config == null || config.EnabledPlugins == null) return;
|
||||||
|
|
||||||
|
var filter = Builders<PluginDocument>.Filter.Empty;
|
||||||
|
var update = Builders<PluginDocument>.Update
|
||||||
|
.Set(x => x.EnabledPlugins, config.EnabledPlugins)
|
||||||
|
.SetOnInsert(x => x.Id, Guid.NewGuid().ToString());
|
||||||
|
|
||||||
|
_dc.Plugins.UpdateOne(filter, update, _options);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
using BotSharp.Abstraction.Agents.Models;
|
||||||
|
using BotSharp.Abstraction.Users.Models;
|
||||||
|
using BotSharp.Plugin.MongoStorage.Collections;
|
||||||
|
using BotSharp.Plugin.MongoStorage.Models;
|
||||||
|
|
||||||
|
|
||||||
|
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||||
|
|
||||||
|
public partial class MongoRepository
|
||||||
|
{
|
||||||
|
public void Add<TTableInterface>(object entity)
|
||||||
|
{
|
||||||
|
if (entity is Agent agent)
|
||||||
|
{
|
||||||
|
_agents.Add(agent);
|
||||||
|
_changedTableNames.Add(nameof(Agent));
|
||||||
|
}
|
||||||
|
else if (entity is User user)
|
||||||
|
{
|
||||||
|
_users.Add(user);
|
||||||
|
_changedTableNames.Add(nameof(User));
|
||||||
|
}
|
||||||
|
else if (entity is UserAgent userAgent)
|
||||||
|
{
|
||||||
|
_userAgents.Add(userAgent);
|
||||||
|
_changedTableNames.Add(nameof(UserAgent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Transaction<TTableInterface>(Action action)
|
||||||
|
{
|
||||||
|
_changedTableNames.Clear();
|
||||||
|
action();
|
||||||
|
|
||||||
|
foreach (var table in _changedTableNames)
|
||||||
|
{
|
||||||
|
if (table == nameof(Agent))
|
||||||
|
{
|
||||||
|
var agents = _agents.Select(x => new AgentDocument
|
||||||
|
{
|
||||||
|
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
||||||
|
Name = x.Name,
|
||||||
|
Description = x.Description,
|
||||||
|
Instruction = x.Instruction,
|
||||||
|
Templates = x.Templates?
|
||||||
|
.Select(t => AgentTemplateMongoElement.ToMongoElement(t))?
|
||||||
|
.ToList() ?? new List<AgentTemplateMongoElement>(),
|
||||||
|
Functions = x.Functions?
|
||||||
|
.Select(f => FunctionDefMongoElement.ToMongoElement(f))?
|
||||||
|
.ToList() ?? new List<FunctionDefMongoElement>(),
|
||||||
|
Responses = x.Responses?
|
||||||
|
.Select(r => AgentResponseMongoElement.ToMongoElement(r))?
|
||||||
|
.ToList() ?? new List<AgentResponseMongoElement>(),
|
||||||
|
Samples = x.Samples ?? new List<string>(),
|
||||||
|
IsPublic = x.IsPublic,
|
||||||
|
AllowRouting = x.AllowRouting,
|
||||||
|
Disabled = x.Disabled,
|
||||||
|
Profiles = x.Profiles,
|
||||||
|
RoutingRules = x.RoutingRules?
|
||||||
|
.Select(r => RoutingRuleMongoElement.ToMongoElement(r))?
|
||||||
|
.ToList() ?? new List<RoutingRuleMongoElement>(),
|
||||||
|
LlmConfig = AgentLlmConfigMongoElement.ToMongoElement(x.LlmConfig),
|
||||||
|
CreatedTime = x.CreatedDateTime,
|
||||||
|
UpdatedTime = x.UpdatedDateTime
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
foreach (var agent in agents)
|
||||||
|
{
|
||||||
|
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agent.Id);
|
||||||
|
var update = Builders<AgentDocument>.Update
|
||||||
|
.Set(x => x.Name, agent.Name)
|
||||||
|
.Set(x => x.Description, agent.Description)
|
||||||
|
.Set(x => x.Instruction, agent.Instruction)
|
||||||
|
.Set(x => x.Templates, agent.Templates)
|
||||||
|
.Set(x => x.Functions, agent.Functions)
|
||||||
|
.Set(x => x.Responses, agent.Responses)
|
||||||
|
.Set(x => x.Samples, agent.Samples)
|
||||||
|
.Set(x => x.IsPublic, agent.IsPublic)
|
||||||
|
.Set(x => x.AllowRouting, agent.AllowRouting)
|
||||||
|
.Set(x => x.Disabled, agent.Disabled)
|
||||||
|
.Set(x => x.Profiles, agent.Profiles)
|
||||||
|
.Set(x => x.RoutingRules, agent.RoutingRules)
|
||||||
|
.Set(x => x.LlmConfig, agent.LlmConfig)
|
||||||
|
.Set(x => x.CreatedTime, agent.CreatedTime)
|
||||||
|
.Set(x => x.UpdatedTime, agent.UpdatedTime);
|
||||||
|
_dc.Agents.UpdateOne(filter, update, _options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (table == nameof(User))
|
||||||
|
{
|
||||||
|
var users = _users.Select(x => new UserDocument
|
||||||
|
{
|
||||||
|
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
||||||
|
UserName = x.UserName,
|
||||||
|
FirstName = x.FirstName,
|
||||||
|
LastName = x.LastName,
|
||||||
|
Salt = x.Salt,
|
||||||
|
Password = x.Password,
|
||||||
|
Email = x.Email,
|
||||||
|
ExternalId = x.ExternalId,
|
||||||
|
Role = x.Role,
|
||||||
|
CreatedTime = x.CreatedTime,
|
||||||
|
UpdatedTime = x.UpdatedTime
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
foreach (var user in users)
|
||||||
|
{
|
||||||
|
var filter = Builders<UserDocument>.Filter.Eq(x => x.Id, user.Id);
|
||||||
|
var update = Builders<UserDocument>.Update
|
||||||
|
.Set(x => x.UserName, user.UserName)
|
||||||
|
.Set(x => x.FirstName, user.FirstName)
|
||||||
|
.Set(x => x.LastName, user.LastName)
|
||||||
|
.Set(x => x.Email, user.Email)
|
||||||
|
.Set(x => x.Salt, user.Salt)
|
||||||
|
.Set(x => x.Password, user.Password)
|
||||||
|
.Set(x => x.ExternalId, user.ExternalId)
|
||||||
|
.Set(x => x.Role, user.Role)
|
||||||
|
.Set(x => x.CreatedTime, user.CreatedTime)
|
||||||
|
.Set(x => x.UpdatedTime, user.UpdatedTime);
|
||||||
|
_dc.Users.UpdateOne(filter, update, _options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (table == nameof(UserAgent))
|
||||||
|
{
|
||||||
|
var userAgents = _userAgents.Select(x => new UserAgentDocument
|
||||||
|
{
|
||||||
|
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
||||||
|
AgentId = x.AgentId,
|
||||||
|
UserId = !string.IsNullOrEmpty(x.UserId) ? x.UserId : string.Empty,
|
||||||
|
Editable = x.Editable,
|
||||||
|
CreatedTime = x.CreatedTime,
|
||||||
|
UpdatedTime = x.UpdatedTime
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
foreach (var userAgent in userAgents)
|
||||||
|
{
|
||||||
|
var filter = Builders<UserAgentDocument>.Filter.Eq(x => x.Id, userAgent.Id);
|
||||||
|
var update = Builders<UserAgentDocument>.Update
|
||||||
|
.Set(x => x.AgentId, userAgent.AgentId)
|
||||||
|
.Set(x => x.UserId, userAgent.UserId)
|
||||||
|
.Set(x => x.Editable, userAgent.Editable)
|
||||||
|
.Set(x => x.CreatedTime, userAgent.CreatedTime)
|
||||||
|
.Set(x => x.UpdatedTime, userAgent.UpdatedTime);
|
||||||
|
_dc.UserAgents.UpdateOne(filter, update, _options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _changedTableNames.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
using BotSharp.Abstraction.Users.Models;
|
||||||
|
using BotSharp.Plugin.MongoStorage.Collections;
|
||||||
|
|
||||||
|
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||||
|
|
||||||
|
public partial class MongoRepository
|
||||||
|
{
|
||||||
|
public User? GetUserByEmail(string email)
|
||||||
|
{
|
||||||
|
var user = _dc.Users.AsQueryable().FirstOrDefault(x => x.Email == email);
|
||||||
|
return user != null ? new User
|
||||||
|
{
|
||||||
|
Id = user.Id,
|
||||||
|
UserName = user.UserName,
|
||||||
|
FirstName = user.FirstName,
|
||||||
|
LastName = user.LastName,
|
||||||
|
Email = user.Email,
|
||||||
|
Password = user.Password,
|
||||||
|
Salt = user.Salt,
|
||||||
|
ExternalId = user.ExternalId,
|
||||||
|
Role = user.Role
|
||||||
|
} : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User? GetUserById(string id)
|
||||||
|
{
|
||||||
|
var user = _dc.Users.AsQueryable().FirstOrDefault(x => x.Id == id || x.ExternalId == id);
|
||||||
|
return user != null ? new User
|
||||||
|
{
|
||||||
|
Id = user.Id,
|
||||||
|
UserName = user.UserName,
|
||||||
|
FirstName = user.FirstName,
|
||||||
|
LastName = user.LastName,
|
||||||
|
Email = user.Email,
|
||||||
|
Password = user.Password,
|
||||||
|
Salt = user.Salt,
|
||||||
|
ExternalId = user.ExternalId,
|
||||||
|
Role = user.Role
|
||||||
|
} : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateUser(User user)
|
||||||
|
{
|
||||||
|
if (user == null) return;
|
||||||
|
|
||||||
|
var userCollection = new UserDocument
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid().ToString(),
|
||||||
|
UserName = user.UserName,
|
||||||
|
FirstName = user.FirstName,
|
||||||
|
LastName = user.LastName,
|
||||||
|
Salt = user.Salt,
|
||||||
|
Password = user.Password,
|
||||||
|
Email = user.Email,
|
||||||
|
ExternalId = user.ExternalId,
|
||||||
|
Role = user.Role,
|
||||||
|
CreatedTime = DateTime.UtcNow,
|
||||||
|
UpdatedTime = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
_dc.Users.InsertOne(userCollection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,10 @@
|
||||||
using BotSharp.Abstraction.Agents.Models;
|
using BotSharp.Abstraction.Agents.Models;
|
||||||
using BotSharp.Abstraction.Conversations.Models;
|
using BotSharp.Abstraction.Conversations.Models;
|
||||||
using BotSharp.Abstraction.Evaluations.Settings;
|
|
||||||
using BotSharp.Abstraction.Functions.Models;
|
|
||||||
using BotSharp.Abstraction.Plugins.Models;
|
|
||||||
using BotSharp.Abstraction.Repositories.Filters;
|
|
||||||
using BotSharp.Abstraction.Repositories.Models;
|
|
||||||
using BotSharp.Abstraction.Routing.Models;
|
|
||||||
using BotSharp.Abstraction.Routing.Settings;
|
|
||||||
using BotSharp.Abstraction.Users.Models;
|
using BotSharp.Abstraction.Users.Models;
|
||||||
using BotSharp.Plugin.MongoStorage.Collections;
|
|
||||||
using BotSharp.Plugin.MongoStorage.Models;
|
|
||||||
|
|
||||||
namespace BotSharp.Plugin.MongoStorage.Repository;
|
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||||
|
|
||||||
public class MongoRepository : IBotSharpRepository
|
public partial class MongoRepository : IBotSharpRepository
|
||||||
{
|
{
|
||||||
private readonly MongoDbContext _dc;
|
private readonly MongoDbContext _dc;
|
||||||
private readonly IServiceProvider _services;
|
private readonly IServiceProvider _services;
|
||||||
|
|
@ -33,957 +24,5 @@ public class MongoRepository : IBotSharpRepository
|
||||||
private List<User> _users = new List<User>();
|
private List<User> _users = new List<User>();
|
||||||
private List<UserAgent> _userAgents = new List<UserAgent>();
|
private List<UserAgent> _userAgents = new List<UserAgent>();
|
||||||
private List<Conversation> _conversations = new List<Conversation>();
|
private List<Conversation> _conversations = new List<Conversation>();
|
||||||
List<string> _changedTableNames = new List<string>();
|
List<string> _changedTableNames = new List<string>();
|
||||||
|
|
||||||
public void Add<TTableInterface>(object entity)
|
|
||||||
{
|
|
||||||
if (entity is Agent agent)
|
|
||||||
{
|
|
||||||
_agents.Add(agent);
|
|
||||||
_changedTableNames.Add(nameof(Agent));
|
|
||||||
}
|
|
||||||
else if (entity is User user)
|
|
||||||
{
|
|
||||||
_users.Add(user);
|
|
||||||
_changedTableNames.Add(nameof(User));
|
|
||||||
}
|
|
||||||
else if (entity is UserAgent userAgent)
|
|
||||||
{
|
|
||||||
_userAgents.Add(userAgent);
|
|
||||||
_changedTableNames.Add(nameof(UserAgent));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Transaction<TTableInterface>(Action action)
|
|
||||||
{
|
|
||||||
_changedTableNames.Clear();
|
|
||||||
action();
|
|
||||||
|
|
||||||
foreach (var table in _changedTableNames)
|
|
||||||
{
|
|
||||||
if (table == nameof(Agent))
|
|
||||||
{
|
|
||||||
var agents = _agents.Select(x => new AgentDocument
|
|
||||||
{
|
|
||||||
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
|
||||||
Name = x.Name,
|
|
||||||
Description = x.Description,
|
|
||||||
Instruction = x.Instruction,
|
|
||||||
Templates = x.Templates?
|
|
||||||
.Select(t => AgentTemplateMongoElement.ToMongoElement(t))?
|
|
||||||
.ToList() ?? new List<AgentTemplateMongoElement>(),
|
|
||||||
Functions = x.Functions?
|
|
||||||
.Select(f => FunctionDefMongoElement.ToMongoElement(f))?
|
|
||||||
.ToList() ?? new List<FunctionDefMongoElement>(),
|
|
||||||
Responses = x.Responses?
|
|
||||||
.Select(r => AgentResponseMongoElement.ToMongoElement(r))?
|
|
||||||
.ToList() ?? new List<AgentResponseMongoElement>(),
|
|
||||||
Samples = x.Samples ?? new List<string>(),
|
|
||||||
IsPublic = x.IsPublic,
|
|
||||||
AllowRouting = x.AllowRouting,
|
|
||||||
Disabled = x.Disabled,
|
|
||||||
Profiles = x.Profiles,
|
|
||||||
RoutingRules = x.RoutingRules?
|
|
||||||
.Select(r => RoutingRuleMongoElement.ToMongoElement(r))?
|
|
||||||
.ToList() ?? new List<RoutingRuleMongoElement>(),
|
|
||||||
LlmConfig = AgentLlmConfigMongoElement.ToMongoElement(x.LlmConfig),
|
|
||||||
CreatedTime = x.CreatedDateTime,
|
|
||||||
UpdatedTime = x.UpdatedDateTime
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
foreach (var agent in agents)
|
|
||||||
{
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agent.Id);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Name, agent.Name)
|
|
||||||
.Set(x => x.Description, agent.Description)
|
|
||||||
.Set(x => x.Instruction, agent.Instruction)
|
|
||||||
.Set(x => x.Templates, agent.Templates)
|
|
||||||
.Set(x => x.Functions, agent.Functions)
|
|
||||||
.Set(x => x.Responses, agent.Responses)
|
|
||||||
.Set(x => x.Samples, agent.Samples)
|
|
||||||
.Set(x => x.IsPublic, agent.IsPublic)
|
|
||||||
.Set(x => x.AllowRouting, agent.AllowRouting)
|
|
||||||
.Set(x => x.Disabled, agent.Disabled)
|
|
||||||
.Set(x => x.Profiles, agent.Profiles)
|
|
||||||
.Set(x => x.RoutingRules, agent.RoutingRules)
|
|
||||||
.Set(x => x.LlmConfig, agent.LlmConfig)
|
|
||||||
.Set(x => x.CreatedTime, agent.CreatedTime)
|
|
||||||
.Set(x => x.UpdatedTime, agent.UpdatedTime);
|
|
||||||
_dc.Agents.UpdateOne(filter, update, _options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (table == nameof(User))
|
|
||||||
{
|
|
||||||
var users = _users.Select(x => new UserDocument
|
|
||||||
{
|
|
||||||
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
|
||||||
UserName = x.UserName,
|
|
||||||
FirstName = x.FirstName,
|
|
||||||
LastName = x.LastName,
|
|
||||||
Salt = x.Salt,
|
|
||||||
Password = x.Password,
|
|
||||||
Email = x.Email,
|
|
||||||
ExternalId = x.ExternalId,
|
|
||||||
Role = x.Role,
|
|
||||||
CreatedTime = x.CreatedTime,
|
|
||||||
UpdatedTime = x.UpdatedTime
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
foreach (var user in users)
|
|
||||||
{
|
|
||||||
var filter = Builders<UserDocument>.Filter.Eq(x => x.Id, user.Id);
|
|
||||||
var update = Builders<UserDocument>.Update
|
|
||||||
.Set(x => x.UserName, user.UserName)
|
|
||||||
.Set(x => x.FirstName, user.FirstName)
|
|
||||||
.Set(x => x.LastName, user.LastName)
|
|
||||||
.Set(x => x.Email, user.Email)
|
|
||||||
.Set(x => x.Salt, user.Salt)
|
|
||||||
.Set(x => x.Password, user.Password)
|
|
||||||
.Set(x => x.ExternalId, user.ExternalId)
|
|
||||||
.Set(x => x.Role, user.Role)
|
|
||||||
.Set(x => x.CreatedTime, user.CreatedTime)
|
|
||||||
.Set(x => x.UpdatedTime, user.UpdatedTime);
|
|
||||||
_dc.Users.UpdateOne(filter, update, _options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (table == nameof(UserAgent))
|
|
||||||
{
|
|
||||||
var userAgents = _userAgents.Select(x => new UserAgentDocument
|
|
||||||
{
|
|
||||||
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
|
||||||
AgentId = x.AgentId,
|
|
||||||
UserId = !string.IsNullOrEmpty(x.UserId) ? x.UserId : string.Empty,
|
|
||||||
Editable = x.Editable,
|
|
||||||
CreatedTime = x.CreatedTime,
|
|
||||||
UpdatedTime = x.UpdatedTime
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
foreach (var userAgent in userAgents)
|
|
||||||
{
|
|
||||||
var filter = Builders<UserAgentDocument>.Filter.Eq(x => x.Id, userAgent.Id);
|
|
||||||
var update = Builders<UserAgentDocument>.Update
|
|
||||||
.Set(x => x.AgentId, userAgent.AgentId)
|
|
||||||
.Set(x => x.UserId, userAgent.UserId)
|
|
||||||
.Set(x => x.Editable, userAgent.Editable)
|
|
||||||
.Set(x => x.CreatedTime, userAgent.CreatedTime)
|
|
||||||
.Set(x => x.UpdatedTime, userAgent.UpdatedTime);
|
|
||||||
_dc.UserAgents.UpdateOne(filter, update, _options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _changedTableNames.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Plugin
|
|
||||||
public PluginConfig GetPluginConfig()
|
|
||||||
{
|
|
||||||
return new PluginConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SavePluginConfig(PluginConfig config)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Agent
|
|
||||||
public void UpdateAgent(Agent agent, AgentField field)
|
|
||||||
{
|
|
||||||
if (agent == null || string.IsNullOrEmpty(agent.Id)) return;
|
|
||||||
|
|
||||||
switch (field)
|
|
||||||
{
|
|
||||||
case AgentField.Name:
|
|
||||||
UpdateAgentName(agent.Id, agent.Name);
|
|
||||||
break;
|
|
||||||
case AgentField.Description:
|
|
||||||
UpdateAgentDescription(agent.Id, agent.Description);
|
|
||||||
break;
|
|
||||||
case AgentField.IsPublic:
|
|
||||||
UpdateAgentIsPublic(agent.Id, agent.IsPublic);
|
|
||||||
break;
|
|
||||||
case AgentField.Disabled:
|
|
||||||
UpdateAgentDisabled(agent.Id, agent.Disabled);
|
|
||||||
break;
|
|
||||||
case AgentField.AllowRouting:
|
|
||||||
UpdateAgentAllowRouting(agent.Id, agent.AllowRouting);
|
|
||||||
break;
|
|
||||||
case AgentField.Profiles:
|
|
||||||
UpdateAgentProfiles(agent.Id, agent.Profiles);
|
|
||||||
break;
|
|
||||||
case AgentField.RoutingRule:
|
|
||||||
UpdateAgentRoutingRules(agent.Id, agent.RoutingRules);
|
|
||||||
break;
|
|
||||||
case AgentField.Instruction:
|
|
||||||
UpdateAgentInstruction(agent.Id, agent.Instruction);
|
|
||||||
break;
|
|
||||||
case AgentField.Function:
|
|
||||||
UpdateAgentFunctions(agent.Id, agent.Functions);
|
|
||||||
break;
|
|
||||||
case AgentField.Template:
|
|
||||||
UpdateAgentTemplates(agent.Id, agent.Templates);
|
|
||||||
break;
|
|
||||||
case AgentField.Response:
|
|
||||||
UpdateAgentResponses(agent.Id, agent.Responses);
|
|
||||||
break;
|
|
||||||
case AgentField.Sample:
|
|
||||||
UpdateAgentSamples(agent.Id, agent.Samples);
|
|
||||||
break;
|
|
||||||
case AgentField.LlmConfig:
|
|
||||||
UpdateAgentLlmConfig(agent.Id, agent.LlmConfig);
|
|
||||||
break;
|
|
||||||
case AgentField.All:
|
|
||||||
UpdateAgentAllFields(agent);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Update Agent Fields
|
|
||||||
private void UpdateAgentName(string agentId, string name)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(name)) return;
|
|
||||||
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Name, name)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentDescription(string agentId, string description)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(description)) return;
|
|
||||||
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Description, description)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentIsPublic(string agentId, bool isPublic)
|
|
||||||
{
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.IsPublic, isPublic)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentDisabled(string agentId, bool disabled)
|
|
||||||
{
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Disabled, disabled)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentAllowRouting(string agentId, bool allowRouting)
|
|
||||||
{
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.AllowRouting, allowRouting)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentProfiles(string agentId, List<string> profiles)
|
|
||||||
{
|
|
||||||
if (profiles.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Profiles, profiles)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentRoutingRules(string agentId, List<RoutingRule> rules)
|
|
||||||
{
|
|
||||||
if (rules.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var ruleElements = rules.Select(x => RoutingRuleMongoElement.ToMongoElement(x)).ToList();
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.RoutingRules, ruleElements)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentInstruction(string agentId, string instruction)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(instruction)) return;
|
|
||||||
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Instruction, instruction)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentFunctions(string agentId, List<FunctionDef> functions)
|
|
||||||
{
|
|
||||||
if (functions.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var functionsToUpdate = functions.Select(f => FunctionDefMongoElement.ToMongoElement(f)).ToList();
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Functions, functionsToUpdate)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentTemplates(string agentId, List<AgentTemplate> templates)
|
|
||||||
{
|
|
||||||
if (templates.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var templatesToUpdate = templates.Select(t => AgentTemplateMongoElement.ToMongoElement(t)).ToList();
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Templates, templatesToUpdate)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentResponses(string agentId, List<AgentResponse> responses)
|
|
||||||
{
|
|
||||||
if (responses.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var responsesToUpdate = responses.Select(r => AgentResponseMongoElement.ToMongoElement(r)).ToList();
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Responses, responsesToUpdate)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentSamples(string agentId, List<string> samples)
|
|
||||||
{
|
|
||||||
if (samples.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Samples, samples)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentLlmConfig(string agentId, AgentLlmConfig? config)
|
|
||||||
{
|
|
||||||
var llmConfig = AgentLlmConfigMongoElement.ToMongoElement(config);
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.LlmConfig, llmConfig)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Agents.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentAllFields(Agent agent)
|
|
||||||
{
|
|
||||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agent.Id);
|
|
||||||
var update = Builders<AgentDocument>.Update
|
|
||||||
.Set(x => x.Name, agent.Name)
|
|
||||||
.Set(x => x.Description, agent.Description)
|
|
||||||
.Set(x => x.Disabled, agent.Disabled)
|
|
||||||
.Set(x => x.AllowRouting, agent.AllowRouting)
|
|
||||||
.Set(x => x.Profiles, agent.Profiles)
|
|
||||||
.Set(x => x.RoutingRules, agent.RoutingRules.Select(r => RoutingRuleMongoElement.ToMongoElement(r)).ToList())
|
|
||||||
.Set(x => x.Instruction, agent.Instruction)
|
|
||||||
.Set(x => x.Templates, agent.Templates.Select(t => AgentTemplateMongoElement.ToMongoElement(t)).ToList())
|
|
||||||
.Set(x => x.Functions, agent.Functions.Select(f => FunctionDefMongoElement.ToMongoElement(f)).ToList())
|
|
||||||
.Set(x => x.Responses, agent.Responses.Select(r => AgentResponseMongoElement.ToMongoElement(r)).ToList())
|
|
||||||
.Set(x => x.Samples, agent.Samples)
|
|
||||||
.Set(x => x.LlmConfig, AgentLlmConfigMongoElement.ToMongoElement(agent.LlmConfig))
|
|
||||||
.Set(x => x.IsPublic, agent.IsPublic)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
var res = _dc.Agents.UpdateOne(filter, update);
|
|
||||||
Console.WriteLine();
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
public Agent? GetAgent(string agentId)
|
|
||||||
{
|
|
||||||
var agent = _dc.Agents.AsQueryable().FirstOrDefault(x => x.Id == agentId);
|
|
||||||
if (agent == null) return null;
|
|
||||||
|
|
||||||
return new Agent
|
|
||||||
{
|
|
||||||
Id = agent.Id,
|
|
||||||
Name = agent.Name,
|
|
||||||
Description = agent.Description,
|
|
||||||
Instruction = agent.Instruction,
|
|
||||||
Templates = !agent.Templates.IsNullOrEmpty() ? agent.Templates
|
|
||||||
.Select(t => AgentTemplateMongoElement.ToDomainElement(t))
|
|
||||||
.ToList() : new List<AgentTemplate>(),
|
|
||||||
Functions = !agent.Functions.IsNullOrEmpty() ? agent.Functions
|
|
||||||
.Select(f => FunctionDefMongoElement.ToDomainElement(f))
|
|
||||||
.ToList() : new List<FunctionDef>(),
|
|
||||||
Responses = !agent.Responses.IsNullOrEmpty() ? agent.Responses
|
|
||||||
.Select(r => AgentResponseMongoElement.ToDomainElement(r))
|
|
||||||
.ToList() : new List<AgentResponse>(),
|
|
||||||
Samples = agent.Samples ?? new List<string>(),
|
|
||||||
IsPublic = agent.IsPublic,
|
|
||||||
Disabled = agent.Disabled,
|
|
||||||
AllowRouting = agent.AllowRouting,
|
|
||||||
Profiles = agent.Profiles,
|
|
||||||
RoutingRules = !agent.RoutingRules.IsNullOrEmpty() ? agent.RoutingRules
|
|
||||||
.Select(r => RoutingRuleMongoElement.ToDomainElement(agent.Id, agent.Name, r))
|
|
||||||
.ToList() : new List<RoutingRule>(),
|
|
||||||
LlmConfig = AgentLlmConfigMongoElement.ToDomainElement(agent.LlmConfig)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Agent> GetAgents(AgentFilter filter)
|
|
||||||
{
|
|
||||||
var agents = new List<Agent>();
|
|
||||||
IQueryable<AgentDocument> query = _dc.Agents.AsQueryable();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(filter.AgentName))
|
|
||||||
{
|
|
||||||
query = query.Where(x => x.Name.ToLower() == filter.AgentName.ToLower());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.Disabled.HasValue)
|
|
||||||
{
|
|
||||||
query = query.Where(x => x.Disabled == filter.Disabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.AllowRouting.HasValue)
|
|
||||||
{
|
|
||||||
query = query.Where(x => x.AllowRouting == filter.AllowRouting);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.IsPublic.HasValue)
|
|
||||||
{
|
|
||||||
query = query.Where(x => x.IsPublic == filter.IsPublic);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.IsRouter.HasValue)
|
|
||||||
{
|
|
||||||
var route = _services.GetRequiredService<RoutingSettings>();
|
|
||||||
query = filter.IsRouter.Value ?
|
|
||||||
query.Where(x => x.Id == route.AgentId) :
|
|
||||||
query.Where(x => x.Id != route.AgentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.IsEvaluator.HasValue)
|
|
||||||
{
|
|
||||||
var evaluate = _services.GetRequiredService<EvaluatorSetting>();
|
|
||||||
query = filter.IsEvaluator.Value ?
|
|
||||||
query.Where(x => x.Id == evaluate.AgentId) :
|
|
||||||
query.Where(x => x.Id != evaluate.AgentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.AgentIds != null)
|
|
||||||
{
|
|
||||||
query = query.Where(x => filter.AgentIds.Contains(x.Id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return query.ToList().Select(x => new Agent
|
|
||||||
{
|
|
||||||
Id = x.Id,
|
|
||||||
Name = x.Name,
|
|
||||||
Description = x.Description,
|
|
||||||
Instruction = x.Instruction,
|
|
||||||
Templates = !x.Templates.IsNullOrEmpty() ? x.Templates
|
|
||||||
.Select(t => AgentTemplateMongoElement.ToDomainElement(t))
|
|
||||||
.ToList() : new List<AgentTemplate>(),
|
|
||||||
Functions = !x.Functions.IsNullOrEmpty() ? x.Functions
|
|
||||||
.Select(f => FunctionDefMongoElement.ToDomainElement(f))
|
|
||||||
.ToList() : new List<FunctionDef>(),
|
|
||||||
Responses = !x.Responses.IsNullOrEmpty() ? x.Responses
|
|
||||||
.Select(r => AgentResponseMongoElement.ToDomainElement(r))
|
|
||||||
.ToList() : new List<AgentResponse>(),
|
|
||||||
Samples = x.Samples ?? new List<string>(),
|
|
||||||
IsPublic = x.IsPublic,
|
|
||||||
Disabled = x.Disabled,
|
|
||||||
AllowRouting = x.AllowRouting,
|
|
||||||
Profiles = x.Profiles,
|
|
||||||
RoutingRules = !x.RoutingRules.IsNullOrEmpty() ? x.RoutingRules
|
|
||||||
.Select(r => RoutingRuleMongoElement.ToDomainElement(x.Id, x.Name, r))
|
|
||||||
.ToList() : new List<RoutingRule>(),
|
|
||||||
LlmConfig = AgentLlmConfigMongoElement.ToDomainElement(x.LlmConfig)
|
|
||||||
}).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Agent> GetAgentsByUser(string userId)
|
|
||||||
{
|
|
||||||
var agentIds = (from ua in _dc.UserAgents.AsQueryable()
|
|
||||||
join u in _dc.Users.AsQueryable() on ua.UserId equals u.Id
|
|
||||||
where ua.UserId == userId || u.ExternalId == userId
|
|
||||||
select ua.AgentId).ToList();
|
|
||||||
|
|
||||||
var filter = new AgentFilter
|
|
||||||
{
|
|
||||||
AgentIds = agentIds,
|
|
||||||
IsPublic = true
|
|
||||||
};
|
|
||||||
var agents = GetAgents(filter);
|
|
||||||
return agents;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<string> GetAgentResponses(string agentId, string prefix, string intent)
|
|
||||||
{
|
|
||||||
var responses = new List<string>();
|
|
||||||
var agent = _dc.Agents.AsQueryable().FirstOrDefault(x => x.Id == agentId);
|
|
||||||
if (agent == null) return responses;
|
|
||||||
|
|
||||||
return agent.Responses.Where(x => x.Prefix == prefix && x.Intent == intent).Select(x => x.Content).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetAgentTemplate(string agentId, string templateName)
|
|
||||||
{
|
|
||||||
var agent = _dc.Agents.AsQueryable().FirstOrDefault(x => x.Id == agentId);
|
|
||||||
if (agent == null) return string.Empty;
|
|
||||||
|
|
||||||
return agent.Templates?.FirstOrDefault(x => x.Name == templateName.ToLower())?.Content ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BulkInsertAgents(List<Agent> agents)
|
|
||||||
{
|
|
||||||
if (agents.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var agentDocs = agents.Select(x => new AgentDocument
|
|
||||||
{
|
|
||||||
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
|
||||||
Name = x.Name,
|
|
||||||
Description = x.Description,
|
|
||||||
Instruction = x.Instruction,
|
|
||||||
Templates = x.Templates?
|
|
||||||
.Select(t => AgentTemplateMongoElement.ToMongoElement(t))?
|
|
||||||
.ToList() ?? new List<AgentTemplateMongoElement>(),
|
|
||||||
Functions = x.Functions?
|
|
||||||
.Select(f => FunctionDefMongoElement.ToMongoElement(f))?
|
|
||||||
.ToList() ?? new List<FunctionDefMongoElement>(),
|
|
||||||
Responses = x.Responses?
|
|
||||||
.Select(r => AgentResponseMongoElement.ToMongoElement(r))?
|
|
||||||
.ToList() ?? new List<AgentResponseMongoElement>(),
|
|
||||||
Samples = x.Samples ?? new List<string>(),
|
|
||||||
IsPublic = x.IsPublic,
|
|
||||||
AllowRouting = x.AllowRouting,
|
|
||||||
Disabled = x.Disabled,
|
|
||||||
Profiles = x.Profiles,
|
|
||||||
RoutingRules = x.RoutingRules?
|
|
||||||
.Select(r => RoutingRuleMongoElement.ToMongoElement(r))?
|
|
||||||
.ToList() ?? new List<RoutingRuleMongoElement>(),
|
|
||||||
LlmConfig = AgentLlmConfigMongoElement.ToMongoElement(x.LlmConfig),
|
|
||||||
CreatedTime = x.CreatedDateTime,
|
|
||||||
UpdatedTime = x.UpdatedDateTime
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
_dc.Agents.InsertMany(agentDocs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BulkInsertUserAgents(List<UserAgent> userAgents)
|
|
||||||
{
|
|
||||||
if (userAgents.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var userAgentDocs = userAgents.Select(x => new UserAgentDocument
|
|
||||||
{
|
|
||||||
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
|
||||||
AgentId = x.AgentId,
|
|
||||||
UserId = !string.IsNullOrEmpty(x.UserId) ? x.UserId : string.Empty,
|
|
||||||
Editable = x.Editable,
|
|
||||||
CreatedTime = x.CreatedTime,
|
|
||||||
UpdatedTime = x.UpdatedTime
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
_dc.UserAgents.InsertMany(userAgentDocs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DeleteAgents()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_dc.UserAgents.DeleteMany(Builders<UserAgentDocument>.Filter.Empty);
|
|
||||||
_dc.Agents.DeleteMany(Builders<AgentDocument>.Filter.Empty);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Conversation
|
|
||||||
public void CreateNewConversation(Conversation conversation)
|
|
||||||
{
|
|
||||||
if (conversation == null) return;
|
|
||||||
|
|
||||||
var convDoc = new ConversationDocument
|
|
||||||
{
|
|
||||||
Id = !string.IsNullOrEmpty(conversation.Id) ? conversation.Id : Guid.NewGuid().ToString(),
|
|
||||||
AgentId = conversation.AgentId,
|
|
||||||
UserId = !string.IsNullOrEmpty(conversation.UserId) ? conversation.UserId : string.Empty,
|
|
||||||
Title = conversation.Title,
|
|
||||||
Channel = conversation.Channel,
|
|
||||||
Status = conversation.Status,
|
|
||||||
CreatedTime = DateTime.UtcNow,
|
|
||||||
UpdatedTime = DateTime.UtcNow,
|
|
||||||
};
|
|
||||||
|
|
||||||
var dialogDoc = new ConversationDialogDocument
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString(),
|
|
||||||
ConversationId = convDoc.Id,
|
|
||||||
Dialogs = new List<DialogMongoElement>()
|
|
||||||
};
|
|
||||||
|
|
||||||
var states = conversation.States ?? new Dictionary<string, string>();
|
|
||||||
var initialStates = states.Select(x => new StateMongoElement
|
|
||||||
{
|
|
||||||
Key = x.Key,
|
|
||||||
Values = new List<StateValueMongoElement>
|
|
||||||
{
|
|
||||||
new StateValueMongoElement { Data = x.Value, UpdateTime = DateTime.UtcNow }
|
|
||||||
}
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
var stateDoc = new ConversationStateDocument
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString(),
|
|
||||||
ConversationId = convDoc.Id,
|
|
||||||
States = initialStates
|
|
||||||
};
|
|
||||||
|
|
||||||
_dc.Conversations.InsertOne(convDoc);
|
|
||||||
_dc.ConversationDialogs.InsertOne(dialogDoc);
|
|
||||||
_dc.ConversationStates.InsertOne(stateDoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DeleteConversation(string conversationId)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(conversationId)) return false;
|
|
||||||
|
|
||||||
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
||||||
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var filterSates = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var filterExeLog = Builders<ExecutionLogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var filterPromptLog = Builders<LlmCompletionLogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
|
|
||||||
var exeLogDeleted = _dc.ExectionLogs.DeleteMany(filterExeLog);
|
|
||||||
var promptLogDeleted = _dc.LlmCompletionLogs.DeleteMany(filterPromptLog);
|
|
||||||
var statesDeleted = _dc.ConversationStates.DeleteMany(filterSates);
|
|
||||||
var dialogDeleted = _dc.ConversationDialogs.DeleteMany(filterDialog);
|
|
||||||
var convDeleted = _dc.Conversations.DeleteMany(filterConv);
|
|
||||||
return convDeleted.DeletedCount > 0 || dialogDeleted.DeletedCount > 0 || statesDeleted.DeletedCount > 0
|
|
||||||
|| exeLogDeleted.DeletedCount > 0 || promptLogDeleted.DeletedCount > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<DialogElement> GetConversationDialogs(string conversationId)
|
|
||||||
{
|
|
||||||
var dialogs = new List<DialogElement>();
|
|
||||||
if (string.IsNullOrEmpty(conversationId)) return dialogs;
|
|
||||||
|
|
||||||
var filter = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var foundDialog = _dc.ConversationDialogs.Find(filter).FirstOrDefault();
|
|
||||||
if (foundDialog == null) return dialogs;
|
|
||||||
|
|
||||||
var formattedDialog = foundDialog.Dialogs?.Select(x => DialogMongoElement.ToDomainElement(x))?.ToList();
|
|
||||||
return formattedDialog ?? new List<DialogElement>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateConversationDialogElements(string conversationId, List<DialogContentUpdateModel> updateElements)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(conversationId) || updateElements.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var foundDialog = _dc.ConversationDialogs.Find(filterDialog).FirstOrDefault();
|
|
||||||
if (foundDialog == null || foundDialog.Dialogs.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
foundDialog.Dialogs = foundDialog.Dialogs.Select((x, idx) =>
|
|
||||||
{
|
|
||||||
var found = updateElements.FirstOrDefault(e => e.Index == idx);
|
|
||||||
if (found != null)
|
|
||||||
{
|
|
||||||
x.Content = found.UpdateContent;
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
_dc.ConversationDialogs.ReplaceOne(filterDialog, foundDialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AppendConversationDialogs(string conversationId, List<DialogElement> dialogs)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(conversationId)) return;
|
|
||||||
|
|
||||||
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
||||||
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var dialogElements = dialogs.Select(x => DialogMongoElement.ToMongoElement(x)).ToList();
|
|
||||||
var updateDialog = Builders<ConversationDialogDocument>.Update.PushEach(x => x.Dialogs, dialogElements);
|
|
||||||
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.ConversationDialogs.UpdateOne(filterDialog, updateDialog);
|
|
||||||
_dc.Conversations.UpdateOne(filterConv, updateConv);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateConversationTitle(string conversationId, string title)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(conversationId)) return;
|
|
||||||
|
|
||||||
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
||||||
var updateConv = Builders<ConversationDocument>.Update
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow)
|
|
||||||
.Set(x => x.Title, title);
|
|
||||||
|
|
||||||
_dc.Conversations.UpdateOne(filterConv, updateConv);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConversationState GetConversationStates(string conversationId)
|
|
||||||
{
|
|
||||||
var states = new ConversationState();
|
|
||||||
if (string.IsNullOrEmpty(conversationId)) return states;
|
|
||||||
|
|
||||||
var filter = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var foundStates = _dc.ConversationStates.Find(filter).FirstOrDefault();
|
|
||||||
if (foundStates == null || foundStates.States.IsNullOrEmpty()) return states;
|
|
||||||
|
|
||||||
var savedStates = foundStates.States.Select(x => StateMongoElement.ToDomainElement(x)).ToList();
|
|
||||||
return new ConversationState(savedStates);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(conversationId) || states.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var filterStates = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var saveStates = states.Select(x => StateMongoElement.ToMongoElement(x)).ToList();
|
|
||||||
var updateStates = Builders<ConversationStateDocument>.Update.Set(x => x.States, saveStates);
|
|
||||||
|
|
||||||
_dc.ConversationStates.UpdateOne(filterStates, updateStates);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateConversationStatus(string conversationId, string status)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(status)) return;
|
|
||||||
|
|
||||||
var filter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
||||||
var update = Builders<ConversationDocument>.Update
|
|
||||||
.Set(x => x.Status, status)
|
|
||||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
||||||
|
|
||||||
_dc.Conversations.UpdateOne(filter, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Conversation GetConversation(string conversationId)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(conversationId)) return null;
|
|
||||||
|
|
||||||
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
||||||
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var filterState = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
|
|
||||||
var conv = _dc.Conversations.Find(filterConv).FirstOrDefault();
|
|
||||||
var dialog = _dc.ConversationDialogs.Find(filterDialog).FirstOrDefault();
|
|
||||||
var states = _dc.ConversationStates.Find(filterState).FirstOrDefault();
|
|
||||||
|
|
||||||
if (conv == null) return null;
|
|
||||||
|
|
||||||
var dialogElements = dialog?.Dialogs?.Select(x => DialogMongoElement.ToDomainElement(x))?.ToList() ?? new List<DialogElement>();
|
|
||||||
var curStates = new Dictionary<string, string>();
|
|
||||||
states.States.ForEach(x =>
|
|
||||||
{
|
|
||||||
curStates[x.Key] = x.Values?.LastOrDefault()?.Data ?? string.Empty;
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Conversation
|
|
||||||
{
|
|
||||||
Id = conv.Id.ToString(),
|
|
||||||
AgentId = conv.AgentId.ToString(),
|
|
||||||
UserId = conv.UserId.ToString(),
|
|
||||||
Title = conv.Title,
|
|
||||||
Channel = conv.Channel,
|
|
||||||
Status = conv.Status,
|
|
||||||
Dialogs = dialogElements,
|
|
||||||
States = curStates,
|
|
||||||
CreatedTime = conv.CreatedTime,
|
|
||||||
UpdatedTime = conv.UpdatedTime
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Conversation> GetConversations(ConversationFilter filter)
|
|
||||||
{
|
|
||||||
var records = new List<Conversation>();
|
|
||||||
var builder = Builders<ConversationDocument>.Filter;
|
|
||||||
var filters = new List<FilterDefinition<ConversationDocument>>();
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
foreach (var conv in conversations)
|
|
||||||
{
|
|
||||||
var convId = conv.Id.ToString();
|
|
||||||
records.Add(new Conversation
|
|
||||||
{
|
|
||||||
Id = convId,
|
|
||||||
AgentId = conv.AgentId.ToString(),
|
|
||||||
UserId = conv.UserId.ToString(),
|
|
||||||
Title = conv.Title,
|
|
||||||
Channel = conv.Channel,
|
|
||||||
Status = conv.Status,
|
|
||||||
CreatedTime = conv.CreatedTime,
|
|
||||||
UpdatedTime = conv.UpdatedTime
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return records;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Conversation> GetLastConversations()
|
|
||||||
{
|
|
||||||
var records = new List<Conversation>();
|
|
||||||
var conversations = _dc.Conversations.Aggregate()
|
|
||||||
.Group(c => c.UserId, g => g.OrderByDescending(x => x.CreatedTime).First())
|
|
||||||
.ToList();
|
|
||||||
return conversations.Select(c => new Conversation()
|
|
||||||
{
|
|
||||||
Id = c.Id.ToString(),
|
|
||||||
AgentId = c.AgentId.ToString(),
|
|
||||||
UserId = c.UserId.ToString(),
|
|
||||||
Title = c.Title,
|
|
||||||
Channel = c.Channel,
|
|
||||||
Status = c.Status,
|
|
||||||
CreatedTime = c.CreatedTime,
|
|
||||||
UpdatedTime = c.UpdatedTime
|
|
||||||
}).ToList();
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region User
|
|
||||||
public User? GetUserByEmail(string email)
|
|
||||||
{
|
|
||||||
var user = _dc.Users.AsQueryable().FirstOrDefault(x => x.Email == email);
|
|
||||||
return user != null ? new User
|
|
||||||
{
|
|
||||||
Id = user.Id,
|
|
||||||
UserName = user.UserName,
|
|
||||||
FirstName = user.FirstName,
|
|
||||||
LastName = user.LastName,
|
|
||||||
Email = user.Email,
|
|
||||||
Password = user.Password,
|
|
||||||
Salt = user.Salt,
|
|
||||||
ExternalId = user.ExternalId,
|
|
||||||
Role = user.Role
|
|
||||||
} : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public User? GetUserById(string id)
|
|
||||||
{
|
|
||||||
var user = _dc.Users.AsQueryable().FirstOrDefault(x => x.Id == id || x.ExternalId == id);
|
|
||||||
return user != null ? new User
|
|
||||||
{
|
|
||||||
Id = user.Id,
|
|
||||||
UserName = user.UserName,
|
|
||||||
FirstName = user.FirstName,
|
|
||||||
LastName = user.LastName,
|
|
||||||
Email = user.Email,
|
|
||||||
Password = user.Password,
|
|
||||||
Salt = user.Salt,
|
|
||||||
ExternalId = user.ExternalId,
|
|
||||||
Role = user.Role
|
|
||||||
} : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateUser(User user)
|
|
||||||
{
|
|
||||||
if (user == null) return;
|
|
||||||
|
|
||||||
var userCollection = new UserDocument
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString(),
|
|
||||||
UserName = user.UserName,
|
|
||||||
FirstName = user.FirstName,
|
|
||||||
LastName = user.LastName,
|
|
||||||
Salt = user.Salt,
|
|
||||||
Password = user.Password,
|
|
||||||
Email = user.Email,
|
|
||||||
ExternalId = user.ExternalId,
|
|
||||||
Role = user.Role,
|
|
||||||
CreatedTime = DateTime.UtcNow,
|
|
||||||
UpdatedTime = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
_dc.Users.InsertOne(userCollection);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Execution Log
|
|
||||||
public void AddExecutionLogs(string conversationId, List<string> logs)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(conversationId) || logs.IsNullOrEmpty()) return;
|
|
||||||
|
|
||||||
var filter = Builders<ExecutionLogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var update = Builders<ExecutionLogDocument>.Update
|
|
||||||
.SetOnInsert(x => x.Id, Guid.NewGuid().ToString())
|
|
||||||
.PushEach(x => x.Logs, logs);
|
|
||||||
|
|
||||||
_dc.ExectionLogs.UpdateOne(filter, update, _options);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<string> GetExecutionLogs(string conversationId)
|
|
||||||
{
|
|
||||||
var logs = new List<string>();
|
|
||||||
if (string.IsNullOrEmpty(conversationId)) return logs;
|
|
||||||
|
|
||||||
var filter = Builders<ExecutionLogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var logCollection = _dc.ExectionLogs.Find(filter).FirstOrDefault();
|
|
||||||
|
|
||||||
logs = logCollection?.Logs ?? new List<string>();
|
|
||||||
return logs;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region LLM Completion Log
|
|
||||||
public void SaveLlmCompletionLog(LlmCompletionLog log)
|
|
||||||
{
|
|
||||||
if (log == null) return;
|
|
||||||
|
|
||||||
var conversationId = log.ConversationId.IfNullOrEmptyAs(Guid.NewGuid().ToString());
|
|
||||||
var messageId = log.MessageId.IfNullOrEmptyAs(Guid.NewGuid().ToString());
|
|
||||||
|
|
||||||
var logElement = new PromptLogMongoElement
|
|
||||||
{
|
|
||||||
MessageId = messageId,
|
|
||||||
AgentId = log.AgentId,
|
|
||||||
Prompt = log.Prompt,
|
|
||||||
Response = log.Response,
|
|
||||||
CreateDateTime = log.CreateDateTime
|
|
||||||
};
|
|
||||||
|
|
||||||
var filter = Builders<LlmCompletionLogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
||||||
var update = Builders<LlmCompletionLogDocument>.Update
|
|
||||||
.SetOnInsert(x => x.Id, Guid.NewGuid().ToString())
|
|
||||||
.Push(x => x.Logs, logElement);
|
|
||||||
|
|
||||||
_dc.LlmCompletionLogs.UpdateOne(filter, update, _options);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue