Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ public class AgentSettings
/// <summary>
/// This is the default LLM config for agent
/// </summary>
public AgentLlmConfig LlmConfig { get; set; }
= new AgentLlmConfig();
public AgentLlmConfig LlmConfig { get; set; } = new AgentLlmConfig();

/// <summary>
/// General coding settings
/// </summary>
public CodingSettings Coding { get; set; } = new CodingSettings();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace BotSharp.Abstraction.Coding.Enums;

public static class BuiltInCodeProcessor
{
public const string PyInterpreter = "botsharp-py-interpreter";
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ public class CodeGenerationOptions : LlmConfigBase
public string? TemplateName { get; set; }

/// <summary>
/// The programming language
/// Programming language
/// </summary>
[JsonPropertyName("language")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Language { get; set; } = "python";
[JsonPropertyName("programming_language")]
public string? ProgrammingLanguage { get; set; }

/// <summary>
/// Data that can be used to fill in the prompt
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace BotSharp.Abstraction.Coding.Settings;

public class CodingSettings
{
/// <summary>
/// Llm provider to generate code script
/// </summary>
public string? Provider { get; set; }

/// <summary>
/// Llm model to generate code script
/// </summary>
public string? Model { get; set; }
}
11 changes: 10 additions & 1 deletion src/Infrastructure/BotSharp.Abstraction/Rules/IRuleEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,14 @@ namespace BotSharp.Abstraction.Rules;

public interface IRuleEngine
{
Task<IEnumerable<string>> Triggered(IRuleTrigger trigger, string data, List<MessageState>? states = null);
/// <summary>
/// Trigger the rule that is subscribed by agents.
/// </summary>
/// <param name="trigger"></param>
/// <param name="text"></param>
/// <param name="options"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
Task<IEnumerable<string>> Trigger(IRuleTrigger trigger, string text, RuleTriggerOptions? options = null)
=> throw new NotImplementedException();
}
7 changes: 7 additions & 0 deletions src/Infrastructure/BotSharp.Abstraction/Rules/IRuleTrigger.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Text.Json;

namespace BotSharp.Abstraction.Rules;

public interface IRuleTrigger
Expand All @@ -9,4 +11,9 @@ public interface IRuleTrigger
string EntityType { get; set; }

string EntityId { get; set; }

/// <summary>
/// The default arguments as input to code trigger (display purpose)
/// </summary>
JsonDocument OutputArgs => JsonDocument.Parse("{}");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Text.Json;

namespace BotSharp.Abstraction.Rules.Options;

public class RuleTriggerOptions
{
/// <summary>
/// Code processor provider
/// </summary>
public string? CodeProcessor { get; set; }

/// <summary>
/// Code script name
/// </summary>
public string? CodeScriptName { get; set; }

/// <summary>
/// Argument name as an input key to the code script
/// </summary>
public string? ArgsName { get; set; }

/// <summary>
/// Json arguments as an input value to the code script
/// </summary>
public JsonDocument? Arguments { get; set; }

/// <summary>
/// States
/// </summary>
public List<MessageState>? States { get; set; } = null;
}
4 changes: 3 additions & 1 deletion src/Infrastructure/BotSharp.Abstraction/Using.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@
global using BotSharp.Abstraction.Knowledges.Models;
global using BotSharp.Abstraction.Crontab.Models;
global using BotSharp.Abstraction.MCP.Models;
global using BotSharp.Abstraction.Settings;
global using BotSharp.Abstraction.Settings;
global using BotSharp.Abstraction.Rules.Options;
global using BotSharp.Abstraction.Coding.Settings;
138 changes: 105 additions & 33 deletions src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
using BotSharp.Abstraction.Coding;
using BotSharp.Abstraction.Coding.Enums;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Rules.Options;
using BotSharp.Abstraction.Utilities;
using Microsoft.Extensions.Logging;
using System.Data;
using System.Text.Json;

namespace BotSharp.Core.Rules.Engines;

public class RuleEngine : IRuleEngine
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
private readonly ILogger<RuleEngine> _logger;

public RuleEngine(IServiceProvider services, ILogger<RuleEngine> logger)
public RuleEngine(
IServiceProvider services,
ILogger<RuleEngine> logger)
{
_services = services;
_logger = logger;
}

public async Task<IEnumerable<string>> Triggered(IRuleTrigger trigger, string data, List<MessageState>? states = null)
public async Task<IEnumerable<string>> Trigger(IRuleTrigger trigger, string text, RuleTriggerOptions? options = null)
{
var newConversationIds = new List<string>();

// Pull all user defined rules
var agentService = _services.GetRequiredService<IAgentService>();
var agents = await agentService.GetAgents(new AgentFilter
Expand All @@ -30,34 +38,41 @@ public async Task<IEnumerable<string>> Triggered(IRuleTrigger trigger, string da
}
});

var preFilteredAgents = agents.Items.Where(x =>
x.Rules.Exists(r => r.TriggerName == trigger.Name &&
!x.Disabled)).ToList();
// Trigger agents
var filteredAgents = agents.Items.Where(x => x.Rules.Exists(r => r.TriggerName == trigger.Name && !x.Disabled)).ToList();
foreach (var agent in filteredAgents)
{
var isTriggered = true;

// Trigger the agents
var instructService = _services.GetRequiredService<IInstructService>();
var newConversationIds = new List<string>();
// Code trigger
if (options != null)
{
isTriggered = await TriggerCodeScript(agent.Id, trigger.Name, options);
}

if (!isTriggered)
{
continue;
}

foreach (var agent in preFilteredAgents)
{
var convService = _services.GetRequiredService<IConversationService>();
var conv = await convService.NewConversation(new Conversation
{
Channel = trigger.Channel,
Title = data,
Title = text,
AgentId = agent.Id
});

var message = new RoleDialogModel(AgentRole.User, data);
var message = new RoleDialogModel(AgentRole.User, text);

var allStates = new List<MessageState>
{
new("channel", trigger.Channel)
};

if (states != null)
if (options?.States != null)
{
allStates.AddRange(states);
allStates.AddRange(options.States);
}

convService.SetConversationId(conv.Id, allStates);
Expand All @@ -69,27 +84,84 @@ await convService.SendMessage(agent.Id,

convService.SaveStates();
newConversationIds.Add(conv.Id);
}

return newConversationIds;
}

#region Private methods
private async Task<bool> TriggerCodeScript(string agentId, string triggerName, RuleTriggerOptions options)
{
if (string.IsNullOrWhiteSpace(agentId))
{
return false;
}

var provider = options.CodeProcessor ?? BuiltInCodeProcessor.PyInterpreter;
var processor = _services.GetServices<ICodeProcessor>().FirstOrDefault(x => x.Provider.IsEqualTo(provider));
if (processor == null)
{
_logger.LogWarning($"Unable to find code processor: {provider}.");
return false;
}

var agentService = _services.GetRequiredService<IAgentService>();
var scriptName = options.CodeScriptName ?? $"{triggerName}_rule.py";
var codeScript = await agentService.GetAgentCodeScript(agentId, scriptName, scriptType: AgentCodeScriptType.Src);

/*foreach (var rule in agent.Rules)
var msg = $"rule trigger ({triggerName}) code script ({scriptName}) in agent ({agentId}) => args: {options.Arguments?.RootElement.GetRawText()}.";

if (string.IsNullOrWhiteSpace(codeScript))
{
_logger.LogWarning($"Unable to find {msg}.");
return false;
}

try
{
var response = await processor.RunAsync(codeScript, options: new()
{
ScriptName = scriptName,
Arguments = BuildArguments(options.ArgsName, options.Arguments)
});

if (response == null || !response.Success)
{
_logger.LogWarning($"Failed to handle {msg}");
return false;
}

bool result;
LogLevel logLevel;
if (response.Result.IsEqualTo("true") || response.Result.IsEqualTo("1"))
{
var userSay = $"===Input data with Before and After values===\r\n{data}\r\n\r\n===Trigger Criteria===\r\n{rule.Criteria}\r\n\r\nJust output 1 or 0 without explanation: ";

var result = await instructService.Execute(BuiltInAgentId.RulesInterpreter, new RoleDialogModel(AgentRole.User, userSay), "criteria_check", "#TEMPLATE#");

// Check if meet the criteria
if (result.Text == "1")
{
// Hit rule
_logger.LogInformation($"Hit rule {rule.TriggerName} {rule.EntityType} {rule.EventName}, {data}");

await convService.SendMessage(agent.Id,
new RoleDialogModel(AgentRole.User, $"The conversation was triggered by {rule.Criteria}"),
null,
msg => Task.CompletedTask);
}
}*/
logLevel = LogLevel.Information;
result = true;
}
else
{
logLevel = LogLevel.Warning;
result = false;
}

_logger.Log(logLevel, $"Code script execution result ({response.Result}) from {msg}");
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error when handling {msg}");
return false;
}
}

return newConversationIds;
private IEnumerable<KeyValue> BuildArguments(string? argName, JsonDocument? args)
{
var keyValues = new List<KeyValue>();
if (args != null)
{
keyValues.Add(new KeyValue(argName ?? "rule_args", args.RootElement.GetRawText()));
}
return keyValues;
}
#endregion
}
3 changes: 0 additions & 3 deletions src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using BotSharp.Abstraction.Templating;
using BotSharp.Abstraction.Users.Enums;
using BotSharp.Core.Agents.Hooks;
using BotSharp.Core.Coding;
using Microsoft.Extensions.Configuration;

namespace BotSharp.Core.Agents;
Expand Down Expand Up @@ -49,8 +48,6 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
render.RegisterType(typeof(AgentSettings));
return settingService.Bind<AgentSettings>("Agent");
});

services.AddSingleton<CodeScriptExecutor>();
}

public bool AttachMenu(List<PluginMenuDef> menu)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using BotSharp.Abstraction.Agents.Options;
using BotSharp.Abstraction.Coding;
using BotSharp.Abstraction.Coding.Enums;
using BotSharp.Abstraction.Coding.Options;

namespace BotSharp.Core.Agents.Services;
Expand Down Expand Up @@ -76,7 +77,7 @@ public async Task<CodeGenerationResult> GenerateCodeScript(string agentId, strin
};
}

var processor = options?.Processor ?? "botsharp-py-interpreter";
var processor = options?.Processor ?? BuiltInCodeProcessor.PyInterpreter;
var codeProcessor = _services.GetServices<ICodeProcessor>().FirstOrDefault(x => x.Provider.IsEqualTo(processor));
if (codeProcessor == null)
{
Expand Down
15 changes: 15 additions & 0 deletions src/Infrastructure/BotSharp.Core/Coding/CodingPlugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.Extensions.Configuration;

namespace BotSharp.Core.Coding;

public class CodingPlugin : IBotSharpPlugin
{
public string Id => "31bc334b-9462-4191-beac-cb4a139b78c1";
public string Name => "Coding";
public string Description => "Handling execution and generation of code scripts";

public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddSingleton<CodeScriptExecutor>();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Coding;
using BotSharp.Abstraction.Coding.Enums;
using BotSharp.Abstraction.Files.Options;
using BotSharp.Abstraction.Files.Proccessors;
using BotSharp.Abstraction.Instructs;
Expand Down Expand Up @@ -179,7 +180,7 @@ await hook.OnResponseGenerated(new InstructResponseModel
var state = _services.GetRequiredService<IConversationStateService>();
var hooks = _services.GetHooks<IInstructHook>(agent.Id);

var codeProvider = codeOptions?.Processor ?? "botsharp-py-interpreter";
var codeProvider = codeOptions?.Processor ?? BuiltInCodeProcessor.PyInterpreter;
var codeProcessor = _services.GetServices<ICodeProcessor>()
.FirstOrDefault(x => x.Provider.IsEqualTo(codeProvider));

Expand Down
1 change: 1 addition & 0 deletions src/Infrastructure/BotSharp.Core/Using.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Conversations.Settings;
global using BotSharp.Abstraction.Coding.Models;
global using BotSharp.Abstraction.Coding.Settings;
global using BotSharp.Abstraction.Crontab.Models;
global using BotSharp.Abstraction.Files;
global using BotSharp.Abstraction.Files.Enums;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public async Task<CodeGenerationResult> GenerateAgentCodeScript([FromRoute] stri
var states = request.Options?.Data?.ToList();
var state = _services.GetRequiredService<IConversationStateService>();
states?.ForEach(x => state.SetState(x.Key, x.Value, source: StateSource.External));
state.SetState("programming_language", request.Options?.Language, source: StateSource.External);
state.SetState("code_processor", request.Options?.Processor, source: StateSource.External);
state.SetState("programming_language", request.Options?.ProgrammingLanguage, source: StateSource.External);

var result = await _agentService.GenerateCodeScript(agentId, request.Text, request?.Options);
return result;
Expand Down
Loading
Loading