diff --git a/BotSharp.sln b/BotSharp.sln index 52b3f9d0..5baf8d10 100644 --- a/BotSharp.sln +++ b/BotSharp.sln @@ -121,6 +121,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Core.SideCar", "sr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.VertexAI", "src\Plugins\BotSharp.Plugin.LangChain\BotSharp.Plugin.VertexAI.csproj", "{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.Crontab", "src\Infrastructure\BotSharp.Core.Crontab\BotSharp.Core.Crontab.csproj", "{F812BAAE-5A7D-4DF7-8E71-70696B51C61F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -489,6 +491,14 @@ Global {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|Any CPU.Build.0 = Release|Any CPU {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|x64.ActiveCfg = Release|Any CPU {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|x64.Build.0 = Release|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Debug|x64.ActiveCfg = Debug|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Debug|x64.Build.0 = Debug|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Release|Any CPU.Build.0 = Release|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Release|x64.ActiveCfg = Release|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -547,6 +557,7 @@ Global {F57F4862-F8D4-44A1-AC12-5C131B5C9785} = {51AFE054-AE99-497D-A593-69BAEFB5106F} {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749} {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B} = {D5293208-2BEF-42FC-A64C-5954F61720BA} + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19} diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs index 53d64a07..455e87ff 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs @@ -10,7 +10,7 @@ public class AgentType /// /// Planning agent /// - public const string Planning = "plan"; + public const string Planning = "planning"; public const string Evaluating = "evaluation"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs index f2f64b6b..ecdda1c7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs @@ -32,11 +32,6 @@ public class BuiltInAgentId /// public const string Learner = "01acc3e5-0af7-49e6-ad7a-a760bd12dc40"; - /// - /// Plan feasible implementation steps for complex problems - /// - public const string Planner = "282a7128-69a1-44b0-878c-a9159b88f3b9"; - /// /// SQL statement generation /// diff --git a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj index 5be126b8..9b31ae6f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj +++ b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) diff --git a/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/CrontabItem.cs b/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/CrontabItem.cs new file mode 100644 index 00000000..a8acdc79 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/CrontabItem.cs @@ -0,0 +1,24 @@ +namespace BotSharp.Abstraction.Crontab.Models; + +public class CrontabItem : ScheduleTaskArgs +{ + [JsonPropertyName("user_id")] + public string UserId { get; set; } = null!; + + [JsonPropertyName("agent_id")] + public string AgentId { get; set; } = null!; + + [JsonPropertyName("conversation_id")] + public string ConversationId { get; set; } = null!; + + [JsonPropertyName("execution_result")] + public string ExecutionResult { get; set; } = null!; + + [JsonPropertyName("created_time")] + public DateTime CreatedTime { get; set; } = DateTime.UtcNow; + + public override string ToString() + { + return $"{Title}: {Description} [AgentId: {AgentId}, UserId: {UserId}]"; + } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/CrontabItemFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/CrontabItemFilter.cs new file mode 100644 index 00000000..7a949dc2 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/CrontabItemFilter.cs @@ -0,0 +1,23 @@ +namespace BotSharp.Abstraction.Crontab.Models; + +public class CrontabItemFilter : Pagination +{ + [JsonPropertyName("user_ids")] + public IEnumerable? UserIds { get; set; } + + [JsonPropertyName("agent_ids")] + public IEnumerable? AgentIds { get; set; } + + [JsonPropertyName("conversation_ids")] + public IEnumerable? ConversationIds { get; set; } + + public CrontabItemFilter() + { + + } + + public static CrontabItemFilter Empty() + { + return new CrontabItemFilter(); + } +} diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Models/ScheduleTaskArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/ScheduleTaskArgs.cs similarity index 63% rename from src/Infrastructure/BotSharp.Core.Crontab/Models/ScheduleTaskArgs.cs rename to src/Infrastructure/BotSharp.Abstraction/Crontab/Models/ScheduleTaskArgs.cs index af1bb23f..df11160a 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/Models/ScheduleTaskArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/ScheduleTaskArgs.cs @@ -1,18 +1,25 @@ -using System.Text.Json.Serialization; - -namespace BotSharp.Core.Crontab.Models; +namespace BotSharp.Abstraction.Crontab.Models; public class ScheduleTaskArgs { [JsonPropertyName("cron_expression")] public string Cron { get; set; } = null!; - [JsonPropertyName("topic")] - public string Topic { get; set; } = null!; + [JsonPropertyName("title")] + public string Title { get; set; } = null!; [JsonPropertyName("description")] public string Description { get; set; } = null!; + [JsonPropertyName("to_do_list")] + public ScheduleTaskItemArgs[] Tasks { get; set; } = []; +} + +public class ScheduleTaskItemArgs +{ + [JsonPropertyName("topic")] + public string Topic { get; set; } = null!; + [JsonPropertyName("script")] public string Script { get; set; } = null!; diff --git a/src/Infrastructure/BotSharp.Abstraction/Planning/ITaskPlanner.cs b/src/Infrastructure/BotSharp.Abstraction/Planning/ITaskPlanner.cs index 67f30fb1..2bd8da83 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Planning/ITaskPlanner.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Planning/ITaskPlanner.cs @@ -8,6 +8,7 @@ namespace BotSharp.Abstraction.Planning; /// public interface ITaskPlanner { + string Name => "Unamed Task Planner"; Task GetNextInstruction(Agent router, string messageId, List dialogs); Task AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs); Task AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs); diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 037e0037..61982cc4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -145,4 +145,10 @@ public interface IBotSharpRepository : IHaveServiceProvider bool DeleteKnolwedgeBaseFileMeta(string collectionName, string vectorStoreProvider, Guid? fileId = null); PagedItems GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, KnowledgeFileFilter filter); #endregion + + #region Crontab + bool UpsertCrontabItem(CrontabItem cron) => throw new NotImplementedException(); + bool DeleteCrontabItem(string conversationId) => throw new NotImplementedException(); + PagedItems GetCrontabItems(CrontabItemFilter filter) => throw new NotImplementedException(); + #endregion } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutableAgent.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutableAgent.cs index 14fd6826..861f3f2f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutableAgent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutableAgent.cs @@ -13,6 +13,9 @@ public class RoutableAgent [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; + [JsonPropertyName("type")] + public string Type { get; set; } = AgentType.Task; + [JsonPropertyName("profiles")] public List Profiles { get; set; } = new List(); diff --git a/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs b/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs index 9a1316fb..84b65573 100644 --- a/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs +++ b/src/Infrastructure/BotSharp.Abstraction/SideCar/IConversationSideCar.cs @@ -9,5 +9,6 @@ public interface IConversationSideCar List GetConversationDialogs(string conversationId); void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint); ConversationBreakpoint? GetConversationBreakpoint(string conversationId); - Task SendMessage(string agentId, string text, PostbackMessageModel? postback = null, List? states = null); + Task SendMessage(string agentId, string text, + PostbackMessageModel? postback = null, List? states = null, List? dialogs = null); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Using.cs b/src/Infrastructure/BotSharp.Abstraction/Using.cs index 9f35c1c8..592803c2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Using.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Using.cs @@ -17,4 +17,5 @@ global using BotSharp.Abstraction.Translation.Attributes; global using BotSharp.Abstraction.Messaging.Enums; global using BotSharp.Abstraction.Files.Models; global using BotSharp.Abstraction.Files.Enums; -global using BotSharp.Abstraction.Knowledges.Models; \ No newline at end of file +global using BotSharp.Abstraction.Knowledges.Models; +global using BotSharp.Abstraction.Crontab.Models; \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Abstraction/ICrontabHook.cs b/src/Infrastructure/BotSharp.Core.Crontab/Abstraction/ICrontabHook.cs index 49ffe54c..1738ef5e 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/Abstraction/ICrontabHook.cs +++ b/src/Infrastructure/BotSharp.Core.Crontab/Abstraction/ICrontabHook.cs @@ -1,5 +1,3 @@ -using BotSharp.Core.Crontab.Models; - namespace BotSharp.Core.Crontab.Abstraction; public interface ICrontabHook diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Abstraction/ICrontabService.cs b/src/Infrastructure/BotSharp.Core.Crontab/Abstraction/ICrontabService.cs index 6a18018d..39c13779 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/Abstraction/ICrontabService.cs +++ b/src/Infrastructure/BotSharp.Core.Crontab/Abstraction/ICrontabService.cs @@ -1,5 +1,3 @@ -using BotSharp.Core.Crontab.Models; - namespace BotSharp.Core.Crontab.Abstraction; public interface ICrontabService diff --git a/src/Infrastructure/BotSharp.Core.Crontab/BotSharp.Core.Crontab.csproj b/src/Infrastructure/BotSharp.Core.Crontab/BotSharp.Core.Crontab.csproj index 0740b6fa..cc072fe6 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/BotSharp.Core.Crontab.csproj +++ b/src/Infrastructure/BotSharp.Core.Crontab/BotSharp.Core.Crontab.csproj @@ -21,7 +21,6 @@ - @@ -29,4 +28,8 @@ + + + + diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Functions/ScheduleTaskFn.cs b/src/Infrastructure/BotSharp.Core.Crontab/Functions/ScheduleTaskFn.cs index 70d3b410..87a1bad6 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/Functions/ScheduleTaskFn.cs +++ b/src/Infrastructure/BotSharp.Core.Crontab/Functions/ScheduleTaskFn.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Routing; using BotSharp.Abstraction.Users; using BotSharp.Core.Crontab.Hooks; @@ -22,16 +23,18 @@ public class ScheduleTaskFn : IFunctionCallback var user = _services.GetRequiredService(); var crontabItem = new CrontabItem { - Topic = args.Topic, + Title = args.Title, Description = args.Description, Cron = args.Cron, - Script = args.Script, - Language = args.Language, UserId = user.Id, AgentId = routing.EntryAgentId, - ConversationId = routing.ConversationId + ConversationId = routing.ConversationId, + Tasks = args.Tasks, }; + var db = _services.GetRequiredService(); + // var ret = db.UpsertCrontabItem(crontabItem); + return true; } } diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Models/CrontabItem.cs b/src/Infrastructure/BotSharp.Core.Crontab/Models/CrontabItem.cs deleted file mode 100644 index c3fab2e8..00000000 --- a/src/Infrastructure/BotSharp.Core.Crontab/Models/CrontabItem.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace BotSharp.Core.Crontab.Models; - -public class CrontabItem : ScheduleTaskArgs -{ - public string UserId { get; set; } = null!; - public string AgentId { get; set; } = null!; - public string ConversationId { get; set; } = null!; - public string ExecutionResult { get; set; } = null!; - - public override string ToString() - { - return $"{Topic}: {Description} [AgentId: {AgentId}, UserId: {UserId}]"; - } -} diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs index d09f9260..e5e68cc0 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs +++ b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs @@ -14,29 +14,7 @@ limitations under the License. ******************************************************************************/ -using BotSharp.Abstraction.Agents.Enums; -using BotSharp.Abstraction.Conversations; -using BotSharp.Abstraction.Routing; -using BotSharp.Core.Crontab.Models; -using BotSharp.Core.Infrastructures; - - -/***************************************************************************** - Copyright 2024 Written by Haiping Chen. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -******************************************************************************/ - +using BotSharp.Abstraction.Repositories; using Microsoft.Extensions.Logging; namespace BotSharp.Core.Crontab.Services; @@ -58,24 +36,9 @@ public class CrontabService : ICrontabService public async Task> GetCrontable() { - var convService = _services.GetRequiredService(); - var conv = await convService.GetConversation("73a9ee27-d597-4739-958f-3bd79760ac8e"); - if (conv == null || !conv.States.ContainsKey("cron_expression")) - { - return []; - } - - return - [ - new CrontabItem - { - Topic = conv.States["topic"], - Cron = conv.States["cron_expression"], - Description = conv.States["description"], - Script = conv.States["script"], - Language = conv.States["language"] - } - ]; + var repo = _services.GetRequiredService(); + var crontable = repo.GetCrontabItems(CrontabItemFilter.Empty()); + return crontable.Items.ToList(); } public async Task ScheduledTimeArrived(CrontabItem item) diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs index f29bd069..a7db1220 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs +++ b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs @@ -46,24 +46,32 @@ public class CrontabWatcher : BackgroundService var crons = await cron.GetCrontable(); foreach (var item in crons) { - var schedule = CrontabSchedule.Parse(item.Cron, new CrontabSchedule.ParseOptions + try { - IncludingSeconds = true // Ensure you account for seconds - }); + var schedule = CrontabSchedule.Parse(item.Cron, new CrontabSchedule.ParseOptions + { + IncludingSeconds = true // Ensure you account for seconds + }); - // Get the current time - var currentTime = DateTime.UtcNow; + // Get the current time + var currentTime = DateTime.UtcNow; - // Get the next occurrence from the schedule - var nextOccurrence = schedule.GetNextOccurrence(currentTime.AddSeconds(-1)); + // Get the next occurrence from the schedule + var nextOccurrence = schedule.GetNextOccurrence(currentTime.AddSeconds(-1)); - // Check if the current time matches the schedule - bool matches = currentTime >= nextOccurrence && currentTime < nextOccurrence.AddSeconds(1); + // Check if the current time matches the schedule + bool matches = currentTime >= nextOccurrence && currentTime < nextOccurrence.AddSeconds(1); - if (matches) + if (matches) + { + _logger.LogDebug($"The current time matches the cron expression {item}"); + cron.ScheduledTimeArrived(item); + } + } + catch (Exception ex) { - _logger.LogDebug($"The current time matches the cron expression {item}"); - cron.ScheduledTimeArrived(item); + _logger.LogWarning($"Error when running cron task ({item.ConversationId}, {item.Title}, {item.Cron}): {ex.Message}\r\n{ex.InnerException}"); + continue; } } } diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Using.cs b/src/Infrastructure/BotSharp.Core.Crontab/Using.cs index 42563238..0420faf7 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/Using.cs +++ b/src/Infrastructure/BotSharp.Core.Crontab/Using.cs @@ -4,10 +4,10 @@ global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using BotSharp.Abstraction.Agents.Enums; +global using BotSharp.Abstraction.Crontab.Models; global using BotSharp.Abstraction.Agents; global using BotSharp.Abstraction.Plugins; global using BotSharp.Abstraction.Conversations.Models; global using BotSharp.Abstraction.Functions; global using BotSharp.Core.Crontab.Services; -global using BotSharp.Core.Crontab.Abstraction; -global using BotSharp.Core.Crontab.Models; +global using BotSharp.Core.Crontab.Abstraction; \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core.Crontab/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-crontab-schedule_task.json b/src/Infrastructure/BotSharp.Core.Crontab/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-crontab-schedule_task.json index 4f866ce1..320f4d71 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-crontab-schedule_task.json +++ b/src/Infrastructure/BotSharp.Core.Crontab/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-crontab-schedule_task.json @@ -8,24 +8,37 @@ "type": "string", "description": "cron expression include seconds" }, - "topic": { + "title": { "type": "string", - "description": "task topic in 1-3 keywords related to business entities" + "description": "task short title" }, "description": { "type": "string", - "description": "task description without cron infromation, should include all key business entities" + "description": "the task summary" }, - "script": { - "type": "string", - "description": "task related script, commands or provided function with parameters" - }, - "language": { - "type": "string", - "enum": ["sql", "function"], - "description": "script programming language" + "to_do_list": { + "type": "array", + "description": "task to do list, should include all key business entities", + "items": { + "type": "object", + "properties": { + "topic": { + "type": "string", + "description": "task topic in 1-3 keywords related to the step" + }, + "script": { + "type": "string", + "description": "task related script, function with parameters or plain text" + }, + "language": { + "type": "string", + "enum": [ "function", "sql", "python", "text" ], + "description": "programming language script, function or plain text" + } + } + } } }, - "required": [ "cron_expression", "topic", "description", "script", "language" ] + "required": [ "cron_expression", "title", "description", "to_do_list" ] } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs index 4ddc3a43..59087311 100644 --- a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs +++ b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs @@ -80,9 +80,9 @@ public class BotSharpConversationSideCar : IConversationSideCar } public async Task SendMessage(string agentId, string text, - PostbackMessageModel? postback = null, List? states = null) + PostbackMessageModel? postback = null, List? states = null, List? dialogs = null) { - BeforeExecute(); + BeforeExecute(dialogs); var response = await InnerExecute(agentId, text, postback, states); AfterExecute(); return response; @@ -114,7 +114,7 @@ public class BotSharpConversationSideCar : IConversationSideCar return response; } - private void BeforeExecute() + private void BeforeExecute(List? dialogs) { enabled = true; var state = _services.GetRequiredService(); @@ -123,8 +123,8 @@ public class BotSharpConversationSideCar : IConversationSideCar var node = new ConversationContext { State = state.GetCurrentState(), - Dialogs = new(), - Breakpoints = new(), + Dialogs = dialogs ?? [], + Breakpoints = [], RecursiveCounter = routing.Context.GetRecursiveCounter(), RoutingStack = routing.Context.GetAgentStack() }; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Crontab.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Crontab.cs new file mode 100644 index 00000000..5d3eb865 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Crontab.cs @@ -0,0 +1,117 @@ +using System.IO; + +namespace BotSharp.Core.Repository; + +public partial class FileRepository +{ + public bool UpsertCrontabItem(CrontabItem cron) + { + if (cron == null || string.IsNullOrWhiteSpace(cron.ConversationId)) + { + return false; + } + + try + { + var baseDir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, cron.ConversationId); + if (!Directory.Exists(baseDir)) + { + return false; + } + + var cronFile = Path.Combine(baseDir, CRON_FILE); + var json = JsonSerializer.Serialize(cron, _options); + File.WriteAllText(cronFile, json); + return true; + } + catch (Exception ex) + { + _logger.LogError($"Error when saving crontab item: {ex.Message}\r\n{ex.InnerException}"); + return false; + } + } + + public bool DeleteCrontabItem(string conversationId) + { + if (string.IsNullOrWhiteSpace(conversationId)) + { + return false; + } + + try + { + var baseDir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversationId); + if (!Directory.Exists(baseDir)) + { + return false; + } + + var cronFile = Path.Combine(baseDir, CRON_FILE); + if (!File.Exists(cronFile)) + { + return false; + } + + File.Delete(cronFile); + return true; + } + catch (Exception ex) + { + _logger.LogError($"Error when deleting crontab item (${conversationId}): {ex.Message}\r\n{ex.InnerException}"); + return false; + } + } + + + public PagedItems GetCrontabItems(CrontabItemFilter filter) + { + + if (filter == null) + { + filter = CrontabItemFilter.Empty(); + } + + var records = new List(); + var baseDir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir); + + if (!Directory.Exists(baseDir)) + { + Directory.CreateDirectory(baseDir); + } + + var totalDirs = Directory.GetDirectories(baseDir); + foreach (var d in totalDirs) + { + var file = Path.Combine(d, CRON_FILE); + if (!File.Exists(file)) continue; + + var json = File.ReadAllText(file); + var record = JsonSerializer.Deserialize(json, _options); + if (record == null) continue; + + var matched = true; + if (filter?.AgentIds != null) + { + matched = matched && filter.AgentIds.Contains(record.AgentId); + } + if (filter?.ConversationIds != null) + { + matched = matched && filter.ConversationIds.Contains(record.ConversationId); + } + if (filter?.UserIds != null) + { + matched = matched && filter.UserIds.Contains(record.UserId); + } + + if (!matched) continue; + + records.Add(record); + } + + return new PagedItems + { + Items = records.OrderByDescending(x => x.CreatedTime).Skip(filter.Offset).Take(filter.Size), + Count = records.Count(), + }; + } +} diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index 57fbcb91..6a718d65 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -55,6 +55,8 @@ public partial class FileRepository : IBotSharpRepository private const string PLUGIN_CONFIG_FILE = "config.json"; private const string STATS_FILE = "stats.json"; + private const string CRON_FILE = "cron.json"; + public FileRepository( IServiceProvider services, BotSharpDatabaseSettings dbSettings, @@ -88,7 +90,6 @@ public partial class FileRepository : IBotSharpRepository private List _agents = new List(); private List _roleAgents = new List(); private List _userAgents = new List(); - private List _conversations = new List(); private PluginConfig? _pluginConfig = null; private IQueryable Roles diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs index a847f078..f83afedf 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Functions; using BotSharp.Abstraction.Routing.Enums; using BotSharp.Abstraction.Routing.Settings; @@ -26,6 +25,14 @@ public class RoutingAgentHook : AgentHookBase var routing = _services.GetRequiredService(); var agents = routing.GetRoutableAgents(_agent.Profiles); + // 过滤 Planner + var planningRule = _agent.RoutingRules.FirstOrDefault(x => x.Type == "planner"); + if (planningRule != null) + { + var planners = agents.Where(x => x.Type == AgentType.Planning && x.Name != planningRule.Field).ToArray(); + agents = agents.Except(planners).ToArray(); + } + // Postprocess agent required fields, remove it if the states exists var states = _services.GetRequiredService(); foreach (var agent in agents) diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs index 0c825a05..e7d9b49e 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs @@ -24,6 +24,7 @@ namespace BotSharp.Core.Routing.Reasoning; /// public class HFReasoner : IRoutingReasoner { + public string Name => "Human-Feedback Reasoner"; private readonly IServiceProvider _services; private readonly ILogger _logger; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs index eb58d3e3..7e6d2624 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs @@ -29,7 +29,7 @@ namespace BotSharp.Core.Routing.Reasoning; /// public class OneStepForwardReasoner : IRoutingReasoner { - public string Name => "one-step-forward"; + public string Name => "One-Step-Forward-Reasoner"; private readonly IServiceProvider _services; private readonly ILogger _logger; diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs index f5c2a9d6..c5317ba4 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs @@ -35,8 +35,7 @@ public class RoutingPlugin : IBotSharpPlugin services.AddScoped(); services.AddScoped(); - services.AddScoped(); - + services.AddScoped(); } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs index 18f330a3..b7dc6cc3 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs @@ -102,7 +102,7 @@ public partial class RoutingService return _services.GetServices().First(x => x.Name == "Naive Reasoner"); } - var reasoner = _services.GetServices().FirstOrDefault(x => x.GetType().Name.EndsWith(rule.Field)); + var reasoner = _services.GetServices().FirstOrDefault(x => x.Name == rule.Field); if (reasoner == null) { diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index d3eb2b1c..ef32f018 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -82,11 +82,10 @@ public partial class RoutingService : IRoutingService var filter = new AgentFilter { - Disabled = false, - Type = AgentType.Task + Disabled = false }; var agents = db.GetAgents(filter); - var records = agents.SelectMany(x => + var records = agents.Where(x => x.Type == AgentType.Task || x.Type == AgentType.Planning).SelectMany(x => { x.RoutingRules.ForEach(r => { @@ -108,16 +107,16 @@ public partial class RoutingService : IRoutingService var filter = new AgentFilter { - Disabled = false, - Type = AgentType.Task + Disabled = false }; var agents = db.GetAgents(filter); - var routableAgents = agents.Select(x => new RoutableAgent + var routableAgents = agents.Where(x => x.Type == AgentType.Task || x.Type == AgentType.Planning).Select(x => new RoutableAgent { AgentId = x.Id, Description = x.Description, Name = x.Name, + Type = x.Type, Profiles = x.Profiles, RequiredFields = x.RoutingRules .Where(p => p.Required) diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index f68cba9b..4a74f1d0 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -14,7 +14,7 @@ global using BotSharp.Abstraction.Routing; global using BotSharp.Abstraction.Plugins; global using BotSharp.Abstraction.Agents; global using BotSharp.Abstraction.Conversations; -global using BotSharp.Abstraction.Knowledges; +global using BotSharp.Abstraction.Crontab.Models; global using BotSharp.Abstraction.Users; global using BotSharp.Abstraction.Roles; global using BotSharp.Abstraction.Roles.Models; diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json index 0a82ae0a..ae99df17 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/agent.json @@ -12,7 +12,11 @@ "routingRules": [ { "type": "reasoner", - "field": "HFReasoner" + "field": "One-Step-Forward-Reasoner" + }, + { + "type": "planner", + "field": "Sequential-Planner" } ] } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs index 4b475e73..4f7ef3d2 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs @@ -1,5 +1,5 @@ +using BotSharp.Abstraction.Crontab.Models; using BotSharp.Core.Crontab.Abstraction; -using BotSharp.Core.Crontab.Models; using Microsoft.AspNetCore.SignalR; namespace BotSharp.Plugin.ChatHub.Hooks; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/CrontabItemDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/CrontabItemDocument.cs new file mode 100644 index 00000000..10e633d7 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/CrontabItemDocument.cs @@ -0,0 +1,48 @@ +using BotSharp.Abstraction.Crontab.Models; + +namespace BotSharp.Plugin.MongoStorage.Collections; + +public class CrontabItemDocument : MongoBase +{ + public string UserId { get; set; } + public string AgentId { get; set; } + public string ConversationId { get; set; } + public string ExecutionResult { get; set; } + public string Cron { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public IEnumerable Tasks { get; set; } = []; + public DateTime CreatedTime { get; set; } = DateTime.UtcNow; + + public static CrontabItem ToDomainModel(CrontabItemDocument item) + { + return new CrontabItem + { + UserId = item.UserId, + AgentId = item.AgentId, + ConversationId = item.ConversationId, + ExecutionResult = item.ExecutionResult, + Cron = item.Cron, + Title = item.Title, + Description = item.Description, + Tasks = item.Tasks?.Select(x => CronTaskMongoElement.ToDomainElement(x))?.ToArray() ?? [], + CreatedTime = item.CreatedTime + }; + } + + public static CrontabItemDocument ToMongoModel(CrontabItem item) + { + return new CrontabItemDocument + { + UserId = item.UserId, + AgentId = item.AgentId, + ConversationId = item.ConversationId, + ExecutionResult = item.ExecutionResult, + Cron = item.Cron, + Title = item.Title, + Description = item.Description, + Tasks = item.Tasks?.Select(x => CronTaskMongoElement.ToMongoElement(x))?.ToList() ?? [], + CreatedTime = item.CreatedTime + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/CronTaskMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/CronTaskMongoElement.cs new file mode 100644 index 00000000..099286b8 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/CronTaskMongoElement.cs @@ -0,0 +1,30 @@ +using BotSharp.Abstraction.Crontab.Models; + +namespace BotSharp.Plugin.MongoStorage.Models; + +public class CronTaskMongoElement +{ + public string Topic { get; set; } + public string Script { get; set; } + public string Language { get; set; } + + public static CronTaskMongoElement ToMongoElement(ScheduleTaskItemArgs model) + { + return new CronTaskMongoElement + { + Topic = model.Topic, + Script = model.Script, + Language = model.Language + }; + } + + public static ScheduleTaskItemArgs ToDomainElement(CronTaskMongoElement model) + { + return new ScheduleTaskItemArgs + { + Topic = model.Topic, + Script = model.Script, + Language = model.Language + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs index b91c35fe..89eb18ad 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs @@ -165,4 +165,7 @@ public class MongoDbContext public IMongoCollection RoleAgents => Database.GetCollection($"{_collectionPrefix}_RoleAgents"); + + public IMongoCollection CrontabItems + => Database.GetCollection($"{_collectionPrefix}_CronTabItems"); } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index ccbfc1af..cdd78ae2 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -56,6 +56,7 @@ public partial class MongoRepository var filterPromptLog = Builders.Filter.In(x => x.ConversationId, conversationIds); var filterContentLog = Builders.Filter.In(x => x.ConversationId, conversationIds); var filterStateLog = Builders.Filter.In(x => x.ConversationId, conversationIds); + var conbTabItems = Builders.Filter.In(x => x.ConversationId, conversationIds); var exeLogDeleted = _dc.ExectionLogs.DeleteMany(filterExeLog); var promptLogDeleted = _dc.LlmCompletionLogs.DeleteMany(filterPromptLog); @@ -63,11 +64,13 @@ public partial class MongoRepository var stateLogDeleted = _dc.StateLogs.DeleteMany(filterStateLog); var statesDeleted = _dc.ConversationStates.DeleteMany(filterSates); var dialogDeleted = _dc.ConversationDialogs.DeleteMany(filterDialog); + var cronDeleted = _dc.CrontabItems.DeleteMany(conbTabItems); var convDeleted = _dc.Conversations.DeleteMany(filterConv); return convDeleted.DeletedCount > 0 || dialogDeleted.DeletedCount > 0 || statesDeleted.DeletedCount > 0 || exeLogDeleted.DeletedCount > 0 || promptLogDeleted.DeletedCount > 0 - || contentLogDeleted.DeletedCount > 0 || stateLogDeleted.DeletedCount > 0; + || contentLogDeleted.DeletedCount > 0 || stateLogDeleted.DeletedCount > 0 + || convDeleted.DeletedCount > 0; } [SideCar] diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Crontab.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Crontab.cs new file mode 100644 index 00000000..a40fc827 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Crontab.cs @@ -0,0 +1,87 @@ +using BotSharp.Abstraction.Crontab.Models; +using Microsoft.Extensions.Logging; + +namespace BotSharp.Plugin.MongoStorage.Repository; + +public partial class MongoRepository +{ + public bool UpsertCrontabItem(CrontabItem item) + { + if (item == null || string.IsNullOrWhiteSpace(item.ConversationId)) + { + return false; + } + + try + { + var cronDoc = CrontabItemDocument.ToMongoModel(item); + cronDoc.Id = Guid.NewGuid().ToString(); + + var filter = Builders.Filter.Eq(x => x.ConversationId, item.ConversationId); + var result = _dc.CrontabItems.ReplaceOne(filter, cronDoc, new ReplaceOptions + { + IsUpsert = true + }); + return true; + } + catch (Exception ex) + { + _logger.LogError($"Error when saving crontab item: {ex.Message}\r\n{ex.InnerException}"); + return false; + } + } + + + public bool DeleteCrontabItem(string conversationId) + { + if (string.IsNullOrWhiteSpace(conversationId)) + { + return false; + } + + var filter = Builders.Filter.Eq(x => x.ConversationId, conversationId); + var result = _dc.CrontabItems.DeleteMany(filter); + return result.DeletedCount > 0; + } + + + public PagedItems GetCrontabItems(CrontabItemFilter filter) + { + if (filter == null) + { + filter = CrontabItemFilter.Empty(); + } + + var cronBuilder = Builders.Filter; + var cronFilters = new List>() { cronBuilder.Empty }; + + // Filter cron + if (filter?.AgentIds != null) + { + cronFilters.Add(cronBuilder.In(x => x.AgentId, filter.AgentIds)); + } + if (filter?.ConversationIds != null) + { + cronFilters.Add(cronBuilder.In(x => x.ConversationId, filter.ConversationIds)); + } + if (filter?.UserIds != null) + { + cronFilters.Add(cronBuilder.In(x => x.UserId, filter.UserIds)); + } + + // Sort and paginate + var filterDef = cronBuilder.And(cronFilters); + var sortDef = Builders.Sort.Descending(x => x.CreatedTime); + + var cronDocs = _dc.CrontabItems.Find(filterDef).Sort(sortDef).Skip(filter.Offset).Limit(filter.Size).ToList(); + var count = _dc.CrontabItems.CountDocuments(filterDef); + + var crontabItems = cronDocs.Select(x => CrontabItemDocument.ToDomainModel(x)).ToList(); + + return new PagedItems + { + Items = crontabItems, + Count = (int)count + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.Planner/BotSharp.Plugin.Planner.csproj b/src/Plugins/BotSharp.Plugin.Planner/BotSharp.Plugin.Planner.csproj index fb183c77..f80327cc 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/BotSharp.Plugin.Planner.csproj +++ b/src/Plugins/BotSharp.Plugin.Planner/BotSharp.Plugin.Planner.csproj @@ -19,6 +19,8 @@ + + @@ -52,6 +54,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.Planner/Enums/PlannerAgentId.cs b/src/Plugins/BotSharp.Plugin.Planner/Enums/PlannerAgentId.cs new file mode 100644 index 00000000..0776d7f1 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Planner/Enums/PlannerAgentId.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Http; + +namespace BotSharp.Plugin.Planner.Enums; + +public class PlannerAgentId +{ + public const string TwoStagePlanner = "282a7128-69a1-44b0-878c-a9159b88f3b9"; + public const string SequentialPlanner = "3e75e818-a139-48a8-9e22-4662548c13a3"; +} diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs index 84378158..fa1b7166 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs @@ -49,8 +49,8 @@ public class PrimaryStagePlanFn : IFunctionCallback var prompt = await GetFirstStagePlanPrompt(message, task.Requirements, knowledges); var plannerAgent = new Agent { - Id = BuiltInAgentId.Planner, - Name = "FirstStagePlanner", + Id = message.CurrentAgentId, + Name = Name, Instruction = prompt, TemplateDict = new Dictionary(), LlmConfig = currentAgent.LlmConfig @@ -70,7 +70,7 @@ public class PrimaryStagePlanFn : IFunctionCallback var render = _services.GetRequiredService(); var knowledgeHooks = _services.GetServices(); - var agent = await agentService.GetAgent(BuiltInAgentId.Planner); + var agent = await agentService.GetAgent(PlannerAgentId.TwoStagePlanner); var template = agent.Templates.FirstOrDefault(x => x.Name == "two_stage.1st.plan")?.Content ?? string.Empty; var responseFormat = JsonSerializer.Serialize(new FirstStagePlan{}); diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs index be780f51..5044139c 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs @@ -53,8 +53,8 @@ public class SecondaryStagePlanFn : IFunctionCallback var plannerAgent = new Agent { - Id = BuiltInAgentId.Planner, - Name = "SecondStagePlanner", + Id = PlannerAgentId.TwoStagePlanner, + Name = Name, Instruction = prompt, TemplateDict = new Dictionary(), LlmConfig = currentAgent.LlmConfig diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs index 2cd16ae9..cfc306f4 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs @@ -1,8 +1,5 @@ -using BotSharp.Abstraction.Planning; using BotSharp.Plugin.Planner.TwoStaging; using BotSharp.Plugin.Planner.TwoStaging.Models; -using static System.Net.Mime.MediaTypeNames; -using System.Text.RegularExpressions; namespace BotSharp.Plugin.Planner.Functions; @@ -62,8 +59,8 @@ public class SummaryPlanFn : IFunctionCallback var plannerAgent = new Agent { - Id = BuiltInAgentId.Planner, - Name = "SummaryPlanner", + Id = PlannerAgentId.TwoStagePlanner, + Name = Name, Instruction = prompt, LlmConfig = currentAgent.LlmConfig }; @@ -93,7 +90,7 @@ public class SummaryPlanFn : IFunctionCallback var render = _services.GetRequiredService(); var knowledgeHooks = _services.GetServices(); - var agent = await agentService.GetAgent(BuiltInAgentId.Planner); + var agent = await agentService.GetAgent(PlannerAgentId.TwoStagePlanner); var template = agent.Templates.FirstOrDefault(x => x.Name == "two_stage.summarize")?.Content ?? string.Empty; var additionalRequirements = new List(); diff --git a/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs b/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs index d4bd1594..8c6f2635 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs @@ -2,7 +2,7 @@ namespace BotSharp.Plugin.Planner.Hooks; public class PlannerAgentHook : AgentHookBase { - public override string SelfId => BuiltInAgentId.Planner; + public override string SelfId => PlannerAgentId.TwoStagePlanner; public PlannerAgentHook(IServiceProvider services, AgentSettings settings) : base(services, settings) @@ -19,7 +19,7 @@ public class PlannerAgentHook : AgentHookBase { var k = hook.GetGlobalKnowledges(new RoleDialogModel(AgentRole.User, template) { - CurrentAgentId = BuiltInAgentId.Planner + CurrentAgentId = PlannerAgentId.TwoStagePlanner }).Result; Knowledges.AddRange(k); } diff --git a/src/Plugins/BotSharp.Plugin.Planner/PlannerPlugin.cs b/src/Plugins/BotSharp.Plugin.Planner/PlannerPlugin.cs index 17d84c1c..b139cde2 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/PlannerPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/PlannerPlugin.cs @@ -1,4 +1,4 @@ -using BotSharp.Abstraction.Planning; +using BotSharp.Plugin.Planner.Sequential; using BotSharp.Plugin.Planner.TwoStaging; namespace BotSharp.Plugin.Planner; @@ -13,10 +13,15 @@ public class PlannerPlugin : IBotSharpPlugin public string Description => "Provide AI with different planning approaches to improve AI's ability to solve complex problems."; public string IconUrl => "https://e7.pngegg.com/pngimages/775/350/png-clipart-action-plan-computer-icons-plan-miscellaneous-text-thumbnail.png"; - public string[] AgentIds => [ BuiltInAgentId.Planner ]; + public string[] AgentIds => + [ + PlannerAgentId.TwoStagePlanner, + PlannerAgentId.SequentialPlanner + ]; public void RegisterDI(IServiceCollection services, IConfiguration config) { + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/SequentialReasoner.cs b/src/Plugins/BotSharp.Plugin.Planner/Sequential/SequentialPlanner.cs similarity index 91% rename from src/Infrastructure/BotSharp.Core/Routing/Reasoning/SequentialReasoner.cs rename to src/Plugins/BotSharp.Plugin.Planner/Sequential/SequentialPlanner.cs index 77f069fc..043c7380 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/SequentialReasoner.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Sequential/SequentialPlanner.cs @@ -14,26 +14,23 @@ limitations under the License. ******************************************************************************/ -using BotSharp.Abstraction.MLTasks; -using BotSharp.Abstraction.Routing.Models; -using BotSharp.Abstraction.Routing.Reasoning; -using BotSharp.Abstraction.Templating; - -namespace BotSharp.Core.Routing.Reasoning; +namespace BotSharp.Plugin.Planner.Sequential; /// -/// Sequential tasks focused reasoning approach +/// A Sequential Planner for a large language model (LLM) is a framework or methodology to execute some list tasks in a predefined order by the user, +/// LLM can follow to produce an organized and coherent output. +/// Sequential Planners are useful for tasks that involve multiple stages of reasoning, information retrieval, or interdependent subtasks. /// -public class SequentialReasoner : IRoutingReasoner +public class SequentialPlanner : ITaskPlanner { private readonly IServiceProvider _services; private readonly ILogger _logger; - + public string Name => "Sequential-Planner"; public bool HideDialogContext => true; public int MaxLoopCount => 100; private FunctionCallFromLlm _lastInst; - public SequentialReasoner(IServiceProvider services, ILogger logger) + public SequentialPlanner(IServiceProvider services, ILogger logger) { _services = services; _logger = logger; @@ -91,7 +88,7 @@ public class SequentialReasoner : IRoutingReasoner { new RoleDialogModel(AgentRole.User, next) { - FunctionName = nameof(SequentialReasoner), + FunctionName = nameof(SequentialPlanner), MessageId = messageId } }; @@ -158,7 +155,7 @@ public class SequentialReasoner : IRoutingReasoner if (message.StopCompletion) { - context.Empty(reason: $"Agent queue is cleared by {nameof(SequentialReasoner)}"); + context.Empty(reason: $"Agent queue is cleared by {nameof(SequentialPlanner)}"); return false; } @@ -204,7 +201,7 @@ public class SequentialReasoner : IRoutingReasoner var response = await completion.GetChatCompletions(new Agent { Id = router.Id, - Name = nameof(SequentialReasoner), + Name = nameof(SequentialPlanner), Instruction = systemPrompt }, dialogs); diff --git a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs index 47b9c03a..6935bfbf 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs @@ -1,13 +1,38 @@ -using BotSharp.Abstraction.Infrastructures.Enums; -using BotSharp.Abstraction.Planning; -using BotSharp.Core.Routing.Reasoning; +/***************************************************************************** + Copyright 2024 Written by Haiping Chen. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +******************************************************************************/ namespace BotSharp.Plugin.Planner.TwoStaging; +/// +/// The Two-Stage Planner approach in large language models(LLMs) is an effective strategy to handle complex tasks by breaking them into manageable steps. +/// This involves creating a high-level plan in the first stage and then drilling down into the details in the second stage. +/// +/// Primary stage: +/// The LLM generates a structured overview or roadmap for solving the problem. +/// The focus here is on creating a broad and logically organized set of actions or categories that guide the overall process. +/// +/// Secondary stage: +/// For each step or component in the high-level plan, the LLM elaborates with specific actions, tools, or techniques. +/// The focus shifts to operationalizing the roadmap. +/// public partial class TwoStageTaskPlanner : ITaskPlanner { private readonly IServiceProvider _services; private readonly ILogger _logger; + public string Name => "Two-Stage-Planner"; public int MaxLoopCount => 10; public TwoStageTaskPlanner(IServiceProvider services, ILogger logger) @@ -23,8 +48,8 @@ public partial class TwoStageTaskPlanner : ITaskPlanner // chat completion var completion = CompletionProvider.GetChatCompletion(_services, - provider: router?.LlmConfig?.Provider, - model: router?.LlmConfig?.Model); + provider: router?.LlmConfig?.Provider, + model: router?.LlmConfig?.Model); // text completion dialogs = new List @@ -36,12 +61,10 @@ public partial class TwoStageTaskPlanner : ITaskPlanner } }; var response = await completion.GetChatCompletions(router, dialogs); - inst = response.Content.JsonContent(); // Fix LLM malformed response ReasonerHelper.FixMalformedResponse(_services, inst); - return inst; } @@ -99,7 +122,7 @@ public partial class TwoStageTaskPlanner : ITaskPlanner private async Task GetNextStepPrompt(Agent router) { var agentService = _services.GetRequiredService(); - var planner = await agentService.LoadAgent(BuiltInAgentId.Planner); + var planner = await agentService.LoadAgent(PlannerAgentId.TwoStagePlanner); var template = planner.Templates.First(x => x.Name == "two_stage.next").Content; var states = _services.GetRequiredService(); var render = _services.GetRequiredService(); diff --git a/src/Plugins/BotSharp.Plugin.Planner/Using.cs b/src/Plugins/BotSharp.Plugin.Planner/Using.cs index 1e6737df..50db1596 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Using.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Using.cs @@ -18,19 +18,19 @@ global using BotSharp.Abstraction.Conversations; global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.Repositories; global using BotSharp.Abstraction.Utilities; - global using BotSharp.Abstraction.Conversations.Models; global using BotSharp.Abstraction.Functions; global using BotSharp.Abstraction.Routing; global using BotSharp.Abstraction.Templating; - global using BotSharp.Abstraction.Knowledges; global using BotSharp.Abstraction.Knowledges.Settings; -global using BotSharp.Abstraction.Knowledges.Enums; -global using BotSharp.Abstraction.VectorStorage.Models; -global using BotSharp.Abstraction.VectorStorage.Extensions; +global using BotSharp.Abstraction.Infrastructures.Enums; +global using BotSharp.Abstraction.Planning; +global using BotSharp.Abstraction.MLTasks; +global using BotSharp.Abstraction.Routing.Models; + +global using BotSharp.Core.Infrastructures; +global using BotSharp.Core.Routing.Reasoning; global using BotSharp.Plugin.Planner.Hooks; global using BotSharp.Plugin.Planner.Enums; - -global using BotSharp.Core.Infrastructures; \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json index 1f91baca..7f837847 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json @@ -1,8 +1,8 @@ { "id": "282a7128-69a1-44b0-878c-a9159b88f3b9", - "name": "Planner", - "description": "Plan feasible implementation steps for complex user task request, including generating sql query", - "type": "task", + "name": "Two-Stage-Planner", + "description": "Plan feasible steps for complex user task request, including generating sql query", + "type": "planning", "createdDateTime": "2023-08-27T10:39:00Z", "updatedDateTime": "2023-08-27T14:39:00Z", "iconUrl": "https://e7.pngegg.com/pngimages/775/350/png-clipart-action-plan-computer-icons-plan-miscellaneous-text-thumbnail.png", @@ -13,7 +13,7 @@ "utilities": [], "llmConfig": { "provider": "openai", - "model": "gpt-4o", + "model": "gpt-4o-2024-11-20", "max_recursion_depth": 10 } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.1st.plan.liquid b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.1st.plan.liquid index 190edf8c..306f4214 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.1st.plan.liquid +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.1st.plan.liquid @@ -15,7 +15,8 @@ Thinking process: 4. Output all the subtasks as much detail as possible in JSON: [{{ response_format }}] 5. You can NOT generate the final query before calling function plan_summary. -Note:If the task includes repeat steps,e.g.same steps for multiple elements, only generate a single detailed solution without repeating steps for each elements. You can add multiple items in the input and output args. +Note: +* If the task includes repeat steps,e.g.same steps for multiple elements, only generate a single detailed solution without repeating steps for each elements. {% if global_knowledges != empty -%} ===== diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/3e75e818-a139-48a8-9e22-4662548c13a3/agent.json b/src/Plugins/BotSharp.Plugin.Planner/data/agents/3e75e818-a139-48a8-9e22-4662548c13a3/agent.json new file mode 100644 index 00000000..59231a43 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/3e75e818-a139-48a8-9e22-4662548c13a3/agent.json @@ -0,0 +1,19 @@ +{ + "id": "3e75e818-a139-48a8-9e22-4662548c13a3", + "name": "Sequential-Planner", + "description": "Plan an ordered plan steps to execute some tasks in a predefined order by the user", + "type": "planning", + "createdDateTime": "2023-08-27T10:39:00Z", + "updatedDateTime": "2023-08-27T14:39:00Z", + "iconUrl": "https://www.shutterstock.com/image-vector/sequential-process-glyph-icon-order-260nw-1015790182.jpg", + "disabled": false, + "isPublic": true, + "profiles": [ "planning" ], + "mergeUtility": true, + "utilities": [], + "llmConfig": { + "provider": "openai", + "model": "gpt-4o-2024-11-20", + "max_recursion_depth": 10 + } +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/3e75e818-a139-48a8-9e22-4662548c13a3/instructions/instruction.liquid b/src/Plugins/BotSharp.Plugin.Planner/data/agents/3e75e818-a139-48a8-9e22-4662548c13a3/instructions/instruction.liquid new file mode 100644 index 00000000..25e26106 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/3e75e818-a139-48a8-9e22-4662548c13a3/instructions/instruction.liquid @@ -0,0 +1,15 @@ +You are a Task Planner. you will breakdown user business requirements into excutable sub-steps. +Follow the instructions below to plan and execute tasks: + +1. Look at all the steps first, think carefully, and decide which step to do first. +2. Look at the description of each step, and use the existing functions and tools to plan the execution steps. + +{% if global_knowledges != empty -%} +===== +Global Knowledge: +Current date time is: {{ "now" | date: "%Y-%m-%d %H:%M" }} +{% for k in global_knowledges %} +{{ k }} +{% endfor %} +===== +{%- endif %} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverAgentHook.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverAgentHook.cs index 53b7866f..14205493 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverAgentHook.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverAgentHook.cs @@ -4,7 +4,7 @@ namespace BotSharp.Plugin.SqlDriver.Hooks; public class SqlDriverAgentHook : AgentHookBase, IAgentHook { - public override string SelfId => BuiltInAgentId.Planner; + public override string SelfId => string.Empty; public SqlDriverAgentHook(IServiceProvider services, AgentSettings settings) : base(services, settings) diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverCrontabHook.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverCrontabHook.cs index 64629fcb..7f645cd9 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverCrontabHook.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverCrontabHook.cs @@ -1,6 +1,7 @@ +using BotSharp.Abstraction.Crontab.Models; +using BotSharp.Abstraction.Models; +using BotSharp.Abstraction.SideCar; using BotSharp.Core.Crontab.Abstraction; -using BotSharp.Core.Crontab.Models; -using Microsoft.EntityFrameworkCore.Query; namespace BotSharp.Plugin.SqlDriver.Hooks; @@ -16,16 +17,22 @@ public class SqlDriverCrontabHook : ICrontabHook public async Task OnCronTriggered(CrontabItem item) { - if (item.Language != "sql") + /*var conv = _services.GetRequiredService(); + conv.SetConversationId("abd9df25-2210-4e4d-80d7-48b6a3b905a8", []); + + if (item.Language == "text") + { + var sidecar = _services.GetService(); + var response = await sidecar.SendMessage(BuiltInAgentId.AIAssistant, item.Description, states: new List()); + return; + } + else if (item.Language != "sql") { return; } _logger.LogWarning($"Crontab item triggered: {item.Topic}. Run {item.Language}: {item.Script}"); - var conv = _services.GetRequiredService(); - conv.SetConversationId("73a9ee27-d597-4739-958f-3bd79760ac8e", []); - var message = new RoleDialogModel(AgentRole.User, $"Run the query") { FunctionName = "sql_select", @@ -42,6 +49,6 @@ public class SqlDriverCrontabHook : ICrontabHook item.ConversationId = conv.ConversationId; item.AgentId = BuiltInAgentId.SqlDriver; item.UserId = "41021346"; - item.ExecutionResult = message.Content; + item.ExecutionResult = message.Content;*/ } } diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index a8f819d6..cb107490 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) @@ -29,6 +29,7 @@ + diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 8c4cd9aa..c3d4bdd5 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -328,6 +328,7 @@ "Assemblies": [ "BotSharp.Core", "BotSharp.Core.SideCar", + "BotSharp.Core.Crontab", "BotSharp.Logger", "BotSharp.Plugin.MongoStorage", "BotSharp.Plugin.Dashboard",