refine repository for agent code script

This commit is contained in:
Jicheng Lu 2025-10-06 01:27:18 -05:00
parent 74d0c96e2f
commit 5be85d3f4b
29 changed files with 494 additions and 385 deletions

View file

@ -0,0 +1,7 @@
namespace BotSharp.Abstraction.Agents.Enums;
public static class AgentCodeScriptType
{
public const string Src = "src";
public const string Test = "test";
}

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Agents.Enums; namespace BotSharp.Abstraction.Agents.Enums;
public class AgentFuncVisMode public static class AgentFuncVisMode
{ {
public const string Manual = "manual"; public const string Manual = "manual";
public const string Auto = "auto"; public const string Auto = "auto";

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Agents.Enums; namespace BotSharp.Abstraction.Agents.Enums;
public class AgentRole public static class AgentRole
{ {
public const string System = "system"; public const string System = "system";
public const string Assistant = "assistant"; public const string Assistant = "assistant";

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Agents.Enums; namespace BotSharp.Abstraction.Agents.Enums;
public class AgentType public static class AgentType
{ {
/// <summary> /// <summary>
/// Routing agent /// Routing agent

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Agents.Enums; namespace BotSharp.Abstraction.Agents.Enums;
public class BuiltInAgentId public static class BuiltInAgentId
{ {
/// <summary> /// <summary>
/// A routing agent can be used as a base router. /// A routing agent can be used as a base router.

View file

@ -1,18 +1,29 @@
namespace BotSharp.Abstraction.Agents.Models; namespace BotSharp.Abstraction.Agents.Models;
public class AgentCodeScript public class AgentCodeScript : AgentCodeScriptBase
{ {
public string Id { get; set; } public string Id { get; set; }
public string AgentId { get; set; } public string AgentId { get; set; } = null!;
public string Name { get; set; }
public string Content { get; set; }
public AgentCodeScript() public AgentCodeScript() : base()
{ {
} }
public override string ToString() public override string ToString()
{ {
return Name; return $"{CodePath}";
} }
} }
public class AgentCodeScriptBase
{
public string Name { get; set; } = null!;
public string Content { get; set; } = null!;
/// <summary>
/// Code script type: src, test
/// </summary>
public string ScriptType { get; set; } = null!;
public string CodePath => $"{ScriptType}/{Name}";
}

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Conversations.Enums; namespace BotSharp.Abstraction.Conversations.Enums;
public class ConversationChannel public static class ConversationChannel
{ {
public const string WebChat = "webchat"; public const string WebChat = "webchat";
public const string OpenAPI = "openapi"; public const string OpenAPI = "openapi";

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Conversations.Enums; namespace BotSharp.Abstraction.Conversations.Enums;
public class ConversationStatus public static class ConversationStatus
{ {
public const string Open = "open"; public const string Open = "open";
public const string Closed = "closed"; public const string Closed = "closed";

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Conversations.Enums; namespace BotSharp.Abstraction.Conversations.Enums;
public class StateDataType public static class StateDataType
{ {
public const string String = "string"; public const string String = "string";
public const string Boolean = "boolean"; public const string Boolean = "boolean";

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Conversations.Enums; namespace BotSharp.Abstraction.Conversations.Enums;
public class StateSource public static class StateSource
{ {
public const string External = "external"; public const string External = "external";
public const string Application = "application"; public const string Application = "application";

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Infrastructures.Enums; namespace BotSharp.Abstraction.Infrastructures.Enums;
public class LanguageType public static class LanguageType
{ {
public const string UNKNOWN = "Unknown"; public const string UNKNOWN = "Unknown";
public const string ENGLISH = "English"; public const string ENGLISH = "English";

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Infrastructures.Enums; namespace BotSharp.Abstraction.Infrastructures.Enums;
public class StateConst public static class StateConst
{ {
public const string EXPECTED_ACTION_AGENT = "expected_next_action_agent"; public const string EXPECTED_ACTION_AGENT = "expected_next_action_agent";
public const string EXPECTED_GOAL_AGENT = "expected_user_goal_agent"; public const string EXPECTED_GOAL_AGENT = "expected_user_goal_agent";

View file

@ -5,7 +5,10 @@ namespace BotSharp.Abstraction.Instructs;
public interface IInstructHook : IHookBase public interface IInstructHook : IHookBase
{ {
Task BeforeCompletion(Agent agent, RoleDialogModel message); Task BeforeCompletion(Agent agent, RoleDialogModel message) => Task.CompletedTask;
Task AfterCompletion(Agent agent, InstructResult result); Task AfterCompletion(Agent agent, InstructResult result) => Task.CompletedTask;
Task OnResponseGenerated(InstructResponseModel response); Task OnResponseGenerated(InstructResponseModel response) => Task.CompletedTask;
Task BeforeCodeExecution(Agent agent, RoleDialogModel message, CodeInstructContext context) => Task.CompletedTask;
Task AfterCodeExecution(Agent agent, InstructResult result) => Task.CompletedTask;
} }

View file

@ -0,0 +1,6 @@
namespace BotSharp.Abstraction.Instructs.Models;
public class CodeInstructContext
{
public List<KeyValue> Arguments { get; set; } = [];
}

View file

@ -0,0 +1,12 @@
namespace BotSharp.Abstraction.Repositories.Filters;
public class AgentCodeScriptFilter
{
public List<string>? ScriptNames { get; set; }
public List<string>? ScriptTypes { get; set; }
public static AgentCodeScriptFilter Empty()
{
return new AgentCodeScriptFilter();
}
}

View file

@ -107,15 +107,15 @@ public interface IBotSharpRepository : IHaveServiceProvider
#endregion #endregion
#region Agent Code #region Agent Code
List<AgentCodeScript> GetAgentCodeScripts(string agentId, List<string>? scriptNames = null) List<AgentCodeScript> GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null)
=> throw new NotImplementedException(); => throw new NotImplementedException();
string? GetAgentCodeScript(string agentId, string scriptName) string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src)
=> throw new NotImplementedException(); => throw new NotImplementedException();
bool UpdateAgentCodeScripts(string agentId, List<AgentCodeScript> scripts) bool UpdateAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
=> throw new NotImplementedException(); => throw new NotImplementedException();
bool BulkInsertAgentCodeScripts(string agentId, List<AgentCodeScript> scripts) bool BulkInsertAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
=> throw new NotImplementedException(); => throw new NotImplementedException();
bool DeleteAgentCodeScripts(string agentId, List<string>? scriptNames) bool DeleteAgentCodeScripts(string agentId, List<AgentCodeScript>? scripts = null)
=> throw new NotImplementedException(); => throw new NotImplementedException();
#endregion #endregion

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Routing.Enums; namespace BotSharp.Abstraction.Routing.Enums;
public class RoutingMode public static class RoutingMode
{ {
public const string Eager = "eager"; public const string Eager = "eager";
public const string Lazy = "lazy"; public const string Lazy = "lazy";

View file

@ -1,6 +1,6 @@
namespace BotSharp.Abstraction.Routing.Enums; namespace BotSharp.Abstraction.Routing.Enums;
public class RuleType public static class RuleType
{ {
/// <summary> /// <summary>
/// Fallback to redirect agent /// Fallback to redirect agent

View file

@ -1,7 +1,7 @@
/// <summary> /// <summary>
/// Agent task status /// Agent task status
/// </summary> /// </summary>
public class TaskStatus public static class TaskStatus
{ {
public const string Scheduled = "scheduled"; public const string Scheduled = "scheduled";
public const string New = "new"; public const string New = "new";

View file

@ -229,12 +229,22 @@ public partial class AgentService
} }
var agentId = fileDir.Split(Path.DirectorySeparatorChar).Last(); var agentId = fileDir.Split(Path.DirectorySeparatorChar).Last();
scripts = Directory.GetFiles(codeDir).Select(file => new AgentCodeScript
foreach (var folder in Directory.EnumerateDirectories(codeDir))
{ {
AgentId = agentId, var scriptType = folder.Split(Path.DirectorySeparatorChar).Last();
Name = Path.GetFileName(file), foreach (var file in Directory.EnumerateFiles(folder))
Content = File.ReadAllText(file) {
}).ToList(); scripts.Add(new AgentCodeScript
{
AgentId = agentId,
Name = Path.GetFileName(file),
ScriptType = scriptType,
Content = File.ReadAllText(file)
});
}
}
return scripts; return scripts;
} }
} }

View file

@ -40,9 +40,12 @@ public partial class InstructService
} }
var provider = string.Empty; // Run code template
var model = string.Empty; var codeResponse = await GetCodeResponse(agent, message, templateName, codeOptions);
var prompt = string.Empty; if (codeResponse != null)
{
return codeResponse;
}
// Before completion hooks // Before completion hooks
@ -63,48 +66,43 @@ public partial class InstructService
} }
// Run code template var provider = string.Empty;
var (text, isCodeComplete) = await GetCodeResponse(agentId, templateName, codeOptions); var model = string.Empty;
if (isCodeComplete)
// Render prompt
var prompt = string.IsNullOrEmpty(templateName) ?
agentService.RenderInstruction(agent) :
agentService.RenderTemplate(agent, templateName);
var completer = CompletionProvider.GetCompletion(_services,
agentConfig: agent.LlmConfig);
if (completer is ITextCompletion textCompleter)
{ {
response.Text = text; instruction = null;
provider = textCompleter.Provider;
model = textCompleter.Model;
var result = await textCompleter.GetCompletion(prompt, agentId, message.MessageId);
response.Text = result;
} }
else else if (completer is IChatCompletion chatCompleter)
{ {
// Render prompt provider = chatCompleter.Provider;
prompt = string.IsNullOrEmpty(templateName) ? model = chatCompleter.Model;
agentService.RenderInstruction(agent) :
agentService.RenderTemplate(agent, templateName);
var completer = CompletionProvider.GetCompletion(_services, if (instruction == "#TEMPLATE#")
agentConfig: agent.LlmConfig);
if (completer is ITextCompletion textCompleter)
{ {
instruction = null; instruction = prompt;
provider = textCompleter.Provider; prompt = message.Content;
model = textCompleter.Model;
var result = await textCompleter.GetCompletion(prompt, agentId, message.MessageId);
response.Text = result;
} }
else if (completer is IChatCompletion chatCompleter)
var result = await chatCompleter.GetChatCompletions(new Agent
{ {
provider = chatCompleter.Provider; Id = agentId,
model = chatCompleter.Model; Name = agent.Name,
Instruction = instruction
if (instruction == "#TEMPLATE#") }, new List<RoleDialogModel>
{
instruction = prompt;
prompt = message.Content;
}
var result = await chatCompleter.GetChatCompletions(new Agent
{
Id = agentId,
Name = agent.Name,
Instruction = instruction
}, new List<RoleDialogModel>
{ {
new RoleDialogModel(AgentRole.User, prompt) new RoleDialogModel(AgentRole.User, prompt)
{ {
@ -113,46 +111,49 @@ public partial class InstructService
Files = files?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData, ContentType = x.ContentType }).ToList() ?? [] Files = files?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData, ContentType = x.ContentType }).ToList() ?? []
} }
}); });
response.Text = result.Content; response.Text = result.Content;
}
} }
// After completion hooks // After completion hooks
foreach (var hook in hooks) foreach (var hook in hooks)
{ {
await hook.AfterCompletion(agent, response); await hook.AfterCompletion(agent, response);
if (!isCodeComplete) await hook.OnResponseGenerated(new InstructResponseModel
{ {
await hook.OnResponseGenerated(new InstructResponseModel AgentId = agentId,
{ Provider = provider,
AgentId = agentId, Model = model,
Provider = provider, TemplateName = templateName,
Model = model, UserMessage = prompt,
TemplateName = templateName, SystemInstruction = instruction,
UserMessage = prompt, CompletionText = response.Text
SystemInstruction = instruction, });
CompletionText = response.Text
});
}
} }
return response; return response;
} }
/// <summary> /// <summary>
/// Get code response => return: (response text, whether code execution is completed) /// Get code response
/// </summary> /// </summary>
/// <param name="agentId"></param> /// <param name="agent"></param>
/// <param name="message"></param>
/// <param name="templateName"></param> /// <param name="templateName"></param>
/// <param name="codeOptions"></param> /// <param name="codeOptions"></param>
/// <returns></returns> /// <returns></returns>
private async Task<(string?, bool)> GetCodeResponse(string agentId, string templateName, CodeInstructOptions? codeOptions) private async Task<InstructResult?> GetCodeResponse(Agent agent, RoleDialogModel message, string templateName, CodeInstructOptions? codeOptions)
{ {
InstructResult? response = null;
if (agent == null)
{
return response;
}
var state = _services.GetRequiredService<IConversationStateService>(); var state = _services.GetRequiredService<IConversationStateService>();
var db = _services.GetRequiredService<IBotSharpRepository>(); var db = _services.GetRequiredService<IBotSharpRepository>();
var hooks = _services.GetHooks<IInstructHook>(agent.Id);
var isComplete = false;
var response = string.Empty;
var codeProvider = codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs("botsharp-py-interpreter"); var codeProvider = codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs("botsharp-py-interpreter");
var codeInterpreter = _services.GetServices<ICodeInterpretService>() var codeInterpreter = _services.GetServices<ICodeInterpretService>()
@ -161,9 +162,9 @@ public partial class InstructService
if (codeInterpreter == null) if (codeInterpreter == null)
{ {
#if DEBUG #if DEBUG
_logger.LogWarning($"No code interpreter found. (Agent: {agentId}, Code interpreter: {codeProvider})"); _logger.LogWarning($"No code interpreter found. (Agent: {agent.Id}, Code interpreter: {codeProvider})");
#endif #endif
return (response, isComplete); return response;
} }
// Get code script name // Get code script name
@ -180,19 +181,19 @@ public partial class InstructService
if (string.IsNullOrEmpty(scriptName)) if (string.IsNullOrEmpty(scriptName))
{ {
#if DEBUG #if DEBUG
_logger.LogWarning($"Empty code script name. (Agent: {agentId}, {scriptName})"); _logger.LogWarning($"Empty code script name. (Agent: {agent.Id}, {scriptName})");
#endif #endif
return (response, isComplete); return response;
} }
// Get code script // Get code script
var codeScript = db.GetAgentCodeScript(agentId, scriptName); var codeScript = db.GetAgentCodeScript(agent.Id, scriptName, scriptType: AgentCodeScriptType.Src);
if (string.IsNullOrWhiteSpace(codeScript)) if (string.IsNullOrWhiteSpace(codeScript))
{ {
#if DEBUG #if DEBUG
_logger.LogWarning($"Empty code script. (Agent: {agentId}, {scriptName})"); _logger.LogWarning($"Empty code script. (Agent: {agent.Id}, {scriptName})");
#endif #endif
return (response, isComplete); return response;
} }
// Get code arguments // Get code arguments
@ -202,14 +203,55 @@ public partial class InstructService
arguments = state.GetStates().Select(x => new KeyValue(x.Key, x.Value)).ToList(); arguments = state.GetStates().Select(x => new KeyValue(x.Key, x.Value)).ToList();
} }
var context = new CodeInstructContext
{
Arguments = arguments
};
// Before code execution
foreach (var hook in hooks)
{
await hook.BeforeCodeExecution(agent, message, context);
// Interrupted by hook
if (message.StopCompletion)
{
return new InstructResult
{
MessageId = message.MessageId,
Text = message.Content
};
}
}
// Run code script // Run code script
var result = await codeInterpreter.RunCode(codeScript, options: new() var result = await codeInterpreter.RunCode(codeScript, options: new()
{ {
Arguments = arguments Arguments = context.Arguments
}); });
response = result?.Result?.ToString(); response = new InstructResult
isComplete = true; {
return (response, isComplete); MessageId = message.MessageId,
Text = result?.Result?.ToString()
};
// After code execution
foreach (var hook in hooks)
{
await hook.AfterCodeExecution(agent, response);
await hook.OnResponseGenerated(new InstructResponseModel
{
AgentId = agent.Id,
Provider = codeInterpreter.Provider,
Model = string.Empty,
TemplateName = scriptName,
UserMessage = string.Empty,
SystemInstruction = string.Empty,
CompletionText = response.Text
});
}
return response;
} }
} }

View file

@ -1,161 +0,0 @@
using System.IO;
namespace BotSharp.Core.Repository;
public partial class FileRepository
{
#region Code
public List<AgentCodeScript> GetAgentCodeScripts(string agentId, List<string>? scriptNames = null)
{
if (string.IsNullOrWhiteSpace(agentId))
{
return [];
}
var dir = BuildAgentCodeDir(agentId);
if (!Directory.Exists(dir))
{
return [];
}
var results = new List<AgentCodeScript>();
foreach (var file in Directory.GetFiles(dir))
{
var fileName = Path.GetFileName(file);
if (scriptNames != null && !scriptNames.Contains(fileName))
{
continue;
}
var script = new AgentCodeScript
{
AgentId = agentId,
Name = fileName,
Content = File.ReadAllText(file)
};
results.Add(script);
}
return results;
}
public string? GetAgentCodeScript(string agentId, string scriptName)
{
if (string.IsNullOrWhiteSpace(agentId)
|| string.IsNullOrWhiteSpace(scriptName))
{
return null;
}
var dir = BuildAgentCodeDir(agentId);
if (!Directory.Exists(dir))
{
return null;
}
foreach (var file in Directory.GetFiles(dir))
{
var fileName = Path.GetFileName(file);
if (scriptName.IsEqualTo(fileName))
{
return File.ReadAllText(file);
}
}
return string.Empty;
}
public bool UpdateAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
{
if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty())
{
return false;
}
var dir = BuildAgentCodeDir(agentId);
if (!Directory.Exists(dir))
{
return false;
}
var dict = scripts.DistinctBy(x => x.Name).ToDictionary(x => x.Name, x => x);
var files = Directory.GetFiles(dir).Where(x => dict.Keys.Contains(Path.GetFileName(x))).ToList();
foreach (var file in files)
{
if (dict.TryGetValue(Path.GetFileName(file), out var script))
{
File.WriteAllText(file, script.Content);
}
}
return true;
}
public bool BulkInsertAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
{
if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty())
{
return false;
}
var dir = BuildAgentCodeDir(agentId);
if (!Directory.Exists(dir))
{
return false;
}
foreach (var script in scripts)
{
if (string.IsNullOrWhiteSpace(script.Name))
{
continue;
}
var path = Path.Combine(dir, script.Name);
File.WriteAllText(path, script.Content);
}
return true;
}
public bool DeleteAgentCodeScripts(string agentId, List<string>? scriptNames)
{
if (string.IsNullOrWhiteSpace(agentId))
{
return false;
}
var dir = BuildAgentCodeDir(agentId);
if (!Directory.Exists(dir))
{
return false;
}
if (scriptNames == null)
{
Directory.Delete(dir, true);
return true;
}
else if (!scriptNames.Any())
{
return false;
}
foreach (var file in Directory.GetFiles(dir))
{
var fileName = Path.GetFileName(file);
if (scriptNames.Contains(fileName))
{
File.Delete(file);
}
}
return true;
}
#endregion
#region Private methods
private string BuildAgentCodeDir(string agentId)
{
return Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER);
}
#endregion
}

View file

@ -0,0 +1,157 @@
using System.IO;
namespace BotSharp.Core.Repository;
public partial class FileRepository
{
#region Code
public List<AgentCodeScript> GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null)
{
if (string.IsNullOrWhiteSpace(agentId))
{
return [];
}
var dir = BuildAgentCodeScriptDir(agentId);
if (!Directory.Exists(dir))
{
return [];
}
filter ??= AgentCodeScriptFilter.Empty();
var results = new List<AgentCodeScript>();
foreach (var folder in Directory.EnumerateDirectories(dir))
{
var scriptType = folder.Split(Path.DirectorySeparatorChar).Last();
if (filter.ScriptTypes != null && !filter.ScriptTypes.Contains(scriptType))
{
continue;
}
foreach (var file in Directory.EnumerateFiles(folder))
{
var fileName = Path.GetFileName(file);
if (filter.ScriptNames != null && !filter.ScriptNames.Contains(fileName))
{
continue;
}
results.Add(new AgentCodeScript
{
AgentId = agentId,
Name = fileName,
ScriptType = scriptType,
Content = File.ReadAllText(file)
});
}
}
return results;
}
public string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src)
{
if (string.IsNullOrWhiteSpace(agentId)
|| string.IsNullOrWhiteSpace(scriptName)
|| string.IsNullOrWhiteSpace(scriptType))
{
return null;
}
var dir = BuildAgentCodeScriptDir(agentId, scriptType);
if (!Directory.Exists(dir))
{
return null;
}
var foundFile = Directory.GetFiles(dir).FirstOrDefault(file => scriptName.IsEqualTo(Path.GetFileName(file)));
if (!string.IsNullOrEmpty(foundFile))
{
return File.ReadAllText(foundFile);
}
return string.Empty;
}
public bool UpdateAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
{
if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty())
{
return false;
}
foreach (var script in scripts)
{
if (string.IsNullOrWhiteSpace(script.Name)
|| string.IsNullOrWhiteSpace(script.ScriptType))
{
continue;
}
var dir = BuildAgentCodeScriptDir(agentId, script.ScriptType);
if (!Directory.Exists(dir))
{
continue;
}
var file = Path.Combine(dir, script.Name);
File.WriteAllText(file, script.Content);
}
return true;
}
public bool BulkInsertAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
{
return UpdateAgentCodeScripts(agentId, scripts);
}
public bool DeleteAgentCodeScripts(string agentId, List<AgentCodeScript>? scripts = null)
{
if (string.IsNullOrWhiteSpace(agentId))
{
return false;
}
var dir = BuildAgentCodeScriptDir(agentId);
if (!Directory.Exists(dir))
{
return false;
}
if (scripts == null)
{
Directory.Delete(dir, true);
return true;
}
else if (!scripts.Any())
{
return false;
}
var dict = scripts.DistinctBy(x => x.CodePath).ToDictionary(x => x.CodePath, x => x);
foreach (var pair in dict)
{
var file = Path.Combine(dir, pair.Value.ScriptType, pair.Value.Name);
if (File.Exists(file))
{
File.Delete(file);
}
}
return true;
}
#endregion
#region Private methods
private string BuildAgentCodeScriptDir(string agentId, string? scirptType = null)
{
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER);
if (!string.IsNullOrWhiteSpace(scirptType))
{
dir = Path.Combine(dir, scirptType);
}
return dir;
}
#endregion
}

View file

@ -2,6 +2,7 @@ using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Abstraction.Instructs.Settings; using BotSharp.Abstraction.Instructs.Settings;
using BotSharp.Abstraction.Loggers.Models; using BotSharp.Abstraction.Loggers.Models;
using BotSharp.Abstraction.Users; using BotSharp.Abstraction.Users;
using BotSharp.Abstraction.Utilities;
namespace BotSharp.Logger.Hooks; namespace BotSharp.Logger.Hooks;
@ -39,7 +40,9 @@ public class InstructionLogHook : InstructHookBase
var state = _services.GetRequiredService<IConversationStateService>(); var state = _services.GetRequiredService<IConversationStateService>();
var user = db.GetUserById(_user.Id); var user = db.GetUserById(_user.Id);
var templateName = response.TemplateName ?? state.GetState("instruct_template_name") ?? null; var templateName = response.TemplateName
.IfNullOrEmptyAs(state.GetState("instruct_template_name"))
.IfNullOrEmptyAs(null);
db.SaveInstructionLogs(new List<InstructionLogModel> db.SaveInstructionLogs(new List<InstructionLogModel>
{ {

View file

@ -2,31 +2,34 @@ using BotSharp.Abstraction.Agents.Models;
namespace BotSharp.Plugin.MongoStorage.Collections; namespace BotSharp.Plugin.MongoStorage.Collections;
public class AgentCodeDocument : MongoBase public class AgentCodeScriptDocument : MongoBase
{ {
public string AgentId { get; set; } = default!; public string AgentId { get; set; } = default!;
public string Name { get; set; } = default!; public string Name { get; set; } = default!;
public string Content { get; set; } = default!; public string Content { get; set; } = default!;
public string ScriptType { get; set; } = default!;
public static AgentCodeDocument ToMongoModel(AgentCodeScript script) public static AgentCodeScriptDocument ToMongoModel(AgentCodeScript script)
{ {
return new AgentCodeDocument return new AgentCodeScriptDocument
{ {
Id = script.Id, Id = script.Id,
AgentId = script.AgentId, AgentId = script.AgentId,
Name = script.Name, Name = script.Name,
Content = script.Content Content = script.Content,
ScriptType = script.ScriptType
}; };
} }
public static AgentCodeScript ToDomainModel(AgentCodeDocument script) public static AgentCodeScript ToDomainModel(AgentCodeScriptDocument script)
{ {
return new AgentCodeScript return new AgentCodeScript
{ {
Id = script.Id, Id = script.Id,
AgentId = script.AgentId, AgentId = script.AgentId,
Name = script.Name, Name = script.Name,
Content = script.Content Content = script.Content,
ScriptType = script.ScriptType
}; };
} }
} }

View file

@ -160,8 +160,8 @@ public class MongoDbContext
public IMongoCollection<AgentTaskDocument> AgentTasks public IMongoCollection<AgentTaskDocument> AgentTasks
=> CreateAgentTaskIndex(); => CreateAgentTaskIndex();
public IMongoCollection<AgentCodeDocument> AgentCodes public IMongoCollection<AgentCodeScriptDocument> AgentCodeScripts
=> GetCollectionOrCreate<AgentCodeDocument>("AgentCodes"); => GetCollectionOrCreate<AgentCodeScriptDocument>("AgentCodeScripts");
public IMongoCollection<ConversationDocument> Conversations public IMongoCollection<ConversationDocument> Conversations
=> CreateConversationIndex(); => CreateConversationIndex();

View file

@ -594,7 +594,7 @@ public partial class MongoRepository
_dc.UserAgents.DeleteMany(Builders<UserAgentDocument>.Filter.Empty); _dc.UserAgents.DeleteMany(Builders<UserAgentDocument>.Filter.Empty);
_dc.RoleAgents.DeleteMany(Builders<RoleAgentDocument>.Filter.Empty); _dc.RoleAgents.DeleteMany(Builders<RoleAgentDocument>.Filter.Empty);
_dc.AgentTasks.DeleteMany(Builders<AgentTaskDocument>.Filter.Empty); _dc.AgentTasks.DeleteMany(Builders<AgentTaskDocument>.Filter.Empty);
_dc.AgentCodes.DeleteMany(Builders<AgentCodeDocument>.Filter.Empty); _dc.AgentCodeScripts.DeleteMany(Builders<AgentCodeScriptDocument>.Filter.Empty);
_dc.Agents.DeleteMany(Builders<AgentDocument>.Filter.Empty); _dc.Agents.DeleteMany(Builders<AgentDocument>.Filter.Empty);
return true; return true;
} }
@ -614,12 +614,12 @@ public partial class MongoRepository
var userAgentFilter = Builders<UserAgentDocument>.Filter.Eq(x => x.AgentId, agentId); var userAgentFilter = Builders<UserAgentDocument>.Filter.Eq(x => x.AgentId, agentId);
var roleAgentFilter = Builders<RoleAgentDocument>.Filter.Eq(x => x.AgentId, agentId); var roleAgentFilter = Builders<RoleAgentDocument>.Filter.Eq(x => x.AgentId, agentId);
var agentTaskFilter = Builders<AgentTaskDocument>.Filter.Eq(x => x.AgentId, agentId); var agentTaskFilter = Builders<AgentTaskDocument>.Filter.Eq(x => x.AgentId, agentId);
var agentCodeFilter = Builders<AgentCodeDocument>.Filter.Eq(x => x.AgentId, agentId); var agentCodeFilter = Builders<AgentCodeScriptDocument>.Filter.Eq(x => x.AgentId, agentId);
_dc.UserAgents.DeleteMany(userAgentFilter); _dc.UserAgents.DeleteMany(userAgentFilter);
_dc.RoleAgents.DeleteMany(roleAgentFilter); _dc.RoleAgents.DeleteMany(roleAgentFilter);
_dc.AgentTasks.DeleteMany(agentTaskFilter); _dc.AgentTasks.DeleteMany(agentTaskFilter);
_dc.AgentCodes.DeleteMany(agentCodeFilter); _dc.AgentCodeScripts.DeleteMany(agentCodeFilter);
_dc.Agents.DeleteOne(agentFilter); _dc.Agents.DeleteOne(agentFilter);
return true; return true;
} }

View file

@ -1,113 +0,0 @@
using BotSharp.Abstraction.Agents.Models;
namespace BotSharp.Plugin.MongoStorage.Repository;
public partial class MongoRepository
{
#region Code
public List<AgentCodeScript> GetAgentCodeScripts(string agentId, List<string>? scriptNames = null)
{
if (string.IsNullOrWhiteSpace(agentId))
{
return [];
}
var builder = Builders<AgentCodeDocument>.Filter;
var filters = new List<FilterDefinition<AgentCodeDocument>>()
{
builder.Eq(x => x.AgentId, agentId)
};
if (!scriptNames.IsNullOrEmpty())
{
filters.Add(builder.In(x => x.Name, scriptNames));
}
var found = _dc.AgentCodes.Find(builder.And(filters)).ToList();
return found.Select(x => AgentCodeDocument.ToDomainModel(x)).ToList();
}
public string? GetAgentCodeScript(string agentId, string scriptName)
{
if (string.IsNullOrWhiteSpace(agentId)
|| string.IsNullOrWhiteSpace(scriptName))
{
return null;
}
var builder = Builders<AgentCodeDocument>.Filter;
var filters = new List<FilterDefinition<AgentCodeDocument>>()
{
builder.Eq(x => x.AgentId, agentId),
builder.Eq(x => x.Name, scriptName)
};
var found = _dc.AgentCodes.Find(builder.And(filters)).FirstOrDefault();
return found?.Content;
}
public bool UpdateAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
{
if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty())
{
return false;
}
var builder = Builders<AgentCodeDocument>.Filter;
var ops = scripts.Where(x => !string.IsNullOrWhiteSpace(x.Name))
.Select(x => new UpdateOneModel<AgentCodeDocument>(
builder.And(new List<FilterDefinition<AgentCodeDocument>>
{
builder.Eq(y => y.AgentId, agentId),
builder.Eq(y => y.Name, x.Name)
}),
Builders<AgentCodeDocument>.Update.Set(y => y.Content, x.Content)
))
.ToList();
var result = _dc.AgentCodes.BulkWrite(ops, new BulkWriteOptions { IsOrdered = false });
return result.ModifiedCount > 0 || result.MatchedCount > 0;
}
public bool BulkInsertAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
{
if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty())
{
return false;
}
var docs = scripts.Select(x =>
{
var script = AgentCodeDocument.ToMongoModel(x);
script.AgentId = agentId;
script.Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString();
return script;
}).ToList();
_dc.AgentCodes.InsertMany(docs);
return true;
}
public bool DeleteAgentCodeScripts(string agentId, List<string>? scriptNames)
{
if (string.IsNullOrWhiteSpace(agentId))
{
return false;
}
var filterDef = Builders<AgentCodeDocument>.Filter.Empty;
if (scriptNames != null)
{
var builder = Builders<AgentCodeDocument>.Filter;
var filters = new List<FilterDefinition<AgentCodeDocument>>
{
builder.In(x => x.Name, scriptNames)
};
filterDef = builder.And(filters);
}
var deleted = _dc.AgentCodes.DeleteMany(filterDef);
return deleted.DeletedCount > 0;
}
#endregion
}

View file

@ -0,0 +1,129 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Repositories.Filters;
namespace BotSharp.Plugin.MongoStorage.Repository;
public partial class MongoRepository
{
#region Code
public List<AgentCodeScript> GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null)
{
if (string.IsNullOrWhiteSpace(agentId))
{
return [];
}
filter ??= AgentCodeScriptFilter.Empty();
var builder = Builders<AgentCodeScriptDocument>.Filter;
var filters = new List<FilterDefinition<AgentCodeScriptDocument>>()
{
builder.Eq(x => x.AgentId, agentId)
};
if (!filter.ScriptNames.IsNullOrEmpty())
{
filters.Add(builder.In(x => x.Name, filter.ScriptNames));
}
if (!filter.ScriptTypes.IsNullOrEmpty())
{
filters.Add(builder.In(x => x.ScriptType, filter.ScriptTypes));
}
var found = _dc.AgentCodeScripts.Find(builder.And(filters)).ToList();
return found.Select(x => AgentCodeScriptDocument.ToDomainModel(x)).ToList();
}
public string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src)
{
if (string.IsNullOrWhiteSpace(agentId)
|| string.IsNullOrWhiteSpace(scriptName)
|| string.IsNullOrWhiteSpace(scriptType))
{
return null;
}
var builder = Builders<AgentCodeScriptDocument>.Filter;
var filters = new List<FilterDefinition<AgentCodeScriptDocument>>()
{
builder.Eq(x => x.AgentId, agentId),
builder.Eq(x => x.Name, scriptName),
builder.Eq(x => x.ScriptType, scriptType)
};
var found = _dc.AgentCodeScripts.Find(builder.And(filters)).FirstOrDefault();
return found?.Content;
}
public bool UpdateAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
{
if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty())
{
return false;
}
var builder = Builders<AgentCodeScriptDocument>.Filter;
var ops = scripts.Where(x => !string.IsNullOrWhiteSpace(x.Name))
.Select(x => new UpdateOneModel<AgentCodeScriptDocument>(
builder.And(new List<FilterDefinition<AgentCodeScriptDocument>>
{
builder.Eq(y => y.AgentId, agentId),
builder.Eq(y => y.Name, x.Name),
builder.Eq(y => y.ScriptType, x.ScriptType)
}),
Builders<AgentCodeScriptDocument>.Update.Set(y => y.Content, x.Content)
))
.ToList();
var result = _dc.AgentCodeScripts.BulkWrite(ops, new BulkWriteOptions { IsOrdered = false });
return result.ModifiedCount > 0 || result.MatchedCount > 0;
}
public bool BulkInsertAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
{
if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty())
{
return false;
}
var docs = scripts.Select(x =>
{
var script = AgentCodeScriptDocument.ToMongoModel(x);
script.AgentId = agentId;
script.Id = x.Id.IfNullOrEmptyAs(Guid.NewGuid().ToString());
return script;
}).ToList();
_dc.AgentCodeScripts.InsertMany(docs);
return true;
}
public bool DeleteAgentCodeScripts(string agentId, List<AgentCodeScript>? scripts = null)
{
if (string.IsNullOrWhiteSpace(agentId))
{
return false;
}
DeleteResult deleted;
if (scripts != null)
{
var scriptPaths = scripts.Select(x => x.CodePath);
var exprFilter = new BsonDocument("$expr", new BsonDocument("$in", new BsonArray
{
new BsonDocument("$concat", new BsonArray { "$ScriptType", "/", "$Name" }),
new BsonArray(scriptPaths)
}));
var filterDef = new BsonDocumentFilterDefinition<AgentCodeScriptDocument>(exprFilter);
deleted = _dc.AgentCodeScripts.DeleteMany(filterDef);
}
else
{
deleted = _dc.AgentCodeScripts.DeleteMany(Builders<AgentCodeScriptDocument>.Filter.Empty);
}
return deleted.DeletedCount > 0;
}
#endregion
}