diff --git a/BotSharp.sln b/BotSharp.sln
index 53eb3da9..c70f6c74 100644
--- a/BotSharp.sln
+++ b/BotSharp.sln
@@ -127,12 +127,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.Rules", "src\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.DeepSeekAI", "src\Plugins\BotSharp.Plugin.DeepSeekAI\BotSharp.Plugin.DeepSeekAI.csproj", "{AF329442-B48E-4B48-A18A-1C869D1BA6F5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.MCP", "src\Infrastructure\BotSharp.MCP\BotSharp.MCP.csproj", "{684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.Realtime", "src\Infrastructure\BotSharp.Core.Realtime\BotSharp.Core.Realtime.csproj", "{781F1465-365C-0F22-1775-25025DAFA4C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.PizzaBot.MCPServer", "tests\BotSharp.PizzaBot.MCPServer\BotSharp.PizzaBot.MCPServer.csproj", "{8D2AD45F-836A-516F-DE6A-71443CEBB18A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.MCP", "src\Infrastructure\BotSharp.Core.MCP\BotSharp.Core.MCP.csproj", "{534D1DD5-9D62-115C-F230-47080D76CE52}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -525,14 +525,6 @@ Global
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|Any CPU.Build.0 = Release|Any CPU
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|x64.ActiveCfg = Release|Any CPU
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|x64.Build.0 = Release|Any CPU
- {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Debug|x64.ActiveCfg = Debug|Any CPU
- {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Debug|x64.Build.0 = Debug|Any CPU
- {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Release|Any CPU.Build.0 = Release|Any CPU
- {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Release|x64.ActiveCfg = Release|Any CPU
- {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Release|x64.Build.0 = Release|Any CPU
{781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -549,6 +541,14 @@ Global
{8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|Any CPU.Build.0 = Release|Any CPU
{8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|x64.ActiveCfg = Release|Any CPU
{8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|x64.Build.0 = Release|Any CPU
+ {534D1DD5-9D62-115C-F230-47080D76CE52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {534D1DD5-9D62-115C-F230-47080D76CE52}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {534D1DD5-9D62-115C-F230-47080D76CE52}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {534D1DD5-9D62-115C-F230-47080D76CE52}.Debug|x64.Build.0 = Debug|Any CPU
+ {534D1DD5-9D62-115C-F230-47080D76CE52}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {534D1DD5-9D62-115C-F230-47080D76CE52}.Release|Any CPU.Build.0 = Release|Any CPU
+ {534D1DD5-9D62-115C-F230-47080D76CE52}.Release|x64.ActiveCfg = Release|Any CPU
+ {534D1DD5-9D62-115C-F230-47080D76CE52}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -610,9 +610,9 @@ Global
{F812BAAE-5A7D-4DF7-8E71-70696B51C61F} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
{AFD64412-4D6A-452E-82A2-79E5D8842E29} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
{AF329442-B48E-4B48-A18A-1C869D1BA6F5} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
- {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
{781F1465-365C-0F22-1775-25025DAFA4C7} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
{8D2AD45F-836A-516F-DE6A-71443CEBB18A} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
+ {534D1DD5-9D62-115C-F230-47080D76CE52} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
diff --git a/Directory.Packages.props b/Directory.Packages.props
index f70cb1ff..71e9c387 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -19,8 +19,7 @@
-
-
+
@@ -110,7 +109,7 @@
-
+
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs
index 1103f2f5..5c8ad126 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs
@@ -60,7 +60,7 @@ public abstract class AgentHookBase : IAgentHook
}
- public virtual void OnAgentMCPToolLoaded(Agent agent)
+ public virtual void OnAgentMcpToolLoaded(Agent agent)
{
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs
index 624616d7..c37688ed 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs
@@ -27,7 +27,7 @@ public interface IAgentHook
void OnAgentUtilityLoaded(Agent agent);
- void OnAgentMCPToolLoaded(Agent agent);
+ void OnAgentMcpToolLoaded(Agent agent);
///
/// Triggered when agent is loaded completely.
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
index fc28e571..a880a0c6 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
@@ -107,7 +107,7 @@ public class Agent
///
/// Agent MCP Tools
///
- public List McpTools { get; set; } = new();
+ public List McpTools { get; set; } = new();
///
/// Agent rules
@@ -305,7 +305,7 @@ public class Agent
return this;
}
- public Agent SetMcps(List mcps)
+ public Agent SetMcpTools(List? mcps)
{
McpTools = mcps ?? [];
return this;
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/MCPTool.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/MCPTool.cs
index a555a7e5..88e2d4b0 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/MCPTool.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/MCPTool.cs
@@ -1,21 +1,26 @@
namespace BotSharp.Abstraction.Agents.Models;
-public class MCPTool
+public class McpTool
{
+ public string Name { get; set; }
public string ServerId { get; set; }
-
public bool Disabled { get; set; }
+ public IEnumerable Functions { get; set; } = [];
- public IEnumerable Functions { get; set; } = [];
-
- public MCPTool()
+ public McpTool()
{
}
- public MCPTool(
- IEnumerable? functions = null)
+ public McpTool(
+ string name,
+ string serverId,
+ bool disabled = false,
+ IEnumerable? functions = null)
{
+ Name = name;
+ ServerId = serverId;
+ Disabled = disabled;
Functions = functions ?? [];
}
@@ -26,12 +31,12 @@ public class MCPTool
}
-public class MCPFunction
+public class McpFunction
{
public string Name { get; set; }
- public MCPFunction(string name)
+ public McpFunction(string name)
{
- this.Name = name;
+ Name = name;
}
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Enums/BroswerActionEnum.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Enums/BroswerActionEnum.cs
index abdfd67b..4efa1448 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Enums/BroswerActionEnum.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Enums/BroswerActionEnum.cs
@@ -7,5 +7,7 @@ public enum BroswerActionEnum
Typing = 3,
Hover = 4,
Scroll = 5,
- DragAndDrop = 6
+ DragAndDrop = 6,
+ DropDown = 7,
+ FileUpload = 8
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs
index 8885feb3..869186b1 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementActionArgs.cs
@@ -12,6 +12,8 @@ public class ElementActionArgs
[JsonPropertyName("content")]
public string? Content { get; set; }
+ [JsonPropertyName("file_urls")]
+ public string[] FileUrl { get; set; }
public ElementPosition? Position { get; set; }
diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/IFileInstructService.cs b/src/Infrastructure/BotSharp.Abstraction/Files/IFileInstructService.cs
index 9ec3c8ed..0b5b41ab 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Files/IFileInstructService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Files/IFileInstructService.cs
@@ -1,13 +1,15 @@
+using BotSharp.Abstraction.Instructs.Models;
+
namespace BotSharp.Abstraction.Files;
public interface IFileInstructService
{
#region Image
- Task ReadImages(string? provider, string? model, string text, IEnumerable images, string? agentId = null);
- Task GenerateImage(string? provider, string? model, string text, string? agentId = null);
- Task VaryImage(string? provider, string? model, InstructFileModel image, string? agentId = null);
- Task EditImage(string? provider, string? model, string text, InstructFileModel image, string? agentId = null);
- Task EditImage(string? provider, string? model, string text, InstructFileModel image, InstructFileModel mask, string? agentId = null);
+ Task ReadImages(string text, IEnumerable images, InstructOptions? options = null);
+ Task GenerateImage(string text, InstructOptions? options = null);
+ Task VaryImage(InstructFileModel image, InstructOptions? options = null);
+ Task EditImage(string text, InstructFileModel image, InstructOptions? options = null);
+ Task EditImage(string text, InstructFileModel image, InstructFileModel mask, InstructOptions? options = null);
#endregion
#region Pdf
@@ -17,11 +19,11 @@ public interface IFileInstructService
///
/// Pdf files
///
- Task ReadPdf(string? provider, string? model, string? modelId, string prompt, List files, string? agentId = null);
+ Task ReadPdf(string text, List files, InstructOptions? options = null);
#endregion
#region Audio
- Task SpeechToText(string? provider, string? model, InstructFileModel audio, string? text = null);
+ Task SpeechToText(InstructFileModel audio, string? text = null, InstructOptions? options = null);
#endregion
#region Select file
diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs
index e6a659cc..7dce2423 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs
@@ -12,7 +12,8 @@ public interface IInstructService
/// Template name
/// System prompt
///
- Task Execute(string agentId, RoleDialogModel message, string? templateName = null, string? instruction = null);
+ Task Execute(string agentId, RoleDialogModel message,
+ string? templateName = null, string? instruction = null, IEnumerable? files = null);
///
/// A generic way to execute completion by using specified instruction or template
@@ -22,5 +23,5 @@ public interface IInstructService
/// Agent id
/// Llm Provider, model, message, prompt data
///
- Task Instruct(string instruction, string agentId, InstructOptions options) where T : class;
+ Task Instruct(string text, InstructOptions? options = null) where T : class;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructOptions.cs
index 46c6c900..ece08b0e 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructOptions.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructOptions.cs
@@ -5,23 +5,28 @@ public class InstructOptions
///
/// Llm provider
///
- public string Provider { get; set; } = null!;
+ public string? Provider { get; set; }
///
/// Llm model
///
- public string Model { get; set; } = null!;
+ public string? Model { get; set; }
+
+ ///
+ /// Agent
+ ///
+ public string? AgentId { get; set; }
+
+ ///
+ /// Agent template name
+ ///
+ public string? TemplateName { get; set; }
///
/// Conversation id. When this field is not null, it will get dialogs from conversation.
///
public string? ConversationId { get; set; }
- ///
- /// The single message. It can be append to the whole dialogs or sent alone.
- ///
- public string? Message { get; set; }
-
///
/// Data to fill in prompt
///
diff --git a/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs
index 70564846..1a08fc5b 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs
@@ -3,4 +3,6 @@ namespace BotSharp.Abstraction.Plugins;
public class PluginSettings
{
public string[] Assemblies { get; set; } = new string[0];
+
+ public string[] ExcludedFunctions { get; set; } = new string[0];
}
diff --git a/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj b/src/Infrastructure/BotSharp.Core.MCP/BotSharp.Core.MCP.csproj
similarity index 90%
rename from src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj
rename to src/Infrastructure/BotSharp.Core.MCP/BotSharp.Core.MCP.csproj
index 94aba37c..5f513dd9 100644
--- a/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj
+++ b/src/Infrastructure/BotSharp.Core.MCP/BotSharp.Core.MCP.csproj
@@ -10,7 +10,6 @@
-
diff --git a/src/Infrastructure/BotSharp.Core.MCP/BotSharpMCPExtensions.cs b/src/Infrastructure/BotSharp.Core.MCP/BotSharpMCPExtensions.cs
new file mode 100644
index 00000000..0f6c3a8f
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core.MCP/BotSharpMCPExtensions.cs
@@ -0,0 +1,58 @@
+using BotSharp.Core.MCP.Functions;
+using BotSharp.Core.MCP.Settings;
+using BotSharp.Core.MCP.Hooks;
+using Microsoft.Extensions.Configuration;
+using ModelContextProtocol.Configuration;
+using ModelContextProtocol.Client;
+using BotSharp.Core.MCP.Managers;
+
+namespace BotSharp.Core.MCP;
+
+public static class BotSharpMcpExtensions
+{
+ ///
+ /// Add mcp
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection AddBotSharpMCP(this IServiceCollection services, IConfiguration config)
+ {
+ var settings = config.GetSection("MCPSettings").Get();
+ services.AddScoped(provider => { return settings; });
+ if (settings != null && !settings.McpServerConfigs.IsNullOrEmpty())
+ {
+ var clientManager = new McpClientManager(settings);
+ services.AddSingleton(clientManager);
+
+ foreach (var server in settings.McpServerConfigs)
+ {
+ RegisterFunctionCall(services, server, clientManager)
+ .ConfigureAwait(false)
+ .GetAwaiter()
+ .GetResult();
+ }
+
+ // Register hooks
+ services.AddScoped();
+ }
+ return services;
+ }
+
+ private static async Task RegisterFunctionCall(IServiceCollection services, McpServerConfig server, McpClientManager clientManager)
+ {
+ var client = await clientManager.GetMcpClientAsync(server.Id);
+ var tools = await client.ListToolsAsync();
+
+ foreach (var tool in tools)
+ {
+ services.AddScoped(provider => { return tool; });
+
+ services.AddScoped(provider =>
+ {
+ var funcTool = new McpToolAdapter(provider, tool, clientManager);
+ return funcTool;
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.MCP/Functions/McpToolAdapter.cs b/src/Infrastructure/BotSharp.Core.MCP/Functions/McpToolAdapter.cs
similarity index 77%
rename from src/Infrastructure/BotSharp.MCP/Functions/McpToolAdapter.cs
rename to src/Infrastructure/BotSharp.Core.MCP/Functions/McpToolAdapter.cs
index 60cfb531..2d4fdc70 100644
--- a/src/Infrastructure/BotSharp.MCP/Functions/McpToolAdapter.cs
+++ b/src/Infrastructure/BotSharp.Core.MCP/Functions/McpToolAdapter.cs
@@ -1,27 +1,18 @@
-using BotSharp.Abstraction.Agents;
-using BotSharp.Abstraction.Conversations.Models;
-using BotSharp.Abstraction.Functions;
-using BotSharp.Abstraction.Utilities;
-using Microsoft.Extensions.DependencyInjection;
-using ModelContextProtocol.Client;
-using ModelContextProtocol.Protocol.Types;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Text.Json;
-using System.Threading.Tasks;
+using BotSharp.Core.MCP.Managers;
+using ModelContextProtocol.Client;
-namespace BotSharp.Core.Mcp.Functions;
+namespace BotSharp.Core.MCP.Functions;
public class McpToolAdapter : IFunctionCallback
{
- private readonly Tool _tool;
- private readonly MCPClientManager _clientManager;
- private readonly IServiceProvider _serviceProvider;
+ private readonly McpClientTool _tool;
+ private readonly McpClientManager _clientManager;
+ private readonly IServiceProvider _services;
- public McpToolAdapter(IServiceProvider provider, Tool tool, MCPClientManager client)
+ public McpToolAdapter(IServiceProvider services, McpClientTool tool, McpClientManager client)
{
- _serviceProvider = provider ?? throw new ArgumentNullException(nameof(provider));
+ _services = services ?? throw new ArgumentNullException(nameof(services));
_tool = tool ?? throw new ArgumentNullException(nameof(tool));
_clientManager = client ?? throw new ArgumentNullException(nameof(client));
}
@@ -33,21 +24,18 @@ public class McpToolAdapter : IFunctionCallback
// Convert arguments to dictionary format expected by mcpdotnet
Dictionary argDict = JsonToDictionary(message.FunctionArgs);
var currentAgentId = message.CurrentAgentId;
- var agentService = _serviceProvider.GetRequiredService();
+ var agentService = _services.GetRequiredService();
var agent = await agentService.LoadAgent(currentAgentId);
var serverId = agent.McpTools.Where(t => t.Functions.Any(f => f.Name == Name)).FirstOrDefault().ServerId;
var client = await _clientManager.GetMcpClientAsync(serverId);
+
// Call the tool through mcpdotnet
- var result = await client.CallToolAsync(
- _tool.Name,
- argDict.Count == 0 ? new() : argDict
- );
+ var result = await client.CallToolAsync(_tool.Name, argDict.IsNullOrEmpty() ? new() : argDict);
// Extract the text content from the result
- var json = string.Join("\n", result.Content
- .Where(c => c.Type == "text")
- .Select(c => c.Text));
+ var json = string.Join("\n", result.Content.Where(c => c.Type == "text").Select(c => c.Text));
+
message.Content = json;
message.Data = json.JsonContent();
return true;
diff --git a/src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs b/src/Infrastructure/BotSharp.Core.MCP/Helpers/AiFunctionHelper.cs
similarity index 57%
rename from src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs
rename to src/Infrastructure/BotSharp.Core.MCP/Helpers/AiFunctionHelper.cs
index 8a409f67..d6f1cb5b 100644
--- a/src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs
+++ b/src/Infrastructure/BotSharp.Core.MCP/Helpers/AiFunctionHelper.cs
@@ -1,24 +1,21 @@
-using BotSharp.Abstraction.Functions.Models;
-using ModelContextProtocol.Protocol.Types;
-using System;
-using System.Collections.Generic;
using System.Text.Json;
+using ModelContextProtocol.Client;
-namespace BotSharp.Core.MCP;
+namespace BotSharp.Core.MCP.Helpers;
-internal static class AIFunctionUtilities
+internal static class AiFunctionHelper
{
- public static FunctionDef MapToFunctionDef(Tool tool)
+ public static FunctionDef MapToFunctionDef(McpClientTool tool)
{
if (tool == null)
{
throw new ArgumentNullException(nameof(tool));
}
- var properties = tool.InputSchema.GetProperty("properties");
- var required = tool.InputSchema.GetProperty("required");
+ var properties = tool.JsonSchema.GetProperty("properties");
+ var required = tool.JsonSchema.GetProperty("required");
- FunctionDef funDef = new FunctionDef
+ var funDef = new FunctionDef
{
Name = tool.Name,
Description = tool.Description ?? string.Empty,
diff --git a/src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs b/src/Infrastructure/BotSharp.Core.MCP/Hooks/MCPToolAgentHook.cs
similarity index 53%
rename from src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs
rename to src/Infrastructure/BotSharp.Core.MCP/Hooks/MCPToolAgentHook.cs
index 84039cfb..40a59e2b 100644
--- a/src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs
+++ b/src/Infrastructure/BotSharp.Core.MCP/Hooks/MCPToolAgentHook.cs
@@ -1,41 +1,32 @@
-using BotSharp.Abstraction.Agents;
-using BotSharp.Abstraction.Agents.Enums;
-using BotSharp.Abstraction.Agents.Models;
-using BotSharp.Abstraction.Agents.Settings;
-using BotSharp.Abstraction.Conversations;
-using BotSharp.Abstraction.Functions.Models;
-using BotSharp.Core.Mcp;
-using BotSharp.Core.MCP;
-using Microsoft.Extensions.DependencyInjection;
+using BotSharp.Core.MCP.Helpers;
+using BotSharp.Core.MCP.Managers;
using ModelContextProtocol.Client;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-namespace BotSharp.MCP.Hooks;
+namespace BotSharp.Core.MCP.Hooks;
-public class MCPToolAgentHook : AgentHookBase
+public class McpToolAgentHook : AgentHookBase
{
public override string SelfId => string.Empty;
- public MCPToolAgentHook(IServiceProvider services, AgentSettings settings)
+ public McpToolAgentHook(IServiceProvider services, AgentSettings settings)
: base(services, settings)
{
}
- public override void OnAgentMCPToolLoaded(Agent agent)
+ public override void OnAgentMcpToolLoaded(Agent agent)
{
if (agent.Type == AgentType.Routing)
+ {
return;
+ }
+
var conv = _services.GetRequiredService();
var isConvMode = conv.IsConversationMode();
if (!isConvMode) return;
agent.SecondaryFunctions ??= [];
- var functions = GetMCPContent(agent).Result;
-
+ var functions = GetMcpContent(agent).Result;
foreach (var fn in functions)
{
if (!agent.SecondaryFunctions.Any(x => x.Name.Equals(fn.Name, StringComparison.OrdinalIgnoreCase)))
@@ -45,21 +36,21 @@ public class MCPToolAgentHook : AgentHookBase
}
}
- private async Task> GetMCPContent(Agent agent)
+ private async Task> GetMcpContent(Agent agent)
{
- List functionDefs = new List();
- var mcpClientManager = _services.GetRequiredService();
- var mcps = agent.McpTools;
+ var functionDefs = new List();
+ var mcpClientManager = _services.GetRequiredService();
+ var mcps = agent.McpTools.Where(x => !x.Disabled);
foreach (var item in mcps)
{
var mcpClient = await mcpClientManager.GetMcpClientAsync(item.ServerId);
if (mcpClient != null)
{
- var tools = await mcpClient.ListToolsAsync().ToListAsync();
+ var tools = await mcpClient.ListToolsAsync();
var toolnames = item.Functions.Select(x => x.Name).ToList();
foreach (var tool in tools.Where(x => toolnames.Contains(x.Name, StringComparer.OrdinalIgnoreCase)))
{
- var funDef = AIFunctionUtilities.MapToFunctionDef(tool);
+ var funDef = AiFunctionHelper.MapToFunctionDef(tool);
functionDefs.Add(funDef);
}
}
diff --git a/src/Infrastructure/BotSharp.Core.MCP/Managers/McpClientManager.cs b/src/Infrastructure/BotSharp.Core.MCP/Managers/McpClientManager.cs
new file mode 100644
index 00000000..523e4b00
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core.MCP/Managers/McpClientManager.cs
@@ -0,0 +1,27 @@
+using BotSharp.Core.MCP.Settings;
+using ModelContextProtocol.Client;
+
+namespace BotSharp.Core.MCP.Managers;
+
+public class McpClientManager : IDisposable
+{
+
+ private readonly McpSettings mcpSettings;
+
+ public McpClientManager(McpSettings settings)
+ {
+ mcpSettings = settings;
+ }
+
+ public async Task GetMcpClientAsync(string serverId)
+ {
+ return await McpClientFactory.CreateAsync(
+ mcpSettings.McpServerConfigs.Where(x=> x.Name == serverId).First(),
+ mcpSettings.McpClientOptions);
+ }
+
+ public void Dispose()
+ {
+
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core.MCP/Settings/MCPSettings.cs b/src/Infrastructure/BotSharp.Core.MCP/Settings/MCPSettings.cs
new file mode 100644
index 00000000..09884f2b
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core.MCP/Settings/MCPSettings.cs
@@ -0,0 +1,11 @@
+using ModelContextProtocol.Client;
+using ModelContextProtocol.Configuration;
+
+namespace BotSharp.Core.MCP.Settings;
+
+public class McpSettings
+{
+ public McpClientOptions McpClientOptions { get; set; }
+ public List McpServerConfigs { get; set; } = new();
+
+}
diff --git a/src/Infrastructure/BotSharp.Core.MCP/Using.cs b/src/Infrastructure/BotSharp.Core.MCP/Using.cs
new file mode 100644
index 00000000..a404f960
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core.MCP/Using.cs
@@ -0,0 +1,16 @@
+global using System;
+global using System.Collections.Generic;
+global using System.Linq;
+global using System.Threading.Tasks;
+global using Microsoft.Extensions.DependencyInjection;
+
+global using BotSharp.Abstraction.Agents;
+global using BotSharp.Abstraction.Agents.Enums;
+global using BotSharp.Abstraction.Agents.Models;
+global using BotSharp.Abstraction.Agents.Settings;
+global using BotSharp.Abstraction.Conversations;
+global using BotSharp.Abstraction.Conversations.Models;
+global using BotSharp.Abstraction.Functions;
+global using BotSharp.Abstraction.Functions.Models;
+global using BotSharp.Abstraction.Utilities;
+
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
index c8f97206..d3ceea8b 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
@@ -72,9 +72,9 @@ public partial class AgentService
hook.OnAgentUtilityLoaded(agent);
}
- if(agent.McpTools != null && agent.McpTools.Count >0)
+ if(!agent.McpTools.IsNullOrEmpty())
{
- hook.OnAgentMCPToolLoaded(agent);
+ hook.OnAgentMcpToolLoaded(agent);
}
hook.OnAgentLoaded(agent);
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs
index 87da12b2..288a7ca1 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs
@@ -119,7 +119,7 @@ public partial class AgentService
var conv = _services.GetRequiredService();
var render = _services.GetRequiredService();
- var template = agent.Templates.First(x => x.Name == templateName).Content;
+ var template = agent.Templates.FirstOrDefault(x => x.Name == templateName)?.Content ?? string.Empty;
// update states
foreach (var t in conv.States.GetStates())
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
index dd8743d4..387cdfd5 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
@@ -107,7 +107,7 @@ public partial class AgentService
.SetResponses(foundAgent.Responses)
.SetSamples(foundAgent.Samples)
.SetUtilities(foundAgent.Utilities)
- .SetMcps(foundAgent.McpTools)
+ .SetMcpTools(foundAgent.McpTools)
.SetKnowledgeBases(foundAgent.KnowledgeBases)
.SetRules(foundAgent.Rules)
.SetLlmConfig(foundAgent.LlmConfig);
diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
index 6b10ad81..cd85e358 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
+++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
@@ -135,6 +135,8 @@ public static class BotSharpCoreExtensions
{
var pluginSettings = new PluginSettings();
config.Bind("PluginLoader", pluginSettings);
+ var excludedFunctions = pluginSettings.ExcludedFunctions ?? [];
+
services.AddScoped(provider =>
{
var settingService = provider.GetRequiredService();
@@ -161,8 +163,9 @@ public static class BotSharpCoreExtensions
// Register function callback
var functions = assembly.GetTypes()
- .Where(x => x.IsClass)
- .Where(x => x.GetInterface(nameof(IFunctionCallback)) != null)
+ .Where(x => x.IsClass
+ && x.GetInterface(nameof(IFunctionCallback)) != null
+ && !excludedFunctions.Contains(x.Name))
.ToArray();
foreach (var function in functions)
diff --git a/src/Infrastructure/BotSharp.Core/Evaluations/Services/EvaluatingService.Evaluate.cs b/src/Infrastructure/BotSharp.Core/Evaluations/Services/EvaluatingService.Evaluate.cs
index c122d552..8cdbc0da 100644
--- a/src/Infrastructure/BotSharp.Core/Evaluations/Services/EvaluatingService.Evaluate.cs
+++ b/src/Infrastructure/BotSharp.Core/Evaluations/Services/EvaluatingService.Evaluate.cs
@@ -62,8 +62,6 @@ public partial class EvaluatingService
var query = "Please see yourself as a user and follow the instruction to generate a message.";
var targetAgentId = request.AgentId;
- var evaluator = await agentService.GetAgent(BuiltInAgentId.Evaluator);
- var simulatorPrompt = evaluator.Templates.FirstOrDefault(x => x.Name == "instruction.simulator")?.Content ?? string.Empty;
while (true)
{
@@ -77,12 +75,13 @@ public partial class EvaluatingService
count++;
- var result = await instructService.Instruct(simulatorPrompt, BuiltInAgentId.Evaluator,
+ var result = await instructService.Instruct(query,
new InstructOptions
{
Provider = request.Provider,
Model = request.Model,
- Message = query,
+ AgentId = BuiltInAgentId.Evaluator,
+ TemplateName = "instruction.simulator",
Data = new Dictionary
{
{ "ref_conversation", refDialogs },
@@ -122,16 +121,14 @@ public partial class EvaluatingService
var curDialogs = storage.GetDialogs(curConversationId);
var curDialogContents = GetConversationContent(curDialogs);
- var evaluator = await agentService.GetAgent(BuiltInAgentId.Evaluator);
- var metricPrompt = evaluator.Templates.FirstOrDefault(x => x.Name == "instruction.metrics")?.Content ?? string.Empty;
var query = "Please follow the instruction for evaluation.";
-
- var result = await instructService.Instruct(metricPrompt, BuiltInAgentId.Evaluator,
+ var result = await instructService.Instruct(query,
new InstructOptions
{
Provider = request.Provider,
Model = request.Model,
- Message = query,
+ AgentId = BuiltInAgentId.Evaluator,
+ TemplateName = "instruction.metrics",
Data = new Dictionary
{
{ "ref_conversation", refDialogs },
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs
index 0b8f25cd..d61b6165 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Audio.cs
@@ -1,19 +1,26 @@
+using BotSharp.Abstraction.Instructs.Models;
using System.IO;
namespace BotSharp.Core.Files.Services;
public partial class FileInstructService
{
- public async Task SpeechToText(string? provider, string? model, InstructFileModel audio, string? text = null)
+ public async Task SpeechToText(InstructFileModel audio, string? text = null, InstructOptions? options = null)
{
- var completion = CompletionProvider.GetAudioTranscriber(_services, provider: provider, model: model);
+ if (string.IsNullOrWhiteSpace(text))
+ {
+ var innerAgentId = options?.AgentId ?? Guid.Empty.ToString();
+ text = await GetAgentTemplate(innerAgentId, options?.TemplateName);
+ }
+
+ var completion = CompletionProvider.GetAudioTranscriber(_services, provider: options?.Provider, model: options?.Model);
var audioBytes = await DownloadFile(audio);
using var stream = new MemoryStream();
stream.Write(audioBytes, 0, audioBytes.Length);
stream.Position = 0;
var fileName = $"{audio.FileName ?? "audio"}.{audio.FileExtension ?? "wav"}";
- var content = await completion.TranscriptTextAsync(stream, fileName, text);
+ var content = await completion.TranscriptTextAsync(stream, fileName, text ?? string.Empty);
stream.Close();
return content;
}
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs
index 2976ab47..2f70bd22 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs
@@ -6,25 +6,28 @@ namespace BotSharp.Core.Files.Services;
public partial class FileInstructService
{
- public async Task ReadImages(string? provider, string? model, string text, IEnumerable images, string? agentId = null)
+ public async Task ReadImages(string text, IEnumerable images, InstructOptions? options = null)
{
- var innerAgentId = agentId ?? Guid.Empty.ToString();
- var completion = CompletionProvider.GetChatCompletion(_services, provider: provider ?? "openai", model: model ?? "gpt-4o", multiModal: true);
+ var innerAgentId = options?.AgentId ?? Guid.Empty.ToString();
+ var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName);
+
+ var completion = CompletionProvider.GetChatCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "gpt-4o", multiModal: true);
var message = await completion.GetChatCompletions(new Agent()
{
Id = innerAgentId,
+ Instruction = instruction
}, new List
{
new RoleDialogModel(AgentRole.User, text)
{
- Files = images?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData }).ToList() ?? new List()
+ Files = images?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData }).ToList() ?? []
}
});
var hooks = _services.GetServices();
foreach (var hook in hooks)
{
- if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
+ if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != innerAgentId)
{
continue;
}
@@ -34,7 +37,9 @@ public partial class FileInstructService
AgentId = innerAgentId,
Provider = completion.Provider,
Model = completion.Model,
+ TemplateName = options?.TemplateName,
UserMessage = text,
+ SystemInstruction = instruction,
CompletionText = message.Content
});
}
@@ -42,19 +47,22 @@ public partial class FileInstructService
return message.Content;
}
- public async Task GenerateImage(string? provider, string? model, string text, string? agentId = null)
+ public async Task GenerateImage(string text, InstructOptions? options = null)
{
- var innerAgentId = agentId ?? Guid.Empty.ToString();
- var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-3");
+ var innerAgentId = options?.AgentId ?? Guid.Empty.ToString();
+ var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName);
+
+ var completion = CompletionProvider.GetImageCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "dall-e-3");
var message = await completion.GetImageGeneration(new Agent()
{
Id = innerAgentId,
+ Instruction = instruction
}, new RoleDialogModel(AgentRole.User, text));
var hooks = _services.GetServices();
foreach (var hook in hooks)
{
- if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
+ if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != innerAgentId)
{
continue;
}
@@ -64,7 +72,9 @@ public partial class FileInstructService
AgentId = innerAgentId,
Provider = completion.Provider,
Model = completion.Model,
+ TemplateName = options?.TemplateName,
UserMessage = text,
+ SystemInstruction = instruction,
CompletionText = message.Content
});
}
@@ -72,15 +82,17 @@ public partial class FileInstructService
return message;
}
- public async Task VaryImage(string? provider, string? model, InstructFileModel image, string? agentId = null)
+ public async Task VaryImage(InstructFileModel image, InstructOptions? options = null)
{
if (string.IsNullOrWhiteSpace(image?.FileUrl) && string.IsNullOrWhiteSpace(image?.FileData))
{
throw new ArgumentException($"Cannot find image url or data!");
}
- var innerAgentId = agentId ?? Guid.Empty.ToString();
- var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
+ var innerAgentId = options?.AgentId ?? Guid.Empty.ToString();
+ var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName);
+
+ var completion = CompletionProvider.GetImageCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "dall-e-2");
var bytes = await DownloadFile(image);
using var stream = new MemoryStream();
stream.Write(bytes, 0, bytes.Length);
@@ -89,7 +101,8 @@ public partial class FileInstructService
var fileName = $"{image.FileName ?? "image"}.{image.FileExtension ?? "png"}";
var message = await completion.GetImageVariation(new Agent()
{
- Id = innerAgentId
+ Id = innerAgentId,
+ Instruction = instruction
}, new RoleDialogModel(AgentRole.User, string.Empty), stream, fileName);
stream.Close();
@@ -97,7 +110,7 @@ public partial class FileInstructService
var hooks = _services.GetServices();
foreach (var hook in hooks)
{
- if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
+ if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != innerAgentId)
{
continue;
}
@@ -107,7 +120,9 @@ public partial class FileInstructService
AgentId = innerAgentId,
Provider = completion.Provider,
Model = completion.Model,
+ TemplateName = options?.TemplateName,
UserMessage = string.Empty,
+ SystemInstruction = instruction,
CompletionText = message.Content
});
}
@@ -115,15 +130,17 @@ public partial class FileInstructService
return message;
}
- public async Task EditImage(string? provider, string? model, string text, InstructFileModel image, string? agentId = null)
+ public async Task EditImage(string text, InstructFileModel image, InstructOptions? options = null)
{
if (string.IsNullOrWhiteSpace(image?.FileUrl) && string.IsNullOrWhiteSpace(image?.FileData))
{
throw new ArgumentException($"Cannot find image url or data!");
}
- var innerAgentId = agentId ?? Guid.Empty.ToString();
- var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
+ var innerAgentId = options?.AgentId ?? Guid.Empty.ToString();
+ var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName);
+
+ var completion = CompletionProvider.GetImageCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "dall-e-2");
var bytes = await DownloadFile(image);
using var stream = new MemoryStream();
stream.Write(bytes, 0, bytes.Length);
@@ -132,7 +149,8 @@ public partial class FileInstructService
var fileName = $"{image.FileName ?? "image"}.{image.FileExtension ?? "png"}";
var message = await completion.GetImageEdits(new Agent()
{
- Id = innerAgentId
+ Id = innerAgentId,
+ Instruction = instruction
}, new RoleDialogModel(AgentRole.User, text), stream, fileName);
stream.Close();
@@ -140,7 +158,7 @@ public partial class FileInstructService
var hooks = _services.GetServices();
foreach (var hook in hooks)
{
- if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
+ if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != innerAgentId)
{
continue;
}
@@ -150,7 +168,9 @@ public partial class FileInstructService
AgentId = innerAgentId,
Provider = completion.Provider,
Model = completion.Model,
+ TemplateName = options?.TemplateName,
UserMessage = text,
+ SystemInstruction = instruction,
CompletionText = message.Content
});
}
@@ -158,7 +178,7 @@ public partial class FileInstructService
return message;
}
- public async Task EditImage(string? provider, string? model, string text, InstructFileModel image, InstructFileModel mask, string? agentId = null)
+ public async Task EditImage(string text, InstructFileModel image, InstructFileModel mask, InstructOptions? options = null)
{
if ((string.IsNullOrWhiteSpace(image?.FileUrl) && string.IsNullOrWhiteSpace(image?.FileData)) ||
(string.IsNullOrWhiteSpace(mask?.FileUrl) && string.IsNullOrWhiteSpace(mask?.FileData)))
@@ -166,8 +186,10 @@ public partial class FileInstructService
throw new ArgumentException($"Cannot find image/mask url or data");
}
- var innerAgentId = agentId ?? Guid.Empty.ToString();
- var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
+ var innerAgentId = options?.AgentId ?? Guid.Empty.ToString();
+ var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName);
+
+ var completion = CompletionProvider.GetImageCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "dall-e-2");
var imageBytes = await DownloadFile(image);
var maskBytes = await DownloadFile(mask);
@@ -183,7 +205,8 @@ public partial class FileInstructService
var maskName = $"{mask.FileName ?? "mask"}.{mask.FileExtension ?? "png"}";
var message = await completion.GetImageEdits(new Agent()
{
- Id = innerAgentId
+ Id = innerAgentId,
+ Instruction = instruction
}, new RoleDialogModel(AgentRole.User, text), imageStream, imageName, maskStream, maskName);
imageStream.Close();
@@ -192,7 +215,7 @@ public partial class FileInstructService
var hooks = _services.GetServices();
foreach (var hook in hooks)
{
- if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
+ if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != innerAgentId)
{
continue;
}
@@ -202,7 +225,9 @@ public partial class FileInstructService
AgentId = innerAgentId,
Provider = completion.Provider,
Model = completion.Model,
+ TemplateName = options?.TemplateName,
UserMessage = text,
+ SystemInstruction = instruction,
CompletionText = message.Content
});
}
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs
index af62e479..a1589de9 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs
@@ -6,11 +6,11 @@ namespace BotSharp.Core.Files.Services;
public partial class FileInstructService
{
- public async Task ReadPdf(string? provider, string? model, string? modelId, string prompt, List files, string? agentId = null)
+ public async Task ReadPdf(string text, List files, InstructOptions? options = null)
{
var content = string.Empty;
- if (string.IsNullOrWhiteSpace(prompt) || files.IsNullOrEmpty())
+ if (string.IsNullOrWhiteSpace(text) || files.IsNullOrEmpty())
{
return content;
}
@@ -25,15 +25,18 @@ public partial class FileInstructService
var images = await ConvertPdfToImages(pdfFiles);
if (images.IsNullOrEmpty()) return content;
- var innerAgentId = agentId ?? Guid.Empty.ToString();
- var completion = CompletionProvider.GetChatCompletion(_services, provider: provider ?? "openai",
- model: model, modelId: modelId ?? "gpt-4o", multiModal: true);
+ var innerAgentId = options?.AgentId ?? Guid.Empty.ToString();
+ var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName);
+
+ var completion = CompletionProvider.GetChatCompletion(_services, provider: options?.Provider ?? "openai",
+ model: options?.Model ?? "gpt-4o", multiModal: true);
var message = await completion.GetChatCompletions(new Agent()
{
Id = innerAgentId,
+ Instruction = instruction
}, new List
{
- new RoleDialogModel(AgentRole.User, prompt)
+ new RoleDialogModel(AgentRole.User, text)
{
Files = images.Select(x => new BotSharpFile { FileStorageUrl = x }).ToList()
}
@@ -42,7 +45,7 @@ public partial class FileInstructService
var hooks = _services.GetServices();
foreach (var hook in hooks)
{
- if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
+ if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != innerAgentId)
{
continue;
}
@@ -52,7 +55,9 @@ public partial class FileInstructService
AgentId = innerAgentId,
Provider = completion.Provider,
Model = completion.Model,
- UserMessage = prompt,
+ TemplateName = options?.TemplateName,
+ UserMessage = text,
+ SystemInstruction = instruction,
CompletionText = message.Content
});
}
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs
index 416d9f30..b2da5f43 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs
@@ -30,4 +30,22 @@ public partial class FileInstructService : IFileInstructService
_fileStorage.CreateDirectory(dir);
}
}
+
+ private async Task GetAgentTemplate(string agentId, string? templateName)
+ {
+ if (string.IsNullOrWhiteSpace(agentId) || string.IsNullOrWhiteSpace(templateName))
+ {
+ return null;
+ }
+
+ var agentService = _services.GetRequiredService();
+ var agent = await agentService.GetAgent(agentId);
+ if (agent == null)
+ {
+ return null;
+ }
+
+ var instruction = agentService.RenderedTemplate(agent, templateName);
+ return instruction;
+ }
}
diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs
index 36f5b523..71a7488b 100644
--- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs
+++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs
@@ -6,7 +6,8 @@ namespace BotSharp.Core.Instructs;
public partial class InstructService
{
- public async Task Execute(string agentId, RoleDialogModel message, string? templateName = null, string? instruction = null)
+ public async Task Execute(string agentId, RoleDialogModel message,
+ string? templateName = null, string? instruction = null, IEnumerable? files = null)
{
var agentService = _services.GetRequiredService();
Agent agent = await agentService.LoadAgent(agentId);
@@ -88,7 +89,8 @@ public partial class InstructService
new RoleDialogModel(AgentRole.User, prompt)
{
CurrentAgentId = agentId,
- MessageId = message.MessageId
+ MessageId = message.MessageId,
+ Files = files?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData }).ToList() ?? []
}
});
response.Text = result.Content;
diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs
index b2de2f28..4b7b5efb 100644
--- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs
+++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs
@@ -8,10 +8,10 @@ namespace BotSharp.Core.Instructs;
public partial class InstructService
{
- public async Task Instruct(string instruction, string agentId, InstructOptions options) where T : class
+ public async Task Instruct(string text, InstructOptions? options = null) where T : class
{
- var prompt = GetPrompt(instruction, options.Data);
- var response = await GetAiResponse(agentId, prompt, options);
+ var agent = await BuildInnerAgent(options);
+ var response = await GetAiResponse(text, agent, options);
if (string.IsNullOrWhiteSpace(response.Content)) return null;
@@ -28,19 +28,18 @@ public partial class InstructService
}
else if (IsListType(type))
{
- var text = response.Content.JsonArrayContent();
- if (!string.IsNullOrWhiteSpace(text))
+ var content = response.Content.JsonArrayContent();
+ if (!string.IsNullOrWhiteSpace(content))
{
-
- result = JsonSerializer.Deserialize(text, botsharpOptions.JsonSerializerOptions);
+ result = JsonSerializer.Deserialize(content, botsharpOptions.JsonSerializerOptions);
}
}
else
{
- var text = response.Content.JsonContent();
- if (!string.IsNullOrWhiteSpace(text))
+ var content = response.Content.JsonContent();
+ if (!string.IsNullOrWhiteSpace(content))
{
- result = JsonSerializer.Deserialize(text, botsharpOptions.JsonSerializerOptions);
+ result = JsonSerializer.Deserialize(content, botsharpOptions.JsonSerializerOptions);
}
}
}
@@ -52,47 +51,62 @@ public partial class InstructService
return result;
}
- private string GetPrompt(string instruction, Dictionary data)
+ private async Task BuildInnerAgent(InstructOptions? options)
+ {
+ Agent? agent = null;
+ string? instruction = null;
+
+ if (!string.IsNullOrWhiteSpace(options?.AgentId))
+ {
+ var agentService = _services.GetRequiredService();
+ agent = await agentService.GetAgent(options.AgentId);
+
+ if (!string.IsNullOrWhiteSpace(options?.TemplateName))
+ {
+ var template = agent?.Templates?.FirstOrDefault(x => x.Name == options.TemplateName)?.Content ?? string.Empty;
+ instruction = BuildInstruction(template, options?.Data ?? []);
+ }
+ }
+
+ return new Agent
+ {
+ Id = agent?.Id ?? Guid.Empty.ToString(),
+ Name = agent?.Name ?? "Unknown",
+ Instruction = instruction,
+ LlmConfig = agent?.LlmConfig ?? new()
+ };
+ }
+
+ private string BuildInstruction(string instruction, Dictionary data)
{
var render = _services.GetRequiredService();
return render.Render(instruction, data ?? new Dictionary());
}
- private async Task GetAiResponse(string agentId, string prompt, InstructOptions options)
+ private async Task GetAiResponse(string text, Agent agent, InstructOptions? options)
{
- var agentService = _services.GetRequiredService();
- var agent = await agentService.LoadAgent(agentId);
-
- var localAgent = new Agent
- {
- Id = agentId,
- Name = agent?.Name ?? "Unknown",
- Instruction = prompt,
- TemplateDict = new()
- };
-
- var messages = BuildDialogs(options);
- var provider = options.Provider ?? agent?.LlmConfig?.Provider ?? "openai";
- var model = options.Model ?? agent?.LlmConfig?.Model ?? "gpt-4o";
+ var dialogs = BuildDialogs(text, options);
+ var provider = options?.Provider ?? agent?.LlmConfig?.Provider ?? "openai";
+ var model = options?.Model ?? agent?.LlmConfig?.Model ?? "gpt-4o";
var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model);
- return await completion.GetChatCompletions(localAgent, messages);
+ return await completion.GetChatCompletions(agent, dialogs);
}
- private List BuildDialogs(InstructOptions options)
+ private List BuildDialogs(string text, InstructOptions? options)
{
var messages = new List();
- if (!string.IsNullOrWhiteSpace(options.ConversationId))
+ if (!string.IsNullOrWhiteSpace(options?.ConversationId))
{
var conv = _services.GetRequiredService();
var dialogs = conv.GetDialogHistory();
messages.AddRange(dialogs);
}
- if (!string.IsNullOrWhiteSpace(options.Message))
+ if (!string.IsNullOrWhiteSpace(text))
{
- messages.Add(new RoleDialogModel(AgentRole.User, options.Message));
+ messages.Add(new RoleDialogModel(AgentRole.User, text));
}
return messages;
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
index 4da61828..5ccc7088 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
@@ -194,7 +194,7 @@ namespace BotSharp.Core.Repository
File.WriteAllText(agentFile, json);
}
- private void UpdateAgentMcpTools(string agentId, List mcptools)
+ private void UpdateAgentMcpTools(string agentId, List mcptools)
{
if (mcptools == null) return;
diff --git a/src/Infrastructure/BotSharp.MCP/MCPClientManager.cs b/src/Infrastructure/BotSharp.MCP/MCPClientManager.cs
deleted file mode 100644
index 90664c7b..00000000
--- a/src/Infrastructure/BotSharp.MCP/MCPClientManager.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using BotSharp.Core.Mcp.Settings;
-using Microsoft.Extensions.Logging;
-using ModelContextProtocol.Client;
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace BotSharp.Core.Mcp;
-
-public class MCPClientManager : IDisposable
-{
-
- private readonly MCPSettings mcpSettings;
-
- public MCPClientManager(MCPSettings settings)
- {
- mcpSettings = settings;
- }
-
- public async Task GetMcpClientAsync(string serverId)
- {
- return await McpClientFactory.CreateAsync(mcpSettings.McpServerConfigs
- .Where(x=> x.Name == serverId).First(), mcpSettings.McpClientOptions);
- }
-
- public void Dispose()
- {
-
- }
-}
diff --git a/src/Infrastructure/BotSharp.MCP/McpPlugin.cs b/src/Infrastructure/BotSharp.MCP/McpPlugin.cs
deleted file mode 100644
index f95965ac..00000000
--- a/src/Infrastructure/BotSharp.MCP/McpPlugin.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using BotSharp.Abstraction.Agents;
-using BotSharp.Abstraction.Conversations;
-using BotSharp.Abstraction.Functions;
-using BotSharp.Abstraction.Plugins;
-using BotSharp.Core.Mcp.Functions;
-using BotSharp.Core.Mcp.Settings;
-using BotSharp.MCP.Hooks;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging.Abstractions;
-using ModelContextProtocol.Client;
-using ModelContextProtocol.Configuration;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace BotSharp.Core.Mcp;
-
-public class McpPlugin : IBotSharpPlugin
-{
- public string Id => "5d779611-0012-46cb-a754-4ca4770e88ac";
- public string Name => "MCP Plugin";
- public string Description => "Integrated MCP tools";
-
- private MCPClientManager clientManager;
-
- public void RegisterDI(IServiceCollection services, IConfiguration config)
- {
- var settings = config.GetSection("MCPSettings").Get();
- services.AddScoped(provider => { return settings; });
-
- clientManager = new MCPClientManager(settings);
- services.AddSingleton(clientManager);
-
- foreach (var server in settings.McpServerConfigs)
- {
- RegisterFunctionCall(services, server)
- .ConfigureAwait(false)
- .GetAwaiter()
- .GetResult();
- }
- // Register hooks
- services.AddScoped();
- }
-
- private async Task RegisterFunctionCall(IServiceCollection services, McpServerConfig server)
- {
- var client = await clientManager.GetMcpClientAsync(server.Id);
- var tools = await client.ListToolsAsync().ToListAsync();
-
- foreach (var tool in tools)
- {
- services.AddScoped(provider => { return tool; });
-
- services.AddScoped(provider =>
- {
- var funcTool = new McpToolAdapter(provider, tool, clientManager);
- return funcTool;
- });
- }
- }
-}
diff --git a/src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs b/src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs
deleted file mode 100644
index 1279a08d..00000000
--- a/src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using ModelContextProtocol.Client;
-using ModelContextProtocol.Configuration;
-using System.Collections.Generic;
-
-namespace BotSharp.Core.Mcp.Settings;
-
-public class MCPSettings
-{
- public McpClientOptions McpClientOptions { get; set; }
-
- public List McpServerConfigs { get; set; }
-
-}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
index 121afd35..9162cd16 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
+++ b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
@@ -47,8 +47,7 @@
-
-
+
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
index 6e49658d..401a693f 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
@@ -1,7 +1,4 @@
using BotSharp.Abstraction.Agents.Models;
-using BotSharp.Core.Mcp;
-using ModelContextProtocol.Client;
-using ModelContextProtocol.Protocol.Types;
namespace BotSharp.OpenAPI.Controllers;
@@ -12,20 +9,17 @@ public class AgentController : ControllerBase
private readonly IAgentService _agentService;
private readonly IUserIdentity _user;
private readonly IServiceProvider _services;
- private readonly MCPClientManager _clientManager;
-
+
public AgentController(
IAgentService agentService,
IUserIdentity user,
- IServiceProvider services,
- MCPClientManager mCPClientManager
+ IServiceProvider services
)
{
_agentService = agentService;
_user = user;
_services = services;
- _clientManager = mCPClientManager;
- }
+ }
[HttpGet("/agent/settings")]
public AgentSettings GetSettings()
@@ -173,17 +167,7 @@ public class AgentController : ControllerBase
}
return utilities.Where(x => !string.IsNullOrWhiteSpace(x.Name)).OrderBy(x => x.Name).ToList();
}
-
- [HttpGet("/agent/mcp/tools")]
- public async Task> GetMCPTools(string serverId)
- {
- var client = await _clientManager.GetMcpClientAsync(serverId);
- var tools = await client.ListToolsAsync().ToListAsync();
-
- return tools.Where(x => !string.IsNullOrWhiteSpace(x.Name))
- .OrderBy(x => x.Name).ToList();
- }
-
+
[HttpGet("/agent/labels")]
public async Task> GetAgentLabels()
{
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs
index 3db47703..55837f8d 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs
@@ -4,6 +4,7 @@ using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Core.Infrastructures;
using BotSharp.OpenAPI.ViewModels.Instructs;
+using static System.Net.Mime.MediaTypeNames;
namespace BotSharp.OpenAPI.Controllers;
@@ -36,7 +37,8 @@ public class InstructModeController : ControllerBase
var result = await instructor.Execute(agentId,
new RoleDialogModel(AgentRole.User, input.Text),
templateName: input.Template,
- instruction: input.Instruction);
+ instruction: input.Instruction,
+ files: input.Files);
result.States = state.GetStates();
@@ -96,6 +98,9 @@ public class InstructModeController : ControllerBase
}, new List
{
new RoleDialogModel(AgentRole.User, input.Text)
+ {
+ Files = input.Files?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData }).ToList() ?? []
+ }
});
var hooks = _services.GetServices();
@@ -131,7 +136,13 @@ public class InstructModeController : ControllerBase
try
{
var fileInstruct = _services.GetRequiredService();
- var content = await fileInstruct.ReadImages(input.Provider, input.Model, input.Text, input.Files, input.AgentId);
+ var content = await fileInstruct.ReadImages(input.Text, input.Files, new InstructOptions
+ {
+ Provider = input.Provider,
+ Model = input.Model,
+ AgentId = input.AgentId,
+ TemplateName = input.TemplateName
+ });
return content;
}
catch (Exception ex)
@@ -144,7 +155,8 @@ public class InstructModeController : ControllerBase
[HttpPost("/instruct/multi-modal/upload")]
public async Task MultiModalCompletion(IFormFile file, [FromForm] string text, [FromForm] string? provider = null,
- [FromForm] string? model = null, [FromForm] List? states = null, [FromForm] string? agentId = null)
+ [FromForm] string? model = null, [FromForm] List? states = null,
+ [FromForm] string? agentId = null, [FromForm] string? templateName = null)
{
var state = _services.GetRequiredService();
states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
@@ -158,7 +170,13 @@ public class InstructModeController : ControllerBase
new InstructFileModel { FileData = data }
};
var fileInstruct = _services.GetRequiredService();
- var content = await fileInstruct.ReadImages(provider, model, text, files, agentId);
+ var content = await fileInstruct.ReadImages(text, files, new InstructOptions
+ {
+ Provider = provider,
+ Model = model,
+ AgentId = agentId,
+ TemplateName = templateName
+ });
viewModel.Content = content;
return viewModel;
}
@@ -183,7 +201,13 @@ public class InstructModeController : ControllerBase
try
{
var fileInstruct = _services.GetRequiredService();
- var message = await fileInstruct.GenerateImage(input.Provider, input.Model, input.Text, input.AgentId);
+ var message = await fileInstruct.GenerateImage(input.Text, new InstructOptions
+ {
+ Provider = input.Provider,
+ Model = input.Model,
+ AgentId = input.AgentId,
+ TemplateName = input.TemplateName
+ });
imageViewModel.Content = message.Content;
imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
return imageViewModel;
@@ -214,7 +238,13 @@ public class InstructModeController : ControllerBase
}
var fileInstruct = _services.GetRequiredService();
- var message = await fileInstruct.VaryImage(input.Provider, input.Model, input.File, input.AgentId);
+ var message = await fileInstruct.VaryImage(input.File, new InstructOptions
+ {
+ Provider = input.Provider,
+ Model = input.Model,
+ AgentId = input.AgentId,
+ TemplateName = input.TemplateName
+ });
imageViewModel.Content = message.Content;
imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
@@ -231,7 +261,8 @@ public class InstructModeController : ControllerBase
[HttpPost("/instruct/image-variation/upload")]
public async Task ImageVariation(IFormFile file, [FromForm] string? provider = null,
- [FromForm] string? model = null, [FromForm] List? states = null, [FromForm] string? agentId = null)
+ [FromForm] string? model = null, [FromForm] List? states = null,
+ [FromForm] string? agentId = null, [FromForm] string? templateName = null)
{
var state = _services.GetRequiredService();
states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
@@ -239,20 +270,18 @@ public class InstructModeController : ControllerBase
try
{
- using var stream = new MemoryStream();
- file.CopyTo(stream);
- stream.Position = 0;
-
- var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
- var message = await completion.GetImageVariation(new Agent()
+ var fileInstruct = _services.GetRequiredService();
+ var fileData = FileUtility.BuildFileDataFromFile(file);
+ var message = await fileInstruct.VaryImage(new InstructFileModel { FileData = fileData }, new InstructOptions
{
- Id = agentId ?? Guid.Empty.ToString()
- }, new RoleDialogModel(AgentRole.User, string.Empty), stream, file.FileName);
+ Provider = provider,
+ Model = model,
+ AgentId = agentId,
+ TemplateName = templateName
+ });
imageViewModel.Content = message.Content;
imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
- stream.Close();
-
return imageViewModel;
}
catch (Exception ex)
@@ -278,7 +307,13 @@ public class InstructModeController : ControllerBase
{
return new ImageGenerationViewModel { Message = "Error! Cannot find a valid image file!" };
}
- var message = await fileInstruct.EditImage(input.Provider, input.Model, input.Text, input.File, input.AgentId);
+ var message = await fileInstruct.EditImage(input.Text, input.File, new InstructOptions
+ {
+ Provider = input.Provider,
+ Model = input.Model,
+ AgentId = input.AgentId,
+ TemplateName = input.TemplateName
+ });
imageViewModel.Content = message.Content;
imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
return imageViewModel;
@@ -294,7 +329,8 @@ public class InstructModeController : ControllerBase
[HttpPost("/instruct/image-edit/upload")]
public async Task ImageEdit(IFormFile file, [FromForm] string text, [FromForm] string? provider = null,
- [FromForm] string? model = null, [FromForm] List? states = null, [FromForm] string? agentId = null)
+ [FromForm] string? model = null, [FromForm] List? states = null,
+ [FromForm] string? agentId = null, [FromForm] string? templateName = null)
{
var fileInstruct = _services.GetRequiredService();
var state = _services.GetRequiredService();
@@ -303,19 +339,17 @@ public class InstructModeController : ControllerBase
try
{
- using var stream = new MemoryStream();
- file.CopyTo(stream);
- stream.Position = 0;
-
- var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
- var message = await completion.GetImageEdits(new Agent()
+ var fileData = FileUtility.BuildFileDataFromFile(file);
+ var message = await fileInstruct.EditImage(text, new InstructFileModel { FileData = fileData }, new InstructOptions
{
- Id = agentId ?? Guid.Empty.ToString()
- }, new RoleDialogModel(AgentRole.User, text), stream, file.FileName);
+ Provider = provider,
+ Model = model,
+ AgentId = agentId,
+ TemplateName = templateName
+ });
imageViewModel.Content = message.Content;
imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
- stream.Close();
return imageViewModel;
}
@@ -344,7 +378,13 @@ public class InstructModeController : ControllerBase
{
return new ImageGenerationViewModel { Message = "Error! Cannot find a valid image or mask!" };
}
- var message = await fileInstruct.EditImage(input.Provider, input.Model, input.Text, image, mask, input.AgentId);
+ var message = await fileInstruct.EditImage(input.Text, image, mask, new InstructOptions
+ {
+ Provider = input.Provider,
+ Model = input.Model,
+ AgentId = input.AgentId,
+ TemplateName = input.TemplateName
+ });
imageViewModel.Content = message.Content;
imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
return imageViewModel;
@@ -359,8 +399,9 @@ public class InstructModeController : ControllerBase
}
[HttpPost("/instruct/image-mask-edit/upload")]
- public async Task ImageMaskEdit(IFormFile image, IFormFile mask, [FromForm] string text, [FromForm] string? provider = null,
- [FromForm] string? model = null, [FromForm] List? states = null, [FromForm] string? agentId = null)
+ public async Task ImageMaskEdit(IFormFile image, IFormFile mask,
+ [FromForm] string text, [FromForm] string? provider = null, [FromForm] string? model = null,
+ [FromForm] List? states = null, [FromForm] string? agentId = null, [FromForm] string? templateName = null)
{
var fileInstruct = _services.GetRequiredService();
var state = _services.GetRequiredService();
@@ -369,24 +410,20 @@ public class InstructModeController : ControllerBase
try
{
- using var imageStream = new MemoryStream();
- image.CopyTo(imageStream);
- imageStream.Position = 0;
-
- using var maskStream = new MemoryStream();
- mask.CopyTo(maskStream);
- maskStream.Position = 0;
-
- var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
- var message = await completion.GetImageEdits(new Agent()
+ var imageData = FileUtility.BuildFileDataFromFile(image);
+ var maskData = FileUtility.BuildFileDataFromFile(mask);
+ var message = await fileInstruct.EditImage(text,
+ new InstructFileModel { FileData = imageData },
+ new InstructFileModel { FileData = maskData }, new InstructOptions
{
- Id = agentId ?? Guid.Empty.ToString()
- }, new RoleDialogModel(AgentRole.User, text), imageStream, image.FileName, maskStream, mask.FileName);
+ Provider = provider,
+ Model = model,
+ AgentId = agentId,
+ TemplateName = templateName
+ });
imageViewModel.Content = message.Content;
imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList();
- imageStream.Close();
- maskStream.Close();
return imageViewModel;
}
@@ -411,7 +448,13 @@ public class InstructModeController : ControllerBase
try
{
var fileInstruct = _services.GetRequiredService();
- var content = await fileInstruct.ReadPdf(input.Provider, input.Model, input.ModelId, input.Text, input.Files, input.AgentId);
+ var content = await fileInstruct.ReadPdf(input.Text, input.Files, new InstructOptions
+ {
+ Provider = input.Provider,
+ Model = input.Model,
+ AgentId = input.AgentId,
+ TemplateName = input.TemplateName
+ });
viewModel.Content = content;
return viewModel;
}
@@ -425,8 +468,9 @@ public class InstructModeController : ControllerBase
}
[HttpPost("/instruct/pdf-completion/upload")]
- public async Task PdfCompletion(IFormFile file, [FromForm] string text, [FromForm] string? provider = null,
- [FromForm] string? model = null, [FromForm] string? modelId = null, [FromForm] List? states = null, [FromForm] string? agentId = null)
+ public async Task PdfCompletion(IFormFile file, [FromForm] string text,
+ [FromForm] string? provider = null, [FromForm] string? model = null, [FromForm] List? states = null,
+ [FromForm] string? agentId = null, [FromForm] string? templateName = null)
{
var state = _services.GetRequiredService();
states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External));
@@ -441,7 +485,13 @@ public class InstructModeController : ControllerBase
};
var fileInstruct = _services.GetRequiredService();
- var content = await fileInstruct.ReadPdf(provider, model, modelId, text, files, agentId);
+ var content = await fileInstruct.ReadPdf(text, files, new InstructOptions
+ {
+ Provider = provider,
+ Model = model,
+ AgentId = agentId,
+ TemplateName = templateName
+ });
viewModel.Content = content;
return viewModel;
}
@@ -471,7 +521,13 @@ public class InstructModeController : ControllerBase
{
return new SpeechToTextViewModel { Message = "Error! Cannot find a valid audio file!" };
}
- var content = await fileInstruct.SpeechToText(input.Provider, input.Model, audio);
+ var content = await fileInstruct.SpeechToText(audio, input.Text, new InstructOptions
+ {
+ Provider = input.Provider,
+ Model = input.Model,
+ AgentId = input.AgentId,
+ TemplateName = input.TemplateName
+ });
viewModel.Content = content;
return viewModel;
}
@@ -485,8 +541,10 @@ public class InstructModeController : ControllerBase
}
[HttpPost("/instruct/speech-to-text/upload")]
- public async Task SpeechToText(IFormFile file, [FromForm] string? provider = null, [FromForm] string? model = null,
- [FromForm] string? text = null, [FromForm] List? states = null)
+ public async Task SpeechToText(IFormFile file,
+ [FromForm] string? provider = null, [FromForm] string? model = null,
+ [FromForm] string? text = null, [FromForm] List? states = null,
+ [FromForm] string? agentId = null, [FromForm] string? templateName = null)
{
var fileInstruct = _services.GetRequiredService();
var state = _services.GetRequiredService();
@@ -495,14 +553,16 @@ public class InstructModeController : ControllerBase
try
{
- using var stream = new MemoryStream();
- file.CopyTo(stream);
- stream.Position = 0;
+ var auditData = FileUtility.BuildFileDataFromFile(file);
+ var content = await fileInstruct.SpeechToText(new InstructFileModel { FileData = auditData }, text, new InstructOptions
+ {
+ Provider = provider,
+ Model = model,
+ AgentId = agentId,
+ TemplateName = templateName
+ });
- var completion = CompletionProvider.GetAudioTranscriber(_services, provider: provider, model: model);
- var content = await completion.TranscriptTextAsync(stream, file.FileName, text);
viewModel.Content = content;
- stream.Close();
return viewModel;
}
catch (Exception ex)
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs
index 81ec1bc7..8265ebe4 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs
@@ -55,7 +55,7 @@ public class AgentCreationModel
public int? MaxMessageCount { get; set; }
public List Utilities { get; set; } = new();
- public List McpTools { get; set; } = new();
+ public List McpTools { get; set; } = new();
public List RoutingRules { get; set; } = new();
public List KnowledgeBases { get; set; } = new();
public List Rules { get; set; } = new();
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs
index 9f2925e5..5dad68b2 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs
@@ -42,7 +42,7 @@ public class AgentUpdateModel
///
/// McpTools
///
- public List? McpTools { get; set; }
+ public List? McpTools { get; set; }
///
/// knowledge bases
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentViewModel.cs
index db3af34c..8a9a790c 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentViewModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentViewModel.cs
@@ -24,7 +24,9 @@ public class AgentViewModel
[JsonPropertyName("merge_utility")]
public bool MergeUtility { get; set; }
public List Utilities { get; set; }
- public List Acps { get; set; }
+
+ [JsonPropertyName("mcp_tools")]
+ public List McpTools { get; set; }
[JsonPropertyName("knowledge_bases")]
public List KnowledgeBases { get; set; }
@@ -87,7 +89,7 @@ public class AgentViewModel
Responses = agent.Responses ?? [],
Samples = agent.Samples ?? [],
Utilities = agent.Utilities ?? [],
- Acps = agent.McpTools ?? [],
+ McpTools = agent.McpTools ?? [],
KnowledgeBases = agent.KnowledgeBases ?? [],
IsPublic= agent.IsPublic,
Disabled = agent.Disabled,
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs
index 88f011be..80368a70 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs
@@ -10,14 +10,14 @@ public class InstructBaseRequest
[JsonPropertyName("model")]
public virtual string? Model { get; set; } = null;
- [JsonPropertyName("model_id")]
- public virtual string? ModelId { get; set; } = null;
-
[JsonPropertyName("agent_id")]
public virtual string? AgentId { get; set; }
+ [JsonPropertyName("template_name")]
+ public virtual string? TemplateName { get; set; }
+
[JsonPropertyName("states")]
- public List States { get; set; } = new();
+ public List States { get; set; } = [];
}
public class MultiModalRequest : InstructBaseRequest
@@ -26,7 +26,7 @@ public class MultiModalRequest : InstructBaseRequest
public string Text { get; set; } = string.Empty;
[JsonPropertyName("files")]
- public List Files { get; set; } = new();
+ public List Files { get; set; } = [];
}
public class ImageGenerationRequest : InstructBaseRequest
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs
index 1ad3aead..aec81ffb 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs
@@ -8,6 +8,7 @@ public class InstructMessageModel : IncomingMessageModel
public string? Instruction { get; set; }
public override string Channel { get; set; } = ConversationChannel.OpenAPI;
public string? Template { get; set; }
+ public List Files { get; set; } = [];
}
@@ -16,4 +17,5 @@ public class IncomingInstructRequest : IncomingMessageModel
public string? AgentId { get; set; }
public string? Instruction { get; set; }
public string? Template { get; set; }
+ public List Files { get; set; } = [];
}
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs
index a0d99e0d..ae628456 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs
@@ -18,7 +18,7 @@ public class AgentDocument : MongoBase
public List Responses { get; set; }
public List Samples { get; set; }
public List Utilities { get; set; }
- public List McpTools { get; set; }
+ public List McpTools { get; set; }
public List KnowledgeBases { get; set; }
public List Profiles { get; set; }
public List Labels { get; set; }
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs
index 614683c3..99c39fce 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs
@@ -3,34 +3,37 @@ using BotSharp.Abstraction.Agents.Models;
namespace BotSharp.Plugin.MongoStorage.Models;
[BsonIgnoreExtraElements(Inherited = true)]
-public class AgentMCPToolMongoElement
+public class AgentMcpToolMongoElement
{
public string Name { get; set; }
-
public string ServerId { get; set; }
-
public bool Disabled { get; set; }
public List Functions { get; set; } = [];
- public static AgentMCPToolMongoElement ToMongoElement(MCPTool utility)
+ public static AgentMcpToolMongoElement ToMongoElement(McpTool tool)
{
- return new AgentMCPToolMongoElement
+ return new AgentMcpToolMongoElement
{
- Disabled = utility.Disabled,
- Functions = utility.Functions?.Select(x => new McpFunctionMongoElement(x.Name))?.ToList() ?? [],
+ Name = tool.Name,
+ ServerId = tool.ServerId,
+ Disabled = tool.Disabled,
+ Functions = tool.Functions?.Select(x => new McpFunctionMongoElement(x.Name))?.ToList() ?? [],
};
}
- public static MCPTool ToDomainElement(AgentMCPToolMongoElement utility)
+ public static McpTool ToDomainElement(AgentMcpToolMongoElement tool)
{
- return new MCPTool
+ return new McpTool
{
- Disabled = utility.Disabled,
- Functions = utility.Functions?.Select(x => new MCPFunction(x.Name))?.ToList() ?? [],
+ Name = tool.Name,
+ ServerId = tool.ServerId,
+ Disabled = tool.Disabled,
+ Functions = tool.Functions?.Select(x => new McpFunction(x.Name))?.ToList() ?? [],
};
}
}
+[BsonIgnoreExtraElements(Inherited = true)]
public class McpFunctionMongoElement
{
public string Name { get; set; }
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
index ba4b3fde..75b2d470 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
@@ -264,11 +264,11 @@ public partial class MongoRepository
_dc.Agents.UpdateOne(filter, update);
}
- private void UpdateAgentMcpTools(string agentId, List mcps)
+ private void UpdateAgentMcpTools(string agentId, List mcps)
{
if (mcps == null) return;
- var elements = mcps?.Select(x => AgentMCPToolMongoElement.ToMongoElement(x))?.ToList() ?? [];
+ var elements = mcps?.Select(x => AgentMcpToolMongoElement.ToMongoElement(x))?.ToList() ?? [];
var filter = Builders.Filter.Eq(x => x.Id, agentId);
var update = Builders.Update
@@ -346,7 +346,7 @@ public partial class MongoRepository
.Set(x => x.Responses, agent.Responses.Select(r => AgentResponseMongoElement.ToMongoElement(r)).ToList())
.Set(x => x.Samples, agent.Samples)
.Set(x => x.Utilities, agent.Utilities.Select(u => AgentUtilityMongoElement.ToMongoElement(u)).ToList())
- .Set(x => x.McpTools, agent.McpTools.Select(u => AgentMCPToolMongoElement.ToMongoElement(u)).ToList())
+ .Set(x => x.McpTools, agent.McpTools.Select(u => AgentMcpToolMongoElement.ToMongoElement(u)).ToList())
.Set(x => x.KnowledgeBases, agent.KnowledgeBases.Select(u => AgentKnowledgeBaseMongoElement.ToMongoElement(u)).ToList())
.Set(x => x.Rules, agent.Rules.Select(e => AgentRuleMongoElement.ToMongoElement(e)).ToList())
.Set(x => x.LlmConfig, AgentLlmConfigMongoElement.ToMongoElement(agent.LlmConfig))
@@ -527,7 +527,7 @@ public partial class MongoRepository
Responses = x.Responses?.Select(r => AgentResponseMongoElement.ToMongoElement(r))?.ToList() ?? [],
RoutingRules = x.RoutingRules?.Select(r => RoutingRuleMongoElement.ToMongoElement(r))?.ToList() ?? [],
Utilities = x.Utilities?.Select(u => AgentUtilityMongoElement.ToMongoElement(u))?.ToList() ?? [],
- McpTools = x.McpTools?.Select(u => AgentMCPToolMongoElement.ToMongoElement(u))?.ToList() ?? [],
+ McpTools = x.McpTools?.Select(u => AgentMcpToolMongoElement.ToMongoElement(u))?.ToList() ?? [],
KnowledgeBases = x.KnowledgeBases?.Select(k => AgentKnowledgeBaseMongoElement.ToMongoElement(k))?.ToList() ?? [],
Rules = x.Rules?.Select(e => AgentRuleMongoElement.ToMongoElement(e))?.ToList() ?? [],
CreatedTime = x.CreatedDateTime,
@@ -622,7 +622,7 @@ public partial class MongoRepository
Responses = agentDoc.Responses?.Select(r => AgentResponseMongoElement.ToDomainElement(r))?.ToList() ?? [],
RoutingRules = agentDoc.RoutingRules?.Select(r => RoutingRuleMongoElement.ToDomainElement(agentDoc.Id, agentDoc.Name, r))?.ToList() ?? [],
Utilities = agentDoc.Utilities?.Select(u => AgentUtilityMongoElement.ToDomainElement(u))?.ToList() ?? [],
- McpTools = agentDoc.McpTools?.Select(u => AgentMCPToolMongoElement.ToDomainElement(u))?.ToList() ?? [],
+ McpTools = agentDoc.McpTools?.Select(u => AgentMcpToolMongoElement.ToDomainElement(u))?.ToList() ?? [],
KnowledgeBases = agentDoc.KnowledgeBases?.Select(x => AgentKnowledgeBaseMongoElement.ToDomainElement(x))?.ToList() ?? [],
Rules = agentDoc.Rules?.Select(e => AgentRuleMongoElement.ToDomainElement(e))?.ToList() ?? []
};
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/IOpenAiRealtimeApi.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/IOpenAiRealtimeApi.cs
index 68382dff..bdf16ea7 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/IOpenAiRealtimeApi.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/IOpenAiRealtimeApi.cs
@@ -7,5 +7,5 @@ namespace BotSharp.Plugin.OpenAI.Providers.Realtime;
public interface IOpenAiRealtimeApi
{
[Post("/v1/realtime/sessions")]
- Task GetSessionAsync(RealtimeSessionCreationRequest model, [Authorize("Bearer")] string token);
+ Task CreateSessionAsync(RealtimeSessionCreationRequest model, [Authorize("Bearer")] string token);
}
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
index ecc553d3..4499f851 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
@@ -297,7 +297,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
var settings = settingsService.GetSetting(Provider, args.Model ?? _model);
var api = _services.GetRequiredService();
- var session = await api.GetSessionAsync(args, settings.ApiKey);
+ var session = await api.CreateSessionAsync(args, settings.ApiKey);
return session;
}
diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlValidateFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlValidateFn.cs
index a4e6d4b8..89b38f93 100644
--- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlValidateFn.cs
+++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlValidateFn.cs
@@ -53,17 +53,17 @@ public class SqlValidateFn : IFunctionCallback
var instructService = _services.GetRequiredService();
var agentService = _services.GetRequiredService();
var states = _services.GetRequiredService();
-
- var agent = await agentService.GetAgent(BuiltInAgentId.SqlDriver);
- var template = agent.Templates.FirstOrDefault(x => x.Name == "sql_statement_correctness")?.Content ?? string.Empty;
+
+ var query = "Correct SQL Statement and keep the comments/explanations";
var ddl = states.GetState("table_ddls");
- var correctedSql = await instructService.Instruct(template, BuiltInAgentId.SqlDriver,
+ var correctedSql = await instructService.Instruct(query,
new InstructOptions
{
- Provider = agent?.LlmConfig?.Provider ?? "openai",
- Model = agent?.LlmConfig?.Model ?? "gpt-4o",
- Message = "Correct SQL Statement and keep the comments/explanations",
+ Provider = "openai",
+ Model = "gpt-4o",
+ AgentId = BuiltInAgentId.SqlDriver,
+ TemplateName = "sql_statement_correctness",
Data = new Dictionary
{
{ "original_sql", message.Content },
diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs
index b851cd1a..865864a0 100644
--- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs
+++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.DoAction.cs
@@ -1,3 +1,6 @@
+using System.IO;
+using System.Net.Http;
+
namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
public partial class PlaywrightWebDriver
@@ -50,6 +53,18 @@ public partial class PlaywrightWebDriver
});
}
}
+ else if (action.Action == BroswerActionEnum.DropDown)
+ {
+ await locator.ClickAsync();
+ var optionLocator = page.Locator($"//div[text()='{action.Content}']");
+ var optionCount = await optionLocator.CountAsync(); ;
+ if (optionCount == 0)
+ {
+ Serilog.Log.Error($"Dropdown option not found: {action.Content}");
+ return;
+ }
+ await optionLocator.First.ClickAsync();
+ }
else if (action.Action == BroswerActionEnum.InputText)
{
await locator.FillAsync(action.Content);
@@ -63,6 +78,36 @@ public partial class PlaywrightWebDriver
await locator.PressAsync(action.PressKey);
}
}
+ else if (action.Action == BroswerActionEnum.FileUpload)
+ {
+ if (action.FileUrl.Length == 0)
+ {
+ return;
+ }
+ var fileChooser = await page.RunAndWaitForFileChooserAsync(async () =>
+ {
+ await locator.ClickAsync();
+ });
+ var guid = Guid.NewGuid().ToString();
+ var directory = Path.Combine(Path.GetTempPath(), guid);
+ if (Directory.Exists(directory))
+ {
+ Directory.Delete(directory, true);
+ }
+ Directory.CreateDirectory(directory);
+ var localPaths = new List();
+ using var httpClient = new HttpClient();
+ foreach (var fileUrl in action.FileUrl)
+ {
+ var bytes = await httpClient.GetByteArrayAsync(fileUrl);
+ var fileName = new Uri(fileUrl).AbsolutePath;
+ var localPath = Path.Combine(directory, Path.GetFileName(fileName));
+ await File.WriteAllBytesAsync(localPath, bytes);
+ await Task.Delay(2000);
+ localPaths.Add(localPath);
+ }
+ await fileChooser.SetFilesAsync(localPaths);
+ }
else if (action.Action == BroswerActionEnum.Typing)
{
await locator.PressSequentiallyAsync(action.Content);
@@ -128,7 +173,6 @@ public partial class PlaywrightWebDriver
await Task.Delay(1000 * action.WaitTime);
}
}
-
public static List GetVelocityTrack(float distance)
{
// Initialize the track list to store the movement distances
diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-web-action_on_element.json b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-web-action_on_element.json
index f91425b4..b397fc49 100644
--- a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-web-action_on_element.json
+++ b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-web-action_on_element.json
@@ -11,12 +11,20 @@
"action": {
"type": "string",
"description": "action to perform on element",
- "enum": [ "Click", "InputText" ]
+ "enum": [ "Click", "InputText", "DropDown", "FileUpload" ]
},
"content": {
"type": "string",
"description": "content to input in element"
},
+ "file_urls": {
+ "type": "array",
+ "description": "The output format must be string array of file urls.",
+ "items": {
+ "type": "string",
+ "description": "file url"
+ }
+ },
"press_key": {
"type": "string",
"enum": [ "Enter" ],
diff --git a/src/WebStarter/Program.cs b/src/WebStarter/Program.cs
index d4015267..33651c05 100644
--- a/src/WebStarter/Program.cs
+++ b/src/WebStarter/Program.cs
@@ -1,4 +1,5 @@
using BotSharp.Core;
+using BotSharp.Core.MCP;
using BotSharp.OpenAPI;
using BotSharp.Logger;
using BotSharp.Plugin.ChatHub;
@@ -23,6 +24,7 @@ string[] allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get
options.JsonSerializerOptions.Converters.Add(new RichContentJsonConverter());
options.JsonSerializerOptions.Converters.Add(new TemplateMessageJsonConverter());
}).AddBotSharpOpenAPI(builder.Configuration, allowedOrigins, builder.Environment, true)
+ .AddBotSharpMCP(builder.Configuration)
.AddBotSharpLogger(builder.Configuration);
// Add service defaults & Aspire components.
diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj
index 7da8de59..0a115a1d 100644
--- a/src/WebStarter/WebStarter.csproj
+++ b/src/WebStarter/WebStarter.csproj
@@ -79,6 +79,7 @@
+
@@ -121,7 +122,6 @@
-
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index b9d6746f..51b2d0bc 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -184,6 +184,7 @@
"Model": "gpt-4o-mini"
}
},
+
"MCPSettings": {
"McpClientOptions": {
"ClientInfo": {
@@ -201,6 +202,7 @@
}
]
},
+
"Conversation": {
"DataDir": "conversations",
"ShowVerboseLog": false,
@@ -425,8 +427,8 @@
"BotSharp.Core.SideCar",
"BotSharp.Core.Crontab",
"BotSharp.Core.Realtime",
+ "BotSharp.Core.MCP",
"BotSharp.Logger",
- "BotSharp.MCP",
"BotSharp.Plugin.MongoStorage",
"BotSharp.Plugin.Dashboard",
"BotSharp.Plugin.OpenAI",
@@ -453,6 +455,9 @@
"BotSharp.Plugin.EmailHandler",
"BotSharp.Plugin.AudioHandler",
"BotSharp.Plugin.TencentCos"
+ ],
+ "ExcludedFunctions": [
+ "McpToolAdapter"
]
}
}
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Program.cs b/tests/BotSharp.PizzaBot.MCPServer/Program.cs
index fb39e6cd..d2cf912e 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/Program.cs
+++ b/tests/BotSharp.PizzaBot.MCPServer/Program.cs
@@ -2,7 +2,8 @@ using BotSharp.PizzaBot.MCPServer;
using ModelContextProtocol;
var builder = WebApplication.CreateBuilder(args);
-builder.Services.AddMcpServer().WithTools();
+builder.Services.AddMcpServer()
+ .WithToolsFromAssembly();
var app = builder.Build();
app.MapGet("/", () => "This is a test server with only stub functionality!");
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs b/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs
index a236d874..da198c47 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs
+++ b/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs
@@ -4,10 +4,10 @@ using System.ComponentModel.DataAnnotations;
namespace BotSharp.PizzaBot.MCPServer.Tools;
-[McpToolType]
+[McpServerToolType]
public static class MakePayment
{
- [McpTool(name: "make_payment"), Description("call this function to make payment.")]
+ [McpServerTool(name: "make_payment"), Description("call this function to make payment.")]
public static string Make_Payment(
[Description("order number"),Required] string order_number,
[Description("total amount"),Required] int total_amount)
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs b/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs
index 7ca766f3..762d67db 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs
+++ b/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs
@@ -6,10 +6,10 @@ using System.Text.Json;
namespace BotSharp.PizzaBot.MCPServer.Tools;
-[McpToolType]
+[McpServerToolType]
public static class PizzaPrices
{
- [McpTool(name: "get_pizza_prices"), Description("call this function to get pizza unit price.")]
+ [McpServerTool(name: "get_pizza_prices"), Description("call this function to get pizza unit price.")]
public static string GetPizzaPrices(
[Description("The pizza type."), Required] string pizza_type,
[Description("quantity of pizza"), Required] int quantity)
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs b/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs
index a6237b6d..73245751 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs
+++ b/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs
@@ -4,10 +4,10 @@ using System.ComponentModel.DataAnnotations;
namespace BotSharp.PizzaBot.MCPServer.Tools;
-[McpToolType]
+[McpServerToolType]
public static class PlaceOrder
{
- [McpTool(name: "place_an_order"), Description("Place an order when user has confirmed the pizza type and quantity.")]
+ [McpServerTool(name: "place_an_order"), Description("Place an order when user has confirmed the pizza type and quantity.")]
public static string PlaceAnOrder(
[Description("The pizza type."), Required] string pizza_type,
[Description("quantity of pizza"), Required] int quantity,