merge with master

This commit is contained in:
Jicheng Lu 2023-09-01 14:56:37 -05:00
commit 7dc43f7554
56 changed files with 887 additions and 118 deletions

View file

@ -45,7 +45,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.MetaMesseng
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.OpenAPI", "src\Infrastructure\BotSharp.OpenAPI\BotSharp.OpenAPI.csproj", "{7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.LLamaSharp", "src\Plugins\BotSharp.Plugin.LLamaSharp\BotSharp.Plugin.LLamaSharp.csproj", "{46B7B54F-1425-4C9D-824A-9B826855D249}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.LLamaSharp", "src\Plugins\BotSharp.Plugin.LLamaSharp\BotSharp.Plugin.LLamaSharp.csproj", "{46B7B54F-1425-4C9D-824A-9B826855D249}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.PizzaBot", "tests\BotSharp.Plugin.PizzaBot\BotSharp.Plugin.PizzaBot.csproj", "{A1118A2C-C6D7-4E22-9462-964AEC7CC46E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{51AFE054-AE99-497D-A593-69BAEFB5106F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.RoutingSpeeder", "src\Plugins\BotSharp.Plugin.RoutingSpeeder\BotSharp.Plugin.RoutingSpeeder.csproj", "{631D9C12-86C4-44F0-99C3-D32C0754BF37}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -167,6 +173,22 @@ Global
{46B7B54F-1425-4C9D-824A-9B826855D249}.Release|Any CPU.Build.0 = Release|Any CPU
{46B7B54F-1425-4C9D-824A-9B826855D249}.Release|x64.ActiveCfg = Release|Any CPU
{46B7B54F-1425-4C9D-824A-9B826855D249}.Release|x64.Build.0 = Release|Any CPU
{A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|x64.ActiveCfg = Debug|Any CPU
{A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|x64.Build.0 = Debug|Any CPU
{A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|Any CPU.Build.0 = Release|Any CPU
{A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|x64.ActiveCfg = Release|Any CPU
{A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|x64.Build.0 = Release|Any CPU
{631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|Any CPU.Build.0 = Debug|Any CPU
{631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|x64.ActiveCfg = Debug|Any CPU
{631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|x64.Build.0 = Debug|Any CPU
{631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|Any CPU.ActiveCfg = Release|Any CPU
{631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|Any CPU.Build.0 = Release|Any CPU
{631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|x64.ActiveCfg = Release|Any CPU
{631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -190,6 +212,9 @@ Global
{8300F66D-9EB8-438A-BF0F-70DFBE07D9DE} = {64264688-0F5C-4AB0-8F2B-B59B717CCE00}
{7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
{46B7B54F-1425-4C9D-824A-9B826855D249} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
{A1118A2C-C6D7-4E22-9462-964AEC7CC46E} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
{51AFE054-AE99-497D-A593-69BAEFB5106F} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C}
{631D9C12-86C4-44F0-99C3-D32C0754BF37} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}

View file

@ -2,16 +2,6 @@ namespace BotSharp.Abstraction.Agents.Settings;
public class AgentSettings
{
/// <summary>
/// Router Agent Id
/// </summary>
public string RouterId { get; set; }
/// <summary>
/// Reasoner Agent Id
/// </summary>
public string ReasonerId { get; set; }
public string DataDir { get; set; }
public string TemplateFormat { get; set; }
}

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion>
<VersionPrefix>0.9.4</VersionPrefix>
<VersionPrefix>0.10.0</VersionPrefix>
<PackageIcon>Icon.png</PackageIcon>
</PropertyGroup>

View file

@ -1,5 +1,3 @@
using BotSharp.Abstraction.Conversations.Models;
namespace BotSharp.Abstraction.Conversations;
public abstract class ConversationHookBase : IConversationHook
@ -13,6 +11,9 @@ public abstract class ConversationHookBase : IConversationHook
protected List<RoleDialogModel> _dialogs;
public List<RoleDialogModel> Dialogs => _dialogs;
protected int _priority = 0;
public int Priority => _priority;
public IConversationHook SetAgent(Agent agent)
{
_agent = agent;
@ -35,7 +36,7 @@ public abstract class ConversationHookBase : IConversationHook
return Task.CompletedTask;
}
public virtual Task BeforeCompletion()
public virtual Task BeforeCompletion(RoleDialogModel message)
{
return Task.CompletedTask;
}

View file

@ -1,10 +1,8 @@
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
namespace BotSharp.Abstraction.Conversations;
public interface IConversationHook
{
int Priority { get; }
Agent Agent { get; }
IConversationHook SetAgent(Agent agent);
@ -22,7 +20,7 @@ public interface IConversationHook
Task OnStateLoaded(ConversationState state);
Task OnStateChanged(string name, string preValue, string currentValue);
Task BeforeCompletion();
Task BeforeCompletion(RoleDialogModel message);
Task OnFunctionExecuting(RoleDialogModel message);
Task OnFunctionExecuted(RoleDialogModel message);
Task AfterCompletion(RoleDialogModel message);

View file

@ -30,6 +30,16 @@ public class RoleDialogModel
/// </summary>
public object ExecutionData { get; set; }
/// <summary>
/// Intent name
/// </summary>
public string IntentName { get; set; }
/// <summary>
/// Stop conversation completion
/// </summary>
public bool StopCompletion { get; set; }
/// <summary>
/// Channel name
/// </summary>

View file

@ -7,5 +7,4 @@ public class ConversationSetting
public bool EnableKnowledgeBase { get; set; }
public bool ShowVerboseLog { get; set; }
public int MaxRecursiveDepth { get; set; } = 3;
public bool EnableReasoning { get; set; }
}

View file

@ -1,4 +1,4 @@
namespace BotSharp.Core.Plugins.Knowledges;
namespace BotSharp.Abstraction.Knowledges.Settings;
public class KnowledgeBaseSettings
{

View file

@ -3,5 +3,4 @@ namespace BotSharp.Abstraction.Plugins;
public class PluginLoaderSettings
{
public string[] Assemblies { get; set; }
public string[] Plugins { get; set; }
}

View file

@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace BotSharp.Abstraction.Routing.Models;
public class RoutingProfileRecord
{
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("agent_ids")]
public string[] AgentIds { get; set; }
}

View file

@ -14,7 +14,7 @@ public class RoutingRecord
public string Description { get; set; }
[JsonPropertyName("required")]
public List<string> RequiredFields { get; set; }
public List<string> RequiredFields { get; set; } = new List<string>();
[JsonPropertyName("redirect_to")]
public string RedirectTo { get; set; }

View file

@ -0,0 +1,14 @@
namespace BotSharp.Abstraction.Routing.Settings;
public class RoutingSettings
{
/// <summary>
/// Router Agent Id
/// </summary>
public string RouterId { get; set; }
/// <summary>
/// Reasoner Agent Id
/// </summary>
public string ReasonerId { get; set; }
}

View file

@ -0,0 +1,8 @@
namespace BotSharp.Abstraction.Templating;
public interface IResponseTemplateService
{
Task<string> RenderFunctionResponse(string agentId, RoleDialogModel message);
Task<string> RenderIntentResponse(string agentId, RoleDialogModel message);
}

View file

@ -2,5 +2,5 @@ namespace BotSharp.Abstraction.Templating;
public interface ITemplateRender
{
bool Render(Agent agent, Dictionary<string, object> dict);
string Render(string template, Dictionary<string, object> dict);
}

View file

@ -4,4 +4,5 @@ global using System.Text;
global using System.Linq;
global using System.Threading.Tasks;
global using System.ComponentModel.DataAnnotations;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.Conversations.Models;

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Templating;
namespace BotSharp.Core.Agents.Services;
@ -45,8 +46,8 @@ public partial class AgentService
}
// render liquid template
var render = _services.GetRequiredService<TemplateRender>();
render.Render(agent, templateDict);
var render = _services.GetRequiredService<ITemplateRender>();
agent.Instruction = render.Render(agent.Instruction, templateDict);
_logger.LogInformation($"Loaded agent {agent}.");

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>10.0</LangVersion>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<VersionPrefix>0.9.4</VersionPrefix>
<VersionPrefix>0.10.0</VersionPrefix>
</PropertyGroup>
<PropertyGroup Label="Globals">

View file

@ -8,7 +8,8 @@ using BotSharp.Core.Templating;
using BotSharp.Core.Plugins.Knowledges.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using DatabaseSettings = BotSharp.Abstraction.Repositories.DatabaseSettings;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core;
@ -43,16 +44,18 @@ public static class BotSharpServiceCollectionExtensions
RegisterPlugins(services, config);
// Register template render
services.AddSingleton<TemplateRender>();
services.AddSingleton<ITemplateRender, TemplateRender>();
services.AddScoped<IResponseTemplateService, ResponseTemplateService>();
// Register router
var routingSettings = new RoutingSettings();
config.Bind("Router", routingSettings);
services.AddSingleton((IServiceProvider x) => routingSettings);
services.AddScoped<Router>();
services.AddScoped<Reasoner>();
services.AddScoped<IAgentRouting>(p =>
{
var setting = p.GetRequiredService<ConversationSetting>();
return setting.EnableReasoning ? p.GetRequiredService<Reasoner>() : p.GetRequiredService<Router>();
});
services.AddScoped<IAgentRouting, Router>();
services.AddScoped<Reasoner>();
// Register function callback
services.AddScoped<IFunctionCallback, RouteToAgentFn>();

View file

@ -7,7 +7,8 @@ public partial class ConversationService
{
private async Task CallFunctions(RoleDialogModel msg)
{
var hooks = _services.GetServices<IConversationHook>().ToList();
var hooks = _services.GetServices<IConversationHook>()
.OrderBy(x => x.Priority).ToList();
// Invoke functions
var functions = _services.GetServices<IFunctionCallback>()

View file

@ -1,7 +1,7 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Conversations.Services;
@ -68,22 +68,48 @@ public partial class ConversationService
{
var agentService = _services.GetRequiredService<IAgentService>();
agent = await agentService.LoadAgent(fn.CurrentAgentId);
wholeDialogs.Add(fn);
await GetChatCompletionsAsyncRecursively(chatCompletion,
conversationId,
agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting,
onFunctionExecuted);
}
else
{
// Find response template
var templateService = _services.GetRequiredService<IResponseTemplateService>();
var response = await templateService.RenderFunctionResponse(agent.Id, fn);
if (!string.IsNullOrEmpty(response))
{
await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, response)
{
CurrentAgentId = agent.Id,
Channel = wholeDialogs.Last().Channel
}, onMessageReceived);
// Add to dialog history
// The server had an error processing your request. Sorry about that!
// _storage.Append(conversationId, preAgentId, fn);
return;
}
// Add to dialog history
// The server had an error processing your request. Sorry about that!
// _storage.Append(conversationId, preAgentId, fn);
// After function is executed, pass the result to LLM to get a natural response
wholeDialogs.Add(fn);
// After function is executed, pass the result to LLM to get a natural response
wholeDialogs.Add(fn);
await GetChatCompletionsAsyncRecursively(chatCompletion,
conversationId,
agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting,
onFunctionExecuted);
await GetChatCompletionsAsyncRecursively(chatCompletion,
conversationId,
agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting,
onFunctionExecuted);
}
});
return result;

View file

@ -2,6 +2,7 @@ using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Core.Routing;
namespace BotSharp.Core.Conversations.Services;
@ -33,8 +34,8 @@ public partial class ConversationService
stateService.Load();
stateService.SetState("channel", lastDialog.Channel);
var router = _services.GetRequiredService<IAgentRouting>();
Agent agent = await router.LoadRouter();
var agentService = _services.GetRequiredService<IAgentService>();
Agent agent = await agentService.LoadAgent(agentId);
_logger.LogInformation($"[{agent.Name}] {lastDialog.Role}: {lastDialog.Content}");
@ -65,11 +66,21 @@ public partial class ConversationService
.SetConversation(converation);
await hook.OnDialogsLoaded(wholeDialogs);
await hook.BeforeCompletion();
await hook.BeforeCompletion(lastDialog);
// Interrupted by hook
if (lastDialog.StopCompletion)
{
var response = new RoleDialogModel(AgentRole.Assistant, lastDialog.Content);
await onMessageReceived(response);
_storage.Append(conversationId, agent.Id, response);
return true;
}
}
// reasoning
if (_settings.EnableReasoning)
var settings = _services.GetRequiredService<RoutingSettings>();
if (settings.ReasonerId == agent.Id)
{
var simulator = _services.GetRequiredService<Simulator>();
var reasonedContext = await simulator.Enter(agent, wholeDialogs);
@ -96,7 +107,6 @@ public partial class ConversationService
{
if (reasonedContext.CurrentAgentId != agent.Id)
{
var agentService = _services.GetRequiredService<IAgentService>();
agent = await agentService.LoadAgent(reasonedContext.CurrentAgentId);
}
}

View file

@ -1,3 +1,4 @@
using BotSharp.Abstraction.Knowledges.Settings;
using BotSharp.Core.Plugins.Knowledges.Services;
using Microsoft.Extensions.Configuration;

View file

@ -1,7 +1,7 @@
using BotSharp.Abstraction.Knowledges.Models;
using BotSharp.Abstraction.Knowledges.Settings;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.VectorStorage;
using System.Text.Json;
namespace BotSharp.Core.Plugins.Knowledges.Services;

View file

@ -40,15 +40,8 @@ public class PluginLoader
foreach (var module in modules)
{
if (_settings.Plugins.Contains(module.GetType().Name))
{
module.RegisterDI(_services, _config);
Console.WriteLine($"Loaded plugin {module.GetType().Name} from {assemblyName}.", Color.Green);
}
else
{
Console.WriteLine($"Skipped plugin {module.GetType().Name} from {assemblyName}.", Color.Yellow);
}
module.RegisterDI(_services, _config);
Console.WriteLine($"Loaded plugin {module.GetType().Name} from {assemblyName}.", Color.Green);
}
_modules.AddRange(modules);
@ -62,7 +55,7 @@ public class PluginLoader
public void Configure(IApplicationBuilder app)
{
if(_modules.Count == 0)
if (_modules.Count == 0)
{
Console.WriteLine($"No plugin loaded. Please check whether the Load() method is called.", Color.Yellow);
}
@ -71,10 +64,7 @@ public class PluginLoader
{
if (module.GetType().GetInterface(nameof(IBotSharpAppPlugin)) != null)
{
if (_settings.Plugins.Contains(module.GetType().Name))
{
(module as IBotSharpAppPlugin).Configure(app);
}
(module as IBotSharpAppPlugin).Configure(app);
}
});
}

View file

@ -1,3 +1,5 @@
using BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Core.Routing;
public class Reasoner : Router
@ -6,7 +8,7 @@ public class Reasoner : Router
public Reasoner(IServiceProvider services,
ILogger<Reasoner> logger,
AgentSettings settings) : base(services, logger, settings)
RoutingSettings settings) : base(services, logger, settings)
{
}
}

View file

@ -1,6 +1,7 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Settings;
using System.IO;
namespace BotSharp.Core.Routing;
@ -9,13 +10,13 @@ public class Router : IAgentRouting
{
protected readonly IServiceProvider _services;
protected readonly ILogger _logger;
protected readonly AgentSettings _settings;
protected readonly RoutingSettings _settings;
public virtual string AgentId => _settings.RouterId;
public Router(IServiceProvider services,
ILogger<Router> logger,
AgentSettings settings)
RoutingSettings settings)
{
_services = services;
_logger = logger;
@ -32,20 +33,24 @@ public class Router : IAgentRouting
{
var agentSettings = _services.GetRequiredService<AgentSettings>();
var dbSettings = _services.GetRequiredService<MyDatabaseSettings>();
var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, agentSettings.RouterId, "route.json");
var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, _settings.RouterId, "route.json");
var records = JsonSerializer.Deserialize<RoutingRecord[]>(File.ReadAllText(filePath));
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.Agent.FirstOrDefault(x => x.Id == agentSettings.RouterId);
var routes = agent?.Routes ?? new List<string>();
var routingRecords = new RoutingRecord[routes.Count];
for (int i = 0; i < routes.Count; i++)
// check if routing profile is specified
filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, "routing-profile.json");
if (File.Exists(filePath))
{
if (string.IsNullOrEmpty(routes[i])) continue;
routingRecords[i] = JsonSerializer.Deserialize<RoutingRecord>(routes[i]);
var state = _services.GetRequiredService<IConversationStateService>();
var name = state.GetState("channel");
var profiles = JsonSerializer.Deserialize<RoutingProfileRecord[]>(File.ReadAllText(filePath));
var spcificedProfile = profiles.FirstOrDefault(x => x.Name == name);
if (spcificedProfile != null)
{
records = records.Where(x => spcificedProfile.AgentIds.Contains(x.AgentId)).ToArray();
}
}
return routingRecords;
return records;
}
public RoutingRecord GetRecordByName(string name)

View file

@ -0,0 +1,120 @@
using BotSharp.Abstraction.Templating;
using System.IO;
using System.Reflection;
namespace BotSharp.Core.Templating;
public class ResponseTemplateService : IResponseTemplateService
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public ResponseTemplateService(IServiceProvider services, ILogger<ResponseTemplateService> logger)
{
_services = services;
_logger = logger;
}
public async Task<string> RenderFunctionResponse(string agentId, RoleDialogModel message)
{
// Find response template
var agentService = _services.GetRequiredService<IAgentService>();
var dir = Path.Combine(agentService.GetAgentDataDir(agentId), "responses");
var responses = Directory.GetFiles(dir)
.Where(f => f.Split(Path.DirectorySeparatorChar).Last().Split('.')[1] == message.FunctionName)
.ToList();
if (responses.Count == 0)
{
return string.Empty;
}
var randomIndex = new Random().Next(0, responses.Count);
var template = File.ReadAllText(responses[randomIndex]);
var render = _services.GetRequiredService<ITemplateRender>();
// Convert args and execute data to dictionary
var dict = new Dictionary<string, object>();
if (message.FunctionArgs != null)
{
ExtractArgs(JsonSerializer.Deserialize<JsonDocument>(message.FunctionArgs), dict);
}
if (message.ExecutionData != null)
{
ExtractExecuteData(message.ExecutionData, dict);
}
var text = render.Render(template, dict);
return text;
}
public async Task<string> RenderIntentResponse(string agentId, RoleDialogModel message)
{
// Find response template
var agentService = _services.GetRequiredService<IAgentService>();
var dir = Path.Combine(agentService.GetAgentDataDir(agentId), "responses");
if (!Directory.Exists(dir))
{
return string.Empty;
}
var responses = Directory.GetFiles(dir)
.Where(f => f.Split(Path.DirectorySeparatorChar).Last().Split('.')[1] == message.IntentName)
.ToList();
if (responses.Count == 0)
{
return string.Empty;
}
var randomIndex = new Random().Next(0, responses.Count);
var template = File.ReadAllText(responses[randomIndex]);
var render = _services.GetRequiredService<ITemplateRender>();
// Convert args and execute data to dictionary
var dict = new Dictionary<string, object>();
if (!string.IsNullOrEmpty(message.FunctionArgs))
{
ExtractArgs(JsonSerializer.Deserialize<JsonDocument>(message.FunctionArgs), dict);
}
if (message.ExecutionData != null)
{
ExtractExecuteData(message.ExecutionData, dict);
}
var text = render.Render(template, dict);
return text;
}
private void ExtractArgs(JsonDocument args, Dictionary<string, object> dict)
{
if (args.RootElement is JsonElement root)
{
foreach (JsonProperty property in root.EnumerateObject())
{
if (!string.IsNullOrEmpty(property.Value.ToString()))
{
dict[property.Name] = property.Value.ToString();
}
}
}
}
private void ExtractExecuteData(object data, Dictionary<string, object> dict)
{
foreach (PropertyInfo property in data.GetType().GetProperties())
{
var value = property.GetValue(data, null);
if (value != null)
{
dict[property.Name] = value;
}
}
}
}

View file

@ -1,8 +1,6 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Templating;
using Fluid;
using Microsoft.Extensions.Options;
namespace BotSharp.Core.Templating;
@ -22,19 +20,18 @@ public class TemplateRender : ITemplateRender
_options.MemberAccessStrategy.Register<RoutingRecord>();
}
public bool Render(Agent agent, Dictionary<string, object> dict)
public string Render(string template, Dictionary<string, object> dict)
{
var template = agent.Instruction;
if (_parser.TryParse(template, out var t, out var error))
{
var context = new TemplateContext(dict, _options);
agent.Instruction = t.Render(context);
return true;
template = t.Render(context);
return template;
}
else
{
return false;
return template;
}
}
}

View file

@ -13,11 +13,12 @@ global using BotSharp.Abstraction.Conversations;
global using BotSharp.Abstraction.Knowledges;
global using BotSharp.Abstraction.Users;
global using BotSharp.Abstraction.Utilities;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Agents.Settings;
global using BotSharp.Abstraction.Conversations.Settings;
global using BotSharp.Core.Repository;
global using BotSharp.Core.Agents.Services;
global using BotSharp.Core.Conversations.Services;
global using BotSharp.Core.Infrastructures;
global using BotSharp.Core.Plugins;
global using BotSharp.Core.Users.Services;
global using BotSharp.Abstraction.Agents.Settings;
global using BotSharp.Abstraction.Conversations.Settings;
global using BotSharp.Core.Users.Services;

View file

@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<VersionPrefix>0.9.0</VersionPrefix>
<VersionPrefix>0.10.0</VersionPrefix>
</PropertyGroup>
<ItemGroup>

View file

@ -40,7 +40,8 @@ public class ConversationController : ControllerBase, IApiAdapter
[HttpPost("/conversation/{agentId}/{conversationId}")]
public async Task<MessageResponseModel> SendMessage([FromRoute] string agentId,
[FromRoute] string conversationId,
[FromBody] NewMessageModel input)
[FromBody] NewMessageModel input,
[FromQuery] string? channel = "openapi")
{
var conv = _services.GetRequiredService<IConversationService>();
@ -50,7 +51,7 @@ public class ConversationController : ControllerBase, IApiAdapter
await conv.SendMessage(agentId, conversationId,
new RoleDialogModel("user", input.Text)
{
Channel = "webapi"
Channel = channel
},
async msg =>
{

View file

@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Http;
using UglyToad.PdfPig.Content;
using UglyToad.PdfPig;
using BotSharp.Core.Plugins.Knowledges;
using BotSharp.Abstraction.Knowledges.Settings;
namespace BotSharp.OpenAPI.Controllers;

View file

@ -4,11 +4,11 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
<VersionPrefix>0.9.0</VersionPrefix>
<VersionPrefix>0.10.0</VersionPrefix>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="1.0.0-beta.6" />
<PackageReference Include="Azure.AI.OpenAI" Version="1.0.0-beta.7" />
</ItemGroup>
<ItemGroup>

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
<VersionPrefix>0.9.0</VersionPrefix>
<VersionPrefix>0.10.0</VersionPrefix>
</PropertyGroup>
<ItemGroup>

View file

@ -15,10 +15,7 @@ public class fastTextEmbeddingProvider : ITextEmbedding
{
get
{
if (!_fastText.IsModelReady())
{
_fastText.LoadModel(_settings.ModelPath);
}
LoadModel();
return _fastText.GetModelDimension();
}
}

View file

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
<VersionPrefix>0.11.0</VersionPrefix>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="TensorFlow.Keras" Version="0.11.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Infrastructure\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,258 @@
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using Tensorflow;
using static Tensorflow.KerasApi;
using Tensorflow.Keras.Engine;
using Tensorflow.NumPy;
using static Tensorflow.Binding;
using Tensorflow.Keras.Callbacks;
using System.Text.RegularExpressions;
using BotSharp.Plugin.RoutingSpeeder.Settings;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Plugin.RoutingSpeeder.Providers.Models;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Tensorflow.Keras;
using BotSharp.Abstraction.Knowledges.Settings;
using System.Numerics;
using Newtonsoft.Json;
using Tensorflow.Keras.Layers;
using BotSharp.Abstraction.Agents;
namespace BotSharp.Plugin.RoutingSpeeder.Providers;
public class IntentClassifier
{
private readonly IServiceProvider _services;
Model _model;
public Model model => _model;
private bool _isModelReady;
public bool isModelReady => _isModelReady;
private ClassifierSetting _settings;
public IntentClassifier(IServiceProvider services, ClassifierSetting settings)
{
_services = services;
_settings = settings;
}
private void Reset()
{
keras.backend.clear_session();
_isModelReady = false;
}
private void Build()
{
if (_isModelReady)
{
return;
}
var vector = _services.GetRequiredService<ITextEmbedding>();
var labels = GetLabels();
var layers = new List<ILayer>
{
keras.layers.InputLayer((vector.Dimension), name: "Input"),
keras.layers.Dense(256, activation:"relu"),
keras.layers.Dense(256, activation:"relu"),
keras.layers.Dense(labels.Length, activation: keras.activations.Softmax)
};
_model = keras.Sequential(layers);
#if DEBUG
Console.WriteLine();
_model.summary();
#endif
_isModelReady = true;
}
private void Fit(NDArray x, NDArray y, TrainingParams trainingParams)
{
_model.compile(optimizer: keras.optimizers.Adam(trainingParams.LearningRate),
loss: keras.losses.SparseCategoricalCrossentropy(),
metrics: new[] { "accuracy" }
);
CallbackParams callback_parameters = new CallbackParams
{
Model = _model,
Epochs = trainingParams.Epochs,
Verbose = 1,
Steps = 10
};
ICallback earlyStop = new EarlyStopping(callback_parameters, "accuracy");
var callbacks = new List<ICallback>() { earlyStop };
var weights = LoadWeights();
_model.fit(x, y,
batch_size: trainingParams.BatchSize,
epochs: trainingParams.Epochs,
callbacks: callbacks,
// validation_split: 0.1f,
shuffle: true);
_model.save_weights(weights);
_isModelReady = true;
}
public string LoadWeights()
{
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
var weightsFile = Path.Combine(agentService.GetDataDir(), _settings.MODEL_DIR, $"intent-classifier.h5");
if (File.Exists(weightsFile))
{
_model.load_weights(weightsFile);
_isModelReady = true;
Console.WriteLine($"Successfully load the weights!");
}
else
{
Console.WriteLine("No available weights.");
}
return weightsFile;
}
public (NDArray x, NDArray y) Vectorize(List<DialoguePredictionModel> items)
{
var vector = _services.GetRequiredService<ITextEmbedding>();
var x = np.zeros((items.Count, vector.Dimension), dtype: np.float32);
var y = np.zeros((items.Count, 1), dtype: np.float32);
for (int i = 0; i < items.Count; i++)
{
x[i] = vector.GetVector(TextClean(items[i].text));
if (_settings.LabelMappingDict.ContainsKey(items[i].label))
{
y[i] = _settings.LabelMappingDict[items[i].label];
}
}
return (x, y);
}
public NDArray GetTextEmbedding(string text)
{
var knowledgeSettings = _services.GetRequiredService<KnowledgeBaseSettings>();
var embedding = _services.GetServices<ITextEmbedding>()
.FirstOrDefault(x => x.GetType().FullName.EndsWith(knowledgeSettings.TextEmbedding));
var x = np.zeros((1, embedding.Dimension), dtype: np.float32);
x[0] = embedding.GetVector(text);
return x;
}
public (NDArray, NDArray) PrepareLoadData()
{
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.RAW_DATA_DIR);
string saveLabelDirectory = Path.Combine(agentService.GetDataDir(), _settings.MODEL_DIR, _settings.LABEL_FILE_NAME);
if (!Directory.Exists(rootDirectory))
{
throw new Exception($"No training data found! Please put training data in this path: {rootDirectory}");
}
var vector = _services.GetRequiredService<ITextEmbedding>();
var vectorList = new List<float[]>();
var labelList = new List<string>();
foreach (var filePath in GetFiles())
{
var texts = File.ReadAllLines(filePath, Encoding.UTF8).Select(x => TextClean(x)).ToList();
vectorList.AddRange(vector.GetVectors(texts));
string fileName = Path.GetFileNameWithoutExtension(filePath);
labelList.AddRange(Enumerable.Repeat(fileName, texts.Count).ToList());
}
// Write label into local file
var uniqueLabelList = labelList.Distinct().OrderBy(x => x).ToArray();
File.WriteAllLines(saveLabelDirectory, uniqueLabelList);
var x = np.zeros((vectorList.Count, vector.Dimension), dtype: np.float32);
var y = np.zeros((vectorList.Count, 1), dtype: np.float32);
for (int i = 0; i < vectorList.Count; i++)
{
x[i] = vectorList[i];
// y[i] = (float)uniqueLabelList.IndexOf(labelList[i]);
y[i] = (float)Array.IndexOf(uniqueLabelList, labelList[i]);
}
return (x, y);
}
public string[] GetFiles()
{
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.RAW_DATA_DIR);
return Directory.GetFiles(rootDirectory).OrderBy(x => x).ToArray();
}
public string[] GetLabels()
{
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.MODEL_DIR, _settings.LABEL_FILE_NAME);
var labelText = File.ReadAllLines(rootDirectory);
return labelText.OrderBy(x => x).ToArray();
}
public string TextClean(string text)
{
// Remove punctuation
// Remove digits
// To lowercase
var processedText = Regex.Replace(text, "[AB0-9]", " ");
processedText = string.Join("", processedText.Select(c => char.IsPunctuation(c) ? ' ' : c).ToList());
processedText = processedText.Replace(" ", " ").ToLower();
return processedText;
}
public string Predict(NDArray vector, float confidenceScore = 0.9f)
{
if (!_isModelReady)
{
InitClassifer();
}
var prob = _model.predict(vector).numpy();
var probLabel = tf.arg_max(prob, -1).numpy().ToArray<long>();
prob = np.squeeze(prob, axis: 0);
if (prob[probLabel[0]] < confidenceScore)
{
return string.Empty;
}
var prediction = GetLabels()[probLabel[0]];
return prediction;
}
public void InitClassifer()
{
Reset();
Build();
LoadWeights();
}
public void Train()
{
var trainingParams = new TrainingParams();
Reset();
(var x, var y) = PrepareLoadData();
Build();
Fit(x, y, trainingParams);
}
}

View file

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BotSharp.Plugin.RoutingSpeeder.Providers.Models;
public class DialoguePredictionModel
{
public int Id { get; set; }
public string text { get; set; }
public string? label { get; set; }
public string? prediction { get; set; }
}

View file

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BotSharp.Plugin.RoutingSpeeder.Providers.Models;
public class TrainingParams
{
public int ClientId { get; set; }
public int Epochs { get; set; } = 10;
public int BatchSize { get; set; } = 16;
public float LearningRate { get; set; } = 1.0e-4f;
}

View file

@ -0,0 +1,75 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;
using BotSharp.Plugin.RoutingSpeeder.Settings;
using BotSharp.Abstraction.Templating;
using BotSharp.Plugin.RoutingSpeeder.Providers;
using System.Runtime.InteropServices;
using BotSharp.Abstraction.Agents;
using System.IO;
using BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Plugin.RoutingSpeeder;
public class RoutingConversationHook: ConversationHookBase
{
private readonly IServiceProvider _services;
private RouterSpeederSettings _settings;
public RoutingConversationHook(IServiceProvider service, RouterSpeederSettings settings)
{
_services = service;
_settings = settings;
}
public override async Task BeforeCompletion(RoleDialogModel message)
{
var intentClassifier = _services.GetRequiredService<IntentClassifier>();
var vector = intentClassifier.GetTextEmbedding(message.Content);
// intentClassifier.Train();
// Utilize local discriminative model to predict intent
var predText = intentClassifier.Predict(vector);
message.IntentName = predText;
// Render by template
var templateService = _services.GetRequiredService<IResponseTemplateService>();
var response = await templateService.RenderIntentResponse(_agent.Id, message);
if (!string.IsNullOrEmpty(response))
{
message.Content = response;
message.StopCompletion = true;
}
}
public override async Task AfterCompletion(RoleDialogModel message)
{
var routerSettings = _services.GetRequiredService<RoutingSettings>();
bool saveFlag = (message.CurrentAgentId != routerSettings.RouterId) && (message.CurrentAgentId != routerSettings.ReasonerId);
if (saveFlag)
{
// save train data
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
var rootDataPath = agentService.GetDataDir();
string rawDataDir = Path.Combine(rootDataPath, "raw_data", $"{message.CurrentAgentId}.txt");
var lastThreeDialogs = _dialogs.Where(x => x.Role == AgentRole.User).Select(x => x.Content).Reverse().Take(3).ToArray();
if (!File.Exists(rawDataDir))
{
await File.WriteAllLinesAsync(rawDataDir, lastThreeDialogs);
}
else
{
await File.AppendAllLinesAsync(rawDataDir, lastThreeDialogs);
}
}
}
}

View file

@ -0,0 +1,24 @@
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Plugins;
using BotSharp.Plugin.RoutingSpeeder.Settings;
using BotSharp.Plugin.RoutingSpeeder.Providers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace BotSharp.Plugin.RoutingSpeeder;
public class RoutingSpeederPlugin : IBotSharpPlugin
{
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
var settings = new RouterSpeederSettings();
config.Bind("RouterSpeeder", settings);
services.AddSingleton(x => settings);
services.AddSingleton<ClassifierSetting>();
services.AddScoped<IConversationHook, RoutingConversationHook>();
services.AddSingleton<IntentClassifier>();
}
}

View file

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BotSharp.Plugin.RoutingSpeeder.Settings;
public class ClassifierSetting
{
public Dictionary<string, float> LabelMappingDict { get; set; } = new Dictionary<string, float>()
{
{"goodbye", 0f},
{"greeting", 1f},
{"other", 2f}
};
public string RAW_DATA_DIR { get; set; } = "raw_data";
public string MODEL_DIR { get; set; } = "models";
public string LABEL_FILE_NAME { get; set; } = "label.txt";
}

View file

@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BotSharp.Plugin.RoutingSpeeder.Settings;
public class RouterSpeederSettings
{
}

View file

@ -34,6 +34,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\tests\BotSharp.Plugin.PizzaBot\BotSharp.Plugin.PizzaBot.csproj" />
<ProjectReference Include="..\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" />
<ProjectReference Include="..\Infrastructure\BotSharp.OpenAPI\BotSharp.OpenAPI.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.AzureOpenAI\BotSharp.Plugin.AzureOpenAI.csproj" />
@ -43,6 +44,7 @@
<ProjectReference Include="..\Plugins\BotSharp.Plugin.MetaAI\BotSharp.Plugin.MetaAI.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.PaddleSharp\BotSharp.Plugin.PaddleSharp.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.Qdrant\BotSharp.Plugin.Qdrant.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.RoutingSpeeder\BotSharp.Plugin.RoutingSpeeder.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.WeChat\BotSharp.Plugin.WeChat.csproj" />
</ItemGroup>

View file

@ -13,14 +13,20 @@
"Key": "31ba6052aa6f4569901facc3a41fcb4a"
},
"Router": {
"RouterId": "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a",
"ReasonerId": "03d3fb55-9ada-423b-a6b4-f9ecddf4b26e"
},
"Agent": {
"RouterId": "",
"DataDir": "agents",
"TemplateFormat": "liquid"
"TemplateFormat": "liquid",
"MaxRecursiveDepth": 3
},
"Conversation": {
"DataDir": "conversations",
"ShowVerboseLog": false,
"ChatCompletion": "AzureOpenAI.Providers.ChatCompletionProvider"
// "ChatCompletion": "LLamaSharp.ChatCompletionProvider"
},
@ -43,10 +49,13 @@
"MetaAi": {
"fastText": {
"ModelPath": "crawl-300d-2M-subword.bin"
"ModelPath": "dbpedia.ftz"
}
},
"RoutingSpeeder": {
},
"MetaMessenger": {
"Endpoint": "https://graph.facebook.com",
"ApiVersion": "v17.0",
@ -98,17 +107,9 @@
"BotSharp.Plugin.MetaAI",
"BotSharp.Plugin.Qdrant",
"BotSharp.Plugin.PaddleSharp",
"BotSharp.Plugin.WeChat"
],
"Plugins": [
"KnowledgeBasePlugin",
"MemVecDbPlugin",
"LLamaSharpPlugin",
"AzureOpenAiPlugin",
"MetaAiPlugin",
"QdrantPlugin",
"PaddleSharpPlugin",
"WeChatPlugin"
"BotSharp.Plugin.WeChat",
"BotSharp.Plugin.RoutingSpeeder",
"BotSharp.Plugin.PizzaBot"
]
}
}

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,3 @@
goodbye
greeting
other

View file

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,19 @@
using BotSharp.Abstraction.Conversations.Models;
using System.Text.Json;
namespace BotSharp.Plugin.PizzaBot.Functions;
public class GetPizzaPricesFn : IFunctionCallback
{
public string Name => "get_pizza_price";
public async Task<bool> Execute(RoleDialogModel message)
{
message.ExecutionData = new
{
cheese = "3.5"
};
message.ExecutionResult = "Pepperoni Pizza: $3.5/slice, Cheese Pizza: $2.5/slice, Margherita Pizza: $3.0/slice";
return true;
}
}

View file

@ -0,0 +1,14 @@
using BotSharp.Abstraction.Conversations.Models;
namespace BotSharp.Plugin.PizzaBot.Functions;
public class GetPizzaTypesFn : IFunctionCallback
{
public string Name => "get_pizza_types";
public async Task<bool> Execute(RoleDialogModel message)
{
message.ExecutionResult = "Pepperoni Pizza, Cheese Pizza, Margherita Pizza";
return true;
}
}

View file

@ -0,0 +1,14 @@
using BotSharp.Abstraction.Conversations.Models;
namespace BotSharp.Plugin.PizzaBot.Functions;
public class MakeOrderFn : IFunctionCallback
{
public string Name => "make_order";
public async Task<bool> Execute(RoleDialogModel message)
{
message.ExecutionResult = "The order number is P123-01";
return true;
}
}

View file

@ -0,0 +1,14 @@
using BotSharp.Abstraction.Conversations.Models;
namespace BotSharp.Plugin.PizzaBot.Functions;
public class OrderFoundFn : IFunctionCallback
{
public string Name => "order_found";
public async Task<bool> Execute(RoleDialogModel message)
{
message.ExecutionResult = "The order number is P123-01";
return true;
}
}

View file

@ -0,0 +1,17 @@
namespace BotSharp.Plugin.PizzaBot.Hooks;
public class PizzaBotAgentHook : AgentHookBase
{
public PizzaBotAgentHook(IServiceProvider services, AgentSettings settings)
: base(services, settings)
{
}
public override bool OnInstructionLoaded(string template, Dictionary<string, object> dict)
{
dict["current_date"] = $"{DateTime.Now:MMM dd, yyyy}";
dict["current_time"] = $"{DateTime.Now:hh:mm t}";
dict["current_weekday"] = $"{DateTime.Now:dddd}";
return true;
}
}

View file

@ -0,0 +1,19 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Plugin.PizzaBot.Hooks;
namespace BotSharp.Plugin.PizzaBot;
public class PizzaBotPlugin : IBotSharpPlugin
{
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
// Register callback function
services.AddScoped<IFunctionCallback, GetPizzaTypesFn>();
services.AddScoped<IFunctionCallback, GetPizzaPricesFn>();
services.AddScoped<IFunctionCallback, MakeOrderFn>();
services.AddScoped<IFunctionCallback, OrderFoundFn>();
// Register hooks
services.AddScoped<IAgentHook, PizzaBotAgentHook>();
}
}

View file

@ -0,0 +1,12 @@
global using System;
global using System.Collections.Generic;
global using System.Text;
global using System.Threading.Tasks;
global using System.ComponentModel.DataAnnotations;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using BotSharp.Abstraction.Plugins;
global using BotSharp.Abstraction.Functions;
global using BotSharp.Plugin.PizzaBot.Functions;
global using BotSharp.Abstraction.Agents.Settings;
global using BotSharp.Core.Agents.Services;