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"
]
}
}