diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs index 1bb221d3..34e8ac19 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs @@ -17,7 +17,8 @@ public enum AgentField Response, Sample, LlmConfig, - Utility + Utility, + MaxMessageCount } public enum AgentTaskField diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs index e9fd7c5e..41b3800c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs @@ -104,6 +104,12 @@ public class Agent /// public string? InheritAgentId { get; set; } + /// + /// Maximum message count when load conversation + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? MaxMessageCount { get; set; } + public List RoutingRules { get; set; } = new(); /// @@ -133,6 +139,8 @@ public class Agent Knowledges = agent.Knowledges, IsPublic = agent.IsPublic, Disabled = agent.Disabled, + MergeUtility = agent.MergeUtility, + MaxMessageCount = agent.MaxMessageCount, Profiles = agent.Profiles, RoutingRules = agent.RoutingRules, LlmConfig = agent.LlmConfig, diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 61982cc4..a848b619 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -53,7 +53,7 @@ public interface IBotSharpRepository : IHaveServiceProvider #region Agent void UpdateAgent(Agent agent, AgentField field); - Agent? GetAgent(string agentId); + Agent? GetAgent(string agentId, bool basicsOnly = false); List GetAgents(AgentFilter filter); List GetUserAgents(string userId); void BulkInsertAgents(List agents); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Hooks/BasicAgentHook.cs b/src/Infrastructure/BotSharp.Core/Agents/Hooks/BasicAgentHook.cs index 2a4a7374..65d454c8 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Hooks/BasicAgentHook.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Hooks/BasicAgentHook.cs @@ -47,7 +47,7 @@ public class BasicAgentHook : AgentHookBase var entryAgentId = routing.EntryAgentId; if (!string.IsNullOrEmpty(entryAgentId)) { - var entryAgent = db.GetAgent(entryAgentId); + var entryAgent = db.GetAgent(entryAgentId, basicsOnly: true); var (fns, tps) = GetUniqueContent(entryAgent?.Utilities); functionNames = functionNames.Concat(fns).Distinct().ToList(); templateNames = templateNames.Concat(tps).Distinct().ToList(); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index f871a2fe..ffea8f8d 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -28,6 +28,7 @@ public partial class AgentService record.IsPublic = agent.IsPublic; record.Disabled = agent.Disabled; record.MergeUtility = agent.MergeUtility; + record.MaxMessageCount = agent.MaxMessageCount; record.Type = agent.Type; record.Profiles = agent.Profiles ?? []; record.RoutingRules = agent.RoutingRules ?? []; diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 55d80c18..d586b421 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -153,7 +153,10 @@ public partial class ConversationService : IConversationService } } - return dialogs.TakeLast(lastCount).ToList(); + var agentMsgCount = GetAgentMessageCount(); + var count = agentMsgCount.HasValue && agentMsgCount.Value > 0 ? agentMsgCount.Value : lastCount; + + return dialogs.TakeLast(count).ToList(); } public void SetConversationId(string conversationId, List states, bool isReadOnly = false) @@ -192,4 +195,16 @@ public partial class ConversationService : IConversationService { return !string.IsNullOrWhiteSpace(_conversationId); } + + + private int? GetAgentMessageCount() + { + var db = _services.GetRequiredService(); + var routingCtx = _services.GetRequiredService(); + + if (string.IsNullOrEmpty(routingCtx.EntryAgentId)) return null; + + var agent = db.GetAgent(routingCtx.EntryAgentId, basicsOnly: true); + return agent?.MaxMessageCount; + } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index d2c519eb..ad2b19b8 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -16,7 +16,7 @@ public class BotSharpDbContext : Database, IBotSharpRepository #endregion #region Agent - public Agent GetAgent(string agentId) + public Agent GetAgent(string agentId, bool basicsOnly = false) => throw new NotImplementedException(); public List GetAgents(AgentFilter filter) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index 07915917..a838870f 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -57,6 +57,9 @@ namespace BotSharp.Core.Repository case AgentField.Utility: UpdateAgentUtilities(agent.Id, agent.MergeUtility, agent.Utilities); break; + case AgentField.MaxMessageCount: + UpdateAgentMaxMessageCount(agent.Id, agent.MaxMessageCount); + break; case AgentField.All: UpdateAgentAllFields(agent); break; @@ -283,6 +286,17 @@ namespace BotSharp.Core.Repository File.WriteAllText(agentFile, json); } + private void UpdateAgentMaxMessageCount(string agentId, int? maxMessageCount) + { + var (agent, agentFile) = GetAgentFromFile(agentId); + if (agent == null) return; + + agent.MaxMessageCount = maxMessageCount; + agent.UpdatedDateTime = DateTime.UtcNow; + var json = JsonSerializer.Serialize(agent, _options); + File.WriteAllText(agentFile, json); + } + private void UpdateAgentAllFields(Agent inputAgent) { var (agent, agentFile) = GetAgentFromFile(inputAgent.Id); @@ -298,6 +312,7 @@ namespace BotSharp.Core.Repository agent.Utilities = inputAgent.Utilities; agent.RoutingRules = inputAgent.RoutingRules; agent.LlmConfig = inputAgent.LlmConfig; + agent.MaxMessageCount = inputAgent.MaxMessageCount; agent.UpdatedDateTime = DateTime.UtcNow; var json = JsonSerializer.Serialize(agent, _options); File.WriteAllText(agentFile, json); @@ -329,7 +344,7 @@ namespace BotSharp.Core.Repository return responses; } - public Agent? GetAgent(string agentId) + public Agent? GetAgent(string agentId, bool basicsOnly = false) { var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir); var dir = Directory.GetDirectories(agentDir).FirstOrDefault(x => x.Split(Path.DirectorySeparatorChar).Last() == agentId); @@ -342,6 +357,8 @@ namespace BotSharp.Core.Repository var record = JsonSerializer.Deserialize(json, _options); if (record == null) return null; + if (basicsOnly) return record; + var (defaultInstruction, channelInstructions) = FetchInstructions(dir); var functions = FetchFunctions(dir); var samples = FetchSamples(dir); diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs index 32b3fdce..44c257df 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs @@ -51,6 +51,8 @@ public class AgentCreationModel public bool MergeUtility { get; set; } + public int? MaxMessageCount { get; set; } + public List Utilities { get; set; } = new(); public List RoutingRules { get; set; } = new(); public AgentLlmConfig? LlmConfig { get; set; } @@ -72,6 +74,7 @@ public class AgentCreationModel Type = Type, Disabled = Disabled, MergeUtility = MergeUtility, + MaxMessageCount = MaxMessageCount, Profiles = Profiles, RoutingRules = RoutingRules?.Select(x => RoutingRuleUpdateModel.ToDomainElement(x))?.ToList() ?? new List(), LlmConfig = LlmConfig diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs index 30308c9f..141f0662 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs @@ -57,6 +57,9 @@ public class AgentUpdateModel public bool Disabled { get; set; } + [JsonPropertyName("max_message_count")] + public int? MaxMessageCount { get; set; } + /// /// Profile by channel /// @@ -77,6 +80,7 @@ public class AgentUpdateModel IsPublic = IsPublic, Disabled = Disabled, MergeUtility = MergeUtility, + MaxMessageCount = MaxMessageCount, Type = Type, Profiles = Profiles ?? new List(), RoutingRules = RoutingRules?.Select(x => RoutingRuleUpdateModel.ToDomainElement(x))?.ToList() ?? new List(), diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index 34b1bcc1..36a8900f 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -46,6 +46,10 @@ public class AgentViewModel [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public AgentLlmConfig? LlmConfig { get; set; } + [JsonPropertyName("max_message_count")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? MaxMessageCount { get; set; } + public PluginDef Plugin { get; set; } public IEnumerable? Actions { get; set; } @@ -75,6 +79,7 @@ public class AgentViewModel Disabled = agent.Disabled, MergeUtility = agent.MergeUtility, IconUrl = agent.IconUrl, + MaxMessageCount = agent.MaxMessageCount, Profiles = agent.Profiles ?? new List(), RoutingRules = agent.RoutingRules, LlmConfig = agent.LlmConfig, diff --git a/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadImageFn.cs b/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadImageFn.cs index f2e79b21..e2c4f8c2 100644 --- a/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadImageFn.cs +++ b/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadImageFn.cs @@ -23,8 +23,9 @@ public class ReadImageFn : IFunctionCallback var agentService = _services.GetRequiredService(); var wholeDialogs = conv.GetDialogHistory(); - var dialogs = AssembleFiles(conv.ConversationId, wholeDialogs); - var agent = await agentService.LoadAgent(BuiltInAgentId.UtilityAssistant); + var dialogs = AssembleFiles(conv.ConversationId, args?.ImageUrls, wholeDialogs); + var agentId = !string.IsNullOrWhiteSpace(message.CurrentAgentId) ? message.CurrentAgentId : BuiltInAgentId.UtilityAssistant; + var agent = await agentService.LoadAgent(agentId); var fileAgent = new Agent { Id = agent?.Id ?? Guid.Empty.ToString(), @@ -38,7 +39,7 @@ public class ReadImageFn : IFunctionCallback return true; } - private List AssembleFiles(string conversationId, List dialogs) + private List AssembleFiles(string conversationId, IEnumerable? imageUrls, List dialogs) { if (dialogs.IsNullOrEmpty()) { @@ -66,6 +67,18 @@ public class ReadImageFn : IFunctionCallback }).ToList(); } + if (!imageUrls.IsNullOrEmpty()) + { + var lastDialog = dialogs.Last(); + var files = lastDialog.Files ?? []; + var addnFiles = imageUrls.Select(x => x?.Trim()) + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Select(x => new BotSharpFile { FileUrl = x }).ToList(); + + files.AddRange(addnFiles); + lastDialog.Files = files; + } + return dialogs; } diff --git a/src/Plugins/BotSharp.Plugin.FileHandler/LlmContexts/LlmContextIn.cs b/src/Plugins/BotSharp.Plugin.FileHandler/LlmContexts/LlmContextIn.cs index 9917cb74..a35e83bd 100644 --- a/src/Plugins/BotSharp.Plugin.FileHandler/LlmContexts/LlmContextIn.cs +++ b/src/Plugins/BotSharp.Plugin.FileHandler/LlmContexts/LlmContextIn.cs @@ -11,4 +11,12 @@ public class LlmContextIn [JsonPropertyName("image_description")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ImageDescription { get; set; } + + //[JsonPropertyName("image_url")] + //[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + //public string? ImageUrl { get; set; } + + [JsonPropertyName("image_urls")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IEnumerable? ImageUrls { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.FileHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-file-read_image.json b/src/Plugins/BotSharp.Plugin.FileHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-file-read_image.json index a0f269a0..b8dda8b1 100644 --- a/src/Plugins/BotSharp.Plugin.FileHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-file-read_image.json +++ b/src/Plugins/BotSharp.Plugin.FileHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-file-read_image.json @@ -7,6 +7,14 @@ "user_request": { "type": "string", "description": "The request posted by user, which is related to analyzing requested images. User can request for multiple images to process at one time." + }, + "image_urls": { + "type": "array", + "description": "The image, photo or picture urls that user requests for analysis. They typically start with 'http' or 'https'. If user doesn't include any url, then leave this array empty. Please remove any duplicated urls", + "items": { + "type": "string", + "description": "The image, photo or picture url that user requests for analysis. It typically starts with http or https." + } } }, "required": [ "user_request" ] diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs index 76fee1b3..3a775025 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs @@ -11,6 +11,7 @@ public class AgentDocument : MongoBase public bool IsPublic { get; set; } public bool Disabled { get; set; } public bool MergeUtility { get; set; } + public int? MaxMessageCount { get; set; } public List ChannelInstructions { get; set; } public List Templates { get; set; } public List Functions { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 7f216b6d..9bd72e3e 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -9,7 +9,7 @@ public partial class MongoRepository { public void UpdateAgent(Agent agent, AgentField field) { - if (agent == null || string.IsNullOrEmpty(agent.Id)) return; + if (agent == null || string.IsNullOrWhiteSpace(agent.Id)) return; switch (field) { @@ -58,6 +58,9 @@ public partial class MongoRepository case AgentField.Utility: UpdateAgentUtilities(agent.Id, agent.MergeUtility, agent.Utilities); break; + case AgentField.MaxMessageCount: + UpdateAgentMaxMessageCount(agent.Id, agent.MaxMessageCount); + break; case AgentField.All: UpdateAgentAllFields(agent); break; @@ -158,10 +161,8 @@ public partial class MongoRepository private void UpdateAgentInstructions(string agentId, string instruction, List? channelInstructions) { - if (string.IsNullOrWhiteSpace(agentId)) return; - var instructionElements = channelInstructions?.Select(x => ChannelInstructionMongoElement.ToMongoElement(x))? - .ToList() ?? new List(); + .ToList() ?? []; var filter = Builders.Filter.Eq(x => x.Id, agentId); var update = Builders.Update @@ -200,7 +201,7 @@ public partial class MongoRepository private void UpdateAgentResponses(string agentId, List responses) { - if (responses == null) return; + if (responses == null || string.IsNullOrWhiteSpace(agentId)) return; var responsesToUpdate = responses.Select(r => AgentResponseMongoElement.ToMongoElement(r)).ToList(); var filter = Builders.Filter.Eq(x => x.Id, agentId); @@ -249,6 +250,16 @@ public partial class MongoRepository _dc.Agents.UpdateOne(filter, update); } + private void UpdateAgentMaxMessageCount(string agentId, int? maxMessageCount) + { + var filter = Builders.Filter.Eq(x => x.Id, agentId); + var update = Builders.Update + .Set(x => x.MaxMessageCount, maxMessageCount) + .Set(x => x.UpdatedTime, DateTime.UtcNow); + + _dc.Agents.UpdateOne(filter, update); + } + private void UpdateAgentAllFields(Agent agent) { var filter = Builders.Filter.Eq(x => x.Id, agent.Id); @@ -258,6 +269,7 @@ public partial class MongoRepository .Set(x => x.Disabled, agent.Disabled) .Set(x => x.MergeUtility, agent.MergeUtility) .Set(x => x.Type, agent.Type) + .Set(x => x.MaxMessageCount, agent.MaxMessageCount) .Set(x => x.Profiles, agent.Profiles) .Set(x => x.RoutingRules, agent.RoutingRules.Select(r => RoutingRuleMongoElement.ToMongoElement(r)).ToList()) .Set(x => x.Instruction, agent.Instruction) @@ -277,7 +289,7 @@ public partial class MongoRepository #endregion - public Agent? GetAgent(string agentId) + public Agent? GetAgent(string agentId, bool basicsOnly = false) { var agent = _dc.Agents.AsQueryable().FirstOrDefault(x => x.Id == agentId); if (agent == null) return null; @@ -420,6 +432,7 @@ public partial class MongoRepository InheritAgentId = x.InheritAgentId, Disabled = x.Disabled, MergeUtility = x.MergeUtility, + MaxMessageCount = x.MaxMessageCount, Profiles = x.Profiles, RoutingRules = x.RoutingRules?.Select(r => RoutingRuleMongoElement.ToMongoElement(r))?.ToList() ?? [], LlmConfig = AgentLlmConfigMongoElement.ToMongoElement(x.LlmConfig), @@ -513,6 +526,7 @@ public partial class MongoRepository Type = agentDoc.Type, InheritAgentId = agentDoc.InheritAgentId, Profiles = agentDoc.Profiles, + MaxMessageCount = agentDoc.MaxMessageCount }; } }