Merge branch 'master' of https://github.com/SciSharp/BotSharp into features/refine-twilio-stream

This commit is contained in:
Jicheng Lu 2025-08-04 09:41:18 -05:00
commit 9af4c759eb
8 changed files with 73 additions and 18 deletions

View file

@ -10,7 +10,7 @@ namespace BotSharp.Abstraction.Agents;
public interface IAgentService
{
Task<Agent> CreateAgent(Agent agent);
Task<string> RefreshAgents();
Task<string> RefreshAgents(IEnumerable<string>? agentIds = null);
Task<PagedItems<Agent>> GetAgents(AgentFilter filter);
Task<List<IdName>> GetAgentOptions(List<string>? agentIds = null, bool byName = false);
Task<IEnumerable<AgentUtility>> GetAgentUtilityOptions();

View file

@ -122,6 +122,11 @@ public static class StringExtensions
return s;
}
if (value is DateTime d)
{
return d.ToString("o");
}
if (value is JsonElement elem
&& elem.ValueKind == JsonValueKind.String)
{

View file

@ -6,7 +6,7 @@ namespace BotSharp.Core.Agents.Services;
public partial class AgentService
{
public async Task<string> RefreshAgents()
public async Task<string> RefreshAgents(IEnumerable<string>? agentIds = null)
{
string refreshResult;
var dbSettings = _services.GetRequiredService<BotSharpDatabaseSettings>();
@ -28,7 +28,13 @@ public partial class AgentService
}
var refreshedAgents = new List<string>();
foreach (var dir in Directory.GetDirectories(agentDir))
var dirs = Directory.GetDirectories(agentDir);
if (!agentIds.IsNullOrEmpty())
{
dirs = dirs.Where(x => agentIds.Contains(x.Split(Path.DirectorySeparatorChar).Last())).ToArray();
}
foreach (var dir in dirs)
{
try
{
@ -78,7 +84,7 @@ public partial class AgentService
}
else
{
refreshResult = "No agent gets refreshed!";
refreshResult = "No agent gets migrated!";
}
_logger.LogInformation(refreshResult);

View file

@ -17,13 +17,14 @@ public partial class AgentService
instructions.AddRange(secondaryInstructions);
// update states
var renderDict = new Dictionary<string, object>(agent.TemplateDict);
foreach (var t in conv.States.GetStates())
{
agent.TemplateDict[t.Key] = t.Value;
renderDict[t.Key] = t.Value;
}
agent.TemplateDict[TemplateRenderConstant.RENDER_AGENT] = agent;
var res = render.Render(string.Join("\r\n", instructions), agent.TemplateDict);
renderDict[TemplateRenderConstant.RENDER_AGENT] = agent;
var res = render.Render(string.Join("\r\n", instructions), renderDict);
return res;
}
@ -143,4 +144,4 @@ public partial class AgentService
return result.IsEqualTo("visible");
}
}
}

View file

@ -64,7 +64,7 @@ public partial class RoutingService
}
catch (JsonException ex)
{
_logger.LogError($"The input does not contain any JSON tokens:\r\n{message.Content}");
_logger.LogError($"The input does not contain any JSON tokens:\r\n{message.Content}\r\n{ex.Message}");
message.StopCompletion = true;
message.Content = ex.Message;
}

View file

@ -111,9 +111,9 @@ public class AgentController : ControllerBase
[BotSharpAuth]
[HttpPost("/refresh-agents")]
public async Task<string> RefreshAgents()
public async Task<string> RefreshAgents([FromBody] AgentMigrationModel request)
{
return await _agentService.RefreshAgents();
return await _agentService.RefreshAgents(request?.AgentIds);
}
[HttpPut("/agent/file/{agentId}")]

View file

@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace BotSharp.OpenAPI.ViewModels.Agents;
public class AgentMigrationModel
{
[JsonPropertyName("agent_ids")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IEnumerable<string>? AgentIds { get; set; }
}

View file

@ -55,15 +55,23 @@ public partial class PlaywrightWebDriver
}
else if (action.Action == BroswerActionEnum.DropDown)
{
await locator.ClickAsync();
var optionLocator = page.Locator($"//div[text()='{action.Content}']");
var optionCount = await optionLocator.CountAsync(); ;
if (optionCount == 0)
var tagName = await locator.EvaluateAsync<string>("el => el.tagName.toLowerCase()");
if (tagName == "select")
{
Serilog.Log.Error($"Dropdown option not found: {action.Content}");
return;
await HandleSelectDropDownAsync(page, locator, action);
}
else
{
await locator.ClickAsync();
var optionLocator = page.Locator($"//div[text()='{action.Content}']");
var optionCount = await optionLocator.CountAsync();
if (optionCount == 0)
{
Serilog.Log.Error($"Dropdown option not found: {action.Content}");
return;
}
await optionLocator.First.ClickAsync();
}
await optionLocator.First.ClickAsync();
}
else if (action.Action == BroswerActionEnum.InputText)
{
@ -247,4 +255,29 @@ public partial class PlaywrightWebDriver
return track;
}
private async Task HandleSelectDropDownAsync(IPage page, ILocator locator, ElementActionArgs action)
{
if (string.IsNullOrWhiteSpace(action.Content))
{
throw new InvalidOperationException("Dropdown target option (action.Content) cannot be null or empty when using a <select> element.");
}
var selectedText = await locator.Locator("option:checked").InnerTextAsync();
if (!string.Equals(selectedText?.Trim(), action.Content.Trim(), StringComparison.OrdinalIgnoreCase))
{
await locator.SelectOptionAsync(new SelectOptionValue
{
Label = action.Content
});
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
_logger.LogInformation($"Dropdown changed to: {action.Content}");
}
else
{
_logger.LogInformation($"Dropdown already on correct option: {action.Content}");
}
}
}