Merge pull request #275 from hchen2020/master

Allow agent to inherit from other agent.
This commit is contained in:
Haiping 2024-01-29 12:18:23 -06:00 committed by GitHub
commit 8aabe3b7ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 160 additions and 77 deletions

View file

@ -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
@ -80,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>();

View file

@ -0,0 +1,11 @@
namespace BotSharp.Abstraction.Plugins;
public class PluginDependencyAttribute : Attribute
{
public string[] PluginNames { get; set; }
public PluginDependencyAttribute(params string[] pluginNames)
{
PluginNames = pluginNames;
}
}

View file

@ -26,7 +26,7 @@ public class RoutingRule
public override string ToString()
{
return $"{AgentName} {Field}";
return $"{Type} {AgentName} {Field}";
}
public RoutingRule()

View file

@ -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);

View file

@ -1,6 +1,6 @@
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Abstraction.Planning;
namespace BotSharp.Abstraction.Routing.Planning;
/// <summary>
/// Task breakdown and execution plan

View file

@ -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

View file

@ -50,11 +50,11 @@
<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" />
@ -87,7 +87,7 @@
<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">

View file

@ -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;

View file

@ -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;
});

View file

@ -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>();

View file

@ -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

View file

@ -181,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;

View 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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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
{

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -1,7 +1,7 @@
{
"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",

View file

@ -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?

View file

@ -53,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);

View file

@ -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)
{

View file

@ -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>();

View file

@ -1,19 +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": [ "default" ],
"profiles": [ "web-driver" ],
"llmConfig": {
"max_recursion_depth": 10
},
"routingRules": [
{
"type": "fallback",
"redirectTo": "01e2fc5c-2c89-4ec7-8470-7688608b496c"
}
]
"max_recursion_depth": 5
}
}

View file

@ -163,8 +163,9 @@
"PluginLoader": {
"Assemblies": [
"BotSharp.Plugin.MongoStorage",
"BotSharp.Core",
"BotSharp.Logger",
"BotSharp.Plugin.MongoStorage",
"BotSharp.Plugin.Dashboard",
"BotSharp.Plugin.AzureOpenAI",
"BotSharp.Plugin.GoogleAI",