Fix CurrentRecursionDepth bug in Testing mode.

This commit is contained in:
Haiping Chen 2023-10-20 18:57:45 -05:00
parent 8fcada182f
commit 13e17acc76
22 changed files with 117 additions and 83 deletions

View file

@ -19,9 +19,11 @@ It's written in C# running on .Net Core that is full cross-platform framework, t
### Some Features
* Built-in multi-agents and conversation management.
* Support multiple LLM platforms.
* Support export/ import agent from other bot platforms directly.
* Built-in multi-agents and conversation with state management.
* Built-in RAG related interfaces, Memeory based vector searching.
* Support multiple LLM platforms (ChatGPT, PaLM 2, LLaMA 2).
* Allow multiple agents with different responsibilities cooperate to complete complex tasks.
* Build, test, evaluate and audit your LLM agent in one place.
* Support different open source UI [Chatbot UI](src/Plugins/BotSharp.Plugin.ChatbotUI/Chatbot-UI.md), [HuggingChat UI](src/Plugins/BotSharp.Plugin.HuggingFace/HuggingChat-UI.md).
* Integrate with popular message channels like Facebook Messenger, Slack and Telegram.
@ -50,7 +52,3 @@ Read the docs: https://botsharp.readthedocs.io
If you feel that this project is helpful to you, please Star the project, we would be very grateful.
Member project of [SciSharp STACK](https://github.com/SciSharp) which is the .NET based ecosystem of open-source software for mathematics, science, and engineering.
Scan QR code to join TIM group:
![SciSharp STACK](https://raw.githubusercontent.com/SciSharp/TensorFlow.NET/master/docs/TIM.jpg)

View file

@ -1,37 +1,37 @@
<mxfile host="Electron" modified="2023-09-05T01:59:06.699Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.8 Chrome/114.0.5735.289 Electron/25.5.0 Safari/537.36" etag="cm-9kpR-IQWk9TqhWNbL" version="21.6.8" type="device">
<mxfile host="Electron" modified="2023-10-20T14:03:23.944Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.8 Chrome/114.0.5735.289 Electron/25.5.0 Safari/537.36" etag="Q2NG1mP47cvCG7Qq9r-D" version="21.6.8" type="device">
<diagram name="Page-1" id="FNTbtRNAYl-cjTgH9x2x">
<mxGraphModel dx="1434" dy="836" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<mxGraphModel dx="1418" dy="820" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="DrYO5MgT0EdguJCGkx49-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;sketch=1;curveFitting=1;jiggle=2;" edge="1" parent="1" source="DrYO5MgT0EdguJCGkx49-1" target="DrYO5MgT0EdguJCGkx49-8">
<mxCell id="DrYO5MgT0EdguJCGkx49-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;sketch=1;curveFitting=1;jiggle=2;" parent="1" source="DrYO5MgT0EdguJCGkx49-1" target="DrYO5MgT0EdguJCGkx49-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-1" value="&lt;b&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;BotSharp&lt;/font&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffcc99;strokeColor=#36393d;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxCell id="DrYO5MgT0EdguJCGkx49-1" value="&lt;b&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;BotSharp&lt;/font&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffcc99;strokeColor=#36393d;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="345" y="460" width="180" height="60" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;fillColor=#e51400;strokeColor=#B20000;" edge="1" parent="1" source="DrYO5MgT0EdguJCGkx49-3" target="DrYO5MgT0EdguJCGkx49-1">
<mxCell id="DrYO5MgT0EdguJCGkx49-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;fillColor=#e51400;strokeColor=#B20000;" parent="1" source="DrYO5MgT0EdguJCGkx49-3" target="DrYO5MgT0EdguJCGkx49-1" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-3" value="&lt;b&gt;User&lt;/b&gt;" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxCell id="DrYO5MgT0EdguJCGkx49-3" value="&lt;b&gt;User&lt;/b&gt;" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="420" y="340" width="30" height="60" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-4" value="&lt;div style=&quot;line-height: 18px;&quot;&gt;&lt;font color=&quot;#1e1e1e&quot;&gt;&lt;b&gt;Can I have a slice of cheese pizza?&lt;/b&gt;&lt;/font&gt;&lt;br&gt;&lt;/div&gt;" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxCell id="DrYO5MgT0EdguJCGkx49-4" value="&lt;div style=&quot;line-height: 18px;&quot;&gt;&lt;font color=&quot;#1e1e1e&quot;&gt;&lt;b&gt;Can I have a slice of cheese pizza?&lt;/b&gt;&lt;/font&gt;&lt;br&gt;&lt;/div&gt;" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="345" y="270" width="230" height="70" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;" edge="1" parent="1" source="DrYO5MgT0EdguJCGkx49-8" target="DrYO5MgT0EdguJCGkx49-12">
<mxCell id="DrYO5MgT0EdguJCGkx49-19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;" parent="1" source="DrYO5MgT0EdguJCGkx49-8" target="DrYO5MgT0EdguJCGkx49-12" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-24" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;" edge="1" parent="1" source="DrYO5MgT0EdguJCGkx49-8" target="DrYO5MgT0EdguJCGkx49-14">
<mxCell id="DrYO5MgT0EdguJCGkx49-24" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;" parent="1" source="DrYO5MgT0EdguJCGkx49-8" target="DrYO5MgT0EdguJCGkx49-14" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;" edge="1" parent="1" source="DrYO5MgT0EdguJCGkx49-8" target="DrYO5MgT0EdguJCGkx49-15">
<mxCell id="DrYO5MgT0EdguJCGkx49-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;" parent="1" source="DrYO5MgT0EdguJCGkx49-8" target="DrYO5MgT0EdguJCGkx49-15" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-8" value="&lt;b&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;Router&lt;/font&gt;&lt;/b&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_2;whiteSpace=wrap;fillColor=#ffcc99;strokeColor=#36393d;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxCell id="DrYO5MgT0EdguJCGkx49-8" value="&lt;b&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;Router&lt;/font&gt;&lt;/b&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_2;whiteSpace=wrap;fillColor=#ffcc99;strokeColor=#36393d;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="385" y="570" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.3333333333333333;entryDx=0;entryDy=0;entryPerimeter=0;fillColor=#e51400;strokeColor=#B20000;sketch=1;curveFitting=1;jiggle=2;" edge="1" parent="1" source="DrYO5MgT0EdguJCGkx49-12" target="DrYO5MgT0EdguJCGkx49-3">
<mxCell id="DrYO5MgT0EdguJCGkx49-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.3333333333333333;entryDx=0;entryDy=0;entryPerimeter=0;fillColor=#e51400;strokeColor=#B20000;sketch=1;curveFitting=1;jiggle=2;" parent="1" source="DrYO5MgT0EdguJCGkx49-12" target="DrYO5MgT0EdguJCGkx49-3" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="190" y="793" />
@ -39,18 +39,18 @@
</Array>
</mxGeometry>
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-29" value="&lt;b style=&quot;font-size: 11px;&quot;&gt;How many slices would you like to order?&lt;/b&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=11;" vertex="1" connectable="0" parent="DrYO5MgT0EdguJCGkx49-28">
<mxCell id="DrYO5MgT0EdguJCGkx49-29" value="&lt;b style=&quot;font-size: 11px;&quot;&gt;How many slices would you like to order?&lt;/b&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=11;" parent="DrYO5MgT0EdguJCGkx49-28" vertex="1" connectable="0">
<mxGeometry x="-0.1958" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-12" value="&lt;b&gt;&lt;br&gt;&lt;br&gt;Ordering&lt;/b&gt;" style="shape=actor;whiteSpace=wrap;html=1;fillColor=#ffff88;strokeColor=#36393d;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxCell id="DrYO5MgT0EdguJCGkx49-12" value="&lt;b&gt;&lt;br&gt;&lt;br&gt;Ordering&lt;/b&gt;" style="shape=actor;whiteSpace=wrap;html=1;fillColor=#ffff88;strokeColor=#36393d;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="240" y="740" width="60" height="70" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-14" value="&lt;b&gt;&lt;br&gt;&lt;br&gt;Baking&lt;br&gt;&lt;/b&gt;" style="shape=actor;whiteSpace=wrap;html=1;fillColor=#ffff88;strokeColor=#36393d;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxCell id="DrYO5MgT0EdguJCGkx49-14" value="&lt;b&gt;&lt;br&gt;&lt;br&gt;Order Inquiry&lt;br&gt;&lt;/b&gt;" style="shape=actor;whiteSpace=wrap;html=1;fillColor=#ffff88;strokeColor=#36393d;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="405" y="740" width="60" height="70" as="geometry" />
</mxCell>
<mxCell id="DrYO5MgT0EdguJCGkx49-15" value="&lt;b&gt;&lt;br&gt;&lt;br&gt;Payment&lt;br&gt;&lt;/b&gt;" style="shape=actor;whiteSpace=wrap;html=1;fillColor=#ffff88;strokeColor=#36393d;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxCell id="DrYO5MgT0EdguJCGkx49-15" value="&lt;b&gt;&lt;br&gt;&lt;br&gt;Payment&lt;br&gt;&lt;/b&gt;" style="shape=actor;whiteSpace=wrap;html=1;fillColor=#ffff88;strokeColor=#36393d;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="570" y="740" width="60" height="70" as="geometry" />
</mxCell>
</root>

View file

@ -1,7 +1,18 @@
using BotSharp.Abstraction.Models;
namespace BotSharp.Abstraction.Functions.Models;
public class ParameterPropertyDef
public class ParameterPropertyDef : NameDesc
{
public ParameterPropertyDef(string name, string description, string type = "string")
: base(name, description)
{
Type = type;
}
/// <summary>
/// string, number, object
/// </summary>
[JsonPropertyName("type")]
public string Type { get; set; } = "string";
public string Description { get; set; }
}

View file

@ -11,9 +11,9 @@ public interface IRoutingHandler
{
string Name { get; }
string Description { get; }
bool IsReasoning { get => false; }
bool Enabled { get => true; }
List<NameDesc> Parameters { get => new List<NameDesc>(); }
bool IsReasoning => false;
bool Enabled => true;
List<NameDesc> Parameters => new List<NameDesc>();
void SetRouter(Agent router) { }

View file

@ -5,6 +5,7 @@ namespace BotSharp.Abstraction.Routing;
public interface IRoutingService
{
List<RoleDialogModel> Dialogs { get; }
void ResetRecursiveCounter();
void RefreshDialogs();
Task<FunctionCallFromLlm> GetNextInstruction();
Task<RoleDialogModel> InvokeAgent(string agentId);

View file

@ -1,7 +1,15 @@
using BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Abstraction.Routing.Models;
public class RoutingContext
{
private readonly RoutingSettings _setting;
public RoutingContext(RoutingSettings setting)
{
_setting = setting;
}
private Stack<string> _stack { get; set; }
= new Stack<string>();
@ -13,8 +21,14 @@ public class RoutingContext
public string OriginAgentId
=> _stack.Last();
public string CurrentAgentId
=> _stack.Peek();
public string GetCurrentAgentId()
{
if (_stack.Count == 0)
{
_stack.Push(_setting.RouterId);
}
return _stack.Peek();
}
public void Push(string agentId)
{

View file

@ -1,4 +1,4 @@
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Abstraction.Routing.Models;
@ -14,5 +14,5 @@ public class RoutingItem
public string Description { get; set; } = string.Empty;
[JsonPropertyName("required_fields")]
public List<NameDesc> RequiredFields { get; set; } = new List<NameDesc>();
public List<ParameterPropertyDef> RequiredFields { get; set; } = new List<ParameterPropertyDef>();
}

View file

@ -10,6 +10,10 @@ public class RoutingRule
public string Field { get; set; }
public string Description { get; set; }
/// <summary>
/// Field type: string, number, object
/// </summary>
public string Type { get; set; }
public bool Required { get; set; }

View file

@ -18,7 +18,12 @@ public partial class ConversationService
var agentService = _services.GetRequiredService<IAgentService>();
Agent agent = await agentService.LoadAgent(agentId);
_logger.LogInformation($"[{agent.Name}] {incoming.Role}: {incoming.Content}");
var message = $"Received [{agent.Name}] {incoming.Role}: {incoming.Content}";
#if DEBUG
Console.WriteLine(message, Color.OrangeRed);
#else
_logger.LogInformation(message);
#endif
incoming.CurrentAgentId = agent.Id;
@ -56,6 +61,7 @@ public partial class ConversationService
var statistics = _services.GetRequiredService<ITokenStatistics>();
statistics.PrintStatistics();
routing.ResetRecursiveCounter();
routing.RefreshDialogs();
return true;
@ -81,16 +87,15 @@ public partial class ConversationService
private async Task HandleAssistantMessage(RoleDialogModel message, Func<RoleDialogModel, Task> onMessageReceived)
{
var routingSetting = _services.GetRequiredService<RoutingSettings>();
var agentName = routingSetting.RouterId == message.CurrentAgentId ?
"Router" :
(await _services.GetRequiredService<IAgentService>().GetAgent(message.CurrentAgentId)).Name;
var agentService = _services.GetRequiredService<IAgentService>();
var agent = await agentService.GetAgent(message.CurrentAgentId);
var agentName = agent.Name;
var text = message.Role == AgentRole.Function ?
$"[{agentName}] {message.FunctionName}: {message.Content}" :
$"[{agentName}] {message.Role}: {message.Content}";
$"Sending [{agentName}] {message.FunctionName}: {message.Content}" :
$"Sending [{agentName}] {message.Role}: {message.Content}";
#if DEBUG
Console.WriteLine(text, Color.Pink);
Console.WriteLine(text, Color.Yellow);
#else
_logger.LogInformation(text);
#endif

View file

@ -46,8 +46,7 @@ public class ConversationStorage : IConversationStorage
}
else
{
var routingSetting = _services.GetRequiredService<RoutingSettings>();
var agentName = routingSetting.RouterId == agentId ? "Router" : db.GetAgent(agentId)?.Name;
var agentName = db.GetAgent(agentId)?.Name;
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{agentName}|");
var content = dialog.Content.Replace("\r", " ").Replace("\n", " ").Trim();

View file

@ -29,7 +29,7 @@ public class RouteToAgentFn : IFunctionCallback
if (!string.IsNullOrEmpty(args.OriginalAgent) && args.OriginalAgent.Length < 32)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var originalAgent = db.GetAgents(args.OriginalAgent).FirstOrDefault();
var originalAgent = db.GetAgents(name: args.OriginalAgent).FirstOrDefault();
if (originalAgent != null)
{
_context.Push(originalAgent.Id);

View file

@ -30,18 +30,19 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
public async Task<RoleDialogModel> Handle(IRoutingService routing, FunctionCallFromLlm inst)
{
var context = _services.GetRequiredService<RoutingContext>();
var function = _services.GetServices<IFunctionCallback>().FirstOrDefault(x => x.Name == inst.Function);
var message = new RoleDialogModel(AgentRole.Function, inst.Question)
{
FunctionName = inst.Function,
FunctionArgs = JsonSerializer.Serialize(inst),
CurrentAgentId = routing.Dialogs.Last().CurrentAgentId
CurrentAgentId = context.GetCurrentAgentId(),
};
var ret = await function.Execute(message);
var context = _services.GetRequiredService<RoutingContext>();
var result = await routing.InvokeAgent(context.CurrentAgentId);
var result = await routing.InvokeAgent(context.GetCurrentAgentId());
// Keep last message data for debug
result.ExecutionData = result.ExecutionData ?? message.ExecutionData;
result.FunctionName = result.FunctionName ?? message.FunctionName;

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing;
@ -89,8 +90,9 @@ public class RouterInstance : IRouterInstance
AgentId = x.Id,
Description = x.Description,
Name = x.Name,
RequiredFields = x.RoutingRules.Where(x => x.Required)
.Select(x => new NameDesc(x.Field, x.Description))
RequiredFields = x.RoutingRules
.Where(x => x.Required)
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type))
.ToList()
}).ToArray();
}

View file

@ -94,7 +94,7 @@ public partial class RoutingService
}
#if DEBUG
Console.WriteLine(response.Content, Color.Gray);
Console.WriteLine(response.Content, Color.Green);
#else
_logger.LogInformation(response.Content);
#endif

View file

@ -6,12 +6,13 @@ namespace BotSharp.Core.Routing;
public partial class RoutingService
{
const int MAXIMUM_RECURSION_DEPTH = 3;
int CurrentRecursionDepth = 0;
private int _currentRecursionDepth = 0;
public async Task<RoleDialogModel> InvokeAgent(string agentId)
{
CurrentRecursionDepth++;
if (CurrentRecursionDepth > MAXIMUM_RECURSION_DEPTH)
_currentRecursionDepth++;
if (_currentRecursionDepth > MAXIMUM_RECURSION_DEPTH)
{
_logger.LogWarning($"Current recursive call depth greater than {MAXIMUM_RECURSION_DEPTH}, which will cause unexpected result.");
return Dialogs.Last();
}
@ -23,13 +24,15 @@ public partial class RoutingService
if (response.Role == AgentRole.Function)
{
await InvokeFunction(agent, response);
return await InvokeFunction(agent, response);
}
else
{
return response;
}
return response;
}
private async Task InvokeFunction(Agent agent, RoleDialogModel response)
private async Task<RoleDialogModel> InvokeFunction(Agent agent, RoleDialogModel response)
{
// execute function
// Save states
@ -41,11 +44,12 @@ public partial class RoutingService
if (string.IsNullOrEmpty(response.Content))
{
response.Content = response.ExecutionResult;
response.Content = response.ExecutionResult ?? JsonSerializer.Serialize(response.ExecutionData);
}
Dialogs.Add(response);
// Pass execution result to LLM to get response
if (!response.StopCompletion)
{
// Find response template
@ -54,17 +58,14 @@ public partial class RoutingService
if (!string.IsNullOrEmpty(responseTemplate))
{
response.Role = AgentRole.Assistant;
response.Content = responseTemplate;
response.Content = responseTemplate.Trim();
}
else
{
var recursiveResponse = await InvokeAgent(response.CurrentAgentId);
response.Role = recursiveResponse.Role;
response.Content = recursiveResponse.Content;
response.ExecutionResult = recursiveResponse.ExecutionResult;
response.ExecutionData = recursiveResponse.ExecutionData ?? response.ExecutionData;
response.StopCompletion = recursiveResponse.StopCompletion;
response = await InvokeAgent(response.CurrentAgentId);
}
}
return response;
}
}

View file

@ -25,6 +25,11 @@ public partial class RoutingService : IRoutingService
}
}
public void ResetRecursiveCounter()
{
_currentRecursionDepth = 0;
}
public void RefreshDialogs()
{
_dialogs = null;
@ -88,7 +93,6 @@ public partial class RoutingService : IRoutingService
if (handler == null)
{
handler = handlers.FirstOrDefault(x => x.Name == "get_next_instruction");
router.Instruction += $"\r\n{AgentRole.System}: the function must be one of {string.Join(",", _routerInstance.GetHandlers().Select(x => x.Name))}.";
continue;
}
handler.SetRouter(router);
@ -96,8 +100,6 @@ public partial class RoutingService : IRoutingService
result = await handler.Handle(this, inst);
message = result.Content.Replace("\r\n", " ");
stop = !_settings.EnableReasoning;
}

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Templating;
@ -21,6 +22,7 @@ public class TemplateRender : ITemplateRender
_options.MemberAccessStrategy.MemberNameStrategy = MemberNameStrategies.SnakeCase;
_options.MemberAccessStrategy.Register<NameDesc>();
_options.MemberAccessStrategy.Register<ParameterPropertyDef>();
_options.MemberAccessStrategy.Register<RoleDialogModel>();
_options.MemberAccessStrategy.Register<Agent>();
_options.MemberAccessStrategy.Register<RoutingItem>();

View file

@ -20,10 +20,10 @@ Response: { "function": "{{ handler.name }}",
* Agent: {{ agent.name }}
{{ agent.description}}
{% if agent.required_fields and agent.required_fields != empty -%}
Required args: {
Required args:
{% for f in agent.required_fields -%}
"{{ f.name }}": "{{ f.description }}"{{ ",\r\n " }}
{%- endfor %}}
- {{ f.name }} ({{ f.type }}): {{ f.description }}{{ "\r\n " }}
{%- endfor %}
{%- endif %}
{% endfor %}

View file

@ -1,10 +1,10 @@
{% 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?
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?
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?
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 %}

View file

@ -9,6 +9,7 @@
{
"field": "order_number",
"description": "order number",
"type": "string",
"required": true,
"redirectTo": "c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd"
}

View file

@ -10,9 +10,9 @@ public class GetPizzaPricesFn : IFunctionCallback
{
message.ExecutionData = new
{
pepperoni_unit_price = "$3.2",
cheese_unit_price = "$3.5",
margherita_unit_price = "$3.8",
pepperoni_unit_price = 3.2,
cheese_unit_price = 3.5,
margherita_unit_price = 3.8,
};
return true;
}

View file

@ -7,13 +7,6 @@ public class PizzaBotPlugin : IBotSharpPlugin
{
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
// Register callback function
services.AddScoped<IFunctionCallback, GetPizzaTypesFn>();
services.AddScoped<IFunctionCallback, GetPizzaPricesFn>();
services.AddScoped<IFunctionCallback, PlaceOrderFn>();
services.AddScoped<IFunctionCallback, GetOrderStatusFn>();
services.AddScoped<IFunctionCallback, MakePaymentFn>();
// Register hooks
services.AddScoped<IAgentHook, PizzaBotAgentHook>();
}