diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index b45ff557a..6075b4543 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -72,8 +72,8 @@ public interface IAgentService Task> GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null) => Task.FromResult(new List()); - Task GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) - => Task.FromResult(string.Empty); + Task GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) + => Task.FromResult((AgentCodeScript?)null); Task UpdateAgentCodeScripts(string agentId, List codeScripts, AgentCodeScriptUpdateOptions? options = null) => Task.FromResult(false); diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs index 840d67210..909f1d002 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs @@ -5,6 +5,9 @@ public class AgentCodeScript : AgentCodeScriptBase public string Id { get; set; } public string AgentId { get; set; } = null!; + public DateTime CreatedTime { get; set; } = DateTime.UtcNow; + public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; + public AgentCodeScript() : base() { } diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs index 887c437ec..69e4752b2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs @@ -10,6 +10,10 @@ public class AgentSettings /// /// This is the default LLM config for agent /// - public AgentLlmConfig LlmConfig { get; set; } - = new AgentLlmConfig(); + public AgentLlmConfig LlmConfig { get; set; } = new AgentLlmConfig(); + + /// + /// General coding settings + /// + public CodingSettings Coding { get; set; } = new CodingSettings(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Coding/Enums/BuiltInCodeProcessor.cs b/src/Infrastructure/BotSharp.Abstraction/Coding/Enums/BuiltInCodeProcessor.cs new file mode 100644 index 000000000..cdbf76a99 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Coding/Enums/BuiltInCodeProcessor.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.Coding.Enums; + +public static class BuiltInCodeProcessor +{ + public const string PyInterpreter = "botsharp-py-interpreter"; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Coding/Options/CodeGenerationOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Coding/Options/CodeGenerationOptions.cs index be6437041..de886f5ef 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Coding/Options/CodeGenerationOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Coding/Options/CodeGenerationOptions.cs @@ -17,11 +17,10 @@ public class CodeGenerationOptions : LlmConfigBase public string? TemplateName { get; set; } /// - /// The programming language + /// Programming language /// - [JsonPropertyName("language")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Language { get; set; } = "python"; + [JsonPropertyName("programming_language")] + public string? ProgrammingLanguage { get; set; } /// /// Data that can be used to fill in the prompt diff --git a/src/Infrastructure/BotSharp.Abstraction/Coding/Settings/CodingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Coding/Settings/CodingSettings.cs new file mode 100644 index 000000000..5f194bf32 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Coding/Settings/CodingSettings.cs @@ -0,0 +1,14 @@ +namespace BotSharp.Abstraction.Coding.Settings; + +public class CodingSettings +{ + /// + /// Llm provider to generate code script + /// + public string? Provider { get; set; } + + /// + /// Llm model to generate code script + /// + public string? Model { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Contexts/CodeInstructContext.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Contexts/CodeInstructContext.cs index 77e37efaa..63b1c2875 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/Contexts/CodeInstructContext.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Contexts/CodeInstructContext.cs @@ -2,7 +2,8 @@ namespace BotSharp.Abstraction.Instructs.Contexts; public class CodeInstructContext { - public string CodeScript { get; set; } + public string ScriptName { get; set; } + public string ScriptContent { get; set; } public string ScriptType { get; set; } public List Arguments { get; set; } = []; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Options/InstructOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Options/InstructOptions.cs index cc5a76cd5..b2ff66fba 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/Options/InstructOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Options/InstructOptions.cs @@ -35,5 +35,5 @@ public class InstructOptions /// /// Image convert provider /// - public string? ImageConvertProvider { get; set; } + public string? ImageConverter { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 52f43fb6b..374271b9f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -114,7 +114,7 @@ bool DeleteAgentTasks(string agentId, List? taskIds = null) #region Agent Code List GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null) => throw new NotImplementedException(); - string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) + AgentCodeScript? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) => throw new NotImplementedException(); bool UpdateAgentCodeScripts(string agentId, List scripts, AgentCodeScriptDbUpdateOptions? options = null) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleEngine.cs b/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleEngine.cs index c870f145f..adcf87aaf 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleEngine.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleEngine.cs @@ -2,5 +2,15 @@ namespace BotSharp.Abstraction.Rules; public interface IRuleEngine { - Task> Triggered(IRuleTrigger trigger, string data, List? states = null); + /// + /// Trigger the rule that is subscribed by agents. + /// + /// + /// + /// + /// + /// + /// + Task> Trigger(IRuleTrigger trigger, string text, IEnumerable? states = null, RuleTriggerOptions? options = null) + => throw new NotImplementedException(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleTrigger.cs b/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleTrigger.cs index 7b6c9bc25..c7ad59d9a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleTrigger.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleTrigger.cs @@ -1,3 +1,5 @@ +using System.Text.Json; + namespace BotSharp.Abstraction.Rules; public interface IRuleTrigger @@ -9,4 +11,14 @@ public interface IRuleTrigger string EntityType { get; set; } string EntityId { get; set; } + + /// + /// The default arguments as input to code trigger (display purpose) + /// + JsonDocument OutputArgs => JsonDocument.Parse("{}"); + + /// + /// Explain the purpose of rule trigger (display purpose) + /// + string Statement => string.Empty; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs new file mode 100644 index 000000000..068052b0b --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs @@ -0,0 +1,26 @@ +using System.Text.Json; + +namespace BotSharp.Abstraction.Rules.Options; + +public class RuleTriggerOptions +{ + /// + /// Code processor provider + /// + public string? CodeProcessor { get; set; } + + /// + /// Code script name + /// + public string? CodeScriptName { get; set; } + + /// + /// Argument name as an input key to the code script + /// + public string? ArgumentName { get; set; } + + /// + /// Json arguments as an input value to the code script + /// + public JsonDocument? ArgumentContent { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Using.cs b/src/Infrastructure/BotSharp.Abstraction/Using.cs index d20775375..ca0cbe7f7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Using.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Using.cs @@ -21,4 +21,6 @@ global using BotSharp.Abstraction.Knowledges.Models; global using BotSharp.Abstraction.Crontab.Models; global using BotSharp.Abstraction.MCP.Models; -global using BotSharp.Abstraction.Settings; \ No newline at end of file +global using BotSharp.Abstraction.Settings; +global using BotSharp.Abstraction.Rules.Options; +global using BotSharp.Abstraction.Coding.Settings; \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs b/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs index 1b3006663..deec9a4ad 100644 --- a/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs +++ b/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs @@ -1,25 +1,33 @@ +using BotSharp.Abstraction.Coding; +using BotSharp.Abstraction.Coding.Enums; using BotSharp.Abstraction.Conversations; using BotSharp.Abstraction.Models; using BotSharp.Abstraction.Repositories.Filters; +using BotSharp.Abstraction.Rules.Options; using BotSharp.Abstraction.Utilities; using Microsoft.Extensions.Logging; using System.Data; +using System.Text.Json; namespace BotSharp.Core.Rules.Engines; public class RuleEngine : IRuleEngine { private readonly IServiceProvider _services; - private readonly ILogger _logger; + private readonly ILogger _logger; - public RuleEngine(IServiceProvider services, ILogger logger) + public RuleEngine( + IServiceProvider services, + ILogger logger) { _services = services; _logger = logger; } - public async Task> Triggered(IRuleTrigger trigger, string data, List? states = null) + public async Task> Trigger(IRuleTrigger trigger, string text, IEnumerable? states = null, RuleTriggerOptions? options = null) { + var newConversationIds = new List(); + // Pull all user defined rules var agentService = _services.GetRequiredService(); var agents = await agentService.GetAgents(new AgentFilter @@ -30,25 +38,32 @@ public async Task> Triggered(IRuleTrigger trigger, string da } }); - var preFilteredAgents = agents.Items.Where(x => - x.Rules.Exists(r => r.TriggerName == trigger.Name && - !x.Disabled)).ToList(); + // Trigger agents + var filteredAgents = agents.Items.Where(x => x.Rules.Exists(r => r.TriggerName.IsEqualTo(trigger.Name) && !x.Disabled)).ToList(); + foreach (var agent in filteredAgents) + { + var isTriggered = true; - // Trigger the agents - var instructService = _services.GetRequiredService(); - var newConversationIds = new List(); + // Code trigger + if (options != null) + { + isTriggered = await TriggerCodeScript(agent.Id, trigger.Name, options); + } + + if (!isTriggered) + { + continue; + } - foreach (var agent in preFilteredAgents) - { var convService = _services.GetRequiredService(); var conv = await convService.NewConversation(new Conversation { Channel = trigger.Channel, - Title = data, + Title = text, AgentId = agent.Id }); - var message = new RoleDialogModel(AgentRole.User, data); + var message = new RoleDialogModel(AgentRole.User, text); var allStates = new List { @@ -69,27 +84,84 @@ await convService.SendMessage(agent.Id, convService.SaveStates(); newConversationIds.Add(conv.Id); + } + + return newConversationIds; + } + + #region Private methods + private async Task TriggerCodeScript(string agentId, string triggerName, RuleTriggerOptions options) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return false; + } + + var provider = options.CodeProcessor ?? BuiltInCodeProcessor.PyInterpreter; + var processor = _services.GetServices().FirstOrDefault(x => x.Provider.IsEqualTo(provider)); + if (processor == null) + { + _logger.LogWarning($"Unable to find code processor: {provider}."); + return false; + } + + var agentService = _services.GetRequiredService(); + var scriptName = options.CodeScriptName ?? $"{triggerName}_rule.py"; + var codeScript = await agentService.GetAgentCodeScript(agentId, scriptName, scriptType: AgentCodeScriptType.Src); - /*foreach (var rule in agent.Rules) + var msg = $"rule trigger ({triggerName}) code script ({scriptName}) in agent ({agentId}) => args: {options.ArgumentContent?.RootElement.GetRawText()}."; + + if (string.IsNullOrWhiteSpace(codeScript?.Content)) + { + _logger.LogWarning($"Unable to find {msg}."); + return false; + } + + try + { + var response = await processor.RunAsync(codeScript.Content, options: new() + { + ScriptName = scriptName, + Arguments = BuildArguments(options.ArgumentName, options.ArgumentContent) + }); + + if (response == null || !response.Success) + { + _logger.LogWarning($"Failed to handle {msg}"); + return false; + } + + bool result; + LogLevel logLevel; + if (response.Result.IsEqualTo("true")) { - var userSay = $"===Input data with Before and After values===\r\n{data}\r\n\r\n===Trigger Criteria===\r\n{rule.Criteria}\r\n\r\nJust output 1 or 0 without explanation: "; - - var result = await instructService.Execute(BuiltInAgentId.RulesInterpreter, new RoleDialogModel(AgentRole.User, userSay), "criteria_check", "#TEMPLATE#"); - - // Check if meet the criteria - if (result.Text == "1") - { - // Hit rule - _logger.LogInformation($"Hit rule {rule.TriggerName} {rule.EntityType} {rule.EventName}, {data}"); - - await convService.SendMessage(agent.Id, - new RoleDialogModel(AgentRole.User, $"The conversation was triggered by {rule.Criteria}"), - null, - msg => Task.CompletedTask); - } - }*/ + logLevel = LogLevel.Information; + result = true; + } + else + { + logLevel = LogLevel.Warning; + result = false; + } + + _logger.Log(logLevel, $"Code script execution result ({response.Result}) from {msg}"); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error when handling {msg}"); + return false; } + } - return newConversationIds; + private IEnumerable BuildArguments(string? name, JsonDocument? args) + { + var keyValues = new List(); + if (args != null) + { + keyValues.Add(new KeyValue(name ?? "trigger_args", args.RootElement.GetRawText())); + } + return keyValues; } +#endregion } diff --git a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs index 449db38da..575385455 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs @@ -5,7 +5,6 @@ using BotSharp.Abstraction.Templating; using BotSharp.Abstraction.Users.Enums; using BotSharp.Core.Agents.Hooks; -using BotSharp.Core.Coding; using Microsoft.Extensions.Configuration; namespace BotSharp.Core.Agents; @@ -49,8 +48,6 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) render.RegisterType(typeof(AgentSettings)); return settingService.Bind("Agent"); }); - - services.AddSingleton(); } public bool AttachMenu(List menu) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Coding.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Coding.cs index aee641c8c..936bdaf40 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Coding.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Coding.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Agents.Options; using BotSharp.Abstraction.Coding; +using BotSharp.Abstraction.Coding.Enums; using BotSharp.Abstraction.Coding.Options; namespace BotSharp.Core.Agents.Services; @@ -13,7 +14,7 @@ public async Task> GetAgentCodeScripts(string agentId, Age return await Task.FromResult(scripts); } - public async Task GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) + public async Task GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) { var db = _services.GetRequiredService(); var script = db.GetAgentCodeScript(agentId, scriptName, scriptType); @@ -76,7 +77,7 @@ public async Task GenerateCodeScript(string agentId, strin }; } - var processor = options?.Processor ?? "botsharp-py-interpreter"; + var processor = options?.Processor ?? BuiltInCodeProcessor.PyInterpreter; var codeProcessor = _services.GetServices().FirstOrDefault(x => x.Provider.IsEqualTo(processor)); if (codeProcessor == null) { diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 8d045f856..d0d517f4a 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -62,50 +62,59 @@ - - - - - - - - - - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + @@ -118,129 +127,135 @@ PreserveNewest - + + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest PreserveNewest - + + PreserveNewest - + PreserveNewest - + + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + + PreserveNewest - + PreserveNewest - + PreserveNewest - + + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - - - + PreserveNewest - + + PreserveNewest - + PreserveNewest - + + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/Coding/CodingPlugin.cs b/src/Infrastructure/BotSharp.Core/Coding/CodingPlugin.cs new file mode 100644 index 000000000..4f788f996 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Coding/CodingPlugin.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Configuration; + +namespace BotSharp.Core.Coding; + +public class CodingPlugin : IBotSharpPlugin +{ + public string Id => "31bc334b-9462-4191-beac-cb4a139b78c1"; + public string Name => "Coding"; + public string Description => "Handling execution and generation of code scripts"; + + public void RegisterDI(IServiceCollection services, IConfiguration config) + { + services.AddSingleton(); + } +} diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs index 62a1356cc..a1cc821e2 100644 --- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs +++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs @@ -9,9 +9,13 @@ public async Task SpeechToText(InstructFileModel audio, string? text = n if (string.IsNullOrWhiteSpace(text)) { var innerAgentId = options?.AgentId ?? Guid.Empty.ToString(); - text = await GetAgentTemplate(innerAgentId, options?.TemplateName); + text = await RenderAgentTemplate(innerAgentId, options?.TemplateName, options?.Data); } - + else + { + text = RenderText(text, options?.Data); + } + var completion = CompletionProvider.GetAudioTranscriber(_services, provider: options?.Provider, model: options?.Model); var audioBinary = await DownloadFile(audio); using var stream = audioBinary.ToStream(); diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs index dbd9de90f..50e912565 100644 --- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs +++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs @@ -10,7 +10,8 @@ public partial class FileInstructService public async Task ReadImages(string text, IEnumerable images, InstructOptions? options = null) { var innerAgentId = options?.AgentId ?? Guid.Empty.ToString(); - var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName); + var instruction = await RenderAgentTemplate(innerAgentId, options?.TemplateName, options?.Data); + text = RenderText(text, options?.Data); var completion = CompletionProvider.GetChatCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "gpt-4o", multiModal: true); var message = await completion.GetChatCompletions(new Agent() @@ -48,7 +49,8 @@ await hook.OnResponseGenerated(new InstructResponseModel public async Task GenerateImage(string text, InstructOptions? options = null) { var innerAgentId = options?.AgentId ?? Guid.Empty.ToString(); - var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName); + var instruction = await RenderAgentTemplate(innerAgentId, options?.TemplateName, options?.Data); + text = RenderText(text, options?.Data); var textContent = text.IfNullOrEmptyAs(instruction).IfNullOrEmptyAs(string.Empty); var completion = CompletionProvider.GetImageCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "gpt-image-1-mini"); @@ -85,7 +87,7 @@ public async Task VaryImage(InstructFileModel image, InstructOp var binary = await DownloadFile(image); // Convert image - var converter = GetImageConverter(options?.ImageConvertProvider); + var converter = GetImageConverter(options?.ImageConverter); if (converter != null) { binary = await converter.ConvertImage(binary); @@ -124,13 +126,14 @@ public async Task EditImage(string text, InstructFileModel imag } var innerAgentId = options?.AgentId ?? Guid.Empty.ToString(); - var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName); + var instruction = await RenderAgentTemplate(innerAgentId, options?.TemplateName, options?.Data); + text = RenderText(text, options?.Data); var completion = CompletionProvider.GetImageCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "gpt-image-1-mini"); var binary = await DownloadFile(image); // Convert image - var converter = GetImageConverter(options?.ImageConvertProvider); + var converter = GetImageConverter(options?.ImageConverter); if (converter != null) { binary = await converter.ConvertImage(binary); @@ -173,14 +176,15 @@ public async Task EditImage(string text, InstructFileModel imag } var innerAgentId = options?.AgentId ?? Guid.Empty.ToString(); - var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName); + var instruction = await RenderAgentTemplate(innerAgentId, options?.TemplateName, options?.Data); + text = RenderText(text, options?.Data); var completion = CompletionProvider.GetImageCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "gpt-image-1-mini"); var imageBinary = await DownloadFile(image); var maskBinary = await DownloadFile(mask); // Convert image - var converter = GetImageConverter(options?.ImageConvertProvider); + var converter = GetImageConverter(options?.ImageConverter); if (converter != null) { imageBinary = await converter.ConvertImage(imageBinary); @@ -225,7 +229,8 @@ await hook.OnResponseGenerated(new InstructResponseModel public async Task ComposeImages(string text, InstructFileModel[] images, InstructOptions? options = null) { var innerAgentId = options?.AgentId ?? Guid.Empty.ToString(); - var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName); + var instruction = await RenderAgentTemplate(innerAgentId, options?.TemplateName, options?.Data); + text = RenderText(text, options?.Data); var completion = CompletionProvider.GetImageCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "gpt-image-1-mini"); @@ -236,7 +241,7 @@ public async Task ComposeImages(string text, InstructFileModel[ var binary = await DownloadFile(image); // Convert image - var converter = GetImageConverter(options?.ImageConvertProvider); + var converter = GetImageConverter(options?.ImageConverter); if (converter != null) { binary = await converter.ConvertImage(binary); diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs index 2e4792222..bee60f1af 100644 --- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs +++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs @@ -26,7 +26,7 @@ public async Task ReadPdf(string text, List files, In var pdfFiles = await DownloadAndSaveFiles(sessionDir, files); var targetFiles = pdfFiles; - var converter = GetImageConverter(options?.ImageConvertProvider); + var converter = GetImageConverter(options?.ImageConverter); if (converter == null && provider == "openai") { var fileCoreSettings = _services.GetRequiredService(); @@ -40,7 +40,8 @@ public async Task ReadPdf(string text, List files, In } var innerAgentId = options?.AgentId ?? Guid.Empty.ToString(); - var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName); + var instruction = await RenderAgentTemplate(innerAgentId, options?.TemplateName, options?.Data); + text = RenderText(text, options?.Data); var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: options?.Model ?? "gpt-5-mini", multiModal: true); diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs index 170a0fc0c..48cf5e83c 100644 --- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs +++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs @@ -1,7 +1,6 @@ - +using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Files.Converters; -using Microsoft.Extensions.Options; -using static System.Net.Mime.MediaTypeNames; +using BotSharp.Abstraction.Templating; namespace BotSharp.Core.Files.Services; @@ -63,7 +62,7 @@ private async Task DownloadFile(InstructFileModel file) } } - private async Task GetAgentTemplate(string agentId, string? templateName) + private async Task RenderAgentTemplate(string agentId, string? templateName, IDictionary? data = null) { if (string.IsNullOrWhiteSpace(agentId) || string.IsNullOrWhiteSpace(templateName)) { @@ -77,21 +76,32 @@ private async Task DownloadFile(InstructFileModel file) return null; } - var instruction = agentService.RenderTemplate(agent, templateName); + var instruction = agentService.RenderTemplate(agent, templateName, data); return instruction; } + private string RenderText(string text, IDictionary? data = null) + { + var agentService = _services.GetRequiredService(); + var render = _services.GetRequiredService(); + + var renderData = data != null + ? new Dictionary(data) + : agentService.CollectRenderData(new Agent()); + return render.Render(text, renderData); + } + private string BuildFileName(string? name, string? extension, string defaultName, string defaultExtension) { var fname = name.IfNullOrEmptyAs(defaultName); - var fextension = extension.IfNullOrEmptyAs(defaultExtension); + var fextension = extension.IfNullOrEmptyAs(defaultExtension)!; fextension = fextension.StartsWith(".") ? fextension.Substring(1) : fextension; return $"{name}.{fextension}"; } private IImageConverter? GetImageConverter(string? provider) { - var converter = _services.GetServices().FirstOrDefault(x => x.Provider == (provider ?? "file-handler")); + var converter = _services.GetServices().FirstOrDefault(x => x.Provider == (provider ?? "image-handler")); return converter; } #endregion diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 8960ecf63..8ba6e499b 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Coding; +using BotSharp.Abstraction.Coding.Enums; using BotSharp.Abstraction.Files.Options; using BotSharp.Abstraction.Files.Proccessors; using BotSharp.Abstraction.Instructs; @@ -179,7 +180,7 @@ await hook.OnResponseGenerated(new InstructResponseModel var state = _services.GetRequiredService(); var hooks = _services.GetHooks(agent.Id); - var codeProvider = codeOptions?.Processor ?? "botsharp-py-interpreter"; + var codeProvider = codeOptions?.Processor ?? BuiltInCodeProcessor.PyInterpreter; var codeProcessor = _services.GetServices() .FirstOrDefault(x => x.Provider.IsEqualTo(codeProvider)); @@ -213,7 +214,7 @@ await hook.OnResponseGenerated(new InstructResponseModel // Get code script var scriptType = codeOptions?.ScriptType ?? AgentCodeScriptType.Src; var codeScript = await agentService.GetAgentCodeScript(agent.Id, scriptName, scriptType); - if (string.IsNullOrWhiteSpace(codeScript)) + if (string.IsNullOrWhiteSpace(codeScript?.Content)) { #if DEBUG _logger.LogWarning($"Empty code script. (Agent: {agent.Id}, {scriptName})"); @@ -230,7 +231,8 @@ await hook.OnResponseGenerated(new InstructResponseModel var context = new CodeInstructContext { - CodeScript = codeScript, + ScriptName = codeScript.Name, + ScriptContent = codeScript.Content, ScriptType = scriptType, Arguments = arguments }; @@ -253,7 +255,7 @@ await hook.OnResponseGenerated(new InstructResponseModel } // Run code script - var codeResponse = await codeProcessor.RunAsync(context.CodeScript, options: new() + var codeResponse = await codeProcessor.RunAsync(context.ScriptContent, options: new() { ScriptName = scriptName, Arguments = context.Arguments @@ -283,7 +285,7 @@ await hook.OnResponseGenerated(new InstructResponseModel Model = string.Empty, TemplateName = scriptName, UserMessage = message.Content, - SystemInstruction = context?.CodeScript, + SystemInstruction = $"Code script name: {codeScript.Name}, Version: {codeScript.UpdatedTime.ToString("o")}", CompletionText = response.Text }); } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs index 7ebfdc84f..fa4d11a9f 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs @@ -50,7 +50,7 @@ public List GetAgentCodeScripts(string agentId, AgentCodeScript return results; } - public string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) + public AgentCodeScript? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) { if (string.IsNullOrWhiteSpace(agentId) || string.IsNullOrWhiteSpace(scriptName) @@ -66,11 +66,20 @@ public List GetAgentCodeScripts(string agentId, AgentCodeScript } var foundFile = Directory.EnumerateFiles(dir).FirstOrDefault(file => scriptName.IsEqualTo(Path.GetFileName(file))); - if (!string.IsNullOrEmpty(foundFile)) + if (!File.Exists(foundFile)) { - return File.ReadAllText(foundFile); + return null; } - return string.Empty; + + return new AgentCodeScript + { + AgentId = agentId, + Name = scriptName, + ScriptType = scriptType, + Content = File.ReadAllText(foundFile), + CreatedTime = File.GetCreationTimeUtc(foundFile), + UpdatedTime = File.GetLastWriteTimeUtc(foundFile) + }; } public bool UpdateAgentCodeScripts(string agentId, List scripts, AgentCodeScriptDbUpdateOptions? options = null) diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index de3fa1e78..39ac00cc8 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -6,6 +6,7 @@ global using BotSharp.Abstraction.Conversations.Models; global using BotSharp.Abstraction.Conversations.Settings; global using BotSharp.Abstraction.Coding.Models; +global using BotSharp.Abstraction.Coding.Settings; global using BotSharp.Abstraction.Crontab.Models; global using BotSharp.Abstraction.Files; global using BotSharp.Abstraction.Files.Enums; diff --git a/src/Infrastructure/BotSharp.Core/data/agents/c2a2faf6-b8b5-47fe-807b-f4714cf25dd4/templates/rule-trigger-code-generate_instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/c2a2faf6-b8b5-47fe-807b-f4714cf25dd4/templates/rule-trigger-code-generate_instruction.liquid new file mode 100644 index 000000000..2293ede7b --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/c2a2faf6-b8b5-47fe-807b-f4714cf25dd4/templates/rule-trigger-code-generate_instruction.liquid @@ -0,0 +1,15 @@ +Based on user's request, help generate a refined python code of function definition, only using base python packages, that can return a boolean value of true or false to determine if based on known states, the function can determine if conditions are met. + +User's request is {{user_request}} + +Couple of notes to address: +1. You need to generate a function named check_trigger_criterion(args), where input is a json object. The example of this json object is {{args_example}}. +2. The input to this function comes from the arguments. You must use "ArgumentParser" to take an argument named "trigger_args", and then use "parse_known_args" to get the raw args. +3. After getting the raw args, you need to use 'clean_args = bytes(raw_args, "utf-8").decode("unicode_escape")' to clean the raw argument, and then use 'args = json.loads(clean_args)' to get the json args. +4. Based on the user's request and input args, generate the function logic and ONLY return a boolean value. +5. You must only call check_trigger_criterion with the parsed json args in "if __name__ == '__main__'" block, and print only the function output. If any error occurs, print "Error". +6. Refine the code so it will return for certain errors if detected, for example, when input is not a valid json, return "Error: Input is not a valid JSON string."; or when certain attributes are missing from json args, so I can better track of this. +7. You can use try-except blocks to catch any errors, and return "Error" if any error occurs. Do not use sys.exit(0) to exit the program. +8. Use as fewer comments and try-except blocks as possible. + +Output the executable code directly in order to directly save it to a py file. \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.Coding.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.Coding.cs index 55c3381bb..40800d35c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.Coding.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.Coding.cs @@ -61,7 +61,8 @@ public async Task GenerateAgentCodeScript([FromRoute] stri var states = request.Options?.Data?.ToList(); var state = _services.GetRequiredService(); states?.ForEach(x => state.SetState(x.Key, x.Value, source: StateSource.External)); - state.SetState("programming_language", request.Options?.Language, source: StateSource.External); + state.SetState("code_processor", request.Options?.Processor, source: StateSource.External); + state.SetState("programming_language", request.Options?.ProgrammingLanguage, source: StateSource.External); var result = await _agentService.GenerateCodeScript(agentId, request.Text, request?.Options); return result; diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.Rule.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.Rule.cs index 4daefc717..52fd719fd 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.Rule.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.Rule.cs @@ -6,13 +6,16 @@ namespace BotSharp.OpenAPI.Controllers; public partial class AgentController { [HttpGet("/rule/triggers")] - public IEnumerable GetRuleTriggers() + public IEnumerable GetRuleTriggers() { var triggers = _services.GetServices(); - return triggers.Select(x => new AgentRule + return triggers.Select(x => new AgentRuleViewModel { - TriggerName = x.GetType().Name - }).OrderBy(x => x.TriggerName).ToList(); + TriggerName = x.Name, + Channel = x.Channel, + Statement = x.Statement, + OutputArgs = x.OutputArgs + }).OrderBy(x => x.TriggerName); } [HttpGet("/rule/formalization")] diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Conversation/GoogleController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Application/GoogleController.cs similarity index 93% rename from src/Infrastructure/BotSharp.OpenAPI/Controllers/Conversation/GoogleController.cs rename to src/Infrastructure/BotSharp.OpenAPI/Controllers/Application/GoogleController.cs index 13faeb2d1..5497d6c2e 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Conversation/GoogleController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Application/GoogleController.cs @@ -11,15 +11,18 @@ public class GoogleController : ControllerBase private readonly IServiceProvider _services; private readonly BotSharpOptions _options; private readonly IHttpClientFactory _httpClientFactory; - private readonly ILogger _logger; + private readonly ILogger _logger; - public GoogleController(IServiceProvider services, + public GoogleController( + IServiceProvider services, + ILogger logger, IHttpClientFactory httpClientFactory, BotSharpOptions options) { _services = services; - _options = options; + _logger = logger; _httpClientFactory = httpClientFactory; + _options = options; } [HttpGet("/address/options")] diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Instruct/InstructModeController.File.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Instruct/InstructModeController.File.cs index 031821433..9c348d8b6 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Instruct/InstructModeController.File.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Instruct/InstructModeController.File.cs @@ -24,7 +24,7 @@ public async Task PdfCompletion([FromBody] PdfReadFileRe Model = request.Model, AgentId = request.AgentId, TemplateName = request.TemplateName, - ImageConvertProvider = request.ImageConvertProvider + ImageConverter = request.ImageConverter }); viewModel.Success = true; @@ -62,7 +62,7 @@ public async Task PdfCompletion([FromForm] IEnumerable ComposeImages([FromBody] ImageCompos Model = request.Model, AgentId = request.AgentId, TemplateName = request.TemplateName, - ImageConvertProvider = request.ImageConvertProvider + ImageConverter = request.ImageConverter }); imageViewModel.Success = true; @@ -103,7 +103,7 @@ public async Task ImageVariation([FromBody] ImageVaria Provider = request.Provider, Model = request.Model, AgentId = request.AgentId, - ImageConvertProvider = request.ImageConvertProvider + ImageConverter = request.ImageConverter }); imageViewModel.Success = true; @@ -142,7 +142,7 @@ public async Task ImageVariation(IFormFile file, [From Provider = request?.Provider, Model = request?.Model, AgentId = request?.AgentId, - ImageConvertProvider = request?.ImageConvertProvider + ImageConverter = request?.ImageConverter }); imageViewModel.Success = true; @@ -182,7 +182,7 @@ public async Task ImageEdit([FromBody] ImageEditFileRe Model = request.Model, AgentId = request.AgentId, TemplateName = request.TemplateName, - ImageConvertProvider = request.ImageConvertProvider + ImageConverter = request.ImageConverter }); imageViewModel.Success = true; @@ -222,7 +222,7 @@ public async Task ImageEdit(IFormFile file, [FromForm] Model = request?.Model, AgentId = request?.AgentId, TemplateName = request?.TemplateName, - ImageConvertProvider = request?.ImageConvertProvider + ImageConverter = request?.ImageConverter }); imageViewModel.Success = true; @@ -264,7 +264,7 @@ public async Task ImageMaskEdit([FromBody] ImageMaskEd Model = request.Model, AgentId = request.AgentId, TemplateName = request.TemplateName, - ImageConvertProvider = request.ImageConvertProvider + ImageConverter = request.ImageConverter }); imageViewModel.Success = true; @@ -312,7 +312,7 @@ public async Task ImageMaskEdit(IFormFile image, IForm Model = request?.Model, AgentId = request?.AgentId, TemplateName = request?.TemplateName, - ImageConvertProvider = request?.ImageConvertProvider + ImageConverter = request?.ImageConverter }); imageViewModel.Success = true; diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentRuleViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentRuleViewModel.cs new file mode 100644 index 000000000..23386c13d --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentRuleViewModel.cs @@ -0,0 +1,35 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.OpenAPI.ViewModels.Agents; + +public class AgentRuleViewModel +{ + [JsonPropertyName("trigger_name")] + public string TriggerName { get; set; } = string.Empty; + + [JsonPropertyName("channel")] + public string Channel { get; set; } = string.Empty; + + [JsonPropertyName("statement")] + public string Statement { get; set; } = string.Empty; + + [JsonPropertyName("output_args")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public JsonDocument? OutputArgs { get; set; } + + [JsonPropertyName("json_args")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? JsonArgs + { + get + { + if (OutputArgs == null) + { + return null; + } + + var json = JsonSerializer.Serialize(OutputArgs.RootElement, new JsonSerializerOptions { WriteIndented = true }); + return $"```json\r\n{json}\r\n```"; + } + } +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs index 96c831cb0..d1f44c34d 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs @@ -44,8 +44,8 @@ public class ImageGenerationRequest : InstructBaseRequest public class ImageVariationRequest : InstructBaseRequest { - [JsonPropertyName("image_convert_provider")] - public string? ImageConvertProvider { get; set; } + [JsonPropertyName("image_converter")] + public string? ImageConverter { get; set; } } public class ImageVariationFileRequest : ImageVariationRequest @@ -60,8 +60,8 @@ public class ImageEditRequest : InstructBaseRequest [JsonPropertyName("text")] public string Text { get; set; } = string.Empty; - [JsonPropertyName("image_convert_provider")] - public string? ImageConvertProvider { get; set; } + [JsonPropertyName("image_converter")] + public string? ImageConverter { get; set; } } public class ImageEditFileRequest : ImageEditRequest @@ -81,8 +81,8 @@ public class ImageMaskEditRequest : InstructBaseRequest [JsonPropertyName("text")] public string Text { get; set; } = string.Empty; - [JsonPropertyName("image_convert_provider")] - public string? ImageConvertProvider { get; set; } + [JsonPropertyName("image_converter")] + public string? ImageConverter { get; set; } } public class ImageMaskEditFileRequest : ImageMaskEditRequest @@ -100,8 +100,8 @@ public class PdfReadRequest : InstructBaseRequest [JsonPropertyName("text")] public string Text { get; set; } = string.Empty; - [JsonPropertyName("image_convert_provider")] - public string? ImageConvertProvider { get; set; } + [JsonPropertyName("image_converter")] + public string? ImageConverter { get; set; } } public class PdfReadFileRequest : PdfReadRequest diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index 7156f29f9..321deed56 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Conversations.Enums; -using BotSharp.Abstraction.Routing.Enums; using BotSharp.Abstraction.Routing.Models; using Microsoft.AspNetCore.SignalR; using System.Runtime.CompilerServices; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs index 9a39fb58c..52f4570a1 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs @@ -31,7 +31,9 @@ public static AgentCodeScript ToDomainModel(AgentCodeScriptDocument script) AgentId = script.AgentId, Name = script.Name, Content = script.Content, - ScriptType = script.ScriptType + ScriptType = script.ScriptType, + CreatedTime = script.CreatedTime, + UpdatedTime = script.UpdatedTime }; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs index 86285583d..b6e0dc148 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs @@ -35,7 +35,7 @@ public List GetAgentCodeScripts(string agentId, AgentCodeScript return found.Select(x => AgentCodeScriptDocument.ToDomainModel(x)).ToList(); } - public string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) + public AgentCodeScript? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) { if (string.IsNullOrWhiteSpace(agentId) || string.IsNullOrWhiteSpace(scriptName) @@ -53,7 +53,7 @@ public List GetAgentCodeScripts(string agentId, AgentCodeScript }; var found = _dc.AgentCodeScripts.Find(builder.And(filters)).FirstOrDefault(); - return found?.Content; + return AgentCodeScriptDocument.ToDomainModel(found); } public bool UpdateAgentCodeScripts(string agentId, List scripts, AgentCodeScriptDbUpdateOptions? options = null) diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyCodeInterpreter.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyCodeInterpreter.cs index 8ee5d536a..9e03b2aae 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyCodeInterpreter.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyCodeInterpreter.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Coding.Models; using Microsoft.Extensions.Logging; using Python.Runtime; using System.Threading; @@ -11,18 +10,21 @@ public class PyCodeInterpreter : ICodeProcessor private readonly IServiceProvider _services; private readonly ILogger _logger; private readonly CodeScriptExecutor _executor; + private readonly AgentSettings _agentSettings; public PyCodeInterpreter( IServiceProvider services, ILogger logger, - CodeScriptExecutor executor) + CodeScriptExecutor executor, + AgentSettings agentSettings) { _services = services; _logger = logger; _executor = executor; + _agentSettings = agentSettings; } - public string Provider => "botsharp-py-interpreter"; + public string Provider => BuiltInCodeProcessor.PyInterpreter; public async Task RunAsync(string codeScript, CodeInterpretOptions? options = null) { @@ -42,10 +44,10 @@ public async Task GenerateCodeScriptAsync(string text, Cod var agentId = options?.AgentId; var templateName = options?.TemplateName; - - var agentService = _services.GetRequiredService(); + if (!string.IsNullOrEmpty(agentId)) { + var agentService = _services.GetRequiredService(); agent = await agentService.GetAgent(agentId); } @@ -55,6 +57,7 @@ public async Task GenerateCodeScriptAsync(string text, Cod instruction = agent.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(templateName))?.Content; } + var (provider, model) = GetLlmProviderModel(); var innerAgent = new Agent { Id = agent?.Id ?? BuiltInAgentId.AIProgrammer, @@ -62,8 +65,8 @@ public async Task GenerateCodeScriptAsync(string text, Cod Instruction = instruction, LlmConfig = new AgentLlmConfig { - Provider = options?.Provider ?? "openai", - Model = options?.Model ?? "gpt-5-mini", + Provider = options?.Provider ?? provider, + Model = options?.Model ?? model, MaxOutputTokens = options?.MaxOutputTokens, ReasoningEffortLevel = options?.ReasoningEffortLevel }, @@ -84,7 +87,7 @@ public async Task GenerateCodeScriptAsync(string text, Cod { Success = true, Content = response.Content, - Language = options?.Language ?? "python" + Language = options?.ProgrammingLanguage ?? "python" }; } @@ -179,5 +182,21 @@ private CodeInterpretResponse CoreRun(string codeScript, CodeInterpretOptions? o } } } + + private (string, string) GetLlmProviderModel() + { + var provider = _agentSettings.Coding?.Provider; + var model = _agentSettings.Coding?.Model; + + if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(model)) + { + return (provider, model); + } + + provider = "openai"; + model = "gpt-5-mini"; + + return (provider, model); + } #endregion } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs index bd2c943b7..9296406a3 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs @@ -21,6 +21,9 @@ global using BotSharp.Abstraction.Messaging.Models.RichContent.Template; global using BotSharp.Abstraction.Routing; global using BotSharp.Abstraction.Coding; +global using BotSharp.Abstraction.Coding.Enums; +global using BotSharp.Abstraction.Coding.Models; +global using BotSharp.Abstraction.Coding.Settings; global using BotSharp.Abstraction.Coding.Options; global using BotSharp.Abstraction.Coding.Responses; global using BotSharp.Core.Coding; diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 071b279a5..e97678903 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -468,6 +468,10 @@ "LlmConfig": { "Provider": "openai", "Model": "gpt-4.1-nano" + }, + "Coding": { + "Provider": "openai", + "Model": "gpt-5-mini" } },