diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeHook.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeHook.cs index 3b77af77..7143d789 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeHook.cs @@ -5,7 +5,7 @@ public interface IKnowledgeHook Task> CollectChunkedKnowledge() => Task.FromResult(new List()); - Task> GetRelevantKnowledges() + Task> GetRelevantKnowledges(string text) => Task.FromResult(new List()); Task> GetGlobalKnowledges() diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs index 447f80b1..291af271 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs @@ -15,6 +15,7 @@ public interface IKnowledgeService Task DeleteVectorCollectionAllData(string collectionName); Task CreateVectorCollectionData(string collectionName, VectorCreateModel create); Task UpdateVectorCollectionData(string collectionName, VectorUpdateModel update); + Task UpsertVectorCollectionData(string collectionName, VectorUpdateModel update); #endregion #region Graph diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs index f165dbc1..daf2e636 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs @@ -5,6 +5,9 @@ public class GenericTemplateMessage : IRichMessage, ITemplateMessage [JsonPropertyName("rich_type")] public string RichType => RichTypeEnum.GenericTemplate; + /// + /// Use model refined content if leaving blank + /// [JsonPropertyName("text")] [Translate] public string Text { get; set; } = string.Empty; diff --git a/src/Infrastructure/BotSharp.Abstraction/Planning/IPlanningHook.cs b/src/Infrastructure/BotSharp.Abstraction/Planning/IPlanningHook.cs index 3f258c98..e609ab11 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Planning/IPlanningHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Planning/IPlanningHook.cs @@ -4,6 +4,7 @@ public interface IPlanningHook { Task GetSummaryAdditionalRequirements(string planner) => Task.FromResult(string.Empty); + Task OnPlanningCompleted(string planner, RoleDialogModel msg) => Task.CompletedTask; } diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorCollectionData.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorCollectionData.cs index e623532a..ba614c2c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorCollectionData.cs +++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorCollectionData.cs @@ -5,5 +5,7 @@ public class VectorCollectionData public string Id { get; set; } public Dictionary Data { get; set; } = new(); public double? Score { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public float[]? Vector { get; set; } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchResult.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchResult.cs index f2c582ce..ce39edbf 100644 --- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchResult.cs @@ -1,5 +1,3 @@ -using BotSharp.Abstraction.Knowledges.Enums; - namespace BotSharp.Abstraction.VectorStorage.Models; public class VectorSearchResult : VectorCollectionData diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index b0410750..13e8edf1 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -123,6 +123,12 @@ public partial class ConversationService Message = new TextMessage(response.SecondaryContent ?? response.Content) }; + // Use model refined response + if (string.IsNullOrEmpty(response.RichContent.Message.Text)) + { + response.RichContent.Message.Text = response.Content; + } + // Patch return function name if (response.PostbackFunctionName != null) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj index 9094107d..28dde755 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj +++ b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj @@ -10,6 +10,12 @@ $(SolutionDir)packages + + + + + + True diff --git a/src/Infrastructure/BotSharp.OpenAPI/Filters/UserSingleLoginFilter.cs b/src/Infrastructure/BotSharp.OpenAPI/Filters/UserSingleLoginFilter.cs index d7c35c8e..6e0a6b2f 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Filters/UserSingleLoginFilter.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Filters/UserSingleLoginFilter.cs @@ -31,8 +31,12 @@ namespace BotSharp.OpenAPI.Filters return; } - if (token.ValidTo.ToLongTimeString() != GetUserExpires().ToLongTimeString()) + var validTo = token.ValidTo.ToLongTimeString(); + var currentExpires = GetUserExpires().ToLongTimeString(); + + if (validTo != currentExpires) { + Serilog.Log.Warning($"Token expired. Token expires at {validTo}, current expires at {currentExpires}"); context.Result = new UnauthorizedResult(); } } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Vector.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Vector.cs index df9fb4ab..0c2d6e65 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Vector.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Vector.cs @@ -164,6 +164,42 @@ public partial class KnowledgeService } } + public async Task UpsertVectorCollectionData(string collectionName, VectorUpdateModel update) + { + try + { + if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(update.Text) || !Guid.TryParse(update.Id, out var guid)) + { + return false; + } + + var db = GetVectorDb(); + var found = await db.GetCollectionData(collectionName, new List { guid }, + withVector: true, + withPayload: true); + if (!found.IsNullOrEmpty()) + { + if (found.First().Data["text"] == update.Text) + { + // Only update payload + return await db.Upsert(collectionName, guid, found.First().Vector, update.Text, update.Payload); + } + } + + var textEmbedding = GetTextEmbedding(collectionName); + var vector = await textEmbedding.GetVectorAsync(update.Text); + var payload = update.Payload ?? new(); + payload[KnowledgePayloadName.DataSource] = !string.IsNullOrWhiteSpace(update.DataSource) ? update.DataSource : VectorDataSource.Api; + + return await db.Upsert(collectionName, guid, vector, update.Text, payload); + } + catch (Exception ex) + { + _logger.LogWarning($"Error when updating vector collection data. {ex.Message}\r\n{ex.InnerException}"); + return false; + } + } + public async Task DeleteVectorCollectionData(string collectionName, string id) { try diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs index d2ef2e5c..f1a34513 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs @@ -27,6 +27,7 @@ public class PrimaryStagePlanFn : IFunctionCallback var collectionName = knowledgeSettings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp; // Get knowledge from vectordb + var hooks = _services.GetServices(); var knowledges = new List(); foreach (var question in task.Questions) { @@ -34,8 +35,13 @@ public class PrimaryStagePlanFn : IFunctionCallback { Confidence = 0.2f }); - knowledges.Add(string.Join("\r\n\r\n=====\r\n", list.Select(x => x.ToQuestionAnswer()))); + + foreach (var hook in hooks) + { + var k = await hook.GetRelevantKnowledges(question); + knowledges.AddRange(k); + } } // Get first stage planning prompt @@ -92,7 +98,7 @@ public class PrimaryStagePlanFn : IFunctionCallback var wholeDialogs = conv.GetDialogHistory(); // Append text - wholeDialogs.Last().Content += "\n\nYou must analyze the table description to infer the table relations."; + wholeDialogs.Last().Content += "\n\nYou must analyze the table description to infer the table relations. Only output the JSON result."; var completion = CompletionProvider.GetChatCompletion(_services, provider: plannerAgent.LlmConfig.Provider, diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs index 29a74364..cfd150d9 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs @@ -75,7 +75,6 @@ public class SecondaryStagePlanFn : IFunctionCallback var template = agent.Templates.FirstOrDefault(x => x.Name == "two_stage.2nd.plan")?.Content ?? string.Empty; var responseFormat = JsonSerializer.Serialize(new SecondStagePlan { - Tool = "tool name if task solution provided", Parameters = [ JsonDocument.Parse("{}") ], Results = [ string.Empty ] }); diff --git a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/Models/SecondStagePlan.cs b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/Models/SecondStagePlan.cs index d4f5dfcf..9076a6b4 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/Models/SecondStagePlan.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/Models/SecondStagePlan.cs @@ -8,9 +8,6 @@ public class SecondStagePlan [JsonPropertyName("description")] public string Description { get; set; } = ""; - [JsonPropertyName("tool_name")] - public string Tool { get; set; } = ""; - [JsonPropertyName("input_args")] public JsonDocument[] Parameters { get; set; } = []; diff --git a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs index 06050d86..2ecd9ac0 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs @@ -96,31 +96,6 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner return true; } - private async Task GetFirstStagePlanPrompt(Agent router) - { - var template = router.Templates.First(x => x.Name == "two_stage.1st.plan").Content; - var responseFormat = JsonSerializer.Serialize(new FirstStagePlan - { - Parameters = new JsonDocument[] { JsonDocument.Parse("{}") }, - Results = new string[] { "" } - }); - - var relevantKnowledges = new List(); - var hooks = _services.GetServices(); - foreach (var hook in hooks) - { - var k = await hook.GetRelevantKnowledges(); - relevantKnowledges.AddRange(k); - } - - var render = _services.GetRequiredService(); - return render.Render(template, new Dictionary - { - { "response_format", responseFormat }, - { "relevant_knowledges", relevantKnowledges.ToArray() } - }); - } - private async Task GetNextStepPrompt(Agent router) { var agentService = _services.GetRequiredService(); @@ -134,17 +109,4 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner { StateConst.EXPECTED_GOAL_AGENT, states.GetState(StateConst.EXPECTED_GOAL_AGENT) } }); } - - private string GetSecondStageTaskPrompt(Agent router, SecondStagePlan plan) - { - var template = router.Templates.First(x => x.Name == "planner_prompt.two_stage.2nd.task").Content; - var render = _services.GetRequiredService(); - return render.Render(template, new Dictionary - { - { "task_description", plan.Description }, - { "related_tables", plan.Tables }, - { "input_arguments", JsonSerializer.Serialize(plan.Parameters) }, - { "output_results", JsonSerializer.Serialize(plan.Results) }, - }); - } } diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid index e542c2ee..8f8b29a5 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid @@ -1,7 +1,8 @@ Use the TwoStagePlanner approach to plan the overall implementation steps, follow the below steps strictly. -1. call plan_primary_stage to generate the primary plan. +1. Call plan_primary_stage to generate the primary plan. 2. If need_additional_information is true, call plan_secondary_stage for the specific primary stage. -3. You must call plan_summary for you final planned output. +3. You must call plan_summary to generate final planned steps. +4. If you can't generate the final accurate planning steps due to missing some specific informations, please ask user for more information. *** IMPORTANT *** Don't run the planning process repeatedly if you have already got the result of user's request. diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.next.liquid b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.next.liquid index 48ae2b39..f7388be6 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.next.liquid +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.next.liquid @@ -10,4 +10,3 @@ Expected user goal agent is {{ expected_user_goal_agent }}. {%- else -%} User goal agent is inferred based on user initial request. {%- endif %} -Always route to planner first. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs index 7833bfb4..0806c82c 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs @@ -29,7 +29,15 @@ public class ExecuteQueryFn : IFunctionCallback _ => throw new NotImplementedException($"Database type {settings.DatabaseType} is not supported.") }; - message.Content = JsonSerializer.Serialize(results); + if (results.Count() == 0) + { + message.Content = "No record found"; + } + else + { + message.Content = JsonSerializer.Serialize(results); + } + return true; }