diff --git a/Directory.Packages.props b/Directory.Packages.props index de3730f08..d5d3954fd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -73,7 +73,7 @@ - + diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentCodeScriptType.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentCodeScriptType.cs new file mode 100644 index 000000000..4bf5f262c --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentCodeScriptType.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Agents.Enums; + +public static class AgentCodeScriptType +{ + public const string Src = "src"; + public const string Test = "test"; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentFuncVisMode.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentFuncVisMode.cs index 212248153..713237837 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentFuncVisMode.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentFuncVisMode.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Agents.Enums; -public class AgentFuncVisMode +public static class AgentFuncVisMode { public const string Manual = "manual"; public const string Auto = "auto"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs index 226313e09..2975e44f1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Agents.Enums; -public class AgentRole +public static class AgentRole { public const string System = "system"; public const string Assistant = "assistant"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs index 455e87ff5..3b9767845 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Agents.Enums; -public class AgentType +public static class AgentType { /// /// Routing agent diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs index 96aa47540..82b0efabe 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Agents.Enums; -public class BuiltInAgentId +public static class BuiltInAgentId { /// /// A routing agent can be used as a base router. diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index c195df5e9..a2f2a17f2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -59,7 +59,6 @@ public interface IAgentService /// /// Task PatchAgentTemplate(Agent agent); - Task UpdateAgentFromFile(string id); string GetDataDir(); string GetAgentDataDir(string agentId); diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs new file mode 100644 index 000000000..840d67210 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs @@ -0,0 +1,29 @@ +namespace BotSharp.Abstraction.Agents.Models; + +public class AgentCodeScript : AgentCodeScriptBase +{ + public string Id { get; set; } + public string AgentId { get; set; } = null!; + + public AgentCodeScript() : base() + { + } + + public override string ToString() + { + return $"{CodePath}"; + } +} + +public class AgentCodeScriptBase +{ + public string Name { get; set; } = null!; + public string Content { get; set; } = null!; + + /// + /// Code script type: src, test + /// + public string ScriptType { get; set; } = null!; + + public string CodePath => $"{ScriptType}/{Name}"; +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs new file mode 100644 index 000000000..bdc10f5c9 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs @@ -0,0 +1,11 @@ +using BotSharp.Abstraction.CodeInterpreter.Models; + +namespace BotSharp.Abstraction.CodeInterpreter; + +public interface ICodeInterpretService +{ + string Provider { get; } + + Task RunCode(string codeScript, CodeInterpretOptions? options = null) + => throw new NotImplementedException(); +} diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs new file mode 100644 index 000000000..23e78a972 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.CodeInterpreter.Models; + +public class CodeInterpretOptions +{ + public IEnumerable? Arguments { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretResult.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretResult.cs new file mode 100644 index 000000000..30b44a97c --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretResult.cs @@ -0,0 +1,8 @@ +namespace BotSharp.Abstraction.CodeInterpreter.Models; + +public class CodeInterpretResult +{ + public object Result { get; set; } + public bool Success { get; set; } + public string? ErrorMsg { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationChannel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationChannel.cs index 1843a1bcc..d569da596 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationChannel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationChannel.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Conversations.Enums; -public class ConversationChannel +public static class ConversationChannel { public const string WebChat = "webchat"; public const string OpenAPI = "openapi"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationStatus.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationStatus.cs index dc8a53b4b..b82e094b2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationStatus.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationStatus.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Conversations.Enums; -public class ConversationStatus +public static class ConversationStatus { public const string Open = "open"; public const string Closed = "closed"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs index 20d635f47..9f80a9e8c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Conversations.Enums; -public class StateDataType +public static class StateDataType { public const string String = "string"; public const string Boolean = "boolean"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs index 31f32e162..cff798025 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Conversations.Enums; -public class StateSource +public static class StateSource { public const string External = "external"; public const string Application = "application"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index 33678eccb..4ca9c0d1e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -140,6 +140,25 @@ public class RoleDialogModel : ITrackableMessage [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public bool IsFromAssistant => Role == AgentRole.Assistant || Role == AgentRole.Model; + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public string RoleContent + { + get + { + var text = Content; + if (Role == AgentRole.User) + { + text = !string.IsNullOrWhiteSpace(Payload) ? Payload : Content; + } + else + { + text = !string.IsNullOrWhiteSpace(RichContent?.Message?.Text) ? RichContent.Message.Text : Content; + } + + return text; + } + } + public RoleDialogModel() { } diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs index 64351e459..9c5dd827a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Infrastructures.Enums; -public class LanguageType +public static class LanguageType { public const string UNKNOWN = "Unknown"; public const string ENGLISH = "English"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs index 038699ac5..4a048b02e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Infrastructures.Enums; -public class StateConst +public static class StateConst { public const string EXPECTED_ACTION_AGENT = "expected_next_action_agent"; public const string EXPECTED_GOAL_AGENT = "expected_user_goal_agent"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs index 23b8bf60b..a7e3c1dfd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs @@ -5,7 +5,10 @@ namespace BotSharp.Abstraction.Instructs; public interface IInstructHook : IHookBase { - Task BeforeCompletion(Agent agent, RoleDialogModel message); - Task AfterCompletion(Agent agent, InstructResult result); - Task OnResponseGenerated(InstructResponseModel response); + Task BeforeCompletion(Agent agent, RoleDialogModel message) => Task.CompletedTask; + Task AfterCompletion(Agent agent, InstructResult result) => Task.CompletedTask; + Task OnResponseGenerated(InstructResponseModel response) => Task.CompletedTask; + + Task BeforeCodeExecution(Agent agent, RoleDialogModel message, CodeInstructContext context) => Task.CompletedTask; + Task AfterCodeExecution(Agent agent, InstructResult result) => Task.CompletedTask; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs index 7dce24237..3fe1c03f5 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs @@ -7,13 +7,16 @@ public interface IInstructService /// /// Execute completion by using specified instruction or template /// - /// Agent (static agent) - /// Additional message provided by user - /// Template name - /// System prompt + /// + /// + /// + /// + /// + /// /// Task Execute(string agentId, RoleDialogModel message, - string? templateName = null, string? instruction = null, IEnumerable? files = null); + string? instruction = null, string? templateName = null, + IEnumerable? files = null, CodeInstructOptions? codeOptions = null); /// /// A generic way to execute completion by using specified instruction or template diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs new file mode 100644 index 000000000..2be0368f1 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Instructs.Models; + +public class CodeInstructContext +{ + public string CodeScript { get; set; } + public List Arguments { get; set; } = []; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs new file mode 100644 index 000000000..721266531 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs @@ -0,0 +1,8 @@ +namespace BotSharp.Abstraction.Instructs.Models; + +public class CodeInstructOptions +{ + public string? CodeScriptName { get; set; } + public string? CodeInterpretProvider { get; set; } + public List? Arguments { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs index fe236863b..efaf4ffd0 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs @@ -6,7 +6,8 @@ public class InstructResult : ITrackableMessage public string MessageId { get; set; } public string Text { get; set; } = string.Empty; public object? Data { get; set; } + [JsonPropertyName("template")] - public string? Template{ get; set; } + public string? Template { get; set; } public Dictionary? States { get; set; } = new(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs b/src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs deleted file mode 100644 index 5a8f7552d..000000000 --- a/src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BotSharp.Abstraction.Interpreters.Models; - -public class InterpretationRequest -{ - [JsonPropertyName("script")] - public string Script { get; set; } = null!; - - [JsonPropertyName("language")] - public string Language { get; set; } = null!; -} diff --git a/src/Infrastructure/BotSharp.Abstraction/Interpreters/Settings/InterpreterSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Interpreters/Settings/InterpreterSettings.cs deleted file mode 100644 index e6ea6cf3f..000000000 --- a/src/Infrastructure/BotSharp.Abstraction/Interpreters/Settings/InterpreterSettings.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace BotSharp.Abstraction.Interpreters.Settings; - -public class InterpreterSettings -{ - public PythonInterpreterSetting Python { get; set; } -} - -public class PythonInterpreterSetting -{ - public string PythonDLL { get; set; } -} diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs index bb3104d36..65ae2dbc1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs +++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs @@ -7,6 +7,7 @@ public interface IRealTimeCompletion string Provider { get; } string Model { get; } void SetModelName(string model); + void SetOptions(RealtimeOptions? options); Task Connect( RealtimeHubConnection conn, diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs b/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs index 3cb9c73f4..8bf0e1898 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs @@ -8,6 +8,17 @@ public class KeyValue [JsonPropertyName("value")] public string? Value { get; set; } + public KeyValue() + { + + } + + public KeyValue(string key, string? value) + { + Key = key; + Value = value; + } + public override string ToString() { return $"Key: {Key}, Value: {Value}"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs index 9d64b6785..e75f46998 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs @@ -13,5 +13,6 @@ public interface IRealtimeHub IRealTimeCompletion Completer { get; } - Task ConnectToModel(Func? responseToUser = null, Func? init = null, List? initStates = null); + Task ConnectToModel(Func? responseToUser = null, Func? init = null, + List? initStates = null, RealtimeOptions? options = null); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Realtime/Models/RealtimeOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Realtime/Models/RealtimeOptions.cs new file mode 100644 index 000000000..a720c88b0 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Realtime/Models/RealtimeOptions.cs @@ -0,0 +1,12 @@ +namespace BotSharp.Abstraction.Realtime.Models; + +public class RealtimeOptions +{ + [JsonPropertyName("input_audio_format")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? InputAudioFormat { get; set; } + + [JsonPropertyName("output_audio_format")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? OutputAudioFormat { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentCodeScriptFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentCodeScriptFilter.cs new file mode 100644 index 000000000..530bb4aa5 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentCodeScriptFilter.cs @@ -0,0 +1,12 @@ +namespace BotSharp.Abstraction.Repositories.Filters; + +public class AgentCodeScriptFilter +{ + public List? ScriptNames { get; set; } + public List? ScriptTypes { get; set; } + + public static AgentCodeScriptFilter Empty() + { + return new AgentCodeScriptFilter(); + } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 8e0e2d55c..f2ebe6a8f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -85,7 +85,6 @@ string GetAgentTemplate(string agentId, string templateName) => throw new NotImplementedException(); bool PatchAgentTemplate(string agentId, AgentTemplate template) => throw new NotImplementedException(); - bool UpdateAgentLabels(string agentId, List labels) => throw new NotImplementedException(); bool AppendAgentLabels(string agentId, List labels) @@ -99,13 +98,24 @@ ValueTask> GetAgentTasks(AgentTaskFilter filter) => throw new NotImplementedException(); void InsertAgentTask(AgentTask task) => throw new NotImplementedException(); - void BulkInsertAgentTasks(List tasks) + void BulkInsertAgentTasks(string agentId, List tasks) => throw new NotImplementedException(); void UpdateAgentTask(AgentTask task, AgentTaskField field) => throw new NotImplementedException(); - bool DeleteAgentTask(string agentId, List taskIds) + bool DeleteAgentTasks(string agentId, List? taskIds = null) + => throw new NotImplementedException(); + #endregion + + #region Agent Code + List GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null) + => throw new NotImplementedException(); + string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) + => throw new NotImplementedException(); + bool UpdateAgentCodeScripts(string agentId, List scripts) + => throw new NotImplementedException(); + bool BulkInsertAgentCodeScripts(string agentId, List scripts) => throw new NotImplementedException(); - bool DeleteAgentTasks() + bool DeleteAgentCodeScripts(string agentId, List? scripts = null) => throw new NotImplementedException(); #endregion diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs index 8f946d01e..bc63066f5 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Routing.Enums; -public class RoutingMode +public static class RoutingMode { public const string Eager = "eager"; public const string Lazy = "lazy"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs index c595d59c6..dc74b6ca8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Routing.Enums; -public class RuleType +public static class RuleType { /// /// Fallback to redirect agent diff --git a/src/Infrastructure/BotSharp.Abstraction/Tasks/Enums/TaskStatus.cs b/src/Infrastructure/BotSharp.Abstraction/Tasks/Enums/TaskStatus.cs index f36d00a0e..48d31c6e6 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Tasks/Enums/TaskStatus.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Tasks/Enums/TaskStatus.cs @@ -1,7 +1,7 @@ /// /// Agent task status /// -public class TaskStatus +public static class TaskStatus { public const string Scheduled = "scheduled"; public const string New = "new"; diff --git a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs index 8c057b950..b34f5fba4 100644 --- a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs +++ b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs @@ -24,7 +24,11 @@ public RealtimeHub(IServiceProvider services, ILogger logger) _logger = logger; } - public async Task ConnectToModel(Func? responseToUser = null, Func? init = null, List? initStates = null) + public async Task ConnectToModel( + Func? responseToUser = null, + Func? init = null, + List? initStates = null, + RealtimeOptions? options = null) { var convService = _services.GetRequiredService(); convService.SetConversationId(_conn.ConversationId, initStates ?? []); @@ -43,6 +47,7 @@ public async Task ConnectToModel(Func? responseToUser = null, Func var settings = _services.GetRequiredService(); _completer = _services.GetServices().First(x => x.Provider == settings.Provider); + _completer.SetOptions(options); await _completer.Connect( conn: _conn, diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index fb4d49c4d..d61f0f3dd 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -218,4 +218,33 @@ private List GetTasksFromFile(string fileDir) task.Content = content.Substring(suffix.Length).Trim(); return task; } + + private List GetCodeScriptsFromFile(string fileDir) + { + var scripts = new List(); + var codeDir = Path.Combine(fileDir, "codes"); + if (!Directory.Exists(codeDir)) + { + return scripts; + } + + var agentId = fileDir.Split(Path.DirectorySeparatorChar).Last(); + + foreach (var folder in Directory.EnumerateDirectories(codeDir)) + { + var scriptType = folder.Split(Path.DirectorySeparatorChar).Last(); + foreach (var file in Directory.EnumerateFiles(folder)) + { + scripts.Add(new AgentCodeScript + { + AgentId = agentId, + Name = Path.GetFileName(file), + ScriptType = scriptType, + Content = File.ReadAllText(file) + }); + } + } + + return scripts; + } } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs index 506e00bb6..cc38a151a 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs @@ -60,13 +60,16 @@ public async Task RefreshAgents(IEnumerable? agentIds = null) .SetSamples(samples); var tasks = GetTasksFromFile(dir); + var codeScripts = GetCodeScriptsFromFile(dir); var isAgentDeleted = _db.DeleteAgent(agent.Id); if (isAgentDeleted) { await Task.Delay(100); - _db.BulkInsertAgents(new List { agent }); - _db.BulkInsertAgentTasks(tasks); + _db.BulkInsertAgents([agent]); + _db.BulkInsertAgentTasks(agent.Id, tasks); + _db.BulkInsertAgentCodeScripts(agent.Id, codeScripts); + refreshedAgents.Add(agent.Name); _logger.LogInformation($"Agent {agent.Name} has been migrated."); } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index d99b39d96..4a2e1ec51 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -1,8 +1,5 @@ -using BotSharp.Abstraction.Repositories.Enums; -using BotSharp.Abstraction.Repositories.Settings; using BotSharp.Abstraction.Users.Enums; using BotSharp.Abstraction.Users.Models; -using System.IO; namespace BotSharp.Core.Agents.Services; @@ -13,7 +10,7 @@ public async Task UpdateAgent(Agent agent, AgentField updateField) if (agent == null || string.IsNullOrEmpty(agent.Id)) return; var userService = _services.GetRequiredService(); - var auth = await userService.GetUserAuthorizations(new List { agent.Id }); + var auth = await userService.GetUserAuthorizations([agent.Id]); var allowEdit = auth.IsAgentActionAllowed(agent.Id, UserAction.Edit); if (!allowEdit) @@ -57,81 +54,6 @@ public async Task UpdateAgent(Agent agent, AgentField updateField) await Task.CompletedTask; } - public async Task UpdateAgentFromFile(string id) - { - string updateResult; - var dbSettings = _services.GetRequiredService(); - var agentSettings = _services.GetRequiredService(); - - if (dbSettings.Default == RepositoryEnum.FileRepository) - { - updateResult = $"Invalid database repository setting: {dbSettings.Default}"; - _logger.LogWarning(updateResult); - return updateResult; - } - - var agent = _db.GetAgent(id); - if (agent == null) - { - updateResult = $"Cannot find agent ${id}"; - _logger.LogError(updateResult); - return updateResult; - } - - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, - dbSettings.FileRepository, - agentSettings.DataDir); - - var clonedAgent = Agent.Clone(agent); - var foundAgent = GetAgentFileById(agent.Id, filePath); - if (foundAgent == null) - { - updateResult = $"Cannot find agent {agent.Name} in file directory: {filePath}"; - _logger.LogError(updateResult); - return updateResult; - } - - try - { - clonedAgent.SetId(foundAgent.Id) - .SetName(foundAgent.Name) - .SetType(foundAgent.Type) - .SetRoutingMode(foundAgent.Mode) - .SetFuncVisMode(foundAgent.FuncVisMode) - .SetIsPublic(foundAgent.IsPublic) - .SetDisabled(foundAgent.Disabled) - .SetDescription(foundAgent.Description) - .SetMergeUtility(foundAgent.MergeUtility) - .SetProfiles(foundAgent.Profiles) - .SetLabels(foundAgent.Labels) - .SetRoutingRules(foundAgent.RoutingRules) - .SetInstruction(foundAgent.Instruction) - .SetChannelInstructions(foundAgent.ChannelInstructions) - .SetTemplates(foundAgent.Templates) - .SetFunctions(foundAgent.Functions) - .SetResponses(foundAgent.Responses) - .SetSamples(foundAgent.Samples) - .SetUtilities(foundAgent.Utilities) - .SetMcpTools(foundAgent.McpTools) - .SetKnowledgeBases(foundAgent.KnowledgeBases) - .SetRules(foundAgent.Rules) - .SetLlmConfig(foundAgent.LlmConfig); - - _db.UpdateAgent(clonedAgent, AgentField.All); - Utilities.ClearCache(); - - updateResult = $"Agent {agent.Name} has been migrated!"; - _logger.LogInformation(updateResult); - return updateResult; - } - catch (Exception ex) - { - updateResult = $"Failed to migrate agent {agent.Name} in file directory {filePath}.\r\nError: {ex.Message}"; - _logger.LogError(updateResult); - return updateResult; - } - } - public async Task PatchAgentTemplate(Agent agent) { @@ -184,31 +106,4 @@ public async Task PatchAgentTemplate(Agent agent) return patchResult; } - - private Agent? GetAgentFileById(string agentId, string filePath) - { - if (!Directory.Exists(filePath)) return null; - - foreach (var dir in Directory.GetDirectories(filePath)) - { - var agentJson = File.ReadAllText(Path.Combine(dir, "agent.json")); - var agent = JsonSerializer.Deserialize(agentJson, _options); - if (agent != null && agent.Id == agentId) - { - var (defaultInstruction, channelInstructions) = GetInstructionsFromFile(dir); - var functions = GetFunctionsFromFile(dir); - var responses = GetResponsesFromFile(dir); - var templates = GetTemplatesFromFile(dir); - var samples = GetSamplesFromFile(dir); - return agent.SetInstruction(defaultInstruction) - .SetChannelInstructions(channelInstructions) - .SetTemplates(templates) - .SetFunctions(functions) - .SetResponses(responses) - .SetSamples(samples); - } - } - - return null; - } } diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 7b0e55ac0..d960ca184 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -80,6 +80,7 @@ + @@ -126,7 +127,7 @@ PreserveNewest - + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs index bfae45bac..9c34de599 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs @@ -6,7 +6,6 @@ using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Messaging.JsonConverters; using BotSharp.Abstraction.Users.Settings; -using BotSharp.Abstraction.Interpreters.Settings; using BotSharp.Abstraction.Infrastructures; using BotSharp.Core.Processors; using StackExchange.Redis; @@ -24,10 +23,6 @@ public static class BotSharpCoreExtensions { public static IServiceCollection AddBotSharpCore(this IServiceCollection services, IConfiguration config, Action? configOptions = null) { - var interpreterSettings = new InterpreterSettings(); - config.Bind("Interpreter", interpreterSettings); - services.AddSingleton(x => interpreterSettings); - services.AddSingleton(); // Register template render services.AddSingleton(); diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Functions/ExecuteTemplateFn.cs b/src/Infrastructure/BotSharp.Core/Instructs/Functions/ExecuteTemplateFn.cs index b81c56910..2684ae02f 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Functions/ExecuteTemplateFn.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Functions/ExecuteTemplateFn.cs @@ -1,6 +1,4 @@ using BotSharp.Abstraction.Functions; -using BotSharp.Abstraction.Infrastructures; -using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Instructs.Models; namespace BotSharp.Core.Instructs.Functions; diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 773392f1c..11ff7518c 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -1,37 +1,54 @@ +using BotSharp.Abstraction.CodeInterpreter; using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Instructs.Models; using BotSharp.Abstraction.MLTasks; +using BotSharp.Abstraction.Models; namespace BotSharp.Core.Instructs; public partial class InstructService { - public async Task Execute(string agentId, RoleDialogModel message, - string? templateName = null, string? instruction = null, IEnumerable? files = null) + public async Task Execute( + string agentId, + RoleDialogModel message, + string? instruction = null, + string? templateName = null, + IEnumerable? files = null, + CodeInstructOptions? codeOptions = null) { var agentService = _services.GetRequiredService(); Agent agent = await agentService.LoadAgent(agentId); + var response = new InstructResult + { + MessageId = message.MessageId, + Template = templateName + }; + + if (agent == null) { - return new InstructResult - { - MessageId = message.MessageId, - Text = $"Agent (id: {agentId}) does not exist!" - }; + response.Text = $"Agent (id: {agentId}) does not exist!"; + return response; } if (agent.Disabled) { var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent."; - return new InstructResult - { - MessageId = message.MessageId, - Text = content - }; + response.Text = content; + return response; + } + + + // Run code template + var codeResponse = await GetCodeResponse(agent, message, templateName, codeOptions); + if (codeResponse != null) + { + return codeResponse; } - // Trigger before completion hooks + + // Before completion hooks var hooks = _services.GetHooks(agentId); foreach (var hook in hooks) { @@ -48,6 +65,7 @@ public async Task Execute(string agentId, RoleDialogModel messag } } + var provider = string.Empty; var model = string.Empty; @@ -59,12 +77,6 @@ public async Task Execute(string agentId, RoleDialogModel messag var completer = CompletionProvider.GetCompletion(_services, agentConfig: agent.LlmConfig); - var response = new InstructResult - { - MessageId = message.MessageId, - Template = templateName, - }; - if (completer is ITextCompletion textCompleter) { instruction = null; @@ -102,7 +114,7 @@ public async Task Execute(string agentId, RoleDialogModel messag response.Text = result.Content; } - + // After completion hooks foreach (var hook in hooks) { await hook.AfterCompletion(agent, response); @@ -120,4 +132,128 @@ await hook.OnResponseGenerated(new InstructResponseModel return response; } + + /// + /// Get code response + /// + /// + /// + /// + /// + /// + private async Task GetCodeResponse(Agent agent, RoleDialogModel message, string templateName, CodeInstructOptions? codeOptions) + { + InstructResult? response = null; + + if (agent == null) + { + return response; + } + + + var state = _services.GetRequiredService(); + var db = _services.GetRequiredService(); + var hooks = _services.GetHooks(agent.Id); + + var codeProvider = codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs("botsharp-py-interpreter"); + var codeInterpreter = _services.GetServices() + .FirstOrDefault(x => x.Provider.IsEqualTo(codeProvider)); + + if (codeInterpreter == null) + { +#if DEBUG + _logger.LogWarning($"No code interpreter found. (Agent: {agent.Id}, Code interpreter: {codeProvider})"); +#endif + return response; + } + + // Get code script name + var scriptName = string.Empty; + if (!string.IsNullOrEmpty(codeOptions?.CodeScriptName)) + { + scriptName = codeOptions.CodeScriptName; + } + else if (!string.IsNullOrEmpty(templateName)) + { + scriptName = $"{templateName}.py"; + } + + if (string.IsNullOrEmpty(scriptName)) + { +#if DEBUG + _logger.LogWarning($"Empty code script name. (Agent: {agent.Id}, {scriptName})"); +#endif + return response; + } + + // Get code script + var codeScript = db.GetAgentCodeScript(agent.Id, scriptName, scriptType: AgentCodeScriptType.Src); + if (string.IsNullOrWhiteSpace(codeScript)) + { +#if DEBUG + _logger.LogWarning($"Empty code script. (Agent: {agent.Id}, {scriptName})"); +#endif + return response; + } + + // Get code arguments + var arguments = codeOptions?.Arguments ?? []; + if (arguments.IsNullOrEmpty()) + { + arguments = state.GetStates().Select(x => new KeyValue(x.Key, x.Value)).ToList(); + } + + var context = new CodeInstructContext + { + CodeScript = codeScript, + Arguments = arguments + }; + + // Before code execution + foreach (var hook in hooks) + { + await hook.BeforeCodeExecution(agent, message, context); + + // Interrupted by hook + if (message.StopCompletion) + { + return new InstructResult + { + MessageId = message.MessageId, + Text = message.Content + }; + } + } + + // Run code script + var result = await codeInterpreter.RunCode(context.CodeScript, options: new() + { + Arguments = context.Arguments + }); + + response = new InstructResult + { + MessageId = message.MessageId, + Template = scriptName, + Text = result?.Result?.ToString() + }; + + // After code execution + foreach (var hook in hooks) + { + await hook.AfterCodeExecution(agent, response); + await hook.OnResponseGenerated(new InstructResponseModel + { + AgentId = agent.Id, + Provider = codeInterpreter.Provider, + Model = string.Empty, + TemplateName = scriptName, + UserMessage = string.Empty, + SystemInstruction = string.Empty, + CompletionText = response.Text + }); + } + + return response; + } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index 35351311c..bd2599526 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -547,6 +547,12 @@ join u in Users on ua.UserId equals u.Id public string GetAgentTemplate(string agentId, string templateName) { + if (string.IsNullOrWhiteSpace(agentId) + || string.IsNullOrWhiteSpace(templateName)) + { + return string.Empty; + } + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_TEMPLATES_FOLDER); if (!Directory.Exists(dir)) return string.Empty; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs new file mode 100644 index 000000000..764287ddf --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs @@ -0,0 +1,157 @@ +using System.IO; + +namespace BotSharp.Core.Repository; + +public partial class FileRepository +{ + #region Code script + public List GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return []; + } + + var dir = BuildAgentCodeScriptDir(agentId); + if (!Directory.Exists(dir)) + { + return []; + } + + filter ??= AgentCodeScriptFilter.Empty(); + var results = new List(); + + foreach (var folder in Directory.EnumerateDirectories(dir)) + { + var scriptType = folder.Split(Path.DirectorySeparatorChar).Last(); + if (filter.ScriptTypes != null && !filter.ScriptTypes.Contains(scriptType)) + { + continue; + } + + foreach (var file in Directory.EnumerateFiles(folder)) + { + var fileName = Path.GetFileName(file); + if (filter.ScriptNames != null && !filter.ScriptNames.Contains(fileName)) + { + continue; + } + + results.Add(new AgentCodeScript + { + AgentId = agentId, + Name = fileName, + ScriptType = scriptType, + Content = File.ReadAllText(file) + }); + } + } + + return results; + } + + public string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) + { + if (string.IsNullOrWhiteSpace(agentId) + || string.IsNullOrWhiteSpace(scriptName) + || string.IsNullOrWhiteSpace(scriptType)) + { + return null; + } + + var dir = BuildAgentCodeScriptDir(agentId, scriptType); + if (!Directory.Exists(dir)) + { + return null; + } + + var foundFile = Directory.GetFiles(dir).FirstOrDefault(file => scriptName.IsEqualTo(Path.GetFileName(file))); + if (!string.IsNullOrEmpty(foundFile)) + { + return File.ReadAllText(foundFile); + } + return string.Empty; + } + + public bool UpdateAgentCodeScripts(string agentId, List scripts) + { + if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) + { + return false; + } + + foreach (var script in scripts) + { + if (string.IsNullOrWhiteSpace(script.Name) + || string.IsNullOrWhiteSpace(script.ScriptType)) + { + continue; + } + + var dir = BuildAgentCodeScriptDir(agentId, script.ScriptType); + if (!Directory.Exists(dir)) + { + continue; + } + + var file = Path.Combine(dir, script.Name); + File.WriteAllText(file, script.Content); + } + + return true; + } + + public bool BulkInsertAgentCodeScripts(string agentId, List scripts) + { + return UpdateAgentCodeScripts(agentId, scripts); + } + + public bool DeleteAgentCodeScripts(string agentId, List? scripts = null) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return false; + } + + var dir = BuildAgentCodeScriptDir(agentId); + if (!Directory.Exists(dir)) + { + return false; + } + + if (scripts == null) + { + Directory.Delete(dir, true); + return true; + } + else if (!scripts.Any()) + { + return false; + } + + var dict = scripts.DistinctBy(x => x.CodePath).ToDictionary(x => x.CodePath, x => x); + foreach (var pair in dict) + { + var file = Path.Combine(dir, pair.Value.ScriptType, pair.Value.Name); + if (File.Exists(file)) + { + File.Delete(file); + } + } + + return true; + } + #endregion + + #region Private methods + private string BuildAgentCodeScriptDir(string agentId, string? scirptType = null) + { + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER); + if (!string.IsNullOrWhiteSpace(scirptType)) + { + dir = Path.Combine(dir, scirptType); + } + return dir; + } + #endregion +} diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs index 2b84f212f..b3926e66f 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs @@ -137,7 +137,7 @@ public void InsertAgentTask(AgentTask task) File.WriteAllText(taskFile, fileContent); } - public void BulkInsertAgentTasks(List tasks) + public void BulkInsertAgentTasks(string agentId, List tasks) { } @@ -194,13 +194,25 @@ public void UpdateAgentTask(AgentTask task, AgentTaskField field) File.WriteAllText(taskFile, fileContent); } - public bool DeleteAgentTask(string agentId, List taskIds) + public bool DeleteAgentTasks(string agentId, List? taskIds = null) { var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId); - if (!Directory.Exists(agentDir) || taskIds.IsNullOrEmpty()) return false; + if (!Directory.Exists(agentDir)) + { + return false; + } var taskDir = Path.Combine(agentDir, AGENT_TASKS_FOLDER); - if (!Directory.Exists(taskDir)) return false; + if (!Directory.Exists(taskDir)) + { + return false; + } + + if (taskIds == null) + { + Directory.Delete(taskDir, true); + return true; + } var deletedTasks = new List(); foreach (var taskId in taskIds) @@ -215,11 +227,6 @@ public bool DeleteAgentTask(string agentId, List taskIds) return deletedTasks.Any(); } - public bool DeleteAgentTasks() - { - return false; - } - private string? FindTaskFileById(string taskDir, string taskId) { if (!Directory.Exists(taskDir) || string.IsNullOrEmpty(taskId)) return null; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index 8e448c25e..69e8bd38f 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -26,6 +26,7 @@ public partial class FileRepository : IBotSharpRepository private const string AGENT_FUNCTIONS_FOLDER = "functions"; private const string AGENT_TEMPLATES_FOLDER = "templates"; private const string AGENT_RESPONSES_FOLDER = "responses"; + private const string AGENT_CODES_FOLDER = "codes"; private const string AGENT_TASKS_FOLDER = "tasks"; private const string AGENT_TASK_PREFIX = "#metadata"; private const string AGENT_TASK_SUFFIX = "/metadata"; diff --git a/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs b/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs index 5ae52a73a..c6ac878db 100644 --- a/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs +++ b/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs @@ -91,7 +91,7 @@ public async Task UpdateTask(AgentTask task, AgentTaskField field) public async Task DeleteTask(string agentId, string taskId) { var db = _services.GetRequiredService(); - var isDeleted = db.DeleteAgentTask(agentId, new List { taskId }); + var isDeleted = db.DeleteAgentTasks(agentId, new List { taskId }); return await Task.FromResult(isDeleted); } } diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/codes/src/demo.py b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/codes/src/demo.py new file mode 100644 index 000000000..438a6a87f --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/codes/src/demo.py @@ -0,0 +1,12 @@ +import argparse + +def main(): + parser = argparse.ArgumentParser(description="Receive named arguments") + parser.add_argument("--first_name", required=True, help="The first name") + parser.add_argument("--last_name", required=True, help="The last name") + + args = parser.parse_args() + print(f"Hello, {args.first_name} {args.last_name}!") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/InstructionLogHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/InstructionLogHook.cs index 8de3db703..c95af0c1d 100644 --- a/src/Infrastructure/BotSharp.Logger/Hooks/InstructionLogHook.cs +++ b/src/Infrastructure/BotSharp.Logger/Hooks/InstructionLogHook.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Instructs.Settings; using BotSharp.Abstraction.Loggers.Models; using BotSharp.Abstraction.Users; +using BotSharp.Abstraction.Utilities; namespace BotSharp.Logger.Hooks; @@ -39,7 +40,9 @@ public override async Task OnResponseGenerated(InstructResponseModel response) var state = _services.GetRequiredService(); var user = db.GetUserById(_user.Id); - var templateName = response.TemplateName ?? state.GetState("instruct_template_name") ?? null; + var templateName = response.TemplateName + .IfNullOrEmptyAs(state.GetState("instruct_template_name")) + .IfNullOrEmptyAs(null); db.SaveInstructionLogs(new List { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index b6950e04b..6aff56aee 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -115,12 +115,6 @@ public async Task RefreshAgents([FromBody] AgentMigrationModel request) return await _agentService.RefreshAgents(request?.AgentIds); } - [HttpPut("/agent/file/{agentId}")] - public async Task UpdateAgentFromFile([FromRoute] string agentId) - { - return await _agentService.UpdateAgentFromFile(agentId); - } - [HttpPut("/agent/{agentId}")] public async Task UpdateAgent([FromRoute] string agentId, [FromBody] AgentUpdateModel agent) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index 852d780a6..eab8637c1 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -36,12 +36,12 @@ public async Task InstructCompletion([FromRoute] string agentId, var instructor = _services.GetRequiredService(); var result = await instructor.Execute(agentId, new RoleDialogModel(AgentRole.User, input.Text), - templateName: input.Template, instruction: input.Instruction, - files: input.Files); + templateName: input.Template, + files: input.Files, + codeOptions: input.CodeOptions); result.States = state.GetStates(); - return result; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs index aec81ffbe..05c151be1 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Instructs.Models; + namespace BotSharp.OpenAPI.ViewModels.Instructs; public class InstructMessageModel : IncomingMessageModel @@ -9,6 +11,7 @@ public class InstructMessageModel : IncomingMessageModel public override string Channel { get; set; } = ConversationChannel.OpenAPI; public string? Template { get; set; } public List Files { get; set; } = []; + public CodeInstructOptions? CodeOptions { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs index 676c182f1..0d24bc41f 100644 --- a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs @@ -136,17 +136,17 @@ public Task GetChatCompletionsStreamingAsync(Agent agent, List< filteredMessages = filteredMessages.Where((_, idx) => idx >= firstUserMsgIdx).ToList(); } - foreach (var conv in filteredMessages) + foreach (var message in filteredMessages) { - if (conv.Role == AgentRole.User) + if (message.Role == AgentRole.User) { - messages.Add(new Message(RoleType.User, conv.Payload ?? conv.Content)); + messages.Add(new Message(RoleType.User, message.RoleContent)); } - else if (conv.Role == AgentRole.Assistant) + else if (message.Role == AgentRole.Assistant) { - messages.Add(new Message(RoleType.Assistant, conv.Content)); + messages.Add(new Message(RoleType.Assistant, message.RoleContent)); } - else if (conv.Role == AgentRole.Function) + else if (message.Role == AgentRole.Function) { messages.Add(new Message { @@ -155,9 +155,9 @@ public Task GetChatCompletionsStreamingAsync(Agent agent, List< { new ToolUseContent() { - Id = conv.ToolCallId, - Name = conv.FunctionName, - Input = JsonNode.Parse(conv.FunctionArgs ?? "{}") + Id = message.ToolCallId, + Name = message.FunctionName, + Input = JsonNode.Parse(message.FunctionArgs ?? "{}") } } }); @@ -169,8 +169,8 @@ public Task GetChatCompletionsStreamingAsync(Agent agent, List< { new ToolResultContent() { - ToolUseId = conv.ToolCallId, - Content = [new TextContent() { Text = conv.Content }] + ToolUseId = message.ToolCallId, + Content = [new TextContent() { Text = message.RoleContent }] } } }); diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs index 8aaf043a4..b7efb11ff 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -432,11 +432,11 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}")) })); - messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content)); + messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.RoleContent)); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; @@ -448,7 +448,7 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, } else if (message.Role == AgentRole.Assistant) { - var text = message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid index dfb4da4f2..303ab4089 100644 --- a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid @@ -1,9 +1,12 @@ -Please take a look at "Plotting Requirement" and generate a javascript code that can be used to render the charts on an html element. +You are a Chart plotter that use javascript code to plot chart. +Please read {% if user_requirement != empty %}"User Requirement" and{% endif %} the chat context, and then generate a javascript code that can be used to render the charts on an html element. + You must strictly follow the "Hard Requirements", "Render Requirements", "Code Requirements" and "Response Format" below. +{% if user_requirement != empty %} === Plotting Requirement === {{ plotting_requirement }} - +{% endif %} ***** Hard Requirements ***** ** Your output javascript code must be wrapped in one or multiple blocks with everything needed inside. diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs index 44a6195a7..a08fda080 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Models; using BotSharp.Abstraction.Realtime.Models.Session; using BotSharp.Core.Session; using Microsoft.AspNetCore.Http; @@ -84,7 +83,7 @@ private async Task HandleWebSocket(IServiceProvider services, string agentId, st _logger.LogCritical($"Start chat stream connection for conversation ({conversationId})"); #endif var request = InitRequest(data, conversationId); - await ConnectToModel(hub, session, request?.States); + await ConnectToModel(hub, session, request); } else if (eventType == "media") { @@ -107,7 +106,7 @@ private async Task HandleWebSocket(IServiceProvider services, string agentId, st await session.DisconnectAsync(); } - private async Task ConnectToModel(IRealtimeHub hub, BotSharpRealtimeSession session, List? states = null) + private async Task ConnectToModel(IRealtimeHub hub, BotSharpRealtimeSession session, ChatStreamRequest? request) { await hub.ConnectToModel(responseToUser: async data => { @@ -115,7 +114,7 @@ await hub.ConnectToModel(responseToUser: async data => { await session.SendEventAsync(data); } - }, initStates: states); + }, initStates: request?.States, options: request?.Options); } private (string, string) MapEvents(RealtimeHubConnection conn, string receivedText, string conversationId) diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs index 1a72d7dce..13ef8f0e7 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs @@ -7,4 +7,8 @@ public class ChatStreamRequest { [JsonPropertyName("states")] public List States { get; set; } = []; + + [JsonPropertyName("realtime_options")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public RealtimeOptions? Options { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs index cb661aaae..eb143ebb3 100644 --- a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs @@ -399,11 +399,11 @@ public void SetModelName(string model) ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}")) })); - messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content)); + messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.RoleContent)); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; @@ -415,7 +415,7 @@ public void SetModelName(string model) } else if (message.Role == AgentRole.Assistant) { - var text = message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs index 85674a4b8..1d2b4ef48 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs @@ -263,17 +263,17 @@ public void SetModelName(string model) Name = message.FunctionName, Response = new JsonObject() { - ["result"] = message.Content ?? string.Empty + ["result"] = message.RoleContent ?? string.Empty } } } ], AgentRole.Function)); - convPrompts.Add($"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs}) => {message.Content}"); + convPrompts.Add($"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs}) => {message.RoleContent}"); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var text = message.RoleContent; var contentParts = new List { new() { Text = text } }; if (allowMultiModal && !message.Files.IsNullOrEmpty()) @@ -285,7 +285,7 @@ public void SetModelName(string model) } else if (message.Role == AgentRole.Assistant) { - var text = message.Content; + var text = message.RoleContent; var contentParts = new List { new() { Text = text } }; if (allowMultiModal && !message.Files.IsNullOrEmpty()) @@ -294,7 +294,7 @@ public void SetModelName(string model) } contents.Add(new Content(contentParts, AgentRole.Model)); - convPrompts.Add($"{AgentRole.Assistant}: {message.Content}"); + convPrompts.Add($"{AgentRole.Assistant}: {text}"); } } diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs index c37bb751c..8dfaa695b 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs @@ -108,7 +108,7 @@ public async Task GetChatCompletions(Agent agent, List new PalmChatMessage(c.Content, c.Role == AgentRole.User ? "user" : "AI")) + var messages = conversations.Select(c => new PalmChatMessage(c.RoleContent, c.Role == AgentRole.User ? "user" : "AI")) .ToList(); if (!functions.IsNullOrEmpty()) @@ -124,8 +124,8 @@ public async Task GetChatCompletions(Agent agent, List {dialog.Content}\r\n" : - $"{dialog.Role}: {dialog.Content}\r\n"; + $"{dialog.Role}: {dialog.FunctionName} => {dialog.RoleContent}\r\n" : + $"{dialog.Role}: {dialog.RoleContent}\r\n"; } prompt += "\r\n\r\n" + router.Templates.FirstOrDefault(x => x.Name == "response_with_function").Content; diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs index 983b203a5..802088bdf 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -58,11 +58,6 @@ public GoogleRealTimeProvider( _logger = logger; } - public void SetModelName(string model) - { - _model = model; - } - public async Task Connect( RealtimeHubConnection conn, Func onModelReady, @@ -420,6 +415,16 @@ await SendEventToModel(new RealtimeClientPayload } } + public void SetModelName(string model) + { + _model = model; + } + + public void SetOptions(RealtimeOptions? options) + { + + } + #region Private methods private List OnFunctionCall(RealtimeHubConnection conn, RealtimeFunctionCall functionCall) { @@ -477,7 +482,8 @@ await hook.AfterGenerated(new RoleDialogModel(AgentRole.Assistant, text) } - private (string, GenerateContentRequest) PrepareOptions(Agent agent, + private (string, GenerateContentRequest) PrepareOptions( + Agent agent, List conversations) { var agentService = _services.GetRequiredService(); @@ -559,25 +565,25 @@ await hook.AfterGenerated(new RoleDialogModel(AgentRole.Assistant, text) Name = message.FunctionName ?? string.Empty, Response = new JsonObject() { - ["result"] = message.Content ?? string.Empty + ["result"] = message.RoleContent ?? string.Empty } } } ], AgentRole.Function)); convPrompts.Add( - $"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs}) => {message.Content}"); + $"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs}) => {message.RoleContent}"); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var text = message.RoleContent; contents.Add(new Content(text, AgentRole.User)); convPrompts.Add($"{AgentRole.User}: {text}"); } else if (message.Role == AgentRole.Assistant) { - contents.Add(new Content(message.Content, AgentRole.Model)); - convPrompts.Add($"{AgentRole.Assistant}: {message.Content}"); + contents.Add(new Content(message.RoleContent, AgentRole.Model)); + convPrompts.Add($"{AgentRole.Assistant}: {message.RoleContent}"); } } diff --git a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs index 1380881ba..ea6f75fdf 100644 --- a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs @@ -42,7 +42,7 @@ public async Task GetChatCompletions(Agent agent, List $"{x.Role}: {x.Content}")).Trim(); + var content = string.Join("\r\n", conversations.Select(x => $"{x.Role}: {x.RoleContent}")).Trim(); content += $"\r\n{AgentRole.Assistant}: "; var llama = _services.GetRequiredService(); @@ -118,7 +118,7 @@ public async Task GetChatCompletionsAsync(Agent agent, Func onMessageReceived, Func onFunctionExecuting) { - var content = string.Join("\r\n", conversations.Select(x => $"{x.Role}: {x.Content}")).Trim(); + var content = string.Join("\r\n", conversations.Select(x => $"{x.Role}: {x.RoleContent}")).Trim(); content += $"\r\n{AgentRole.Assistant}: "; var state = _services.GetRequiredService(); diff --git a/src/Plugins/BotSharp.Plugin.LangChain/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.LangChain/Providers/ChatCompletionProvider.cs index 9e2aa1d46..a7a4adddf 100644 --- a/src/Plugins/BotSharp.Plugin.LangChain/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.LangChain/Providers/ChatCompletionProvider.cs @@ -41,7 +41,7 @@ public async Task GetChatCompletions(Agent agent, List new Message(c.Content, c.Role == AgentRole.User ? MessageRole.Human : MessageRole.Ai)).ToList(); + .Select(c => new Message(c.RoleContent, c.Role == AgentRole.User ? MessageRole.Human : MessageRole.Ai)).ToList(); var response = await model.GenerateAsync(new ChatRequest { Messages = messages }, _settings); diff --git a/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs index f7831e1a9..2505eed1e 100644 --- a/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs @@ -111,8 +111,8 @@ private string PrepareOptions(Agent agent, List conversations, foreach (var message in samples) { messages.Add(message.Role == AgentRole.User ? - new MessageItem("user", message.Content) : - new MessageItem("assistant", message.Content)); + new MessageItem("user", message.RoleContent) : + new MessageItem("assistant", message.RoleContent)); } foreach (var function in functions) @@ -129,13 +129,13 @@ private string PrepareOptions(Agent agent, List conversations, } else if (message.Role == "user") { - var userMessage = new MessageItem("user",message.Content); + var userMessage = new MessageItem("user",message.RoleContent); messages.Add(userMessage); } else if (message.Role == "assistant") { - messages.Add(new MessageItem("assistant", message.Content)); + messages.Add(new MessageItem("assistant", message.RoleContent)); } } diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs index 8440dc455..43f5134a0 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs @@ -109,16 +109,16 @@ public async Task GetChatCompletions(Agent agent, List>(x.FunctionArgs ?? "{}")), - new FunctionResultContent(x.FunctionName, x.Content) + new FunctionResultContent(x.FunctionName, x.RoleContent) ])); } else if (x.Role == AgentRole.System || x.Role == AgentRole.Assistant) { - messages.Add(new(x.Role == AgentRole.System ? ChatRole.System : ChatRole.Assistant, x.Content)); + messages.Add(new(x.Role == AgentRole.System ? ChatRole.System : ChatRole.Assistant, x.RoleContent)); } else if (x.Role == AgentRole.User) { - List contents = [new TextContent(!string.IsNullOrWhiteSpace(x.Payload) ? x.Payload : x.Content)]; + List contents = [new TextContent(x.RoleContent)]; if (allowMultiModal) { foreach (var file in x.Files) diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs new file mode 100644 index 000000000..9a39fb58c --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs @@ -0,0 +1,37 @@ +using BotSharp.Abstraction.Agents.Models; + +namespace BotSharp.Plugin.MongoStorage.Collections; + +public class AgentCodeScriptDocument : MongoBase +{ + public string AgentId { get; set; } = default!; + public string Name { get; set; } = default!; + public string Content { get; set; } = default!; + public string ScriptType { get; set; } = default!; + public DateTime CreatedTime { get; set; } + public DateTime UpdatedTime { get; set; } + + public static AgentCodeScriptDocument ToMongoModel(AgentCodeScript script) + { + return new AgentCodeScriptDocument + { + Id = script.Id, + AgentId = script.AgentId, + Name = script.Name, + Content = script.Content, + ScriptType = script.ScriptType + }; + } + + public static AgentCodeScript ToDomainModel(AgentCodeScriptDocument script) + { + return new AgentCodeScript + { + Id = script.Id, + AgentId = script.AgentId, + Name = script.Name, + Content = script.Content, + ScriptType = script.ScriptType + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs index 992dc6f82..a0d0ccff6 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs @@ -160,6 +160,9 @@ public IMongoCollection Agents public IMongoCollection AgentTasks => CreateAgentTaskIndex(); + public IMongoCollection AgentCodeScripts + => GetCollectionOrCreate("AgentCodeScripts"); + public IMongoCollection Conversations => CreateConversationIndex(); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 40558e131..76cadd324 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -593,6 +593,8 @@ public bool DeleteAgents() { _dc.UserAgents.DeleteMany(Builders.Filter.Empty); _dc.RoleAgents.DeleteMany(Builders.Filter.Empty); + _dc.AgentTasks.DeleteMany(Builders.Filter.Empty); + _dc.AgentCodeScripts.DeleteMany(Builders.Filter.Empty); _dc.Agents.DeleteMany(Builders.Filter.Empty); return true; } @@ -612,11 +614,13 @@ public bool DeleteAgent(string agentId) var userAgentFilter = Builders.Filter.Eq(x => x.AgentId, agentId); var roleAgentFilter = Builders.Filter.Eq(x => x.AgentId, agentId); var agentTaskFilter = Builders.Filter.Eq(x => x.AgentId, agentId); + var agentCodeFilter = Builders.Filter.Eq(x => x.AgentId, agentId); - _dc.Agents.DeleteOne(agentFilter); _dc.UserAgents.DeleteMany(userAgentFilter); _dc.RoleAgents.DeleteMany(roleAgentFilter); _dc.AgentTasks.DeleteMany(agentTaskFilter); + _dc.AgentCodeScripts.DeleteMany(agentCodeFilter); + _dc.Agents.DeleteOne(agentFilter); return true; } catch diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs new file mode 100644 index 000000000..3d551b7d0 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs @@ -0,0 +1,132 @@ +using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Repositories.Filters; + +namespace BotSharp.Plugin.MongoStorage.Repository; + +public partial class MongoRepository +{ + #region Code script + public List GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return []; + } + + filter ??= AgentCodeScriptFilter.Empty(); + + var builder = Builders.Filter; + var filters = new List>() + { + builder.Eq(x => x.AgentId, agentId) + }; + + if (!filter.ScriptNames.IsNullOrEmpty()) + { + filters.Add(builder.In(x => x.Name, filter.ScriptNames)); + } + if (!filter.ScriptTypes.IsNullOrEmpty()) + { + filters.Add(builder.In(x => x.ScriptType, filter.ScriptTypes)); + } + + var found = _dc.AgentCodeScripts.Find(builder.And(filters)).ToList(); + return found.Select(x => AgentCodeScriptDocument.ToDomainModel(x)).ToList(); + } + + public string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) + { + if (string.IsNullOrWhiteSpace(agentId) + || string.IsNullOrWhiteSpace(scriptName) + || string.IsNullOrWhiteSpace(scriptType)) + { + return null; + } + + var builder = Builders.Filter; + var filters = new List>() + { + builder.Eq(x => x.AgentId, agentId), + builder.Eq(x => x.Name, scriptName), + builder.Eq(x => x.ScriptType, scriptType) + }; + + var found = _dc.AgentCodeScripts.Find(builder.And(filters)).FirstOrDefault(); + return found?.Content; + } + + public bool UpdateAgentCodeScripts(string agentId, List scripts) + { + if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) + { + return false; + } + + var builder = Builders.Filter; + var ops = scripts.Where(x => !string.IsNullOrWhiteSpace(x.Name)) + .Select(x => new UpdateOneModel( + builder.And(new List> + { + builder.Eq(y => y.AgentId, agentId), + builder.Eq(y => y.Name, x.Name), + builder.Eq(y => y.ScriptType, x.ScriptType) + }), + Builders.Update.Set(y => y.Content, x.Content) + .Set(x => x.UpdatedTime, DateTime.UtcNow) + )) + .ToList(); + + var result = _dc.AgentCodeScripts.BulkWrite(ops, new BulkWriteOptions { IsOrdered = false }); + return result.ModifiedCount > 0 || result.MatchedCount > 0; + } + + public bool BulkInsertAgentCodeScripts(string agentId, List scripts) + { + if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) + { + return false; + } + + var docs = scripts.Select(x => + { + var script = AgentCodeScriptDocument.ToMongoModel(x); + script.AgentId = agentId; + script.Id = x.Id.IfNullOrEmptyAs(Guid.NewGuid().ToString()); + script.CreatedTime = DateTime.UtcNow; + script.UpdatedTime = DateTime.UtcNow; + return script; + }).ToList(); + + _dc.AgentCodeScripts.InsertMany(docs); + return true; + } + + public bool DeleteAgentCodeScripts(string agentId, List? scripts = null) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return false; + } + + DeleteResult deleted; + if (scripts != null) + { + var scriptPaths = scripts.Select(x => x.CodePath); + var exprFilter = new BsonDocument("$expr", new BsonDocument("$in", new BsonArray + { + new BsonDocument("$concat", new BsonArray { "$ScriptType", "/", "$Name" }), + new BsonArray(scriptPaths) + })); + + var filterDef = new BsonDocumentFilterDefinition(exprFilter); + deleted = _dc.AgentCodeScripts.DeleteMany(filterDef); + } + else + { + deleted = _dc.AgentCodeScripts.DeleteMany(Builders.Filter.Empty); + } + + return deleted.DeletedCount > 0; + } + #endregion +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs index 85f142157..060994d44 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Tasks.Models; +using MongoDB.Driver; namespace BotSharp.Plugin.MongoStorage.Repository; @@ -86,13 +87,17 @@ public void InsertAgentTask(AgentTask task) _dc.AgentTasks.InsertOne(taskDoc); } - public void BulkInsertAgentTasks(List tasks) + public void BulkInsertAgentTasks(string agentId, List tasks) { - if (tasks.IsNullOrEmpty()) return; + if (string.IsNullOrWhiteSpace(agentId) || tasks.IsNullOrEmpty()) + { + return; + } var taskDocs = tasks.Select(x => { var task = AgentTaskDocument.ToMongoModel(x); + task.AgentId = agentId; task.Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(); return task; }).ToList(); @@ -138,30 +143,21 @@ public void UpdateAgentTask(AgentTask task, AgentTaskField field) _dc.AgentTasks.ReplaceOne(filter, taskDoc); } - public bool DeleteAgentTask(string agentId, List taskIds) - { - if (taskIds.IsNullOrEmpty()) return false; - - var builder = Builders.Filter; - var filters = new List> - { - builder.In(x => x.Id, taskIds) - }; - var taskDeleted = _dc.AgentTasks.DeleteMany(builder.And(filters)); - return taskDeleted.DeletedCount > 0; - } - - public bool DeleteAgentTasks() + public bool DeleteAgentTasks(string agentId, List? taskIds = null) { - try + var filterDef = Builders.Filter.Empty; + if (taskIds != null) { - _dc.AgentTasks.DeleteMany(Builders.Filter.Empty); - return true; - } - catch - { - return false; + var builder = Builders.Filter; + var filters = new List> + { + builder.In(x => x.Id, taskIds) + }; + filterDef = builder.And(filters); } + + var taskDeleted = _dc.AgentTasks.DeleteMany(filterDef); + return taskDeleted.DeletedCount > 0; } #endregion } diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs index a36e32b66..3812a5916 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -402,11 +402,11 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}")) })); - messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content)); + messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.RoleContent)); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; @@ -418,7 +418,7 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, } else if (message.Role == AgentRole.Assistant) { - var text = message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs index 890a8619c..e6b614e67 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -18,6 +18,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion private string _model = "gpt-4o-mini-realtime-preview"; private LlmRealtimeSession _session; + private RealtimeOptions? _realtimeOptions; private bool _isBlocking = false; private RealtimeHubConnection _conn; @@ -338,8 +339,8 @@ public async Task UpdateSession(RealtimeHubConnection conn, bool isInit type = "session.update", session = new RealtimeSessionUpdateRequest { - InputAudioFormat = realtimeModelSettings.InputAudioFormat, - OutputAudioFormat = realtimeModelSettings.OutputAudioFormat, + InputAudioFormat = _realtimeOptions?.InputAudioFormat ?? realtimeModelSettings.InputAudioFormat, + OutputAudioFormat = _realtimeOptions?.OutputAudioFormat ?? realtimeModelSettings.OutputAudioFormat, Voice = realtimeModelSettings.Voice, Instructions = instruction, ToolChoice = "auto", @@ -457,6 +458,11 @@ public void SetModelName(string model) _model = model; } + public void SetOptions(RealtimeOptions? options) + { + _realtimeOptions = options; + } + #region Private methods private async Task> OnResponsedDone(RealtimeHubConnection conn, string response) { @@ -549,10 +555,8 @@ private async Task OnUserAudioTranscriptionCompleted(RealtimeHu { var agentService = _services.GetRequiredService(); var state = _services.GetRequiredService(); - var fileStorage = _services.GetRequiredService(); var settingsService = _services.GetRequiredService(); var settings = settingsService.GetSetting(Provider, _model); - var allowMultiModal = settings != null && settings.MultiModal; var messages = new List(); @@ -617,44 +621,15 @@ private async Task OnUserAudioTranscriptionCompleted(RealtimeHu ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}")) })); - messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content)); + messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.RoleContent)); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; - var textPart = ChatMessageContentPart.CreateTextPart(text); - var contentParts = new List { textPart }; - - if (allowMultiModal && !message.Files.IsNullOrEmpty()) - { - foreach (var file in message.Files) - { - if (!string.IsNullOrEmpty(file.FileData)) - { - var (contentType, binary) = FileUtility.GetFileInfoFromData(file.FileData); - var contentPart = ChatMessageContentPart.CreateImagePart(binary, contentType, ChatImageDetailLevel.Auto); - contentParts.Add(contentPart); - } - else if (!string.IsNullOrEmpty(file.FileStorageUrl)) - { - var contentType = FileUtility.GetFileContentType(file.FileStorageUrl); - var binary = fileStorage.GetFileBytes(file.FileStorageUrl); - var contentPart = ChatMessageContentPart.CreateImagePart(binary, contentType, ChatImageDetailLevel.Auto); - contentParts.Add(contentPart); - } - else if (!string.IsNullOrEmpty(file.FileUrl)) - { - var uri = new Uri(file.FileUrl); - var contentPart = ChatMessageContentPart.CreateImagePart(uri, ChatImageDetailLevel.Auto); - contentParts.Add(contentPart); - } - } - } - messages.Add(new UserChatMessage(contentParts) { ParticipantName = message.FunctionName }); + messages.Add(new UserChatMessage(message.RoleContent)); } else if (message.Role == AgentRole.Assistant) { - messages.Add(new AssistantChatMessage(message.Content)); + messages.Add(new AssistantChatMessage(message.RoleContent)); } } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj b/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj index 391593b36..8b0775e8c 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) @@ -11,15 +11,17 @@ - - + - + PreserveNewest - + + PreserveNewest + + PreserveNewest @@ -29,7 +31,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs index 95a5b8a2b..813031af1 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs @@ -2,5 +2,5 @@ namespace BotSharp.Plugin.PythonInterpreter.Enums; public class UtilityName { - public const string PythonInterpreter = "python-interpreter"; + public const string PythonProgrammer = "python-programmer"; } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/InterpretationFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/InterpretationFn.cs deleted file mode 100644 index b12cb7390..000000000 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/InterpretationFn.cs +++ /dev/null @@ -1,46 +0,0 @@ -using BotSharp.Abstraction.Conversations.Models; -using BotSharp.Abstraction.Functions; -using BotSharp.Abstraction.Interpreters.Models; -using Microsoft.Extensions.Logging; -using Python.Runtime; -using System.Text.Json; -using System.Threading.Tasks; - -namespace BotSharp.Plugin.PythonInterpreter.Functions; - -public class InterpretationFn : IFunctionCallback -{ - public string Name => "python_interpreter"; - public string Indication => "Interpreting python code"; - - private readonly IServiceProvider _services; - private readonly ILogger _logger; - - public async Task Execute(RoleDialogModel message) - { - var args = JsonSerializer.Deserialize(message.FunctionArgs); - - using (Py.GIL()) - { - // Import necessary Python modules - dynamic sys = Py.Import("sys"); - dynamic io = Py.Import("io"); - - // Redirect standard output to capture it - dynamic stringIO = io.StringIO(); - sys.stdout = stringIO; - - // Execute a simple Python script - using var locals = new PyDict(); - PythonEngine.Exec(args.Script, null, locals); - - // Console.WriteLine($"Result from Python: {result}"); - message.Content = stringIO.getvalue(); - - // Restore the original stdout - sys.stdout = sys.__stdout__; - } - - return true; - } -} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs new file mode 100644 index 000000000..30d306587 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs @@ -0,0 +1,189 @@ +using Microsoft.Extensions.Logging; +using Python.Runtime; +using System.Text.Json; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.PythonInterpreter.Functions; + +public class PyProgrammerFn : IFunctionCallback +{ + public string Name => "util-code-python_programmer"; + public string Indication => "Programming and executing code"; + + private readonly IServiceProvider _services; + private readonly ILogger _logger; + private readonly PythonInterpreterSettings _settings; + + public PyProgrammerFn( + IServiceProvider services, + ILogger logger, + PythonInterpreterSettings settings) + { + _services = services; + _logger = logger; + _settings = settings; + } + + public async Task Execute(RoleDialogModel message) + { + var agentService = _services.GetRequiredService(); + var convService = _services.GetRequiredService(); + var routingCtx = _services.GetRequiredService(); + + var args = JsonSerializer.Deserialize(message.FunctionArgs); + + var agent = await agentService.GetAgent(message.CurrentAgentId); + var inst = GetPyCodeInterpreterInstruction(message.CurrentAgentId); + var innerAgent = new Agent + { + Id = agent.Id, + Name = agent.Name, + Instruction = inst, + LlmConfig = GetLlmConfig(), + TemplateDict = new Dictionary + { + { "python_version", _settings.PythonVersion ?? "3.11" }, + { "user_requirement", args?.UserRquirement ?? string.Empty } + } + }; + + var dialogs = routingCtx.GetDialogs(); + if (dialogs.IsNullOrEmpty()) + { + dialogs = convService.GetDialogHistory(); + } + + var messageLimit = _settings.CodeGeneration?.MessageLimit > 0 ? _settings.CodeGeneration.MessageLimit.Value : 50; + dialogs = dialogs.TakeLast(messageLimit).ToList(); + dialogs.Add(new RoleDialogModel(AgentRole.User, "Please follow the instruction and chat context to generate valid python code.") + { + CurrentAgentId = message.CurrentAgentId, + MessageId = message.MessageId + }); + + var response = await GetChatCompletion(innerAgent, dialogs); + var ret = response.JsonContent(); + + try + { + using (Py.GIL()) + { + // Import necessary Python modules + dynamic sys = Py.Import("sys"); + dynamic io = Py.Import("io"); + + // Redirect standard output/error to capture it + dynamic stringIO = io.StringIO(); + sys.stdout = stringIO; + sys.stderr = stringIO; + + // Set global items + using var globals = new PyDict(); + if (ret.PythonCode?.Contains("__main__") == true) + { + globals.SetItem("__name__", new PyString("__main__")); + } + + // Execute Python script + PythonEngine.Exec(ret.PythonCode, globals); + + // Get result + var result = stringIO.getvalue()?.ToString() as string; + message.Content = result?.TrimEnd('\r', '\n') ?? string.Empty; + message.RichContent = new RichContent + { + Recipient = new Recipient { Id = convService.ConversationId }, + Message = new ProgramCodeTemplateMessage + { + Text = ret.PythonCode ?? string.Empty, + Language = "python" + } + }; + message.StopCompletion = true; + + // Restore the original stdout/stderr + sys.stdout = sys.__stdout__; + sys.stderr = sys.__stderr__; + sys.argv = new PyList(); + } + } + catch (Exception ex) + { + var errorMsg = $"Error when executing python code."; + message.Content = $"{errorMsg} {ex.Message}"; + _logger.LogError(ex, errorMsg); + } + + return true; + } + + private async Task GetChatCompletion(Agent agent, List dialogs) + { + try + { + var (provider, model) = GetLlmProviderModel(); + var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model); + var response = await completion.GetChatCompletions(agent, dialogs); + return response.Content; + } + catch (Exception ex) + { + var error = $"Error when generating python code. {ex.Message}"; + _logger.LogWarning(ex, error); + return error; + } + } + + private string GetPyCodeInterpreterInstruction(string agentId) + { + var db = _services.GetRequiredService(); + var state = _services.GetRequiredService(); + + var templateContent = string.Empty; + var templateName = state.GetState("python_generate_template"); + + if (!string.IsNullOrEmpty(templateName)) + { + templateContent = db.GetAgentTemplate(agentId, templateName); + } + else + { + templateName = "util-code-python_generate_instruction"; + templateContent = db.GetAgentTemplate(BuiltInAgentId.UtilityAssistant, templateName); + } + + return templateContent; + } + + private (string, string) GetLlmProviderModel() + { + var provider = "openai"; + var model = "gpt-5"; + + var state = _services.GetRequiredService(); + provider = state.GetState("py_intepreter_llm_provider") + .IfNullOrEmptyAs(_settings.CodeGeneration?.LlmProvider) + .IfNullOrEmptyAs(provider); + model = state.GetState("py_intepreter_llm_model") + .IfNullOrEmptyAs(_settings.CodeGeneration?.LlmModel) + .IfNullOrEmptyAs(model); + + return (provider, model); + } + + private AgentLlmConfig GetLlmConfig() + { + var maxOutputTokens = _settings?.CodeGeneration?.MaxOutputTokens ?? 8192; + var reasoningEffortLevel = _settings?.CodeGeneration?.ReasoningEffortLevel ?? "minimal"; + + var state = _services.GetRequiredService(); + maxOutputTokens = int.TryParse(state.GetState("py_intepreter_max_output_tokens"), out var tokens) ? tokens : maxOutputTokens; + reasoningEffortLevel = state.GetState("py_intepreter_reasoning_effort_level").IfNullOrEmptyAs(reasoningEffortLevel); + + return new AgentLlmConfig + { + MaxOutputTokens = maxOutputTokens, + ReasoningEffortLevel = reasoningEffortLevel + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Helpers/PyPackageHelper.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Helpers/PyPackageHelper.cs new file mode 100644 index 000000000..2c8cfd3c5 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Helpers/PyPackageHelper.cs @@ -0,0 +1,178 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.PythonInterpreter.Helpers; + +internal static class PyPackageHelper +{ + /// + /// Install python packages + /// + /// + /// + internal static async Task InstallPackages(List? packages) + { + if (packages.IsNullOrEmpty()) + { + return new PackageInstallResult { Success = true }; + } + + try + { + var packageList = string.Join(" ", packages); + var startInfo = new ProcessStartInfo + { + FileName = "pip", + Arguments = $"install {packageList}", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + using var process = Process.Start(startInfo); + if (process == null) + { + return new PackageInstallResult + { + Success = false, + ErrorMsg = "Failed to start pip process" + }; + } + + await process.WaitForExitAsync(); + var output = await process.StandardOutput.ReadToEndAsync(); + var error = await process.StandardError.ReadToEndAsync(); + + if (process.ExitCode == 0) + { + return new PackageInstallResult { Success = true }; + } + else + { + var errorMsg = $"Failed to install packages. Exit code: {process.ExitCode}. Error: {error}"; + return new PackageInstallResult + { + Success = false, + ErrorMsg = errorMsg + }; + } + } + catch (Exception ex) + { + var errorMsg = $"Exception occurred while installing packages: {ex.Message}"; + return new PackageInstallResult + { + Success = false, + ErrorMsg = errorMsg + }; + } + } + + /// + /// Get packages that are not installed + /// + /// + /// + public static async Task> GetUninstalledPackages(List? packages) + { + var missingPackages = new List(); + + if (packages.IsNullOrEmpty()) + { + return missingPackages; + } + + try + { + var installedPackages = await GetInstalledPackages(); + foreach (var package in packages) + { + // Check for common package name mappings + var mappedPackageName = MapToPackageName(package); + var isInstalled = installedPackages.Any(x => x.IsEqualTo(mappedPackageName)); + if (!isInstalled) + { + missingPackages.Add(mappedPackageName); + } + } + return missingPackages; + } + catch (Exception ex) + { + throw; + } + } + + + private static async Task> GetInstalledPackages() + { + try + { + var startInfo = new ProcessStartInfo + { + FileName = "pip", + Arguments = "list --format=freeze", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + using var process = Process.Start(startInfo); + if (process == null) + { + throw new InvalidOperationException("Failed to start pip process"); + } + + await process.WaitForExitAsync(); + var output = await process.StandardOutput.ReadToEndAsync(); + + if (process.ExitCode != 0) + { + var error = await process.StandardError.ReadToEndAsync(); + throw new InvalidOperationException($"pip list failed with exit code {process.ExitCode}: {error}"); + } + + // Parse pip list output (format: package==version) + var packages = output.Split("\n", StringSplitOptions.RemoveEmptyEntries) + .Select(line => line.Split("==", StringSplitOptions.None)[0].Trim().ToLowerInvariant()) + .Where(pkg => !string.IsNullOrEmpty(pkg)) + .ToList(); + return packages; + } + catch (Exception ex) + { + throw; + } + } + + /// + /// Map common import name to actual package name + /// + /// + /// + private static string MapToPackageName(string importName) + { + var packageMappings = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "cv2", "opencv-python" }, + { "PIL", "Pillow" }, + { "sklearn", "scikit-learn" }, + { "yaml", "PyYAML" }, + { "bs4", "beautifulsoup4" }, + { "dateutil", "python-dateutil" }, + { "serial", "pyserial" }, + { "psutil", "psutil" }, + { "requests", "requests" }, + { "numpy", "numpy" }, + { "pandas", "pandas" }, + { "matplotlib", "matplotlib" }, + { "scipy", "scipy" }, + { "seaborn", "seaborn" }, + { "plotly", "plotly" } + }; + + return packageMappings.TryGetValue(importName, out var actualName) ? actualName : importName; + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PyProgrammerUtilityHook.cs similarity index 50% rename from src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs rename to src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PyProgrammerUtilityHook.cs index 7b644be2b..0866478ae 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PyProgrammerUtilityHook.cs @@ -1,20 +1,20 @@ namespace BotSharp.Plugin.PythonInterpreter.Hooks; -public class InterpreterUtilityHook : IAgentUtilityHook +public class PyProgrammerUtilityHook : IAgentUtilityHook { - private static string FUNCTION_NAME = "python_interpreter"; + private const string PY_PROGRAMMER_FN = "util-code-python_programmer"; public void AddUtilities(List utilities) { var utility = new AgentUtility() { - Category = "coding", - Name = UtilityName.PythonInterpreter, + Category = "code", + Name = UtilityName.PythonProgrammer, Items = [ new UtilityItem { - FunctionName = FUNCTION_NAME, - TemplateName = $"{FUNCTION_NAME}.fn" + FunctionName = PY_PROGRAMMER_FN, + TemplateName = $"{PY_PROGRAMMER_FN}.fn" } ] }; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs deleted file mode 100644 index 7d195286c..000000000 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs +++ /dev/null @@ -1,40 +0,0 @@ -using BotSharp.Abstraction.Interpreters.Settings; -using BotSharp.Plugin.PythonInterpreter.Hooks; -using Microsoft.AspNetCore.Builder; -using Python.Runtime; -using System.IO; - -namespace BotSharp.Plugin.PythonInterpreter; - -public class InterpreterPlugin : IBotSharpAppPlugin -{ - public string Id => "23174e08-e866-4173-824a-cf1d97afa8d0"; - public string Name => "Python Interpreter"; - public string Description => "Python Interpreter enables AI to write and execute Python code within a secure, sandboxed environment."; - public string? IconUrl => "https://static.vecteezy.com/system/resources/previews/012/697/295/non_2x/3d-python-programming-language-logo-free-png.png"; - - public void RegisterDI(IServiceCollection services, IConfiguration config) - { - services.AddScoped(); - } - - public void Configure(IApplicationBuilder app) - { - var settings = app.ApplicationServices.GetRequiredService(); - - // For Python interpreter plugin - if (File.Exists(settings.Python.PythonDLL)) - { - Runtime.PythonDLL = settings.Python.PythonDLL; - PythonEngine.Initialize(); - PythonEngine.BeginAllowThreads(); - } - else - { - Serilog.Log.Error("Python DLL found at {PythonDLL}", settings.Python.PythonDLL); - } - - // Shut down the Python engine - // PythonEngine.Shutdown(); - } -} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs new file mode 100644 index 000000000..0f7619128 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.PythonInterpreter.LlmContext; + +public class LlmContextIn +{ + [JsonPropertyName("user_requirement")] + public string UserRquirement { get; set; } = string.Empty; + + [JsonPropertyName("imported_packages")] + public List ImportedPackages { get; set; } = []; +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs new file mode 100644 index 000000000..2af106ee0 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.PythonInterpreter.LlmContext; + +public class LlmContextOut +{ + [JsonPropertyName("python_code")] + public string PythonCode { get; set; } + + [JsonPropertyName("imported_packages")] + public List? ImportedPackages { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Models/PackageInstallResult.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Models/PackageInstallResult.cs new file mode 100644 index 000000000..b6fdb9941 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Models/PackageInstallResult.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Plugin.PythonInterpreter.Models; + +internal class PackageInstallResult +{ + internal bool Success { get; set; } + internal string ErrorMsg { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs new file mode 100644 index 000000000..168d83f82 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs @@ -0,0 +1,59 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Python.Runtime; +using System.IO; + +namespace BotSharp.Plugin.PythonInterpreter; + +public class PythonInterpreterPlugin : IBotSharpAppPlugin +{ + public string Id => "23174e08-e866-4173-824a-cf1d97afa8d0"; + public string Name => "Python Interpreter"; + public string Description => "Python Interpreter enables AI to write and execute Python code within a secure, sandboxed environment."; + public string? IconUrl => "https://static.vecteezy.com/system/resources/previews/012/697/295/non_2x/3d-python-programming-language-logo-free-png.png"; + + private nint _pyState; + + public void RegisterDI(IServiceCollection services, IConfiguration config) + { + var settings = new PythonInterpreterSettings(); + config.Bind("PythonInterpreter", settings); + services.AddSingleton(x => settings); + + services.AddScoped(); + services.AddScoped(); + } + + public void Configure(IApplicationBuilder app) + { + var sp = app.ApplicationServices; + var settings = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var pyLoc = settings.InstallLocation; + + try + { + if (File.Exists(pyLoc)) + { + Runtime.PythonDLL = pyLoc; + PythonEngine.Initialize(); + _pyState = PythonEngine.BeginAllowThreads(); + + var lifetime = app.ApplicationServices.GetRequiredService(); + lifetime.ApplicationStopping.Register(() => { + PythonEngine.EndAllowThreads(_pyState); + PythonEngine.Shutdown(); + }); + } + else + { + logger.LogError($"Python dll not found at {pyLoc}"); + } + } + catch (Exception ex) + { + logger.LogError(ex, $"Error when loading python dll {pyLoc}"); + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs new file mode 100644 index 000000000..740499d5c --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs @@ -0,0 +1,91 @@ +using Microsoft.Extensions.Logging; +using Python.Runtime; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.PythonInterpreter.Services; + +public class PyInterpretService : ICodeInterpretService +{ + private readonly IServiceProvider _services; + private readonly ILogger _logger; + + public PyInterpretService( + IServiceProvider services, + ILogger logger) + { + _services = services; + _logger = logger; + } + + public string Provider => "botsharp-py-interpreter"; + + public async Task RunCode(string codeScript, CodeInterpretOptions? options = null) + { + try + { + using (Py.GIL()) + { + // Import necessary Python modules + dynamic sys = Py.Import("sys"); + dynamic io = Py.Import("io"); + + // Redirect standard output/error to capture it + dynamic stringIO = io.StringIO(); + sys.stdout = stringIO; + sys.stderr = stringIO; + + // Set global items + using var globals = new PyDict(); + if (codeScript.Contains("__main__") == true) + { + globals.SetItem("__name__", new PyString("__main__")); + } + + // Set arguments + var list = new PyList(); + if (options?.Arguments?.Any() == true) + { + list.Append(new PyString("code.py")); + + foreach (var arg in options.Arguments) + { + if (!string.IsNullOrWhiteSpace(arg.Key) && !string.IsNullOrWhiteSpace(arg.Value)) + { + list.Append(new PyString($"--{arg.Key}")); + list.Append(new PyString($"{arg.Value}")); + } + } + } + sys.argv = list; + + // Execute Python script + PythonEngine.Exec(codeScript, globals); + + // Get result + var result = stringIO.getvalue()?.ToString() as string; + + // Restore the original stdout/stderr + sys.stdout = sys.__stdout__; + sys.stderr = sys.__stderr__; + sys.argv = new PyList(); + + return new CodeInterpretResult + { + Result = result?.TrimEnd('\r', '\n'), + Success = true + }; + } + } + catch (Exception ex) + { + var errorMsg = $"Error when executing python code in {nameof(PyInterpretService)}: {Provider}. {ex.Message}"; + _logger.LogError(ex, errorMsg); + + return new CodeInterpretResult + { + Success = false, + ErrorMsg = errorMsg + }; + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs new file mode 100644 index 000000000..8fa79dc8a --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs @@ -0,0 +1,18 @@ +using BotSharp.Abstraction.Models; + +namespace BotSharp.Plugin.PythonInterpreter.Settings; + +public class PythonInterpreterSettings +{ + /// + /// Python installation path to .dll or .so + /// + public string InstallLocation { get; set; } + public string PythonVersion { get; set; } + public CodeGenerationSetting? CodeGeneration { get; set; } +} + +public class CodeGenerationSetting : LlmConfigBase +{ + public int? MessageLimit { get; set; } +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs index 8cc31aa17..11a023fe7 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs @@ -14,5 +14,22 @@ global using BotSharp.Abstraction.Conversations; global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.Repositories; +global using BotSharp.Abstraction.Conversations.Models; +global using BotSharp.Abstraction.Functions; +global using BotSharp.Abstraction.Messaging; +global using BotSharp.Abstraction.Messaging.Models.RichContent; +global using BotSharp.Abstraction.Messaging.Models.RichContent.Template; +global using BotSharp.Abstraction.Routing; +global using BotSharp.Abstraction.CodeInterpreter.Models; +global using BotSharp.Abstraction.CodeInterpreter; + +global using BotSharp.Core.Infrastructures; global using BotSharp.Plugin.PythonInterpreter.Enums; +global using BotSharp.Plugin.PythonInterpreter.LlmContext; +global using BotSharp.Plugin.PythonInterpreter.Settings; +global using BotSharp.Plugin.PythonInterpreter.Functions; +global using BotSharp.Plugin.PythonInterpreter.Hooks; +global using BotSharp.Plugin.PythonInterpreter.Models; +global using BotSharp.Plugin.PythonInterpreter.Helpers; +global using BotSharp.Plugin.PythonInterpreter.Services; \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/python_interpreter.json b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/python_interpreter.json deleted file mode 100644 index ac2b2a916..000000000 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/python_interpreter.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "python_interpreter", - "description": "write and execute python code, print the result in Console", - "parameters": { - "type": "object", - "properties": { - "script": { - "type": "string", - "description": "python code" - }, - "language": { - "type": "string", - "enum": [ "python" ], - "description": "python code" - } - }, - "required": [ "language", "script" ] - } -} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_programmer.json b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_programmer.json new file mode 100644 index 000000000..80064bc81 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_programmer.json @@ -0,0 +1,14 @@ +{ + "name": "util-code-python_programmer", + "description": "If user's requirement can be fulfilled by python code, you can call this function to generate python code to execute.", + "parameters": { + "type": "object", + "properties": { + "user_requirement": { + "type": "string", + "description": "The requirement that user posted for generating python code." + } + }, + "required": [ "user_requirement" ] + } +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/python_interpreter.fn.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/python_interpreter.fn.liquid deleted file mode 100644 index 8dd2425f3..000000000 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/python_interpreter.fn.liquid +++ /dev/null @@ -1 +0,0 @@ -Write and execute Python script in python_interpreter function, and use python function print(a) to output the result in stand output. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid new file mode 100644 index 000000000..e98757cdc --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid @@ -0,0 +1,49 @@ +You are a Python code generator that can produce python code to fulfill user's requirement. +Please read {% if user_requirement != empty %}"User Requirement" and{% endif %} the chat context, and then generate valid python code that can fulfill user's requirement. + +You must strictly follow the "Hard Requirements", "Code Requirements", and "Response Format" below. + +{% if user_requirement != empty %} +=== User Requirement === +{{ user_requirement }} +{% endif %} + + +***** Hard Requirements ***** +1. You must import packages at the beginning of the code. DO NOT include any import statement in the middle of the functions. +2. Your output python code must be well constructed inside one or multiple functions. +3. You must not include any code, explanations, or comments outside these functions. +4. Do not include explanations, comments outside code. +5. You must use print() once to output the final result only. Do not print intermediate values, logs, or debug info. +6. If randomness is required, set a fixed seed inside a function. + + +***** Code Requirements ***** +1. Structure + a). You need to create small and focused functions. + b). You need to include a main() function to orchestrates these steps. + c). You must only call main() in "if __name__ == '__main__'" block. +2. Data type & Validation + a). When unsure about a variable’s type, check it at runtime (e.g., isinstance) before applying methods. + b). Validate inputs and handle edge cases, such as empty lists, division by zero, out-of-range values. + c). Except the main() function, it is preferable to generate functions that accept parameters and return values. +3. Error handling + a). If necessary, use try/except block inside main() to catch any errors and produce a single final printed message. + b). Do not apply try/except block for package import. +4. Output + a). You must print the final result once in main(). +5. Compatibility + a). You must keep the code compatible with "Python {{ python_version }}" +6. Package Usage + a). Prefer using Python standard library modules when possible (os, sys, json, csv, math, statistics, datetime, etc.) + b). Only use external packages (like pandas, numpy, matplotlib) when absolutely necessary for the task + c). If using external packages, provide fallback implementations using standard library when feasible + d). Always include external packages in the "imported_packages" list in your response + + +***** Response Format ***** +You must output the response in the following JSON format: +{ + "python_code": "The python code that can fulfill user's request.", + "imported_packages": a list of strings that contains the packages that are imported in the code you generated. +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_programmer.fn.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_programmer.fn.liquid new file mode 100644 index 000000000..604e986f6 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_programmer.fn.liquid @@ -0,0 +1 @@ +Please call function util-code-python_interpreter if you think it is necessary to fulfill user's request through python code. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs index 59c43dc5f..93b90d13f 100644 --- a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs @@ -269,13 +269,13 @@ public void SetModelName(string model) } else if (message.Role == "user") { - var userMessage = ChatMessage.FromUser(message.Content); + var userMessage = ChatMessage.FromUser(message.RoleContent); messages.Add(userMessage); } else if (message.Role == "assistant") { - messages.Add(ChatMessage.FromAssistant(message.Content)); + messages.Add(ChatMessage.FromAssistant(message.RoleContent)); } } diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index 6e0176157..e5b90ddde 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -37,6 +37,7 @@ + diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index c55e9e2c1..1304313c9 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -488,7 +488,8 @@ "LlmProvider": "openai", "LlmModel": "gpt-5", "MaxOutputTokens": 8192, - "ReasoningEffortLevel": "minimal" + "ReasoningEffortLevel": "minimal", + "MessageLimit": 50 } }, @@ -564,9 +565,29 @@ } }, - "Interpreter": { - "Python": { - "PythonDLL": "C:/Users/xxx/AppData/Local/Programs/Python/Python311/python311.dll" + "PythonInterpreter": { + "InstallLocation": "C:/Users/xxx/AppData/Local/Programs/Python/Python313/python313.dll", + "PythonVersion": "3.13.3", + "CodeGeneration": { + "LlmProvider": "openai", + "LlmModel": "gpt-5", + "MaxOutputTokens": 8192, + "ReasoningEffortLevel": "minimal", + "MessageLimit": 50 + } + }, + + "RealtimeModel": { + "Provider": "openai", + "Model": "gpt-realtime", + "InputAudioFormat": "pcm16", + "OutputAudioFormat": "pcm16", + "InterruptResponse": true, + "MaxResponseOutputTokens": 4096, + "InputAudioTranscribe": true, + "InputAudioTranscription": { + "Model": "whisper-1", + "Language": "en" } }, @@ -606,7 +627,8 @@ "BotSharp.Plugin.AudioHandler", "BotSharp.Plugin.ExcelHandler", "BotSharp.Plugin.SqlDriver", - "BotSharp.Plugin.TencentCos" + "BotSharp.Plugin.TencentCos", + "BotSharp.Plugin.PythonInterpreter" ] } }