diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs
index b0276e15..26d9ecef 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs
@@ -6,6 +6,7 @@ namespace BotSharp.Abstraction.Functions.Models;
public class FunctionCallFromLlm : RoutingArgs
{
[JsonPropertyName("question")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Question { get; set; }
[JsonPropertyName("args")]
@@ -29,6 +30,7 @@ public class FunctionCallFromLlm : RoutingArgs
/// Conversation summary
///
[JsonPropertyName("summary")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Summary { get; set; }
public override string ToString()
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHook.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHook.cs
index 9fb25a63..c60bf4f1 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHook.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHook.cs
@@ -10,13 +10,21 @@ public interface IRoutingHook
/// routing instruction
/// message
///
- Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message);
+ Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message)
+ => Task.CompletedTask;
- Task OnAgentEnqueued(string agentId, string preAgentId, string? reason = null);
+ Task OnRoutingInstructionRevised(FunctionCallFromLlm instruct, RoleDialogModel message)
+ => Task.CompletedTask;
- Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null);
+ Task OnAgentEnqueued(string agentId, string preAgentId, string? reason = null)
+ => Task.CompletedTask;
- Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null);
+ Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null)
+ => Task.CompletedTask;
- Task OnAgentQueueEmptied(string agentId, string? reason = null);
+ Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null)
+ => Task.CompletedTask;
+
+ Task OnAgentQueueEmptied(string agentId, string? reason = null)
+ => Task.CompletedTask;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs
index bca8018e..ebeac256 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs
@@ -8,13 +8,15 @@ public class RoutingArgs
///
/// The reason why you select this function or agent
///
- [JsonPropertyName("reason")]
+ [JsonPropertyName("next_action_reason")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Reason { get; set; } = string.Empty;
///
/// The content of replying to user
///
[JsonPropertyName("response")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Response { get; set; }
///
@@ -31,6 +33,10 @@ public class RoutingArgs
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string OriginalAgent { get; set; }
+ [JsonPropertyName("user_goal_description")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string UserGoal { get; set; }
+
public override string ToString()
{
var route = string.IsNullOrEmpty(AgentName) ? "" : $"";
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs
index bca532f0..476611d9 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs
@@ -58,15 +58,8 @@ public class RouteToAgentFn : IFunctionCallback
// Push next action agent
if (!string.IsNullOrEmpty(args.AgentName) && args.AgentName.Length < 32)
{
- var db = _services.GetRequiredService();
- var filter = new AgentFilter { AgentName = args.AgentName };
- var actionAgent = db.GetAgents(filter).FirstOrDefault();
- if (actionAgent != null)
- {
- _context.Push(actionAgent.Id, args.Reason);
- }
-
- states.SetState("last_action_agent", args.AgentName, isNeedVersion: true);
+ _context.Push(args.AgentName, args.Reason);
+ states.SetState("next_action_agent", args.AgentName, isNeedVersion: true);
}
if (string.IsNullOrEmpty(args.AgentName))
@@ -93,7 +86,7 @@ public class RouteToAgentFn : IFunctionCallback
if (missingfield && message.CurrentAgentId != agentId)
{
// Stack redirection agent
- _context.Push(agentId, reason: $"redirection: {message.Content}");
+ _context.Push(agentId, reason: $"REDIRECTION {message.Content}");
}
}
@@ -155,7 +148,7 @@ public class RouteToAgentFn : IFunctionCallback
{
// Add field to args
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "missing_fields", missingFields);
- message.Content = $"missing some information: {string.Join(',', missingFields)}";
+ message.Content = $"missing some information: {string.Join(", ", missingFields)}";
// Handle redirect
var routingRule = routingRules.FirstOrDefault(x => missingFields.Contains(x.Field));
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs
index ae440748..b21250db 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs
@@ -10,7 +10,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
public List Parameters => new List
{
- new ParameterPropertyDef("reason", "why route to agent")
+ new ParameterPropertyDef("next_action_reason", "the reason why route to this agent")
{
Required = true
},
@@ -18,6 +18,10 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
{
Required = true
},
+ new ParameterPropertyDef("user_goal_description", "user original goal")
+ {
+ Required = true
+ },
new ParameterPropertyDef("user_goal_agent", "agent who can achieve user original goal")
{
Required = true
@@ -38,6 +42,16 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
message.FunctionArgs = JsonSerializer.Serialize(inst);
var ret = await routing.InvokeFunction(message.FunctionName, message);
+ var states = _services.GetRequiredService();
+ var goalAgent = states.GetState("user_goal_agent");
+ if (!string.IsNullOrEmpty(goalAgent))
+ {
+ inst.OriginalAgent = goalAgent;
+ }
+ await HookEmitter.Emit(_services, async hook =>
+ await hook.OnRoutingInstructionRevised(inst, message)
+ );
+
var agentId = routing.Context.GetCurrentAgentId();
// Update next action agent's name
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs
index 37277b3c..bca5105c 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs
@@ -102,6 +102,7 @@ public class NaivePlanner : IPlaner
else
{
context.Empty(reason: $"Agent queue is cleared by {nameof(NaivePlanner)}");
+ // context.Push(inst.OriginalAgent, "Push user goal agent");
}
return true;
}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
index db8900b4..96cd95e6 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
@@ -61,8 +61,23 @@ public class RoutingContext : IRoutingContext
return _stack.Peek();
}
+ ///
+ /// Push agent
+ ///
+ /// Id or Name
+ ///
public void Push(string agentId, string? reason = null)
{
+ // Convert id to name
+ if (!Guid.TryParse(agentId, out _))
+ {
+ var agentService = _services.GetRequiredService();
+ agentId = agentService.GetAgents(new AgentFilter
+ {
+ AgentName = agentId
+ }).Result.Items.First().Id;
+ }
+
if (_stack.Count == 0 || _stack.Peek() != agentId)
{
var preAgentId = _stack.Count == 0 ? agentId : _stack.Peek();
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
index 303f6dd3..3a166e2b 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
@@ -41,7 +41,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
AllowTrailingCommas = true,
- WriteIndented = true
+ WriteIndented = true,
};
}
@@ -66,6 +66,11 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
public override async Task OnFunctionExecuted(RoleDialogModel message)
{
+ if (message.FunctionName == "route_to_agent")
+ {
+ return;
+ }
+
var conversationId = _state.GetConversationId();
var agent = await _agentService.LoadAgent(message.CurrentAgentId);
message.FunctionArgs = message.FunctionArgs ?? "{}";
@@ -128,6 +133,11 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
log += $"\r\n```json\r\n{richContent}\r\n```";
}
+ if (!string.IsNullOrEmpty(message.FunctionName))
+ {
+ log += $"\r\n\r\n**{message.FunctionName}**";
+ }
+
var input = new ContentLogInputModel(conv.ConversationId, message)
{
Name = agent?.Name,
@@ -223,7 +233,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
}
- public async Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message)
+ public async Task OnRoutingInstructionRevised(FunctionCallFromLlm instruct, RoleDialogModel message)
{
var conversationId = _state.GetConversationId();
var agent = await _agentService.LoadAgent(message.CurrentAgentId);