diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
index c764f391..b722d3fa 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
@@ -69,21 +69,6 @@ public interface IConversationHook
Task OnResponseGenerated(RoleDialogModel message);
- ///
- /// LLM detected user requested a new task different from previous topic.
- ///
- ///
- ///
- Task OnNewTaskDetected(RoleDialogModel message, string reason);
-
- ///
- /// LLM detected the current task is completed.
- /// It's useful for the situation of multiple tasks in the same conversation.
- ///
- ///
- ///
- Task OnTaskCompleted(RoleDialogModel message);
-
///
/// LLM detected the whole conversation is going to be end.
///
diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs
index 661a1437..0bb248f4 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs
@@ -36,13 +36,6 @@ public class FunctionCallFromLlm : RoutingArgs
{
var route = string.IsNullOrEmpty(AgentName) ? "" : $"";
- if (string.IsNullOrEmpty(Response))
- {
- return $"[{Function} {route} {JsonSerializer.Serialize(Arguments)}]: {Question}";
- }
- else
- {
- return $"[{Function} {route} {JsonSerializer.Serialize(Arguments)}]: {Question} => {Response}";
- }
+ return $"[{Function} {route} {JsonSerializer.Serialize(Arguments)}]: {Question}";
}
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs
deleted file mode 100644
index 95cddf2e..00000000
--- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using BotSharp.Abstraction.Functions.Models;
-
-namespace BotSharp.Abstraction.Routing;
-
-///
-/// The routing handler will be injected to Router's FUNCTIONS section of the system prompt
-/// So the handler will be invoked by LLM autonomously.
-///
-public interface IRoutingHandler
-{
- string Name { get; }
- string Description { get; }
- List Planers => null;
- bool Enabled => true;
- List Parameters => new List();
-
- void SetDialogs(List dialogs);
-
- Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message);
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs
index c0f7c81c..97be8874 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs
@@ -26,15 +26,13 @@ public interface IRoutingService
///
RoutingRule[] GetRulesByAgentId(string id);
- List GetHandlers(Agent router);
-
//void ResetRecursiveCounter();
//int GetRecursiveCounter();
//void SetRecursiveCounter(int counter);
Task InvokeAgent(string agentId, List dialogs);
Task InvokeFunction(string name, RoleDialogModel messages);
- Task InstructLoop(RoleDialogModel message, List dialogs);
+ Task InstructLoop(Agent agent, RoleDialogModel message, List dialogs);
///
/// Talk to a specific Agent directly, bypassing the Router
@@ -42,7 +40,7 @@ public interface IRoutingService
///
///
///
- Task InstructDirect(Agent agent, RoleDialogModel message);
+ Task InstructDirect(Agent agent, RoleDialogModel message, List dialogs);
Task GetConversationContent(List dialogs, int maxDialogCount = 100);
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/ResponseUserArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/ResponseUserArgs.cs
new file mode 100644
index 00000000..4a3a027b
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/ResponseUserArgs.cs
@@ -0,0 +1,7 @@
+namespace BotSharp.Abstraction.Routing.Models;
+
+public class ResponseUserArgs
+{
+ [JsonPropertyName("content")]
+ public string Content { get; set; } = string.Empty;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs
index bc9a6fa8..44d41f31 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs
@@ -15,19 +15,6 @@ public class RoutingArgs
[JsonPropertyName("conversation_end")]
public bool ConversationEnd { get; set; }
- [JsonPropertyName("task_completed")]
- public bool TaskCompleted { get; set; }
-
- [JsonPropertyName("is_new_task")]
- public bool IsNewTask { get; set; }
-
- ///
- /// The content of replying to user
- ///
- [JsonPropertyName("response")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string Response { get; set; } = string.Empty;
-
///
/// Agent for next action based on user latest response
///
@@ -38,10 +25,12 @@ public class RoutingArgs
///
/// Agent who can achieve user original goal
///
+ [Obsolete("Will be replaced by dedicate Reasoner")]
[JsonPropertyName("user_goal_agent")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string OriginalAgent { get; set; } = string.Empty;
+ [Obsolete("Will be replaced by dedicate Reasoner")]
[JsonPropertyName("user_goal_description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string UserGoal { get; set; } = string.Empty;
@@ -50,13 +39,6 @@ public class RoutingArgs
{
var route = string.IsNullOrEmpty(AgentName) ? "" : $"";
- if (string.IsNullOrEmpty(Response))
- {
- return $"[{Function} {route}]";
- }
- else
- {
- return $"[{Function} {route}] => {Response}";
- }
+ return $"[{Function} {route}]";
}
}
diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
index cd85e358..19b6df53 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
+++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
@@ -150,17 +150,6 @@ public static class BotSharpCoreExtensions
var loader = new PluginLoader(services, config, pluginSettings);
loader.Load(assembly =>
{
- // Register routing handlers
- var handlers = assembly.GetTypes()
- .Where(x => x.IsClass)
- .Where(x => x.GetInterface(nameof(IRoutingHandler)) != null)
- .ToArray();
-
- foreach (var handler in handlers)
- {
- services.AddScoped(typeof(IRoutingHandler), handler);
- }
-
// Register function callback
var functions = assembly.GetTypes()
.Where(x => x.IsClass
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
index 3967aaaa..5ec1ae76 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
@@ -38,17 +38,6 @@ public partial class ConversationService
var routing = _services.GetRequiredService();
routing.Context.SetMessageId(_conversationId, message.MessageId);
- // Check the routing mode
- var states = _services.GetRequiredService();
- var routingMode = states.GetState(StateConst.ROUTING_MODE, "hard");
- routing.Context.Push(agent.Id, reason: "request started", updateLazyRouting: false);
-
- if (routingMode == "lazy")
- {
- message.CurrentAgentId = states.GetState(StateConst.LAZY_ROUTING_AGENT_ID, message.CurrentAgentId);
- routing.Context.Push(message.CurrentAgentId, reason: "lazy routing", updateLazyRouting: false);
- }
-
// Save payload in order to assign the payload before hook is invoked
if (replyMessage != null && !string.IsNullOrEmpty(replyMessage.Payload))
{
@@ -91,11 +80,22 @@ public partial class ConversationService
if (agent.Type == AgentType.Routing)
{
- response = await routing.InstructLoop(message, dialogs);
+ // Check the routing mode
+ var states = _services.GetRequiredService();
+ var routingMode = states.GetState(StateConst.ROUTING_MODE, "eager");
+ routing.Context.Push(agent.Id, reason: "request started", updateLazyRouting: false);
+
+ if (routingMode == "lazy")
+ {
+ message.CurrentAgentId = states.GetState(StateConst.LAZY_ROUTING_AGENT_ID, message.CurrentAgentId);
+ routing.Context.Push(message.CurrentAgentId, reason: "lazy routing", updateLazyRouting: false);
+ }
+
+ response = await routing.InstructLoop(agent, message, dialogs);
}
else
{
- response = await routing.InstructDirect(agent, message);
+ response = await routing.InstructDirect(agent, message, dialogs);
}
routing.Context.ResetRecursiveCounter();
@@ -155,14 +155,6 @@ public partial class ConversationService
var conversation = _services.GetRequiredService();
var updatedConversation = await conversation.UpdateConversationTitle(_conversationId, response.Instruction.NextActionReason);
- // Emit conversation task completed hook
- if (response.Instruction.TaskCompleted)
- {
- await HookEmitter.Emit(_services, async hook =>
- await hook.OnTaskCompleted(response)
- );
- }
-
// Emit conversation ending hook
if (response.Instruction.ConversationEnd)
{
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/ResponseToUserFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/ResponseToUserFn.cs
index 0ee3e585..fe322f31 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Functions/ResponseToUserFn.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/ResponseToUserFn.cs
@@ -20,8 +20,8 @@ public class ResponseToUserFn : IFunctionCallback
public Task Execute(RoleDialogModel message)
{
- var args = JsonSerializer.Deserialize(message.FunctionArgs);
- message.Content = args.Response;
+ var args = JsonSerializer.Deserialize(message.FunctionArgs);
+ message.Content = args.Content;
message.Handled = true;
message.StopCompletion = true;
return Task.FromResult(true);
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs
deleted file mode 100644
index ea50ef72..00000000
--- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using BotSharp.Abstraction.Functions.Models;
-using BotSharp.Abstraction.Models;
-using BotSharp.Abstraction.Repositories;
-using BotSharp.Abstraction.Repositories.Filters;
-using BotSharp.Abstraction.Routing;
-using BotSharp.Abstraction.Routing.Settings;
-using BotSharp.Core.Routing.Reasoning;
-
-namespace BotSharp.Core.Routing.Handlers;
-
-public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase//, IRoutingHandler
-{
- public string Name => "continue_execute_task";
-
- public string Description => "Continue to execute user's request without further information retrival.";
-
- public List Parameters => new List
- {
- 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 continue to execute current task"),
- new ParameterPropertyDef("args", "required parameters extracted from question")
- {
- Type = "object"
- }
- };
-
- public List Planers => new List
- {
- nameof(HFReasoner)
- };
-
- public ContinueExecuteTaskRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings)
- : base(services, logger, settings)
- {
- }
-
- public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
- {
- var db = _services.GetRequiredService();
- var filter = new AgentFilter { AgentNames = [inst.AgentName] };
- var record = db.GetAgents(filter).FirstOrDefault();
-
- message.FunctionName = inst.Function;
- message.CurrentAgentId = record.Id;
- message.FunctionArgs = JsonSerializer.Serialize(inst.Arguments);
-
- return true;
- }
-}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs
deleted file mode 100644
index 0d11f296..00000000
--- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using BotSharp.Abstraction.Functions.Models;
-using BotSharp.Abstraction.Routing;
-using BotSharp.Abstraction.Routing.Settings;
-using BotSharp.Core.Routing.Reasoning;
-
-namespace BotSharp.Core.Routing.Handlers;
-
-public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase//, IRoutingHandler
-{
- public string Name => "interrupt_task_execution";
-
- public string Description => "Can't continue user's request becauase the requirements are not met.";
-
- public List Parameters => new List
- {
- new ParameterPropertyDef("reason", "the reason why the request is interrupted"),
- new ParameterPropertyDef("answer", "the content response to user")
- };
-
- public List Planers => new List
- {
- nameof(HFReasoner)
- };
-
- public InterruptTaskExecutionRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings)
- : base(services, logger, settings)
- {
- }
-
- public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
- {
- message.FunctionName = inst.Function;
- message.StopCompletion = true;
-
- return true;
- }
-}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs
deleted file mode 100644
index 038488eb..00000000
--- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using BotSharp.Abstraction.Routing.Settings;
-using BotSharp.Core.Routing.Reasoning;
-
-namespace BotSharp.Core.Routing.Handlers;
-
-///
-/// Retrieve information from specific agent
-///
-public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase//, IRoutingHandler
-{
- public string Name => "retrieve_data_from_agent";
-
- public string Description => "Retrieve data from appropriate agent.";
-
- public List Parameters => new List
- {
- new ParameterPropertyDef("reason", "why choose this function"),
- new ParameterPropertyDef("question", "the question you will ask the next action agent to get the necessary information"),
- new ParameterPropertyDef("next_action_agent", "agent that can handle the question"),
- new ParameterPropertyDef("user_goal_agent", "agent that can achieve user original goal"),
- new ParameterPropertyDef("args", "required parameters extracted from question and hand over to the next agent")
- {
- Type = "object"
- }
- };
-
- public List Planers => new List
- {
- nameof(HFReasoner)
- };
-
- public RetrieveDataFromAgentRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings)
- : base(services, logger, settings)
- {
- }
-
- public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
- {
- var context = _services.GetRequiredService();
- var agentId = context.GetCurrentAgentId();
- var dialogs = new List
- {
- new RoleDialogModel(AgentRole.User, inst.Question)
- {
- CurrentAgentId = agentId,
- MessageId = message.MessageId
- }
- };
-
- var ret = await routing.InvokeAgent(agentId, dialogs);
- var response = dialogs.Last();
- inst.Response = response.Content;
-
- // Add final response to parent dialog
- _dialogs.Add(response);
-
- return ret;
- }
-}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs
deleted file mode 100644
index 1dc808a0..00000000
--- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using BotSharp.Abstraction.Infrastructures.Enums;
-using BotSharp.Abstraction.Routing.Settings;
-
-namespace BotSharp.Core.Routing.Handlers;
-
-public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
-{
- public string Name => "route_to_agent";
-
- public string Description => "Route request to appropriate virtual agent.";
-
- public List Parameters => new List
- {
- new ParameterPropertyDef("next_action_reason",
- "the reason why route to this virtual agent.",
- required: true),
- new ParameterPropertyDef("next_action_agent",
- "agent for next action based on user latest response, if user is replying last agent's question, you must route to this agent.",
- required: true),
- new ParameterPropertyDef("args",
- "useful parameters of next action agent, format: { }",
- type: "object"),
- new ParameterPropertyDef("user_goal_description",
- "user goal based on user initial task.",
- required: true),
- new ParameterPropertyDef("user_goal_agent",
- "agent who can acheive user initial task.",
- required: true),
- new ParameterPropertyDef("conversation_end",
- "user is ending the conversation.",
- type: "boolean",
- required: true),
- new ParameterPropertyDef("is_new_task",
- "whether the user is requesting a new task that is different from the previous topic. Set the first round of conversation to false.",
- type: "boolean")
- };
-
- public RouteToAgentRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings)
- : base(services, logger, settings)
- {
- }
-
- public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
- {
- var states = _services.GetRequiredService();
- var goalAgent = states.GetState(StateConst.EXPECTED_GOAL_AGENT);
- if (!string.IsNullOrEmpty(goalAgent) && inst.OriginalAgent != goalAgent)
- {
- inst.OriginalAgent = goalAgent;
- // Emit hook
- await HookEmitter.Emit(_services, async hook =>
- await hook.OnRoutingInstructionRevised(inst, message)
- );
- }
-
- if (inst.IsNewTask)
- {
- await HookEmitter.Emit(_services, async hook =>
- await hook.OnNewTaskDetected(message, inst.NextActionReason)
- );
- }
-
- message.FunctionArgs = JsonSerializer.Serialize(inst);
- if (message.FunctionName != null)
- {
- var msg = RoleDialogModel.From(message, role: AgentRole.Function);
- await routing.InvokeFunction(message.FunctionName, msg);
- }
-
- var agentId = routing.Context.GetCurrentAgentId();
-
- // Update next action agent's name
- var agentService = _services.GetRequiredService();
- var agent = await agentService.GetAgent(agentId);
- inst.AgentName = agent.Name;
-
- if (inst.ExecutingDirectly)
- {
- message.Content = inst.Question;
- }
-
- if (agent.Disabled)
- {
- var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent.";
-
- message = RoleDialogModel.From(message, role: AgentRole.Assistant, content: content);
- _dialogs.Add(message);
- }
- else
- {
- var ret = await routing.InvokeAgent(agentId, _dialogs);
- }
-
- var response = _dialogs.Last();
- inst.Response = response.Content;
-
- return true;
- }
-}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs
index f83afedf..53a54669 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs
@@ -59,7 +59,6 @@ public class RoutingAgentHook : AgentHookBase
}
dict["routing_agents"] = agents;
- dict["routing_handlers"] = routing.GetHandlers(_agent);
return base.OnInstructionLoaded(template, dict);
}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs
index fc7c1009..55867e00 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/HFReasoner.cs
@@ -38,43 +38,21 @@ public class HFReasoner : IRoutingReasoner
{
var next = GetNextStepPrompt(router);
- RoleDialogModel response = default;
- var inst = new FunctionCallFromLlm();
-
var completion = CompletionProvider.GetChatCompletion(_services,
provider: router?.LlmConfig?.Provider,
model: router?.LlmConfig?.Model);
- int retryCount = 0;
- while (retryCount < 3)
+ dialogs = new List
{
- try
+ new RoleDialogModel(AgentRole.User, next)
{
- dialogs = new List
- {
- new RoleDialogModel(AgentRole.User, next)
- {
- FunctionName = nameof(HFReasoner),
- MessageId = messageId
- }
- };
- response = await completion.GetChatCompletions(router, dialogs);
+ FunctionName = nameof(HFReasoner),
+ MessageId = messageId
+ }
+ };
+ var response = await completion.GetChatCompletions(router, dialogs);
- inst = response.Content.JsonContent();
- break;
- }
- catch (Exception ex)
- {
- _logger.LogError($"{ex.Message}: {response.Content}");
- inst.Function = "response_to_user";
- inst.Response = ex.Message;
- inst.AgentName = "Router";
- }
- finally
- {
- retryCount++;
- }
- }
+ var inst = response.Content.JsonContent();
// Fix LLM malformed response
ReasonerHelper.FixMalformedResponse(_services, inst);
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs
index 6bb23e97..e911437d 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/InstructExecutor.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Planning;
namespace BotSharp.Core.Routing.Reasoning;
@@ -18,17 +19,50 @@ public class InstructExecutor : IExecutor
RoleDialogModel message,
List dialogs)
{
- message.Instruction = inst;
+ var states = _services.GetRequiredService();
+ var goalAgent = states.GetState(StateConst.EXPECTED_GOAL_AGENT);
+ if (!string.IsNullOrEmpty(goalAgent) && inst.OriginalAgent != goalAgent)
+ {
+ inst.OriginalAgent = goalAgent;
+ // Emit hook
+ await HookEmitter.Emit(_services, async hook =>
+ await hook.OnRoutingInstructionRevised(inst, message)
+ );
+ }
- var handlers = _services.GetServices();
- var handler = handlers.FirstOrDefault(x => x.Name == inst.Function);
- handler.SetDialogs(dialogs);
+ message.FunctionArgs = JsonSerializer.Serialize(inst);
+ if (message.FunctionName != null)
+ {
+ var msg = RoleDialogModel.From(message, role: AgentRole.Function);
+ await routing.InvokeFunction(message.FunctionName, msg);
+ }
- var handled = await handler.Handle(routing, inst, message);
+ var agentId = routing.Context.GetCurrentAgentId();
+
+ // Update next action agent's name
+ var agentService = _services.GetRequiredService();
+ var agent = await agentService.GetAgent(agentId);
+ inst.AgentName = agent.Name;
+
+ if (inst.ExecutingDirectly)
+ {
+ message.Content = inst.Question;
+ }
+
+ if (agent.Disabled)
+ {
+ var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent.";
+
+ message = RoleDialogModel.From(message, role: AgentRole.Assistant, content: content);
+ dialogs.Add(message);
+ }
+ else
+ {
+ var ret = await routing.InvokeAgent(agentId, dialogs);
+ }
- // For client display purpose
var response = dialogs.Last();
- response.MessageId = message.MessageId;
+
response.Instruction = inst;
return response;
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs
index 8980246e..6adc9bc5 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/NaiveReasoner.cs
@@ -41,53 +41,22 @@ public class NaiveReasoner : IRoutingReasoner
{
var next = GetNextStepPrompt(router);
- var inst = new FunctionCallFromLlm();
-
- // text completion
- /*var agentService = _services.GetRequiredService();
- var instruction = agentService.RenderedInstruction(router);
- var content = $"{instruction}\r\n###\r\n{next}";
- content = content + "\r\nResponse: ";
- var completion = CompletionProvider.GetTextCompletion(_services);*/
-
// chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: router?.LlmConfig?.Provider,
model: router?.LlmConfig?.Model);
- int retryCount = 0;
- while (retryCount < 3)
+ dialogs = new List
{
- string text = string.Empty;
- try
+ new RoleDialogModel(AgentRole.User, next)
{
- // text completion
- // text = await completion.GetCompletion(content, router.Id, messageId);
- dialogs = new List
- {
- new RoleDialogModel(AgentRole.User, next)
- {
- FunctionName = nameof(NaiveReasoner),
- MessageId = messageId
- }
- };
- var response = await completion.GetChatCompletions(router, dialogs);
+ FunctionName = nameof(NaiveReasoner),
+ MessageId = messageId
+ }
+ };
+ var response = await completion.GetChatCompletions(router, dialogs);
- inst = (response.FunctionArgs ?? response.Content).JsonContent();
- break;
- }
- catch (Exception ex)
- {
- _logger.LogError($"{ex.Message}: {text}");
- inst.Function = "response_to_user";
- inst.Response = ex.Message;
- inst.AgentName = "Router";
- }
- finally
- {
- retryCount++;
- }
- }
+ var inst = (response.FunctionArgs ?? response.Content).JsonContent();
// Fix LLM malformed response
ReasonerHelper.FixMalformedResponse(_services, inst);
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs
index 7e6d2624..800b2457 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/OneStepForwardReasoner.cs
@@ -44,46 +44,26 @@ public class OneStepForwardReasoner : IRoutingReasoner
{
var next = GetNextStepPrompt(router);
- var inst = new FunctionCallFromLlm();
-
// chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: router?.LlmConfig?.Provider,
model: router?.LlmConfig?.Model);
- int retryCount = 0;
- while (retryCount < 3)
- {
- string text = string.Empty;
- try
- {
- // text completion
- // text = await completion.GetCompletion(content, router.Id, messageId);
- dialogs = new List
- {
- new RoleDialogModel(AgentRole.User, next)
- {
- FunctionName = Name,
- MessageId = messageId
- }
- };
- var response = await completion.GetChatCompletions(router, dialogs);
+ string text = string.Empty;
- inst = response.Content.JsonContent();
- break;
- }
- catch (Exception ex)
+ // text completion
+ // text = await completion.GetCompletion(content, router.Id, messageId);
+ dialogs = new List
+ {
+ new RoleDialogModel(AgentRole.User, next)
{
- _logger.LogError($"{ex.Message}: {text}");
- inst.Function = "response_to_user";
- inst.Response = ex.Message;
- inst.AgentName = "Router";
+ FunctionName = Name,
+ MessageId = messageId
}
- finally
- {
- retryCount++;
- }
- }
+ };
+ var response = await completion.GetChatCompletions(router, dialogs);
+
+ var inst = response.Content.JsonContent();
// Fix LLM malformed response
ReasonerHelper.FixMalformedResponse(_services, inst);
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
index 4cba2e2d..1c4ca518 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
@@ -294,7 +294,7 @@ public class RoutingContext : IRoutingContext
// Set next handling agent for lazy routing mode
var states = _services.GetRequiredService();
- var routingMode = states.GetState(StateConst.ROUTING_MODE, "hard");
+ var routingMode = states.GetState(StateConst.ROUTING_MODE, "eager");
if (routingMode == "lazy")
{
var agentId = GetCurrentAgentId();
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs
index 07b2f6fa..364e7198 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs
@@ -7,7 +7,7 @@ namespace BotSharp.Core.Routing;
public partial class RoutingService
{
- public async Task InstructLoop(RoleDialogModel message, List dialogs)
+ public async Task InstructLoop(Agent agent, RoleDialogModel message, List dialogs)
{
RoleDialogModel response = default;
@@ -15,7 +15,7 @@ public partial class RoutingService
var convService = _services.GetRequiredService();
var storage = _services.GetRequiredService();
- _router = await agentService.LoadAgent(message.CurrentAgentId);
+ _router = await agentService.GetAgent(message.CurrentAgentId);
var states = _services.GetRequiredService();
var executor = _services.GetRequiredService();
@@ -58,11 +58,8 @@ public partial class RoutingService
// Save states
states.SaveStateByArgs(inst.Arguments);
-#if DEBUG
- Console.WriteLine($"*** Next Instruction *** {inst}");
-#else
- _logger.LogInformation($"*** Next Instruction *** {inst}");
-#endif
+ _logger.LogDebug($"*** Next Instruction *** {inst}");
+
await reasoner.AgentExecuting(_router, inst, message, dialogs);
// Handover to Task Agent
@@ -79,7 +76,7 @@ public partial class RoutingService
await reasoner.AgentExecuted(_router, inst, response, dialogs);
- if (loopCount >= reasoner.MaxLoopCount || _context.IsEmpty)
+ if (loopCount >= reasoner.MaxLoopCount || _context.IsEmpty || response.StopCompletion)
{
break;
}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs
index 19654deb..3e45fd63 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs
@@ -26,54 +26,40 @@ public partial class RoutingService : IRoutingService
_logger = logger;
}
- public async Task InstructDirect(Agent agent, RoleDialogModel message)
+ public async Task InstructDirect(Agent agent, RoleDialogModel message, List dialogs)
{
- var handlers = _services.GetServices();
-
- var handler = handlers.FirstOrDefault(x => x.Name == "route_to_agent");
-
var conv = _services.GetRequiredService();
var storage = _services.GetRequiredService();
storage.Append(conv.ConversationId, message);
- var dialogs = conv.GetDialogHistory();
+ dialogs.Add(message);
Context.SetDialogs(dialogs);
- handler.SetDialogs(dialogs);
- var inst = new FunctionCallFromLlm
+ var routing = _services.GetRequiredService();
+ routing.Context.Push(agent.Id, "instruct directly");
+ var agentId = routing.Context.GetCurrentAgentId();
+
+ // Update next action agent's name
+ var agentService = _services.GetRequiredService();
+
+ if (agent.Disabled)
{
- Function = "route_to_agent",
- Question = message.Content,
- NextActionReason = message.Content,
- AgentName = agent.Name,
- OriginalAgent = agent.Name,
- ExecutingDirectly = true
- };
+ var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent.";
- var result = await handler.Handle(this, inst, message);
+ message = RoleDialogModel.From(message, role: AgentRole.Assistant, content: content);
+ dialogs.Add(message);
+ }
+ else
+ {
+ var ret = await routing.InvokeAgent(agentId, dialogs);
+ }
var response = dialogs.Last();
response.MessageId = message.MessageId;
- response.Instruction = inst;
return response;
}
- public List GetHandlers(Agent router)
- {
- var reasoner = GetReasoner(router);
-
- return _services.GetServices()
- .Where(x => x.Planers == null || x.Planers.Contains(reasoner.GetType().Name))
- .Where(x => !string.IsNullOrEmpty(x.Description))
- .Select((x, i) => new RoutingHandlerDef
- {
- Name = x.Name,
- Description = x.Description,
- Parameters = x.Parameters
- }).ToList();
- }
-
#if !DEBUG
[SharpCache(10)]
#endif
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/functions/response_to_user.json b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/functions/response_to_user.json
index a2685810..9e7f8929 100644
--- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/functions/response_to_user.json
+++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/functions/response_to_user.json
@@ -1,15 +1,14 @@
{
"name": "response_to_user",
- "description": "Response to user without routing to any other agent",
- "visibility_expression": "{% if states.routing_mode == 'lazy' %}visible{% endif %}",
+ "description": "Response to user without routing to any other agent, user has no specific request.",
"parameters": {
"type": "object",
"properties": {
- "response": {
+ "content": {
"type": "string",
"description": "Response content"
}
},
- "required": [ "response" ]
+ "required": [ "content" ]
}
}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/functions/route_to_agent.json b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/functions/route_to_agent.json
index 08f938ec..01301bbf 100644
--- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/functions/route_to_agent.json
+++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/functions/route_to_agent.json
@@ -1,7 +1,6 @@
{
"name": "route_to_agent",
"description": "Route request to appropriate AI agent.",
- "visibility_expression": "{% if states.routing_mode == 'lazy' %}visible{% endif %}",
"parameters": {
"type": "object",
"properties": {
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid
index 99f7a219..775de66c 100644
--- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid
+++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid
@@ -5,11 +5,7 @@ Follow these steps to handle user request:
2. Determine which agent is suitable to handle this conversation. Try to minimize the routing of human service.
3. Extract and populate agent required arguments, think carefully, leave it as blank object if user didn't provide the specific arguments.
4. You must include all required args for the selected agent, but you must not make up any parameters when there is no exact value provided, those parameters must set value as null if not declared.
-{% if routing_mode != 'lazy' %}
-5. Response must be in JSON format.
-{% else %}
5. If user is greeting, you can call function response_to_user with a greeting message.
-{% endif %}
{% if routing_requirements and routing_requirements != empty %}
[REQUIREMENTS]
@@ -18,21 +14,6 @@ Follow these steps to handle user request:
{%- endfor %}
{% endif %}
-{% if routing_mode != 'lazy' %}
-[FUNCTIONS]
-{% for handler in routing_handlers -%}
-# {{ handler.description}}
-{% if handler.parameters and handler.parameters != empty -%}
-Parameters:
- - function: {{ handler.name }}
- {% for p in handler.parameters -%}
- - {{ p.name }} {% if p.required -%}(required){%- endif %}: {{ p.description }}{{ "\r\n " }}
- {%- endfor %}
-{%- endif %}
-{{ "\r\n" }}
-{%- endfor %}
-{% endif %}
-
[AGENTS]
{% for agent in routing_agents -%}
* Agent: {{ agent.name }}
diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs
index fb3c37a2..391db1a9 100644
--- a/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs
+++ b/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs
@@ -27,7 +27,7 @@ public class VerboseLogHook : IContentGeneratingHook
if (dialog != null)
{
var log = $"{dialog.Role}: {dialog.Content} [msg_id: {dialog.MessageId}] ==>";
- _logger.LogInformation(log);
+ _logger.LogDebug(log);
}
await Task.CompletedTask;
@@ -44,7 +44,7 @@ public class VerboseLogHook : IContentGeneratingHook
$"[{agent?.Name}]: {message.Indication} {message.FunctionName}({message.FunctionArgs})" :
$"[{agent?.Name}]: {message.Content}" + $" <== [msg_id: {message.MessageId}]";
- _logger.LogInformation(tokenStats.Prompt);
- _logger.LogInformation(log);
+ _logger.LogDebug(tokenStats.Prompt);
+ _logger.LogDebug(log);
}
}
diff --git a/src/Plugins/BotSharp.Plugin.Planner/Sequential/SequentialPlanner.cs b/src/Plugins/BotSharp.Plugin.Planner/Sequential/SequentialPlanner.cs
index 043c7380..94b4a687 100644
--- a/src/Plugins/BotSharp.Plugin.Planner/Sequential/SequentialPlanner.cs
+++ b/src/Plugins/BotSharp.Plugin.Planner/Sequential/SequentialPlanner.cs
@@ -41,7 +41,7 @@ public class SequentialPlanner : ITaskPlanner
var decomposation = await GetDecomposedStepAsync(router, messageId, dialogs);
if (decomposation.TotalRemainingSteps > 0 && _lastInst != null)
{
- _lastInst.Response = decomposation.Description;
+ // _lastInst.Response = decomposation.Description;
_lastInst.NextActionReason = $"Having {decomposation.TotalRemainingSteps} steps left.";
return _lastInst;
}
@@ -62,8 +62,6 @@ public class SequentialPlanner : ITaskPlanner
var next = GetNextStepPrompt(router);
- var inst = new FunctionCallFromLlm();
-
// text completion
/*var agentService = _services.GetRequiredService();
var instruction = agentService.RenderedInstruction(router);
@@ -76,43 +74,26 @@ public class SequentialPlanner : ITaskPlanner
provider: router?.LlmConfig?.Provider,
model: router?.LlmConfig?.Model);
- int retryCount = 0;
- while (retryCount < 3)
- {
- string text = string.Empty;
- try
- {
- // text completion
- // text = await completion.GetCompletion(content, router.Id, messageId);
- dialogs = new List
- {
- new RoleDialogModel(AgentRole.User, next)
- {
- FunctionName = nameof(SequentialPlanner),
- MessageId = messageId
- }
- };
- var response = await completion.GetChatCompletions(router, dialogs);
- inst = response.Content.JsonContent();
- break;
- }
- catch (Exception ex)
+ string text = string.Empty;
+
+ // text completion
+ // text = await completion.GetCompletion(content, router.Id, messageId);
+ dialogs = new List
+ {
+ new RoleDialogModel(AgentRole.User, next)
{
- _logger.LogError($"{ex.Message}: {text}");
- inst.Function = "response_to_user";
- inst.Response = ex.Message;
- inst.AgentName = "Router";
+ FunctionName = nameof(SequentialPlanner),
+ MessageId = messageId
}
- finally
- {
- retryCount++;
- }
- }
+ };
+ var response = await completion.GetChatCompletions(router, dialogs);
+
+ var inst = response.Content.JsonContent();
if (decomposation.TotalRemainingSteps > 0)
{
- inst.Response = decomposation.Description;
+ // inst.Response = decomposation.Description;
inst.NextActionReason = $"{decomposation.TotalRemainingSteps} steps left.";
inst.HandleDialogsByPlanner = true;
}
@@ -125,10 +106,10 @@ public class SequentialPlanner : ITaskPlanner
{
var taskAgentDialogs = new List
{
- new RoleDialogModel(AgentRole.User, inst.Response)
+ /*new RoleDialogModel(AgentRole.User, inst.Response)
{
MessageId = message.MessageId,
- }
+ }*/
};
return taskAgentDialogs;
diff --git a/src/Plugins/BotSharp.Plugin.Planner/SqlGeneration/SqlGenerationPlanner.cs b/src/Plugins/BotSharp.Plugin.Planner/SqlGeneration/SqlGenerationPlanner.cs
index 62340e9c..a804b073 100644
--- a/src/Plugins/BotSharp.Plugin.Planner/SqlGeneration/SqlGenerationPlanner.cs
+++ b/src/Plugins/BotSharp.Plugin.Planner/SqlGeneration/SqlGenerationPlanner.cs
@@ -42,14 +42,14 @@ public class SqlGenerationPlanner : ITaskPlanner
public List BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List dialogs)
{
- var question = inst.Response;
+ // var question = inst.Response;
var taskAgentDialogs = new List
{
- new RoleDialogModel(AgentRole.User, question)
+ /*new RoleDialogModel(AgentRole.User, question)
{
MessageId = message.MessageId,
- }
+ }*/
};
return taskAgentDialogs;
diff --git a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs
index 6935bfbf..ea89cd71 100644
--- a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs
+++ b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs
@@ -70,14 +70,14 @@ public partial class TwoStageTaskPlanner : ITaskPlanner
public List BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List dialogs)
{
- var question = inst.Response;
+ // var question = inst.Response;
var taskAgentDialogs = new List
{
- new RoleDialogModel(AgentRole.User, question)
+ /*new RoleDialogModel(AgentRole.User, question)
{
MessageId = message.MessageId,
- }
+ }*/
};
return taskAgentDialogs;
diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs
index 095f3d25..e5f9c0bb 100644
--- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs
+++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs
@@ -152,7 +152,7 @@ public class TwilioInboundController : TwilioController
AgentId = request.AgentId,
Channel = ConversationChannel.Phone,
ChannelId = request.CallSid,
- Title = $"Incoming phone call from {request.From}",
+ Title = request.Intent ?? $"Incoming phone call from {request.From}",
Tags = [],
};
@@ -167,6 +167,15 @@ public class TwilioInboundController : TwilioController
new("twilio_call_sid", request.CallSid),
};
+ var requestStates = ParseStates(request.States);
+ foreach (var s in requestStates)
+ {
+ if (!states.Any(x => x.Key == s.Key))
+ {
+ states.Add(new MessageState(s.Key, s.Value));
+ }
+ }
+
if (request.InitAudioFile != null)
{
states.Add(new("init_audio_file", request.InitAudioFile));
@@ -179,7 +188,20 @@ public class TwilioInboundController : TwilioController
{
states.Add(new(StateConst.ROUTING_MODE, agent.Mode));
}
+
convService.SetConversationId(conversation.Id, states);
+
+ if (!string.IsNullOrEmpty(request.Intent))
+ {
+ var storage = _services.GetRequiredService();
+
+ storage.Append(conversation.Id, new RoleDialogModel(AgentRole.User, request.Intent)
+ {
+ CurrentAgentId = conversation.Id,
+ CreatedAt = DateTime.UtcNow
+ });
+ }
+
convService.SaveStates();
// reload agent rendering with states
diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Models/ConversationalVoiceRequest.cs b/src/Plugins/BotSharp.Plugin.Twilio/Models/ConversationalVoiceRequest.cs
index df08df0a..344ddc06 100644
--- a/src/Plugins/BotSharp.Plugin.Twilio/Models/ConversationalVoiceRequest.cs
+++ b/src/Plugins/BotSharp.Plugin.Twilio/Models/ConversationalVoiceRequest.cs
@@ -17,7 +17,10 @@ public class ConversationalVoiceRequest : VoiceRequest
public int AIResponseWaitTime { get; set; } = 0;
public string? AIResponseErrorMessage { get; set; } = string.Empty;
- public string Intent { get; set; } = string.Empty;
+ ///
+ /// Initial intent when incoming call connected
+ ///
+ public string? Intent { get; set; }
[FromQuery(Name = "init-audio-file")]
public string? InitAudioFile { get; set; }
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index ce052798..b48d2963 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -254,8 +254,8 @@
"HostAgentId": "01e2fc5c-2c89-4ec7-8470-7688608b496c",
"EnableTranslator": false,
"LlmConfig": {
- "Provider": "azure-openai",
- "Model": "gpt-4o-mini"
+ "Provider": "openai",
+ "Model": "gpt-4.1-nano"
}
},
diff --git a/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj b/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj
index d132125f..c056982d 100644
--- a/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj
+++ b/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj
@@ -32,9 +32,7 @@
-
-
@@ -63,9 +61,6 @@
PreserveNewest
-
- PreserveNewest
-
PreserveNewest
@@ -93,7 +88,7 @@
PreserveNewest
-
+
PreserveNewest
diff --git a/tests/BotSharp.Plugin.PizzaBot/Functions/PlaceOrderFn.cs b/tests/BotSharp.Plugin.PizzaBot/Functions/PlaceOrderFn.cs
index 87466956..e4198f34 100644
--- a/tests/BotSharp.Plugin.PizzaBot/Functions/PlaceOrderFn.cs
+++ b/tests/BotSharp.Plugin.PizzaBot/Functions/PlaceOrderFn.cs
@@ -1,11 +1,12 @@
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;
+using BotSharp.Abstraction.Infrastructures.Enums;
namespace BotSharp.Plugin.PizzaBot.Functions;
public class PlaceOrderFn : IFunctionCallback
{
- public string Name => "place_an_order";
+ public string Name => "place_order";
private readonly IServiceProvider _service;
public PlaceOrderFn(IServiceProvider service)
@@ -19,6 +20,9 @@ public class PlaceOrderFn : IFunctionCallback
var state = _service.GetRequiredService();
state.SetState("order_number", "P123-01");
+ // Set the next action agent to Payment
+ state.SetState(StateConst.EXPECTED_ACTION_AGENT, "Payment", activeRounds: 2);
+
return true;
}
}
diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/8970b1e5-d260-4e2c-90b1-f1415a257c18/agent.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/8970b1e5-d260-4e2c-90b1-f1415a257c18/agent.json
index 3a28b3e0..99f1f2fd 100644
--- a/tests/BotSharp.Plugin.PizzaBot/data/agents/8970b1e5-d260-4e2c-90b1-f1415a257c18/agent.json
+++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/8970b1e5-d260-4e2c-90b1-f1415a257c18/agent.json
@@ -1,7 +1,7 @@
{
"id": "8970b1e5-d260-4e2c-90b1-f1415a257c18",
"name": "Pizza Bot",
- "description": "AI assistant that can help customer place pizza order, make payment or inquiry order status.",
+ "description": "AI assistant that can help customer place pizza order, make payment or inquiry existing order.",
"type": "routing",
"inheritAgentId": "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a",
"createdDateTime": "2023-08-18T10:39:32.2349685Z",
@@ -11,6 +11,10 @@
"isPublic": true,
"profiles": [ "pizza" ],
"labels": [ "experiment" ],
+ "llmConfig": {
+ "provider": "openai",
+ "model": "gpt-4.1-nano"
+ },
"routingRules": [
{
"type": "reasoner",
diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json
index 09f7489b..e9bb78d0 100644
--- a/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json
+++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json
@@ -1,6 +1,6 @@
{
"name": "Order Inquiry",
- "description": "Check the order status like payment, delivery or baking.",
+ "description": "Check the existing order status like payment, delivery or baking.",
"createdDateTime": "2023-08-18T14:39:32.2349685Z",
"updatedDateTime": "2023-08-18T14:39:32.2349686Z",
"id": "b284db86-e9c2-4c25-a59e-4649797dd130",
diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/functions/get_order_status.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/functions/get_order_status.json
index 49de63fe..fc7abb8b 100644
--- a/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/functions/get_order_status.json
+++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/functions/get_order_status.json
@@ -6,7 +6,7 @@
"properties": {
"order_number": {
"type": "string",
- "description": "order number."
+ "description": "order number, value must be provided by user."
}
},
"required": [ "order_number" ]
diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json
index a6948ac4..8cade6f1 100644
--- a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json
+++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json
@@ -1,5 +1,5 @@
{
- "name": "Ordering",
+ "name": "Order Placement",
"description": "Provide types of pizza available, pizza unit price, total cost and place the order.",
"createdDateTime": "2023-07-26T02:29:25.123224Z",
"updatedDateTime": "2023-07-26T02:29:25.123274Z",
diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/functions/place_an_order.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/functions/place_order.json
similarity index 94%
rename from tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/functions/place_an_order.json
rename to tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/functions/place_order.json
index d5514fba..9fe8278a 100644
--- a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/functions/place_an_order.json
+++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/functions/place_order.json
@@ -1,5 +1,5 @@
{
- "name": "place_an_order",
+ "name": "place_order",
"description": "Place an order when user has confirmed the pizza type and quantity.",
"parameters": {
"type": "object",
diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/instructions/instruction.liquid b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/instructions/instruction.liquid
index 73b33cc3..5194fc79 100644
--- a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/instructions/instruction.liquid
+++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/instructions/instruction.liquid
@@ -1,10 +1,9 @@
You are now a Pizza Ordering agent, and you can help customers order a pizza according to the user's preferences.
-Follow below step to place order:
-1: Ask user preferences, call function get_pizza_types to provide the variety of pizza options.
-2: Confirm with user the pizza type and quantity.
-3: Call function place_an_order to purchase.
-4: Ask user how to pay for this order.
-
-Use below information to help ordering process:
* Today is {{current_date}}, the time now is {{current_time}}, day of week is {{current_weekday}}.
+
+Follow below step to response:
+1: Ask user preferences, call function get_pizza_types to provide the variety of pizza options.
+2: Call get_pizza_price to tell customer the price per unit, then ask user for the quantity.
+3: Confirm the order with total price, call function place_order to place the order.
+4: Ask user how to pay for this order.
diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/responses/func.get_pizza_price.0.liquid b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/responses/func.get_pizza_price.0.liquid
deleted file mode 100644
index 62041ece..00000000
--- a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/responses/func.get_pizza_price.0.liquid
+++ /dev/null
@@ -1,13 +0,0 @@
-{% assign pizza_type = pizza_type | downcase %}
-{% if pizza_type contains "cheese" -%}
- The price for a slice of {{pizza_type}} pizza is ${{ cheese_unit_price }}. Would you like to proceed the order?
-{%- elsif pizza_type contains "pepperoni" -%}
- The price for a slice of {{pizza_type}} pizza is ${{ pepperoni_unit_price }}. Would you like to proceed the order?
-{%- elsif pizza_type contains "margherita" -%}
- The price for a slice of {{pizza_type}} pizza is ${{ margherita_unit_price }}. Would you like to proceed the order?
-{%- else -%}
- We don't have {{pizza_type}} pizza, would you like something else?
-{%- endif %}
-{% if quantity == nil -%}
- How many slices would you like to order?
-{%- endif %}
\ No newline at end of file