Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,18 @@ public interface IAgentService
/// <returns></returns>
Task InheritAgent(Agent agent);

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

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

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

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

(string, IEnumerable<FunctionDef>) PrepareInstructionAndFunctions(Agent agent, StringComparer? comparer = null);
IEnumerable<FunctionDef> FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null);
IEnumerable<FunctionDef> FilterFunctions(string instruction, IEnumerable<FunctionDef> functions, StringComparer? comparer = null);
(string, IEnumerable<FunctionDef>) PrepareInstructionAndFunctions(Agent agent, Dictionary<string, object>? renderData = null, StringComparer? comparer = null);

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


/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,18 @@ public override void OnAgentUtilityLoaded(Agent agent)
var agentService = _services.GetRequiredService<IAgentService>();
var innerUtilities = utilities!.Where(x => !string.IsNullOrEmpty(x.Name) && !x.Disabled).ToList();

var renderDict = agentService.CollectRenderData(agent);
var functionNames = new List<string>();
var templateNames = new List<string>();

foreach (var utility in innerUtilities)
{
var isVisible = agentService.RenderVisibility(utility.VisibilityExpression, agent.TemplateDict);
var isVisible = agentService.RenderVisibility(utility.VisibilityExpression, renderDict);
if (!isVisible || utility.Items.IsNullOrEmpty()) continue;

foreach (var item in utility.Items)
{
isVisible = agentService.RenderVisibility(item.VisibilityExpression, agent.TemplateDict);
isVisible = agentService.RenderVisibility(item.VisibilityExpression, renderDict);
if (!isVisible) continue;

if (item.FunctionName?.StartsWith(UTIL_PREFIX) == true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace BotSharp.Core.Agents.Services;

public partial class AgentService
{
public string RenderInstruction(Agent agent)
public string RenderInstruction(Agent agent, Dictionary<string, object>? renderData = null)
{
var render = _services.GetRequiredService<ITemplateRender>();
var conv = _services.GetRequiredService<IConversationService>();
Expand All @@ -18,19 +18,16 @@ public string RenderInstruction(Agent agent)
instructions.AddRange(secondaryInstructions);

// update states
var renderDict = new Dictionary<string, object>(agent.TemplateDict);
foreach (var t in conv.States.GetStates())
{
renderDict[t.Key] = t.Value;
}

var renderDict = renderData != null ? new Dictionary<string, object>(renderData ?? []) : CollectRenderData(agent);
renderDict[TemplateRenderConstant.RENDER_AGENT] = agent;

var res = render.Render(string.Join("\r\n", instructions), renderDict);
return res;
}

public bool RenderFunction(Agent agent, FunctionDef def)
public bool RenderFunction(Agent agent, FunctionDef def, Dictionary<string, object>? renderData = null)
{
var renderDict = renderData ?? agent.TemplateDict;
var isRender = true;

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

if (!string.IsNullOrWhiteSpace(def.VisibilityExpression))
{
isRender = RenderVisibility(def.VisibilityExpression, agent.TemplateDict);
isRender = RenderVisibility(def.VisibilityExpression, renderDict);
}

return isRender;
}

public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def)
public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def, Dictionary<string, object>? renderData = null)
{
var parameterDef = def?.Parameters;
var propertyDef = parameterDef?.Properties;
if (propertyDef == null) return null;

var renderDict = renderData ?? agent.TemplateDict;
var visibleExpress = "visibility_expression";
var root = propertyDef.RootElement;
var iterator = root.EnumerateObject();
Expand All @@ -73,7 +71,7 @@ public bool RenderFunction(Agent agent, FunctionDef def)
if (node.TryGetProperty(visibleExpress, out var element))
{
var expression = element.GetString();
matched = RenderVisibility(expression, agent.TemplateDict);
matched = RenderVisibility(expression, renderDict);
}

if (matched)
Expand Down Expand Up @@ -107,52 +105,31 @@ public bool RenderFunction(Agent agent, FunctionDef def)
return parameterDef;
}

public (string, IEnumerable<FunctionDef>) PrepareInstructionAndFunctions(Agent agent, StringComparer? comparer = null)
public (string, IEnumerable<FunctionDef>) PrepareInstructionAndFunctions(Agent agent, Dictionary<string, object>? renderData = null, StringComparer ? comparer = null)
{
var text = string.Empty;
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
{
text = RenderInstruction(agent);
text = RenderInstruction(agent, renderData);
}

var functions = FilterFunctions(text, agent, comparer);
return (text, functions);
}

public IEnumerable<FunctionDef> FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null)
{
var functions = agent.Functions.Concat(agent.SecondaryFunctions ?? []);
if (agent.FuncVisMode.IsEqualTo(AgentFuncVisMode.Auto) && !string.IsNullOrWhiteSpace(instruction))
{
functions = FilterFunctions(instruction, functions, comparer);
}
return functions;
}

public IEnumerable<FunctionDef> FilterFunctions(string instruction, IEnumerable<FunctionDef> functions, StringComparer? comparer = null)
{
comparer = comparer ?? StringComparer.OrdinalIgnoreCase;
var matches = Regex.Matches(instruction, @"\b[A-Za-z0-9_-]+\b");
var words = new HashSet<string>(matches.Select(m => m.Value), comparer);
return functions.Where(x => words.Contains(x.Name, comparer));
}

public string RenderTemplate(Agent agent, string templateName)
public string RenderTemplate(Agent agent, string templateName, Dictionary<string, object>? renderData = null)
{
var conv = _services.GetRequiredService<IConversationService>();
var render = _services.GetRequiredService<ITemplateRender>();

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

// update states
foreach (var t in conv.States.GetStates())
{
agent.TemplateDict[t.Key] = t.Value;
}
var renderDict = renderData != null ? new Dictionary<string, object>(renderData ?? []) : CollectRenderData(agent);
renderDict[TemplateRenderConstant.RENDER_AGENT] = agent;

// render liquid template
agent.TemplateDict[TemplateRenderConstant.RENDER_AGENT] = agent;
var content = render.Render(template, agent.TemplateDict);
var content = render.Render(template, renderDict);

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

return result.IsEqualTo("visible");
}

public Dictionary<string, object> CollectRenderData(Agent agent)
{
var state = _services.GetRequiredService<IConversationStateService>();

var renderDict = new Dictionary<string, object>(agent?.TemplateDict ?? []);
foreach (var t in state.GetStates())
{
renderDict[t.Key] = t.Value;
}

return renderDict;
}

#region Private methods
private IEnumerable<FunctionDef> FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null)
{
var functions = agent.Functions.Concat(agent.SecondaryFunctions ?? []);
if (agent.FuncVisMode.IsEqualTo(AgentFuncVisMode.Auto) && !string.IsNullOrWhiteSpace(instruction))
{
functions = FilterFunctions(instruction, functions, comparer);
}
return functions;
}

private IEnumerable<FunctionDef> FilterFunctions(string instruction, IEnumerable<FunctionDef> functions, StringComparer? comparer = null)
{
comparer = comparer ?? StringComparer.OrdinalIgnoreCase;
var matches = Regex.Matches(instruction, @"\b[A-Za-z0-9_-]+\b");
var words = new HashSet<string>(matches.Select(m => m.Value), comparer);
return functions.Where(x => words.Contains(x.Name, comparer));
}
#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ public Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent, List<
renderedInstructions = [];

// Prepare instruction and functions
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
var renderData = agentService.CollectRenderData(agent);
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
if (!string.IsNullOrWhiteSpace(instruction))
{
renderedInstructions.Add(instruction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,8 @@ public async Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent,
var options = InitChatCompletionOption(agent);

// Prepare instruction and functions
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
var renderData = agentService.CollectRenderData(agent);
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
if (!string.IsNullOrWhiteSpace(instruction))
{
renderedInstructions.Add(instruction);
Expand All @@ -384,9 +385,12 @@ public async Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent,
{
foreach (var function in functions)
{
if (!agentService.RenderFunction(agent, function)) continue;
if (!agentService.RenderFunction(agent, function, renderData))
{
continue;
}

var property = agentService.RenderFunctionProperty(agent, function);
var property = agentService.RenderFunctionProperty(agent, function, renderData);

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

foreach (var function in functions)
{
if (!agentService.RenderFunction(agent, function)) continue;

var property = agentService.RenderFunctionProperty(agent, function);

options.Tools.Add(ChatTool.CreateFunctionTool(
functionName: function.Name,
functionDescription: function.Description,
functionParameters: BinaryData.FromObjectAsJson(property)));
}

if (!string.IsNullOrEmpty(agent.Knowledges))
{
messages.Add(new SystemChatMessage(agent.Knowledges));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,8 @@ public void SetModelName(string model)
var options = InitChatCompletionOption(agent);

// Prepare instruction and functions
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
var renderData = agentService.CollectRenderData(agent);
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
if (!string.IsNullOrWhiteSpace(instruction))
{
renderedInstructions.Add(instruction);
Expand All @@ -357,9 +358,12 @@ public void SetModelName(string model)
{
foreach (var function in functions)
{
if (!agentService.RenderFunction(agent, function)) continue;
if (!agentService.RenderFunction(agent, function, renderData))
{
continue;
}

var property = agentService.RenderFunctionProperty(agent, function);
var property = agentService.RenderFunctionProperty(agent, function, renderData);

options.Tools.Add(ChatTool.CreateFunctionTool(
functionName: function.Name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ public void SetModelName(string model)
var funcPrompts = new List<string>();

// Prepare instruction and functions
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
var renderData = agentService.CollectRenderData(agent);
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
if (!string.IsNullOrWhiteSpace(instruction))
{
renderedInstructions.Add(instruction);
Expand All @@ -207,9 +208,12 @@ public void SetModelName(string model)

foreach (var function in functions)
{
if (!agentService.RenderFunction(agent, function)) continue;
if (!agentService.RenderFunction(agent, function, renderData))
{
continue;
}

var def = agentService.RenderFunctionProperty(agent, function);
var def = agentService.RenderFunctionProperty(agent, function, renderData);
var props = JsonSerializer.Serialize(def?.Properties);
var parameters = !string.IsNullOrWhiteSpace(props) && props != "{}" ? new Schema()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDial
var router = routing.Router;

// Prepare instruction and functions
var (prompt, functions) = agentService.PrepareInstructionAndFunctions(agent);
var renderData = agentService.CollectRenderData(agent);
var (prompt, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
if (!string.IsNullOrWhiteSpace(prompt))
{
renderedInstructions.Add(prompt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,8 @@ await hook.AfterGenerated(new RoleDialogModel(AgentRole.Assistant, text)
var funcPrompts = new List<string>();

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

foreach (var function in functions)
{
if (!agentService.RenderFunction(agent, function)) continue;
if (!agentService.RenderFunction(agent, function, renderData))
{
continue;
}

var def = agentService.RenderFunctionProperty(agent, function);
var def = agentService.RenderFunctionProperty(agent, function, renderData);
var props = JsonSerializer.Serialize(def?.Properties);
var parameters = !string.IsNullOrWhiteSpace(props) && props != "{}"
? new Schema()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ private string PrepareOptions(Agent agent, List<RoleDialogModel> conversations,
renderedInstructions = [];

// Prepare instruction and functions
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
var renderData = agentService.CollectRenderData(agent);
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
if (!string.IsNullOrWhiteSpace(instruction))
{
renderedInstructions.Add(instruction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDial
var agentService = _services.GetRequiredService<IAgentService>();

// Prepare instruction and functions
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
var renderData = agentService.CollectRenderData(agent);
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent, renderData);
if (!string.IsNullOrWhiteSpace(instruction))
{
renderedInstructions.Add(instruction);
Expand All @@ -82,9 +83,9 @@ public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDial

foreach (var function in functions)
{
if (agentService.RenderFunction(agent, function))
if (agentService.RenderFunction(agent, function, renderData))
{
var property = agentService.RenderFunctionProperty(agent, function);
var property = agentService.RenderFunctionProperty(agent, function, renderData);
(options.Tools ??= []).Add(new NopAIFunction(function.Name, function.Description, JsonSerializer.SerializeToElement(property)));
}
}
Expand Down
Loading
Loading