Build-in routing prompt.

This commit is contained in:
Haiping Chen 2023-09-20 05:31:50 -05:00
commit a7b6c4083f
57 changed files with 573 additions and 148 deletions

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<LangVersion>10.0</LangVersion>
<OutputPath>..\..\..\packages</OutputPath>
<PackageVersion>0.13.0</PackageVersion>
<BotSharpVersion>0.13.0</BotSharpVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
</Project>

View file

@ -6,6 +6,10 @@ public enum AgentField
Name,
Description,
IsPublic,
Disabled,
AllowRouting,
Profiles,
RoutingRules,
Instruction,
Function,
Template,

View file

@ -6,7 +6,7 @@ public class Agent
{
public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Description { get; set; }
public string Description { get; set; } = string.Empty;
public DateTime CreatedDateTime { get; set; }
public DateTime UpdatedDateTime { get; set; }
@ -81,6 +81,10 @@ public class Agent
Samples = agent.Samples,
Knowledges = agent.Knowledges,
IsPublic = agent.IsPublic,
Disabled = agent.Disabled,
AllowRouting = agent.AllowRouting,
Profiles = agent.Profiles,
RoutingRules = agent.RoutingRules,
CreatedDateTime = agent.CreatedDateTime,
UpdatedDateTime = agent.UpdatedDateTime,
};
@ -94,7 +98,7 @@ public class Agent
public Agent SetTemplates(List<AgentTemplate> templates)
{
Templates = templates;
Templates = templates ?? new List<AgentTemplate>();
return this;
}
@ -133,4 +137,28 @@ public class Agent
IsPublic = isPublic;
return this;
}
public Agent SetDisabled(bool disabled)
{
Disabled = disabled;
return this;
}
public Agent SetAllowRouting(bool allowRouting)
{
AllowRouting = allowRouting;
return this;
}
public Agent SetProfiles(List<string> profiles)
{
Profiles = profiles ?? new List<string>();
return this;
}
public Agent SetRoutingRules(List<RoutingRule> rules)
{
RoutingRules = rules ?? new List<RoutingRule>();
return this;
}
}

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<PackageIcon>Icon.png</PackageIcon>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -14,6 +14,6 @@ public class FunctionCallFromLlm
public override string ToString()
{
return $"{Function}: {Parameters}";
return $"{Function} {Parameters}";
}
}

View file

@ -2,11 +2,23 @@ namespace BotSharp.Abstraction.MLTasks;
public interface IChatCompletion
{
/// <summary>
/// The LLM provider like Microsoft Azure, OpenAI, ClaudAI
/// </summary>
string Provider { get; }
/// <summary>
/// Set model name, one provider can consume different model or version(s)
/// </summary>
/// <param name="model"></param>
void SetModelName(string model);
Task<bool> GetChatCompletionsAsync(Agent agent,
List<RoleDialogModel> conversations,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting);
Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived);
Task<bool> GetChatCompletionsStreamingAsync(Agent agent,
List<RoleDialogModel> conversations,
Func<RoleDialogModel, Task> onMessageReceived);
}

View file

@ -2,6 +2,7 @@ namespace BotSharp.Abstraction.Routing;
public interface IRoutingService
{
Agent LoadRouter();
List<RoleDialogModel> Dialogs { get; }
Task<RoleDialogModel> Enter(Agent agent, List<RoleDialogModel> whileDialogs);
}

View file

@ -19,6 +19,6 @@ public class RetrievalArgs : RoutingArgs
public override string ToString()
{
return $"{AgentName} {Question} ({JsonSerializer.Serialize(Arguments)}) => {Answer} ({Reason})";
return $" [{AgentName}]: {Question} ({JsonSerializer.Serialize(Arguments)}) => {Answer} ({Reason})";
}
}

View file

@ -18,4 +18,9 @@ public class RoutingRule
{
return $"{AgentName} {Field}";
}
public RoutingRule()
{
}
}

View file

@ -7,8 +7,5 @@ public class RoutingSettings
/// </summary>
public string RouterId { get; set; } = string.Empty;
/// <summary>
/// Reasoner Agent Id
/// </summary>
public string ReasonerId { get; set; } = string.Empty;
public bool EnableReasoning { get; set; } = false;
}

View file

@ -35,6 +35,10 @@ public partial class AgentService
.SetName(foundAgent.Name)
.SetDescription(foundAgent.Description)
.SetIsPublic(foundAgent.IsPublic)
.SetDisabled(foundAgent.Disabled)
.SetAllowRouting(foundAgent.AllowRouting)
.SetProfiles(foundAgent.Profiles)
.SetRoutingRules(foundAgent.RoutingRules)
.SetInstruction(foundAgent.Instruction)
.SetTemplates(foundAgent.Templates)
.SetFunctions(foundAgent.Functions)

View file

@ -1,4 +1,6 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Agents.Services;
@ -18,7 +20,9 @@ public partial class AgentService
hook.OnAgentLoading(ref id);
}
var agent = await GetAgent(id);
var settings = _services.GetRequiredService<RoutingSettings>();
var routingService = _services.GetRequiredService<IRoutingService>();
var agent = settings.RouterId == id ? routingService.LoadRouter() : await GetAgent(id);
var templateDict = new Dictionary<string, object>();
PopulateState(templateDict);

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing.Models;
using System.IO;
namespace BotSharp.Core.Agents.Services;
@ -15,6 +16,11 @@ public partial class AgentService
record.Name = agent.Name ?? string.Empty;
record.Description = agent.Description ?? string.Empty;
record.IsPublic = agent.IsPublic;
record.Disabled = agent.Disabled;
record.AllowRouting = agent.AllowRouting;
record.Profiles = agent.Profiles ?? new List<string>();
record.RoutingRules = agent.RoutingRules ?? new List<RoutingRule>();
record.Instruction = agent.Instruction ?? string.Empty;
record.Functions = agent.Functions ?? new List<string>();
record.Templates = agent.Templates ?? new List<AgentTemplate>();
@ -53,6 +59,10 @@ public partial class AgentService
.SetName(foundAgent.Name)
.SetDescription(foundAgent.Description)
.SetIsPublic(foundAgent.IsPublic)
.SetDisabled(foundAgent.Disabled)
.SetAllowRouting(foundAgent.AllowRouting)
.SetProfiles(foundAgent.Profiles)
.SetRoutingRules(foundAgent.RoutingRules)
.SetInstruction(foundAgent.Instruction)
.SetTemplates(foundAgent.Templates)
.SetFunctions(foundAgent.Functions)

View file

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>
@ -74,6 +74,20 @@
<Compile Remove="CoreController.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="Routing\Prompts\reasoning_functions.liquid" />
<None Remove="Routing\router_prompt.liquid" />
</ItemGroup>
<ItemGroup>
<Content Include="Routing\Prompts\reasoning_functions.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Routing\Prompts\router_prompt.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\arts\Icon.png">
<Pack>True</Pack>

View file

@ -51,16 +51,11 @@ public static class BotSharpServiceCollectionExtensions
config.Bind("Router", routingSettings);
services.AddSingleton((IServiceProvider x) => routingSettings);
services.AddScoped<Router>();
services.AddScoped<Reasoner>();
services.AddScoped<IAgentRouting, Router>();
// Register function callback
services.AddScoped<IFunctionCallback, RouteToAgentFn>();
// Register Hooks
services.AddScoped<IAgentHook, RoutingHook>();
services.AddScoped<Simulator>();
services.AddScoped<IRoutingService, RoutingService>();

View file

@ -50,7 +50,7 @@ public partial class ConversationService
}
}
// reasoning
// Routing with reasoning
var settings = _services.GetRequiredService<RoutingSettings>();
if (settings.RouterId == agent.Id)
{
@ -82,6 +82,13 @@ public partial class ConversationService
agent = await agentService.LoadAgent(reasonedContext.CurrentAgentId);
}
}
else if (reasonedContext.FunctionName == "route_to_agent")
{
if (reasonedContext.CurrentAgentId != agent.Id)
{
agent = await agentService.LoadAgent(reasonedContext.CurrentAgentId);
}
}
routing.Dialogs.ForEach(x =>
{

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing.Settings;
using System.IO;
namespace BotSharp.Core.Conversations.Services;
@ -44,9 +45,10 @@ public class ConversationStorage : IConversationStorage
}
else
{
var agent = db.Agents.First(x => x.Id == agentId);
var routingSetting = _services.GetRequiredService<RoutingSettings>();
var agentName = routingSetting.RouterId == agentId ? "Router" : db.Agents.First(x => x.Id == agentId).Name;
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{agent.Name}|");
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{agentName}|");
var content = dialog.Content.Replace("\r", " ").Replace("\n", " ").Trim();
if (string.IsNullOrEmpty(content))
{

View file

@ -4,16 +4,31 @@ namespace BotSharp.Core.Infrastructures;
public class CompletionProvider
{
public static IChatCompletion GetChatCompletion(IServiceProvider services, string? provider = null)
public static IChatCompletion GetChatCompletion(IServiceProvider services, string? provider = null, string? model = null)
{
var completions = services.GetServices<IChatCompletion>();
var state = services.GetRequiredService<IConversationStateService>();
if (provider == null)
{
provider = state.GetState("provider", "azure-gpt-3.5");
provider = state.GetState("provider", "azure-openai");
}
return completions.FirstOrDefault(x => x.Provider == provider);
if (model == null)
{
model = state.GetState("model", "gpt-3.5-turbo");
}
var completer = completions.FirstOrDefault(x => x.Provider == provider);
if (completer == null)
{
var logger = services.GetRequiredService<ILogger<CompletionProvider>>();
logger.LogError($"Can't resolve completion provider by {provider}");
}
completer.SetModelName(model);
return completer;
}
}

View file

@ -4,6 +4,7 @@ using FunctionDef = BotSharp.Abstraction.Functions.Models.FunctionDef;
using BotSharp.Abstraction.Users.Models;
using BotSharp.Abstraction.Agents.Models;
using MongoDB.Driver;
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.Core.Repository;
@ -240,6 +241,18 @@ public class FileRepository : IBotSharpRepository
case AgentField.IsPublic:
UpdateAgentIsPublic(agent.Id, agent.IsPublic);
break;
case AgentField.Disabled:
UpdateAgentDisabled(agent.Id, agent.Disabled);
break;
case AgentField.AllowRouting:
UpdateAgentAllowRouting(agent.Id, agent.AllowRouting);
break;
case AgentField.Profiles:
UpdateAgentProfiles(agent.Id, agent.Profiles);
break;
case AgentField.RoutingRules:
UpdateAgentRoutingRules(agent.Id, agent.RoutingRules);
break;
case AgentField.Instruction:
UpdateAgentInstruction(agent.Id, agent.Instruction);
break;
@ -298,6 +311,54 @@ public class FileRepository : IBotSharpRepository
File.WriteAllText(agentFile, json);
}
private void UpdateAgentDisabled(string agentId, bool disabled)
{
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
agent.Disabled = disabled;
agent.UpdatedDateTime = DateTime.UtcNow;
var json = JsonSerializer.Serialize(agent, _options);
File.WriteAllText(agentFile, json);
}
private void UpdateAgentAllowRouting(string agentId, bool allowRouting)
{
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
agent.AllowRouting = allowRouting;
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;
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
agent.Profiles = profiles;
agent.UpdatedDateTime = DateTime.UtcNow;
var json = JsonSerializer.Serialize(agent, _options);
File.WriteAllText(agentFile, json);
}
private void UpdateAgentRoutingRules(string agentId, List<RoutingRule> rules)
{
if (rules.IsNullOrEmpty()) return;
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
agent.RoutingRules = rules;
agent.UpdatedDateTime = DateTime.UtcNow;
var json = JsonSerializer.Serialize(agent, _options);
File.WriteAllText(agentFile, json);
}
private void UpdateAgentInstruction(string agentId, string instruction)
{
if (string.IsNullOrEmpty(instruction)) return;
@ -396,6 +457,10 @@ public class FileRepository : IBotSharpRepository
agent.Name = inputAgent.Name;
agent.Description = inputAgent.Description;
agent.IsPublic = inputAgent.IsPublic;
agent.Disabled = inputAgent.Disabled;
agent.AllowRouting = inputAgent.AllowRouting;
agent.Profiles = inputAgent.Profiles;
agent.RoutingRules = inputAgent.RoutingRules;
agent.UpdatedDateTime = DateTime.UtcNow;
var json = JsonSerializer.Serialize(agent, _options);
File.WriteAllText(agentFile, json);

View file

@ -0,0 +1,22 @@
# retrieve_data_from_agent
Retrieve data from appropriate agent.
Parameters:
1. agent_name: the name of the agent;
2. question: the question you will ask the agent to get the necessary data
3. args: required parameters extracted from question and hand over to the next agent. The args should be in JSON format.
# continue_execute_task
Continue to execute user's request without further information retrival.
Parameters:
1. agent_name: the name of the agent;
2. args: required parameters extracted from question.
# interrupt_task_execution
Can't continue user's request becauase the requirements are not met or you have already known the answer.
Parameters:
1. reason: the reason why the request is interrupted;
# response_to_user
You have already known the answer according the dialogs.
Parameters:
1. answer: the answer of user's question;

View file

@ -0,0 +1,31 @@
You're a Agent Router with reasoning, you can dispatch request to different agent to complete the task.
{% for agent in routing_records %}
# Agent: {{ agent.name }}
{{ agent.description }}
{% if agent.required_fields != empty -%}Required information: {{ agent.required_fields }}.{%- endif %}
{% endfor %}
### Function instructions
# route_to_agent
Route request to appropriate agent.
Parameters:
1. agent_name: the name of the agent;
# task_end
Call this function when current task is completed.
Parameters:
1. abandoned_arguments: the arguments next task can't reuse;
# conversation_end
Call this function when user wants to end this conversation or all tasks have been completed.
# transfer_to_csr
Reach out to a real customer representative to help.
{{ reasoning_functions }}
### Your response must meet below requirements strictly
* If you can find an appropriate Agent, you must call appropriate function with required arguments.
### Below are the dialogs between user and different agents:

View file

@ -1,14 +0,0 @@
using BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Core.Routing;
public class Reasoner : Router
{
public override string AgentId => _settings.ReasonerId;
public Reasoner(IServiceProvider services,
ILogger<Reasoner> logger,
RoutingSettings settings) : base(services, logger, settings)
{
}
}

View file

@ -1,30 +0,0 @@
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.Core.Routing;
public class RoutingHook : AgentHookBase
{
public RoutingHook(IServiceProvider services, AgentSettings settings)
: base(services, settings)
{
}
public override bool OnInstructionLoaded(string template, Dictionary<string, object> dict)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agents = db.Agents.Where(x => !x.Disabled && x.AllowRouting).ToArray();
var router = _services.GetRequiredService<IAgentRouting>();
dict["routing_records"] = agents.Select(x => new RoutingItem
{
AgentId = x.Id,
Description = x.Description,
Name = x.Name,
RequiredFields = x.RoutingRules.Where(x => x.Required)
.Select(x => x.Field)
.ToArray()
}).ToArray();
return true;
}
}

View file

@ -4,19 +4,26 @@ using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Templating;
using System.IO;
namespace BotSharp.Core.Routing;
public class RoutingService : IRoutingService
{
private readonly IServiceProvider _services;
private readonly RoutingSettings _settings;
private readonly ILogger _logger;
private List<RoleDialogModel> _dialogs;
public List<RoleDialogModel> Dialogs => _dialogs;
public RoutingService(IServiceProvider services, ILogger<RoutingService> logger)
public RoutingService(IServiceProvider services,
RoutingSettings settings,
ILogger<RoutingService> logger)
{
_services = services;
_settings = settings;
_logger = logger;
}
@ -90,7 +97,9 @@ public class RoutingService : IRoutingService
new RoleDialogModel(AgentRole.User, inst.Parameters.Question)
});
response.Content += $"\r\nDo you want to continue current task?";
inst.Parameters.Answer = response.Content;
response.Content += $"\r\nDo you want to continue current task?";
_dialogs.Add(new RoleDialogModel(AgentRole.Function, $"{record.Name}: {response.Content}")
{
FunctionName = inst.Function,
@ -111,9 +120,10 @@ public class RoutingService : IRoutingService
private async Task<FunctionCallFromLlm> GetNextInstructionFromReasoner(Agent reasoner)
{
var responseFormat = "{\"function\": \"\", \"parameters\": {\"agent_name\": \"\", \"args\":{}}";
var wholeDialogs = new List<RoleDialogModel>
{
new RoleDialogModel(AgentRole.User, @"What's the next step? Response in JSON format with ""function"" and ""parameters"".")
new RoleDialogModel(AgentRole.User, $"What's the next step? Response in JSON format {responseFormat}.")
};
var chatCompletion = CompletionProvider.GetChatCompletion(_services);
@ -132,6 +142,8 @@ public class RoutingService : IRoutingService
args.Function = args.Function.Split('.').Last();
_logger.LogInformation($"Next Instruction: {args}");
return args;
}
@ -180,4 +192,39 @@ public class RoutingService : IRoutingService
}
}
}
public Agent LoadRouter()
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var router = new Agent()
{
Id = _settings.RouterId,
};
var agents = db.Agents.Where(x => !x.Disabled && x.AllowRouting).ToArray();
var dict = new Dictionary<string, object>();
dict["routing_records"] = agents.Select(x => new RoutingItem
{
AgentId = x.Id,
Description = x.Description,
Name = x.Name,
RequiredFields = x.RoutingRules.Where(x => x.Required)
.Select(x => x.Field)
.ToArray()
}).ToArray();
var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Routing", "Prompts");
var template = File.ReadAllText(Path.Combine(dir, "router_prompt.liquid"));
if (_settings.EnableReasoning)
{
dict["reasoning_functions"] = File.ReadAllText(Path.Combine(dir, "reasoning_functions.liquid"));
}
var render = _services.GetRequiredService<ITemplateRender>();
router.Instruction = render.Render(template, dict);
return router;
}
}

View file

@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -66,6 +66,38 @@ public class AgentController : ControllerBase, IApiAdapter
await _agentService.UpdateAgent(model, AgentField.IsPublic);
}
[HttpPut("/agent/{agentId}/disabled")]
public async Task UpdateAgentDisabled([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.Disabled);
}
[HttpPut("/agent/{agentId}/allow-routing")]
public async Task UpdateAgentAllowRouting([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.AllowRouting);
}
[HttpPut("/agent/{agentId}/profiles")]
public async Task UpdateAgentProfiles([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.Profiles);
}
[HttpPut("/agent/{agentId}/routing-rules")]
public async Task UpdateAgentRoutingRules([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.RoutingRules);
}
[HttpPut("/agent/{agentId}/instruction")]
public async Task UpdateAgentInstruction([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
@ -83,7 +115,7 @@ public class AgentController : ControllerBase, IApiAdapter
}
[HttpPut("/agent/{agentId}/templates")]
public async Task UpdateAgenttemplates([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
public async Task UpdateAgentTemplates([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;

View file

@ -20,7 +20,7 @@ public class InstructModeController : ControllerBase, IApiAdapter
}
[HttpPost("/instruct/{agentId}")]
public async Task<InstructResult> NewConversation([FromRoute] string agentId,
public async Task<InstructResult> InstructCompletion([FromRoute] string agentId,
[FromBody] InstructMessageModel input)
{
var instructor = _services.GetRequiredService<IInstructService>();
@ -28,13 +28,17 @@ public class InstructModeController : ControllerBase, IApiAdapter
Agent agent = await agentService.LoadAgent(agentId);
// switch to different instruction template
if (!string.IsNullOrEmpty(input.TemplateName))
if (!string.IsNullOrEmpty(input.Template))
{
var agentSettings = _services.GetRequiredService<AgentSettings>();
var filePath = Path.Combine(agentService.GetAgentDataDir(agentId), $"{input.TemplateName}.{agentSettings.TemplateFormat}");
var filePath = Path.Combine(agentService.GetAgentDataDir(agentId), $"{input.Template}.{agentSettings.TemplateFormat}");
agent.Instruction = System.IO.File.ReadAllText(filePath);
}
var conv = _services.GetRequiredService<IConversationService>();
conv.States.SetState("provider", input.Provider)
.SetState("model", input.Model);
return await instructor.ExecuteInstruction(agent,
new RoleDialogModel(AgentRole.User, input.Text),
fn => Task.CompletedTask,

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.OpenAPI.ViewModels.Agents;
@ -11,6 +12,10 @@ public class AgentCreationModel
public List<string> Functions { get; set; }
public List<AgentResponse> Responses { get; set; }
public bool IsPublic { get; set; }
public bool AllowRouting { get; set; }
public bool Disabled { get; set; }
public List<string> Profiles { get; set; }
public List<RoutingRuleUpdateModel> RoutingRules { get; set; }
public Agent ToAgent()
{
@ -22,7 +27,13 @@ public class AgentCreationModel
Templates = Templates,
Functions = Functions,
Responses = Responses,
IsPublic = IsPublic
IsPublic = IsPublic,
AllowRouting = AllowRouting,
Disabled = Disabled,
Profiles = Profiles,
RoutingRules = RoutingRules?
.Select(x => RoutingRuleUpdateModel.ToDomainElement(x))?
.ToList() ?? new List<RoutingRule>()
};
}
}

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.OpenAPI.ViewModels.Agents;
@ -32,12 +33,32 @@ public class AgentUpdateModel
/// </summary>
public List<AgentResponse>? Responses { get; set; }
public bool IsPublic { get; set; }
public bool AllowRouting { get; set; }
public bool Disabled { get; set; }
/// <summary>
/// Profile by channel
/// </summary>
public List<string>? Profiles { get; set; }
public List<RoutingRuleUpdateModel>? RoutingRules { get; set; }
public Agent ToAgent()
{
var agent = new Agent()
{
Name = Name ?? string.Empty,
Description = Description ?? string.Empty,
IsPublic = IsPublic,
Disabled = Disabled,
AllowRouting = AllowRouting,
Profiles = Profiles ?? new List<string>(),
RoutingRules = RoutingRules?
.Select(x => RoutingRuleUpdateModel.ToDomainElement(x))?
.ToList() ?? new List<RoutingRule>(),
Instruction = Instruction ?? string.Empty,
Templates = Templates ?? new List<AgentTemplate>(),
Functions = Functions ?? new List<string>(),

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.OpenAPI.ViewModels.Agents;
@ -12,6 +13,11 @@ public class AgentViewModel
public List<string> Functions { get; set; }
public List<AgentResponse> Responses { get; set; }
public bool IsPublic { get; set; }
public bool AllowRouting { get; set; }
public bool Disabled { get; set; }
public List<string> Profiles { get; set; }
public List<RoutingRule> RoutingRules { get; set; }
public DateTime CreatedDateTime { get; set; }
public DateTime UpdatedDateTime { get; set; }
@ -27,6 +33,10 @@ public class AgentViewModel
Functions = agent.Functions,
Responses = agent.Responses,
IsPublic= agent.IsPublic,
Disabled = agent.Disabled,
AllowRouting = agent.AllowRouting,
Profiles = agent.Profiles,
RoutingRules = agent.RoutingRules,
CreatedDateTime = agent.CreatedDateTime,
UpdatedDateTime = agent.UpdatedDateTime
};

View file

@ -0,0 +1,25 @@
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.OpenAPI.ViewModels.Agents;
public class RoutingRuleUpdateModel
{
public string Field { get; set; }
public bool Required { get; set; }
public string? RedirectTo { get; set; }
public RoutingRuleUpdateModel()
{
}
public static RoutingRule ToDomainElement(RoutingRuleUpdateModel model)
{
return new RoutingRule
{
Field = model.Field,
Required = model.Required,
RedirectTo = model.RedirectTo
};
}
}

View file

@ -4,5 +4,5 @@ namespace BotSharp.OpenAPI.ViewModels.Instructs;
public class InstructMessageModel : IncomingMessageModel
{
public override string Channel { get; set; } = "openapi";
public string? TemplateName { get; set; }
public string? Template { get; set; }
}

View file

@ -23,6 +23,5 @@ public class AzureOpenAiPlugin : IBotSharpPlugin
services.AddScoped<ITextCompletion, TextCompletionProvider>();
services.AddScoped<IChatCompletion, ChatCompletionProvider>();
services.AddScoped<IChatCompletion, GPT4CompletionProvider>();
}
}

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -20,11 +20,12 @@ namespace BotSharp.Plugin.AzureOpenAI.Providers;
public class ChatCompletionProvider : IChatCompletion
{
protected readonly AzureOpenAiSettings _settings;
protected readonly IServiceProvider _services;
protected readonly ILogger _logger;
private readonly AzureOpenAiSettings _settings;
private readonly IServiceProvider _services;
private readonly ILogger _logger;
private string _model;
public virtual string Provider => "azure-gpt-3.5";
public virtual string Provider => "azure-openai";
public ChatCompletionProvider(AzureOpenAiSettings settings,
ILogger<ChatCompletionProvider> logger,
@ -37,8 +38,16 @@ public class ChatCompletionProvider : IChatCompletion
protected virtual (OpenAIClient, string) GetClient()
{
var client = new OpenAIClient(new Uri(_settings.Endpoint), new AzureKeyCredential(_settings.ApiKey));
return (client, _settings.DeploymentModel.ChatCompletionModel);
if (_model == "gpt-4")
{
var client = new OpenAIClient(new Uri(_settings.GPT4.Endpoint), new AzureKeyCredential(_settings.GPT4.ApiKey));
return (client, _settings.GPT4.DeploymentModel);
}
else
{
var client = new OpenAIClient(new Uri(_settings.Endpoint), new AzureKeyCredential(_settings.ApiKey));
return (client, _settings.DeploymentModel.ChatCompletionModel);
}
}
public List<RoleDialogModel> GetChatSamples(string sampleText)
@ -243,4 +252,9 @@ public class ChatCompletionProvider : IChatCompletion
return chatCompletionsOptions;
}
public void SetModelName(string model)
{
_model = model;
}
}

View file

@ -1,24 +0,0 @@
using Azure;
using Azure.AI.OpenAI;
using BotSharp.Plugin.AzureOpenAI.Settings;
using Microsoft.Extensions.Logging;
using System;
namespace BotSharp.Plugin.AzureOpenAI.Providers;
public class GPT4CompletionProvider : ChatCompletionProvider
{
public override string Provider => "azure-gpt-4";
public GPT4CompletionProvider(AzureOpenAiSettings settings,
ILogger<GPT4CompletionProvider> logger,
IServiceProvider services) : base(settings, logger, services)
{
}
protected override (OpenAIClient, string) GetClient()
{
var client = new OpenAIClient(new Uri(_settings.GPT4.Endpoint), new AzureKeyCredential(_settings.GPT4.ApiKey));
return (client, _settings.GPT4.DeploymentModel);
}
}

View file

@ -1,4 +1,4 @@
namespace BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Plugin.AzureOpenAI.Settings;
public class GPT4Settings
{

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -13,6 +13,7 @@ public class ChatCompletionProvider : IChatCompletion
private readonly IServiceProvider _services;
private readonly HuggingFaceSettings _settings;
private readonly ILogger _logger;
private string _model;
public ChatCompletionProvider(IServiceProvider services,
HuggingFaceSettings settings,
@ -69,4 +70,9 @@ public class ChatCompletionProvider : IChatCompletion
{
return true;
}
public void SetModelName(string model)
{
_model = model;
}
}

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -23,6 +23,7 @@ public class ChatCompletionProvider : IChatCompletion
private readonly IServiceProvider _services;
private readonly ILogger _logger;
private readonly LlamaSharpSettings _settings;
private string _model;
public ChatCompletionProvider(IServiceProvider services,
ILogger<ChatCompletionProvider> logger,
@ -118,4 +119,9 @@ public class ChatCompletionProvider : IChatCompletion
return true;
}
public void SetModelName(string model)
{
_model = model;
}
}

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Plugin.MongoStorage.Models;
namespace BotSharp.Plugin.MongoStorage.Collections;
@ -11,6 +12,10 @@ public class AgentCollection : MongoBase
public List<string> Functions { get; set; }
public List<AgentResponse> Responses { get; set; }
public bool IsPublic { get; set; }
public bool AllowRouting { get; set; }
public bool Disabled { get; set; }
public List<string> Profiles { get; set; }
public List<RoutingRuleMongoElement> RoutingRules { get; set; }
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }

View file

@ -1,11 +0,0 @@
namespace BotSharp.Plugin.MongoStorage.Collections;
public class RoutingItemCollection : MongoBase
{
public Guid AgentId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<string> RequiredFields { get; set; }
public Guid? RedirectTo { get; set; }
public bool Disabled { get; set; }
}

View file

@ -1,7 +0,0 @@
namespace BotSharp.Plugin.MongoStorage.Collections;
public class RoutingProfileCollection : MongoBase
{
public string Name { get; set; }
public List<Guid> AgentIds { get; set; }
}

View file

@ -0,0 +1,37 @@
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.Plugin.MongoStorage.Models;
public class RoutingRuleMongoElement
{
public string Field { get; set; }
public bool Required { get; set; }
public Guid? RedirectTo { get; set; }
public RoutingRuleMongoElement()
{
}
public static RoutingRuleMongoElement ToMongoElement(RoutingRule routingRule)
{
return new RoutingRuleMongoElement
{
Field = routingRule.Field,
Required = routingRule.Required,
RedirectTo = !string.IsNullOrEmpty(routingRule.RedirectTo) ? Guid.Parse(routingRule.RedirectTo) : null
};
}
public static RoutingRule ToDomainElement(string agentId, string agentName, RoutingRuleMongoElement rule)
{
return new RoutingRule
{
AgentId = agentId,
AgentName = agentName,
Field = rule.Field,
Required = rule.Required,
RedirectTo = rule.RedirectTo?.ToString()
};
}
}

View file

@ -42,10 +42,4 @@ public class MongoDbContext
public IMongoCollection<UserAgentCollection> UserAgents
=> Database.GetCollection<UserAgentCollection>($"{_collectionPrefix}_UserAgents");
public IMongoCollection<RoutingItemCollection> RoutingItems
=> Database.GetCollection<RoutingItemCollection>($"{_collectionPrefix}_RoutingItems");
public IMongoCollection<RoutingProfileCollection> RoutingProfiles
=> Database.GetCollection<RoutingProfileCollection>($"{_collectionPrefix}_RoutingProfiles");
}

View file

@ -1,7 +1,9 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Users.Models;
using BotSharp.Plugin.MongoStorage.Collections;
using BotSharp.Plugin.MongoStorage.Models;
namespace BotSharp.Plugin.MongoStorage.Repository;
@ -42,6 +44,12 @@ public class MongoRepository : IBotSharpRepository
Functions = x.Functions,
Responses = x.Responses,
IsPublic = x.IsPublic,
Disabled = x.Disabled,
AllowRouting = x.AllowRouting,
Profiles = x.Profiles,
RoutingRules = x.RoutingRules?
.Select(r => RoutingRuleMongoElement.ToDomainElement(x.Id.ToString(), x.Name, r))?
.ToList() ?? new List<RoutingRule>(),
CreatedDateTime = x.CreatedTime,
UpdatedDateTime = x.UpdatedTime
}).ToList();
@ -206,6 +214,12 @@ public class MongoRepository : IBotSharpRepository
Functions = x.Functions,
Responses = x.Responses,
IsPublic = x.IsPublic,
AllowRouting = x.AllowRouting,
Disabled = x.Disabled,
Profiles = x.Profiles,
RoutingRules = x.RoutingRules?
.Select(r => RoutingRuleMongoElement.ToMongoElement(r))?
.ToList() ?? new List<RoutingRuleMongoElement>(),
CreatedTime = x.CreatedDateTime,
UpdatedTime = x.UpdatedDateTime
}).ToList();
@ -221,6 +235,10 @@ public class MongoRepository : IBotSharpRepository
.Set(x => x.Functions, agent.Functions)
.Set(x => x.Responses, agent.Responses)
.Set(x => x.IsPublic, agent.IsPublic)
.Set(x => x.AllowRouting, agent.AllowRouting)
.Set(x => x.Disabled, agent.Disabled)
.Set(x => x.Profiles, agent.Profiles)
.Set(x => x.RoutingRules, agent.RoutingRules)
.Set(x => x.CreatedTime, agent.CreatedTime)
.Set(x => x.UpdatedTime, agent.UpdatedTime);
_dc.Agents.UpdateOne(filter, update, _options);
@ -299,6 +317,18 @@ public class MongoRepository : IBotSharpRepository
case AgentField.IsPublic:
UpdateAgentIsPublic(agent.Id, agent.IsPublic);
break;
case AgentField.Disabled:
UpdateAgentDisabled(agent.Id, agent.Disabled);
break;
case AgentField.AllowRouting:
UpdateAgentAllowRouting(agent.Id, agent.AllowRouting);
break;
case AgentField.Profiles:
UpdateAgentProfiles(agent.Id, agent.Profiles);
break;
case AgentField.RoutingRules:
UpdateAgentRoutingRules(agent.Id, agent.RoutingRules);
break;
case AgentField.Instruction:
UpdateAgentInstruction(agent.Id, agent.Instruction);
break;
@ -354,6 +384,51 @@ public class MongoRepository : IBotSharpRepository
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentDisabled(string agentId, bool disabled)
{
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.Disabled, disabled)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentAllowRouting(string agentId, bool allowRouting)
{
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.AllowRouting, allowRouting)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentProfiles(string agentId, List<string> profiles)
{
if (profiles.IsNullOrEmpty()) return;
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.Profiles, profiles)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentRoutingRules(string agentId, List<RoutingRule> rules)
{
if (rules.IsNullOrEmpty()) return;
var ruleElements = rules.Select(x => RoutingRuleMongoElement.ToMongoElement(x)).ToList();
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.RoutingRules, ruleElements)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentInstruction(string agentId, string instruction)
{
if (string.IsNullOrEmpty(instruction)) return;
@ -408,6 +483,10 @@ public class MongoRepository : IBotSharpRepository
var update = Builders<AgentCollection>.Update
.Set(x => x.Name, agent.Name)
.Set(x => x.Description, agent.Description)
.Set(x => x.Disabled, agent.Disabled)
.Set(x => x.AllowRouting, agent.AllowRouting)
.Set(x => x.Profiles, agent.Profiles)
.Set(x => x.RoutingRules, agent.RoutingRules.Select(x => RoutingRuleMongoElement.ToMongoElement(x)).ToList())
.Set(x => x.Instruction, agent.Instruction)
.Set(x => x.Templates, agent.Templates)
.Set(x => x.Functions, agent.Functions)

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>

View file

@ -53,7 +53,7 @@ public class RoutingConversationHook: ConversationHookBase
public override async Task AfterCompletion(RoleDialogModel message)
{
var routerSettings = _services.GetRequiredService<RoutingSettings>();
bool saveFlag = (message.CurrentAgentId != routerSettings.RouterId) && (message.CurrentAgentId != routerSettings.ReasonerId);
bool saveFlag = message.CurrentAgentId != routerSettings.RouterId;
if (saveFlag)
{

View file

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
</PropertyGroup>
@ -14,7 +14,6 @@
<PackageProjectUrl>https://github.com/Oceania2018/botsharp-channel-weixin</PackageProjectUrl>
<Copyright>Apache 2.0</Copyright>
<PackageTags>botsharp, wechat, wexin, chatbot</PackageTags>
<VersionPrefix>$(PackageVersion)</VersionPrefix>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

View file

@ -9,6 +9,12 @@ public class GetPizzaTypesFn : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message)
{
message.ExecutionResult = "Pepperoni Pizza, Cheese Pizza, Margherita Pizza";
message.ExecutionData = new List<string>
{
"Pepperoni Pizza",
"Cheese Pizza",
"Margherita Pizza"
};
return true;
}
}