Merge branch 'master' of https://github.com/SciSharp/BotSharp into features/refine-twilio-stream
This commit is contained in:
commit
19185addd3
11
BotSharp.sln
11
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}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
<PackageVersion Include="Nanoid" Version="3.1.0" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="8.1.2" />
|
||||
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="8.0.1" />
|
||||
<PackageVersion Include="Anthropic.SDK" Version="5.1.1" />
|
||||
<PackageVersion Include="Anthropic.SDK" Version="5.5.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
||||
<PackageVersion Include="NAudio" Version="2.2.1" />
|
||||
<PackageVersion Include="NAudio.Core" Version="2.2.1" />
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
<PackageVersion Include="Whisper.net.Runtime" Version="1.8.1" />
|
||||
<PackageVersion Include="NCrontab" Version="3.3.3" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="2.2.0-beta.5" />
|
||||
<PackageVersion Include="OpenAI" Version="2.2.0" />
|
||||
<PackageVersion Include="OpenAI" Version="2.3.0" />
|
||||
<PackageVersion Include="MailKit" Version="4.11.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.8" />
|
||||
<PackageVersion Include="MySql.Data" Version="9.0.0" />
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
<PackageVersion Include="Sdcb.PaddleOCR.Models.LocalV3" Version="2.7.0.1" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="8.0.14" />
|
||||
<PackageVersion Include="pythonnet" Version="3.0.4" />
|
||||
<PackageVersion Include="Qdrant.Client" Version="1.13.0" />
|
||||
<PackageVersion Include="Qdrant.Client" Version="1.15.0" />
|
||||
<PackageVersion Include="Selenium.WebDriver" Version="4.27.0" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.12.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Abstractions" Version="1.16.0" />
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,4 +34,11 @@ public class AgentLlmConfig
|
|||
[JsonPropertyName("max_output_tokens")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public int? MaxOutputTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reasoning effort level
|
||||
/// </summary>
|
||||
[JsonPropertyName("reasoning_effort_level")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? ReasoningEffortLevel { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,4 +30,5 @@ public interface IWebBrowser
|
|||
Task<BrowserActionResult> SendHttpRequest(MessageInfo message, HttpRequestParams actionParams);
|
||||
Task<BrowserActionResult> GetAttributeValue(MessageInfo message, ElementLocatingArgs location);
|
||||
Task<BrowserActionResult> SetAttributeValue(MessageInfo message, ElementLocatingArgs location);
|
||||
string GetCurrentUrl(MessageInfo message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ public interface IConversationHook : IHookBase
|
|||
/// <param name="message"></param>
|
||||
/// <param name="from"></param>
|
||||
/// <returns></returns>
|
||||
Task OnFunctionExecuting(RoleDialogModel message, string from = InvokeSource.Manual);
|
||||
Task OnFunctionExecuting(RoleDialogModel message, InvokeFunctionOptions? options = null);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the function calling completed.
|
||||
|
|
@ -69,7 +69,7 @@ public interface IConversationHook : IHookBase
|
|||
/// <param name="message"></param>
|
||||
/// <param name="from"></param>
|
||||
/// <returns></returns>
|
||||
Task OnFunctionExecuted(RoleDialogModel message, string from = InvokeSource.Manual);
|
||||
Task OnFunctionExecuted(RoleDialogModel message, InvokeFunctionOptions? options = null);
|
||||
|
||||
Task OnResponseGenerated(RoleDialogModel message);
|
||||
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
using BotSharp.Abstraction.Messaging.Enums;
|
||||
|
||||
namespace BotSharp.Abstraction.Conversations.Models;
|
||||
|
||||
public class ConversationSenderActionModel
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ public class RoleDialogModel : ITrackableMessage
|
|||
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
|
||||
public bool IsStreaming { get; set; }
|
||||
|
||||
private RoleDialogModel()
|
||||
public RoleDialogModel()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<VectorCollectionDetails?> GetVectorCollectionDetails(string collectionName);
|
||||
Task<IEnumerable<VectorSearchResult>> SearchVectorKnowledge(string query, string collectionName, VectorSearchOptions options);
|
||||
Task<StringIdPagedItems<VectorSearchResult>> GetPagedVectorCollectionData(string collectionName, VectorFilter filter);
|
||||
Task<IEnumerable<VectorCollectionData>> GetVectorCollectionData(string collectionName, IEnumerable<string> ids, VectorQueryOptions? options = null);
|
||||
Task<bool> DeleteVectorCollectionData(string collectionName, string id);
|
||||
Task<bool> DeleteVectorCollectionAllData(string collectionName);
|
||||
Task<bool> CreateVectorCollectionData(string collectionName, VectorCreateModel create);
|
||||
|
|
@ -69,6 +71,11 @@ public interface IKnowledgeService
|
|||
Task<bool> DeleteVectorCollectionSnapshot(string collectionName, string snapshotName);
|
||||
#endregion
|
||||
|
||||
#region Index
|
||||
Task<SuccessFailResponse<string>> CreateVectorCollectionPayloadIndexes(string collectionName, IEnumerable<CreateVectorCollectionIndexOptions> options);
|
||||
Task<SuccessFailResponse<string>> DeleteVectorCollectionPayloadIndexes(string collectionName, IEnumerable<DeleteVectorCollectionIndexOptions> options);
|
||||
#endregion
|
||||
|
||||
#region Common
|
||||
Task<bool> RefreshVectorKnowledgeConfigs(VectorCollectionConfigsModel configs);
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -47,7 +47,15 @@ public class LlmModelSetting
|
|||
/// </summary>
|
||||
public int Dimension { get; set; }
|
||||
|
||||
public LlmCost Cost { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Settings for reasoning model
|
||||
/// </summary>
|
||||
public ReasoningSetting? Reasoning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Settings for llm cost
|
||||
/// </summary>
|
||||
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; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cost per 1K tokens
|
||||
/// </summary>
|
||||
public class LlmCost
|
||||
public class LlmCostSetting
|
||||
{
|
||||
// Input
|
||||
public float TextInputCost { get; set; } = 0f;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
namespace BotSharp.Abstraction.MessageHub.Models;
|
||||
|
||||
public class HubObserveData<TData> : ObserveDataBase where TData : class, new()
|
||||
{
|
||||
public TData Data { get; set; } = null!;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace BotSharp.Abstraction.MessageHub.Models;
|
||||
|
||||
public class ObserveDataBase
|
||||
{
|
||||
public string EventName { get; set; } = null!;
|
||||
public string RefId { get; set; } = null!;
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
using BotSharp.Abstraction.MessageHub.Observers;
|
||||
|
||||
namespace BotSharp.Abstraction.MessageHub.Models;
|
||||
|
||||
public class ObserverSubscription<T>
|
||||
{
|
||||
public IBotSharpObserver<T> Observer { get; set; }
|
||||
public IDisposable Subscription { get; set; }
|
||||
|
||||
public ObserverSubscription()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ObserverSubscription(
|
||||
IBotSharpObserver<T> observer,
|
||||
IDisposable subscription)
|
||||
{
|
||||
Observer = observer;
|
||||
Subscription = subscription;
|
||||
}
|
||||
|
||||
public void UnSubscribe()
|
||||
{
|
||||
Observer.Deactivate();
|
||||
Subscription.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
namespace BotSharp.Abstraction.MessageHub.Observers;
|
||||
|
||||
public abstract class BotSharpObserverBase<T> : IBotSharpObserver<T>
|
||||
{
|
||||
private bool _active = false;
|
||||
protected Dictionary<string, Func<T, Task>> _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<string, Func<T, Task>> listeners)
|
||||
{
|
||||
_listeners = listeners;
|
||||
}
|
||||
|
||||
public virtual void OnCompleted()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnError(Exception error)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnNext(T value)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
namespace BotSharp.Abstraction.MessageHub.Observers;
|
||||
|
||||
public interface IBotSharpObserver<T> : IObserver<T>
|
||||
{
|
||||
string Name { get; }
|
||||
bool Active { get; }
|
||||
|
||||
void SetEventListeners(Dictionary<string, Func<T, Task>> listeners);
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
using BotSharp.Abstraction.MessageHub.Models;
|
||||
|
||||
namespace BotSharp.Abstraction.MessageHub.Services;
|
||||
|
||||
public interface IObserverService
|
||||
{
|
||||
IDisposable SubscribeObservers<T>(
|
||||
string refId,
|
||||
IEnumerable<string>? names = null,
|
||||
Dictionary<string, Func<T, Task>>? listeners = null) where T : ObserveDataBase;
|
||||
|
||||
void UnSubscribeObservers<T>(IEnumerable<string>? names = null) where T : ObserveDataBase;
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace BotSharp.Abstraction.Models;
|
||||
|
||||
public class SuccessFailResponse<T>
|
||||
{
|
||||
public List<T> Success { get; set; } = [];
|
||||
public List<T> Fail { get; set; } = [];
|
||||
}
|
||||
|
|
@ -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!;
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
namespace BotSharp.Abstraction.Observables.Models;
|
||||
|
||||
public abstract class ObserveDataBase
|
||||
{
|
||||
public IServiceProvider ServiceProvider { get; set; } = null!;
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<bool> 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<bool> ShouldReconnect(RealtimeHubConnection conn, RoleDialogModel message)
|
||||
=> Task.FromResult(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// Agent task id
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ public class ConversationStateKeysFilter
|
|||
public bool PreLoad { get; set; }
|
||||
public List<string>? AgentIds { get; set; }
|
||||
public List<string>? UserIds { get; set; }
|
||||
public DateTime? StartTime { get; set; }
|
||||
public DateTime? EndTime { get; set; }
|
||||
|
||||
public ConversationStateKeysFilter()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ public class InstructLogFilter : Pagination
|
|||
public List<string>? TemplateNames { get; set; }
|
||||
public List<string>? UserIds { get; set; }
|
||||
public List<KeyValue>? States { get; set; }
|
||||
public DateTime? StartTime { get; set; }
|
||||
public DateTime? EndTime { get; set; }
|
||||
|
||||
public static InstructLogFilter Empty()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ public class InstructLogKeysFilter
|
|||
public bool PreLoad { get; set; }
|
||||
public List<string>? AgentIds { get; set; }
|
||||
public List<string>? UserIds { get; set; }
|
||||
public DateTime? StartTime { get; set; }
|
||||
public DateTime? EndTime { get; set; }
|
||||
|
||||
public InstructLogKeysFilter()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<string> userIds, bool isDisable) => throw new NotImplementedException();
|
||||
PagedItems<User> GetUsers(UserFilter filter) => throw new NotImplementedException();
|
||||
ValueTask<PagedItems<User>> GetUsers(UserFilter filter) => throw new NotImplementedException();
|
||||
List<User> 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<AgentTask> GetAgentTasks(AgentTaskFilter filter)
|
||||
ValueTask<PagedItems<AgentTask>> 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<Conversation> GetConversations(ConversationFilter filter)
|
||||
ValueTask<PagedItems<Conversation>> 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<InstructionLogModel> logs)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
PagedItems<InstructionLogModel> GetInstructionLogs(InstructLogFilter filter)
|
||||
ValueTask<PagedItems<InstructionLogModel>> GetInstructionLogs(InstructLogFilter filter)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
List<string> GetInstructionLogSearchKeys(InstructLogKeysFilter filter)
|
||||
|
|
@ -227,7 +227,7 @@ public interface IBotSharpRepository : IHaveServiceProvider
|
|||
/// <returns></returns>
|
||||
bool DeleteKnolwedgeBaseFileMeta(string collectionName, string vectorStoreProvider, Guid? fileId = null)
|
||||
=> throw new NotImplementedException();
|
||||
PagedItems<KnowledgeDocMetaData> GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, KnowledgeFileFilter filter)
|
||||
ValueTask<PagedItems<KnowledgeDocMetaData>> 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<CrontabItem> GetCrontabItems(CrontabItemFilter filter)
|
||||
ValueTask<PagedItems<CrontabItem>> GetCrontabItems(CrontabItemFilter filter)
|
||||
=> throw new NotImplementedException();
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ public interface IRoutingService
|
|||
/// <returns></returns>
|
||||
RoutingRule[] GetRulesByAgentId(string id);
|
||||
|
||||
Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs, string from = InvokeSource.Manual, bool useStream = false);
|
||||
Task<bool> InvokeFunction(string name, RoleDialogModel messages, string from = InvokeSource.Manual);
|
||||
Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs, InvokeAgentOptions? options = null);
|
||||
Task<bool> InvokeFunction(string name, RoleDialogModel messages, InvokeFunctionOptions? options = null);
|
||||
Task<RoleDialogModel> InstructLoop(Agent agent, RoleDialogModel message, List<RoleDialogModel> dialogs);
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,19 +3,23 @@ namespace BotSharp.Abstraction.SideCar.Models;
|
|||
public class SideCarOptions
|
||||
{
|
||||
public bool IsInheritStates { get; set; }
|
||||
public IEnumerable<string>? InheritStateKeys { get; set; }
|
||||
public HashSet<string>? InheritStateKeys { get; set; }
|
||||
public HashSet<string>? ExcludedStateKeys { get; set; }
|
||||
|
||||
public static SideCarOptions Empty()
|
||||
{
|
||||
return new();
|
||||
}
|
||||
|
||||
public static SideCarOptions InheritStates(IEnumerable<string>? targetStates = null)
|
||||
public static SideCarOptions InheritStates(
|
||||
HashSet<string>? includedStates = null,
|
||||
HashSet<string>? excludedStates = null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
IsInheritStates = true,
|
||||
InheritStateKeys = targetStates
|
||||
InheritStateKeys = includedStates,
|
||||
ExcludedStateKeys = excludedStates
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ namespace BotSharp.Abstraction.Users;
|
|||
public interface IUserService
|
||||
{
|
||||
Task<User> GetUser(string id);
|
||||
Task<List<User>> GetUsers(List<string> ids);
|
||||
Task<PagedItems<User>> GetUsers(UserFilter filter);
|
||||
Task<List<User>> SearchLoginUsers(User filter);
|
||||
Task<User?> GetUserDetails(string userId, bool includeAgent = false);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,6 @@ public class Pagination : ICacheKey
|
|||
|
||||
public class PagedItems<T>
|
||||
{
|
||||
public int Count { get; set; }
|
||||
public long Count { get; set; }
|
||||
public IEnumerable<T> Items { get; set; } = new List<T>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ public interface IVectorDb
|
|||
=> throw new NotImplementedException();
|
||||
Task<StringIdPagedItems<VectorCollectionData>> GetPagedCollectionData(string collectionName, VectorFilter filter)
|
||||
=> throw new NotImplementedException();
|
||||
Task<IEnumerable<VectorCollectionData>> GetCollectionData(string collectionName, IEnumerable<Guid> ids,
|
||||
bool withPayload = false, bool withVector = false)
|
||||
Task<IEnumerable<VectorCollectionData>> GetCollectionData(string collectionName, IEnumerable<Guid> ids, VectorQueryOptions? options = null)
|
||||
=> throw new NotImplementedException();
|
||||
Task<bool> CreateCollection(string collectionName, int dimension)
|
||||
=> throw new NotImplementedException();
|
||||
|
|
@ -23,8 +22,7 @@ public interface IVectorDb
|
|||
=> throw new NotImplementedException();
|
||||
Task<bool> Upsert(string collectionName, Guid id, float[] vector, string text, Dictionary<string, object>? payload = null)
|
||||
=> throw new NotImplementedException();
|
||||
Task<IEnumerable<VectorCollectionData>> Search(string collectionName, float[] vector, IEnumerable<string>? fields,
|
||||
int limit = 5, float confidence = 0.5f, bool withVector = false)
|
||||
Task<IEnumerable<VectorCollectionData>> Search(string collectionName, float[] vector, VectorSearchOptions? options = null)
|
||||
=> throw new NotImplementedException();
|
||||
Task<bool> DeleteCollectionData(string collectionName, List<Guid> ids)
|
||||
=> throw new NotImplementedException();
|
||||
|
|
@ -40,4 +38,9 @@ public interface IVectorDb
|
|||
=> throw new NotImplementedException();
|
||||
Task<bool> DeleteCollectionShapshot(string collectionName, string snapshotName)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
Task<bool> CreateCollectionPayloadIndex(string collectionName, CreateVectorCollectionIndexOptions options)
|
||||
=> throw new NotImplementedException();
|
||||
Task<bool> DeleteCollectionPayloadIndex(string collectionName, DeleteVectorCollectionIndexOptions options)
|
||||
=> throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -6,15 +6,23 @@ public class VectorFilter : StringIdPagination
|
|||
public bool WithVector { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For keyword search
|
||||
/// Filter group: each item contains a logical operator and a list of key-value pairs
|
||||
/// </summary>
|
||||
[JsonPropertyName("search_pairs")]
|
||||
public IEnumerable<KeyValue>? SearchPairs { get; set; }
|
||||
|
||||
[JsonPropertyName("filter_groups")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public IEnumerable<VectorFilterGroup>? FilterGroups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Included payload keys
|
||||
/// Order by a specific field
|
||||
/// </summary>
|
||||
[JsonPropertyName("included_payloads")]
|
||||
public IEnumerable<string>? IncludedPayloads { get; set; }
|
||||
[JsonPropertyName("order_by")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public VectorSort? OrderBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Included payload fields
|
||||
/// </summary>
|
||||
[JsonPropertyName("fields")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public IEnumerable<string>? Fields { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
namespace BotSharp.Abstraction.VectorStorage.Models;
|
||||
|
||||
public class VectorFilterGroup
|
||||
{
|
||||
[JsonPropertyName("filters")]
|
||||
public IEnumerable<KeyValue>? Filters { get; set; }
|
||||
|
||||
[JsonPropertyName("filter_operator")]
|
||||
public string FilterOperator { get; set; } = "or";
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,21 @@ namespace BotSharp.Abstraction.VectorStorage.Models;
|
|||
|
||||
public class VectorSearchOptions
|
||||
{
|
||||
public IEnumerable<string>? Fields { get; set; } = new List<string> { KnowledgePayloadName.Text, KnowledgePayloadName.Answer };
|
||||
public IEnumerable<string>? Fields { get; set; } = [KnowledgePayloadName.Text, KnowledgePayloadName.Answer];
|
||||
public IEnumerable<VectorFilterGroup>? 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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ public class CrontabService : ICrontabService, ITaskFeeder
|
|||
public async Task<List<CrontabItem>> GetCrontable()
|
||||
{
|
||||
var repo = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var crontable = repo.GetCrontabItems(CrontabItemFilter.Empty());
|
||||
var crontable = await repo.GetCrontabItems(CrontabItemFilter.Empty());
|
||||
|
||||
// Add fixed crontab items from cronsources
|
||||
var fixedCrantabItems = crontable.Items.ToList();
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ public class CrontabWatcher : BackgroundService
|
|||
{
|
||||
var cron = services.GetRequiredService<ICrontabService>();
|
||||
var crons = await cron.GetCrontable();
|
||||
var settings = services.GetRequiredService<CrontabSettings>();
|
||||
var publisher = services.GetService<IEventPublisher>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<IRealtimeHub>();
|
||||
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<IRealtimeHub>();
|
||||
if (from != InvokeSource.Llm || hub.HubConn == null)
|
||||
if (options?.From != InvokeSource.Llm || hub.HubConn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<IRealtimeHook>(_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<IRealtimeHook>(_conn.CurrentAgentId);
|
||||
foreach (var hook in realtimeHooks)
|
||||
{
|
||||
isReconnect = await hook.ShouldReconnect(_conn);
|
||||
if (isReconnect) break;
|
||||
}
|
||||
|
||||
if (isReconnect)
|
||||
{
|
||||
await _completer.Reconnect(_conn);
|
||||
}
|
||||
},
|
||||
onConversationItemCreated: async response =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<List<StateKeyValue>>(JsonSerializer.Serialize(preValues));
|
||||
var innerStates = new ConversationState(copy ?? []);
|
||||
var state = _services.GetRequiredService<IConversationStateService>();
|
||||
|
||||
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<string, Type>
|
||||
{
|
||||
{ "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<int>(preVal) + ParseNumber<int>(curVal),
|
||||
Type t when t == typeof(float) => ParseNumber<float>(preVal) + ParseNumber<float>(curVal),
|
||||
_ => default
|
||||
};
|
||||
|
||||
var cur = curState.GetValueOrDefault(pair.Key);
|
||||
if (cur?.Values?.LastOrDefault() != null)
|
||||
{
|
||||
cur.Values.Last().Data = $"{data}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private T ParseNumber<T>(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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +86,6 @@
|
|||
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\conversation.summary.liquid" />
|
||||
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\response_with_function.liquid" />
|
||||
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\translation_prompt.liquid" />
|
||||
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\select_file_prompt.liquid" />
|
||||
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-routing-fallback_to_router.fn.liquid" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\agent.json" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\instructions\instruction.liquid" />
|
||||
|
|
@ -97,7 +96,9 @@
|
|||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.simulator.liquid" />
|
||||
<None Remove="data\plugins\config.json" />
|
||||
|
||||
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-file-select_file_instruction.liquid" />
|
||||
<None Remove="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\functions\get_weather.json" />
|
||||
<None Remove="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\functions\get_fun_events.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -188,7 +189,7 @@
|
|||
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\instructions\instruction.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\select_file_prompt.liquid">
|
||||
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-file-select_file_instruction.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.simulator.liquid">
|
||||
|
|
@ -211,6 +212,9 @@
|
|||
<Content Include="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\functions\get_weather.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\functions\get_fun_events.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -242,5 +246,4 @@
|
|||
<Pack>true</Pack>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<GoogleApiSettings>("GoogleApi");
|
||||
});
|
||||
|
||||
services.AddSingleton<MessageHub<HubObserveData>>();
|
||||
// Observer and observable
|
||||
services.AddSingleton<MessageHub<HubObserveData<RoleDialogModel>>>();
|
||||
services.AddScoped<ObserverSubscriptionContainer<HubObserveData<RoleDialogModel>>>();
|
||||
services.AddScoped<IBotSharpObserver<HubObserveData<RoleDialogModel>>, ConversationObserver>();
|
||||
services.AddScoped<IObserverService, ObserverService>();
|
||||
|
||||
services.AddScoped<IConversationStorage, ConversationStorage>();
|
||||
services.AddScoped<IConversationService, ConversationService>();
|
||||
services.AddScoped<IConversationProgressService, ConversationProgressService>();
|
||||
services.AddScoped<IConversationStateService, ConversationStateService>();
|
||||
services.AddScoped<ITranslationService, TranslationService>();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
namespace BotSharp.Core.Conversations.Services
|
||||
{
|
||||
public class ConversationProgressService : IConversationProgressService
|
||||
{
|
||||
|
||||
public FunctionExecuting OnFunctionExecuting { get; set; }
|
||||
|
||||
|
||||
public FunctionExecuted OnFunctionExecuted { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ public partial class ConversationService : IConversationService
|
|||
}
|
||||
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var conversations = db.GetConversations(filter);
|
||||
var conversations = await db.GetConversations(filter);
|
||||
return conversations;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<bool> Execute(RoleDialogModel message)
|
||||
{
|
||||
var args = JsonSerializer.Deserialize<WeatherLocation>(message.FunctionArgs);
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var messageHub = _services.GetRequiredService<MessageHub<HubObserveData<RoleDialogModel>>>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<bool> Execute(RoleDialogModel message)
|
||||
{
|
||||
var args = JsonSerializer.Deserialize<WeatherLocation>(message.FunctionArgs);
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var messageHub = _services.GetRequiredService<MessageHub<HubObserveData<RoleDialogModel>>>();
|
||||
|
||||
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<IConversationSideCar>();
|
||||
if (sidecar != null)
|
||||
{
|
||||
var text = $"I want to know fun events in {args?.City}";
|
||||
var states = new List<MessageState>
|
||||
{
|
||||
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; }
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -18,22 +18,18 @@ public partial class LoggerService
|
|||
|
||||
filter.UserIds = !isAdmin && user?.Id != null ? [user.Id] : null;
|
||||
|
||||
var agents = new List<Agent>();
|
||||
var users = new List<User>();
|
||||
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var logs = db.GetInstructionLogs(filter);
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
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 =>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
using System.Reactive.Subjects;
|
||||
|
||||
namespace BotSharp.Core.Observables.Queues;
|
||||
namespace BotSharp.Core.MessageHub;
|
||||
|
||||
public class MessageHub<T> where T : class
|
||||
public class MessageHub<T> where T : ObserveDataBase
|
||||
{
|
||||
private readonly ILogger<MessageHub<T>> _logger;
|
||||
private readonly ISubject<T> _observable = new Subject<T>();
|
||||
private readonly ISubject<T> _observable = Subject.Synchronize(new Subject<T>());
|
||||
public IObservable<T> Events => _observable;
|
||||
|
||||
public MessageHub(ILogger<MessageHub<T>> logger)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
using BotSharp.Abstraction.MessageHub.Observers;
|
||||
|
||||
namespace BotSharp.Core.MessageHub.Observers;
|
||||
|
||||
public class ConversationObserver : BotSharpObserverBase<HubObserveData<RoleDialogModel>>
|
||||
{
|
||||
private readonly ILogger<ConversationObserver> _logger;
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public ConversationObserver(
|
||||
IServiceProvider services,
|
||||
ILogger<ConversationObserver> 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<RoleDialogModel> value)
|
||||
{
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ObserverService> _logger;
|
||||
|
||||
public ObserverService(
|
||||
IServiceProvider services,
|
||||
ILogger<ObserverService> logger)
|
||||
{
|
||||
_services = services;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IDisposable SubscribeObservers<T>(
|
||||
string refId,
|
||||
IEnumerable<string>? names = null,
|
||||
Dictionary<string, Func<T, Task>>? listeners = null) where T : ObserveDataBase
|
||||
{
|
||||
var container = _services.GetRequiredService<ObserverSubscriptionContainer<T>>();
|
||||
var observers = _services.GetServices<IBotSharpObserver<T>>()
|
||||
.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<ObserverSubscription<T>>();
|
||||
var messageHub = _services.GetRequiredService<MessageHub<T>>();
|
||||
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<T>(IEnumerable<string>? names = null) where T : ObserveDataBase
|
||||
{
|
||||
var container = _services.GetRequiredService<ObserverSubscriptionContainer<T>>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
namespace BotSharp.Core.MessageHub.Services;
|
||||
|
||||
public class ObserverSubscriptionContainer<T> : IDisposable
|
||||
{
|
||||
private readonly ILogger<ObserverSubscriptionContainer<T>> _logger;
|
||||
|
||||
private List<ObserverSubscription<T>> _subscriptions = [];
|
||||
private bool _disposed = false;
|
||||
|
||||
public ObserverSubscriptionContainer(
|
||||
ILogger<ObserverSubscriptionContainer<T>> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ObserverSubscription<T>> GetSubscriptions(IEnumerable<string>? names = null)
|
||||
{
|
||||
if (!names.IsNullOrEmpty())
|
||||
{
|
||||
return _subscriptions.Where(x => names.Contains(x.Observer.Name)).ToList();
|
||||
}
|
||||
return _subscriptions;
|
||||
}
|
||||
|
||||
public void Append(List<ObserverSubscription<T>> subscriptions)
|
||||
{
|
||||
_subscriptions = _subscriptions.Concat(subscriptions).DistinctBy(x => x.Observer.Name).ToList();
|
||||
}
|
||||
|
||||
public void Remove(IEnumerable<string>? 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Agent> GetAgents(AgentFilter filter)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public List<UserAgent> 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<string> GetAgentResponses(string agentId, string prefix, string intent)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public void BulkInsertAgents(List<Agent> agents)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public void BulkInsertUserAgents(List<UserAgent> userAgents)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public bool DeleteAgents()
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public bool DeleteAgent(string agentId)
|
||||
=> throw new NotImplementedException();
|
||||
#endregion
|
||||
|
||||
#region Agent Task
|
||||
public PagedItems<AgentTask> 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<AgentTask> tasks)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public void UpdateAgentTask(AgentTask task, AgentTaskField field)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public bool DeleteAgentTask(string agentId, List<string> 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<string> conversationIds)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public Conversation GetConversation(string conversationId, bool isLoadStates = false)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public PagedItems<Conversation> GetConversations(ConversationFilter filter)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public List<Conversation> GetLastConversations()
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public List<string> GetIdleConversations(int batchSize, int messageLimit, int bufferHours, IEnumerable<string> excludeAgentIds)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
[SideCar]
|
||||
public List<DialogElement> GetConversationDialogs(string conversationId)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public ConversationState GetConversationStates(string conversationId)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
[SideCar]
|
||||
public void AppendConversationDialogs(string conversationId, List<DialogElement> 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<string> tags)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public bool AppendConversationTags(string conversationId, List<string> 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<StateKeyValue> states)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public void UpdateConversationStatus(string conversationId, string status)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public List<string> 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<ContentLogOutputModel> GetConversationContentLogs(string conversationId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Conversation State Log
|
||||
public void SaveConversationStateLog(ConversationStateLogModel log)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public List<ConversationStateLogModel> GetConversationStateLogs(string conversationId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Stats
|
||||
public void IncrementConversationCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Translation
|
||||
public IEnumerable<TranslationMemoryOutput> GetTranslationMemories(IEnumerable<TranslationMemoryQuery> queries)
|
||||
=> throw new NotImplementedException();
|
||||
public bool SaveTranslationMemories(IEnumerable<TranslationMemoryInput> inputs) =>
|
||||
throw new NotImplementedException();
|
||||
#endregion
|
||||
|
||||
#region KnowledgeBase
|
||||
public bool AddKnowledgeCollectionConfigs(List<VectorCollectionConfig> configs, bool reset = false) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public bool DeleteKnowledgeCollectionConfig(string collectionName) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public IEnumerable<VectorCollectionConfig> 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<KnowledgeDocMetaData> GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, KnowledgeFileFilter filter) =>
|
||||
throw new NotImplementedException();
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace BotSharp.Core.Repository;
|
|||
public partial class FileRepository
|
||||
{
|
||||
#region Task
|
||||
public PagedItems<AgentTask> GetAgentTasks(AgentTaskFilter filter)
|
||||
public async ValueTask<PagedItems<AgentTask>> GetAgentTasks(AgentTaskFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<Conversation> GetConversations(ConversationFilter filter)
|
||||
public async ValueTask<PagedItems<Conversation>> 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<Conversation>
|
||||
{
|
||||
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<DialogElement> 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<StateKeyValue> 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<StateKeyValue> 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<ConversationBreakpoint> 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);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public partial class FileRepository
|
|||
}
|
||||
|
||||
|
||||
public PagedItems<CrontabItem> GetCrontabItems(CrontabItemFilter filter)
|
||||
public async ValueTask<PagedItems<CrontabItem>> GetCrontabItems(CrontabItemFilter filter)
|
||||
{
|
||||
|
||||
if (filter == null)
|
||||
|
|
@ -111,7 +111,7 @@ public partial class FileRepository
|
|||
return new PagedItems<CrontabItem>
|
||||
{
|
||||
Items = records.OrderByDescending(x => x.CreatedTime).Skip(filter.Offset).Take(filter.Size),
|
||||
Count = records.Count(),
|
||||
Count = records.Count()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<List<VectorCollectionConfig>>(str, _options) ?? new();
|
||||
|
||||
|
|
@ -155,7 +155,7 @@ public partial class FileRepository
|
|||
return true;
|
||||
}
|
||||
|
||||
public PagedItems<KnowledgeDocMetaData> GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, KnowledgeFileFilter filter)
|
||||
public async ValueTask<PagedItems<KnowledgeDocMetaData>> GetKnowledgeBaseFileMeta(string collectionName, string vectorStoreProvider, KnowledgeFileFilter filter)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(collectionName)
|
||||
|| string.IsNullOrWhiteSpace(vectorStoreProvider))
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ namespace BotSharp.Core.Repository
|
|||
return true;
|
||||
}
|
||||
|
||||
public PagedItems<InstructionLogModel> GetInstructionLogs(InstructLogFilter filter)
|
||||
public async ValueTask<PagedItems<InstructionLogModel>> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ public partial class FileRepository
|
|||
File.WriteAllText(path, JsonSerializer.Serialize(user, _options));
|
||||
}
|
||||
|
||||
public PagedItems<User> GetUsers(UserFilter filter)
|
||||
public async ValueTask<PagedItems<User>> GetUsers(UserFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<IConversationStateService>();
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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<bool> InvokeAgent(
|
||||
string agentId,
|
||||
List<RoleDialogModel> dialogs,
|
||||
string from = InvokeSource.Manual,
|
||||
bool useStream = false)
|
||||
InvokeAgentOptions? options = null)
|
||||
{
|
||||
options ??= InvokeAgentOptions.Default();
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
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<bool> InvokeFunction(
|
||||
RoleDialogModel message,
|
||||
List<RoleDialogModel> dialogs,
|
||||
string from,
|
||||
bool useStream)
|
||||
InvokeAgentOptions? options = null)
|
||||
{
|
||||
// execute function
|
||||
// Save states
|
||||
|
|
@ -93,7 +93,8 @@ public partial class RoutingService
|
|||
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -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<bool> InvokeFunction(string name, RoleDialogModel message, string from = InvokeSource.Manual)
|
||||
public async Task<bool> InvokeFunction(string name, RoleDialogModel message, InvokeFunctionOptions? options = null)
|
||||
{
|
||||
options ??= InvokeFunctionOptions.Default();
|
||||
var currentAgentId = message.CurrentAgentId;
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
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<IConversationProgressService>();
|
||||
clonedMessage.Indication = await funcExecutor.GetIndicatorAsync(message);
|
||||
|
||||
if (progressService?.OnFunctionExecuting != null)
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var messageHub = _services.GetRequiredService<MessageHub<HubObserveData<RoleDialogModel>>>();
|
||||
messageHub.Push(new()
|
||||
{
|
||||
await progressService.OnFunctionExecuting(clonedMessage);
|
||||
}
|
||||
|
||||
EventName = ChatEvent.OnIndicationReceived,
|
||||
Data = clonedMessage,
|
||||
RefId = conv.ConversationId
|
||||
});
|
||||
|
||||
var hooks = _services.GetHooksOrderByPriority<IConversationHook>(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
|
||||
|
|
|
|||
|
|
@ -53,11 +53,12 @@ public partial class RoutingService : IRoutingService
|
|||
{
|
||||
var state = _services.GetRequiredService<IConversationStateService>();
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public class AgentTaskService : IAgentTaskService
|
|||
else
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var pagedTasks = db.GetAgentTasks(filter);
|
||||
var pagedTasks = await db.GetAgentTasks(filter);
|
||||
return await Task.FromResult(pagedTasks);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -411,10 +411,18 @@ public class UserService : IUserService
|
|||
return user;
|
||||
}
|
||||
|
||||
[SharpCache(10)]
|
||||
public async Task<List<User>> GetUsers(List<string> ids)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var users = db.GetUserByIds(ids);
|
||||
return users;
|
||||
}
|
||||
|
||||
public async Task<PagedItems<User>> GetUsers(UserFilter filter)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var users = db.GetUsers(filter);
|
||||
var users = await db.GetUsers(filter);
|
||||
return users;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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" ]
|
||||
}
|
||||
}
|
||||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ public class AgentController : ControllerBase
|
|||
public AgentController(
|
||||
IAgentService agentService,
|
||||
IUserIdentity user,
|
||||
IServiceProvider services
|
||||
)
|
||||
IServiceProvider services)
|
||||
{
|
||||
_agentService = agentService;
|
||||
_user = user;
|
||||
|
|
|
|||
|
|
@ -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<IObserverService>();
|
||||
using var container = observer.SubscribeObservers<HubObserveData<RoleDialogModel>>(conversationId);
|
||||
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
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<IObserverService>();
|
||||
using var container = observer.SubscribeObservers<HubObserveData<RoleDialogModel>>(conversationId, listeners: new()
|
||||
{
|
||||
{ ChatEvent.OnIndicationReceived, async data => await OnReceiveToolCallIndication(conversationId, data.Data) }
|
||||
});
|
||||
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
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<IConversationProgressService>();
|
||||
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<string, string>()
|
||||
};
|
||||
await OnChunkReceived(Response, indicator);
|
||||
ConversationId = conversationId,
|
||||
MessageId = msg.MessageId,
|
||||
Text = msg.Indication,
|
||||
Function = "indicating",
|
||||
Instruction = msg.Instruction,
|
||||
States = new Dictionary<string, string>()
|
||||
};
|
||||
progressService.OnFunctionExecuted = async msg => { };
|
||||
await OnChunkReceived(Response, indicator);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -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<StringIdPagedItems<VectorKnowledgeViewModel>> 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<VectorKnowledgeViewModel>();
|
||||
var items = data.Items?.Select(x => VectorKnowledgeViewModel.From(x))?.ToList() ?? [];
|
||||
|
||||
return new StringIdPagedItems<VectorKnowledgeViewModel>
|
||||
{
|
||||
|
|
@ -97,6 +98,19 @@ public class KnowledgeBaseController : ControllerBase
|
|||
return created;
|
||||
}
|
||||
|
||||
[HttpGet("/knowledge/vector/{collection}/points")]
|
||||
public async Task<IEnumerable<VectorKnowledgeViewModel>> 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<bool> UpdateVectorKnowledge([FromRoute] string collection, [FromBody] VectorKnowledgeUpdateRequest request)
|
||||
{
|
||||
|
|
@ -126,6 +140,21 @@ public class KnowledgeBaseController : ControllerBase
|
|||
#endregion
|
||||
|
||||
|
||||
#region Index
|
||||
[HttpPost("/knowledge/vector/{collection}/payload/indexes")]
|
||||
public async Task<SuccessFailResponse<string>> CreateCollectionPayloadIndexes([FromRoute] string collection, [FromBody] CreateVectorCollectionIndexRequest request)
|
||||
{
|
||||
return await _knowledgeService.CreateVectorCollectionPayloadIndexes(collection, request.Options);
|
||||
}
|
||||
|
||||
[HttpDelete("/knowledge/vector/{collection}/payload/indexes")]
|
||||
public async Task<SuccessFailResponse<string>> DeleteCollectionPayloadIndexes([FromRoute] string collection, [FromBody] DeleteVectorCollectionIndexRequest request)
|
||||
{
|
||||
return await _knowledgeService.DeleteVectorCollectionPayloadIndexes(collection, request.Options);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Snapshot
|
||||
[HttpGet("/knowledge/vector/{collection}/snapshots")]
|
||||
public async Task<IEnumerable<VectorCollectionSnapshotViewModel>> GetVectorCollectionSnapshots([FromRoute] string collection)
|
||||
|
|
@ -263,6 +292,7 @@ public class KnowledgeBaseController : ControllerBase
|
|||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Private methods
|
||||
private FileStreamResult BuildFileResult(string fileName, BinaryData fileData)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class RealtimeController : ControllerBase
|
|||
FunctionName = functionName,
|
||||
FunctionArgs = JsonSerializer.Serialize(args)
|
||||
};
|
||||
await routing.InvokeFunction(functionName, message, from: InvokeSource.Llm);
|
||||
await routing.InvokeFunction(functionName, message, options: new() { From = InvokeSource.Llm });
|
||||
return message.Content;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
namespace BotSharp.OpenAPI.ViewModels.Knowledges.Request;
|
||||
|
||||
public class QueryVectorDataRequest
|
||||
{
|
||||
public List<string> Ids { get; set; } = [];
|
||||
public bool WithVector { get; set; }
|
||||
public bool WithPayload { get; set; }
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.VectorStorage.Models;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BotSharp.OpenAPI.ViewModels.Knowledges;
|
||||
|
|
@ -10,6 +11,9 @@ public class SearchVectorKnowledgeRequest
|
|||
[JsonPropertyName("fields")]
|
||||
public IEnumerable<string>? Fields { get; set; }
|
||||
|
||||
[JsonPropertyName("filter_groups")]
|
||||
public IEnumerable<VectorFilterGroup>? FilterGroups { get; set; }
|
||||
|
||||
[JsonPropertyName("limit")]
|
||||
public int? Limit { get; set; } = 5;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
using BotSharp.Abstraction.VectorStorage.Models;
|
||||
|
||||
namespace BotSharp.OpenAPI.ViewModels.Knowledges.Request;
|
||||
|
||||
public class CreateVectorCollectionIndexRequest
|
||||
{
|
||||
public IEnumerable<CreateVectorCollectionIndexOptions> Options { get; set; } = [];
|
||||
}
|
||||
|
||||
public class DeleteVectorCollectionIndexRequest
|
||||
{
|
||||
public IEnumerable<DeleteVectorCollectionIndexOptions> Options { get; set; } = [];
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ public class VectorKnowledgeCreateRequest
|
|||
public string Text { get; set; }
|
||||
|
||||
[JsonPropertyName("data_source")]
|
||||
public string DataSource { get; set; } = VectorDataSource.Api;
|
||||
public string DataSource { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("payload")]
|
||||
public Dictionary<string, object>? Payload { get; set; }
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ public class VectorKnowledgeViewModel
|
|||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public double? Score { get; set; }
|
||||
|
||||
[JsonPropertyName("vector")]
|
||||
[JsonPropertyName("vector_dimension")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public float[]? Vector { get; set; }
|
||||
public int? VectorDimension { get; set; }
|
||||
|
||||
|
||||
public static VectorKnowledgeViewModel From(VectorSearchResult result)
|
||||
|
|
@ -27,7 +27,17 @@ public class VectorKnowledgeViewModel
|
|||
Id = result.Id,
|
||||
Data = result.Data,
|
||||
Score = result.Score,
|
||||
Vector = result.Vector
|
||||
VectorDimension = result.Vector?.Length
|
||||
};
|
||||
}
|
||||
|
||||
public static VectorKnowledgeViewModel From(VectorCollectionData data)
|
||||
{
|
||||
return new VectorKnowledgeViewModel
|
||||
{
|
||||
Id = data.Id,
|
||||
Data = data.Data,
|
||||
VectorDimension = data.Vector?.Length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using Azure;
|
||||
using BotSharp.Abstraction.Conversations.Enums;
|
||||
using BotSharp.Abstraction.Files.Utilities;
|
||||
using BotSharp.Abstraction.Hooks;
|
||||
using BotSharp.Abstraction.Observables.Models;
|
||||
using BotSharp.Abstraction.MessageHub.Models;
|
||||
using BotSharp.Core.Infrastructures.Streams;
|
||||
using BotSharp.Core.Observables.Queues;
|
||||
using BotSharp.Core.MessageHub;
|
||||
using OpenAI.Chat;
|
||||
using System.ClientModel;
|
||||
|
||||
|
|
@ -212,7 +212,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var chatClient = client.GetChatClient(_model);
|
||||
var (prompt, messages, options) = PrepareOptions(agent, conversations);
|
||||
|
||||
var hub = _services.GetRequiredService<MessageHub<HubObserveData>>();
|
||||
var hub = _services.GetRequiredService<MessageHub<HubObserveData<RoleDialogModel>>>();
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var messageId = conversations.LastOrDefault()?.MessageId ?? string.Empty;
|
||||
|
||||
var contentHooks = _services.GetHooks<IContentGeneratingHook>(agent.Id);
|
||||
|
|
@ -224,8 +225,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
hub.Push(new()
|
||||
{
|
||||
ServiceProvider = _services,
|
||||
EventName = "BeforeReceiveLlmStreamMessage",
|
||||
EventName = ChatEvent.BeforeReceiveLlmStreamMessage,
|
||||
RefId = conv.ConversationId,
|
||||
Data = new RoleDialogModel(AgentRole.Assistant, string.Empty)
|
||||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
|
|
@ -268,8 +269,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
};
|
||||
hub.Push(new()
|
||||
{
|
||||
ServiceProvider = _services,
|
||||
EventName = "OnReceiveLlmStreamMessage",
|
||||
EventName = ChatEvent.OnReceiveLlmStreamMessage,
|
||||
RefId = conv.ConversationId,
|
||||
Data = content
|
||||
});
|
||||
}
|
||||
|
|
@ -311,8 +312,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
hub.Push(new()
|
||||
{
|
||||
ServiceProvider = _services,
|
||||
EventName = "AfterReceiveLlmStreamMessage",
|
||||
EventName = ChatEvent.AfterReceiveLlmStreamMessage,
|
||||
RefId = conv.ConversationId,
|
||||
Data = responseMessage
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
|
||||
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
|
||||
<GenerateDocumentationFile>$(GenerateDocumentationFile)</GenerateDocumentationFile>
|
||||
<OutputPath>$(SolutionDir)packages</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\util-chart-plot_chart.json" />
|
||||
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-chart-plot_chart.fn.liquid" />
|
||||
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-chart-plot_instruction.liquid" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\util-chart-plot_chart.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-chart-plot_chart.fn.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-chart-plot_instruction.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace BotSharp.Plugin.ChartHandler;
|
||||
|
||||
public class ChartHandlerPlugin : IBotSharpPlugin
|
||||
{
|
||||
public string Id => "9dacac1d-2e29-4f01-9d66-b0201f05a9fa";
|
||||
public string Name => "Chart Plotter";
|
||||
public string Description => "AI plots chart";
|
||||
public string IconUrl => "https://cdn-icons-png.flaticon.com/512/423/423786.png";
|
||||
public string[] AgentIds => [];
|
||||
|
||||
public void RegisterDI(IServiceCollection services, IConfiguration config)
|
||||
{
|
||||
services.AddScoped<IAgentUtilityHook, ChartHandlerUtilityHook>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
namespace BotSharp.Plugin.ChartHandler.Enums;
|
||||
|
||||
public class UtilityName
|
||||
{
|
||||
public const string ChartPlotter = "chart-plotter";
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
|
||||
|
||||
namespace BotSharp.Plugin.ChartHandler.Functions;
|
||||
|
||||
public class PlotChartFn : IFunctionCallback
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger<PlotChartFn> _logger;
|
||||
|
||||
public string Name => "util-chart-plot_chart";
|
||||
public string Indication => "Plotting chart";
|
||||
|
||||
|
||||
public PlotChartFn(
|
||||
IServiceProvider services,
|
||||
ILogger<PlotChartFn> logger)
|
||||
{
|
||||
_services = services;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<bool> Execute(RoleDialogModel message)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var convService = _services.GetRequiredService<IConversationService>();
|
||||
|
||||
var args = JsonSerializer.Deserialize<LlmContextIn>(message.FunctionArgs);
|
||||
var agent = await agentService.GetAgent(message.CurrentAgentId);
|
||||
var inst = db.GetAgentTemplate(BuiltInAgentId.UtilityAssistant, "util-chart-plot_instruction");
|
||||
var innerAgent = new Agent
|
||||
{
|
||||
Id = agent.Id,
|
||||
Name = agent.Name,
|
||||
Instruction = inst,
|
||||
TemplateDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "plotting_requirement", args?.PlottingRequirement ?? string.Empty },
|
||||
{ "chart_element_id", $"chart-{message.MessageId}" }
|
||||
}
|
||||
};
|
||||
|
||||
var response = await GetChatCompletion(innerAgent, [
|
||||
new RoleDialogModel(AgentRole.User, "Please follow the instruction to generate the javascript code.")
|
||||
{
|
||||
CurrentAgentId = message.CurrentAgentId,
|
||||
MessageId = message.MessageId
|
||||
}
|
||||
]);
|
||||
|
||||
var obj = response.JsonContent<LlmContextOut>();
|
||||
message.Content = obj?.GreetingMessage ?? "Here is the chart you ask for:";
|
||||
message.RichContent = new RichContent<IRichMessage>
|
||||
{
|
||||
Recipient = new Recipient { Id = convService.ConversationId },
|
||||
Message = new ProgramCodeTemplateMessage
|
||||
{
|
||||
Text = obj?.JsCode ?? string.Empty,
|
||||
Language = "javascript"
|
||||
}
|
||||
};
|
||||
message.StopCompletion = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<string> GetChatCompletion(Agent agent, List<RoleDialogModel> dialogs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
|
||||
var completion = CompletionProvider.GetChatCompletion(_services, provider: "openai", model: "gpt-4.1");
|
||||
var response = await completion.GetChatCompletions(agent, dialogs);
|
||||
return response.Content;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var error = $"Error when plotting chart. {ex.Message}";
|
||||
_logger.LogWarning(ex, error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
namespace BotSharp.Plugin.ChartHandler.Hooks;
|
||||
|
||||
public class ChartHandlerUtilityHook : IAgentUtilityHook
|
||||
{
|
||||
private const string PLOT_CHART_FN = "util-chart-plot_chart";
|
||||
|
||||
public void AddUtilities(List<AgentUtility> utilities)
|
||||
{
|
||||
var item = new AgentUtility
|
||||
{
|
||||
Category = "chart",
|
||||
Name = UtilityName.ChartPlotter,
|
||||
Items = [
|
||||
new UtilityItem
|
||||
{
|
||||
FunctionName = PLOT_CHART_FN,
|
||||
TemplateName = $"{PLOT_CHART_FN}.fn"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
utilities.Add(item);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BotSharp.Plugin.ChartHandler.LlmContext;
|
||||
|
||||
public class LlmContextIn
|
||||
{
|
||||
[JsonPropertyName("plotting_requirement")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? PlottingRequirement { get; set; }
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue