resolve conflict.

This commit is contained in:
Haiping Chen 2024-07-16 20:27:20 -05:00
commit 52d1387121
55 changed files with 685 additions and 302 deletions

View file

@ -97,6 +97,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.OpenAI", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.EmailHandler", "src\Plugins\BotSharp.Plugin.EmailHandler\BotSharp.Plugin.EmailHandler.csproj", "{A72B3BEB-E14B-4917-BE44-97EAE4E122D2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.FileHandler", "src\Plugins\BotSharp.Plugin.FileHandler\BotSharp.Plugin.FileHandler.csproj", "{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -393,6 +395,14 @@ Global
{A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|Any CPU.Build.0 = Release|Any CPU
{A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|x64.ActiveCfg = Release|Any CPU
{A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|x64.Build.0 = Release|Any CPU
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|x64.ActiveCfg = Debug|Any CPU
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|x64.Build.0 = Debug|Any CPU
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|Any CPU.Build.0 = Release|Any CPU
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|x64.ActiveCfg = Release|Any CPU
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -439,6 +449,7 @@ Global
{806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
{6507D336-3A4D-41D4-81C0-2B900173A5FE} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
{A72B3BEB-E14B-4917-BE44-97EAE4E122D2} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}

View file

@ -110,7 +110,7 @@ public class RoleDialogModel : ITrackableMessage
{
if (Role == AgentRole.Function)
{
return $"{Role}: {FunctionName}({FunctionArgs}) => {Content}";
return $"{Role}: {Content} <= {FunctionName}({FunctionArgs})";
}
else
{

View file

@ -3,7 +3,9 @@ namespace BotSharp.Abstraction.Files;
public interface IBotSharpFileService
{
string GetDirectory(string conversationId);
Task<IEnumerable<MessageFileModel>> GetChatImages(string conversationId, string source, IEnumerable<string> fileTypes, List<RoleDialogModel> conversations, int? offset = null);
Task<IEnumerable<MessageFileModel>> GetChatImages(string conversationId, string source,
IEnumerable<RoleDialogModel> conversations, IEnumerable<string> contentTypes,
bool includeScreenShot = false, int? offset = null);
IEnumerable<MessageFileModel> GetMessageFiles(string conversationId, IEnumerable<string> messageIds, string source, bool imageOnly = false);
string GetMessageFile(string conversationId, string messageId, string source, string index, string fileName);
IEnumerable<MessageFileModel> GetMessagesWithFile(string conversationId, IEnumerable<string> messageIds);

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(TargetFramework)</TargetFramework>
@ -48,10 +48,8 @@
<ItemGroup>
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\agent.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\generate_image.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\read_file.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\instruction.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\generate_image.fn.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\read_file.fn.liquid" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\agent.json" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions.json" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions\human_intervention_needed.json" />
@ -159,12 +157,6 @@
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\instruction.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\read_file.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\read_file.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\generate_image.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@ -191,7 +183,6 @@
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Nanoid" Version="3.1.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.7" />
</ItemGroup>
<ItemGroup>

View file

@ -65,14 +65,14 @@ public class EvaluatingService : IEvaluatingService
if (roundCount > 10)
{
Console.WriteLine($"Conversation ended due to execced max round count {roundCount}", Color.Red);
Console.WriteLine($"Conversation ended due to execced max round count {roundCount}");
break;
}
if (response.FunctionName == "conversation_end" ||
response.FunctionName == "human_intervention_needed")
{
Console.WriteLine($"Conversation ended by function {response.FunctionName}", Color.Green);
Console.WriteLine($"Conversation ended by function {response.FunctionName}");
break;
}
}

View file

@ -17,8 +17,6 @@ public class FilePlugin : IBotSharpPlugin
{
services.AddScoped<IBotSharpFileService, BotSharpFileService>();
services.AddScoped<IAgentHook, FileReaderHook>();
services.AddScoped<IAgentUtilityHook, FileReaderUtilityHook>();
services.AddScoped<IAgentHook, ImageGeneratorHook>();
services.AddScoped<IAgentUtilityHook, ImageGeneratorUtilityHook>();
}

View file

@ -1,52 +0,0 @@
namespace BotSharp.Core.Files.Hooks;
public class FileReaderHook : AgentHookBase
{
private static string FUNCTION_NAME = "read_file";
public override string SelfId => string.Empty;
public FileReaderHook(IServiceProvider services, AgentSettings settings)
: base(services, settings)
{
}
public override void OnAgentLoaded(Agent agent)
{
var conv = _services.GetRequiredService<IConversationService>();
var isConvMode = conv.IsConversationMode();
var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(AgentUtility.FileReader);
if (isConvMode && isEnabled)
{
var (prompt, fn) = GetPromptAndFunction();
if (fn != null)
{
if (!string.IsNullOrWhiteSpace(prompt))
{
agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n";
}
if (agent.Functions == null)
{
agent.Functions = new List<FunctionDef> { fn };
}
else
{
agent.Functions.Add(fn);
}
}
}
base.OnAgentLoaded(agent);
}
private (string, FunctionDef?) GetPromptAndFunction()
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant);
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty;
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME));
return (prompt, loadAttachmentFn);
}
}

View file

@ -1,9 +0,0 @@
namespace BotSharp.Core.Files.Hooks;
public class FileReaderUtilityHook : IAgentUtilityHook
{
public void AddUtilities(List<string> utilities)
{
utilities.Add(AgentUtility.FileReader);
}
}

View file

@ -1,124 +1,43 @@
using AspectInjector.Broker;
using BotSharp.Abstraction.Files.Converters;
using Microsoft.EntityFrameworkCore;
using System.IO;
using System.Threading;
namespace BotSharp.Core.Files.Services;
public partial class BotSharpFileService
{
public async Task<IEnumerable<MessageFileModel>> GetChatImages(string conversationId, string source, IEnumerable<string> fileTypes,
List<RoleDialogModel> conversations, int? offset = null)
public async Task<IEnumerable<MessageFileModel>> GetChatImages(string conversationId, string source,
IEnumerable<RoleDialogModel> conversations, IEnumerable<string> contentTypes,
bool includeScreenShot = false, int? offset = null)
{
var files = new List<MessageFileModel>();
if (string.IsNullOrEmpty(conversationId) || conversations.IsNullOrEmpty())
{
return new List<MessageFileModel>();
return files;
}
if (offset <= 0)
var messageIds = GetMessageIds(conversations, offset);
var pathPrefix = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER);
foreach (var messageId in messageIds)
{
offset = MIN_OFFSET;
}
else if (offset > MAX_OFFSET)
{
offset = MAX_OFFSET;
}
var dir = Path.Combine(pathPrefix, messageId, source);
if (!ExistDirectory(dir)) continue;
var messageIds = new List<string>();
if (offset.HasValue)
{
messageIds = conversations.Select(x => x.MessageId).Distinct().TakeLast(offset.Value).ToList();
}
else
{
messageIds = conversations.Select(x => x.MessageId).Distinct().ToList();
}
files = await GetMessageFiles(conversationId, messageIds, source, fileTypes);
return files;
}
private async Task<List<MessageFileModel>> GetMessageFiles(string conversationId, IEnumerable<string> messageIds, string source, IEnumerable<string> fileTypes)
{
var files = new List<MessageFileModel>();
if (string.IsNullOrEmpty(conversationId) || messageIds.IsNullOrEmpty() || fileTypes.IsNullOrEmpty()) return files;
var isNeedScreenShot = fileTypes.Any(x => _allowScreenShotTypes.Contains(x));
var onlyScreenShot = fileTypes.All(x => _allowScreenShotTypes.Contains(x));
try
{
var preFixPath = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER);
foreach (var messageId in messageIds)
foreach (var subDir in Directory.GetDirectories(dir))
{
var dir = Path.Combine(preFixPath, messageId, source);
if (!ExistDirectory(dir)) continue;
var file = Directory.GetFiles(subDir).FirstOrDefault();
if (file == null) continue;
foreach (var subDir in Directory.GetDirectories(dir))
{
var file = Directory.GetFiles(subDir).FirstOrDefault();
if (file == null) continue;
var contentType = GetFileContentType(file);
if (contentTypes?.Contains(contentType) != true) continue;
var index = subDir.Split(Path.DirectorySeparatorChar).Last();
var contentType = GetFileContentType(file);
var foundFiles = await GetMessageFiles(file, subDir, contentType, messageId, source, includeScreenShot);
if (foundFiles.IsNullOrEmpty()) continue;
if ((!isNeedScreenShot || (isNeedScreenShot && !onlyScreenShot)) && _allowedImageTypes.Contains(contentType))
{
var model = new MessageFileModel()
{
MessageId = messageId,
FileStorageUrl = file,
ContentType = contentType
};
files.Add(model);
}
else if ((isNeedScreenShot && !onlyScreenShot || onlyScreenShot) && !_allowedImageTypes.Contains(contentType))
{
var screenShotDir = Path.Combine(subDir, SCREENSHOT_FILE_FOLDER);
if (ExistDirectory(screenShotDir) && Directory.GetFiles(screenShotDir).Any())
{
foreach (var screenShot in Directory.GetFiles(screenShotDir))
{
contentType = GetFileContentType(screenShot);
if (!_allowedImageTypes.Contains(contentType)) continue;
var model = new MessageFileModel()
{
MessageId = messageId,
FileStorageUrl = screenShot,
ContentType = contentType
};
files.Add(model);
}
}
else
{
var screenShotPath = Path.Combine(subDir, SCREENSHOT_FILE_FOLDER);
var images = await ConvertPdfToImages(file, screenShotPath);
foreach (var image in images)
{
contentType = GetFileContentType(image);
var model = new MessageFileModel()
{
MessageId = messageId,
FileStorageUrl = image,
ContentType = contentType
};
files.Add(model);
}
}
}
}
files.AddRange(foundFiles);
}
}
catch (Exception ex)
{
_logger.LogWarning($"Error when reading conversation ({conversationId}) files: {ex.Message}\r\n{ex.InnerException}\r\n{ex.StackTrace}");
}
return files;
}
@ -144,7 +63,7 @@ public partial class BotSharpFileService
foreach (var file in Directory.GetFiles(subDir))
{
var contentType = GetFileContentType(file);
if (imageOnly && !_allowedImageTypes.Contains(contentType))
if (imageOnly && !_imageTypes.Contains(contentType))
{
continue;
}
@ -327,6 +246,102 @@ public partial class BotSharpFileService
return dir;
}
private IEnumerable<string> GetMessageIds(IEnumerable<RoleDialogModel> conversations, int? offset = null)
{
if (conversations.IsNullOrEmpty()) return Enumerable.Empty<string>();
if (offset <= 0)
{
offset = MIN_OFFSET;
}
else if (offset > MAX_OFFSET)
{
offset = MAX_OFFSET;
}
var messageIds = new List<string>();
if (offset.HasValue)
{
messageIds = conversations.Select(x => x.MessageId).Distinct().TakeLast(offset.Value).ToList();
}
else
{
messageIds = conversations.Select(x => x.MessageId).Distinct().ToList();
}
return messageIds;
}
private async Task<IEnumerable<MessageFileModel>> GetMessageFiles(string file, string fileDir, string contentType,
string messageId, string source, bool includeScreenShot)
{
var files = new List<MessageFileModel>();
try
{
if (!_imageTypes.Contains(contentType) && includeScreenShot)
{
var screenShotDir = Path.Combine(fileDir, SCREENSHOT_FILE_FOLDER);
if (ExistDirectory(screenShotDir) && !Directory.GetFiles(screenShotDir).IsNullOrEmpty())
{
foreach (var screenShot in Directory.GetFiles(screenShotDir))
{
contentType = GetFileContentType(screenShot);
if (!_imageTypes.Contains(contentType)) continue;
var model = new MessageFileModel()
{
MessageId = messageId,
FileName = Path.GetFileName(screenShot),
FileStorageUrl = screenShot,
ContentType = contentType,
FileSource = source
};
files.Add(model);
}
}
else if (contentType == MediaTypeNames.Application.Pdf)
{
var images = await ConvertPdfToImages(file, screenShotDir);
foreach (var image in images)
{
contentType = GetFileContentType(image);
var model = new MessageFileModel()
{
MessageId = messageId,
FileName = Path.GetFileName(image),
FileStorageUrl = image,
ContentType = contentType,
FileSource = source
};
files.Add(model);
}
}
}
else
{
var model = new MessageFileModel()
{
MessageId = messageId,
FileName = Path.GetFileName(file),
FileStorageUrl = file,
ContentType = contentType,
FileSource = source
};
files.Add(model);
}
return files;
}
catch (Exception ex)
{
_logger.LogWarning($"Error when getting message files {file} (messageId: {messageId}), Error: {ex.Message}\r\n{ex.InnerException}");
return files;
}
}
private async Task<IEnumerable<string>> ConvertPdfToImages(string pdfLoc, string imageLoc)
{
var converters = _services.GetServices<IPdf2ImageConverter>();

View file

@ -1,7 +1,4 @@
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
namespace BotSharp.Core.Files.Services;

View file

@ -10,8 +10,11 @@ public partial class BotSharpFileService : IBotSharpFileService
private readonly IUserIdentity _user;
private readonly ILogger<BotSharpFileService> _logger;
private readonly string _baseDir;
private readonly IEnumerable<string> _allowedImageTypes = new List<string> { "image/png", "image/jpeg" };
private readonly IEnumerable<string> _allowScreenShotTypes = new List<string> { "pdf" };
private readonly IEnumerable<string> _imageTypes = new List<string>
{
MediaTypeNames.Image.Png,
MediaTypeNames.Image.Jpeg
};
private const string CONVERSATION_FOLDER = "conversations";
private const string FILE_FOLDER = "files";

View file

@ -67,7 +67,7 @@ public class PluginLoader
if (!_plugins.Any(x => x.Assembly == plugin))
{
Console.WriteLine($"Load dependent plugin {plugin} failed by {module.Name}.", Color.Red);
Console.WriteLine($"Load dependent plugin {plugin} failed by {module.Name}.");
}
}
}
@ -79,7 +79,7 @@ public class PluginLoader
}
else
{
Console.WriteLine($"Can't find assemble {assemblyPath}.", Color.Red);
Console.WriteLine($"Can't find assemble {assemblyPath}.");
}
});
}
@ -101,7 +101,7 @@ public class PluginLoader
AgentIds = module.AgentIds
});
Console.Write($"Loaded plugin ");
Console.Write(name, Color.Green);
Console.Write(name);
Console.WriteLine($" from {assembly}.");
if (!string.IsNullOrEmpty(module.Description))
{
@ -258,7 +258,7 @@ public class PluginLoader
{
if (_modules.Count == 0)
{
Console.WriteLine($"No plugin loaded. Please check whether the Load() method is called.", Color.Yellow);
Console.WriteLine($"No plugin loaded. Please check whether the Load() method is called.");
}
_modules.ForEach(module =>

View file

@ -89,7 +89,6 @@ public partial class RouteToAgentFn : IFunctionCallback
// Stack redirection agent
_context.Push(agentId, reason: $"REDIRECTION {reason}");
message.Content = reason;
message.Role = AgentRole.Function;
}
}

View file

@ -59,12 +59,8 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
message.FunctionArgs = JsonSerializer.Serialize(inst);
if (message.FunctionName != null)
{
var msg = RoleDialogModel.From(message);
var msg = RoleDialogModel.From(message, role: AgentRole.Function);
var ret = await routing.InvokeFunction(message.FunctionName, msg);
if (msg.Role == AgentRole.Function)
{
_dialogs.Add(msg);
}
}
var agentId = routing.Context.GetCurrentAgentId();

View file

@ -88,7 +88,7 @@ public partial class RoutingService
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "redirect_to", record.Name);
agentId = routingRule.RedirectTo;
#if DEBUG
Console.WriteLine($"*** Routing redirect to {record.Name.ToUpper()} ***", Color.Yellow);
Console.WriteLine($"*** Routing redirect to {record.Name.ToUpper()} ***");
#else
logger.LogInformation($"*** Routing redirect to {record.Name.ToUpper()} ***");
#endif

View file

@ -4,6 +4,9 @@ global using System.Text;
global using System.Threading.Tasks;
global using System.Linq;
global using System.Text.Json;
global using System.Net.Mime;
global using System.Net.Http;
global using System.Threading;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Logging;
global using EntityFrameworkCore.BootKit;

View file

@ -1,18 +0,0 @@
{
"name": "read_file",
"description": "If the user's request is related to analyzing files and/or images, you can call this function to analyze files and images.",
"parameters": {
"type": "object",
"properties": {
"user_request": {
"type": "string",
"description": "The request posted by user, which is related to analyzing requested files. User can request for multiple files to process at one time."
},
"file_types": {
"type": "string",
"description": "The file types requested by user to analyze, such as image, png, jpeg, and pdf. There can be multiple file types in a single request. An example output is, 'image,pdf'."
}
},
"required": [ "user_request", "file_types" ]
}
}

View file

@ -1 +0,0 @@
Please call read_file if user wants to describe files, such as images, pdf.

View file

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace BotSharp.Plugin.EmailHandler.Enums
{
public class Utility
public class UtilityName
{
public const string EmailHandler = "email-handler";
}

View file

@ -26,7 +26,7 @@ public class EmailHandlerHook : AgentHookBase
{
var conv = _services.GetRequiredService<IConversationService>();
var isConvMode = conv.IsConversationMode();
var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(Utility.EmailHandler);
var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.EmailHandler);
if (isConvMode && isEnabled)
{

View file

@ -12,7 +12,7 @@ namespace BotSharp.Plugin.EmailHandler.Hooks
{
public void AddUtilities(List<string> utilities)
{
utilities.Add(Utility.EmailHandler);
utilities.Add(UtilityName.EmailHandler);
}
}
}

View file

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(TargetFramework)</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
<GenerateDocumentationFile>$(GenerateDocumentationFile)</GenerateDocumentationFile>
<OutputPath>$(SolutionDir)packages</OutputPath>
</PropertyGroup>
<ItemGroup>
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\read_image.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\read_pdf.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\read_image.fn.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\read_pdf.fn.liquid" />
</ItemGroup>
<ItemGroup>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\read_image.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\read_image.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\read_pdf.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\read_pdf.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,7 @@
namespace BotSharp.Plugin.FileHandler.Enums;
public class UtilityName
{
public const string ImageReader = "image-reader";
public const string PdfReader = "pdf-reader";
}

View file

@ -0,0 +1,26 @@
using BotSharp.Plugin.FileHandler.Hooks;
using Microsoft.Extensions.Configuration;
namespace BotSharp.Plugin.FileHandler;
public class FileHandlerPlugin : IBotSharpPlugin
{
public string Id => "65be5aee-48df-4ff8-a50a-05c8bcd2a793";
public string Name => "File Handler";
public string Description => "AI reads files, such as image, pdf, excel";
public string IconUrl => "https://lirp.cdn-website.com/6f8d6d8a/dms3rep/multi/opt/API_Icon-640w.png";
public string[] AgentIds => [];
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddScoped(provider =>
{
var settingService = provider.GetRequiredService<ISettingService>();
return settingService.Bind<FileHandlerSettings>("FileHandler");
});
services.AddScoped<IAgentHook, FileHandlerHook>();
services.AddScoped<IAgentUtilityHook, FileHandlerUtilityHook>();
}
}

View file

@ -0,0 +1,90 @@
namespace BotSharp.Plugin.FileHandler.Functions;
public class ReadImageFn : IFunctionCallback
{
public string Name => "read_image";
public string Indication => "Reading images";
private readonly IServiceProvider _services;
private readonly ILogger<ReadImageFn> _logger;
private readonly IEnumerable<string> _imageContentTypes = new List<string>
{
MediaTypeNames.Image.Png,
MediaTypeNames.Image.Jpeg,
};
public ReadImageFn(
IServiceProvider services,
ILogger<ReadImageFn> logger)
{
_services = services;
_logger = logger;
}
public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<LlmContextIn>(message.FunctionArgs);
var conv = _services.GetRequiredService<IConversationService>();
var agentService = _services.GetRequiredService<IAgentService>();
var wholeDialogs = conv.GetDialogHistory();
var dialogs = await AssembleFiles(conv.ConversationId, wholeDialogs);
var agent = await agentService.LoadAgent(BuiltInAgentId.UtilityAssistant);
var fileAgent = new Agent
{
Id = agent?.Id ?? Guid.Empty.ToString(),
Name = agent?.Name ?? "Unkown",
Instruction = !string.IsNullOrWhiteSpace(args?.UserRequest) ? args.UserRequest : "Please describe the image(s).",
TemplateDict = new Dictionary<string, object>()
};
var response = await GetChatCompletion(fileAgent, dialogs);
message.Content = response;
return true;
}
private async Task<List<RoleDialogModel>> AssembleFiles(string conversationId, List<RoleDialogModel> dialogs)
{
if (dialogs.IsNullOrEmpty())
{
return new List<RoleDialogModel>();
}
var fileService = _services.GetRequiredService<IBotSharpFileService>();
var images = await fileService.GetChatImages(conversationId, FileSourceType.User, dialogs, _imageContentTypes);
foreach (var dialog in dialogs)
{
var found = images.Where(x => x.MessageId == dialog.MessageId).ToList();
if (found.IsNullOrEmpty()) continue;
dialog.Files = found.Select(x => new BotSharpFile
{
ContentType = x.ContentType,
FileStorageUrl = x.FileStorageUrl
}).ToList();
}
return dialogs;
}
private async Task<string> GetChatCompletion(Agent agent, List<RoleDialogModel> dialogs)
{
try
{
var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
var provider = llmProviderService.GetProviders().FirstOrDefault(x => x == "openai");
var model = llmProviderService.GetProviderModel(provider: provider, id: "gpt-4", multiModal: true);
var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model.Name);
var response = await completion.GetChatCompletions(agent, dialogs);
return response.Content;
}
catch (Exception ex)
{
var error = $"Error when analyzing images.";
_logger.LogWarning($"{error} {ex.Message}\r\n{ex.InnerException}");
return error;
}
}
}

View file

@ -1,21 +1,21 @@
using BotSharp.Abstraction.Functions;
using BotSharp.Abstraction.MLTasks;
namespace BotSharp.Plugin.FileHandler.Functions;
namespace BotSharp.Core.Files.Functions;
public class ReadFileFn : IFunctionCallback
public class ReadPdfFn : IFunctionCallback
{
public string Name => "read_file";
public string Indication => "Reading files";
public string Name => "read_pdf";
public string Indication => "Reading pdf";
private readonly IServiceProvider _services;
private readonly ILogger<ReadFileFn> _logger;
private readonly IEnumerable<string> _imageTypes = new List<string> { "image", "images", "png", "jpg", "jpeg" };
private readonly IEnumerable<string> _pdfTypes = new List<string> { "pdf" };
private readonly ILogger<ReadPdfFn> _logger;
public ReadFileFn(
private readonly IEnumerable<string> _pdfContentTypes = new List<string>
{
MediaTypeNames.Application.Pdf
};
public ReadPdfFn(
IServiceProvider services,
ILogger<ReadFileFn> logger)
ILogger<ReadPdfFn> logger)
{
_services = services;
_logger = logger;
@ -23,38 +23,35 @@ public class ReadFileFn : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<LlmFileContext>(message.FunctionArgs);
var args = JsonSerializer.Deserialize<LlmContextIn>(message.FunctionArgs);
var conv = _services.GetRequiredService<IConversationService>();
var agentService = _services.GetRequiredService<IAgentService>();
var wholeDialogs = conv.GetDialogHistory();
var fileTypes = args?.FileTypes?.Split(",", StringSplitOptions.RemoveEmptyEntries)?.ToList() ?? new List<string>();
var dialogs = await AssembleFiles(conv.ConversationId, wholeDialogs, fileTypes);
var dialogs = await AssembleFiles(conv.ConversationId, wholeDialogs);
var agent = await agentService.LoadAgent(BuiltInAgentId.UtilityAssistant);
var fileAgent = new Agent
{
Id = agent?.Id ?? Guid.Empty.ToString(),
Name = agent?.Name ?? "Unkown",
Instruction = !string.IsNullOrWhiteSpace(args?.UserRequest) ? args.UserRequest : "Please describe the files.",
Instruction = !string.IsNullOrWhiteSpace(args?.UserRequest) ? args.UserRequest : "Please describe the pdf file(s).",
TemplateDict = new Dictionary<string, object>()
};
var response = await GetChatCompletion(fileAgent, dialogs);
message.Content = response;
message.StopCompletion = true;
return true;
}
private async Task<List<RoleDialogModel>> AssembleFiles(string conversationId, List<RoleDialogModel> dialogs, List<string> fileTypes)
private async Task<List<RoleDialogModel>> AssembleFiles(string conversationId, List<RoleDialogModel> dialogs)
{
if (dialogs.IsNullOrEmpty())
{
return new List<RoleDialogModel>();
}
var parsedTypes = ParseFileTypes(fileTypes);
var fileService = _services.GetRequiredService<IBotSharpFileService>();
var files = await fileService.GetChatImages(conversationId, FileSourceType.User, parsedTypes, dialogs);
var files = await fileService.GetChatImages(conversationId, FileSourceType.User, dialogs, _pdfContentTypes, includeScreenShot: true);
foreach (var dialog in dialogs)
{
@ -71,38 +68,6 @@ public class ReadFileFn : IFunctionCallback
return dialogs;
}
private IEnumerable<string> ParseFileTypes(IEnumerable<string> fileTypes)
{
var imageType = "image";
var pdfType = "pdf";
var parsed = new List<string>();
if (fileTypes.IsNullOrEmpty())
{
return new List<string> { imageType };
}
foreach (var fileType in fileTypes)
{
var type = fileType?.Trim();
if (string.IsNullOrWhiteSpace(type) || _imageTypes.Any(x => type.IsEqualTo(x)))
{
parsed.Add(imageType);
}
else if (_pdfTypes.Any(x => type.IsEqualTo(x)))
{
parsed.Add(pdfType);
}
}
if (parsed.IsNullOrEmpty())
{
parsed.Add(imageType);
}
return parsed.Distinct();
}
private async Task<string> GetChatCompletion(Agent agent, List<RoleDialogModel> dialogs)
{
try
@ -116,8 +81,8 @@ public class ReadFileFn : IFunctionCallback
}
catch (Exception ex)
{
var error = $"Error when analyzing files.";
_logger.LogWarning($"{error} {ex.Message}");
var error = $"Error when analyzing pdf file(s).";
_logger.LogWarning($"{error} {ex.Message}\r\n{ex.InnerException}");
return error;
}
}

View file

@ -0,0 +1,64 @@
namespace BotSharp.Plugin.FileHandler.Hooks;
public class FileHandlerHook : AgentHookBase, IAgentHook
{
private const string READ_IMAGE_FN = "read_image";
private const string READ_PDF_FN = "read_pdf";
public override string SelfId => string.Empty;
public FileHandlerHook(IServiceProvider services, AgentSettings settings) : base(services, settings)
{
}
public override void OnAgentLoaded(Agent agent)
{
var conv = _services.GetRequiredService<IConversationService>();
var isConvMode = conv.IsConversationMode();
if (isConvMode)
{
AddUtility(agent, UtilityName.ImageReader, READ_IMAGE_FN);
AddUtility(agent, UtilityName.PdfReader, READ_PDF_FN);
}
base.OnAgentLoaded(agent);
}
private void AddUtility(Agent agent, string utility, string functionName)
{
if (!IsEnableUtility(agent, utility)) return;
var (prompt, fn) = GetPromptAndFunction(functionName);
if (fn != null)
{
if (!string.IsNullOrWhiteSpace(prompt))
{
agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n";
}
if (agent.Functions == null)
{
agent.Functions = new List<FunctionDef> { fn };
}
else
{
agent.Functions.Add(fn);
}
}
}
private bool IsEnableUtility(Agent agent, string utility)
{
return !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(utility);
}
private (string, FunctionDef?) GetPromptAndFunction(string functionName)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant);
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{functionName}.fn"))?.Content ?? string.Empty;
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(functionName));
return (prompt, loadAttachmentFn);
}
}

View file

@ -0,0 +1,10 @@
namespace BotSharp.Plugin.FileHandler.Hooks;
public class FileHandlerUtilityHook : IAgentUtilityHook
{
public void AddUtilities(List<string> utilities)
{
utilities.Add(UtilityName.ImageReader);
utilities.Add(UtilityName.PdfReader);
}
}

View file

@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace BotSharp.Plugin.FileHandler.LlmContexts;
public class LlmContextIn
{
[JsonPropertyName("user_request")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? UserRequest { get; set; }
}

View file

@ -0,0 +1,5 @@
namespace BotSharp.Plugin.FileHandler.Settings;
public class FileHandlerSettings
{
}

View file

@ -0,0 +1,32 @@
global using System;
global using System.Collections.Generic;
global using System.Text;
global using System.Linq;
global using System.Text.Json;
global using System.Net.Mime;
global using System.Threading.Tasks;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Logging;
global using BotSharp.Abstraction.Agents;
global using BotSharp.Abstraction.Conversations;
global using BotSharp.Abstraction.Plugins;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Functions;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Files.Enums;
global using BotSharp.Abstraction.Files.Models;
global using BotSharp.Abstraction.Files;
global using BotSharp.Abstraction.MLTasks;
global using BotSharp.Abstraction.Utilities;
global using BotSharp.Abstraction.Agents.Settings;
global using BotSharp.Abstraction.Functions.Models;
global using BotSharp.Abstraction.Repositories;
global using BotSharp.Abstraction.Settings;
global using BotSharp.Abstraction.Messaging;
global using BotSharp.Abstraction.Messaging.Models.RichContent;
global using BotSharp.Abstraction.Options;
global using BotSharp.Core.Infrastructures;
global using BotSharp.Plugin.FileHandler.Enums;
global using BotSharp.Plugin.FileHandler.Settings;
global using BotSharp.Plugin.FileHandler.LlmContexts;

View file

@ -0,0 +1,14 @@
{
"name": "read_image",
"description": "If the user's request is related to analyzing images, you can call this function to analyze images.",
"parameters": {
"type": "object",
"properties": {
"user_request": {
"type": "string",
"description": "The request posted by user, which is related to analyzing requested images. User can request for multiple images to process at one time."
}
},
"required": [ "user_request" ]
}
}

View file

@ -0,0 +1,14 @@
{
"name": "read_pdf",
"description": "If the user's request is related to analyzing pdf files, you can call this function to analyze pdf.",
"parameters": {
"type": "object",
"properties": {
"user_request": {
"type": "string",
"description": "The request posted by user, which is related to analyzing requested a pdf file or pdf files."
}
},
"required": [ "user_request" ]
}
}

View file

@ -0,0 +1 @@
Please call function read_image if user wants to describe an image or images.

View file

@ -0,0 +1 @@
Please call function read_pdf if user wants to describe an pdf file or pdf files.

View file

@ -1,6 +1,6 @@
namespace BotSharp.Plugin.HttpHandler.Enums;
public class Utility
public class UtilityName
{
public const string HttpHandler = "http-handler";
}

View file

@ -1,4 +1,5 @@
using System.Net.Http;
using System.Net.Mime;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
@ -40,7 +41,6 @@ public class HandleHttpRequestFn : IFunctionCallback
var response = await SendHttpRequest(url, method, content);
var responseContent = await HandleHttpResponse(response);
message.Content = responseContent;
message.StopCompletion = true;
return true;
}
catch (Exception ex)
@ -48,7 +48,6 @@ public class HandleHttpRequestFn : IFunctionCallback
var msg = $"Fail when sending http request. Url: {url}, method: {method}, content: {content}";
_logger.LogWarning($"{msg}\n(Error: {ex.Message})");
message.Content = msg;
message.StopCompletion = true;
return false;
}
}
@ -152,7 +151,7 @@ public class HandleHttpRequestFn : IFunctionCallback
_logger.LogWarning($"Error when build http content: {content}\n(Error: {ex.Message})");
}
return new StringContent(str, Encoding.UTF8, "application/json");
return new StringContent(str, Encoding.UTF8, MediaTypeNames.Application.Json);
}
private string BuildQuery(string url, string? content)

View file

@ -21,11 +21,11 @@ public class HttpHandlerHook : AgentHookBase
{
var conv = _services.GetRequiredService<IConversationService>();
var isConvMode = conv.IsConversationMode();
var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(Utility.HttpHandler);
var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.HttpHandler);
if (isConvMode && isEnabled)
{
var (prompt, fn) = GetPromptAndFunction();
var (prompt, fn) = GetPromptAndFunction(FUNCTION_NAME);
if (fn != null)
{
if (!string.IsNullOrWhiteSpace(prompt))
@ -47,12 +47,12 @@ public class HttpHandlerHook : AgentHookBase
base.OnAgentLoaded(agent);
}
private (string, FunctionDef?) GetPromptAndFunction()
private (string, FunctionDef?) GetPromptAndFunction(string functionName)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant);
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty;
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME));
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{functionName}.fn"))?.Content ?? string.Empty;
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(functionName));
return (prompt, loadAttachmentFn);
}
}

View file

@ -6,6 +6,6 @@ public class HttpHandlerUtilityHook : IAgentUtilityHook
{
public void AddUtilities(List<string> utilities)
{
utilities.Add(Utility.HttpHandler);
utilities.Add(UtilityName.HttpHandler);
}
}

View file

@ -1,16 +1,16 @@
global using System;
global using System.Collections.Generic;
global using System.Text;
global using System.Linq;
global using System.Text.Json;
global using System.Threading.Tasks;
global using BotSharp.Abstraction.Conversations;
global using BotSharp.Abstraction.Plugins;
global using System.Text.Json;
global using BotSharp.Abstraction.Conversations.Models;
global using System.Threading.Tasks;
global using BotSharp.Abstraction.Functions;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.Templating;
global using Microsoft.Extensions.DependencyInjection;
global using System.Linq;
global using BotSharp.Abstraction.Utilities;
global using BotSharp.Abstraction.Messaging;
global using BotSharp.Abstraction.Messaging.Models.RichContent;

View file

@ -19,7 +19,7 @@
<PackageReference Include="Sdcb.PaddleInference.runtime.win64.mkl" Version="2.5.1" />
<PackageReference Include="Sdcb.PaddleOCR" Version="2.7.0.1" />
<PackageReference Include="Sdcb.PaddleOCR.Models.LocalV3" Version="2.7.0.1" />
<PackageReference Include="System.Drawing.Common" Version="8.0.5" />
<PackageReference Include="System.Drawing.Common" Version="8.0.7" />
</ItemGroup>
<ItemGroup>

View file

@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ImageMagick;
using OpenCvSharp;
using Microsoft.AspNetCore.Http;
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR.Models;
using Sdcb.PaddleOCR.Models.LocalV3;
using Sdcb.PaddleOCR;
using System.Threading.Tasks;
using BotSharp.Abstraction.Knowledges;

View file

@ -11,6 +11,8 @@
</PropertyGroup>
<ItemGroup>
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_select.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_executor.fn.liquid" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\get_table_columns.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\lookup_dictionary.json" />
@ -42,6 +44,12 @@
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_select.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_select.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_executor.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View file

@ -0,0 +1,6 @@
namespace BotSharp.Plugin.SqlDriver.Enum;
public class Utility
{
public const string SqlExecutor = "sql-executor";
}

View file

@ -1,12 +1,3 @@
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
using BotSharp.Plugin.SqlDriver.Models;
using BotSharp.Plugin.SqlHero.Settings;
using Dapper;
using MySqlConnector;
using System.Text.Json;
using System.Threading.Tasks;
namespace BotSharp.Plugin.SqlDriver.Functions;
public class ExecuteQueryFn : IFunctionCallback

View file

@ -0,0 +1,84 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Settings;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories;
namespace BotSharp.Plugin.SqlDriver.Hooks;
public class SqlExecutorHook : AgentHookBase, IAgentHook
{
private const string SQL_EXECUTOR_TEMPLATE = "sql_executor.fn";
private IEnumerable<string> _targetSqlExecutorFunctions = new List<string>
{
"sql_select"
};
public override string SelfId => string.Empty;
public SqlExecutorHook(IServiceProvider services, AgentSettings settings) : base(services, settings)
{
}
public override void OnAgentLoaded(Agent agent)
{
var conv = _services.GetRequiredService<IConversationService>();
var isConvMode = conv.IsConversationMode();
var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(Utility.SqlExecutor);
if (isConvMode && isEnabled)
{
var (prompt, fns) = GetPromptAndFunctions();
if (!fns.IsNullOrEmpty())
{
if (!string.IsNullOrWhiteSpace(prompt))
{
agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n";
}
if (agent.Functions == null)
{
agent.Functions = fns;
}
else
{
agent.Functions.AddRange(fns);
}
}
}
base.OnAgentLoaded(agent);
}
private (string, List<FunctionDef>?) GetPromptAndFunctions()
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant);
var fns = agent?.Functions?.Where(x => _targetSqlExecutorFunctions.Contains(x.Name))?.ToList();
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(SQL_EXECUTOR_TEMPLATE))?.Content ?? string.Empty;
var dbType = GetDatabaseType();
var render = _services.GetRequiredService<ITemplateRender>();
prompt = render.Render(prompt, new Dictionary<string, object>
{
{ "db_type", dbType }
});
return (prompt, fns);
}
private string GetDatabaseType()
{
var settings = _services.GetRequiredService<SqlDriverSetting>();
var dbType = "MySQL";
if (!string.IsNullOrWhiteSpace(settings?.SqlServerConnectionString))
{
dbType = "SQL Server";
}
else if (!string.IsNullOrWhiteSpace(settings?.SqlLiteConnectionString))
{
dbType = "SQL Lite";
}
return dbType;
}
}

View file

@ -0,0 +1,9 @@
namespace BotSharp.Plugin.SqlDriver.Hooks;
public class SqlExecutorUtilityHook : IAgentUtilityHook
{
public void AddUtilities(List<string> utilities)
{
utilities.Add(Utility.SqlExecutor);
}
}

View file

@ -25,21 +25,21 @@ public class SqlDriverService
Console.WriteLine($"{sql.Reason}");
Console.WriteLine(sql.Statement, Color.Yellow);
Console.WriteLine(sql.Statement);
foreach (var p in sql.Parameters)
{
Console.WriteLine($"@{p.Name} = '{p.Value}'", Color.Green);
Console.WriteLine($"@{p.Name} = '{p.Value}'");
}
if (sql.Return != null)
{
Console.Write($"Return: ");
if (!string.IsNullOrEmpty(sql.Return.Value))
{
Console.WriteLine($" {sql.Return.Value}", Color.Red);
Console.WriteLine($" {sql.Return.Value}");
}
else
{
Console.WriteLine($"{sql.Return.Name} as @{sql.Return.Alias}", Color.Green);
Console.WriteLine($"{sql.Return.Name} as @{sql.Return.Alias}");
}
}
}

View file

@ -3,4 +3,6 @@ namespace BotSharp.Plugin.SqlHero.Settings;
public class SqlDriverSetting
{
public string MySqlConnectionString { get; set; }
public string SqlServerConnectionString { get; set; }
public string SqlLiteConnectionString { get; set; }
}

View file

@ -17,5 +17,7 @@ public class SqlDriverPlugin : IBotSharpPlugin
services.AddScoped<SqlDriverService>();
services.AddScoped<IKnowledgeHook, SqlDriverKnowledgeHook>();
services.AddScoped<IAgentHook, SqlExecutorHook>();
services.AddScoped<IAgentUtilityHook, SqlExecutorUtilityHook>();
}
}

View file

@ -19,5 +19,6 @@ global using BotSharp.Abstraction.Knowledges.Models;
global using BotSharp.Abstraction.Settings;
global using BotSharp.Plugin.SqlDriver.Hooks;
global using BotSharp.Plugin.SqlDriver.Services;
global using BotSharp.Plugin.SqlDriver.Enum;
global using BotSharp.Plugin.SqlHero.Settings;
global using System.Drawing;

View file

@ -0,0 +1,56 @@
{
"name": "sql_select",
"description": "Get the specific value from table",
"parameters": {
"type": "object",
"properties": {
"sql_statement": {
"type": "string",
"description": "SQL statement with SELECT"
},
"reason": {
"type": "string",
"description": "reason"
},
"table": {
"type": "string",
"description": "related table"
},
"parameters": {
"type": "array",
"description": "data criteria for the query",
"items": {
"type": "object",
"description": "the name and value for the parameter",
"properties": {
"name": {
"type": "string",
"description": "field name"
},
"value": {
"type": "string",
"description": "real value inferred by the context"
}
},
"required": [ "name", "value" ]
}
},
"return_field": {
"type": "object",
"description": "the name and alias for the return field",
"properties": {
"name": {
"type": "string",
"description": "field in the table"
},
"alias": {
"type": "string",
"description": "meaningful field alias"
}
},
"required": [ "name", "value" ]
}
},
"required": [ "sql_statement", "reason", "table", "parameters", "return_field" ]
}
}

View file

@ -0,0 +1,5 @@
You are connecting to {{ db_type }} database. Please generate SQL statements following {{ db_type }} rules.
Please call function sql_select if user wants to get or retrieve data from data tables.
If there are any parameters, please add them in the WHERE clause, each of which starts with "@".
For example, SELECT * FROM table WHERE Id=@Id AND Name=@Name

View file

@ -55,11 +55,13 @@
<ProjectReference Include="..\Plugins\BotSharp.Plugin.Twilio\BotSharp.Plugin.Twilio.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.TelegramBots\BotSharp.Plugin.TelegramBots.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.ChatHub\BotSharp.Plugin.ChatHub.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.HttpHandler\BotSharp.Plugin.HttpHandler.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.LLamaSharp\BotSharp.Plugin.LLamaSharp.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.SqlDriver\BotSharp.Plugin.SqlDriver.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.WebDriver\BotSharp.Plugin.WebDriver.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.AnthropicAI\BotSharp.Plugin.AnthropicAI.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.HttpHandler\BotSharp.Plugin.HttpHandler.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.FileHandler\BotSharp.Plugin.FileHandler.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.EmailHandler\BotSharp.Plugin.EmailHandler.csproj" />
</ItemGroup>
<ItemGroup>

View file

@ -159,6 +159,12 @@
"Origin": ""
},
"SqlDriver": {
"MySqlConnectionString": "",
"SqlServerConnectionString": "",
"SqlLiteConnectionString": ""
},
"Statistics": {
"DataDir": "stats"
},
@ -295,7 +301,9 @@
"BotSharp.Plugin.LLamaSharp",
"BotSharp.Plugin.SparkDesk",
"BotSharp.Plugin.MetaGLM",
"BotSharp.Plugin.HttpHandler"
"BotSharp.Plugin.HttpHandler",
"BotSharp.Plugin.FileHandler",
"BotSharp.Plugin.EmailHandler"
]
}
}