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
15 changes: 8 additions & 7 deletions src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Plugins.Models;
using BotSharp.Abstraction.Repositories.Filters;
using System.Collections.Concurrent;

namespace BotSharp.Abstraction.Agents;

Expand Down Expand Up @@ -30,18 +31,18 @@ public interface IAgentService
/// <returns></returns>
Task InheritAgent(Agent agent);

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

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

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

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

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

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


/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public class ImageGenerationSetting
public class ImageEditSetting
{
public ModelSettingBase? Size { get; set; }
public ModelSettingBase? Quality { get; set; }
public ModelSettingBase? ResponseFormat { get; set; }
public ModelSettingBase? Background { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ public class BotSharpOptions
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
AllowTrailingCommas = true,
WriteIndented = true
WriteIndented = true,
ReferenceHandler = ReferenceHandler.IgnoreCycles
};


private JsonSerializerOptions _jsonSerializerOptions;

public JsonSerializerOptions JsonSerializerOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ namespace BotSharp.Abstraction.Templating;

public interface ITemplateRender
{
string Render(string template, Dictionary<string, object> dict);
string Render(string template, IDictionary<string, object> dict);
void RegisterType(Type type);
}
Original file line number Diff line number Diff line change
@@ -1,55 +1,41 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BotSharp.Abstraction.Options;
using System.Text.Json;
using System.Threading.Tasks;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace BotSharp.Abstraction.Utilities
namespace BotSharp.Abstraction.Utilities;

public static class ObjectExtensions
{
public static class ObjectExtensions
private static readonly JsonSerializerOptions DefaultJsonOptions = new()
{
private static readonly JsonSerializerOptions DefaultOptions = new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles
};
private static JsonSerializerSettings DefaultSettings = new()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
AllowTrailingCommas = true,
ReferenceHandler = ReferenceHandler.IgnoreCycles
};

public static T DeepClone<T>(this T obj) where T : class
public static T? DeepClone<T>(this T? obj, Action<T>? modifier = null, BotSharpOptions? options = null) where T : class
{
if (obj == null)
{
if (obj == null) return null;

try
{
var json = JsonSerializer.Serialize(obj, DefaultOptions);
return JsonSerializer.Deserialize<T>(json, DefaultOptions);
}
catch(Exception ex)
{
Console.WriteLine($"DeepClone Error:{ex}");
return DeepCloneWithNewtonsoft(obj);
}
return null;
}

private static T DeepCloneWithNewtonsoft<T>(this T obj) where T : class
try
{
if (obj == null) return null;

try
var json = JsonSerializer.Serialize(obj, options?.JsonSerializerOptions ?? DefaultJsonOptions);
var newObj = JsonSerializer.Deserialize<T>(json, options?.JsonSerializerOptions ?? DefaultJsonOptions);
if (modifier != null && newObj != null)
{
var json = JsonConvert.SerializeObject(obj, DefaultSettings);
return JsonConvert.DeserializeObject<T>(json, DefaultSettings);
modifier(newObj);
}
catch(Exception ex)
{
Console.WriteLine($"DeepClone Error:{ex}");
return obj;
}

return newObj;
}
catch (Exception ex)
{
Console.WriteLine($"DeepClone Error in {nameof(DeepClone)}: {ex}");
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private List<FunctionDef> GetFunctionsFromFile(string fileDir)
if (extension != "json") continue;

var json = File.ReadAllText(file);
var function = JsonSerializer.Deserialize<FunctionDef>(json, _options);
var function = JsonSerializer.Deserialize<FunctionDef>(json, _options.JsonSerializerOptions);
functions.Add(function);
}
catch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public async Task<List<IdName>> GetAgentOptions(List<string>? agentIdsOrNames, b
return agents?.Select(x => new IdName(x.Id, x.Name))?.OrderBy(x => x.Name)?.ToList() ?? [];
}

[SharpCache(10)]
[SharpCache(10, perInstanceCache: true)]
public async Task<Agent> GetAgent(string id)
{
if (string.IsNullOrWhiteSpace(id) || id == Guid.Empty.ToString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,29 @@ public partial class AgentService
// [SharpCache(10, perInstanceCache: true)]
public async Task<Agent> LoadAgent(string id, bool loadUtility = true)
{
if (string.IsNullOrEmpty(id) || id == Guid.Empty.ToString()) return null;
if (string.IsNullOrEmpty(id) || id == Guid.Empty.ToString())
{
return null;
}

HookEmitter.Emit<IAgentHook>(_services, hook => hook.OnAgentLoading(ref id), id);

var agent = (await GetAgent(id)).DeepClone();
if (agent == null) return null;
var originalAgent = await GetAgent(id);
var agent = originalAgent.DeepClone(modifier: agt =>
{
agt.ChannelInstructions = originalAgent.ChannelInstructions.DeepClone() ?? new();
agt.Functions = originalAgent.Functions.DeepClone() ?? new();
agt.Templates = originalAgent.Templates.DeepClone() ?? new();
agt.Samples = originalAgent.Samples.DeepClone() ?? new();
agt.Responses = originalAgent.Responses.DeepClone() ?? new();
agt.LlmConfig = originalAgent.LlmConfig.DeepClone() ?? new();
agt.Plugin = originalAgent.Plugin.DeepClone() ?? new();
});

if (agent == null)
{
return null;
}

agent.TemplateDict = [];
agent.SecondaryInstructions = [];
Expand All @@ -25,16 +42,18 @@ public async Task<Agent> LoadAgent(string id, bool loadUtility = true)
OverrideInstructionByChannel(agent);
AddOrUpdateParameters(agent);

// Populate state into dictionary
PopulateState(agent.TemplateDict);
// Populate state
PopulateState(agent);

// After agent is loaded
HookEmitter.Emit<IAgentHook>(_services, hook => {
hook.SetAgent(agent);

if (!string.IsNullOrEmpty(agent.Instruction))
{
hook.OnInstructionLoaded(agent.Instruction, agent.TemplateDict);
var dict = new Dictionary<string, object>(agent.TemplateDict);
hook.OnInstructionLoaded(agent.Instruction, dict);
agent.TemplateDict = new Dictionary<string, object>(dict);
}

if (agent.Functions != null)
Expand Down Expand Up @@ -69,7 +88,10 @@ public async Task<Agent> LoadAgent(string id, bool loadUtility = true)
private void OverrideInstructionByChannel(Agent agent)
{
var instructions = agent.ChannelInstructions;
if (instructions.IsNullOrEmpty()) return;
if (instructions.IsNullOrEmpty())
{
return;
}

var state = _services.GetRequiredService<IConversationStateService>();
var channel = state.GetState("channel");
Expand All @@ -79,19 +101,19 @@ private void OverrideInstructionByChannel(Agent agent)
agent.Instruction = !string.IsNullOrWhiteSpace(found?.Instruction) ? found.Instruction : defaultInstruction?.Instruction;
}

private void PopulateState(Dictionary<string, object> dict)
private void PopulateState(Agent agent)
{
var conv = _services.GetRequiredService<IConversationService>();
foreach (var t in conv.States.GetStates())
{
dict[t.Key] = t.Value;
}
var dict = CollectRenderData(agent);
agent.TemplateDict = new Dictionary<string, object>(dict);
}

private void AddOrUpdateParameters(Agent agent)
{
var agentId = agent.Id ?? agent.Name;
if (AgentParameterTypes.ContainsKey(agentId)) return;
if (AgentParameterTypes.ContainsKey(agentId))
{
return;
}

AddOrUpdateRoutesParameters(agentId, agent.RoutingRules);
AddOrUpdateFunctionsParameters(agentId, agent.Functions);
Expand All @@ -103,7 +125,10 @@ private void AddOrUpdateRoutesParameters(string agentId, List<RoutingRule> routi

foreach (var rule in routingRules.Where(x => x.Required))
{
if (string.IsNullOrEmpty(rule.FieldType)) continue;
if (string.IsNullOrEmpty(rule.FieldType))
{
continue;
}
parameterTypes[rule.Field] = rule.FieldType;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public async Task<string> RefreshAgents(IEnumerable<string>? agentIds = null)
try
{
var agentJson = File.ReadAllText(Path.Combine(dir, "agent.json"));
var agent = JsonSerializer.Deserialize<Agent>(agentJson, _options);
var agent = JsonSerializer.Deserialize<Agent>(agentJson, _options.JsonSerializerOptions);

if (agent == null)
{
Expand Down
Loading
Loading