diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index 7a06cce1d..1f7283fc8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -53,7 +53,8 @@ public interface IAgentService /// /// Original agent information Task GetAgent(string id); - + Task GetAgentTemplateDetail(string agentId, string templateName); + Task DeleteAgent(string id, AgentDeleteOptions? options = null); Task UpdateAgent(Agent agent, AgentField updateField); diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs index 2eb3b4a02..a39be67ab 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs @@ -1,9 +1,8 @@ namespace BotSharp.Abstraction.Agents.Models; -public class AgentTemplate +public class AgentTemplate : AgentTemplateConfig { - public string Name { get; set; } - public string Content { get; set; } + public string Content { get; set; } = string.Empty; public AgentTemplate() { @@ -20,3 +19,23 @@ public override string ToString() return Name; } } + +public class AgentTemplateConfig +{ + public string Name { get; set; } + + /// + /// Response format: json, xml, markdown, yaml, etc. + /// + [JsonPropertyName("response_format")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? ResponseFormat { get; set; } + + [JsonPropertyName("llm_config")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public AgentTemplateLlmConfig? LlmConfig { get; set; } +} + +public class AgentTemplateLlmConfig : LlmConfigBase +{ +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs b/src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs index 899f6e7ab..20c66b0a3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs @@ -5,11 +5,15 @@ public class LlmConfigBase : LlmProviderModel /// /// Llm maximum output tokens /// + [JsonPropertyName("max_output_tokens")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? MaxOutputTokens { get; set; } /// - /// Llm reasoning effort level + /// Llm reasoning effort level, thinking level /// + [JsonPropertyName("reasoning_effort_level")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ReasoningEffortLevel { get; set; } } @@ -22,4 +26,7 @@ public class LlmProviderModel [JsonPropertyName("model")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Model { get; set; } + + [JsonIgnore] + public bool IsValid => !string.IsNullOrEmpty(Provider) && !string.IsNullOrEmpty(Model); } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 437e6cdd7..cb44f9340 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -88,6 +88,8 @@ Task> GetAgentResponses(string agentId, string prefix, string inten => throw new NotImplementedException(); Task GetAgentTemplate(string agentId, string templateName) => throw new NotImplementedException(); + Task GetAgentTemplateDetail(string agentId, string templateName) + => throw new NotImplementedException(); Task PatchAgentTemplate(string agentId, AgentTemplate template) => throw new NotImplementedException(); Task UpdateAgentLabels(string agentId, List labels) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index 334e385b9..b2d056b6b 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -30,7 +30,7 @@ public async Task CreateAgent(Agent agent) var userService = _services.GetRequiredService(); var auth = await userService.GetUserAuthorizations(); - await _db.BulkInsertAgents(new List { agentRecord }); + await _db.BulkInsertAgents([agentRecord]); if (auth.IsAdmin || auth.Permissions.Contains(UserPermission.CreateAgent)) { await _db.BulkInsertUserAgents(new List @@ -39,7 +39,7 @@ await _db.BulkInsertUserAgents(new List { UserId = user.Id, AgentId = agentRecord.Id, - Actions = new List { UserAction.Edit, UserAction.Train, UserAction.Evaluate, UserAction.Chat }, + Actions = [UserAction.Edit, UserAction.Train, UserAction.Evaluate, UserAction.Chat], CreatedTime = DateTime.UtcNow, UpdatedTime = DateTime.UtcNow } @@ -98,6 +98,9 @@ private List GetTemplatesFromFile(string fileDir) var templateDir = Path.Combine(fileDir, "templates"); if (!Directory.Exists(templateDir)) return templates; + // Load template configs + var configs = GetAgentTemplateConfigs(fileDir); + foreach (var file in Directory.GetFiles(templateDir)) { var extension = Path.GetExtension(file).Substring(1); @@ -105,13 +108,41 @@ private List GetTemplatesFromFile(string fileDir) { var name = Path.GetFileNameWithoutExtension(file); var content = File.ReadAllText(file); - templates.Add(new AgentTemplate(name, content)); + var template = new AgentTemplate(name, content); + var config = configs.FirstOrDefault(x => x.Name.IsEqualTo(name)); + if (config != null) + { + template.ResponseFormat = config.ResponseFormat; + template.LlmConfig = config.LlmConfig; + } + templates.Add(template); } } - + return templates; } + private IEnumerable GetAgentTemplateConfigs(string baseDir) + { + var configFile = Path.Combine(baseDir, "template_configs.json"); + var configs = new List(); + + try + { + if (File.Exists(configFile)) + { + var configJson = File.ReadAllText(configFile); + configs = JsonSerializer.Deserialize>(configJson, _options.JsonSerializerOptions) ?? []; + } + return configs; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error when loading template configs in {configFile}", configFile); + return configs; + } + } + private List GetFunctionsFromFile(string fileDir) { var functions = new List(); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs index 800a393c3..955c9b2b3 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs @@ -105,6 +105,36 @@ private void AddDefaultInstruction(Agent agent, string instruction) agent.ChannelInstructions = instructions; } +#if !DEBUG + [SharpCache(10, perInstanceCache: true)] +#endif + public async Task GetAgentTemplateDetail(string agentId, string templateName) + { + var template = await _db.GetAgentTemplateDetail(agentId, templateName); + if (template == null) + { + return template; + } + + if (template.LlmConfig == null) + { + var agent = await _db.GetAgent(agentId); + if (!string.IsNullOrEmpty(agent?.LlmConfig?.Provider) + && !string.IsNullOrEmpty(agent?.LlmConfig?.Model)) + { + template.LlmConfig = new AgentTemplateLlmConfig + { + Provider = agent.LlmConfig.Provider, + Model = agent.LlmConfig.Model, + MaxOutputTokens = agent.LlmConfig.MaxOutputTokens, + ReasoningEffortLevel = agent.LlmConfig.ReasoningEffortLevel + }; + } + } + + return template.DeepClone(); + } + public async Task InheritAgent(Agent agent) { if (string.IsNullOrWhiteSpace(agent?.InheritAgentId)) diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 7aa3a42dd..e56d01eec 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -68,6 +68,7 @@ + @@ -131,6 +132,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 6995b49ec..29fa0724b 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -242,12 +242,31 @@ private async Task RunLlm( var result = string.Empty; // Render prompt - var prompt = string.IsNullOrEmpty(templateName) ? - agentService.RenderInstruction(agent) : - agentService.RenderTemplate(agent, templateName); + var prompt = string.Empty; + var llmConfig = agent.LlmConfig; + + if (!string.IsNullOrEmpty(templateName)) + { + prompt = agentService.RenderTemplate(agent, templateName); + var templateLlmConfig = agent.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(templateName))?.LlmConfig; + if (templateLlmConfig?.IsValid == true) + { + llmConfig = new AgentLlmConfig + { + Provider = templateLlmConfig.Provider, + Model = templateLlmConfig.Model, + MaxOutputTokens = templateLlmConfig.MaxOutputTokens, + ReasoningEffortLevel = templateLlmConfig.ReasoningEffortLevel + }; + } + } + else + { + prompt = agentService.RenderInstruction(agent); + } var completer = CompletionProvider.GetCompletion(_services, - agentConfig: agent.LlmConfig); + agentConfig: llmConfig); if (completer is ITextCompletion textCompleter) { @@ -292,7 +311,7 @@ private async Task RunLlm( } else { - result = await GetChatCompletion(chatCompleter, agent, instruction, prompt, message.MessageId, files); + result = await GetChatCompletion(chatCompleter, agent, instruction, prompt, message.MessageId, llmConfig, files); } // Repair JSON format if needed @@ -343,6 +362,7 @@ private async Task GetChatCompletion( string instruction, string text, string messageId, + AgentLlmConfig? llmConfig = null, IEnumerable? files = null) { var result = await chatCompleter.GetChatCompletions(new Agent @@ -350,7 +370,7 @@ private async Task GetChatCompletion( Id = agent.Id, Name = agent.Name, Instruction = instruction, - LlmConfig = agent.LlmConfig + LlmConfig = llmConfig ?? agent.LlmConfig }, new List { new RoleDialogModel(AgentRole.User, text) diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs index f75707605..1ca2d926c 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs @@ -56,6 +56,7 @@ public partial class InstructService private async Task BuildInnerAgent(InstructOptions? options) { Agent? agent = null; + AgentLlmConfig? llmConfig = null; string? instruction = null; if (!string.IsNullOrWhiteSpace(options?.AgentId)) @@ -65,8 +66,19 @@ private async Task BuildInnerAgent(InstructOptions? options) if (!string.IsNullOrWhiteSpace(options?.TemplateName)) { - var template = agent?.Templates?.FirstOrDefault(x => x.Name == options.TemplateName)?.Content ?? string.Empty; - instruction = BuildInstruction(template, options?.Data ?? []); + var template = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(options.TemplateName)); + instruction = BuildInstruction(template?.Content ?? string.Empty, options?.Data ?? []); + var templateLlmConfig = template?.LlmConfig; + if (templateLlmConfig?.IsValid == true) + { + llmConfig = new AgentLlmConfig + { + Provider = templateLlmConfig.Provider, + Model = templateLlmConfig.Model, + MaxOutputTokens = templateLlmConfig.MaxOutputTokens, + ReasoningEffortLevel = templateLlmConfig.ReasoningEffortLevel + }; + } } } @@ -75,7 +87,7 @@ private async Task BuildInnerAgent(InstructOptions? options) Id = agent?.Id ?? Guid.Empty.ToString(), Name = agent?.Name ?? "Unknown", Instruction = instruction, - LlmConfig = agent?.LlmConfig ?? new() + LlmConfig = llmConfig ?? agent?.LlmConfig ?? new() }; } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index 43a5bf659..2ab0b1f2b 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -425,7 +425,8 @@ private async Task UpdateAgentTemplates(string agentId, List temp return; } - var templateDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_TEMPLATES_FOLDER); + var baseDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId); + var templateDir = Path.Combine(baseDir, AGENT_TEMPLATES_FOLDER); DeleteBeforeCreateDirectory(templateDir); foreach (var template in templates) @@ -433,6 +434,18 @@ private async Task UpdateAgentTemplates(string agentId, List temp var file = Path.Combine(templateDir, $"{template.Name}.{_agentSettings.TemplateFormat}"); await File.WriteAllTextAsync(file, template.Content); } + + // Save template configs + var configFile = Path.Combine(baseDir, AGENT_TEMPLATE_CONFIG_FILE); + var configs = templates.Select(t => new AgentTemplateConfig + { + Name = t.Name, + ResponseFormat = t.ResponseFormat, + LlmConfig = t.LlmConfig + }).ToList(); + + var configJson = JsonSerializer.Serialize(configs, _options); + await File.WriteAllTextAsync(configFile, configJson); } private async Task UpdateAgentResponses(string agentId, List responses) @@ -710,6 +723,50 @@ public async Task GetAgentTemplate(string agentId, string templateName) return string.Empty; } + public async Task GetAgentTemplateDetail(string agentId, string templateName) + { + var template = new AgentTemplate { Name = templateName }; + + if (string.IsNullOrWhiteSpace(agentId) + || string.IsNullOrWhiteSpace(templateName)) + { + return template; + } + + var baseDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId); + var dir = Path.Combine(baseDir, AGENT_TEMPLATES_FOLDER); + if (!Directory.Exists(dir)) + { + return template; + } + + // Get template content + foreach (var file in Directory.EnumerateFiles(dir)) + { + var fileName = file.Split(Path.DirectorySeparatorChar).Last(); + var splitIdx = fileName.LastIndexOf("."); + var name = fileName.Substring(0, splitIdx); + var extension = fileName.Substring(splitIdx + 1); + if (name.IsEqualTo(templateName) && extension.IsEqualTo(_agentSettings.TemplateFormat)) + { + template.Content = await File.ReadAllTextAsync(file); + break; + } + } + + // Get template configs + var (configs, _) = GetAgentTemplateConfigs(baseDir); + var configFile = Path.Combine(baseDir, AGENT_TEMPLATE_CONFIG_FILE); + var found = configs?.FirstOrDefault(x => x.Name.IsEqualTo(templateName)); + if (found != null) + { + template.ResponseFormat = found.ResponseFormat; + template.LlmConfig = found.LlmConfig; + } + + return template; + } + public async Task PatchAgentTemplate(string agentId, AgentTemplate template) { if (string.IsNullOrEmpty(agentId) || template == null) @@ -717,7 +774,8 @@ public async Task PatchAgentTemplate(string agentId, AgentTemplate templat return false; } - var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_TEMPLATES_FOLDER); + var baseDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId); + var dir = Path.Combine(baseDir, AGENT_TEMPLATES_FOLDER); if (!Directory.Exists(dir)) { return false; @@ -736,6 +794,28 @@ public async Task PatchAgentTemplate(string agentId, AgentTemplate templat } await File.WriteAllTextAsync(foundTemplate, template.Content); + + // Update template config + var (configs, configFile) = GetAgentTemplateConfigs(baseDir); + + var existingConfig = configs.FirstOrDefault(x => x.Name.IsEqualTo(template.Name)); + if (existingConfig != null) + { + existingConfig.ResponseFormat = template.ResponseFormat; + existingConfig.LlmConfig = template.LlmConfig; + } + else + { + configs.Add(new AgentTemplateConfig + { + Name = template.Name, + ResponseFormat = template.ResponseFormat, + LlmConfig = template.LlmConfig + }); + } + + var updatedJson = JsonSerializer.Serialize(configs, _options); + await File.WriteAllTextAsync(configFile, updatedJson); return true; } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index 255d80a83..501a2655d 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -59,6 +59,8 @@ public partial class FileRepository : IBotSharpRepository private const string CRON_FILE = "cron.json"; private const string INSTRUCTION_LOG_FOLDER = "instruction-logs"; + private const string AGENT_TEMPLATE_CONFIG_FILE = "template_configs.json"; + public FileRepository( IServiceProvider services, BotSharpDatabaseSettings dbSettings, @@ -387,6 +389,10 @@ private List FetchTemplates(string fileDir) var templateDir = Path.Combine(fileDir, AGENT_TEMPLATES_FOLDER); if (!Directory.Exists(templateDir)) return templates; + // Load template configs + var (configs, _) = GetAgentTemplateConfigs(fileDir); + + // Load templates foreach (var file in Directory.GetFiles(templateDir)) { var fileName = Path.GetFileName(file); @@ -396,7 +402,14 @@ private List FetchTemplates(string fileDir) if (extension.Equals(_agentSettings.TemplateFormat, StringComparison.OrdinalIgnoreCase)) { var content = File.ReadAllText(file); - templates.Add(new AgentTemplate(name, content)); + var template = new AgentTemplate(name, content); + var config = configs.FirstOrDefault(x => x.Name.IsEqualTo(name)); + if (config != null) + { + template.ResponseFormat = config.ResponseFormat; + template.LlmConfig = config.LlmConfig; + } + templates.Add(template); } } @@ -490,5 +503,31 @@ private string[] ParseFileNameByPath(string path, string separator = ".") var name = path.Split(Path.DirectorySeparatorChar).Last(); return name.Split(separator); } + + /// + /// Get agent template configs: (configs, config file location) + /// + /// + /// + private (List, string) GetAgentTemplateConfigs(string baseDir) + { + var configFile = Path.Combine(baseDir, AGENT_TEMPLATE_CONFIG_FILE); + var configs = new List(); + + try + { + if (File.Exists(configFile)) + { + var configJson = File.ReadAllText(configFile); + configs = JsonSerializer.Deserialize>(configJson, _options) ?? []; + } + return (configs, configFile); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error when loading template configs in {configFile}", configFile); + return (configs, configFile); + } + } #endregion } diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/template_configs.json b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/template_configs.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/template_configs.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs index 79de888ef..1c894ca3f 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs @@ -37,7 +37,7 @@ public AgentSettings GetSettings() { var pagedAgents = await _agentService.GetAgents(new AgentFilter { - AgentIds = new List { id } + AgentIds = [id] }); var foundAgent = pagedAgents.Items.FirstOrDefault(); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateLlmConfigMongoModel.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateLlmConfigMongoModel.cs new file mode 100644 index 000000000..6ca7c48f1 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateLlmConfigMongoModel.cs @@ -0,0 +1,43 @@ +using BotSharp.Abstraction.Agents.Models; + +namespace BotSharp.Plugin.MongoStorage.Models; + +public class AgentTemplateLlmConfigMongoModel +{ + public string? Provider { get; set; } + public string? Model { get; set; } + public int? MaxOutputTokens { get; set; } + public string? ReasoningEffortLevel { get; set; } + + public static AgentTemplateLlmConfigMongoModel? ToMongoModel(AgentTemplateLlmConfig? config) + { + if (config == null) + { + return null; + } + + return new AgentTemplateLlmConfigMongoModel + { + Provider = config.Provider, + Model = config.Model, + MaxOutputTokens = config.MaxOutputTokens, + ReasoningEffortLevel = config.ReasoningEffortLevel + }; + } + + public static AgentTemplateLlmConfig? ToDomainModel(AgentTemplateLlmConfigMongoModel? config) + { + if (config == null) + { + return null; + } + + return new AgentTemplateLlmConfig + { + Provider = config.Provider, + Model = config.Model, + MaxOutputTokens = config.MaxOutputTokens, + ReasoningEffortLevel = config.ReasoningEffortLevel + }; + } +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs index 6021ef3c4..ca95e6185 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs @@ -6,14 +6,18 @@ namespace BotSharp.Plugin.MongoStorage.Models; public class AgentTemplateMongoElement { public string Name { get; set; } = default!; - public string Content { get; set; } = default!; + public string Content { get; set; } = string.Empty; + public string? ResponseFormat { get; set; } + public AgentTemplateLlmConfigMongoModel? LlmConfig { get; set; } public static AgentTemplateMongoElement ToMongoElement(AgentTemplate template) { return new AgentTemplateMongoElement { Name = template.Name, - Content = template.Content + Content = template.Content, + ResponseFormat = template.ResponseFormat, + LlmConfig = AgentTemplateLlmConfigMongoModel.ToMongoModel(template.LlmConfig) }; } @@ -22,7 +26,9 @@ public static AgentTemplate ToDomainElement(AgentTemplateMongoElement mongoTempl return new AgentTemplate { Name = mongoTemplate.Name, - Content = mongoTemplate.Content + Content = mongoTemplate.Content, + ResponseFormat = mongoTemplate.ResponseFormat, + LlmConfig = AgentTemplateLlmConfigMongoModel.ToDomainModel(mongoTemplate.LlmConfig) }; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index c1e20f5d6..a18bf7f79 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -553,6 +553,24 @@ public async Task GetAgentTemplate(string agentId, string templateName) return agent.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(templateName))?.Content ?? string.Empty; } + public async Task GetAgentTemplateDetail(string agentId, string templateName) + { + var filter = Builders.Filter.Eq(x => x.Id, agentId); + var agent = await _dc.Agents.Find(filter).FirstOrDefaultAsync(); + if (agent == null) + { + return new AgentTemplate { Name = templateName }; + } + + var foundTemplate = agent.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(templateName)); + if (foundTemplate == null) + { + return new AgentTemplate { Name = templateName }; + } + + return AgentTemplateMongoElement.ToDomainElement(foundTemplate); + } + public async Task PatchAgentTemplate(string agentId, AgentTemplate template) { if (string.IsNullOrEmpty(agentId) || template == null) @@ -574,6 +592,8 @@ public async Task PatchAgentTemplate(string agentId, AgentTemplate templat } foundTemplate.Content = template.Content; + foundTemplate.ResponseFormat = template.ResponseFormat; + foundTemplate.LlmConfig = AgentTemplateLlmConfigMongoModel.ToMongoModel(template.LlmConfig); var update = Builders.Update.Set(x => x.Templates, agent.Templates); await _dc.Agents.UpdateOneAsync(filter, update); return true; diff --git a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs index 5e702fea9..487e72f24 100644 --- a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs +++ b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs @@ -132,5 +132,10 @@ public ResponseFormatType GetTemplateResponseFormat(Agent agent, string template { return ResponseFormatType.Text; } + + public Task GetAgentTemplateDetail(string agentId, string templateName) + { + return Task.FromResult(new AgentTemplate { Name = templateName }); + } } } \ No newline at end of file