Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@
<OutputPath>$(SolutionDir)packages</OutputPath>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Infrastructures\**" />
<EmbeddedResource Remove="Infrastructures\**" />
<None Remove="Infrastructures\**" />
</ItemGroup>

<ItemGroup>
<None Include="..\..\..\arts\Icon.png">
<Pack>True</Pack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,4 @@ public virtual Task OnConversationInitialized(Conversation conversation)

public virtual Task OnUserAgentConnectedInitially(Conversation conversation)
=> Task.CompletedTask;

public virtual Task OnConversationRedirected(string toAgentId, RoleDialogModel message)
=> Task.CompletedTask;

public virtual Task OnConversationRouting(FunctionCallFromLlm instruct, RoleDialogModel message)
=> Task.CompletedTask;
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,4 @@ public interface IConversationHook
/// <param name="conversation"></param>
/// <returns></returns>
Task OnHumanInterventionNeeded(RoleDialogModel message);

/// <summary>
/// Conversation is redirected to another agent
/// </summary>
/// <param name="toAgentId"></param>
/// <param name="message"></param>
/// <returns></returns>
Task OnConversationRedirected(string toAgentId, RoleDialogModel message);

/// <summary>
/// Routing instruction is received from Router
/// </summary>
/// <param name="instruct">routing instruction</param>
/// <param name="message">message</param>
/// <returns></returns>
Task OnConversationRouting(FunctionCallFromLlm instruct, RoleDialogModel message);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace BotSharp.Abstraction.Infrastructures;

public class HookEmittedResult
{
}
13 changes: 13 additions & 0 deletions src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace BotSharp.Abstraction.Routing;

public interface IRoutingContext
{
string GetCurrentAgentId();
string OriginAgentId { get; }
bool IsEmpty { get; }
string IntentName { get; set; }
void Push(string agentId);
void Pop();
void Replace(string agentId);
void Empty();
}
30 changes: 30 additions & 0 deletions src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHook.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using BotSharp.Abstraction.Functions.Models;

namespace BotSharp.Abstraction.Routing;

public interface IRoutingHook
{
/// <summary>
/// Conversation is redirected to another agent
/// </summary>
/// <param name="toAgentId"></param>
/// <param name="message"></param>
/// <returns></returns>
Task OnConversationRedirected(string toAgentId, RoleDialogModel message);

/// <summary>
/// Routing instruction is received from Router
/// </summary>
/// <param name="instruct">routing instruction</param>
/// <param name="message">message</param>
/// <returns></returns>
Task OnConversationRouting(FunctionCallFromLlm instruct, RoleDialogModel message);

Task OnAgentEnqueued(string agentId, string preAgentId);

Task OnAgentDequeued(string agentId, string currentAgentId);

Task OnAgentReplaced(string fromAgentId, string toAgentId);

Task OnAgentQueueEmptied(string agentId);
}
27 changes: 27 additions & 0 deletions src/Infrastructure/BotSharp.Core/Infrastructures/HookEmitter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using BotSharp.Abstraction.Infrastructures;

namespace BotSharp.Core.Infrastructures;

public static class HookEmitter
{
public static async Task<HookEmittedResult> Emit<T>(IServiceProvider services, Action<T> action)
{
var logger = services.GetRequiredService<ILogger<T>>();
var result = new HookEmittedResult();
var hooks = services.GetServices<T>();

foreach (var hook in hooks)
{
try
{
action(hook);
}
catch (Exception ex)
{
logger.LogError(ex.ToString());
}
}

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public async Task<bool> Execute(RoleDialogModel message)
return false;
}

var routing = _services.GetRequiredService<RoutingContext>();
var routing = _services.GetRequiredService<IRoutingContext>();
routing.Replace(targetAgent.Id);

var router = _services.GetRequiredService<IRoutingService>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public class RouteToAgentFn : IFunctionCallback
{
public string Name => "route_to_agent";
private readonly IServiceProvider _services;
private readonly RoutingContext _context;
private readonly IRoutingContext _context;

public RouteToAgentFn(IServiceProvider services, RoutingContext context)
public RouteToAgentFn(IServiceProvider services, IRoutingContext context)
{
_services = services;
_context = context;
Expand Down Expand Up @@ -153,11 +153,9 @@ private bool HasMissingRequiredField(RoleDialogModel message, out string agentId
#else
logger.LogInformation($"*** Routing redirect to {record.Name.ToUpper()} ***");
#endif
var hooks = _services.GetServices<IConversationHook>();
foreach (var hook in hooks)
{
hook.OnConversationRedirected(routingRule.RedirectTo, message).Wait();
}
HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnConversationRedirected(routingRule.RedirectTo, message)
).Wait();
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public RetrieveDataFromAgentRoutingHandler(IServiceProvider services, ILogger<Re

public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
{
var context = _services.GetRequiredService<RoutingContext>();
var context = _services.GetRequiredService<IRoutingContext>();
var agentId = context.GetCurrentAgentId();
var dialogs = new List<RoleDialogModel>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,18 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler

public List<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef>
{
new ParameterPropertyDef("reason", "why route to agent"),
new ParameterPropertyDef("next_action_agent", "agent for next action based on user latest response"),
new ParameterPropertyDef("user_goal_agent", "agent who can achieve user original goal"),
new ParameterPropertyDef("reason", "why route to agent")
{
Required = true
},
new ParameterPropertyDef("next_action_agent", "agent for next action based on user latest response")
{
Required = true
},
new ParameterPropertyDef("user_goal_agent", "agent who can achieve user original goal")
{
Required = true
},
new ParameterPropertyDef("args", "useful parameters of next action agent, format: { }")
{
Type = "object"
Expand All @@ -30,7 +39,7 @@ public RouteToAgentRoutingHandler(IServiceProvider services, ILogger<RouteToAgen

public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
{
var context = _services.GetRequiredService<RoutingContext>();
var context = _services.GetRequiredService<IRoutingContext>();
var function = _services.GetServices<IFunctionCallback>().FirstOrDefault(x => x.Name == inst.Function);
message.FunctionArgs = JsonSerializer.Serialize(inst);
var ret = await function.Execute(message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Templating;

Expand Down Expand Up @@ -75,7 +75,7 @@ public async Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, R
var filter = new AgentFilter { AgentName = inst.AgentName };
var agent = db.GetAgents(filter).FirstOrDefault();

var context = _services.GetRequiredService<RoutingContext>();
var context = _services.GetRequiredService<IRoutingContext>();
context.Push(agent.Id);
}

Expand All @@ -84,7 +84,7 @@ public async Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, R

public async Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var context = _services.GetRequiredService<RoutingContext>();
var context = _services.GetRequiredService<IRoutingContext>();
context.Empty();
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public async Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, R

public async Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var context = _services.GetRequiredService<RoutingContext>();
var context = _services.GetRequiredService<IRoutingContext>();
if (inst.UnmatchedAgent)
{
var unmatchedAgentId = context.GetCurrentAgentId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public async Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, R

public async Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var context = _services.GetRequiredService<RoutingContext>();
var context = _services.GetRequiredService<IRoutingContext>();

if (message.StopCompletion)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public async Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, R

public async Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var context = _services.GetRequiredService<RoutingContext>();
var context = _services.GetRequiredService<IRoutingContext>();

if (message.StopCompletion || _isTaskCompleted)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing.Settings;
using Microsoft.Extensions.DependencyInjection;

namespace BotSharp.Abstraction.Routing.Models;
namespace BotSharp.Core.Routing;

public class RoutingContext
public class RoutingContext : IRoutingContext
{
private readonly IServiceProvider _services;
private readonly RoutingSettings _setting;
Expand Down Expand Up @@ -56,7 +54,12 @@ public void Push(string agentId)
{
if (_stack.Count == 0 || _stack.Peek() != agentId)
{
var preAgentId = _stack.Count == 0 ? agentId : _stack.Peek();
_stack.Push(agentId);

HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnAgentEnqueued(agentId, preAgentId)
).Wait();
}
}

Expand All @@ -65,24 +68,50 @@ public void Push(string agentId)
/// </summary>
public void Pop()
{
_stack.Pop();
if (_stack.Count == 0)
{
return;
}

var agentId = _stack.Pop();

HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnAgentDequeued(agentId, _stack.Peek())
).Wait();
}

public void Replace(string agentId)
{
var fromAgent = agentId;
var toAgent = agentId;

if (_stack.Count == 0)
{
_stack.Push(agentId);
}
else if (_stack.Peek() != agentId)
{
fromAgent = _stack.Peek();
_stack.Pop();
_stack.Push(agentId);

HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnAgentReplaced(fromAgent, toAgent)
).Wait();
}
}

public void Empty()
{
if (_stack.Count == 0)
{
return;
}

var agentId = GetCurrentAgentId();
_stack.Clear();
HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnAgentQueueEmptied(agentId)
).Wait();
}
}
2 changes: 1 addition & 1 deletion src/Infrastructure/BotSharp.Core/Routing/RoutingPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public object GetNewSettingsInstance() =>

public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddScoped<RoutingContext>();
services.AddScoped<IRoutingContext, RoutingContext>();

// Register router
services.AddScoped(provider =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private async Task<bool> InvokeFunction(RoleDialogModel message, List<RoleDialog
// Pass execution result to LLM to get response
if (!message.StopCompletion)
{
var routing = _services.GetRequiredService<RoutingContext>();
var routing = _services.GetRequiredService<IRoutingContext>();

// Find response template
var templateService = _services.GetRequiredService<IResponseTemplateService>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public async Task<bool> InvokeFunction(string name, RoleDialogModel message)
var originalFunctionName = message.FunctionName;
message.FunctionName = name;
message.Role = AgentRole.Function;
message.FunctionArgs = message.FunctionArgs;
var result = await function.Execute(message);

// restore original function name
Expand Down
10 changes: 4 additions & 6 deletions src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message)
var conv = _services.GetRequiredService<IConversationService>();
var dialogs = conv.GetDialogHistory();

var context = _services.GetRequiredService<RoutingContext>();
var context = _services.GetRequiredService<IRoutingContext>();
var executor = _services.GetRequiredService<IExecutor>();

var planner = GetPlanner(_router);
Expand All @@ -98,11 +98,9 @@ public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message)
// Get instruction from Planner
var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);

var hooks = _services.GetServices<IConversationHook>();
foreach (var hook in hooks)
{
await hook.OnConversationRouting(inst, message);
}
await HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnConversationRouting(inst, message)
);

// Save states
states.SaveStateByArgs(inst.Arguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public async Task<string> RenderIntentResponse(string agentId, RoleDialogModel m
// .ToList();

var db = _services.GetRequiredService<IBotSharpRepository>();
var context = _services.GetRequiredService<RoutingContext>();
var responses = db.GetAgentResponses(agentId, "intent", context.IntentName);
var routing = _services.GetRequiredService<IRoutingContext>();
var responses = db.GetAgentResponses(agentId, "intent", routing.IntentName);

if (responses.Count == 0)
{
Expand Down
Loading