Skip to content

Commit 00469d8

Browse files
authored
Merge pull request #1364 from yileicn/master
fix: avoid saving routing instruction args as state
2 parents 3ba2daf + 56d1ee7 commit 00469d8

6 files changed

Lines changed: 122 additions & 5 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System.Text.Json;
2+
using BotSharp.Abstraction.Routing.Models;
3+
using BotSharp.Core.Agents.Services;
4+
using Xunit;
5+
6+
namespace BotSharp.Core.UnitTests.Routing;
7+
8+
public class RoutingRuleTests
9+
{
10+
[Fact]
11+
public void AllowLlmFill_defaults_to_true_for_existing_rules()
12+
{
13+
var rule = JsonSerializer.Deserialize<RoutingRule>("""{"field":"department_type","required":true}""");
14+
15+
Assert.NotNull(rule);
16+
Assert.True(rule.AllowLlmFill);
17+
}
18+
19+
[Fact]
20+
public void AllowLlmFill_maps_from_json_and_survives_clone()
21+
{
22+
var rule = JsonSerializer.Deserialize<RoutingRule>("""{"field":"department_type","required":true,"allow_llm_fill":false}""");
23+
24+
Assert.NotNull(rule);
25+
Assert.False(rule.AllowLlmFill);
26+
Assert.False(rule.Clone().AllowLlmFill);
27+
}
28+
29+
[Fact]
30+
public void MergeRoutingRules_preserves_existing_llm_fill_flag_for_matching_rule()
31+
{
32+
var existingRules = new List<RoutingRule>
33+
{
34+
new()
35+
{
36+
Field = "department_type",
37+
Type = "data_validation",
38+
Required = true,
39+
AllowLlmFill = false
40+
}
41+
};
42+
var incomingRules = new List<RoutingRule>
43+
{
44+
new()
45+
{
46+
Field = "department_type",
47+
Type = "data_validation",
48+
Required = true,
49+
AllowLlmFill = true
50+
}
51+
};
52+
53+
var mergedRules = MergeRoutingRules(existingRules, incomingRules);
54+
55+
Assert.False(mergedRules.Single().AllowLlmFill);
56+
}
57+
58+
[Fact]
59+
public void MergeRoutingRules_uses_incoming_llm_fill_flag_for_new_rule()
60+
{
61+
var incomingRules = new List<RoutingRule>
62+
{
63+
new()
64+
{
65+
Field = "new_field",
66+
Type = "data_validation",
67+
Required = true,
68+
AllowLlmFill = false
69+
}
70+
};
71+
72+
var mergedRules = MergeRoutingRules(existingRules: [], incomingRules);
73+
74+
Assert.False(mergedRules.Single().AllowLlmFill);
75+
}
76+
77+
private static List<RoutingRule> MergeRoutingRules(List<RoutingRule>? existingRules, List<RoutingRule>? incomingRules)
78+
{
79+
var method = typeof(AgentService).GetMethod("MergeRoutingRules", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
80+
Assert.NotNull(method);
81+
82+
return Assert.IsType<List<RoutingRule>>(method.Invoke(null, [existingRules, incomingRules]));
83+
}
84+
}

src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRule.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ public class RoutingRule
2323

2424
public bool Required { get; set; }
2525

26+
[JsonPropertyName("allow_llm_fill")]
27+
public bool AllowLlmFill { get; set; } = true;
28+
2629
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2730
public string? RedirectTo { get; set; }
2831

@@ -51,6 +54,7 @@ public RoutingRule()
5154
Description = Description,
5255
FieldType = FieldType,
5356
Required = Required,
57+
AllowLlmFill = AllowLlmFill,
5458
RedirectTo = RedirectTo,
5559
RedirectToAgentName = RedirectToAgentName
5660
};

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using BotSharp.Abstraction.Routing.Models;
12
using BotSharp.Abstraction.Users.Enums;
23
using BotSharp.Abstraction.Users.Models;
34

@@ -32,7 +33,7 @@ public async Task UpdateAgent(Agent agent, AgentField updateField)
3233
record.FuncVisMode = agent.FuncVisMode;
3334
record.Profiles = agent.Profiles ?? [];
3435
record.Labels = agent.Labels ?? [];
35-
record.RoutingRules = agent.RoutingRules ?? [];
36+
record.RoutingRules = MergeRoutingRules(record.RoutingRules, agent.RoutingRules);
3637
record.Instruction = agent.Instruction ?? string.Empty;
3738
record.ChannelInstructions = agent.ChannelInstructions ?? [];
3839
record.Functions = agent.Functions ?? [];
@@ -54,6 +55,28 @@ public async Task UpdateAgent(Agent agent, AgentField updateField)
5455
await Task.CompletedTask;
5556
}
5657

58+
private static List<RoutingRule> MergeRoutingRules(List<RoutingRule>? existingRules, List<RoutingRule>? incomingRules)
59+
{
60+
if (incomingRules == null)
61+
{
62+
return [];
63+
}
64+
65+
foreach (var incomingRule in incomingRules)
66+
{
67+
var existingRule = existingRules?.FirstOrDefault(x =>
68+
string.Equals(x.Field, incomingRule.Field, StringComparison.OrdinalIgnoreCase) &&
69+
string.Equals(x.Type, incomingRule.Type, StringComparison.OrdinalIgnoreCase));
70+
71+
if (existingRule != null)
72+
{
73+
incomingRule.AllowLlmFill = existingRule.AllowLlmFill;
74+
}
75+
}
76+
77+
return incomingRules;
78+
}
79+
5780

5881
public async Task<string> PatchAgentTemplate(Agent agent)
5982
{

src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,13 @@ public async Task<RoutableAgent[]> GetRoutableAgents(List<string> profiles)
113113
Type = x.Type,
114114
Profiles = x.Profiles,
115115
RequiredFields = x.RoutingRules
116-
.Where(p => p.Required)
116+
.Where(p => p.Required && p.AllowLlmFill)
117117
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.FieldType)
118118
{
119119
Required = p.Required
120120
}).ToList(),
121121
OptionalFields = x.RoutingRules
122-
.Where(p => !p.Required)
122+
.Where(p => !p.Required && p.AllowLlmFill)
123123
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.FieldType)
124124
{
125125
Required = p.Required

src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/RoutingRuleUpdateModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public class RoutingRuleUpdateModel
1111
[JsonPropertyName("field_type")]
1212
public string? FieldType { get; set; }
1313
public bool Required { get; set; }
14+
[JsonPropertyName("allow_llm_fill")]
15+
public bool? AllowLlmFill { get; set; }
1416
public string? RedirectTo { get; set; }
1517

1618
public RoutingRuleUpdateModel()
@@ -27,6 +29,7 @@ public static RoutingRule ToDomainElement(RoutingRuleUpdateModel model)
2729
Type = model.Type,
2830
FieldType = model.FieldType,
2931
Required = model.Required,
32+
AllowLlmFill = model.AllowLlmFill ?? true,
3033
RedirectTo = model.RedirectTo
3134
};
3235
}

src/Plugins/BotSharp.Plugin.MongoStorage/Models/RoutingRuleMongoElement.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public class RoutingRuleMongoElement
1111
public string? RedirectTo { get; set; }
1212
public string Type { get; set; } = default!;
1313
public string FieldType { get; set; } = default!;
14+
public bool AllowLlmFill { get; set; } = true;
1415

1516
public static RoutingRuleMongoElement ToMongoElement(RoutingRule routingRule)
1617
{
@@ -21,7 +22,8 @@ public static RoutingRuleMongoElement ToMongoElement(RoutingRule routingRule)
2122
Required = routingRule.Required,
2223
RedirectTo = routingRule.RedirectTo,
2324
Type = routingRule.Type,
24-
FieldType = routingRule.FieldType
25+
FieldType = routingRule.FieldType,
26+
AllowLlmFill = routingRule.AllowLlmFill
2527
};
2628
}
2729

@@ -36,7 +38,8 @@ public static RoutingRule ToDomainElement(string agentId, string agentName, Rout
3638
Required = rule.Required,
3739
RedirectTo = rule.RedirectTo,
3840
Type = rule.Type,
39-
FieldType= rule.FieldType
41+
FieldType = rule.FieldType,
42+
AllowLlmFill = rule.AllowLlmFill
4043
};
4144
}
4245

0 commit comments

Comments
 (0)