diff --git a/BotSharp.sln b/BotSharp.sln
index 972dc35f..4aa709de 100644
--- a/BotSharp.sln
+++ b/BotSharp.sln
@@ -143,6 +143,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.LLM.Tests", "tests
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Test.RealtimeVoice", "tests\BotSharp.Test.RealtimeVoice\BotSharp.Test.RealtimeVoice.csproj", "{B067B126-88CD-4282-BEEF-7369B64423EF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.ChartHandler", "src\Plugins\BotSharp.Plugin.ChartHandler\BotSharp.Plugin.ChartHandler.csproj", "{0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -599,6 +601,14 @@ Global
{B067B126-88CD-4282-BEEF-7369B64423EF}.Release|Any CPU.Build.0 = Release|Any CPU
{B067B126-88CD-4282-BEEF-7369B64423EF}.Release|x64.ActiveCfg = Release|Any CPU
{B067B126-88CD-4282-BEEF-7369B64423EF}.Release|x64.Build.0 = Release|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|x64.Build.0 = Debug|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x64.ActiveCfg = Release|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -668,6 +678,7 @@ Global
{7D0DB012-9798-4BB9-B15B-A5B0B7B3B094} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
{7C0C7D13-D161-4AB0-9C29-83A0F1FF990E} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
{B067B126-88CD-4282-BEEF-7369B64423EF} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 03798e37..b51dfc1e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -38,7 +38,7 @@
-
+
@@ -46,7 +46,7 @@
-
+
@@ -74,7 +74,7 @@
-
+
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Constants/LlmConstant.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Constants/LlmConstant.cs
index 3919ca57..e45c8b15 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Constants/LlmConstant.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Constants/LlmConstant.cs
@@ -3,4 +3,5 @@ namespace BotSharp.Abstraction.Agents.Constants;
public static class LlmConstant
{
public const int DEFAULT_MAX_OUTPUT_TOKEN = 1024;
+ public const string DEFAULT_REASONING_EFFORT_LEVEL = "low";
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs
index f1df3a01..75e3aa93 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs
@@ -34,4 +34,11 @@ public class AgentLlmConfig
[JsonPropertyName("max_output_tokens")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public int? MaxOutputTokens { get; set; }
+
+ ///
+ /// Reasoning effort level
+ ///
+ [JsonPropertyName("reasoning_effort_level")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? ReasoningEffortLevel { get; set; }
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs
index 0e6b9ba3..eebf7bfe 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/IWebBrowser.cs
@@ -30,4 +30,5 @@ public interface IWebBrowser
Task SendHttpRequest(MessageInfo message, HttpRequestParams actionParams);
Task GetAttributeValue(MessageInfo message, ElementLocatingArgs location);
Task SetAttributeValue(MessageInfo message, ElementLocatingArgs location);
+ string GetCurrentUrl(MessageInfo message);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs
index 0a5244f8..855bd79e 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs
@@ -49,10 +49,10 @@ public abstract class ConversationHookBase : IConversationHook
public virtual Task OnHumanInterventionNeeded(RoleDialogModel message)
=> Task.CompletedTask;
- public virtual Task OnFunctionExecuting(RoleDialogModel message, string from = InvokeSource.Manual)
+ public virtual Task OnFunctionExecuting(RoleDialogModel message, InvokeFunctionOptions? options = null)
=> Task.CompletedTask;
- public virtual Task OnFunctionExecuted(RoleDialogModel message, string from = InvokeSource.Manual)
+ public virtual Task OnFunctionExecuted(RoleDialogModel message, InvokeFunctionOptions? options = null)
=> Task.CompletedTask;
public virtual Task OnMessageReceived(RoleDialogModel message)
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Dtos/ChatResponseDto.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Dtos/ChatResponseDto.cs
index 19b5e8b2..9e4fcb41 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Dtos/ChatResponseDto.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Dtos/ChatResponseDto.cs
@@ -32,6 +32,10 @@ public class ChatResponseDto : InstructResult
[JsonPropertyName("payload")]
public string? Payload { get; set; }
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("indication")]
+ public string? Indication { get; set; }
+
[JsonPropertyName("has_message_files")]
public bool HasMessageFiles { get; set; }
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ChatEvent.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ChatEvent.cs
new file mode 100644
index 00000000..0d68fef7
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ChatEvent.cs
@@ -0,0 +1,22 @@
+namespace BotSharp.Abstraction.Conversations.Enums;
+
+public static class ChatEvent
+{
+ public const string OnConversationInitFromClient = nameof(OnConversationInitFromClient);
+ public const string OnMessageReceivedFromClient = nameof(OnMessageReceivedFromClient);
+ public const string OnMessageReceivedFromAssistant = nameof(OnMessageReceivedFromAssistant);
+
+ public const string OnMessageDeleted = nameof(OnMessageDeleted);
+ public const string OnNotificationGenerated = nameof(OnNotificationGenerated);
+ public const string OnIndicationReceived = nameof(OnIndicationReceived);
+
+ public const string OnConversationContentLogGenerated = nameof(OnConversationContentLogGenerated);
+ public const string OnConversateStateLogGenerated = nameof(OnConversateStateLogGenerated);
+ public const string OnAgentQueueChanged = nameof(OnAgentQueueChanged);
+ public const string OnStateChangeGenerated = nameof(OnStateChangeGenerated);
+
+ public const string BeforeReceiveLlmStreamMessage = nameof(BeforeReceiveLlmStreamMessage);
+ public const string OnReceiveLlmStreamMessage = nameof(OnReceiveLlmStreamMessage);
+ public const string AfterReceiveLlmStreamMessage = nameof(AfterReceiveLlmStreamMessage);
+ public const string OnSenderActionGenerated = nameof(OnSenderActionGenerated);
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
index d1b6f83c..5a1d8aa2 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
@@ -61,7 +61,7 @@ public interface IConversationHook : IHookBase
///
///
///
- Task OnFunctionExecuting(RoleDialogModel message, string from = InvokeSource.Manual);
+ Task OnFunctionExecuting(RoleDialogModel message, InvokeFunctionOptions? options = null);
///
/// Triggered when the function calling completed.
@@ -69,7 +69,7 @@ public interface IConversationHook : IHookBase
///
///
///
- Task OnFunctionExecuted(RoleDialogModel message, string from = InvokeSource.Manual);
+ Task OnFunctionExecuted(RoleDialogModel message, InvokeFunctionOptions? options = null);
Task OnResponseGenerated(RoleDialogModel message);
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationProgressService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationProgressService.cs
deleted file mode 100644
index 8958cab5..00000000
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationProgressService.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace BotSharp.Abstraction.Conversations;
-
-public delegate Task FunctionExecuting(RoleDialogModel msg);
-public delegate Task FunctionExecuted(RoleDialogModel msg);
-
-public interface IConversationProgressService
-{
- FunctionExecuted OnFunctionExecuted { get; set; }
- FunctionExecuting OnFunctionExecuting { get; set; }
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs
index b49fc55d..502cca4c 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs
@@ -1,5 +1,3 @@
-using BotSharp.Abstraction.Messaging.Enums;
-
namespace BotSharp.Abstraction.Conversations.Models;
public class ConversationSenderActionModel
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
index 6a8af2d6..6bc82f81 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
@@ -120,7 +120,7 @@ public class RoleDialogModel : ITrackableMessage
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
public bool IsStreaming { get; set; }
- private RoleDialogModel()
+ public RoleDialogModel()
{
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs
index 688e939e..d7d9d528 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Graph.Models;
+using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.VectorStorage.Models;
namespace BotSharp.Abstraction.Knowledges;
@@ -13,6 +14,7 @@ public interface IKnowledgeService
Task GetVectorCollectionDetails(string collectionName);
Task> SearchVectorKnowledge(string query, string collectionName, VectorSearchOptions options);
Task> GetPagedVectorCollectionData(string collectionName, VectorFilter filter);
+ Task> GetVectorCollectionData(string collectionName, IEnumerable ids, VectorQueryOptions? options = null);
Task DeleteVectorCollectionData(string collectionName, string id);
Task DeleteVectorCollectionAllData(string collectionName);
Task CreateVectorCollectionData(string collectionName, VectorCreateModel create);
@@ -69,6 +71,11 @@ public interface IKnowledgeService
Task DeleteVectorCollectionSnapshot(string collectionName, string snapshotName);
#endregion
+ #region Index
+ Task> CreateVectorCollectionPayloadIndexes(string collectionName, IEnumerable options);
+ Task> DeleteVectorCollectionPayloadIndexes(string collectionName, IEnumerable options);
+ #endregion
+
#region Common
Task RefreshVectorKnowledgeConfigs(VectorCollectionConfigsModel configs);
#endregion
diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs
index e74a7c37..23e055d2 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs
@@ -47,7 +47,15 @@ public class LlmModelSetting
///
public int Dimension { get; set; }
- public LlmCost Cost { get; set; } = new();
+ ///
+ /// Settings for reasoning model
+ ///
+ public ReasoningSetting? Reasoning { get; set; }
+
+ ///
+ /// Settings for llm cost
+ ///
+ public LlmCostSetting Cost { get; set; } = new();
public override string ToString()
{
@@ -55,10 +63,16 @@ public class LlmModelSetting
}
}
+public class ReasoningSetting
+{
+ public float Temperature { get; set; } = 1.0f;
+ public string? EffortLevel { get; set; }
+}
+
///
/// Cost per 1K tokens
///
-public class LlmCost
+public class LlmCostSetting
{
// Input
public float TextInputCost { get; set; } = 0f;
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
new file mode 100644
index 00000000..d345ab86
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
@@ -0,0 +1,6 @@
+namespace BotSharp.Abstraction.MessageHub.Models;
+
+public class HubObserveData : ObserveDataBase where TData : class, new()
+{
+ public TData Data { get; set; } = null!;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
new file mode 100644
index 00000000..c7d5c0f6
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
@@ -0,0 +1,7 @@
+namespace BotSharp.Abstraction.MessageHub.Models;
+
+public class ObserveDataBase
+{
+ public string EventName { get; set; } = null!;
+ public string RefId { get; set; } = null!;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs
new file mode 100644
index 00000000..0ba75531
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs
@@ -0,0 +1,28 @@
+using BotSharp.Abstraction.MessageHub.Observers;
+
+namespace BotSharp.Abstraction.MessageHub.Models;
+
+public class ObserverSubscription
+{
+ public IBotSharpObserver Observer { get; set; }
+ public IDisposable Subscription { get; set; }
+
+ public ObserverSubscription()
+ {
+
+ }
+
+ public ObserverSubscription(
+ IBotSharpObserver observer,
+ IDisposable subscription)
+ {
+ Observer = observer;
+ Subscription = subscription;
+ }
+
+ public void UnSubscribe()
+ {
+ Observer.Deactivate();
+ Subscription.Dispose();
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs
new file mode 100644
index 00000000..1381ea03
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs
@@ -0,0 +1,48 @@
+
+namespace BotSharp.Abstraction.MessageHub.Observers;
+
+public abstract class BotSharpObserverBase : IBotSharpObserver
+{
+ private bool _active = false;
+ protected Dictionary> _listeners = [];
+
+ protected BotSharpObserverBase()
+ {
+
+ }
+
+ public virtual string Name => string.Empty;
+
+ public virtual bool Active => _active;
+
+ public virtual void Activate()
+ {
+ _active = true;
+ }
+
+ public virtual void Deactivate()
+ {
+ _active = false;
+ _listeners = [];
+ }
+
+ public virtual void SetEventListeners(Dictionary> listeners)
+ {
+ _listeners = listeners;
+ }
+
+ public virtual void OnCompleted()
+ {
+
+ }
+
+ public virtual void OnError(Exception error)
+ {
+
+ }
+
+ public virtual void OnNext(T value)
+ {
+
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs
new file mode 100644
index 00000000..158ef4a6
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs
@@ -0,0 +1,11 @@
+namespace BotSharp.Abstraction.MessageHub.Observers;
+
+public interface IBotSharpObserver : IObserver
+{
+ string Name { get; }
+ bool Active { get; }
+
+ void SetEventListeners(Dictionary> listeners);
+ void Activate();
+ void Deactivate();
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs
new file mode 100644
index 00000000..582849aa
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs
@@ -0,0 +1,13 @@
+using BotSharp.Abstraction.MessageHub.Models;
+
+namespace BotSharp.Abstraction.MessageHub.Services;
+
+public interface IObserverService
+{
+ IDisposable SubscribeObservers(
+ string refId,
+ IEnumerable? names = null,
+ Dictionary>? listeners = null) where T : ObserveDataBase;
+
+ void UnSubscribeObservers(IEnumerable? names = null) where T : ObserveDataBase;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs
index 1f495a44..25a97b2b 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs
@@ -1,7 +1,7 @@
-using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
-using BotSharp.Abstraction.Messaging.Models.RichContent;
using System.Text.Json;
using System.Reflection;
+using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
+using BotSharp.Abstraction.Messaging.Models.RichContent;
namespace BotSharp.Abstraction.Messaging;
@@ -38,6 +38,10 @@ public static class BotSharpMessageParser
{
targetType = typeof(TextMessage);
}
+ else if (richType == RichTypeEnum.ProgramCode)
+ {
+ targetType = typeof(ProgramCodeTemplateMessage);
+ }
else if (richType == RichTypeEnum.GenericTemplate)
{
if (root.TryGetProperty("element_type", out element))
@@ -84,9 +88,9 @@ public static class BotSharpMessageParser
{
targetType = typeof(CouponTemplateMessage);
}
- else if (templateType == TemplateTypeEnum.Product)
+ else if (templateType == TemplateTypeEnum.ProgramCode)
{
- targetType = typeof(ProductTemplateMessage);
+ targetType = typeof(ProgramCodeTemplateMessage);
}
else if (templateType == TemplateTypeEnum.Generic)
{
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs
index 7603f790..8d66904b 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs
@@ -9,4 +9,5 @@ public static class RichTypeEnum
public const string QuickReply = "quick_reply";
public const string Text = "text";
public const string Attachment = "attachment";
+ public const string ProgramCode = "program_code";
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/TemplateTypeEnum.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/TemplateTypeEnum.cs
index 74f88735..73a4e9d4 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/TemplateTypeEnum.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/TemplateTypeEnum.cs
@@ -6,5 +6,5 @@ public static class TemplateTypeEnum
public const string Coupon = "coupon";
public const string Generic = "generic";
public const string MultiSelect = "multi-select";
- public const string Product = "product";
+ public const string ProgramCode = "program_code";
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs
deleted file mode 100644
index 05af097a..00000000
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template;
-
-public class ProductTemplateMessage : IRichMessage, ITemplateMessage
-{
- [JsonPropertyName("rich_type")]
- public string RichType => RichTypeEnum.GenericTemplate;
-
- [JsonPropertyName("text")]
- [Translate]
- public string Text { get; set; } = string.Empty;
-
- [JsonPropertyName("template_type")]
- public string TemplateType => TemplateTypeEnum.Product;
-}
-
-public class ProductElement
-{
- public string Id { get; set; }
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProgramCodeTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProgramCodeTemplateMessage.cs
new file mode 100644
index 00000000..60f6464a
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProgramCodeTemplateMessage.cs
@@ -0,0 +1,16 @@
+namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template;
+
+public class ProgramCodeTemplateMessage : IRichMessage, ITemplateMessage
+{
+ [JsonPropertyName("rich_type")]
+ public string RichType => RichTypeEnum.ProgramCode;
+
+ [JsonPropertyName("text")]
+ public string Text { get; set; } = string.Empty;
+
+ [JsonPropertyName("template_type")]
+ public virtual string TemplateType { get; set; } = TemplateTypeEnum.ProgramCode;
+
+ [JsonPropertyName("language")]
+ public string Language { get; set; } = string.Empty;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/SuccessFailResponse.cs b/src/Infrastructure/BotSharp.Abstraction/Models/SuccessFailResponse.cs
new file mode 100644
index 00000000..11985259
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Models/SuccessFailResponse.cs
@@ -0,0 +1,7 @@
+namespace BotSharp.Abstraction.Models;
+
+public class SuccessFailResponse
+{
+ public List Success { get; set; } = [];
+ public List Fail { get; set; } = [];
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Observables/Models/HubObserveData.cs b/src/Infrastructure/BotSharp.Abstraction/Observables/Models/HubObserveData.cs
deleted file mode 100644
index bf771cb4..00000000
--- a/src/Infrastructure/BotSharp.Abstraction/Observables/Models/HubObserveData.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace BotSharp.Abstraction.Observables.Models;
-
-public class HubObserveData : ObserveDataBase
-{
- public string EventName { get; set; } = null!;
- public RoleDialogModel Data { get; set; } = null!;
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Observables/Models/ObserveDataBase.cs b/src/Infrastructure/BotSharp.Abstraction/Observables/Models/ObserveDataBase.cs
deleted file mode 100644
index 17773272..00000000
--- a/src/Infrastructure/BotSharp.Abstraction/Observables/Models/ObserveDataBase.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace BotSharp.Abstraction.Observables.Models;
-
-public abstract class ObserveDataBase
-{
- public IServiceProvider ServiceProvider { get; set; } = null!;
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs
index 1a08fc5b..70564846 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs
@@ -3,6 +3,4 @@ namespace BotSharp.Abstraction.Plugins;
public class PluginSettings
{
public string[] Assemblies { get; set; } = new string[0];
-
- public string[] ExcludedFunctions { get; set; } = new string[0];
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs
index 51d7383a..b4fed514 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs
@@ -6,8 +6,15 @@ namespace BotSharp.Abstraction.Realtime;
public interface IRealtimeHook : IHookBase
{
- Task OnModelReady(Agent agent, IRealTimeCompletion completer);
- string[] OnModelTranscriptPrompt(Agent agent);
- Task OnTranscribeCompleted(RoleDialogModel message, TranscriptionData data);
- Task ShouldReconnect(RealtimeHubConnection conn) => Task.FromResult(false);
+ Task OnModelReady(Agent agent, IRealTimeCompletion completer)
+ => Task.CompletedTask;
+
+ string[] OnModelTranscriptPrompt(Agent agent)
+ => [];
+
+ Task OnTranscribeCompleted(RoleDialogModel message, TranscriptionData data)
+ => Task.CompletedTask;
+
+ Task ShouldReconnect(RealtimeHubConnection conn, RoleDialogModel message)
+ => Task.FromResult(false);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs
index b94876c3..e274adb7 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs
@@ -16,6 +16,7 @@ public class ConversationFilter
public string? ChannelId { get; set; }
public string? UserId { get; set; }
public DateTime? StartTime { get; set; }
+ public DateTime? EndTime { get; set; }
///
/// Agent task id
diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationStateKeysFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationStateKeysFilter.cs
index bff4b43c..201a031c 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationStateKeysFilter.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationStateKeysFilter.cs
@@ -8,6 +8,8 @@ public class ConversationStateKeysFilter
public bool PreLoad { get; set; }
public List? AgentIds { get; set; }
public List? UserIds { get; set; }
+ public DateTime? StartTime { get; set; }
+ public DateTime? EndTime { get; set; }
public ConversationStateKeysFilter()
{
diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/InstructLogFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/InstructLogFilter.cs
index c1040f94..aeec7b18 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/InstructLogFilter.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/InstructLogFilter.cs
@@ -8,6 +8,8 @@ public class InstructLogFilter : Pagination
public List? TemplateNames { get; set; }
public List? UserIds { get; set; }
public List? States { get; set; }
+ public DateTime? StartTime { get; set; }
+ public DateTime? EndTime { get; set; }
public static InstructLogFilter Empty()
{
diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/InstructLogKeysFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/InstructLogKeysFilter.cs
index c3eade89..ef04ede6 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/InstructLogKeysFilter.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/InstructLogKeysFilter.cs
@@ -8,6 +8,8 @@ public class InstructLogKeysFilter
public bool PreLoad { get; set; }
public List? AgentIds { get; set; }
public List? UserIds { get; set; }
+ public DateTime? StartTime { get; set; }
+ public DateTime? EndTime { get; set; }
public InstructLogKeysFilter()
{
diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs
index d608d71f..8e0e2d55 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs
@@ -56,7 +56,7 @@ public interface IBotSharpRepository : IHaveServiceProvider
void UpdateUserPhone(string userId, string Iphone, string regionCode) => throw new NotImplementedException();
void UpdateUserIsDisable(string userId, bool isDisable) => throw new NotImplementedException();
void UpdateUsersIsDisable(List userIds, bool isDisable) => throw new NotImplementedException();
- PagedItems GetUsers(UserFilter filter) => throw new NotImplementedException();
+ ValueTask> GetUsers(UserFilter filter) => throw new NotImplementedException();
List SearchLoginUsers(User filter, string source = UserSource.Internal) =>throw new NotImplementedException();
User? GetUserDetails(string userId, bool includeAgent = false) => throw new NotImplementedException();
bool UpdateUser(User user, bool updateUserAgents = false) => throw new NotImplementedException();
@@ -93,7 +93,7 @@ public interface IBotSharpRepository : IHaveServiceProvider
#endregion
#region Agent Task
- PagedItems GetAgentTasks(AgentTaskFilter filter)
+ ValueTask> GetAgentTasks(AgentTaskFilter filter)
=> throw new NotImplementedException();
AgentTask? GetAgentTask(string agentId, string taskId)
=> throw new NotImplementedException();
@@ -126,7 +126,7 @@ public interface IBotSharpRepository : IHaveServiceProvider
=> throw new NotImplementedException();
Conversation GetConversation(string conversationId, bool isLoadStates = false)
=> throw new NotImplementedException();
- PagedItems GetConversations(ConversationFilter filter)
+ ValueTask> GetConversations(ConversationFilter filter)
=> throw new NotImplementedException();
void UpdateConversationTitle(string conversationId, string title)
=> throw new NotImplementedException();
@@ -179,7 +179,7 @@ public interface IBotSharpRepository : IHaveServiceProvider
bool SaveInstructionLogs(IEnumerable logs)
=> throw new NotImplementedException();
- PagedItems GetInstructionLogs(InstructLogFilter filter)
+ ValueTask> GetInstructionLogs(InstructLogFilter filter)
=> throw new NotImplementedException();
List GetInstructionLogSearchKeys(InstructLogKeysFilter filter)
@@ -227,7 +227,7 @@ public interface IBotSharpRepository : IHaveServiceProvider
///
bool DeleteKnolwedgeBaseFileMeta(string collectionName, string vectorStoreProvider, Guid? fileId = null)
=> throw new NotImplementedException();
- PagedItems GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, KnowledgeFileFilter filter)
+ ValueTask> GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, KnowledgeFileFilter filter)
=> throw new NotImplementedException();
#endregion
@@ -236,7 +236,7 @@ public interface IBotSharpRepository : IHaveServiceProvider
=> throw new NotImplementedException();
bool DeleteCrontabItem(string conversationId)
=> throw new NotImplementedException();
- PagedItems GetCrontabItems(CrontabItemFilter filter)
+ ValueTask> GetCrontabItems(CrontabItemFilter filter)
=> throw new NotImplementedException();
#endregion
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs
index 56c98a82..d387fab1 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs
@@ -26,8 +26,8 @@ public interface IRoutingService
///
RoutingRule[] GetRulesByAgentId(string id);
- Task InvokeAgent(string agentId, List dialogs, string from = InvokeSource.Manual, bool useStream = false);
- Task InvokeFunction(string name, RoleDialogModel messages, string from = InvokeSource.Manual);
+ Task InvokeAgent(string agentId, List dialogs, InvokeAgentOptions? options = null);
+ Task InvokeFunction(string name, RoleDialogModel messages, InvokeFunctionOptions? options = null);
Task InstructLoop(Agent agent, RoleDialogModel message, List dialogs);
///
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs
new file mode 100644
index 00000000..5e031787
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/InvokeOptions.cs
@@ -0,0 +1,31 @@
+namespace BotSharp.Abstraction.Routing.Models;
+
+public abstract class InvokeOptions
+{
+ public string From { get; set; }
+}
+
+public class InvokeAgentOptions : InvokeOptions
+{
+ public bool UseStream { get; set; }
+
+ public static InvokeAgentOptions Default()
+ {
+ return new()
+ {
+ From = InvokeSource.Manual,
+ UseStream = false
+ };
+ }
+}
+
+public class InvokeFunctionOptions : InvokeOptions
+{
+ public static InvokeFunctionOptions Default()
+ {
+ return new()
+ {
+ From = InvokeSource.Manual
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs b/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs
index 4426d71f..1d93dcba 100644
--- a/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs
@@ -23,7 +23,12 @@ public class SideCarAttribute : AsyncMoAttribute
var instance = context.Target;
var retType = context.ReturnType;
- var serviceProvider = ((IHaveServiceProvider)instance).ServiceProvider;
+ var serviceProvider = (instance as IHaveServiceProvider)?.ServiceProvider;
+ if (serviceProvider == null)
+ {
+ return;
+ }
+
var (sidecar, sidecarMethod) = GetSideCarMethod(serviceProvider, methodName, retType, methodArgs);
if (sidecar == null || sidecarMethod == null)
{
diff --git a/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs b/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs
index f221cab2..bc6609c0 100644
--- a/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/SideCar/Models/SideCarOptions.cs
@@ -3,19 +3,23 @@ namespace BotSharp.Abstraction.SideCar.Models;
public class SideCarOptions
{
public bool IsInheritStates { get; set; }
- public IEnumerable? InheritStateKeys { get; set; }
+ public HashSet? InheritStateKeys { get; set; }
+ public HashSet? ExcludedStateKeys { get; set; }
public static SideCarOptions Empty()
{
return new();
}
- public static SideCarOptions InheritStates(IEnumerable? targetStates = null)
+ public static SideCarOptions InheritStates(
+ HashSet? includedStates = null,
+ HashSet? excludedStates = null)
{
return new()
{
IsInheritStates = true,
- InheritStateKeys = targetStates
+ InheritStateKeys = includedStates,
+ ExcludedStateKeys = excludedStates
};
}
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs
index 1499ea17..d03687ff 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs
@@ -7,6 +7,7 @@ namespace BotSharp.Abstraction.Users;
public interface IUserService
{
Task GetUser(string id);
+ Task> GetUsers(List ids);
Task> GetUsers(UserFilter filter);
Task> SearchLoginUsers(User filter);
Task GetUserDetails(string userId, bool includeAgent = false);
diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs
index 4d21013b..c63b0056 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs
@@ -50,6 +50,6 @@ public class Pagination : ICacheKey
public class PagedItems
{
- public int Count { get; set; }
+ public long Count { get; set; }
public IEnumerable Items { get; set; } = new List();
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs
index cb830bcc..95d6257c 100644
--- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs
@@ -14,8 +14,7 @@ public interface IVectorDb
=> throw new NotImplementedException();
Task> GetPagedCollectionData(string collectionName, VectorFilter filter)
=> throw new NotImplementedException();
- Task> GetCollectionData(string collectionName, IEnumerable ids,
- bool withPayload = false, bool withVector = false)
+ Task> GetCollectionData(string collectionName, IEnumerable ids, VectorQueryOptions? options = null)
=> throw new NotImplementedException();
Task CreateCollection(string collectionName, int dimension)
=> throw new NotImplementedException();
@@ -23,8 +22,7 @@ public interface IVectorDb
=> throw new NotImplementedException();
Task Upsert(string collectionName, Guid id, float[] vector, string text, Dictionary? payload = null)
=> throw new NotImplementedException();
- Task> Search(string collectionName, float[] vector, IEnumerable? fields,
- int limit = 5, float confidence = 0.5f, bool withVector = false)
+ Task> Search(string collectionName, float[] vector, VectorSearchOptions? options = null)
=> throw new NotImplementedException();
Task DeleteCollectionData(string collectionName, List ids)
=> throw new NotImplementedException();
@@ -40,4 +38,9 @@ public interface IVectorDb
=> throw new NotImplementedException();
Task DeleteCollectionShapshot(string collectionName, string snapshotName)
=> throw new NotImplementedException();
+
+ Task CreateCollectionPayloadIndex(string collectionName, CreateVectorCollectionIndexOptions options)
+ => throw new NotImplementedException();
+ Task DeleteCollectionPayloadIndex(string collectionName, DeleteVectorCollectionIndexOptions options)
+ => throw new NotImplementedException();
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorCollectionIndexOptions.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorCollectionIndexOptions.cs
new file mode 100644
index 00000000..809bbf6b
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorCollectionIndexOptions.cs
@@ -0,0 +1,18 @@
+namespace BotSharp.Abstraction.VectorStorage.Models;
+
+public class VectorCollectionIndexOptions
+{
+ [JsonPropertyName("field_name")]
+ public string FieldName { get; set; } = null!;
+}
+
+public class CreateVectorCollectionIndexOptions : VectorCollectionIndexOptions
+{
+ [JsonPropertyName("field_schema_type")]
+ public string FieldSchemaType { get; set; } = null!;
+}
+
+public class DeleteVectorCollectionIndexOptions : VectorCollectionIndexOptions
+{
+
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilter.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilter.cs
index 584e8653..43ce6e04 100644
--- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilter.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilter.cs
@@ -6,15 +6,23 @@ public class VectorFilter : StringIdPagination
public bool WithVector { get; set; }
///
- /// For keyword search
+ /// Filter group: each item contains a logical operator and a list of key-value pairs
///
- [JsonPropertyName("search_pairs")]
- public IEnumerable? SearchPairs { get; set; }
-
+ [JsonPropertyName("filter_groups")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public IEnumerable? FilterGroups { get; set; }
///
- /// Included payload keys
+ /// Order by a specific field
///
- [JsonPropertyName("included_payloads")]
- public IEnumerable? IncludedPayloads { get; set; }
+ [JsonPropertyName("order_by")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public VectorSort? OrderBy { get; set; }
+
+ ///
+ /// Included payload fields
+ ///
+ [JsonPropertyName("fields")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public IEnumerable? Fields { get; set; }
}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilterGroup.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilterGroup.cs
new file mode 100644
index 00000000..0b2eb6ac
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorFilterGroup.cs
@@ -0,0 +1,10 @@
+namespace BotSharp.Abstraction.VectorStorage.Models;
+
+public class VectorFilterGroup
+{
+ [JsonPropertyName("filters")]
+ public IEnumerable? Filters { get; set; }
+
+ [JsonPropertyName("filter_operator")]
+ public string FilterOperator { get; set; } = "or";
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorQueryOptions.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorQueryOptions.cs
new file mode 100644
index 00000000..37bad608
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorQueryOptions.cs
@@ -0,0 +1,12 @@
+namespace BotSharp.Abstraction.VectorStorage.Models;
+
+public class VectorQueryOptions
+{
+ public bool WithPayload { get; set; }
+ public bool WithVector { get; set; }
+
+ public static VectorQueryOptions Default()
+ {
+ return new();
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchOptions.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchOptions.cs
index 64943dd1..ef888bb5 100644
--- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchOptions.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSearchOptions.cs
@@ -4,8 +4,21 @@ namespace BotSharp.Abstraction.VectorStorage.Models;
public class VectorSearchOptions
{
- public IEnumerable? Fields { get; set; } = new List { KnowledgePayloadName.Text, KnowledgePayloadName.Answer };
+ public IEnumerable? Fields { get; set; } = [KnowledgePayloadName.Text, KnowledgePayloadName.Answer];
+ public IEnumerable? FilterGroups { get; set; }
public int? Limit { get; set; } = 5;
public float? Confidence { get; set; } = 0.5f;
public bool WithVector { get; set; }
+
+ public static VectorSearchOptions Default()
+ {
+ return new()
+ {
+ Fields = [KnowledgePayloadName.Text, KnowledgePayloadName.Answer],
+ FilterGroups = null,
+ Limit = 5,
+ Confidence = 0.5f,
+ WithVector = false
+ };
+ }
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSort.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSort.cs
new file mode 100644
index 00000000..469018b0
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/VectorSort.cs
@@ -0,0 +1,10 @@
+namespace BotSharp.Abstraction.VectorStorage.Models;
+
+public class VectorSort
+{
+ [JsonPropertyName("field")]
+ public string? Field { get; set; }
+
+ [JsonPropertyName("order")]
+ public string? Order { get; set; } = "desc";
+}
diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs
index 255faa71..b2101ee5 100644
--- a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs
+++ b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs
@@ -44,7 +44,7 @@ public class CrontabService : ICrontabService, ITaskFeeder
public async Task> GetCrontable()
{
var repo = _services.GetRequiredService();
- var crontable = repo.GetCrontabItems(CrontabItemFilter.Empty());
+ var crontable = await repo.GetCrontabItems(CrontabItemFilter.Empty());
// Add fixed crontab items from cronsources
var fixedCrantabItems = crontable.Items.ToList();
diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs
index 1546cb7c..f6ce7a26 100644
--- a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs
+++ b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs
@@ -45,6 +45,7 @@ public class CrontabWatcher : BackgroundService
{
var cron = services.GetRequiredService();
var crons = await cron.GetCrontable();
+ var settings = services.GetRequiredService();
var publisher = services.GetService();
foreach (var item in crons)
@@ -87,7 +88,7 @@ public class CrontabWatcher : BackgroundService
{
_logger.LogInformation($"The current time matches the cron expression {item}");
- if (publisher != null)
+ if (publisher != null && settings.EventSubscriber.Enabled)
{
await publisher.PublishAsync($"Crontab:{item.Title}", item.Cron);
}
diff --git a/src/Infrastructure/BotSharp.Core.Realtime/Hooks/RealtimeConversationHook.cs b/src/Infrastructure/BotSharp.Core.Realtime/Hooks/RealtimeConversationHook.cs
index 095ff2b8..07b8646c 100644
--- a/src/Infrastructure/BotSharp.Core.Realtime/Hooks/RealtimeConversationHook.cs
+++ b/src/Infrastructure/BotSharp.Core.Realtime/Hooks/RealtimeConversationHook.cs
@@ -11,7 +11,7 @@ public class RealtimeConversationHook : ConversationHookBase, IConversationHook
_services = services;
}
- public async Task OnFunctionExecuting(RoleDialogModel message, string from = InvokeSource.Manual)
+ public async Task OnFunctionExecuting(RoleDialogModel message, InvokeFunctionOptions? options = null)
{
var hub = _services.GetRequiredService();
if (hub.HubConn == null)
@@ -32,10 +32,10 @@ public class RealtimeConversationHook : ConversationHookBase, IConversationHook
}
}
- public async Task OnFunctionExecuted(RoleDialogModel message, string from = InvokeSource.Manual)
+ public async Task OnFunctionExecuted(RoleDialogModel message, InvokeFunctionOptions? options = null)
{
var hub = _services.GetRequiredService();
- if (from != InvokeSource.Llm || hub.HubConn == null)
+ if (options?.From != InvokeSource.Llm || hub.HubConn == null)
{
return;
}
diff --git a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
index fe544b4d..8c057b95 100644
--- a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
+++ b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
@@ -99,7 +99,18 @@ public class RealtimeHub : IRealtimeHub
agent.Id);
}
- await routing.InvokeFunction(message.FunctionName, message, from: InvokeSource.Llm);
+ await routing.InvokeFunction(message.FunctionName, message, options: new() { From = InvokeSource.Llm });
+
+ var hooks = _services.GetHooks(_conn.CurrentAgentId);
+ foreach (var hook in hooks)
+ {
+ if (await hook.ShouldReconnect(_conn, message))
+ {
+ await _completer.Reconnect(_conn);
+ _logger.LogWarning("Reconnecting to model due to function call: {FunctionName}", message.FunctionName);
+ break;
+ }
+ }
}
else
{
@@ -117,19 +128,6 @@ public class RealtimeHub : IRealtimeHub
}
}
}
-
- var isReconnect = false;
- var realtimeHooks = _services.GetHooks(_conn.CurrentAgentId);
- foreach (var hook in realtimeHooks)
- {
- isReconnect = await hook.ShouldReconnect(_conn);
- if (isReconnect) break;
- }
-
- if (isReconnect)
- {
- await _completer.Reconnect(_conn);
- }
},
onConversationItemCreated: async response =>
{
diff --git a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs
index d636fa71..79711cb6 100644
--- a/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs
+++ b/src/Infrastructure/BotSharp.Core.SideCar/Services/BotSharpConversationSideCar.cs
@@ -15,6 +15,9 @@
******************************************************************************/
using BotSharp.Core.Infrastructures;
+using NetTopologySuite.Index.KdTree;
+using Newtonsoft.Json.Linq;
+using static System.Runtime.InteropServices.JavaScript.JSType;
namespace BotSharp.Core.SideCar.Services;
@@ -109,7 +112,7 @@ public class BotSharpConversationSideCar : IConversationSideCar
var response = await InnerExecute(agentId, text, postback, states);
AfterExecute();
- _logger.LogInformation($"Existing side car conversation...");
+ _logger.LogInformation($"Exiting side car conversation...");
return response;
}
@@ -189,24 +192,29 @@ public class BotSharpConversationSideCar : IConversationSideCar
private void RestoreStates(ConversationState prevStates)
{
- var innerStates = prevStates;
+ var preValues = prevStates.Values.ToList();
+ var copy = JsonSerializer.Deserialize>(JsonSerializer.Serialize(preValues));
+ var innerStates = new ConversationState(copy ?? []);
var state = _services.GetRequiredService();
if (_sideCarOptions?.IsInheritStates == true)
{
+ var hasIncludedStates = _sideCarOptions?.InheritStateKeys?.Any() == true;
+ var hasExcludedStates = _sideCarOptions?.ExcludedStateKeys?.Any() == true;
var curStates = state.GetCurrentState();
+
foreach (var pair in curStates)
{
var endNode = pair.Value.Values.LastOrDefault();
if (endNode == null) continue;
- if (_sideCarOptions?.InheritStateKeys?.Any() == true
- && !_sideCarOptions.InheritStateKeys.Contains(pair.Key))
+ if ((hasIncludedStates && !_sideCarOptions.InheritStateKeys.Contains(pair.Key))
+ || (hasExcludedStates && _sideCarOptions.ExcludedStateKeys.Contains(pair.Key)))
{
continue;
}
- if (innerStates.ContainsKey(pair.Key))
+ if (innerStates.ContainsKey(pair.Key) && innerStates[pair.Key].Versioning)
{
innerStates[pair.Key].Values.Add(endNode);
}
@@ -223,6 +231,51 @@ public class BotSharpConversationSideCar : IConversationSideCar
}
}
+ AccumulateLlmStats(state, prevStates, innerStates);
state.SetCurrentState(innerStates);
}
+
+ private void AccumulateLlmStats(IConversationStateService state, ConversationState prevState, ConversationState curState)
+ {
+ var dict = new Dictionary
+ {
+ { "prompt_total", typeof(int) },
+ { "completion_total", typeof(int) },
+ { "llm_total_cost", typeof(float) }
+ };
+
+ foreach (var pair in dict)
+ {
+ var preVal = prevState.GetValueOrDefault(pair.Key)?.Values?.LastOrDefault()?.Data;
+ var curVal = state.GetState(pair.Key);
+
+ object data = pair.Value switch
+ {
+ Type t when t == typeof(int) => ParseNumber(preVal) + ParseNumber(curVal),
+ Type t when t == typeof(float) => ParseNumber(preVal) + ParseNumber(curVal),
+ _ => default
+ };
+
+ var cur = curState.GetValueOrDefault(pair.Key);
+ if (cur?.Values?.LastOrDefault() != null)
+ {
+ cur.Values.Last().Data = $"{data}";
+ }
+ }
+ }
+
+ private T ParseNumber(string? data) where T : struct
+ {
+ if (string.IsNullOrEmpty(data))
+ {
+ return default;
+ }
+
+ return typeof(T) switch
+ {
+ Type t when t == typeof(int) => (T)(object)(int.TryParse(data, out var i) ? i : 0),
+ Type t when t == typeof(float) => (T)(object)(float.TryParse(data, out var f) ? f : 0),
+ _ => default
+ };
+ }
}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
index ddcf68cb..6833fdfc 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
+++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
@@ -86,7 +86,6 @@
-
@@ -97,7 +96,9 @@
+
+
@@ -188,7 +189,7 @@
PreserveNewest
-
+
PreserveNewest
@@ -211,6 +212,9 @@
PreserveNewest
+
+ PreserveNewest
+
@@ -242,5 +246,4 @@
true
-
diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
index 5c00de0d..bfae45ba 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
+++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
@@ -125,7 +125,6 @@ public static class BotSharpCoreExtensions
{
var pluginSettings = new PluginSettings();
config.Bind("PluginLoader", pluginSettings);
- var excludedFunctions = pluginSettings.ExcludedFunctions ?? [];
services.AddScoped(provider =>
{
@@ -143,8 +142,7 @@ public static class BotSharpCoreExtensions
// Register function callback
var functions = assembly.GetTypes()
.Where(x => x.IsClass
- && x.GetInterface(nameof(IFunctionCallback)) != null
- && !excludedFunctions.Contains(x.Name))
+ && x.GetInterface(nameof(IFunctionCallback)) != null)
.ToArray();
foreach (var function in functions)
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
index a559877f..61c23d1b 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
@@ -1,18 +1,23 @@
using BotSharp.Abstraction.Google.Settings;
using BotSharp.Abstraction.Instructs;
+using BotSharp.Abstraction.MessageHub;
+using BotSharp.Abstraction.MessageHub.Observers;
+using BotSharp.Abstraction.MessageHub.Services;
using BotSharp.Abstraction.Messaging;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Plugins.Models;
using BotSharp.Abstraction.Settings;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Instructs;
+using BotSharp.Core.MessageHub;
+using BotSharp.Core.MessageHub.Observers;
+using BotSharp.Core.MessageHub.Services;
using BotSharp.Core.Messaging;
using BotSharp.Core.Routing.Reasoning;
using BotSharp.Core.Templating;
using BotSharp.Core.Translation;
-using BotSharp.Core.Observables.Queues;
+using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
-using BotSharp.Abstraction.Observables.Models;
namespace BotSharp.Core.Conversations;
@@ -43,11 +48,14 @@ public class ConversationPlugin : IBotSharpPlugin
return settingService.Bind("GoogleApi");
});
- services.AddSingleton>();
+ // Observer and observable
+ services.AddSingleton>>();
+ services.AddScoped>>();
+ services.AddScoped>, ConversationObserver>();
+ services.AddScoped();
services.AddScoped();
services.AddScoped();
- services.AddScoped();
services.AddScoped();
services.AddScoped();
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationProgressService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationProgressService.cs
deleted file mode 100644
index d7e6440c..00000000
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationProgressService.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace BotSharp.Core.Conversations.Services
-{
- public class ConversationProgressService : IConversationProgressService
- {
-
- public FunctionExecuting OnFunctionExecuting { get; set; }
-
-
- public FunctionExecuted OnFunctionExecuted { get; set; }
- }
-}
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
index 676ea362..9e410b49 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
@@ -87,7 +87,7 @@ public partial class ConversationService : IConversationService
}
var db = _services.GetRequiredService();
- var conversations = db.GetConversations(filter);
+ var conversations = await db.GetConversations(filter);
return conversations;
}
diff --git a/src/Infrastructure/BotSharp.Core/Demo/Functions/GetFunEventsFn.cs b/src/Infrastructure/BotSharp.Core/Demo/Functions/GetFunEventsFn.cs
new file mode 100644
index 00000000..87ddb1ee
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Demo/Functions/GetFunEventsFn.cs
@@ -0,0 +1,51 @@
+using BotSharp.Abstraction.Functions;
+using BotSharp.Core.MessageHub;
+using System.Text.Json.Serialization;
+
+namespace BotSharp.Core.Demo.Functions;
+
+public class GetFunEventsFn : IFunctionCallback
+{
+ private readonly IServiceProvider _services;
+
+ public GetFunEventsFn(IServiceProvider services)
+ {
+ _services = services;
+ }
+
+ public string Name => "get_fun_events";
+ public string Indication => "Searching fun events";
+
+ public async Task Execute(RoleDialogModel message)
+ {
+ var args = JsonSerializer.Deserialize(message.FunctionArgs);
+ var conv = _services.GetRequiredService();
+ var messageHub = _services.GetRequiredService>>();
+
+ await Task.Delay(1000);
+
+ message.Indication = $"Start querying event data in {args?.City}";
+ messageHub.Push(new()
+ {
+ EventName = ChatEvent.OnIndicationReceived,
+ Data = message,
+ RefId = conv.ConversationId
+ });
+
+ await Task.Delay(1500);
+
+ message.Indication = $"Still searching events in {args?.City}";
+ messageHub.Push(new()
+ {
+ EventName = ChatEvent.OnIndicationReceived,
+ Data = message,
+ RefId = conv.ConversationId
+ });
+
+ await Task.Delay(1500);
+
+ message.Content = $"Here in {args?.City}, there are a lot of fun events in summer.";
+ message.StopCompletion = true;
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs b/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
index cb6bcd88..6465b88e 100644
--- a/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
+++ b/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
@@ -1,4 +1,8 @@
using BotSharp.Abstraction.Functions;
+using BotSharp.Abstraction.Models;
+using BotSharp.Abstraction.SideCar;
+using BotSharp.Core.MessageHub;
+using System.Text.Json.Serialization;
namespace BotSharp.Core.Demo.Functions;
@@ -16,8 +20,56 @@ public class GetWeatherFn : IFunctionCallback
public async Task Execute(RoleDialogModel message)
{
+ var args = JsonSerializer.Deserialize(message.FunctionArgs);
+ var conv = _services.GetRequiredService();
+ var messageHub = _services.GetRequiredService>>();
+
+ await Task.Delay(1000);
+
+ message.Indication = $"Start querying weather data in {args?.City}";
+ messageHub.Push(new()
+ {
+ EventName = ChatEvent.OnIndicationReceived,
+ Data = message,
+ RefId = conv.ConversationId
+ });
+
+ await Task.Delay(1500);
+
+ message.Indication = $"Still working on it... Hold on, {args?.City}";
+ messageHub.Push(new()
+ {
+ EventName = ChatEvent.OnIndicationReceived,
+ Data = message,
+ RefId = conv.ConversationId
+ });
+
+ await Task.Delay(1500);
+
message.Content = $"It is a sunny day!";
message.StopCompletion = false;
+
+#if DEBUG
+ var sidecar = _services.GetService();
+ if (sidecar != null)
+ {
+ var text = $"I want to know fun events in {args?.City}";
+ var states = new List
+ {
+ new() { Key = "channel", Value = "email" }
+ };
+
+ var msg = await sidecar.SendMessage(message.CurrentAgentId, text, states: states);
+ message.Content = $"{message.Content} {msg.Content}";
+ }
+#endif
+
return true;
}
+}
+
+class WeatherLocation
+{
+ [JsonPropertyName("city")]
+ public string City { get; set; }
}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/Evaluations/EvaluationConversationHook.cs b/src/Infrastructure/BotSharp.Core/Evaluations/EvaluationConversationHook.cs
index b1b4541e..80757219 100644
--- a/src/Infrastructure/BotSharp.Core/Evaluations/EvaluationConversationHook.cs
+++ b/src/Infrastructure/BotSharp.Core/Evaluations/EvaluationConversationHook.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Evaluations;
+using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.Core.Evaluations;
@@ -22,13 +23,13 @@ public class EvaluationConversationHook : ConversationHookBase
return base.OnMessageReceived(message);
}
- public override Task OnFunctionExecuted(RoleDialogModel message, string from = InvokeSource.Manual)
+ public override Task OnFunctionExecuted(RoleDialogModel message, InvokeFunctionOptions? options = null)
{
if (Conversation != null && _convSettings.EnableExecutionLog)
{
_logger.Append(Conversation.Id, $"[{DateTime.Now}] {message.Role}: {message.FunctionName}({message.FunctionArgs}) => {message.Content}");
}
- return base.OnFunctionExecuted(message, from: from);
+ return base.OnFunctionExecuted(message, options);
}
public override Task OnResponseGenerated(RoleDialogModel message)
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.SelectFile.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.SelectFile.cs
index 13525fb1..e00f9158 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.SelectFile.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.SelectFile.cs
@@ -65,7 +65,7 @@ public partial class FileInstructService
}).ToList();
var agentId = !string.IsNullOrWhiteSpace(options.AgentId) ? options.AgentId : BuiltInAgentId.UtilityAssistant;
- var template = !string.IsNullOrWhiteSpace(options.Template) ? options.Template : "select_file_prompt";
+ var template = !string.IsNullOrWhiteSpace(options.Template) ? options.Template : "util-file-select_file_instruction";
var foundAgent = db.GetAgent(agentId);
var prompt = db.GetAgentTemplate(BuiltInAgentId.UtilityAssistant, template);
diff --git a/src/Infrastructure/BotSharp.Core/Loggers/Services/LoggerService.Instruction.cs b/src/Infrastructure/BotSharp.Core/Loggers/Services/LoggerService.Instruction.cs
index 42ad45ab..965212ca 100644
--- a/src/Infrastructure/BotSharp.Core/Loggers/Services/LoggerService.Instruction.cs
+++ b/src/Infrastructure/BotSharp.Core/Loggers/Services/LoggerService.Instruction.cs
@@ -18,22 +18,18 @@ public partial class LoggerService
filter.UserIds = !isAdmin && user?.Id != null ? [user.Id] : null;
- var agents = new List();
var users = new List();
var db = _services.GetRequiredService();
- var logs = db.GetInstructionLogs(filter);
+ var agentService = _services.GetRequiredService();
+ var logs = await db.GetInstructionLogs(filter);
var agentIds = logs.Items.Where(x => !string.IsNullOrEmpty(x.AgentId)).Select(x => x.AgentId).ToList();
var userIds = logs.Items.Where(x => !string.IsNullOrEmpty(x.UserId)).Select(x => x.UserId).ToList();
- agents = db.GetAgents(new AgentFilter
- {
- AgentIds = agentIds,
- Pager = new Pagination { Size = filter.Size }
- });
+ var agents = await agentService.GetAgentOptions(agentIds);
if (isAdmin)
{
- users = db.GetUserByIds(userIds);
+ users = await userService.GetUsers(userIds);
}
var items = logs.Items.Select(x =>
diff --git a/src/Infrastructure/BotSharp.Core/Observables/Queues/MessageHub.cs b/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
similarity index 83%
rename from src/Infrastructure/BotSharp.Core/Observables/Queues/MessageHub.cs
rename to src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
index affb142d..8f01612d 100644
--- a/src/Infrastructure/BotSharp.Core/Observables/Queues/MessageHub.cs
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
@@ -1,11 +1,11 @@
using System.Reactive.Subjects;
-namespace BotSharp.Core.Observables.Queues;
+namespace BotSharp.Core.MessageHub;
-public class MessageHub where T : class
+public class MessageHub where T : ObserveDataBase
{
private readonly ILogger> _logger;
- private readonly ISubject _observable = new Subject();
+ private readonly ISubject _observable = Subject.Synchronize(new Subject());
public IObservable Events => _observable;
public MessageHub(ILogger> logger)
diff --git a/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs b/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
new file mode 100644
index 00000000..e2588c6c
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
@@ -0,0 +1,45 @@
+using BotSharp.Abstraction.MessageHub.Observers;
+
+namespace BotSharp.Core.MessageHub.Observers;
+
+public class ConversationObserver : BotSharpObserverBase>
+{
+ private readonly ILogger _logger;
+ private readonly IServiceProvider _services;
+
+ public ConversationObserver(
+ IServiceProvider services,
+ ILogger logger) : base()
+ {
+ _services = services;
+ _logger = logger;
+ }
+
+ public override string Name => nameof(ConversationObserver);
+
+ public override void OnCompleted()
+ {
+ _logger.LogWarning($"{nameof(ConversationObserver)} receives complete notification.");
+ }
+
+ public override void OnError(Exception error)
+ {
+ _logger.LogError(error, $"{nameof(ConversationObserver)} receives error notification: {error.Message}");
+ }
+
+ public override void OnNext(HubObserveData value)
+ {
+ var conv = _services.GetRequiredService();
+
+ if (value.EventName == ChatEvent.OnIndicationReceived)
+ {
+#if DEBUG
+ _logger.LogCritical($"Receiving {value.EventName} ({value.Data.Indication}) in {nameof(ConversationObserver)} - {conv.ConversationId}");
+#endif
+ if (_listeners.TryGetValue(value.EventName, out var func) && func != null)
+ {
+ func(value).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
+ }
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverService.cs b/src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverService.cs
new file mode 100644
index 00000000..4679235d
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverService.cs
@@ -0,0 +1,86 @@
+using BotSharp.Abstraction.MessageHub.Observers;
+using BotSharp.Abstraction.MessageHub.Services;
+using System.Reactive.Linq;
+
+namespace BotSharp.Core.MessageHub.Services;
+
+public class ObserverService : IObserverService
+{
+ private readonly IServiceProvider _services;
+ private readonly ILogger _logger;
+
+ public ObserverService(
+ IServiceProvider services,
+ ILogger logger)
+ {
+ _services = services;
+ _logger = logger;
+ }
+
+ public IDisposable SubscribeObservers(
+ string refId,
+ IEnumerable? names = null,
+ Dictionary>? listeners = null) where T : ObserveDataBase
+ {
+ var container = _services.GetRequiredService>();
+ var observers = _services.GetServices>()
+ .Where(x => !x.Active)
+ .ToList();
+
+ if (!names.IsNullOrEmpty())
+ {
+ observers = observers.Where(x => names.Contains(x.Name)).ToList();
+ }
+
+ if (observers.IsNullOrEmpty())
+ {
+ return container;
+ }
+
+ if (!listeners.IsNullOrEmpty())
+ {
+ foreach (var observer in observers)
+ {
+ observer.SetEventListeners(listeners ?? []);
+ }
+ }
+
+#if DEBUG
+ _logger.LogCritical($"Subscribe observers: {string.Join(",", observers.Select(x => x.Name))}");
+#endif
+
+ var subscriptions = new List>();
+ var messageHub = _services.GetRequiredService>();
+ foreach (var observer in observers)
+ {
+ observer.Activate();
+ var sub = messageHub.Events.Where(x => x.RefId == refId).Subscribe(observer);
+ subscriptions.Add(new()
+ {
+ Observer = observer,
+ Subscription = sub
+ });
+ }
+
+ container.Append(subscriptions);
+ return container;
+ }
+
+ public void UnSubscribeObservers(IEnumerable? names = null) where T : ObserveDataBase
+ {
+ var container = _services.GetRequiredService>();
+ var subscriptions = container.GetSubscriptions(names);
+
+#if DEBUG
+ _logger.LogCritical($"UnSubscribe observers: {string.Join(",", subscriptions.Select(x => x.Observer.Name))}");
+#endif
+
+ foreach (var sub in subscriptions)
+ {
+ if (!sub.Observer.Active) continue;
+
+ sub.UnSubscribe();
+ }
+ container.Remove(names);
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverSubscriptionContainer.cs b/src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverSubscriptionContainer.cs
new file mode 100644
index 00000000..59e33e2e
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverSubscriptionContainer.cs
@@ -0,0 +1,64 @@
+namespace BotSharp.Core.MessageHub.Services;
+
+public class ObserverSubscriptionContainer : IDisposable
+{
+ private readonly ILogger> _logger;
+
+ private List> _subscriptions = [];
+ private bool _disposed = false;
+
+ public ObserverSubscriptionContainer(
+ ILogger> logger)
+ {
+ _logger = logger;
+ }
+
+ public List> GetSubscriptions(IEnumerable? names = null)
+ {
+ if (!names.IsNullOrEmpty())
+ {
+ return _subscriptions.Where(x => names.Contains(x.Observer.Name)).ToList();
+ }
+ return _subscriptions;
+ }
+
+ public void Append(List> subscriptions)
+ {
+ _subscriptions = _subscriptions.Concat(subscriptions).DistinctBy(x => x.Observer.Name).ToList();
+ }
+
+ public void Remove(IEnumerable? names = null)
+ {
+ if (!names.IsNullOrEmpty())
+ {
+ _subscriptions = _subscriptions.Where(x => !names.Contains(x.Observer.Name)).ToList();
+ return;
+ }
+ _subscriptions.Clear();
+ }
+
+ public void Clear()
+ {
+ _subscriptions.Clear();
+ }
+
+ public void Dispose()
+ {
+ if (!_disposed)
+ {
+#if DEBUG
+ _logger.LogCritical($"Start disposing subscriptions...");
+#endif
+ // UnSubscribe all observers
+ foreach (var sub in _subscriptions)
+ {
+ sub.UnSubscribe();
+ }
+ _subscriptions.Clear();
+#if DEBUG
+ _logger.LogCritical($"End disposing subscriptions...");
+#endif
+ _disposed = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs
index 492dcde3..c2dfc25f 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs
@@ -1,202 +1,6 @@
-using BotSharp.Abstraction.Loggers.Models;
-using BotSharp.Abstraction.Plugins.Models;
-using BotSharp.Abstraction.Tasks.Models;
-using BotSharp.Abstraction.Translation.Models;
-using BotSharp.Abstraction.VectorStorage.Models;
-
namespace BotSharp.Core.Repository;
public class BotSharpDbContext : Database, IBotSharpRepository
{
public IServiceProvider ServiceProvider => throw new NotImplementedException();
-
- #region Plugin
- public PluginConfig GetPluginConfig() => throw new NotImplementedException();
- public void SavePluginConfig(PluginConfig config) => throw new NotImplementedException();
- #endregion
-
- #region Agent
- public Agent GetAgent(string agentId, bool basicsOnly = false)
- => throw new NotImplementedException();
-
- public List GetAgents(AgentFilter filter)
- => throw new NotImplementedException();
-
- public List GetUserAgents(string userId)
- => throw new NotImplementedException();
-
- public void UpdateAgent(Agent agent, AgentField field)
- => throw new NotImplementedException();
-
- public string GetAgentTemplate(string agentId, string templateName)
- => throw new NotImplementedException();
-
- public bool PatchAgentTemplate(string agentId, AgentTemplate template)
- => throw new NotImplementedException();
-
- public List GetAgentResponses(string agentId, string prefix, string intent)
- => throw new NotImplementedException();
-
- public void BulkInsertAgents(List agents)
- => throw new NotImplementedException();
-
- public void BulkInsertUserAgents(List userAgents)
- => throw new NotImplementedException();
-
- public bool DeleteAgents()
- => throw new NotImplementedException();
-
- public bool DeleteAgent(string agentId)
- => throw new NotImplementedException();
- #endregion
-
- #region Agent Task
- public PagedItems GetAgentTasks(AgentTaskFilter filter)
- => throw new NotImplementedException();
-
- public AgentTask? GetAgentTask(string agentId, string taskId)
- => throw new NotImplementedException();
-
- public void InsertAgentTask(AgentTask task)
- => throw new NotImplementedException();
-
- public void BulkInsertAgentTasks(List tasks)
- => throw new NotImplementedException();
-
- public void UpdateAgentTask(AgentTask task, AgentTaskField field)
- => throw new NotImplementedException();
-
- public bool DeleteAgentTask(string agentId, List taskIds)
- => throw new NotImplementedException();
-
- public bool DeleteAgentTasks()
- => throw new NotImplementedException();
- #endregion
-
- #region Conversation
- public void CreateNewConversation(Conversation conversation)
- => throw new NotImplementedException();
-
- public bool DeleteConversations(IEnumerable conversationIds)
- => throw new NotImplementedException();
-
- public Conversation GetConversation(string conversationId, bool isLoadStates = false)
- => throw new NotImplementedException();
-
- public PagedItems GetConversations(ConversationFilter filter)
- => throw new NotImplementedException();
-
- public List GetLastConversations()
- => throw new NotImplementedException();
-
- public List GetIdleConversations(int batchSize, int messageLimit, int bufferHours, IEnumerable excludeAgentIds)
- => throw new NotImplementedException();
-
- [SideCar]
- public List GetConversationDialogs(string conversationId)
- => throw new NotImplementedException();
-
- public ConversationState GetConversationStates(string conversationId)
- => throw new NotImplementedException();
-
- [SideCar]
- public void AppendConversationDialogs(string conversationId, List dialogs)
- => throw new NotImplementedException();
-
- public void UpdateConversationTitle(string conversationId, string title)
- => throw new NotImplementedException();
- public void UpdateConversationTitleAlias(string conversationId, string titleAlias)
- => throw new NotImplementedException();
-
- public bool UpdateConversationTags(string conversationId, List tags)
- => throw new NotImplementedException();
-
- public bool AppendConversationTags(string conversationId, List tags)
- => throw new NotImplementedException();
-
- public bool UpdateConversationMessage(string conversationId, UpdateMessageRequest request)
- => throw new NotImplementedException();
-
- [SideCar]
- public void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint)
- => throw new NotImplementedException();
-
- [SideCar]
- public ConversationBreakpoint? GetConversationBreakpoint(string conversationId)
- => throw new NotImplementedException();
-
- public void UpdateConversationStates(string conversationId, List states)
- => throw new NotImplementedException();
-
- public void UpdateConversationStatus(string conversationId, string status)
- => throw new NotImplementedException();
-
- public List TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
- => throw new NotImplementedException();
- #endregion
-
- #region LLM Completion Log
- public void SaveLlmCompletionLog(LlmCompletionLog log)
- {
- throw new NotImplementedException();
- }
- #endregion
-
- #region Conversation Content Log
- public void SaveConversationContentLog(ContentLogOutputModel log)
- {
- throw new NotImplementedException();
- }
-
- public List GetConversationContentLogs(string conversationId)
- {
- throw new NotImplementedException();
- }
- #endregion
-
- #region Conversation State Log
- public void SaveConversationStateLog(ConversationStateLogModel log)
- {
- throw new NotImplementedException();
- }
-
- public List GetConversationStateLogs(string conversationId)
- {
- throw new NotImplementedException();
- }
- #endregion
-
- #region Stats
- public void IncrementConversationCount()
- {
- throw new NotImplementedException();
- }
- #endregion
-
- #region Translation
- public IEnumerable GetTranslationMemories(IEnumerable queries)
- => throw new NotImplementedException();
- public bool SaveTranslationMemories(IEnumerable inputs) =>
- throw new NotImplementedException();
- #endregion
-
- #region KnowledgeBase
- public bool AddKnowledgeCollectionConfigs(List configs, bool reset = false) =>
- throw new NotImplementedException();
-
- public bool DeleteKnowledgeCollectionConfig(string collectionName) =>
- throw new NotImplementedException();
-
- public IEnumerable GetKnowledgeCollectionConfigs(VectorCollectionConfigFilter filter) =>
- throw new NotImplementedException();
-
- public bool SaveKnolwedgeBaseFileMeta(KnowledgeDocMetaData metaData) =>
- throw new NotImplementedException();
-
- public bool DeleteKnolwedgeBaseFileMeta(string collectionName, string vectorStoreProvider, Guid? fileId = null) =>
- throw new NotImplementedException();
-
- public PagedItems GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, KnowledgeFileFilter filter) =>
- throw new NotImplementedException();
- #endregion
}
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs
index 016e0c36..2b84f212 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs
@@ -6,7 +6,7 @@ namespace BotSharp.Core.Repository;
public partial class FileRepository
{
#region Task
- public PagedItems GetAgentTasks(AgentTaskFilter filter)
+ public async ValueTask> GetAgentTasks(AgentTaskFilter filter)
{
if (filter == null)
{
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
index f772acb2..68881ea3 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
@@ -241,10 +241,9 @@ public partial class FileRepository
if (!string.IsNullOrEmpty(convDir))
{
var breakpointFile = Path.Combine(convDir, BREAKPOINT_FILE);
-
if (!File.Exists(breakpointFile))
{
- File.Create(breakpointFile);
+ File.WriteAllText(breakpointFile, "[]");
}
var content = File.ReadAllText(breakpointFile);
@@ -285,7 +284,7 @@ public partial class FileRepository
var breakpointFile = Path.Combine(convDir, BREAKPOINT_FILE);
if (!File.Exists(breakpointFile))
{
- File.Create(breakpointFile);
+ File.WriteAllText(breakpointFile, "[]");
}
var content = File.ReadAllText(breakpointFile);
@@ -380,7 +379,7 @@ public partial class FileRepository
return record;
}
- public PagedItems GetConversations(ConversationFilter filter)
+ public async ValueTask> GetConversations(ConversationFilter filter)
{
if (filter == null)
{
@@ -453,6 +452,10 @@ public partial class FileRepository
{
matched = matched && record.CreatedTime >= filter.StartTime.Value;
}
+ if (filter?.EndTime != null)
+ {
+ matched = matched && record.CreatedTime <= filter.EndTime.Value;
+ }
if (filter?.Tags != null && filter.Tags.Any())
{
matched = matched && !record.Tags.IsNullOrEmpty() && record.Tags.Exists(t => filter.Tags.Contains(t));
@@ -543,7 +546,7 @@ public partial class FileRepository
return new PagedItems
{
Items = records.OrderByDescending(x => x.CreatedTime).Skip(pager.Offset).Take(pager.Size),
- Count = records.Count(),
+ Count = records.Count()
};
}
@@ -705,7 +708,9 @@ public partial class FileRepository
if (conv == null
|| states.IsNullOrEmpty()
|| (!filter.AgentIds.IsNullOrEmpty() && !filter.AgentIds.Contains(conv.AgentId))
- || (!filter.UserIds.IsNullOrEmpty() && !filter.UserIds.Contains(conv.UserId)))
+ || (!filter.UserIds.IsNullOrEmpty() && !filter.UserIds.Contains(conv.UserId))
+ || (filter.StartTime.HasValue && conv.CreatedTime < filter.StartTime.Value)
+ || (filter.EndTime.HasValue && conv.CreatedTime > filter.EndTime.Value))
{
continue;
}
@@ -920,7 +925,6 @@ public partial class FileRepository
private bool SaveTruncatedDialogs(string dialogDir, List dialogs)
{
if (string.IsNullOrEmpty(dialogDir) || dialogs == null) return false;
- if (!File.Exists(dialogDir)) File.Create(dialogDir);
var texts = ParseDialogElements(dialogs);
File.WriteAllText(dialogDir, texts);
@@ -930,7 +934,6 @@ public partial class FileRepository
private bool SaveTruncatedStates(string stateDir, List states)
{
if (string.IsNullOrEmpty(stateDir) || states == null) return false;
- if (!File.Exists(stateDir)) File.Create(stateDir);
var stateStr = JsonSerializer.Serialize(states, _options);
File.WriteAllText(stateDir, stateStr);
@@ -940,7 +943,6 @@ public partial class FileRepository
private bool SaveTruncatedLatestStates(string latestStateDir, List states)
{
if (string.IsNullOrEmpty(latestStateDir) || states == null) return false;
- if (!File.Exists(latestStateDir)) File.Create(latestStateDir);
var latestStates = BuildLatestStates(states);
var stateStr = JsonSerializer.Serialize(latestStates, _options);
@@ -951,7 +953,6 @@ public partial class FileRepository
private bool SaveTruncatedBreakpoints(string breakpointDir, List breakpoints)
{
if (string.IsNullOrEmpty(breakpointDir) || breakpoints == null) return false;
- if (!File.Exists(breakpointDir)) File.Create(breakpointDir);
var breakpointStr = JsonSerializer.Serialize(breakpoints, _options);
File.WriteAllText(breakpointDir, breakpointStr);
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Crontab.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Crontab.cs
index 4a99027a..84f2794c 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Crontab.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Crontab.cs
@@ -63,7 +63,7 @@ public partial class FileRepository
}
- public PagedItems GetCrontabItems(CrontabItemFilter filter)
+ public async ValueTask> GetCrontabItems(CrontabItemFilter filter)
{
if (filter == null)
@@ -111,7 +111,7 @@ public partial class FileRepository
return new PagedItems
{
Items = records.OrderByDescending(x => x.CreatedTime).Skip(filter.Offset).Take(filter.Size),
- Count = records.Count(),
+ Count = records.Count()
};
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.KnowledgeBase.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.KnowledgeBase.cs
index b0a95027..ae9d0405 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.KnowledgeBase.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.KnowledgeBase.cs
@@ -15,17 +15,17 @@ public partial class FileRepository
}
var configFile = Path.Combine(vectorDir, COLLECTION_CONFIG_FILE);
+ if (!File.Exists(configFile))
+ {
+ File.WriteAllText(configFile, "[]");
+ }
+
if (reset)
{
File.WriteAllText(configFile, JsonSerializer.Serialize(configs ?? new(), _options));
return true;
}
- if (!File.Exists(configFile))
- {
- File.Create(configFile);
- }
-
var str = File.ReadAllText(configFile);
var savedConfigs = JsonSerializer.Deserialize>(str, _options) ?? new();
@@ -155,7 +155,7 @@ public partial class FileRepository
return true;
}
- public PagedItems GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, KnowledgeFileFilter filter)
+ public async ValueTask> GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, KnowledgeFileFilter filter)
{
if (string.IsNullOrWhiteSpace(collectionName)
|| string.IsNullOrWhiteSpace(vectorStoreProvider))
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Log.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Log.cs
index 3913a404..c0851401 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Log.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Log.cs
@@ -160,7 +160,7 @@ namespace BotSharp.Core.Repository
return true;
}
- public PagedItems GetInstructionLogs(InstructLogFilter filter)
+ public async ValueTask> GetInstructionLogs(InstructLogFilter filter)
{
if (filter == null)
{
@@ -202,6 +202,14 @@ namespace BotSharp.Core.Repository
{
matched = matched && filter.UserIds.Contains(log.UserId);
}
+ if (filter.StartTime.HasValue)
+ {
+ matched = matched && log.CreatedTime >= filter.StartTime.Value;
+ }
+ if (filter.EndTime.HasValue)
+ {
+ matched = matched && log.CreatedTime <= filter.EndTime.Value;
+ }
// Check states
if (matched && filter != null && !filter.States.IsNullOrEmpty())
@@ -308,7 +316,9 @@ namespace BotSharp.Core.Repository
if (log == null
|| log.InnerStates.IsNullOrEmpty()
|| (!filter.UserIds.IsNullOrEmpty() && !filter.UserIds.Contains(log.UserId))
- || (!filter.AgentIds.IsNullOrEmpty() && !filter.AgentIds.Contains(log.AgentId)))
+ || (!filter.AgentIds.IsNullOrEmpty() && !filter.AgentIds.Contains(log.AgentId))
+ || (filter.StartTime.HasValue && log.CreatedTime < filter.StartTime.Value)
+ || (filter.EndTime.HasValue && log.CreatedTime > filter.EndTime.Value))
{
continue;
}
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs
index 7bd5a1a9..09212e20 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs
@@ -105,7 +105,7 @@ public partial class FileRepository
File.WriteAllText(path, JsonSerializer.Serialize(user, _options));
}
- public PagedItems GetUsers(UserFilter filter)
+ public async ValueTask> GetUsers(UserFilter filter)
{
if (filter == null)
{
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs
index e0ac69a2..9dd19c09 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs
@@ -1,5 +1,6 @@
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Planning;
+using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.Core.Routing.Reasoning;
@@ -33,7 +34,7 @@ public class InstructExecutor : IExecutor
if (message.FunctionName != null)
{
var msg = RoleDialogModel.From(message, role: AgentRole.Function);
- await routing.InvokeFunction(message.FunctionName, msg, from: InvokeSource.Llm);
+ await routing.InvokeFunction(message.FunctionName, msg, options: new() { From = InvokeSource.Routing });
}
var agentId = routing.Context.GetCurrentAgentId();
@@ -59,11 +60,12 @@ public class InstructExecutor : IExecutor
{
var state = _services.GetRequiredService();
var useStreamMsg = state.GetState("use_stream_message");
- var ret = await routing.InvokeAgent(
- agentId,
- dialogs,
- from: InvokeSource.Routing,
- useStream: bool.TryParse(useStreamMsg, out var useStream) && useStream);
+ var options = new InvokeAgentOptions()
+ {
+ From = InvokeSource.Routing,
+ UseStream = bool.TryParse(useStreamMsg, out var useStream) && useStream
+ };
+ var ret = await routing.InvokeAgent(agentId, dialogs, options);
}
var response = dialogs.Last();
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs
index 6d2813d9..6acb58b2 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Routing;
@@ -7,9 +8,9 @@ public partial class RoutingService
public async Task InvokeAgent(
string agentId,
List dialogs,
- string from = InvokeSource.Manual,
- bool useStream = false)
+ InvokeAgentOptions? options = null)
{
+ options ??= InvokeAgentOptions.Default();
var agentService = _services.GetRequiredService();
var agent = await agentService.LoadAgent(agentId);
@@ -36,7 +37,7 @@ public partial class RoutingService
RoleDialogModel response;
var message = dialogs.Last();
- if (useStream)
+ if (options?.UseStream == true)
{
response = await chatCompletion.GetChatCompletionsStreamingAsync(agent, dialogs);
}
@@ -59,7 +60,7 @@ public partial class RoutingService
message.CurrentAgentId = agent.Id;
message.IsStreaming = response.IsStreaming;
- await InvokeFunction(message, dialogs, from: from, useStream: useStream);
+ await InvokeFunction(message, dialogs, options);
}
else
{
@@ -83,8 +84,7 @@ public partial class RoutingService
private async Task InvokeFunction(
RoleDialogModel message,
List dialogs,
- string from,
- bool useStream)
+ InvokeAgentOptions? options = null)
{
// execute function
// Save states
@@ -93,7 +93,8 @@ public partial class RoutingService
var routing = _services.GetRequiredService();
// Call functions
- await routing.InvokeFunction(message.FunctionName, message, from: from);
+ var funcOptions = options != null ? new InvokeFunctionOptions() { From = options.From } : null;
+ await routing.InvokeFunction(message.FunctionName, message, options: funcOptions);
// Pass execution result to LLM to get response
if (!message.StopCompletion)
@@ -120,7 +121,7 @@ public partial class RoutingService
// Send to Next LLM
var curAgentId = routing.Context.GetCurrentAgentId();
- await InvokeAgent(curAgentId, dialogs, from, useStream);
+ await InvokeAgent(curAgentId, dialogs, options);
}
}
else
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
index 20c691f8..dea0947d 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
@@ -1,12 +1,14 @@
-using BotSharp.Abstraction.Hooks;
+using BotSharp.Abstraction.Routing.Models;
+using BotSharp.Core.MessageHub;
using BotSharp.Core.Routing.Executor;
namespace BotSharp.Core.Routing;
public partial class RoutingService
{
- public async Task InvokeFunction(string name, RoleDialogModel message, string from = InvokeSource.Manual)
+ public async Task InvokeFunction(string name, RoleDialogModel message, InvokeFunctionOptions? options = null)
{
+ options ??= InvokeFunctionOptions.Default();
var currentAgentId = message.CurrentAgentId;
var agentService = _services.GetRequiredService();
var agent = await agentService.GetAgent(currentAgentId);
@@ -23,20 +25,22 @@ public partial class RoutingService
// Clone message
var clonedMessage = RoleDialogModel.From(message);
clonedMessage.FunctionName = name;
-
- var progressService = _services.GetService();
clonedMessage.Indication = await funcExecutor.GetIndicatorAsync(message);
- if (progressService?.OnFunctionExecuting != null)
+ var conv = _services.GetRequiredService();
+ var messageHub = _services.GetRequiredService>>();
+ messageHub.Push(new()
{
- await progressService.OnFunctionExecuting(clonedMessage);
- }
-
+ EventName = ChatEvent.OnIndicationReceived,
+ Data = clonedMessage,
+ RefId = conv.ConversationId
+ });
+
var hooks = _services.GetHooksOrderByPriority(clonedMessage.CurrentAgentId);
foreach (var hook in hooks)
{
hook.SetAgent(agent);
- await hook.OnFunctionExecuting(clonedMessage, from: from);
+ await hook.OnFunctionExecuting(clonedMessage, options);
}
bool result = false;
@@ -48,7 +52,7 @@ public partial class RoutingService
// After functions have been executed
foreach (var hook in hooks)
{
- await hook.OnFunctionExecuted(clonedMessage, from: from);
+ await hook.OnFunctionExecuted(clonedMessage, options);
}
// Set result to original message
@@ -64,7 +68,7 @@ public partial class RoutingService
}
catch (JsonException ex)
{
- _logger.LogError($"The input does not contain any JSON tokens:\r\n{message.Content}\r\n{ex.Message}");
+ _logger.LogError(ex, $"The input does not contain any JSON tokens:\r\n{message.Content}\r\n{ex.Message}");
message.StopCompletion = true;
message.Content = ex.Message;
}
@@ -72,7 +76,7 @@ public partial class RoutingService
{
message.StopCompletion = true;
message.Content = ex.Message;
- _logger.LogError(ex.ToString());
+ _logger.LogError(ex, ex.ToString());
}
// Make sure content has been populated
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs
index c66ef4b2..4e43cbd5 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs
@@ -53,11 +53,12 @@ public partial class RoutingService : IRoutingService
{
var state = _services.GetRequiredService();
var useStreamMsg = state.GetState("use_stream_message");
- var ret = await routing.InvokeAgent(
- agentId,
- dialogs,
- from: InvokeSource.Routing,
- useStream: bool.TryParse(useStreamMsg, out var useStream) && useStream);
+ var options = new InvokeAgentOptions()
+ {
+ From = InvokeSource.Routing,
+ UseStream = bool.TryParse(useStreamMsg, out var useStream) && useStream
+ };
+ var ret = await routing.InvokeAgent(agentId, dialogs, options);
}
var response = dialogs.Last();
diff --git a/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs b/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs
index ce5d72b8..5ae52a73 100644
--- a/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs
+++ b/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs
@@ -39,7 +39,7 @@ public class AgentTaskService : IAgentTaskService
else
{
var db = _services.GetRequiredService();
- var pagedTasks = db.GetAgentTasks(filter);
+ var pagedTasks = await db.GetAgentTasks(filter);
return await Task.FromResult(pagedTasks);
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs
index e4ebe311..f7dd61c6 100644
--- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs
+++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs
@@ -411,10 +411,18 @@ public class UserService : IUserService
return user;
}
+ [SharpCache(10)]
+ public async Task> GetUsers(List ids)
+ {
+ var db = _services.GetRequiredService();
+ var users = db.GetUserByIds(ids);
+ return users;
+ }
+
public async Task> GetUsers(UserFilter filter)
{
var db = _services.GetRequiredService();
- var users = db.GetUsers(filter);
+ var users = await db.GetUsers(filter);
return users;
}
diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs
index 0e8e2762..f67baef0 100644
--- a/src/Infrastructure/BotSharp.Core/Using.cs
+++ b/src/Infrastructure/BotSharp.Core/Using.cs
@@ -36,6 +36,9 @@ global using BotSharp.Abstraction.Loggers.Services;
global using BotSharp.Abstraction.Infrastructures.Events;
global using BotSharp.Abstraction.Templating.Constants;
global using BotSharp.Abstraction.Realtime.Models.Session;
+global using BotSharp.Abstraction.Conversations.Enums;
+global using BotSharp.Abstraction.Hooks;
+global using BotSharp.Abstraction.MessageHub.Models;
global using BotSharp.Core.Agents.Services;
global using BotSharp.Core.Conversations.Services;
global using BotSharp.Core.Infrastructures;
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/functions/get_fun_events.json b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/functions/get_fun_events.json
new file mode 100644
index 00000000..94b6abbf
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/functions/get_fun_events.json
@@ -0,0 +1,15 @@
+{
+ "name": "get_fun_events",
+ "description": "Get fun events information for user.",
+ "visibility_expression": "{% if states.channel == 'email' %}visible{% endif %}",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "city": {
+ "type": "string",
+ "description": "The city where user wants to find fun events."
+ }
+ },
+ "required": [ "city" ]
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/functions/get_weather.json b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/functions/get_weather.json
index 0fd0a459..ab73340f 100644
--- a/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/functions/get_weather.json
+++ b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/functions/get_weather.json
@@ -1,6 +1,7 @@
{
"name": "get_weather",
"description": "Get weather information for user.",
+ "visibility_expression": "{% if states.channel != 'email' %}visible{% endif %}",
"parameters": {
"type": "object",
"properties": {
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/select_file_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-file-select_file_instruction.liquid
similarity index 100%
rename from src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/select_file_prompt.liquid
rename to src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-file-select_file_instruction.liquid
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
index 4543fe19..b6950e04 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
@@ -14,8 +14,7 @@ public class AgentController : ControllerBase
public AgentController(
IAgentService agentService,
IUserIdentity user,
- IServiceProvider services
- )
+ IServiceProvider services)
{
_agentService = agentService;
_user = user;
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
index bd131ca1..45f53c0c 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
@@ -1,6 +1,8 @@
using BotSharp.Abstraction.Files.Constants;
using BotSharp.Abstraction.Files.Enums;
using BotSharp.Abstraction.Files.Utilities;
+using BotSharp.Abstraction.MessageHub.Models;
+using BotSharp.Abstraction.MessageHub.Services;
using BotSharp.Abstraction.Options;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Users.Dtos;
@@ -64,11 +66,11 @@ public class ConversationController : ControllerBase
var agents = await agentService.GetAgentOptions(agentIds);
var userIds = list.Select(x => x.User.Id).ToList();
- var users = await userService.GetUsers(new UserFilter { UserIds = userIds, Size = filter.Pager.Size });
+ var users = await userService.GetUsers(userIds);
foreach (var item in list)
{
- user = users.Items.FirstOrDefault(x => x.Id == item.User.Id);
+ user = users.FirstOrDefault(x => x.Id == item.User.Id);
item.User = UserViewModel.FromUser(user);
var agent = agents.FirstOrDefault(x => x.Id == item.AgentId);
item.AgentName = agent?.Name ?? "Unkown";
@@ -343,6 +345,9 @@ public class ConversationController : ControllerBase
[FromRoute] string conversationId,
[FromBody] NewMessageModel input)
{
+ var observer = _services.GetRequiredService();
+ using var container = observer.SubscribeObservers>(conversationId);
+
var conv = _services.GetRequiredService();
var inputMsg = new RoleDialogModel(AgentRole.User, input.Text)
{
@@ -357,7 +362,6 @@ public class ConversationController : ControllerBase
SetStates(conv, input);
var response = new ChatResponseModel();
-
await conv.SendMessage(agentId, inputMsg,
replyMessage: input.Postback,
async msg =>
@@ -381,6 +385,12 @@ public class ConversationController : ControllerBase
[HttpPost("/conversation/{agentId}/{conversationId}/sse")]
public async Task SendMessageSse([FromRoute] string agentId, [FromRoute] string conversationId, [FromBody] NewMessageModel input)
{
+ var observer = _services.GetRequiredService();
+ using var container = observer.SubscribeObservers>(conversationId, listeners: new()
+ {
+ { ChatEvent.OnIndicationReceived, async data => await OnReceiveToolCallIndication(conversationId, data.Data) }
+ });
+
var conv = _services.GetRequiredService();
var inputMsg = new RoleDialogModel(AgentRole.User, input.Text)
{
@@ -406,7 +416,6 @@ public class ConversationController : ControllerBase
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.ContentType, "text/event-stream");
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.CacheControl, "no-cache");
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.Connection, "keep-alive");
- InitProgressService(conversationId);
await conv.SendMessage(agentId, inputMsg,
replyMessage: input.Postback,
@@ -430,23 +439,18 @@ public class ConversationController : ControllerBase
// await OnEventCompleted(Response);
}
- private void InitProgressService(string conversationId)
+ private async Task OnReceiveToolCallIndication(string conversationId, RoleDialogModel msg)
{
- var progressService = _services.GetService();
- progressService.OnFunctionExecuting = async msg =>
+ var indicator = new ChatResponseModel
{
- var indicator = new ChatResponseModel
- {
- ConversationId = conversationId,
- MessageId = msg.MessageId,
- Text = msg.Indication,
- Function = "indicating",
- Instruction = msg.Instruction,
- States = new Dictionary()
- };
- await OnChunkReceived(Response, indicator);
+ ConversationId = conversationId,
+ MessageId = msg.MessageId,
+ Text = msg.Indication,
+ Function = "indicating",
+ Instruction = msg.Instruction,
+ States = new Dictionary()
};
- progressService.OnFunctionExecuted = async msg => { };
+ await OnChunkReceived(Response, indicator);
}
#endregion
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs
index 0f7b440c..f8bcacef 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs
@@ -2,6 +2,7 @@ using BotSharp.Abstraction.Files.Utilities;
using BotSharp.Abstraction.Graph.Models;
using BotSharp.Abstraction.VectorStorage.Models;
using BotSharp.OpenAPI.ViewModels.Knowledges;
+using BotSharp.OpenAPI.ViewModels.Knowledges.Request;
namespace BotSharp.OpenAPI.Controllers;
@@ -59,6 +60,7 @@ public class KnowledgeBaseController : ControllerBase
var options = new VectorSearchOptions
{
Fields = request.Fields,
+ FilterGroups = request.FilterGroups,
Limit = request.Limit ?? 5,
Confidence = request.Confidence ?? 0.5f,
WithVector = request.WithVector
@@ -72,8 +74,7 @@ public class KnowledgeBaseController : ControllerBase
public async Task> GetPagedVectorCollectionData([FromRoute] string collection, [FromBody] VectorFilter filter)
{
var data = await _knowledgeService.GetPagedVectorCollectionData(collection, filter);
- var items = data.Items?.Select(x => VectorKnowledgeViewModel.From(x))?
- .ToList() ?? new List();
+ var items = data.Items?.Select(x => VectorKnowledgeViewModel.From(x))?.ToList() ?? [];
return new StringIdPagedItems
{
@@ -97,6 +98,19 @@ public class KnowledgeBaseController : ControllerBase
return created;
}
+ [HttpGet("/knowledge/vector/{collection}/points")]
+ public async Task> GetVectorCollectionData([FromRoute] string collection, [FromQuery] QueryVectorDataRequest request)
+ {
+ var options = new VectorQueryOptions
+ {
+ WithPayload = request.WithPayload,
+ WithVector = request.WithVector
+ };
+
+ var points = await _knowledgeService.GetVectorCollectionData(collection, request.Ids, options);
+ return points.Select(x => VectorKnowledgeViewModel.From(x));
+ }
+
[HttpPut("/knowledge/vector/{collection}/update")]
public async Task