commit
f7cef69803
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -286,6 +286,10 @@ __pycache__/
|
|||
|
||||
/docs/_build
|
||||
*.bin
|
||||
/src/WebStarter/data/conversations
|
||||
src/WebStarter/Properties/PublishProfiles
|
||||
src/WebStarter/Properties/ServiceDependencies
|
||||
XMLs
|
||||
logs
|
||||
wwwroot
|
||||
appsettings.Production.json
|
||||
*.csproj.user
|
||||
11
BotSharp.sln
11
BotSharp.sln
|
|
@ -81,6 +81,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.HttpHandler
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.SqlDriver", "src\Plugins\BotSharp.Plugin.SqlDriver\BotSharp.Plugin.SqlDriver.csproj", "{D775DB67-A4B4-44E5-9144-522689590057}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.Dashboard", "src\Plugins\BotSharp.Plugin.Dashboard\BotSharp.Plugin.Dashboard.csproj", "{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -313,6 +315,14 @@ Global
|
|||
{D775DB67-A4B4-44E5-9144-522689590057}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D775DB67-A4B4-44E5-9144-522689590057}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D775DB67-A4B4-44E5-9144-522689590057}.Release|x64.Build.0 = Release|Any CPU
|
||||
{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -353,6 +363,7 @@ Global
|
|||
{5CA3335E-E6AD-46FD-B277-29BBC3A16500} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
|
||||
{32D9E720-6FE6-4F29-94B1-B10B05BFAD75} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
|
||||
{D775DB67-A4B4-44E5-9144-522689590057} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
|
||||
{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ BotSharp uses component design, the kernel is kept to a minimum, and business fu
|
|||
|
||||
#### Tools
|
||||
- BotSharp.Plugin.RoutingSpeeder
|
||||
- BotSharp.Plugin.WebDriver
|
||||
- BotSharp.Plugin.PizzaBot
|
||||
|
||||
#### UIs
|
||||
|
|
|
|||
BIN
docs/static/screenshots/agent-builder-agents.png
vendored
BIN
docs/static/screenshots/agent-builder-agents.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 107 KiB |
|
|
@ -8,6 +8,7 @@ public enum AgentField
|
|||
IsPublic,
|
||||
Disabled,
|
||||
Type,
|
||||
InheritAgentId,
|
||||
Profiles,
|
||||
RoutingRule,
|
||||
Instruction,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ public class Agent
|
|||
/// Templates
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public List<AgentTemplate>? Templates { get; set; }
|
||||
public List<AgentTemplate> Templates { get; set; }
|
||||
= new List<AgentTemplate>();
|
||||
|
||||
/// <summary>
|
||||
/// Samples
|
||||
|
|
@ -46,7 +47,8 @@ public class Agent
|
|||
/// Functions
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public List<FunctionDef> Functions { get; set; } = new List<FunctionDef>();
|
||||
public List<FunctionDef> Functions { get; set; }
|
||||
= new List<FunctionDef>();
|
||||
|
||||
/// <summary>
|
||||
/// Responses
|
||||
|
|
@ -62,9 +64,6 @@ public class Agent
|
|||
|
||||
public bool IsPublic { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsHost { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public PluginDef Plugin { get; set; }
|
||||
|
||||
|
|
@ -83,6 +82,11 @@ public class Agent
|
|||
public List<string> Profiles { get; set; }
|
||||
= new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Inherit from agent
|
||||
/// </summary>
|
||||
public string? InheritAgentId { get; set; }
|
||||
|
||||
public List<RoutingRule> RoutingRules { get; set; }
|
||||
= new List<RoutingRule>();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ public interface IConversationService
|
|||
Task<Conversation> UpdateConversationTitle(string id, string title);
|
||||
Task<List<Conversation>> GetLastConversations();
|
||||
Task<bool> DeleteConversation(string id);
|
||||
Task<bool> TruncateConversation(string conversationId, string messageId);
|
||||
|
||||
/// <summary>
|
||||
/// Send message to LLM
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
namespace BotSharp.Abstraction.Conversations.Models;
|
||||
|
||||
public class TruncateMessageRequest
|
||||
{
|
||||
public string? TruncateMessageId { get; set; }
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
namespace BotSharp.Abstraction.Models;
|
||||
|
||||
public class MessageConfig
|
||||
public class MessageConfig : TruncateMessageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Completion Provider
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
namespace BotSharp.Abstraction.Plugins;
|
||||
|
||||
public class PluginDependencyAttribute : Attribute
|
||||
{
|
||||
public string[] PluginNames { get; set; }
|
||||
|
||||
public PluginDependencyAttribute(params string[] pluginNames)
|
||||
{
|
||||
PluginNames = pluginNames;
|
||||
}
|
||||
}
|
||||
|
|
@ -46,8 +46,11 @@ public interface IBotSharpRepository
|
|||
PagedItems<Conversation> GetConversations(ConversationFilter filter);
|
||||
void UpdateConversationTitle(string conversationId, string title);
|
||||
List<Conversation> GetLastConversations();
|
||||
bool TruncateConversation(string conversationId, string messageId);
|
||||
#endregion
|
||||
#region Statistics
|
||||
void IncrementConversationCount();
|
||||
#endregion
|
||||
|
||||
#region Execution Log
|
||||
void AddExecutionLogs(string conversationId, List<string> logs);
|
||||
List<string> GetExecutionLogs(string conversationId);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class RoutingRule
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{AgentName} {Field}";
|
||||
return $"{Type} {AgentName} {Field}";
|
||||
}
|
||||
|
||||
public RoutingRule()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
|
||||
namespace BotSharp.Abstraction.Planning;
|
||||
namespace BotSharp.Abstraction.Routing.Planning;
|
||||
|
||||
public interface IExecutor
|
||||
{
|
||||
Task<RoleDialogModel> Execute(IRoutingService routing,
|
||||
Task<RoleDialogModel> Execute(IRoutingService routing,
|
||||
FunctionCallFromLlm inst,
|
||||
RoleDialogModel message,
|
||||
List<RoleDialogModel> dialogs);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
|
||||
namespace BotSharp.Abstraction.Planning;
|
||||
namespace BotSharp.Abstraction.Routing.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// Task breakdown and execution plan
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BotSharp.Abstraction.Statistics.Model
|
||||
{
|
||||
public class Statistics
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public int ConversationCount { get; set; }
|
||||
public DateTime UpdatedDateTime { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BotSharp.Abstraction.Statistics.Settings
|
||||
{
|
||||
public class StatisticsSettings
|
||||
{
|
||||
public string DataDir { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -107,9 +107,9 @@ public partial class AgentService
|
|||
foreach (var file in Directory.GetFiles(templateDir))
|
||||
{
|
||||
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
|
||||
var splits = fileName.ToLower().Split('.');
|
||||
var name = splits[0];
|
||||
var extension = splits[1];
|
||||
var splitIdx = fileName.LastIndexOf(".");
|
||||
var name = fileName.Substring(0, splitIdx);
|
||||
var extension = fileName.Substring(splitIdx + 1);
|
||||
if (extension.IsEqualTo(_agentSettings.TemplateFormat))
|
||||
{
|
||||
var content = File.ReadAllText(file);
|
||||
|
|
|
|||
|
|
@ -13,20 +13,12 @@ public partial class AgentService
|
|||
{
|
||||
var agents = _db.GetAgents(filter);
|
||||
|
||||
// Set IsRouter
|
||||
var routeSetting = _services.GetRequiredService<RoutingSettings>();
|
||||
foreach (var agent in agents)
|
||||
{
|
||||
agent.Plugin = GetPlugin(agent.Id);
|
||||
}
|
||||
|
||||
// Set IsHost
|
||||
var agentSetting = _services.GetRequiredService<AgentSettings>();
|
||||
foreach (var agent in agents)
|
||||
{
|
||||
agent.IsHost = agentSetting.HostAgentId == agent.Id;
|
||||
}
|
||||
|
||||
agents = agents.Where(x => x.Installed).ToList();
|
||||
var pager = filter?.Pager ?? new Pagination();
|
||||
return new PagedItems<Agent>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,27 @@ public partial class AgentService
|
|||
throw new Exception($"Can't load agent by id: {id}");
|
||||
}
|
||||
|
||||
if (agent.InheritAgentId != null)
|
||||
{
|
||||
var inheritedAgent = await GetAgent(agent.InheritAgentId);
|
||||
agent.Templates.AddRange(inheritedAgent.Templates
|
||||
// exclude private template
|
||||
.Where(x => !x.Name.StartsWith("."))
|
||||
// exclude duplicate name
|
||||
.Where(x => !agent.Templates.Exists(t => t.Name == x.Name)));
|
||||
|
||||
agent.Functions.AddRange(inheritedAgent.Functions
|
||||
// exclude private template
|
||||
.Where(x => !x.Name.StartsWith("."))
|
||||
// exclude duplicate name
|
||||
.Where(x => !agent.Functions.Exists(t => t.Name == x.Name)));
|
||||
|
||||
if (agent.Instruction == null)
|
||||
{
|
||||
agent.Instruction = inheritedAgent.Instruction;
|
||||
}
|
||||
}
|
||||
|
||||
agent.TemplateDict = new Dictionary<string, object>();
|
||||
|
||||
// Populate state into dictionary
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
|
|
@ -50,11 +50,15 @@
|
|||
<None Remove="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\instruction.liquid" />
|
||||
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\agent.json" />
|
||||
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\instruction.liquid" />
|
||||
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\.welcome.liquid" />
|
||||
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.hf.liquid" />
|
||||
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.naive.liquid" />
|
||||
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.sequential.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\welcome.liquid" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\agent.json" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\instruction.liquid" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.executor.liquid" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.reviewer.liquid" />
|
||||
<None Remove="data\plugins\config.json" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
@ -83,7 +87,19 @@
|
|||
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\response_with_function.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\welcome.liquid">
|
||||
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\.welcome.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\agent.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\instruction.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.executor.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.reviewer.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\plugins\config.json">
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
using BotSharp.Abstraction.Instructs;
|
||||
using BotSharp.Abstraction.Messaging;
|
||||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Plugins.Models;
|
||||
using BotSharp.Abstraction.Routing.Planning;
|
||||
using BotSharp.Abstraction.Settings;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
using BotSharp.Core.Instructs;
|
||||
using BotSharp.Core.Messaging;
|
||||
using BotSharp.Core.Planning;
|
||||
using BotSharp.Core.Routing.Planning;
|
||||
using BotSharp.Core.Templating;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
using BotSharp.Abstraction.Repositories;
|
||||
|
||||
namespace BotSharp.Core.Conversations.Services;
|
||||
|
||||
public partial class ConversationService : IConversationService
|
||||
{
|
||||
public async Task<bool> TruncateConversation(string conversationId, string messageId)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var isSaved = db.TruncateConversation(conversationId, messageId);
|
||||
return await Task.FromResult(isSaved);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,10 +21,11 @@ public class LlmProviderPlugin : IBotSharpPlugin
|
|||
services.AddScoped(provider =>
|
||||
{
|
||||
var settingService = provider.GetRequiredService<ISettingService>();
|
||||
var loger = provider.GetRequiredService<ILogger<LlmProviderPlugin>>();
|
||||
var llmProviders = settingService.Bind<List<LlmProviderSetting>>("LlmProviders");
|
||||
foreach (var llmProvider in llmProviders)
|
||||
{
|
||||
Console.WriteLine($"Loaded LlmProvider {llmProvider.Provider} settings with {llmProvider.Models.Count} models.");
|
||||
loger.LogInformation($"Loaded LlmProvider {llmProvider.Provider} settings with {llmProvider.Models.Count} models.");
|
||||
}
|
||||
return llmProviders;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,6 +16,14 @@ public partial class InstructService : IInstructService
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute completion by using specified instruction or template
|
||||
/// </summary>
|
||||
/// <param name="agentId">Agent (static agent)</param>
|
||||
/// <param name="message">Additional message provided by user</param>
|
||||
/// <param name="templateName">Template name</param>
|
||||
/// <param name="instruction">System prompt</param>
|
||||
/// <returns></returns>
|
||||
public async Task<InstructResult> Execute(string agentId, RoleDialogModel message, string? templateName = null, string? instruction = null)
|
||||
{
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
|
|
|
|||
|
|
@ -27,12 +27,17 @@ public class PluginLoader
|
|||
_settings = settings;
|
||||
}
|
||||
|
||||
public void Load(Action<Assembly> loaded)
|
||||
public void Load(Action<Assembly> loaded, string? plugin = null)
|
||||
{
|
||||
_executingDir = Directory.GetParent(Assembly.GetEntryAssembly().Location).FullName;
|
||||
|
||||
_settings.Assemblies.ToList().ForEach(assemblyName =>
|
||||
{
|
||||
if (plugin != null && plugin != assemblyName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var assemblyPath = Path.Combine(_executingDir, assemblyName + ".dll");
|
||||
if (File.Exists(assemblyPath))
|
||||
{
|
||||
|
|
@ -45,38 +50,66 @@ public class PluginLoader
|
|||
|
||||
foreach (var module in modules)
|
||||
{
|
||||
module.RegisterDI(_services, _config);
|
||||
// string classSummary = GetSummaryComment(module.GetType());
|
||||
var name = string.IsNullOrEmpty(module.Name) ? module.GetType().Name : module.Name;
|
||||
_modules.Add(module);
|
||||
_plugins.Add(new PluginDef
|
||||
if (_plugins.Exists(x => x.Id == module.Id))
|
||||
{
|
||||
Id = module.Id,
|
||||
Name = name,
|
||||
Module = module,
|
||||
Description = module.Description,
|
||||
Assembly = assemblyName,
|
||||
IconUrl = module.IconUrl,
|
||||
AgentIds = module.AgentIds
|
||||
});
|
||||
Console.Write($"Loaded plugin ");
|
||||
Console.Write(name, Color.Green);
|
||||
Console.WriteLine($" from {assemblyName}.");
|
||||
if (!string.IsNullOrEmpty(module.Description))
|
||||
{
|
||||
Console.WriteLine(module.Description);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Solve plugin dependency
|
||||
var attr = module.GetType().GetCustomAttribute<PluginDependencyAttribute>();
|
||||
if (attr != null)
|
||||
{
|
||||
foreach (var plugin in attr.PluginNames)
|
||||
{
|
||||
if (!_plugins.Any(x => x.Assembly == plugin))
|
||||
{
|
||||
Load(loaded, plugin);
|
||||
}
|
||||
|
||||
if (!_plugins.Any(x => x.Assembly == plugin))
|
||||
{
|
||||
Console.WriteLine($"Load dependent plugin {plugin} failed by {module.Name}.", Color.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InitModule(assemblyName, module);
|
||||
}
|
||||
|
||||
loaded(assembly);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Can't find assemble {assemblyPath}.");
|
||||
Console.WriteLine($"Can't find assemble {assemblyPath}.", Color.Red);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void InitModule(string assembly, IBotSharpPlugin module)
|
||||
{
|
||||
module.RegisterDI(_services, _config);
|
||||
// string classSummary = GetSummaryComment(module.GetType());
|
||||
var name = string.IsNullOrEmpty(module.Name) ? module.GetType().Name : module.Name;
|
||||
_modules.Add(module);
|
||||
_plugins.Add(new PluginDef
|
||||
{
|
||||
Id = module.Id,
|
||||
Name = name,
|
||||
Module = module,
|
||||
Description = module.Description,
|
||||
Assembly = assembly,
|
||||
IconUrl = module.IconUrl,
|
||||
AgentIds = module.AgentIds
|
||||
});
|
||||
Console.Write($"Loaded plugin ");
|
||||
Console.Write(name, Color.Green);
|
||||
Console.WriteLine($" from {assembly}.");
|
||||
if (!string.IsNullOrEmpty(module.Description))
|
||||
{
|
||||
Console.WriteLine(module.Description);
|
||||
}
|
||||
}
|
||||
|
||||
public List<PluginDef> GetPlugins(IServiceProvider services)
|
||||
{
|
||||
// Apply user configurations
|
||||
|
|
@ -124,6 +157,13 @@ public class PluginLoader
|
|||
var agent = agentService.LoadAgent(agentId).Result;
|
||||
agent.Disabled = false;
|
||||
agentService.UpdateAgent(agent, AgentField.Disabled);
|
||||
|
||||
if (agent.InheritAgentId != null)
|
||||
{
|
||||
agent = agentService.LoadAgent(agent.InheritAgentId).Result;
|
||||
agent.Disabled = false;
|
||||
agentService.UpdateAgent(agent, AgentField.Disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -178,8 +178,18 @@ public class BotSharpDbContext : Database, IBotSharpRepository
|
|||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
|
||||
public bool TruncateConversation(string conversationId, string messageId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
#region Stats
|
||||
public void IncrementConversationCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region User
|
||||
public User? GetUserByEmail(string email)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ namespace BotSharp.Core.Repository
|
|||
case AgentField.Type:
|
||||
UpdateAgentType(agent.Id, agent.Type);
|
||||
break;
|
||||
case AgentField.InheritAgentId:
|
||||
UpdateAgentInheritAgentId(agent.Id, agent.InheritAgentId);
|
||||
break;
|
||||
case AgentField.Profiles:
|
||||
UpdateAgentProfiles(agent.Id, agent.Profiles);
|
||||
break;
|
||||
|
|
@ -123,6 +126,17 @@ namespace BotSharp.Core.Repository
|
|||
File.WriteAllText(agentFile, json);
|
||||
}
|
||||
|
||||
private void UpdateAgentInheritAgentId(string agentId, string? inheritAgentId)
|
||||
{
|
||||
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||
if (agent == null) return;
|
||||
|
||||
agent.InheritAgentId = inheritAgentId;
|
||||
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||
var json = JsonSerializer.Serialize(agent, _options);
|
||||
File.WriteAllText(agentFile, json);
|
||||
}
|
||||
|
||||
private void UpdateAgentProfiles(string agentId, List<string> profiles)
|
||||
{
|
||||
if (profiles.IsNullOrEmpty()) return;
|
||||
|
|
@ -379,9 +393,9 @@ namespace BotSharp.Core.Repository
|
|||
foreach (var file in Directory.GetFiles(dir))
|
||||
{
|
||||
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
|
||||
var splits = ParseFileNameByPath(fileName.ToLower());
|
||||
var name = splits[0];
|
||||
var extension = splits[1];
|
||||
var splitIdx = fileName.LastIndexOf(".");
|
||||
var name = fileName.Substring(0, splitIdx);
|
||||
var extension = fileName.Substring(splitIdx + 1);
|
||||
if (name.IsEqualTo(templateName) && extension.IsEqualTo(_agentSettings.TemplateFormat))
|
||||
{
|
||||
return File.ReadAllText(file);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Repositories.Models;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace BotSharp.Core.Repository
|
||||
|
|
@ -256,6 +257,35 @@ namespace BotSharp.Core.Repository
|
|||
}
|
||||
|
||||
|
||||
public bool TruncateConversation(string conversationId, string messageId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) return false;
|
||||
|
||||
var dialogs = new List<DialogElement>();
|
||||
var convDir = FindConversationDirectory(conversationId);
|
||||
if (string.IsNullOrEmpty(convDir)) return false;
|
||||
|
||||
var dialogDir = Path.Combine(convDir, DIALOG_FILE);
|
||||
dialogs = CollectDialogElements(dialogDir);
|
||||
if (dialogs.IsNullOrEmpty()) return false;
|
||||
|
||||
var foundIdx = dialogs.FindIndex(x => x.MetaData?.MessageId == messageId);
|
||||
if (foundIdx < 0) return false;
|
||||
|
||||
// Handle truncated dialogs
|
||||
var isSaved = HandleTruncatedDialogs(dialogDir, dialogs, foundIdx);
|
||||
if (!isSaved) return false;
|
||||
|
||||
// Handle truncated states
|
||||
var refTime = dialogs.ElementAt(foundIdx).MetaData.CreateTime;
|
||||
var stateDir = Path.Combine(convDir, STATE_FILE);
|
||||
var states = CollectConversationStates(stateDir);
|
||||
isSaved = HandleTruncatedStates(stateDir, states, refTime);
|
||||
|
||||
return isSaved;
|
||||
}
|
||||
|
||||
|
||||
#region Private methods
|
||||
private string? FindConversationDirectory(string conversationId)
|
||||
{
|
||||
|
|
@ -304,8 +334,9 @@ namespace BotSharp.Core.Repository
|
|||
foreach (var element in dialogs)
|
||||
{
|
||||
var meta = element.MetaData;
|
||||
var createTime = meta.CreateTime.ToString("MM/dd/yyyy hh:mm:ss.fff tt", CultureInfo.InvariantCulture);
|
||||
var source = meta.FunctionName ?? meta.SenderId;
|
||||
var metaStr = $"{meta.CreateTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{source}";
|
||||
var metaStr = $"{createTime}|{meta.Role}|{meta.AgentId}|{meta.MessageId}|{source}";
|
||||
dialogTexts.Add(metaStr);
|
||||
var content = $" - {element.Content}";
|
||||
dialogTexts.Add(content);
|
||||
|
|
@ -325,6 +356,49 @@ namespace BotSharp.Core.Repository
|
|||
states = JsonSerializer.Deserialize<List<StateKeyValue>>(stateStr, _options);
|
||||
return states ?? new List<StateKeyValue>();
|
||||
}
|
||||
|
||||
private bool HandleTruncatedDialogs(string dialogDir, List<DialogElement> dialogs, int foundIdx)
|
||||
{
|
||||
var truncatedDialogs = dialogs.Where((x, idx) => idx < foundIdx).ToList();
|
||||
var isSaved = SaveTruncatedDialogs(dialogDir, truncatedDialogs);
|
||||
return isSaved;
|
||||
}
|
||||
|
||||
private bool HandleTruncatedStates(string stateDir, List<StateKeyValue> states, DateTime refTime)
|
||||
{
|
||||
var truncatedStates = new List<StateKeyValue>();
|
||||
foreach (var state in states)
|
||||
{
|
||||
var values = state.Values.Where(x => x.UpdateTime < refTime).ToList();
|
||||
if (values.Count == 0) continue;
|
||||
|
||||
state.Values = values;
|
||||
truncatedStates.Add(state);
|
||||
}
|
||||
|
||||
var isSaved = SaveTruncatedStates(stateDir, truncatedStates);
|
||||
return isSaved;
|
||||
}
|
||||
|
||||
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.WriteAllLines(dialogDir, texts);
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
using BotSharp.Abstraction.Statistics.Model;
|
||||
using System.IO;
|
||||
|
||||
namespace BotSharp.Core.Repository
|
||||
{
|
||||
public partial class FileRepository
|
||||
{
|
||||
public void IncrementConversationCount()
|
||||
{
|
||||
var statsFileDirectory = FindCurrentStatsDirectory();
|
||||
if (statsFileDirectory == null)
|
||||
{
|
||||
statsFileDirectory = CreateStatsFileDirectory();
|
||||
}
|
||||
var fileName = GenerateStatsFileName();
|
||||
var statsFile = Path.Combine(statsFileDirectory, fileName);
|
||||
if (!File.Exists(statsFile))
|
||||
{
|
||||
File.WriteAllText(statsFile, JsonSerializer.Serialize(new Statistics()
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
UpdatedDateTime = DateTime.UtcNow
|
||||
}, _options));
|
||||
}
|
||||
var json = File.ReadAllText(statsFile);
|
||||
var stats = JsonSerializer.Deserialize<Statistics>(json, _options);
|
||||
stats.ConversationCount += 1;
|
||||
stats.UpdatedDateTime = DateTime.UtcNow;
|
||||
File.WriteAllText(statsFile, JsonSerializer.Serialize(stats, _options));
|
||||
}
|
||||
public string? CreateStatsFileDirectory()
|
||||
{
|
||||
var dir = GenerateStatsDirectoryName();
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
private string? FindCurrentStatsDirectory()
|
||||
{
|
||||
var dir = GenerateStatsDirectoryName();
|
||||
if (!Directory.Exists(dir)) return null;
|
||||
|
||||
return dir;
|
||||
}
|
||||
private string GenerateStatsDirectoryName()
|
||||
{
|
||||
return Path.Combine(_dbSettings.FileRepository, _statisticsSetting.DataDir, DateTime.UtcNow.Year.ToString(), DateTime.UtcNow.ToString("MM"));
|
||||
}
|
||||
private string GenerateStatsFileName()
|
||||
{
|
||||
var fileName = DateTime.UtcNow.ToString("MMdd");
|
||||
return $"{fileName}-{STATS_FILE}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ using BotSharp.Abstraction.Routing.Settings;
|
|||
using BotSharp.Abstraction.Evaluations.Settings;
|
||||
using System.Text.Encodings.Web;
|
||||
using BotSharp.Abstraction.Plugins.Models;
|
||||
using BotSharp.Abstraction.Statistics.Settings;
|
||||
|
||||
namespace BotSharp.Core.Repository;
|
||||
|
||||
|
|
@ -20,6 +21,7 @@ public partial class FileRepository : IBotSharpRepository
|
|||
private readonly BotSharpDatabaseSettings _dbSettings;
|
||||
private readonly AgentSettings _agentSettings;
|
||||
private readonly ConversationSetting _conversationSettings;
|
||||
private readonly StatisticsSettings _statisticsSetting;
|
||||
private JsonSerializerOptions _options;
|
||||
|
||||
private const string AGENT_FILE = "agent.json";
|
||||
|
|
@ -29,6 +31,7 @@ public partial class FileRepository : IBotSharpRepository
|
|||
private const string USER_FILE = "user.json";
|
||||
private const string USER_AGENT_FILE = "agents.json";
|
||||
private const string CONVERSATION_FILE = "conversation.json";
|
||||
private const string STATS_FILE = "stats.json";
|
||||
private const string DIALOG_FILE = "dialogs.txt";
|
||||
private const string STATE_FILE = "state.json";
|
||||
private const string EXECUTION_LOG_FILE = "execution.log";
|
||||
|
|
@ -38,12 +41,14 @@ public partial class FileRepository : IBotSharpRepository
|
|||
IServiceProvider services,
|
||||
BotSharpDatabaseSettings dbSettings,
|
||||
AgentSettings agentSettings,
|
||||
ConversationSetting conversationSettings)
|
||||
ConversationSetting conversationSettings,
|
||||
StatisticsSettings statisticsSettings)
|
||||
{
|
||||
_services = services;
|
||||
_dbSettings = dbSettings;
|
||||
_agentSettings = agentSettings;
|
||||
_conversationSettings = conversationSettings;
|
||||
_statisticsSetting = statisticsSettings;
|
||||
|
||||
_options = new JsonSerializerOptions
|
||||
{
|
||||
|
|
@ -176,10 +181,10 @@ public partial class FileRepository : IBotSharpRepository
|
|||
return (agent, agentFile);
|
||||
}
|
||||
|
||||
private string FetchInstruction(string fileDir)
|
||||
private string? FetchInstruction(string fileDir)
|
||||
{
|
||||
var file = Path.Combine(fileDir, $"{AGENT_INSTRUCTION_FILE}.{_agentSettings.TemplateFormat}");
|
||||
if (!File.Exists(file)) return string.Empty;
|
||||
if (!File.Exists(file)) return null;
|
||||
|
||||
var instruction = File.ReadAllText(file);
|
||||
return instruction;
|
||||
|
|
@ -212,9 +217,9 @@ public partial class FileRepository : IBotSharpRepository
|
|||
foreach (var file in Directory.GetFiles(templateDir))
|
||||
{
|
||||
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
|
||||
var splits = fileName.ToLower().Split('.');
|
||||
var name = string.Join('.', splits.Take(splits.Length - 1));
|
||||
var extension = splits.Last();
|
||||
var splitIdx = fileName.LastIndexOf(".");
|
||||
var name = fileName.Substring(0, splitIdx);
|
||||
var extension = fileName.Substring(splitIdx + 1);
|
||||
if (extension.Equals(_agentSettings.TemplateFormat, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var content = File.ReadAllText(file);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ using BotSharp.Abstraction.Repositories;
|
|||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using BotSharp.Core.Planning;
|
||||
using BotSharp.Core.Routing.Planning;
|
||||
|
||||
namespace BotSharp.Core.Routing.Handlers;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using BotSharp.Core.Planning;
|
||||
using BotSharp.Core.Routing.Planning;
|
||||
|
||||
namespace BotSharp.Core.Routing.Handlers;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ using BotSharp.Abstraction.Functions.Models;
|
|||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using BotSharp.Core.Planning;
|
||||
using BotSharp.Core.Routing.Planning;
|
||||
|
||||
namespace BotSharp.Core.Routing.Handlers;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using BotSharp.Core.Planning;
|
||||
using BotSharp.Core.Routing.Planning;
|
||||
|
||||
namespace BotSharp.Core.Routing.Handlers;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Routing.Planning;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
|
||||
namespace BotSharp.Core.Planning;
|
||||
namespace BotSharp.Core.Routing.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// Human feedback based planner
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Planning;
|
||||
|
||||
namespace BotSharp.Core.Planning;
|
||||
namespace BotSharp.Core.Routing.Planning;
|
||||
|
||||
public class InstructExecutor : IExecutor
|
||||
{
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Routing.Planning;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
|
||||
namespace BotSharp.Core.Planning;
|
||||
namespace BotSharp.Core.Routing.Planning;
|
||||
|
||||
public class NaivePlanner : IPlaner
|
||||
{
|
||||
|
|
@ -32,7 +32,7 @@ public class NaivePlanner : IPlaner
|
|||
var completion = CompletionProvider.GetTextCompletion(_services);*/
|
||||
|
||||
// chat completion
|
||||
var completion = CompletionProvider.GetChatCompletion(_services,
|
||||
var completion = CompletionProvider.GetChatCompletion(_services,
|
||||
provider: router?.LlmConfig?.Provider,
|
||||
model: router?.LlmConfig?.Model);
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ public class NaivePlanner : IPlaner
|
|||
{
|
||||
// text completion
|
||||
// text = await completion.GetCompletion(content, router.Id, messageId);
|
||||
var dialogs = new List<RoleDialogModel>
|
||||
var dialogs = new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, next)
|
||||
{
|
||||
|
|
@ -91,7 +91,7 @@ public class NaivePlanner : IPlaner
|
|||
if (inst.UnmatchedAgent)
|
||||
{
|
||||
var unmatchedAgentId = context.GetCurrentAgentId();
|
||||
|
||||
|
||||
// Exclude the wrong routed agent
|
||||
var agents = router.TemplateDict["routing_agents"] as RoutableAgent[];
|
||||
router.TemplateDict["routing_agents"] = agents.Where(x => x.AgentId != unmatchedAgentId).ToArray();
|
||||
|
|
@ -123,8 +123,8 @@ public class NaivePlanner : IPlaner
|
|||
private void FixMalformedResponse(FunctionCallFromLlm args)
|
||||
{
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var agents = agentService.GetAgents(new AgentFilter
|
||||
{
|
||||
var agents = agentService.GetAgents(new AgentFilter
|
||||
{
|
||||
Type = AgentType.Task
|
||||
}).Result.Items.ToList();
|
||||
var malformed = false;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Routing.Planning;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
|
||||
namespace BotSharp.Core.Planning;
|
||||
namespace BotSharp.Core.Routing.Planning;
|
||||
|
||||
public class SequentialPlanner : IPlaner
|
||||
{
|
||||
|
|
@ -91,7 +91,7 @@ public class SequentialPlanner : IPlaner
|
|||
context.Empty();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Handover to Router;
|
||||
context.Pop();
|
||||
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using BotSharp.Abstraction.Settings;
|
||||
using BotSharp.Core.Planning;
|
||||
using BotSharp.Core.Routing.Hooks;
|
||||
using BotSharp.Core.Routing.Planning;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace BotSharp.Core.Routing;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Routing.Enums;
|
||||
using BotSharp.Core.Planning;
|
||||
using BotSharp.Abstraction.Routing.Planning;
|
||||
using BotSharp.Core.Routing.Planning;
|
||||
|
||||
namespace BotSharp.Core.Routing;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Routing.Planning;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using System.Drawing;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
{
|
||||
"id": "01e2fc5c-2c89-4ec7-8470-7688608b496c",
|
||||
"name": "Chatbot",
|
||||
"description": "AI chatbot that can do variaty of tasks",
|
||||
"description": "Chatbot is used to test the performance of different large models and does not interact with external APIs.",
|
||||
"type": "task",
|
||||
"createdDateTime": "2024-01-15T10:39:32Z",
|
||||
"updatedDateTime": "2024-01-15T14:39:32Z",
|
||||
"iconUrl": "/images/users/bot.png",
|
||||
"iconUrl": "/images/logo.png",
|
||||
"disabled": false,
|
||||
"isPublic": true,
|
||||
"profiles": [ "standalone" ]
|
||||
"isPublic": true
|
||||
}
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
In order to execute the instructions listed by the user in the order specified by the user.
|
||||
In order to execute the listed instructions in the order specified by the user.
|
||||
What is the next step based on the CONVERSATION?
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"id": "dfd9b46d-d00c-40af-8a75-3fbdc2b89869",
|
||||
"name": "Evaluator",
|
||||
"description": "Evaluate the performance of the LLM agents",
|
||||
"createdDateTime": "2023-08-18T00:00:00Z",
|
||||
"updatedDateTime": "2023-08-18T00:00:00Z",
|
||||
"disabled": true,
|
||||
"llmConfig": {
|
||||
"model": "gpt-35-turbo-instruct"
|
||||
}
|
||||
}
|
||||
|
|
@ -25,23 +25,37 @@ public class AgentController : ControllerBase
|
|||
[HttpGet("/agent/{id}")]
|
||||
public async Task<AgentViewModel> GetAgent([FromRoute] string id)
|
||||
{
|
||||
var agent = await _agentService.GetAgent(id);
|
||||
return AgentViewModel.FromAgent(agent);
|
||||
var agents = await GetAgents(new AgentFilter
|
||||
{
|
||||
AgentIds = new List<string> { id }
|
||||
});
|
||||
return agents.Items.First();
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("/agents")]
|
||||
public async Task<PagedItems<AgentViewModel>> GetAgents([FromQuery] AgentFilter filter)
|
||||
{
|
||||
var agentSetting = _services.GetRequiredService<AgentSettings>();
|
||||
var pagedAgents = await _agentService.GetAgents(filter);
|
||||
|
||||
// prerender agent
|
||||
var items = new List<Agent>();
|
||||
foreach (var agent in pagedAgents.Items)
|
||||
{
|
||||
var renderedAgent = await _agentService.LoadAgent(agent.Id);
|
||||
items.Add(renderedAgent);
|
||||
}
|
||||
|
||||
// Set IsHost
|
||||
var agents = items.Select(x => AgentViewModel.FromAgent(x)).ToList();
|
||||
foreach(var agent in agents)
|
||||
{
|
||||
agent.IsHost = agentSetting.HostAgentId == agent.Id;
|
||||
}
|
||||
|
||||
return new PagedItems<AgentViewModel>
|
||||
{
|
||||
Items = items.Select(x => AgentViewModel.FromAgent(x)).ToList(),
|
||||
Items = agents,
|
||||
Count = pagedAgents.Count
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ public class ConversationController : ControllerBase
|
|||
var conversations = await service.GetConversations(filter);
|
||||
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var list = conversations.Items
|
||||
.Select(x => ConversationViewModel.FromSession(x))
|
||||
.ToList();
|
||||
|
|
@ -45,6 +46,9 @@ public class ConversationController : ControllerBase
|
|||
{
|
||||
var user = await userService.GetUser(item.User.Id);
|
||||
item.User = UserViewModel.FromUser(user);
|
||||
|
||||
var agent = await agentService.GetAgent(item.AgentId);
|
||||
item.AgentName = agent.Name;
|
||||
}
|
||||
|
||||
return new PagedItems<ConversationViewModel>
|
||||
|
|
@ -110,14 +114,26 @@ public class ConversationController : ControllerBase
|
|||
return response;
|
||||
}
|
||||
|
||||
[HttpDelete("/conversation/{conversationId}/message/{messageId}")]
|
||||
public async Task<bool> DeleteConversationMessage([FromRoute] string conversationId, [FromRoute] string messageId)
|
||||
{
|
||||
var conversationService = _services.GetRequiredService<IConversationService>();
|
||||
var response = await conversationService.TruncateConversation(conversationId, messageId);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("/conversation/{agentId}/{conversationId}")]
|
||||
public async Task<ChatResponseModel> SendMessage([FromRoute] string agentId,
|
||||
[FromRoute] string conversationId,
|
||||
[FromBody] NewMessageModel input)
|
||||
{
|
||||
var inputMsg = new RoleDialogModel(AgentRole.User, input.Text);
|
||||
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
if (!string.IsNullOrEmpty(input.TruncateMessageId))
|
||||
{
|
||||
await conv.TruncateConversation(conversationId, input.TruncateMessageId);
|
||||
}
|
||||
|
||||
var inputMsg = new RoleDialogModel(AgentRole.User, input.Text);
|
||||
conv.SetConversationId(conversationId, input.States);
|
||||
conv.States.SetState("channel", input.Channel)
|
||||
.SetState("provider", input.Provider)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ public class PluginController : ControllerBase
|
|||
{
|
||||
var menu = new List<PluginMenuDef>
|
||||
{
|
||||
new PluginMenuDef("Dashboard", link: "/page/dashboard", icon: "bx bx-home-circle", weight: 1),
|
||||
new PluginMenuDef("Apps", weight: 5)
|
||||
{
|
||||
IsHeader = true,
|
||||
|
|
@ -54,15 +53,15 @@ public class PluginController : ControllerBase
|
|||
return menu;
|
||||
}
|
||||
|
||||
[HttpPost("/plugin/{id}/enable")]
|
||||
public PluginDef EnablePlugin([FromRoute] string id)
|
||||
[HttpPost("/plugin/{id}/install")]
|
||||
public PluginDef InstallPlugin([FromRoute] string id)
|
||||
{
|
||||
var loader = _services.GetRequiredService<PluginLoader>();
|
||||
return loader.UpdatePluginStatus(_services, id, true);
|
||||
}
|
||||
|
||||
[HttpPost("/plugin/{id}/disable")]
|
||||
public PluginDef DisablePluginStats([FromRoute] string id)
|
||||
[HttpPost("/plugin/{id}/remove")]
|
||||
public PluginDef RemovePluginStats([FromRoute] string id)
|
||||
{
|
||||
var loader = _services.GetRequiredService<PluginLoader>();
|
||||
return loader.UpdatePluginStatus(_services, id, false);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ public class AgentViewModel
|
|||
Responses = agent.Responses,
|
||||
Samples = agent.Samples,
|
||||
IsPublic= agent.IsPublic,
|
||||
IsHost = agent.IsHost,
|
||||
Disabled = agent.Disabled,
|
||||
IconUrl = agent.IconUrl,
|
||||
Profiles = agent.Profiles ?? new List<string>(),
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ public class ConversationViewModel
|
|||
[JsonPropertyName("agent_id")]
|
||||
public string AgentId { get; set; }
|
||||
|
||||
[JsonPropertyName("agent_name")]
|
||||
public string AgentName { get; set; }
|
||||
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
public UserViewModel User { get; set; } = new UserViewModel();
|
||||
|
||||
[JsonPropertyName("unread_msg_count")]
|
||||
public int UnreadMsgCount { get; set; }
|
||||
|
||||
public string Event { get; set; }
|
||||
|
||||
public string Channel { get; set; } = ConversationChannel.OpenAPI;
|
||||
|
|
|
|||
|
|
@ -292,7 +292,9 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
else if (x.Role == ChatRole.User)
|
||||
{
|
||||
var m = x as ChatRequestUserMessage;
|
||||
return $"{m.Role}: {m.Content}";
|
||||
return !string.IsNullOrEmpty(m.Name) ?
|
||||
$"{m.Name}: {m.Content}" :
|
||||
$"{m.Role}: {m.Content}";
|
||||
}
|
||||
else if (x.Role == ChatRole.Assistant)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public class ChatHubConversationHook : ConversationHookBase
|
|||
var agent = await agentService.LoadAgent(conversation.AgentId);
|
||||
|
||||
// Check if the Welcome template exists.
|
||||
var welcomeTemplate = agent.Templates?.FirstOrDefault(x => x.Name == "welcome");
|
||||
var welcomeTemplate = agent.Templates?.FirstOrDefault(x => x.Name == ".welcome");
|
||||
if (welcomeTemplate != null)
|
||||
{
|
||||
var richContentService = _services.GetRequiredService<IRichContentService>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Infrastructure\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
32
src/Plugins/BotSharp.Plugin.Dashboard/DashboardPlugin.cs
Normal file
32
src/Plugins/BotSharp.Plugin.Dashboard/DashboardPlugin.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using BotSharp.Abstraction.Conversations;
|
||||
using BotSharp.Abstraction.Plugins;
|
||||
using BotSharp.Abstraction.Plugins.Models;
|
||||
using BotSharp.Abstraction.Settings;
|
||||
using BotSharp.Abstraction.Statistics.Settings;
|
||||
using BotSharp.Plugin.Dashboard.Hooks;
|
||||
|
||||
namespace BotSharp.Plugin.Dashboard;
|
||||
|
||||
public class DashboardPlugin : IBotSharpPlugin
|
||||
{
|
||||
public string Id => "d42a0c21-b461-44f6-ada2-499510d260af";
|
||||
public string Name => "Dashboard";
|
||||
public string Description => "Dashboard that offers real-time statistics on model performance, usage trends, and user feedback";
|
||||
|
||||
public void RegisterDI(IServiceCollection services, IConfiguration config)
|
||||
{
|
||||
services.AddScoped<IConversationHook, StatsConversationHook>();
|
||||
services.AddScoped(provider =>
|
||||
{
|
||||
var settingService = provider.GetRequiredService<ISettingService>();
|
||||
return settingService.Bind<StatisticsSettings>("Statistics");
|
||||
});
|
||||
}
|
||||
|
||||
public bool AttachMenu(List<PluginMenuDef> menu)
|
||||
{
|
||||
var section = menu.First(x => x.Label == "Apps");
|
||||
menu.Add(new PluginMenuDef("Dashboard", link: "/page/dashboard", icon: "bx bx-home-circle", weight: section.Weight - 1));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
using BotSharp.Abstraction.Conversations;
|
||||
using BotSharp.Abstraction.Plugins.Models;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
|
||||
namespace BotSharp.Plugin.Dashboard.Hooks;
|
||||
|
||||
public class StatsConversationHook : ConversationHookBase
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
public StatsConversationHook(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public override async Task OnConversationInitialized(Conversation conversation)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
db.IncrementConversationCount();
|
||||
}
|
||||
}
|
||||
13
src/Plugins/BotSharp.Plugin.Dashboard/Using.cs
Normal file
13
src/Plugins/BotSharp.Plugin.Dashboard/Using.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Text;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Linq;
|
||||
global using System.Text.Json;
|
||||
global using BotSharp.Abstraction.Conversations.Models;
|
||||
global using BotSharp.Abstraction.Agents.Models;
|
||||
global using BotSharp.Abstraction.MLTasks;
|
||||
global using Microsoft.Extensions.Configuration;
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using System.Text.Json.Serialization;
|
||||
global using BotSharp.Abstraction.Utilities;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="PdfPig" Version="0.1.9-alpha-20231119-4537e" />
|
||||
<PackageReference Include="PdfPig" Version="0.1.9-alpha-20240121-04fc8" />
|
||||
<PackageReference Include="TensorFlow.Keras" Version="0.15.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ public class AgentDocument : MongoBase
|
|||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string? InheritAgentId { get; set; }
|
||||
public string? IconUrl { get; set; }
|
||||
public string Instruction { get; set; }
|
||||
public List<AgentTemplateMongoElement> Templates { get; set; }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ public class RoutingRuleMongoElement
|
|||
public string Description { get; set; }
|
||||
public bool Required { get; set; }
|
||||
public string? RedirectTo { get; set; }
|
||||
public string Type { get; set; }
|
||||
|
||||
public RoutingRuleMongoElement()
|
||||
{
|
||||
|
|
@ -22,7 +23,8 @@ public class RoutingRuleMongoElement
|
|||
Field = routingRule.Field,
|
||||
Description = routingRule.Description,
|
||||
Required = routingRule.Required,
|
||||
RedirectTo = routingRule.RedirectTo
|
||||
RedirectTo = routingRule.RedirectTo,
|
||||
Type = routingRule.Type,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +37,8 @@ public class RoutingRuleMongoElement
|
|||
Field = rule.Field,
|
||||
Description = rule.Description,
|
||||
Required = rule.Required,
|
||||
RedirectTo = rule.RedirectTo
|
||||
RedirectTo = rule.RedirectTo,
|
||||
Type = rule.Type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ public partial class MongoRepository
|
|||
case AgentField.Type:
|
||||
UpdateAgentType(agent.Id, agent.Type);
|
||||
break;
|
||||
case AgentField.InheritAgentId:
|
||||
UpdateAgentInheritAgentId(agent.Id, agent.InheritAgentId);
|
||||
break;
|
||||
case AgentField.Profiles:
|
||||
UpdateAgentProfiles(agent.Id, agent.Profiles);
|
||||
break;
|
||||
|
|
@ -119,6 +122,16 @@ public partial class MongoRepository
|
|||
_dc.Agents.UpdateOne(filter, update);
|
||||
}
|
||||
|
||||
private void UpdateAgentInheritAgentId(string agentId, string? inheritAgentId)
|
||||
{
|
||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||
var update = Builders<AgentDocument>.Update
|
||||
.Set(x => x.InheritAgentId, inheritAgentId)
|
||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||
|
||||
_dc.Agents.UpdateOne(filter, update);
|
||||
}
|
||||
|
||||
private void UpdateAgentProfiles(string agentId, List<string> profiles)
|
||||
{
|
||||
if (profiles.IsNullOrEmpty()) return;
|
||||
|
|
@ -268,6 +281,7 @@ public partial class MongoRepository
|
|||
IsPublic = agent.IsPublic,
|
||||
Disabled = agent.Disabled,
|
||||
Type = agent.Type,
|
||||
InheritAgentId = agent.InheritAgentId,
|
||||
Profiles = agent.Profiles,
|
||||
RoutingRules = !agent.RoutingRules.IsNullOrEmpty() ? agent.RoutingRules
|
||||
.Select(r => RoutingRuleMongoElement.ToDomainElement(agent.Id, agent.Name, r))
|
||||
|
|
@ -329,6 +343,7 @@ public partial class MongoRepository
|
|||
IsPublic = x.IsPublic,
|
||||
Disabled = x.Disabled,
|
||||
Type = x.Type,
|
||||
InheritAgentId = x.InheritAgentId,
|
||||
Profiles = x.Profiles,
|
||||
RoutingRules = !x.RoutingRules.IsNullOrEmpty() ? x.RoutingRules
|
||||
.Select(r => RoutingRuleMongoElement.ToDomainElement(x.Id, x.Name, r))
|
||||
|
|
@ -393,6 +408,7 @@ public partial class MongoRepository
|
|||
Samples = x.Samples ?? new List<string>(),
|
||||
IsPublic = x.IsPublic,
|
||||
Type = x.Type,
|
||||
InheritAgentId = x.InheritAgentId,
|
||||
Disabled = x.Disabled,
|
||||
Profiles = x.Profiles,
|
||||
RoutingRules = x.RoutingRules?
|
||||
|
|
|
|||
|
|
@ -263,4 +263,42 @@ public partial class MongoRepository
|
|||
UpdatedTime = c.UpdatedTime
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public bool TruncateConversation(string conversationId, string messageId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) return false;
|
||||
|
||||
var dialogFilter = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||
var foundDialog = _dc.ConversationDialogs.Find(dialogFilter).FirstOrDefault();
|
||||
if (foundDialog == null || foundDialog.Dialogs.IsNullOrEmpty()) return false;
|
||||
|
||||
var foundIdx = foundDialog.Dialogs.FindIndex(x => x.MetaData?.MessageId == messageId);
|
||||
if (foundIdx < 0) return false;
|
||||
|
||||
// Handle truncated dialogs
|
||||
var truncatedDialogs = foundDialog.Dialogs.Where((x, idx) => idx < foundIdx).ToList();
|
||||
|
||||
// Handle truncated states
|
||||
var refTime = foundDialog.Dialogs.ElementAt(foundIdx).MetaData.CreateTime;
|
||||
var stateFilter = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||
var foundStates = _dc.ConversationStates.Find(stateFilter).FirstOrDefault();
|
||||
if (foundStates == null || foundStates.States.IsNullOrEmpty()) return false;
|
||||
|
||||
var truncatedStates = new List<StateMongoElement>();
|
||||
foreach (var state in foundStates.States)
|
||||
{
|
||||
var values = state.Values.Where(x => x.UpdateTime < refTime).ToList();
|
||||
if (values.Count == 0) continue;
|
||||
|
||||
state.Values = values;
|
||||
truncatedStates.Add(state);
|
||||
}
|
||||
|
||||
// Save
|
||||
foundDialog.Dialogs = truncatedDialogs;
|
||||
foundStates.States = truncatedStates;
|
||||
_dc.ConversationDialogs.ReplaceOne(dialogFilter, foundDialog);
|
||||
_dc.ConversationStates.ReplaceOne(stateFilter, foundStates);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Repository
|
||||
{
|
||||
public partial class MongoRepository
|
||||
{
|
||||
#region Statistics
|
||||
public void IncrementConversationCount()
|
||||
{
|
||||
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -55,6 +55,7 @@ public partial class MongoRepository
|
|||
Samples = x.Samples ?? new List<string>(),
|
||||
IsPublic = x.IsPublic,
|
||||
Type = x.Type,
|
||||
InheritAgentId = x.InheritAgentId,
|
||||
Disabled = x.Disabled,
|
||||
Profiles = x.Profiles,
|
||||
RoutingRules = x.RoutingRules?
|
||||
|
|
@ -78,6 +79,7 @@ public partial class MongoRepository
|
|||
.Set(x => x.Samples, agent.Samples)
|
||||
.Set(x => x.IsPublic, agent.IsPublic)
|
||||
.Set(x => x.Type, agent.Type)
|
||||
.Set(x => x.InheritAgentId, agent.InheritAgentId)
|
||||
.Set(x => x.Disabled, agent.Disabled)
|
||||
.Set(x => x.Profiles, agent.Profiles)
|
||||
.Set(x => x.RoutingRules, agent.RoutingRules)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Playwright" Version="1.41.1" />
|
||||
<PackageReference Include="Microsoft.Playwright" Version="1.41.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ public class PlaywrightInstance : IDisposable
|
|||
{
|
||||
_playwright = await Playwright.CreateAsync();
|
||||
|
||||
/*_browser = await _playwright.Chromium.LaunchPersistentContextAsync(@"C:\Users\haipi\AppData\Local\Google\Chrome\User Data", new BrowserTypeLaunchPersistentContextOptions
|
||||
{
|
||||
Headless = false,
|
||||
Channel = "chrome",
|
||||
});*/
|
||||
_browser = await _playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
|
||||
{
|
||||
Headless = false,
|
||||
|
|
|
|||
|
|
@ -57,23 +57,11 @@ public partial class PlaywrightWebDriver
|
|||
}
|
||||
|
||||
var driverService = _services.GetRequiredService<WebDriverService>();
|
||||
var htmlElementContextOut = await driverService.LocateElement(agent, string.Join("", str), context.ElementName, messageId);
|
||||
|
||||
if (htmlElementContextOut.Index < 0)
|
||||
{
|
||||
throw new Exception($"Can't locate the web element {context.ElementName}.");
|
||||
}
|
||||
|
||||
ILocator element = default;
|
||||
if (!string.IsNullOrEmpty(htmlElementContextOut.ElementId))
|
||||
{
|
||||
// await _instance.Page.WaitForSelectorAsync($"#{htmlElementContextOut.ElementId}", new PageWaitForSelectorOptions { Timeout = 3 });
|
||||
element = _instance.Page.Locator($"#{htmlElementContextOut.ElementId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
element = _instance.Page.Locator(htmlElementContextOut.TagName).Nth(htmlElementContextOut.Index);
|
||||
}
|
||||
var htmlElementContextOut = await driverService.LocateElement(agent,
|
||||
string.Join("", str),
|
||||
context.ElementName,
|
||||
messageId);
|
||||
ILocator element = Locator(htmlElementContextOut);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
using BotSharp.Plugin.WebDriver.Services;
|
||||
|
||||
namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
|
||||
|
||||
public partial class PlaywrightWebDriver
|
||||
{
|
||||
public async Task ClickElement(Agent agent, BrowsingContextIn context, string messageId)
|
||||
{
|
||||
var driverService = _services.GetRequiredService<WebDriverService>();
|
||||
|
||||
// Retrieve the page raw html and infer the element path
|
||||
var body = await _instance.Page.QuerySelectorAsync("body");
|
||||
|
||||
|
|
@ -23,18 +23,19 @@ public partial class PlaywrightWebDriver
|
|||
var text = await btn.TextContentAsync();
|
||||
var name = await btn.GetAttributeAsync("name");
|
||||
var id = await btn.GetAttributeAsync("id");
|
||||
str.Add($"<button name='{name}' id='{id}'>{text}</button>");
|
||||
str.Add(driverService.AssembleMarkup("button", new MarkupProperties
|
||||
{
|
||||
Id = id,
|
||||
Name = name,
|
||||
Text = text
|
||||
}));
|
||||
}
|
||||
|
||||
var driverService = _services.GetRequiredService<WebDriverService>();
|
||||
var htmlElementContextOut = await driverService.LocateElement(agent, string.Join("", str), context.ElementName, messageId);
|
||||
|
||||
var tags = await _instance.Page.QuerySelectorAllAsync(htmlElementContextOut.TagName);
|
||||
var button = tags[htmlElementContextOut.Index];
|
||||
if (button.AsElement() == null)
|
||||
{
|
||||
throw new Exception($"Can't find web element {context.ElementName}");
|
||||
}
|
||||
await button.ClickAsync();
|
||||
var htmlElementContextOut = await driverService.LocateElement(agent,
|
||||
string.Join("", str),
|
||||
context.ElementName,
|
||||
messageId);
|
||||
ILocator element = Locator(htmlElementContextOut);
|
||||
await element.ClickAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
using BotSharp.Plugin.WebDriver.Services;
|
||||
|
||||
namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
|
||||
|
||||
public partial class PlaywrightWebDriver
|
||||
{
|
||||
public async Task InputUserText(Agent agent, BrowsingContextIn context, string messageId)
|
||||
{
|
||||
var driverService = _services.GetRequiredService<WebDriverService>();
|
||||
|
||||
// Retrieve the page raw html and infer the element path
|
||||
var body = await _instance.Page.QuerySelectorAsync("body");
|
||||
|
||||
|
|
@ -14,32 +14,47 @@ public partial class PlaywrightWebDriver
|
|||
foreach (var input in inputs)
|
||||
{
|
||||
var text = await input.TextContentAsync();
|
||||
var id = await input.GetAttributeAsync("id");
|
||||
var name = await input.GetAttributeAsync("name");
|
||||
var type = await input.GetAttributeAsync("type");
|
||||
str.Add($"<input name='{name}' type='{type}'>{text}</input>");
|
||||
str.Add(driverService.AssembleMarkup("input", new MarkupProperties
|
||||
{
|
||||
Id = id,
|
||||
Name = name,
|
||||
Type = type,
|
||||
Text = text
|
||||
}));
|
||||
}
|
||||
|
||||
inputs = await body.QuerySelectorAllAsync("textarea");
|
||||
foreach (var input in inputs)
|
||||
{
|
||||
var text = await input.TextContentAsync();
|
||||
var id = await input.GetAttributeAsync("id");
|
||||
var name = await input.GetAttributeAsync("name");
|
||||
var type = await input.GetAttributeAsync("type");
|
||||
str.Add($"<textarea name='{name}' type='{type}'>{text}</textarea>");
|
||||
str.Add(driverService.AssembleMarkup("textarea", new MarkupProperties
|
||||
{
|
||||
Id = id,
|
||||
Name = name,
|
||||
Type = type,
|
||||
Text = text
|
||||
}));
|
||||
}
|
||||
|
||||
var htmlElementContextOut = await driverService.LocateElement(agent,
|
||||
string.Join("", str),
|
||||
context.ElementName,
|
||||
messageId);
|
||||
ILocator element = Locator(htmlElementContextOut);
|
||||
|
||||
var driverService = _services.GetRequiredService<WebDriverService>();
|
||||
var htmlElementContextOut = await driverService.LocateElement(agent, string.Join("", str), context.ElementName, messageId);
|
||||
|
||||
if (htmlElementContextOut.Index < 0)
|
||||
{
|
||||
throw new Exception($"Can't locate the web element {context.ElementName}.");
|
||||
}
|
||||
|
||||
var element = _instance.Page.Locator(htmlElementContextOut.TagName).Nth(htmlElementContextOut.Index);
|
||||
try
|
||||
{
|
||||
await element.FillAsync(context.InputText);
|
||||
if (context.PressEnter)
|
||||
{
|
||||
await element.PressAsync("Enter");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,4 +11,24 @@ public partial class PlaywrightWebDriver
|
|||
_services = services;
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
private ILocator Locator(HtmlElementContextOut context)
|
||||
{
|
||||
ILocator element = default;
|
||||
if (!string.IsNullOrEmpty(context.ElementId))
|
||||
{
|
||||
// await _instance.Page.WaitForSelectorAsync($"#{htmlElementContextOut.ElementId}", new PageWaitForSelectorOptions { Timeout = 3 });
|
||||
element = _instance.Page.Locator($"#{context.ElementId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.Index < 0)
|
||||
{
|
||||
throw new Exception($"Can't locate the web element {context.Index}.");
|
||||
}
|
||||
element = _instance.Page.Locator(context.TagName).Nth(context.Index);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ public class BrowsingContextIn
|
|||
[JsonPropertyName("input_text")]
|
||||
public string? InputText { get; set; }
|
||||
|
||||
[JsonPropertyName("press_enter")]
|
||||
public bool PressEnter { get; set; }
|
||||
|
||||
[JsonPropertyName("update_value")]
|
||||
public string? UpdateValue { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
namespace BotSharp.Plugin.WebDriver.Models;
|
||||
|
||||
internal class MarkupProperties
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Text { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using BotSharp.Plugin.WebDriver.LlmContexts;
|
||||
|
||||
namespace BotSharp.Plugin.WebDriver.Services;
|
||||
|
||||
public partial class WebDriverService
|
||||
{
|
||||
internal string AssembleMarkup(string tagName, MarkupProperties properties)
|
||||
{
|
||||
var html = $"<{tagName}";
|
||||
if (!string.IsNullOrEmpty(properties.Id))
|
||||
{
|
||||
html += $" id=\"{properties.Id}\"";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(properties.Name))
|
||||
{
|
||||
html += $" name=\"{properties.Name}\"";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(properties.Type))
|
||||
{
|
||||
html += $" type=\"{properties.Type}\"";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(properties.Text))
|
||||
{
|
||||
html += $">{properties.Text}</{tagName}>";
|
||||
return html;
|
||||
}
|
||||
|
||||
return html + $"></{tagName}>";
|
||||
}
|
||||
}
|
||||
|
|
@ -14,4 +14,6 @@ global using BotSharp.Abstraction.Templating;
|
|||
global using BotSharp.Plugin.WebDriver.LlmContexts;
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using System.Linq;
|
||||
global using BotSharp.Abstraction.Utilities;
|
||||
global using BotSharp.Abstraction.Utilities;
|
||||
global using BotSharp.Plugin.WebDriver.Models;
|
||||
global using BotSharp.Plugin.WebDriver.Services;
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
{
|
||||
"id": "f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b",
|
||||
"name": "Web Driver",
|
||||
"description": "Perform a specific action on a web browser",
|
||||
"description": "Perform the web page related task in browser by using automation tools.",
|
||||
"type": "task",
|
||||
"createdDateTime": "2024-01-02T00:00:00Z",
|
||||
"updatedDateTime": "2024-01-02T00:00:00Z",
|
||||
"isPublic": true,
|
||||
"profiles": [ "web-driver" ],
|
||||
"llmConfig": {
|
||||
"max_recursion_depth": 10
|
||||
"max_recursion_depth": 5
|
||||
}
|
||||
}
|
||||
|
|
@ -51,10 +51,14 @@
|
|||
"type": "string",
|
||||
"description": "the html input box element name."
|
||||
},
|
||||
"input_text": {
|
||||
"type": "string",
|
||||
"description": "non-sensitive text user provided."
|
||||
}
|
||||
"input_text": {
|
||||
"type": "string",
|
||||
"description": "non-sensitive text user provided."
|
||||
},
|
||||
"press_enter": {
|
||||
"type": "boolean",
|
||||
"description": "whether to press ENTER"
|
||||
}
|
||||
},
|
||||
"required": ["element_name", "input_text"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\tests\BotSharp.Plugin.PizzaBot\BotSharp.Plugin.PizzaBot.csproj" />
|
||||
<ProjectReference Include="..\Plugins\BotSharp.Plugin.Dashboard\BotSharp.Plugin.Dashboard.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -82,7 +82,9 @@
|
|||
"EnableLlmCompletionLog": false,
|
||||
"EnableExecutionLog": true
|
||||
},
|
||||
|
||||
"Stats": {
|
||||
"DataDir": "stats"
|
||||
},
|
||||
"LlamaSharp": {
|
||||
"Interactive": true,
|
||||
"ModelDir": "C:/Users/haipi/Downloads",
|
||||
|
|
@ -161,8 +163,10 @@
|
|||
|
||||
"PluginLoader": {
|
||||
"Assemblies": [
|
||||
"BotSharp.Plugin.MongoStorage",
|
||||
"BotSharp.Core",
|
||||
"BotSharp.Logger",
|
||||
"BotSharp.Plugin.MongoStorage",
|
||||
"BotSharp.Plugin.Dashboard",
|
||||
"BotSharp.Plugin.AzureOpenAI",
|
||||
"BotSharp.Plugin.GoogleAI",
|
||||
"BotSharp.Plugin.MetaAI",
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="data\agents\8970b1e5-d260-4e2c-90b1-f1415a257c18\agent.json" />
|
||||
<None Remove="data\agents\8970b1e5-d260-4e2c-90b1-f1415a257c18\templates\task.place_pizza_order.liquid" />
|
||||
<None Remove="data\agents\b284db86-e9c2-4c25-a59e-4649797dd130\agent.json" />
|
||||
<None Remove="data\agents\b284db86-e9c2-4c25-a59e-4649797dd130\functions.json" />
|
||||
<None Remove="data\agents\b284db86-e9c2-4c25-a59e-4649797dd130\instruction.liquid" />
|
||||
|
|
@ -27,11 +29,6 @@
|
|||
<None Remove="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\functions.json" />
|
||||
<None Remove="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\instruction.liquid" />
|
||||
<None Remove="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\responses\func.get_pizza_price.0.liquid" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\agent.json" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\instruction.liquid" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.executor.liquid" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.reviewer.liquid" />
|
||||
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\task.place_pizza_order.liquid" />
|
||||
<None Remove="data\agents\fe8c60aa-b114-4ef3-93cb-a8efeac80f75\agent.json" />
|
||||
<None Remove="data\agents\fe8c60aa-b114-4ef3-93cb-a8efeac80f75\functions.json" />
|
||||
<None Remove="data\agents\fe8c60aa-b114-4ef3-93cb-a8efeac80f75\instruction.liquid" />
|
||||
|
|
@ -42,6 +39,12 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="data\agents\8970b1e5-d260-4e2c-90b1-f1415a257c18\agent.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\8970b1e5-d260-4e2c-90b1-f1415a257c18\templates\task.place_pizza_order.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\b284db86-e9c2-4c25-a59e-4649797dd130\agent.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
@ -63,21 +66,6 @@
|
|||
<Content Include="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\responses\func.get_pizza_price.0.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\agent.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\instruction.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.executor.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.reviewer.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\task.place_pizza_order.liquid">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="data\agents\fe8c60aa-b114-4ef3-93cb-a8efeac80f75\agent.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ public class PizzaBotPlugin : IBotSharpPlugin
|
|||
public string IconUrl => "https://cdn-icons-png.flaticon.com/512/6978/6978255.png";
|
||||
public string[] AgentIds => new[]
|
||||
{
|
||||
"8970b1e5-d260-4e2c-90b1-f1415a257c18",
|
||||
"b284db86-e9c2-4c25-a59e-4649797dd130",
|
||||
"c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd",
|
||||
"dfd9b46d-d00c-40af-8a75-3fbdc2b89869",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "8970b1e5-d260-4e2c-90b1-f1415a257c18",
|
||||
"name": "Pizza Bot",
|
||||
"description": "AI assistant that can help customer place pizza order.",
|
||||
"type": "routing",
|
||||
"createdDateTime": "2023-08-18T10:39:32.2349685Z",
|
||||
"updatedDateTime": "2023-08-18T14:39:32.2349686Z",
|
||||
"iconUrl": "https://cdn-icons-png.flaticon.com/512/6978/6978255.png",
|
||||
"disabled": true,
|
||||
"isPublic": true,
|
||||
"profiles": [ "pizza" ],
|
||||
"routingRules": [
|
||||
{
|
||||
"type": "planner",
|
||||
"field": "NaviePlanner"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
"createdDateTime": "2023-08-18T14:39:32.2349685Z",
|
||||
"updatedDateTime": "2023-08-18T14:39:32.2349686Z",
|
||||
"id": "b284db86-e9c2-4c25-a59e-4649797dd130",
|
||||
"allowRouting": true,
|
||||
"disabled": true,
|
||||
"isPublic": true,
|
||||
"profiles": [ "pizza" ],
|
||||
"routingRules": [
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"createdDateTime": "2023-07-26T02:29:25.123224Z",
|
||||
"updatedDateTime": "2023-07-26T02:29:25.123274Z",
|
||||
"id": "c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd",
|
||||
"allowRouting": true,
|
||||
"disabled": true,
|
||||
"isPublic": true,
|
||||
"profiles": [ "pizza" ]
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"name": "EvaluationAgent",
|
||||
"description": "Evaluate the performance of the LLM agents",
|
||||
"createdDateTime": "2023-08-18T00:00:00Z",
|
||||
"updatedDateTime": "2023-08-18T00:00:00Z",
|
||||
"id": "dfd9b46d-d00c-40af-8a75-3fbdc2b89869",
|
||||
"llmConfig": {
|
||||
"model": "gpt-35-turbo-instruct"
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,9 @@
|
|||
"createdDateTime": "2023-07-26T02:29:25.123224Z",
|
||||
"updatedDateTime": "2023-07-26T02:29:25.123274Z",
|
||||
"id": "fe8c60aa-b114-4ef3-93cb-a8efeac80f75",
|
||||
"allowRouting": true,
|
||||
"disabled": true,
|
||||
"isPublic": true,
|
||||
"profiles": [ "pizza" ],
|
||||
"routingRules": [
|
||||
{
|
||||
"field": "order_number",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.2.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="xunit" Version="2.6.6" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
|
||||
|
|
|
|||
Loading…
Reference in a new issue