Skip to content

Commit f4b669d

Browse files
authored
Merge pull request #1173 from iceljc/master
fix template rendering by states
2 parents 6581b2e + 8b92892 commit f4b669d

File tree

16 files changed

+122
-100
lines changed

16 files changed

+122
-100
lines changed

src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,18 @@ public interface IAgentService
2929
/// <returns></returns>
3030
Task InheritAgent(Agent agent);
3131

32-
string RenderInstruction(Agent agent);
32+
string RenderInstruction(Agent agent, Dictionary<string, object>? renderData = null);
3333

34-
string RenderTemplate(Agent agent, string templateName);
34+
string RenderTemplate(Agent agent, string templateName, Dictionary<string, object>? renderData = null);
3535

36-
bool RenderFunction(Agent agent, FunctionDef def);
36+
bool RenderFunction(Agent agent, FunctionDef def, Dictionary<string, object>? renderData = null);
3737

38-
FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def);
38+
FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def, Dictionary<string, object>? renderData = null);
3939

40-
(string, IEnumerable<FunctionDef>) PrepareInstructionAndFunctions(Agent agent, StringComparer? comparer = null);
41-
IEnumerable<FunctionDef> FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null);
42-
IEnumerable<FunctionDef> FilterFunctions(string instruction, IEnumerable<FunctionDef> functions, StringComparer? comparer = null);
40+
(string, IEnumerable<FunctionDef>) PrepareInstructionAndFunctions(Agent agent, Dictionary<string, object>? renderData = null, StringComparer? comparer = null);
4341

4442
bool RenderVisibility(string? visibilityExpression, Dictionary<string, object> dict);
43+
Dictionary<string, object> CollectRenderData(Agent agent);
4544

4645

4746
/// <summary>

src/Infrastructure/BotSharp.Core/Agents/Hooks/BasicAgentHook.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,18 @@ public override void OnAgentUtilityLoaded(Agent agent)
6363
var agentService = _services.GetRequiredService<IAgentService>();
6464
var innerUtilities = utilities!.Where(x => !string.IsNullOrEmpty(x.Name) && !x.Disabled).ToList();
6565

66+
var renderDict = agentService.CollectRenderData(agent);
6667
var functionNames = new List<string>();
6768
var templateNames = new List<string>();
6869

6970
foreach (var utility in innerUtilities)
7071
{
71-
var isVisible = agentService.RenderVisibility(utility.VisibilityExpression, agent.TemplateDict);
72+
var isVisible = agentService.RenderVisibility(utility.VisibilityExpression, renderDict);
7273
if (!isVisible || utility.Items.IsNullOrEmpty()) continue;
7374

7475
foreach (var item in utility.Items)
7576
{
76-
isVisible = agentService.RenderVisibility(item.VisibilityExpression, agent.TemplateDict);
77+
isVisible = agentService.RenderVisibility(item.VisibilityExpression, renderDict);
7778
if (!isVisible) continue;
7879

7980
if (item.FunctionName?.StartsWith(UTIL_PREFIX) == true)

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

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace BotSharp.Core.Agents.Services;
77

88
public partial class AgentService
99
{
10-
public string RenderInstruction(Agent agent)
10+
public string RenderInstruction(Agent agent, Dictionary<string, object>? renderData = null)
1111
{
1212
var render = _services.GetRequiredService<ITemplateRender>();
1313
var conv = _services.GetRequiredService<IConversationService>();
@@ -18,19 +18,16 @@ public string RenderInstruction(Agent agent)
1818
instructions.AddRange(secondaryInstructions);
1919

2020
// update states
21-
var renderDict = new Dictionary<string, object>(agent.TemplateDict);
22-
foreach (var t in conv.States.GetStates())
23-
{
24-
renderDict[t.Key] = t.Value;
25-
}
26-
21+
var renderDict = renderData != null ? new Dictionary<string, object>(renderData ?? []) : CollectRenderData(agent);
2722
renderDict[TemplateRenderConstant.RENDER_AGENT] = agent;
23+
2824
var res = render.Render(string.Join("\r\n", instructions), renderDict);
2925
return res;
3026
}
3127

32-
public bool RenderFunction(Agent agent, FunctionDef def)
28+
public bool RenderFunction(Agent agent, FunctionDef def, Dictionary<string, object>? renderData = null)
3329
{
30+
var renderDict = renderData ?? agent.TemplateDict;
3431
var isRender = true;
3532

3633
var channels = def.Channels;
@@ -48,18 +45,19 @@ public bool RenderFunction(Agent agent, FunctionDef def)
4845

4946
if (!string.IsNullOrWhiteSpace(def.VisibilityExpression))
5047
{
51-
isRender = RenderVisibility(def.VisibilityExpression, agent.TemplateDict);
48+
isRender = RenderVisibility(def.VisibilityExpression, renderDict);
5249
}
5350

5451
return isRender;
5552
}
5653

57-
public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def)
54+
public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def, Dictionary<string, object>? renderData = null)
5855
{
5956
var parameterDef = def?.Parameters;
6057
var propertyDef = parameterDef?.Properties;
6158
if (propertyDef == null) return null;
6259

60+
var renderDict = renderData ?? agent.TemplateDict;
6361
var visibleExpress = "visibility_expression";
6462
var root = propertyDef.RootElement;
6563
var iterator = root.EnumerateObject();
@@ -73,7 +71,7 @@ public bool RenderFunction(Agent agent, FunctionDef def)
7371
if (node.TryGetProperty(visibleExpress, out var element))
7472
{
7573
var expression = element.GetString();
76-
matched = RenderVisibility(expression, agent.TemplateDict);
74+
matched = RenderVisibility(expression, renderDict);
7775
}
7876

7977
if (matched)
@@ -107,52 +105,31 @@ public bool RenderFunction(Agent agent, FunctionDef def)
107105
return parameterDef;
108106
}
109107

110-
public (string, IEnumerable<FunctionDef>) PrepareInstructionAndFunctions(Agent agent, StringComparer? comparer = null)
108+
public (string, IEnumerable<FunctionDef>) PrepareInstructionAndFunctions(Agent agent, Dictionary<string, object>? renderData = null, StringComparer ? comparer = null)
111109
{
112110
var text = string.Empty;
113111
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
114112
{
115-
text = RenderInstruction(agent);
113+
text = RenderInstruction(agent, renderData);
116114
}
117115

118116
var functions = FilterFunctions(text, agent, comparer);
119117
return (text, functions);
120118
}
121119

122-
public IEnumerable<FunctionDef> FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null)
123-
{
124-
var functions = agent.Functions.Concat(agent.SecondaryFunctions ?? []);
125-
if (agent.FuncVisMode.IsEqualTo(AgentFuncVisMode.Auto) && !string.IsNullOrWhiteSpace(instruction))
126-
{
127-
functions = FilterFunctions(instruction, functions, comparer);
128-
}
129-
return functions;
130-
}
131-
132-
public IEnumerable<FunctionDef> FilterFunctions(string instruction, IEnumerable<FunctionDef> functions, StringComparer? comparer = null)
133-
{
134-
comparer = comparer ?? StringComparer.OrdinalIgnoreCase;
135-
var matches = Regex.Matches(instruction, @"\b[A-Za-z0-9_-]+\b");
136-
var words = new HashSet<string>(matches.Select(m => m.Value), comparer);
137-
return functions.Where(x => words.Contains(x.Name, comparer));
138-
}
139-
140-
public string RenderTemplate(Agent agent, string templateName)
120+
public string RenderTemplate(Agent agent, string templateName, Dictionary<string, object>? renderData = null)
141121
{
142122
var conv = _services.GetRequiredService<IConversationService>();
143123
var render = _services.GetRequiredService<ITemplateRender>();
144124

145125
var template = agent.Templates.FirstOrDefault(x => x.Name == templateName)?.Content ?? string.Empty;
146126

147127
// update states
148-
foreach (var t in conv.States.GetStates())
149-
{
150-
agent.TemplateDict[t.Key] = t.Value;
151-
}
128+
var renderDict = renderData != null ? new Dictionary<string, object>(renderData ?? []) : CollectRenderData(agent);
129+
renderDict[TemplateRenderConstant.RENDER_AGENT] = agent;
152130

153131
// render liquid template
154-
agent.TemplateDict[TemplateRenderConstant.RENDER_AGENT] = agent;
155-
var content = render.Render(template, agent.TemplateDict);
132+
var content = render.Render(template, renderDict);
156133

157134
HookEmitter.Emit<IContentGeneratingHook>(_services, async hook => await hook.OnRenderingTemplate(agent, templateName, content),
158135
agent.Id).Wait();
@@ -175,4 +152,37 @@ public bool RenderVisibility(string? visibilityExpression, Dictionary<string, ob
175152

176153
return result.IsEqualTo("visible");
177154
}
155+
156+
public Dictionary<string, object> CollectRenderData(Agent agent)
157+
{
158+
var state = _services.GetRequiredService<IConversationStateService>();
159+
160+
var renderDict = new Dictionary<string, object>(agent?.TemplateDict ?? []);
161+
foreach (var t in state.GetStates())
162+
{
163+
renderDict[t.Key] = t.Value;
164+
}
165+
166+
return renderDict;
167+
}
168+
169+
#region Private methods
170+
private IEnumerable<FunctionDef> FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null)
171+
{
172+
var functions = agent.Functions.Concat(agent.SecondaryFunctions ?? []);
173+
if (agent.FuncVisMode.IsEqualTo(AgentFuncVisMode.Auto) && !string.IsNullOrWhiteSpace(instruction))
174+
{
175+
functions = FilterFunctions(instruction, functions, comparer);
176+
}
177+
return functions;
178+
}
179+
180+
private IEnumerable<FunctionDef> FilterFunctions(string instruction, IEnumerable<FunctionDef> functions, StringComparer? comparer = null)
181+
{
182+
comparer = comparer ?? StringComparer.OrdinalIgnoreCase;
183+
var matches = Regex.Matches(instruction, @"\b[A-Za-z0-9_-]+\b");
184+
var words = new HashSet<string>(matches.Select(m => m.Value), comparer);
185+
return functions.Where(x => words.Contains(x.Name, comparer));
186+
}
187+
#endregion
178188
}

src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ public Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent, List<
108108
renderedInstructions = [];
109109

110110
// Prepare instruction and functions
111-
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
111+
var renderData = agentService.CollectRenderData(agent);
112+
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
112113
if (!string.IsNullOrWhiteSpace(instruction))
113114
{
114115
renderedInstructions.Add(instruction);

src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,8 @@ public async Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent,
372372
var options = InitChatCompletionOption(agent);
373373

374374
// Prepare instruction and functions
375-
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
375+
var renderData = agentService.CollectRenderData(agent);
376+
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
376377
if (!string.IsNullOrWhiteSpace(instruction))
377378
{
378379
renderedInstructions.Add(instruction);
@@ -384,9 +385,12 @@ public async Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent,
384385
{
385386
foreach (var function in functions)
386387
{
387-
if (!agentService.RenderFunction(agent, function)) continue;
388+
if (!agentService.RenderFunction(agent, function, renderData))
389+
{
390+
continue;
391+
}
388392

389-
var property = agentService.RenderFunctionProperty(agent, function);
393+
var property = agentService.RenderFunctionProperty(agent, function, renderData);
390394

391395
options.Tools.Add(ChatTool.CreateFunctionTool(
392396
functionName: function.Name,
@@ -395,18 +399,6 @@ public async Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent,
395399
}
396400
}
397401

398-
foreach (var function in functions)
399-
{
400-
if (!agentService.RenderFunction(agent, function)) continue;
401-
402-
var property = agentService.RenderFunctionProperty(agent, function);
403-
404-
options.Tools.Add(ChatTool.CreateFunctionTool(
405-
functionName: function.Name,
406-
functionDescription: function.Description,
407-
functionParameters: BinaryData.FromObjectAsJson(property)));
408-
}
409-
410402
if (!string.IsNullOrEmpty(agent.Knowledges))
411403
{
412404
messages.Add(new SystemChatMessage(agent.Knowledges));

src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,8 @@ public void SetModelName(string model)
345345
var options = InitChatCompletionOption(agent);
346346

347347
// Prepare instruction and functions
348-
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
348+
var renderData = agentService.CollectRenderData(agent);
349+
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
349350
if (!string.IsNullOrWhiteSpace(instruction))
350351
{
351352
renderedInstructions.Add(instruction);
@@ -357,9 +358,12 @@ public void SetModelName(string model)
357358
{
358359
foreach (var function in functions)
359360
{
360-
if (!agentService.RenderFunction(agent, function)) continue;
361+
if (!agentService.RenderFunction(agent, function, renderData))
362+
{
363+
continue;
364+
}
361365

362-
var property = agentService.RenderFunctionProperty(agent, function);
366+
var property = agentService.RenderFunctionProperty(agent, function, renderData);
363367

364368
options.Tools.Add(ChatTool.CreateFunctionTool(
365369
functionName: function.Name,

src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ public void SetModelName(string model)
198198
var funcPrompts = new List<string>();
199199

200200
// Prepare instruction and functions
201-
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
201+
var renderData = agentService.CollectRenderData(agent);
202+
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
202203
if (!string.IsNullOrWhiteSpace(instruction))
203204
{
204205
renderedInstructions.Add(instruction);
@@ -207,9 +208,12 @@ public void SetModelName(string model)
207208

208209
foreach (var function in functions)
209210
{
210-
if (!agentService.RenderFunction(agent, function)) continue;
211+
if (!agentService.RenderFunction(agent, function, renderData))
212+
{
213+
continue;
214+
}
211215

212-
var def = agentService.RenderFunctionProperty(agent, function);
216+
var def = agentService.RenderFunctionProperty(agent, function, renderData);
213217
var props = JsonSerializer.Serialize(def?.Properties);
214218
var parameters = !string.IsNullOrWhiteSpace(props) && props != "{}" ? new Schema()
215219
{

src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDial
101101
var router = routing.Router;
102102

103103
// Prepare instruction and functions
104-
var (prompt, functions) = agentService.PrepareInstructionAndFunctions(agent);
104+
var renderData = agentService.CollectRenderData(agent);
105+
var (prompt, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
105106
if (!string.IsNullOrWhiteSpace(prompt))
106107
{
107108
renderedInstructions.Add(prompt);

src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,8 @@ await hook.AfterGenerated(new RoleDialogModel(AgentRole.Assistant, text)
492492
var funcPrompts = new List<string>();
493493

494494
// Prepare instruction and functions
495-
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
495+
var renderData = agentService.CollectRenderData(agent);
496+
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
496497
if (!string.IsNullOrWhiteSpace(instruction))
497498
{
498499
renderedInstructions.Add(instruction);
@@ -501,9 +502,12 @@ await hook.AfterGenerated(new RoleDialogModel(AgentRole.Assistant, text)
501502

502503
foreach (var function in functions)
503504
{
504-
if (!agentService.RenderFunction(agent, function)) continue;
505+
if (!agentService.RenderFunction(agent, function, renderData))
506+
{
507+
continue;
508+
}
505509

506-
var def = agentService.RenderFunctionProperty(agent, function);
510+
var def = agentService.RenderFunctionProperty(agent, function, renderData);
507511
var props = JsonSerializer.Serialize(def?.Properties);
508512
var parameters = !string.IsNullOrWhiteSpace(props) && props != "{}"
509513
? new Schema()

src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ private string PrepareOptions(Agent agent, List<RoleDialogModel> conversations,
9494
renderedInstructions = [];
9595

9696
// Prepare instruction and functions
97-
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
97+
var renderData = agentService.CollectRenderData(agent);
98+
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
9899
if (!string.IsNullOrWhiteSpace(instruction))
99100
{
100101
renderedInstructions.Add(instruction);

0 commit comments

Comments
 (0)