diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs
index e7b054d79..b6b106928 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs
@@ -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;
@@ -30,18 +31,18 @@ public interface IAgentService
///
Task InheritAgent(Agent agent);
- string RenderInstruction(Agent agent, Dictionary? renderData = null);
+ string RenderInstruction(Agent agent, IDictionary? renderData = null);
- string RenderTemplate(Agent agent, string templateName, Dictionary? renderData = null);
+ string RenderTemplate(Agent agent, string templateName, IDictionary? renderData = null);
- bool RenderFunction(Agent agent, FunctionDef def, Dictionary? renderData = null);
+ bool RenderFunction(Agent agent, FunctionDef def, IDictionary? renderData = null);
- FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def, Dictionary? renderData = null);
+ FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def, IDictionary? renderData = null);
- (string, IEnumerable) PrepareInstructionAndFunctions(Agent agent, Dictionary? renderData = null, StringComparer? comparer = null);
+ (string, IEnumerable) PrepareInstructionAndFunctions(Agent agent, IDictionary? renderData = null, StringComparer? comparer = null);
- bool RenderVisibility(string? visibilityExpression, Dictionary dict);
- Dictionary CollectRenderData(Agent agent);
+ bool RenderVisibility(string? visibilityExpression, IDictionary dict);
+ ConcurrentDictionary CollectRenderData(Agent agent);
///
diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs
index 1bc7ef743..3249b9eb4 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs
@@ -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; }
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Options/BotSharpOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Options/BotSharpOptions.cs
index b8609422d..63469bcd8 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Options/BotSharpOptions.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Options/BotSharpOptions.cs
@@ -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
diff --git a/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs b/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs
index fecc36c45..9a555f9c7 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs
@@ -2,6 +2,6 @@ namespace BotSharp.Abstraction.Templating;
public interface ITemplateRender
{
- string Render(string template, Dictionary dict);
+ string Render(string template, IDictionary dict);
void RegisterType(Type type);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/ObjectExtensions.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/ObjectExtensions.cs
index 7b3bca61b..bed3b71ab 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Utilities/ObjectExtensions.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/ObjectExtensions.cs
@@ -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(this T obj) where T : class
+ public static T? DeepClone(this T? obj, Action? 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(json, DefaultOptions);
- }
- catch(Exception ex)
- {
- Console.WriteLine($"DeepClone Error:{ex}");
- return DeepCloneWithNewtonsoft(obj);
- }
+ return null;
}
- private static T DeepCloneWithNewtonsoft(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(json, options?.JsonSerializerOptions ?? DefaultJsonOptions);
+ if (modifier != null && newObj != null)
{
- var json = JsonConvert.SerializeObject(obj, DefaultSettings);
- return JsonConvert.DeserializeObject(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;
+ }
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs
index d61f0f3dd..5404c0d44 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs
@@ -127,7 +127,7 @@ private List GetFunctionsFromFile(string fileDir)
if (extension != "json") continue;
var json = File.ReadAllText(file);
- var function = JsonSerializer.Deserialize(json, _options);
+ var function = JsonSerializer.Deserialize(json, _options.JsonSerializerOptions);
functions.Add(function);
}
catch
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs
index 5fae2184e..7c46427f7 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs
@@ -41,7 +41,7 @@ public async Task> GetAgentOptions(List? 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 GetAgent(string id)
{
if (string.IsNullOrWhiteSpace(id) || id == Guid.Empty.ToString())
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
index 2dcd15b77..53d9a05bc 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
@@ -10,12 +10,29 @@ public partial class AgentService
// [SharpCache(10, perInstanceCache: true)]
public async Task 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(_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 = [];
@@ -25,8 +42,8 @@ public async Task 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(_services, hook => {
@@ -34,7 +51,9 @@ public async Task LoadAgent(string id, bool loadUtility = true)
if (!string.IsNullOrEmpty(agent.Instruction))
{
- hook.OnInstructionLoaded(agent.Instruction, agent.TemplateDict);
+ var dict = new Dictionary(agent.TemplateDict);
+ hook.OnInstructionLoaded(agent.Instruction, dict);
+ agent.TemplateDict = new Dictionary(dict);
}
if (agent.Functions != null)
@@ -69,7 +88,10 @@ public async Task 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();
var channel = state.GetState("channel");
@@ -79,19 +101,19 @@ private void OverrideInstructionByChannel(Agent agent)
agent.Instruction = !string.IsNullOrWhiteSpace(found?.Instruction) ? found.Instruction : defaultInstruction?.Instruction;
}
- private void PopulateState(Dictionary dict)
+ private void PopulateState(Agent agent)
{
- var conv = _services.GetRequiredService();
- foreach (var t in conv.States.GetStates())
- {
- dict[t.Key] = t.Value;
- }
+ var dict = CollectRenderData(agent);
+ agent.TemplateDict = new Dictionary(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);
@@ -103,7 +125,10 @@ private void AddOrUpdateRoutesParameters(string agentId, List 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;
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
index cc38a151a..045c71cf5 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
@@ -39,7 +39,7 @@ public async Task RefreshAgents(IEnumerable? agentIds = null)
try
{
var agentJson = File.ReadAllText(Path.Combine(dir, "agent.json"));
- var agent = JsonSerializer.Deserialize(agentJson, _options);
+ var agent = JsonSerializer.Deserialize(agentJson, _options.JsonSerializerOptions);
if (agent == null)
{
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs
index 00f50a1db..5ab883864 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs
@@ -1,13 +1,14 @@
using BotSharp.Abstraction.Loggers;
using BotSharp.Abstraction.Templating;
using Newtonsoft.Json.Linq;
+using System.Collections.Concurrent;
using System.Text.RegularExpressions;
namespace BotSharp.Core.Agents.Services;
public partial class AgentService
{
- public string RenderInstruction(Agent agent, Dictionary? renderData = null)
+ public string RenderInstruction(Agent agent, IDictionary? renderData = null)
{
var render = _services.GetRequiredService();
var conv = _services.GetRequiredService();
@@ -18,16 +19,18 @@ public string RenderInstruction(Agent agent, Dictionary? renderD
instructions.AddRange(secondaryInstructions);
// update states
- var renderDict = renderData != null ? new Dictionary(renderData ?? []) : CollectRenderData(agent);
- renderDict[TemplateRenderConstant.RENDER_AGENT] = agent;
+ var renderDict = renderData != null
+ ? new ConcurrentDictionary(renderData)
+ : CollectRenderData(agent);
+ renderDict.AddOrUpdate(TemplateRenderConstant.RENDER_AGENT, agent, (key, curValue) => agent);
var res = render.Render(string.Join("\r\n", instructions), renderDict);
return res;
}
- public bool RenderFunction(Agent agent, FunctionDef def, Dictionary? renderData = null)
+ public bool RenderFunction(Agent agent, FunctionDef def, IDictionary? renderData = null)
{
- var renderDict = renderData ?? agent.TemplateDict;
+ var renderDict = new Dictionary(renderData ?? agent.TemplateDict ?? []);
var isRender = true;
var channels = def.Channels;
@@ -41,7 +44,10 @@ public bool RenderFunction(Agent agent, FunctionDef def, Dictionary? renderData = null)
+ public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def, IDictionary? renderData = null)
{
- var parameterDef = def?.Parameters;
+ var parameterDef = def?.Parameters?.DeepClone(options: _options);
var propertyDef = parameterDef?.Properties;
- if (propertyDef == null) return null;
+ if (propertyDef == null)
+ {
+ return null;
+ }
- var renderDict = renderData ?? agent.TemplateDict;
+ var renderDict = new Dictionary(renderData ?? agent.TemplateDict ?? []);
var visibleExpress = "visibility_expression";
var root = propertyDef.RootElement;
var iterator = root.EnumerateObject();
@@ -105,7 +114,7 @@ public bool RenderFunction(Agent agent, FunctionDef def, Dictionary) PrepareInstructionAndFunctions(Agent agent, Dictionary? renderData = null, StringComparer ? comparer = null)
+ public (string, IEnumerable) PrepareInstructionAndFunctions(Agent agent, IDictionary? renderData = null, StringComparer ? comparer = null)
{
var text = string.Empty;
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
@@ -117,7 +126,7 @@ public bool RenderFunction(Agent agent, FunctionDef def, Dictionary? renderData = null)
+ public string RenderTemplate(Agent agent, string templateName, IDictionary? renderData = null)
{
var conv = _services.GetRequiredService();
var render = _services.GetRequiredService();
@@ -125,8 +134,10 @@ public string RenderTemplate(Agent agent, string templateName, Dictionary x.Name == templateName)?.Content ?? string.Empty;
// update states
- var renderDict = renderData != null ? new Dictionary(renderData ?? []) : CollectRenderData(agent);
- renderDict[TemplateRenderConstant.RENDER_AGENT] = agent;
+ var renderDict = renderData != null
+ ? new ConcurrentDictionary(renderData)
+ : CollectRenderData(agent);
+ renderDict.AddOrUpdate(TemplateRenderConstant.RENDER_AGENT, agent, (key, curValue) => agent);
// render liquid template
var content = render.Render(template, renderDict);
@@ -137,7 +148,7 @@ public string RenderTemplate(Agent agent, string templateName, Dictionary dict)
+ public bool RenderVisibility(string? visibilityExpression, IDictionary dict)
{
if (string.IsNullOrWhiteSpace(visibilityExpression))
{
@@ -145,25 +156,38 @@ public bool RenderVisibility(string? visibilityExpression, Dictionary();
+ var copy = new Dictionary(dict);
var result = render.Render(visibilityExpression, new Dictionary
{
- { "states", dict ?? [] }
+ { "states", copy }
});
return result.IsEqualTo("visible");
}
- public Dictionary CollectRenderData(Agent agent)
+ public ConcurrentDictionary CollectRenderData(Agent agent)
{
var state = _services.GetRequiredService();
- var renderDict = new Dictionary(agent?.TemplateDict ?? []);
- foreach (var t in state.GetStates())
+ var innerDict = new ConcurrentDictionary();
+ if (agent?.TemplateDict != null)
+ {
+ var dict = new ConcurrentDictionary(agent.TemplateDict);
+ if (dict != null)
+ {
+ foreach (var p in dict)
+ {
+ innerDict.AddOrUpdate(p.Key, p.Value, (key, curValue) => p.Value);
+ }
+ }
+ }
+
+ foreach (var p in state.GetStates())
{
- renderDict[t.Key] = t.Value;
+ innerDict.AddOrUpdate(p.Key, p.Value, (key, curValue) => p.Value);
}
- return renderDict;
+ return innerDict;
}
#region Private methods
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs
index ca025447c..d06cc3764 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs
@@ -1,6 +1,6 @@
+using BotSharp.Abstraction.Options;
using BotSharp.Abstraction.Repositories.Settings;
using System.IO;
-using System.Reflection;
namespace BotSharp.Core.Agents.Services;
@@ -10,24 +10,19 @@ public partial class AgentService : IAgentService
private readonly IBotSharpRepository _db;
private readonly ILogger _logger;
private readonly AgentSettings _agentSettings;
- private readonly JsonSerializerOptions _options;
+ private readonly BotSharpOptions _options;
public AgentService(IServiceProvider services,
IBotSharpRepository db,
ILogger logger,
- AgentSettings agentSettings)
+ AgentSettings agentSettings,
+ BotSharpOptions options)
{
_services = services;
_db = db;
_logger = logger;
_agentSettings = agentSettings;
- _options = new JsonSerializerOptions
- {
- PropertyNameCaseInsensitive = true,
- PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
- WriteIndented = true,
- AllowTrailingCommas = true
- };
+ _options = options;
}
public string GetDataDir()
@@ -49,7 +44,10 @@ public string GetAgentDataDir(string agentId)
public async Task> GetUserAgents(string userId)
{
- if (string.IsNullOrEmpty(userId)) return [];
+ if (string.IsNullOrEmpty(userId))
+ {
+ return [];
+ }
var userAgents = _db.GetUserAgents(userId);
return userAgents;
diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/HookEmitter.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/HookEmitter.cs
index 3615faf52..66ce90446 100644
--- a/src/Infrastructure/BotSharp.Core/Infrastructures/HookEmitter.cs
+++ b/src/Infrastructure/BotSharp.Core/Infrastructures/HookEmitter.cs
@@ -1,4 +1,3 @@
-using BotSharp.Abstraction.Hooks;
using BotSharp.Abstraction.Infrastructures;
namespace BotSharp.Core.Infrastructures;
diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs
index 3556cabc6..43accacce 100644
--- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs
+++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs
@@ -23,7 +23,7 @@ public async Task Execute(
{
var agentService = _services.GetRequiredService();
var state = _services.GetRequiredService();
- Agent agent = await agentService.LoadAgent(agentId);
+ var agent = await agentService.LoadAgent(agentId);
var response = new InstructResult
{
diff --git a/src/Infrastructure/BotSharp.Core/MCP/Hooks/MCPToolAgentHook.cs b/src/Infrastructure/BotSharp.Core/MCP/Hooks/MCPToolAgentHook.cs
index 743bf4c06..b117555fd 100644
--- a/src/Infrastructure/BotSharp.Core/MCP/Hooks/MCPToolAgentHook.cs
+++ b/src/Infrastructure/BotSharp.Core/MCP/Hooks/MCPToolAgentHook.cs
@@ -27,7 +27,7 @@ public override void OnAgentMcpToolLoaded(Agent agent)
agent.SecondaryFunctions ??= [];
- var functions = GetMcpContent(agent).Result;
+ var functions = GetMcpContent(agent).ConfigureAwait(false).GetAwaiter().GetResult();
agent.SecondaryFunctions = agent.SecondaryFunctions.Concat(functions).DistinctBy(x => x.Name, StringComparer.OrdinalIgnoreCase).ToList();
}
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs
index 083ff85aa..96c703121 100644
--- a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs
+++ b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs
@@ -159,13 +159,13 @@ public PluginDef UpdatePluginStatus(IServiceProvider services, string id, bool e
var agentService = services.GetRequiredService();
foreach (var agentId in dependentAgentIds)
{
- var agent = agentService.LoadAgent(agentId).Result;
+ var agent = agentService.LoadAgent(agentId).ConfigureAwait(false).GetAwaiter().GetResult();
agent.Disabled = false;
agentService.UpdateAgent(agent, AgentField.Disabled);
if (agent.InheritAgentId != null)
{
- agent = agentService.LoadAgent(agent.InheritAgentId).Result;
+ agent = agentService.LoadAgent(agent.InheritAgentId).ConfigureAwait(false).GetAwaiter().GetResult();
agent.Disabled = false;
agentService.UpdateAgent(agent, AgentField.Disabled);
}
@@ -183,7 +183,7 @@ public PluginDef UpdatePluginStatus(IServiceProvider services, string id, bool e
var agentService = services.GetRequiredService();
foreach (var agentId in plugin.AgentIds)
{
- var agent = agentService.LoadAgent(agentId).Result;
+ var agent = agentService.LoadAgent(agentId).ConfigureAwait(false).GetAwaiter().GetResult();
if (agent != null)
{
agent.Disabled = true;
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs
index 53a54669e..3b1231be0 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs
@@ -74,7 +74,7 @@ public override bool OnFunctionsLoaded(List functions)
if (rule != null)
{
var agentService = _services.GetRequiredService();
- var redirectAgent = agentService.GetAgent(rule.RedirectTo).Result;
+ var redirectAgent = agentService.GetAgent(rule.RedirectTo).ConfigureAwait(false).GetAwaiter().GetResult();
var json = JsonSerializer.Serialize(new
{
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/ReasonerHelper.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/ReasonerHelper.cs
index a37e24077..278fd155b 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Reasoning/ReasonerHelper.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoning/ReasonerHelper.cs
@@ -12,7 +12,7 @@ public static void FixMalformedResponse(IServiceProvider services, FunctionCallF
var agents = agentService.GetAgents(new AgentFilter
{
Types = [AgentType.Task]
- }).Result.Items.ToList();
+ }).ConfigureAwait(false).GetAwaiter().GetResult().Items.ToList();
var malformed = false;
// Sometimes it populate malformed Function in Agent name
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
index 4212c08c5..167866ddb 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
@@ -46,7 +46,7 @@ public string OriginAgentId
{
Types = [AgentType.Routing],
Pager = new Pagination { Size = 100 }
- }).Result.Items.Select(x => x.Id).ToArray();
+ }).ConfigureAwait(false).GetAwaiter().GetResult().Items.Select(x => x.Id).ToArray();
}
return _stack.Where(x => !_routerAgentIds.Contains(x)).LastOrDefault() ?? string.Empty;
@@ -87,7 +87,7 @@ public void Push(string agentId, string? reason = null, bool updateLazyRouting =
if (!Guid.TryParse(agentId, out _))
{
var agentService = _services.GetRequiredService();
- var agents = agentService.GetAgentOptions([agentId], byName: true).Result;
+ var agents = agentService.GetAgentOptions([agentId], byName: true).ConfigureAwait(false).GetAwaiter().GetResult();
if (agents.Count > 0)
{
@@ -129,8 +129,8 @@ public void Pop(string? reason = null, bool updateLazyRouting = true)
}
// Run the routing rule
- var agency = _services.GetRequiredService();
- var agent = agency.GetAgent(currentAgentId).Result;
+ var agentService = _services.GetRequiredService();
+ var agent = agentService.GetAgent(currentAgentId).ConfigureAwait(false).GetAwaiter().GetResult();
var message = new RoleDialogModel(AgentRole.User, $"Try to route to agent {agent.Name}")
{
diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs
index 2124da16e..797b76fc2 100644
--- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs
+++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs
@@ -46,7 +46,7 @@ public TemplateRender(IServiceProvider services, ILogger logger)
});
}
- public string Render(string template, Dictionary dict)
+ public string Render(string template, IDictionary dict)
{
if (_parser.TryParse(template, out var t, out var error))
{
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ImageGenerationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ImageGenerationController.cs
index 6dd03f641..9d30faa79 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ImageGenerationController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ImageGenerationController.cs
@@ -27,7 +27,7 @@ public async Task ComposeImages([FromBody] ImageCompos
try
{
- if (request.Files.Length < 1)
+ if (request.Files.IsNullOrEmpty())
{
return new ImageGenerationViewModel { Message = "No image found" };
}
@@ -46,7 +46,7 @@ public async Task ComposeImages([FromBody] ImageCompos
}
catch (Exception ex)
{
- var error = $"Error in image edit. {ex.Message}";
+ var error = $"Error in image composition. {ex.Message}";
_logger.LogError(ex, error);
imageViewModel.Message = error;
return imageViewModel;
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
index 514666926..4ba2e30c0 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
@@ -1,11 +1,7 @@
-using BotSharp.Abstraction.Crontab;
using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Abstraction.MessageHub.Observers;
-using BotSharp.Core.MessageHub;
-using BotSharp.Core.MessageHub.Observers;
using BotSharp.Plugin.ChatHub.Hooks;
using BotSharp.Plugin.ChatHub.Observers;
-using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
namespace BotSharp.Plugin.ChatHub;
@@ -34,6 +30,5 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
services.AddScoped();
services.AddScoped();
services.AddScoped();
- services.AddScoped();
}
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs
deleted file mode 100644
index dd584f172..000000000
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using BotSharp.Abstraction.Conversations.Dtos;
-using BotSharp.Abstraction.Conversations.Enums;
-using BotSharp.Abstraction.Crontab;
-using BotSharp.Abstraction.Crontab.Models;
-using Microsoft.AspNetCore.SignalR;
-
-namespace BotSharp.Plugin.ChatHub.Hooks;
-
-public class ChatHubCrontabHook : ICrontabHook
-{
- private readonly IServiceProvider _services;
- private readonly IHubContext _chatHub;
- private readonly ILogger _logger;
- private readonly IUserIdentity _user;
- private readonly BotSharpOptions _options;
- private readonly ChatHubSettings _settings;
-
- public ChatHubCrontabHook(
- IServiceProvider services,
- IHubContext chatHub,
- ILogger logger,
- IUserIdentity user,
- BotSharpOptions options,
- ChatHubSettings settings)
- {
- _services = services;
- _chatHub = chatHub;
- _logger = logger;
- _user = user;
- _options = options;
- _settings = settings;
- }
-
- public async Task OnCronTriggered(CrontabItem item)
- {
- var data = new ChatResponseDto()
- {
- ConversationId = item.ConversationId,
- MessageId = Guid.NewGuid().ToString(),
- Text = item.ExecutionResult,
- Function = "",
- Sender = new()
- {
- FirstName = "Crontab",
- LastName = "AI",
- Role = AgentRole.Assistant
- }
- };
-
- await SendEvent(item, data);
- }
-
- private async Task SendEvent(CrontabItem item, ChatResponseDto data)
- {
- try
- {
- var json = JsonSerializer.Serialize(data, _options.JsonSerializerOptions);
- await _chatHub.Clients.User(item.UserId).SendAsync(ChatEvent.OnNotificationGenerated, json);
- }
- catch { }
- }
-}
diff --git a/src/Plugins/BotSharp.Plugin.ImageHandler/Functions/ComposeImageFn.cs b/src/Plugins/BotSharp.Plugin.ImageHandler/Functions/ComposeImageFn.cs
index 4c1544e09..177ab3c5b 100644
--- a/src/Plugins/BotSharp.Plugin.ImageHandler/Functions/ComposeImageFn.cs
+++ b/src/Plugins/BotSharp.Plugin.ImageHandler/Functions/ComposeImageFn.cs
@@ -11,7 +11,6 @@ public class ComposeImageFn : IFunctionCallback
private readonly ILogger _logger;
private readonly ImageHandlerSettings _settings;
- private Agent _agent;
private string _conversationId;
private string _messageId;
@@ -29,22 +28,23 @@ public async Task Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize(message.FunctionArgs);
var descrpition = args?.UserRequest ?? string.Empty;
- await Init(message);
+
+ var agentService = _services.GetRequiredService();
+ var agent = await agentService.GetAgent(message.CurrentAgentId);
+
+ Init(message);
SetImageOptions();
var image = await SelectImage(descrpition);
- var response = await GetImageEditGeneration(message, descrpition, image);
+ var response = await GetImageEditGeneration(agent, message, descrpition, image);
message.Content = response;
message.StopCompletion = true;
return true;
}
- private async Task Init(RoleDialogModel message)
+ private void Init(RoleDialogModel message)
{
- var agentService = _services.GetRequiredService();
var convService = _services.GetRequiredService();
-
- _agent = await agentService.GetAgent(message.CurrentAgentId);
_conversationId = convService.ConversationId;
_messageId = message.MessageId;
}
@@ -74,7 +74,7 @@ private void SetImageOptions()
return selecteds?.FirstOrDefault();
}
- private async Task GetImageEditGeneration(RoleDialogModel message, string description, MessageFileModel? image)
+ private async Task GetImageEditGeneration(Agent agent, RoleDialogModel message, string description, MessageFileModel? image)
{
if (image == null)
{
@@ -83,15 +83,10 @@ private async Task GetImageEditGeneration(RoleDialogModel message, strin
try
{
- var (provider, model) = GetLlmProviderModel();
+ var (provider, model) = GetLlmProviderModel(agent);
var completion = CompletionProvider.GetImageCompletion(_services, provider: provider, model: model);
var text = !string.IsNullOrWhiteSpace(description) ? description : message.Content;
var dialog = RoleDialogModel.From(message, AgentRole.User, text);
- var agent = new Agent
- {
- Id = _agent?.Id ?? BuiltInAgentId.UtilityAssistant,
- Name = _agent?.Name ?? "Utility Assistant"
- };
var fileStorage = _services.GetRequiredService();
var fileBinary = fileStorage.GetFileBytes(image.FileStorageUrl);
@@ -110,7 +105,7 @@ private async Task GetImageEditGeneration(RoleDialogModel message, strin
return response.Content;
}
- return await GetImageEditResponse(description, defaultContent: null);
+ return await GetImageEditResponse(agent, description);
}
catch (Exception ex)
{
@@ -120,43 +115,24 @@ private async Task GetImageEditGeneration(RoleDialogModel message, strin
}
}
- private async Task GetImageEditResponse(string description, string? defaultContent)
+ private async Task GetImageEditResponse(Agent agent, string description)
{
- if (defaultContent != null)
- {
- return defaultContent;
- }
-
- var llmConfig = _agent.LlmConfig;
- var agent = new Agent
- {
- Id = _agent?.Id ?? BuiltInAgentId.UtilityAssistant,
- Name = _agent?.Name ?? "Utility Assistant",
- LlmConfig = new AgentLlmConfig
- {
- Provider = llmConfig?.Provider ?? "openai",
- Model = llmConfig?.Model ?? "gpt-5-mini",
- MaxOutputTokens = llmConfig?.MaxOutputTokens,
- ReasoningEffortLevel = llmConfig?.ReasoningEffortLevel
- }
- };
-
return await AiResponseHelper.GetImageGenerationResponse(_services, agent, description);
}
- private (string, string) GetLlmProviderModel()
+ private (string, string) GetLlmProviderModel(Agent agent)
{
- var state = _services.GetRequiredService();
- var llmProviderService = _services.GetRequiredService();
-
- var provider = state.GetState("image_edit_llm_provider");
- var model = state.GetState("image_edit_llm_provider");
+ var provider = agent?.LlmConfig?.ImageEdit?.Provider;
+ var model = agent?.LlmConfig?.ImageEdit?.Model;
if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(model))
{
return (provider, model);
}
+ provider = _settings?.Edit?.Provider;
+ model = _settings?.Edit?.Model;
+
if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(model))
{
return (provider, model);
diff --git a/src/Plugins/BotSharp.Plugin.ImageHandler/Functions/EditImageFn.cs b/src/Plugins/BotSharp.Plugin.ImageHandler/Functions/EditImageFn.cs
index 8e73d639c..c171b93f2 100644
--- a/src/Plugins/BotSharp.Plugin.ImageHandler/Functions/EditImageFn.cs
+++ b/src/Plugins/BotSharp.Plugin.ImageHandler/Functions/EditImageFn.cs
@@ -32,7 +32,7 @@ public async Task Execute(RoleDialogModel message)
var agentService = _services.GetRequiredService();
var agent = await agentService.GetAgent(message.CurrentAgentId);
- await Init(message);
+ Init(message);
SetImageOptions();
var image = await SelectImage(descrpition);
@@ -42,10 +42,9 @@ public async Task Execute(RoleDialogModel message)
return true;
}
- private async Task Init(RoleDialogModel message)
+ private void Init(RoleDialogModel message)
{
var convService = _services.GetRequiredService();
-
_conversationId = convService.ConversationId;
_messageId = message.MessageId;
}
diff --git a/src/Plugins/BotSharp.Plugin.MetaGLM/Modules/Embeddings.cs b/src/Plugins/BotSharp.Plugin.MetaGLM/Modules/Embeddings.cs
index 9d7f357fe..5522e6191 100644
--- a/src/Plugins/BotSharp.Plugin.MetaGLM/Modules/Embeddings.cs
+++ b/src/Plugins/BotSharp.Plugin.MetaGLM/Modules/Embeddings.cs
@@ -35,8 +35,8 @@ private IEnumerable ProcessBase(EmbeddingRequestBase requestBody)
};
- var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
- var stream = response.Content.ReadAsStreamAsync().Result;
+ var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false).GetAwaiter().GetResult();
+ var stream = response.Content.ReadAsStreamAsync().ConfigureAwait(false).GetAwaiter().GetResult();
byte[] buffer = new byte[8192];
int bytesRead;
diff --git a/src/Plugins/BotSharp.Plugin.MetaGLM/Modules/Images.cs b/src/Plugins/BotSharp.Plugin.MetaGLM/Modules/Images.cs
index 22fd81ad2..ee0a1b76e 100644
--- a/src/Plugins/BotSharp.Plugin.MetaGLM/Modules/Images.cs
+++ b/src/Plugins/BotSharp.Plugin.MetaGLM/Modules/Images.cs
@@ -31,8 +31,8 @@ private IEnumerable GenerateBase(ImageRequestBase requestBody)
};
- var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
- var stream = response.Content.ReadAsStreamAsync().Result;
+ var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false).GetAwaiter().GetResult();
+ var stream = response.Content.ReadAsStreamAsync().ConfigureAwait(false).GetAwaiter().GetResult();
byte[] buffer = new byte[8192];
int bytesRead;
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageClientExtensions.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageClientExtensions.cs
index 2fb14e9ef..7ce780357 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageClientExtensions.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageClientExtensions.cs
@@ -2,6 +2,8 @@
using OpenAI.Images;
using System.ClientModel;
using System.ClientModel.Primitives;
+using System.Net.Http;
+using System.Net.Http.Headers;
using System.Reflection;
namespace BotSharp.Plugin.OpenAI.Providers.Image;
@@ -23,52 +25,62 @@ public static class ImageClientExtensions
/// ClientResult containing the generated image collection
public static ClientResult GenerateImageEdits(
this ImageClient client,
+ string model,
Stream[] images,
string[] imageFileNames,
string prompt,
int? imageCount = null,
- ImageEditOptions options = null)
+ ImageEditOptions? options = null)
{
if (client == null)
+ {
throw new ArgumentNullException(nameof(client));
+ }
- if (images == null || images.Length == 0)
+ if (images.IsNullOrEmpty())
+ {
throw new ArgumentException("At least one image is required", nameof(images));
+ }
if (imageFileNames == null || imageFileNames.Length != images.Length)
+ {
throw new ArgumentException("Image file names array must match images array length", nameof(imageFileNames));
+ }
if (string.IsNullOrWhiteSpace(prompt))
+ {
throw new ArgumentException("Prompt cannot be null or empty", nameof(prompt));
+ }
// Get the pipeline from the client
var pipeline = client.Pipeline;
using var message = pipeline.CreateMessage();
// Build the request
- BuildMultipartRequest(message, images, imageFileNames, prompt, imageCount, options);
+ BuildMultipartRequest(message, model, images, imageFileNames, prompt, imageCount, options);
// Send the request
pipeline.Send(message);
- if (message.Response.IsError)
+ if (message.Response == null || message.Response.IsError)
{
- throw new InvalidOperationException($"API request failed with status {message.Response.Status}: {message.Response.ReasonPhrase} \r\n{message.Response.Content}");
+ throw new InvalidOperationException($"API request failed with status {message.Response?.Status}: {message.Response?.ReasonPhrase} \r\n{message.Response?.Content}");
}
// Parse the response
- var generatedImages = ParseResponse(message.Response, options?.ResponseFormat);
+ var generatedImages = ParseResponse(message.Response);
return ClientResult.FromValue(generatedImages, message.Response);
}
private static void BuildMultipartRequest(
PipelineMessage message,
+ string model,
Stream[] images,
string[] imageFileNames,
string prompt,
int? imageCount,
- ImageEditOptions options)
+ ImageEditOptions? options)
{
message.Request.Method = "POST";
@@ -76,102 +88,82 @@ private static void BuildMultipartRequest(
var endpoint = "https://api.openai.com";
message.Request.Uri = new Uri($"{endpoint.TrimEnd('/')}/v1/images/edits");
- // Create multipart form data
var boundary = $"----WebKitFormBoundary{Guid.NewGuid():N}";
- var contentBuilder = new MemoryStream();
-
- // Add prompt
- WriteFormField(contentBuilder, boundary, "prompt", prompt);
-
- WriteFormField(contentBuilder, boundary, "model", "gpt-image-1-mini");
-
- // Add image count
- WriteFormField(contentBuilder, boundary, "n", imageCount.Value.ToString() ?? "1");
-
- for (var i = 0; i < images.Length; i++)
- {
- WriteFormField(contentBuilder, boundary, "image[]", imageFileNames[i], images[i], "image/png");
- }
-
- // Add optional parameters supported by OpenAI image edits API
- if (options.Quality.HasValue)
+ using var form = new MultipartFormDataContent(boundary)
{
- WriteFormField(contentBuilder, boundary, "quality", options.Quality.ToString() ?? "auto");
- }
+ { new StringContent(prompt), "prompt" },
+ { new StringContent(model ?? "gpt-image-1-mini"), "model" }
+ };
- if (options.Size.HasValue)
+ if (imageCount.HasValue)
{
- WriteFormField(contentBuilder, boundary, "size", ConvertImageSizeToString(options.Size.Value));
+ form.Add(new StringContent(imageCount.Value.ToString()), "n");
}
-
- if (options.Background.HasValue)
+ else
{
- WriteFormField(contentBuilder, boundary, "background", options.Background.ToString() ?? "auto");
+ form.Add(new StringContent("1"), "n");
}
- WriteFormField(contentBuilder, boundary, "output_format", "png");
-
- if (!string.IsNullOrEmpty(options.EndUserId))
+ if (options != null)
{
- WriteFormField(contentBuilder, boundary, "user", options.EndUserId);
- }
-
- WriteFormField(contentBuilder, boundary, "moderation", "auto");
-
- // Write closing boundary
- var closingBoundary = Encoding.UTF8.GetBytes($"--{boundary}--\r\n");
- contentBuilder.Write(closingBoundary, 0, closingBoundary.Length);
-
- // Set the content
- contentBuilder.Position = 0;
- message.Request.Content = BinaryContent.Create(BinaryData.FromStream(contentBuilder));
+ if (options.Quality.HasValue)
+ {
+ form.Add(new StringContent(options.Quality.ToString()), "quality");
+ }
- // Set content type header
- message.Request.Headers.Set("Content-Type", $"multipart/form-data; boundary={boundary}");
- }
+ if (options.Size.HasValue)
+ {
+ form.Add(new StringContent(ConvertImageSizeToString(options.Size.Value)), "size");
+ }
- private static void WriteFormField(MemoryStream stream, string boundary, string name, string value)
- {
- var header = $"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n";
- var body = $"{header}\r\n{value}\r\n";
- var bytes = Encoding.UTF8.GetBytes(body);
- stream.Write(bytes, 0, bytes.Length);
- }
+ if (options.Background.HasValue)
+ {
+ form.Add(new StringContent(options.Background.ToString()), "background");
+ }
- private static void WriteFormField(MemoryStream stream, string boundary, string name, string fileName, Stream fileStream, string contentType)
- {
- var header = $"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{fileName}\"\r\nContent-Type: {contentType}\r\n\r\n";
- var headerBytes = Encoding.UTF8.GetBytes(header);
- stream.Write(headerBytes, 0, headerBytes.Length);
+ if (options.ResponseFormat.HasValue)
+ {
+ form.Add(new StringContent(options.ResponseFormat.ToString()), "response_format");
+ }
+ }
- // Copy file stream
- if (fileStream.CanSeek)
+ for (var i = 0; i < images.Length; i++)
{
- fileStream.Position = 0;
- }
- fileStream.CopyTo(stream);
+ if (images[i].CanSeek)
+ {
+ images[i].Position = 0;
+ }
+ var fileContent = new StreamContent(images[i]);
+ fileContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
- var newLine = Encoding.UTF8.GetBytes("\r\n");
- stream.Write(newLine, 0, newLine.Length);
- }
+ if (images.Length > 1)
+ {
+ form.Add(fileContent, name: "image[]", fileName: imageFileNames[i]);
+ }
+ else
+ {
+ form.Add(fileContent, name: "image", fileName: imageFileNames[i]);
+ }
+ }
- #region Helper Methods
+ using var ms = new MemoryStream();
+ form.CopyTo(ms, null, CancellationToken.None);
+ ms.Position = 0;
- private static string GetEndpoint(PipelineMessage message)
- {
- // Try to get the endpoint from the request URI if already set
- return message.Request.Uri?.GetLeftPart(UriPartial.Authority);
+ message.Request.Headers.Set("Content-Type", form.Headers.ContentType.ToString());
+ message.Request.Content = BinaryContent.Create(BinaryData.FromStream(ms));
}
- private static GeneratedImageCollection ParseResponse(PipelineResponse response, GeneratedImageFormat? format)
+ #region Private Methods
+ private static GeneratedImageCollection ParseResponse(PipelineResponse response)
{
try
{
// Try to use ModelReaderWriter to deserialize the response
- var modelReaderWriter = ModelReaderWriter.Read(response.Content);
- if (modelReaderWriter != null)
+ var result = ModelReaderWriter.Read(response.Content);
+ if (result != null)
{
- return modelReaderWriter;
+ return result;
}
}
catch (Exception ex)
@@ -196,7 +188,7 @@ private static GeneratedImageCollection ParseResponse(PipelineResponse response,
var result = fromResponseMethod.Invoke(null, new object[] { response });
if (result != null)
{
- return (GeneratedImageCollection)result;
+ return result as GeneratedImageCollection;
}
}
@@ -211,7 +203,7 @@ private static GeneratedImageCollection ParseResponse(PipelineResponse response,
var result = deserializeMethod.Invoke(null, new object[] { jsonDocument.RootElement });
if (result != null)
{
- return (GeneratedImageCollection)result;
+ return result as GeneratedImageCollection;
}
}
}
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Compose.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Compose.cs
index d07c4b7b0..6215ba1af 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Compose.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Compose.cs
@@ -1,6 +1,4 @@
#pragma warning disable OPENAI001
-using OpenAI.Images;
-
namespace BotSharp.Plugin.OpenAI.Providers.Image;
public partial class ImageCompletionProvider
@@ -20,11 +18,7 @@ public async Task GetImageComposition(Agent agent, RoleDialogMo
var imageClient = client.GetImageClient(_model);
// Use the new extension method to support multiple images
- options.ResponseFormat = "b64_json";
- options.Quality = "medium";
- options.Background = "auto";
- options.Size = GeneratedImageSize.Auto;
- var response = imageClient.GenerateImageEdits(images, imageFileNames, prompt, imageCount, options);
+ var response = imageClient.GenerateImageEdits(_model, images, imageFileNames, prompt, imageCount, options);
var generatedImageCollection = response.Value;
var generatedImages = GetImageGenerations(generatedImageCollection, options.ResponseFormat);
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Edit.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Edit.cs
index 732cc7a96..7a8d44725 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Edit.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Edit.cs
@@ -56,12 +56,14 @@ public async Task GetImageEdits(Agent agent, RoleDialogModel me
var state = _services.GetRequiredService();
var size = state.GetState("image_size");
+ var quality = state.GetState("image_quality");
var responseFormat = state.GetState("image_response_format");
var background = state.GetState("image_background");
var settings = settingsService.GetSetting(Provider, _model)?.Image?.Edit;
size = settings?.Size != null ? LlmUtility.VerifyModelParameter(size, settings.Size.Default, settings.Size.Options) : null;
+ quality = settings?.Quality != null ? LlmUtility.VerifyModelParameter(quality, settings.Quality.Default, settings.Quality.Options) : null;
responseFormat = settings?.ResponseFormat != null ? LlmUtility.VerifyModelParameter(responseFormat, settings.ResponseFormat.Default, settings.ResponseFormat.Options) : null;
background = settings?.Background != null ? LlmUtility.VerifyModelParameter(background, settings.Background.Default, settings.Background.Options) : null;
@@ -70,6 +72,10 @@ public async Task GetImageEdits(Agent agent, RoleDialogModel me
{
options.Size = GetImageSize(size);
}
+ if (!string.IsNullOrEmpty(quality))
+ {
+ options.Quality = GetImageQuality(quality);
+ }
if (!string.IsNullOrEmpty(responseFormat))
{
options.ResponseFormat = GetImageResponseFormat(responseFormat);
diff --git a/src/Plugins/BotSharp.Plugin.Planner/SqlGeneration/Hooks/SqlPlannerAgentHook.cs b/src/Plugins/BotSharp.Plugin.Planner/SqlGeneration/Hooks/SqlPlannerAgentHook.cs
index 64645eac8..f2a8aca96 100644
--- a/src/Plugins/BotSharp.Plugin.Planner/SqlGeneration/Hooks/SqlPlannerAgentHook.cs
+++ b/src/Plugins/BotSharp.Plugin.Planner/SqlGeneration/Hooks/SqlPlannerAgentHook.cs
@@ -20,7 +20,7 @@ public override bool OnInstructionLoaded(string template, Dictionary GetReplySpeechFileName(string conversationId,
private static string GetHints(string agentId, AssistantMessage reply, IServiceProvider sp)
{
var agentService = sp.GetRequiredService();
- var agent = agentService.GetAgent(agentId).Result;
+ var agent = agentService.GetAgent(agentId).ConfigureAwait(false).GetAwaiter().GetResult();
var extraWords = new List();
HookEmitter.Emit(sp, hook => extraWords.AddRange(hook.OnModelTranscriptPrompt(agent)),
agentId);
diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs
index 3a4d62f2b..37a0a0a21 100644
--- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs
+++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs
@@ -44,7 +44,7 @@ public void SetAgent(Agent agent)
_ => AriaRole.Generic
};
element = _instance.GetPage(contextId).Locator($"[name='{context.ElementName}']");
- var count = element.CountAsync().Result;
+ var count = element.CountAsync().ConfigureAwait(false).GetAwaiter().GetResult();
if (count == 0)
{
_logger.LogError($"Can't locate element {role} {context.ElementName}");
diff --git a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs
index 45d6dc6d5..506858cc3 100644
--- a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs
+++ b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs
@@ -6,6 +6,7 @@
using BotSharp.Abstraction.Plugins.Models;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Utilities;
+using System.Collections.Concurrent;
namespace BotSharp.Plugin.Google.Core
{
@@ -41,32 +42,32 @@ public Task InheritAgent(Agent agent)
return Task.CompletedTask;
}
- public string RenderInstruction(Agent agent, Dictionary? renderData = null)
+ public string RenderInstruction(Agent agent, IDictionary? renderData = null)
{
return "Fake Instruction";
}
- public string RenderTemplate(Agent agent, string templateName, Dictionary? renderData = null)
+ public string RenderTemplate(Agent agent, string templateName, IDictionary? renderData = null)
{
return $"Rendered template for {templateName}";
}
- public bool RenderFunction(Agent agent, FunctionDef def, Dictionary? renderData = null)
+ public bool RenderFunction(Agent agent, FunctionDef def, IDictionary? renderData = null)
{
return true;
}
- public (string, IEnumerable) PrepareInstructionAndFunctions(Agent agent, Dictionary? renderData = null, StringComparer? comparer = null)
+ public (string, IEnumerable) PrepareInstructionAndFunctions(Agent agent, IDictionary? renderData = null, StringComparer? comparer = null)
{
return (string.Empty, []);
}
- public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def, Dictionary? renderData = null)
+ public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def, IDictionary? renderData = null)
{
return def.Parameters;
}
- public bool RenderVisibility(string? visibilityExpression, Dictionary dict)
+ public bool RenderVisibility(string? visibilityExpression, IDictionary dict)
{
return true;
}
@@ -121,7 +122,7 @@ public Task> GetAgentUtilityOptions()
return Task.FromResult(Enumerable.Empty());
}
- public Dictionary CollectRenderData(Agent agent)
+ public ConcurrentDictionary CollectRenderData(Agent agent)
{
return [];
}