Merge pull request #1167 from iceljc/features/refine-model-settings

Features/refine model settings
This commit is contained in:
iceljc 2025-09-17 15:20:54 -05:00 committed by GitHub
commit 22ce231411
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 832 additions and 820 deletions

View file

@ -145,6 +145,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Test.RealtimeVoice
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.ChartHandler", "src\Plugins\BotSharp.Plugin.ChartHandler\BotSharp.Plugin.ChartHandler.csproj", "{0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.ChartHandler", "src\Plugins\BotSharp.Plugin.ChartHandler\BotSharp.Plugin.ChartHandler.csproj", "{0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.ExcelHandler", "src\Plugins\BotSharp.Plugin.ExcelHandler\BotSharp.Plugin.ExcelHandler.csproj", "{FC63C875-E880-D8BB-B8B5-978AB7B62983}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -609,6 +611,14 @@ Global
{0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|Any CPU.Build.0 = Release|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|Any CPU.Build.0 = Release|Any CPU
{0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x64.ActiveCfg = Release|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x64.ActiveCfg = Release|Any CPU
{0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x64.Build.0 = Release|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x64.Build.0 = Release|Any CPU
{FC63C875-E880-D8BB-B8B5-978AB7B62983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC63C875-E880-D8BB-B8B5-978AB7B62983}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC63C875-E880-D8BB-B8B5-978AB7B62983}.Debug|x64.ActiveCfg = Debug|Any CPU
{FC63C875-E880-D8BB-B8B5-978AB7B62983}.Debug|x64.Build.0 = Debug|Any CPU
{FC63C875-E880-D8BB-B8B5-978AB7B62983}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC63C875-E880-D8BB-B8B5-978AB7B62983}.Release|Any CPU.Build.0 = Release|Any CPU
{FC63C875-E880-D8BB-B8B5-978AB7B62983}.Release|x64.ActiveCfg = Release|Any CPU
{FC63C875-E880-D8BB-B8B5-978AB7B62983}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -679,6 +689,7 @@ Global
{7C0C7D13-D161-4AB0-9C29-83A0F1FF990E} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC} {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
{B067B126-88CD-4282-BEEF-7369B64423EF} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC} {B067B126-88CD-4282-BEEF-7369B64423EF} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
{0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702} = {51AFE054-AE99-497D-A593-69BAEFB5106F} {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
{FC63C875-E880-D8BB-B8B5-978AB7B62983} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19} SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}

View file

@ -62,6 +62,11 @@ public class LlmModelSetting
/// </summary> /// </summary>
public ImageSetting? Image { get; set; } public ImageSetting? Image { get; set; }
/// <summary>
/// Settings for audio
/// </summary>
public AudioSetting? Audio { get; set; }
/// <summary> /// <summary>
/// Settings for llm cost /// Settings for llm cost
/// </summary> /// </summary>
@ -128,6 +133,20 @@ public class ImageVariationSetting
} }
#endregion #endregion
#region Audio model settings
public class AudioSetting
{
public AudioTranscriptionSetting? Transcription { get; set; }
}
public class AudioTranscriptionSetting
{
public float? Temperature { get; set; }
public ModelSettingBase? ResponseFormat { get; set; }
public ModelSettingBase? Granularity { get; set; }
}
#endregion
public class ModelSettingBase public class ModelSettingBase
{ {
public string? Default { get; set; } public string? Default { get; set; }

View file

@ -1,135 +0,0 @@
using BotSharp.Abstraction.Routing;
using BotSharp.Core.Infrastructures;
using Microsoft.AspNetCore.StaticFiles;
namespace BotSharp.Plugin.AudioHandler.Functions;
public class HandleAudioRequestFn : IFunctionCallback
{
public string Name => "util-audio-handle_audio_request";
public string Indication => "Handling audio request";
private readonly IServiceProvider _serviceProvider;
private readonly IFileStorageService _fileStorage;
private readonly ILogger<HandleAudioRequestFn> _logger;
private readonly BotSharpOptions _options;
private readonly IEnumerable<string> _audioContentTypes = new List<string>
{
AudioType.mp3.ToFileType(),
AudioType.wav.ToFileType(),
};
public HandleAudioRequestFn(
IFileStorageService fileStorage,
IServiceProvider serviceProvider,
ILogger<HandleAudioRequestFn> logger,
BotSharpOptions options)
{
_fileStorage = fileStorage;
_serviceProvider = serviceProvider;
_logger = logger;
_options = options;
}
public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<LlmContextIn>(message.FunctionArgs, _options.JsonSerializerOptions);
var conv = _serviceProvider.GetRequiredService<IConversationService>();
var routingCtx = _serviceProvider.GetRequiredService<IRoutingContext>();
var wholeDialogs = routingCtx.GetDialogs();
if (wholeDialogs.IsNullOrEmpty())
{
wholeDialogs = conv.GetDialogHistory();
}
var dialogs = AssembleFiles(conv.ConversationId, wholeDialogs);
var response = await GetResponeFromDialogs(dialogs);
message.Content = response;
dialogs.ForEach(x => x.Files = null);
return true;
}
private List<RoleDialogModel> AssembleFiles(string convId, List<RoleDialogModel> dialogs)
{
if (dialogs.IsNullOrEmpty())
{
return new List<RoleDialogModel>();
}
var messageId = dialogs.Select(x => x.MessageId).Distinct().ToList();
var audioFiles = _fileStorage.GetMessageFiles(convId, messageId, options: new()
{
Sources = [FileSource.User],
ContentTypes = _audioContentTypes
});
audioFiles = audioFiles.Where(x => x.ContentType.Contains("audio")).ToList();
foreach (var dialog in dialogs)
{
var found = audioFiles.Where(x => x.MessageId == dialog.MessageId
&& x.FileSource.IsEqualTo(FileSource.User)).ToList();
if (found.IsNullOrEmpty() || !dialog.IsFromUser)
{
continue;
}
dialog.Files = found.Select(x => new BotSharpFile
{
ContentType = x.ContentType,
FileUrl = x.FileUrl,
FileStorageUrl = x.FileStorageUrl
}).ToList();
}
return dialogs;
}
private async Task<string> GetResponeFromDialogs(List<RoleDialogModel> dialogs)
{
var audioCompletion = PrepareModel();
var dialog = dialogs.Where(x => !x.Files.IsNullOrEmpty()).Last();
var transcripts = new List<string>();
foreach (var file in dialog.Files)
{
if (file == null || string.IsNullOrWhiteSpace(file.FileStorageUrl)) continue;
var extension = Path.GetExtension(file.FileStorageUrl);
var fileName = Path.GetFileName(file.FileStorageUrl);
if (!ParseAudioFileType(fileName)) continue;
var binary = _fileStorage.GetFileBytes(file.FileStorageUrl);
using var stream = binary.ToStream();
stream.Position = 0;
var result = await audioCompletion.TranscriptTextAsync(stream, fileName);
transcripts.Add(result);
stream.Close();
}
if (transcripts.IsNullOrEmpty())
{
throw new FileNotFoundException($"No audio files found in the dialog. MessageId: {dialog.MessageId}");
}
return string.Join("\r\n\r\n", transcripts);
}
private IAudioTranscription PrepareModel()
{
return CompletionProvider.GetAudioTranscriber(_serviceProvider);
}
private bool ParseAudioFileType(string fileName)
{
var extension = Path.GetExtension(fileName).TrimStart('.').ToLower();
var provider = new FileExtensionContentTypeProvider();
bool canParse = Enum.TryParse<AudioType>(extension, out _) || provider.TryGetContentType(fileName, out _);
return canParse;
}
}

View file

@ -0,0 +1,171 @@
namespace BotSharp.Plugin.AudioHandler.Functions;
public class ReadAudioFn : IFunctionCallback
{
public string Name => "util-audio-handle_audio_request";
public string Indication => "Reading audio";
private readonly IServiceProvider _services;
private readonly IFileStorageService _fileStorage;
private readonly ILogger<ReadAudioFn> _logger;
private readonly BotSharpOptions _options;
private readonly AudioHandlerSettings _settings;
private readonly IEnumerable<string> _audioContentTypes = new List<string>
{
AudioType.mp3.ToFileType(),
AudioType.wav.ToFileType(),
};
public ReadAudioFn(
IServiceProvider services,
ILogger<ReadAudioFn> logger,
BotSharpOptions options,
AudioHandlerSettings settings,
IFileStorageService fileStorage)
{
_services = services;
_logger = logger;
_options = options;
_settings = settings;
_fileStorage = fileStorage;
}
public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<LlmContextIn>(message.FunctionArgs, _options.JsonSerializerOptions);
var conv = _services.GetRequiredService<IConversationService>();
var routingCtx = _services.GetRequiredService<IRoutingContext>();
var wholeDialogs = routingCtx.GetDialogs();
if (wholeDialogs.IsNullOrEmpty())
{
wholeDialogs = conv.GetDialogHistory();
}
var dialogs = AssembleFiles(conv.ConversationId, wholeDialogs);
var response = await GetAudioTranscription(dialogs);
message.Content = response;
dialogs.ForEach(x => x.Files = null);
return true;
}
private List<RoleDialogModel> AssembleFiles(string convId, List<RoleDialogModel> dialogs)
{
if (dialogs.IsNullOrEmpty())
{
return new List<RoleDialogModel>();
}
var messageId = dialogs.Select(x => x.MessageId).Distinct().ToList();
var audioFiles = _fileStorage.GetMessageFiles(convId, messageId, options: new()
{
Sources = [FileSource.User],
ContentTypes = _audioContentTypes
});
foreach (var dialog in dialogs)
{
var found = audioFiles.Where(x => x.MessageId == dialog.MessageId
&& x.FileSource.IsEqualTo(FileSource.User)).ToList();
if (found.IsNullOrEmpty() || !dialog.IsFromUser)
{
continue;
}
dialog.Files = found.Select(x => new BotSharpFile
{
ContentType = x.ContentType,
FileUrl = x.FileUrl,
FileStorageUrl = x.FileStorageUrl
}).ToList();
}
return dialogs;
}
private async Task<string> GetAudioTranscription(List<RoleDialogModel> dialogs)
{
var audioCompletion = PrepareModel();
var dialog = dialogs.Where(x => !x.Files.IsNullOrEmpty()).LastOrDefault();
var transcripts = new List<string>();
if (dialog != null)
{
foreach (var file in dialog.Files)
{
if (string.IsNullOrWhiteSpace(file?.FileStorageUrl))
{
continue;
}
var extension = Path.GetExtension(file.FileStorageUrl);
var fileName = Path.GetFileName(file.FileStorageUrl);
if (!VerifyAudioFileType(fileName))
{
continue;
}
var binary = _fileStorage.GetFileBytes(file.FileStorageUrl);
using var stream = binary.ToStream();
stream.Position = 0;
var result = await audioCompletion.TranscriptTextAsync(stream, fileName);
transcripts.Add(result);
stream.Close();
await Task.Delay(100);
}
}
if (transcripts.IsNullOrEmpty())
{
var msg = "No audio is found in the chat.";
_logger.LogWarning(msg);
transcripts.Add(msg);
}
return string.Join("\r\n\r\n", transcripts);
}
private IAudioTranscription PrepareModel()
{
var (provider, model) = GetLlmProviderModel();
return CompletionProvider.GetAudioTranscriber(_services, provider: provider, model: model);
}
private bool VerifyAudioFileType(string fileName)
{
var extension = Path.GetExtension(fileName).TrimStart('.').ToLower();
return Enum.TryParse<AudioType>(extension, out _)
|| !string.IsNullOrEmpty(FileUtility.GetFileContentType(fileName));
}
private (string, string) GetLlmProviderModel()
{
var state = _services.GetRequiredService<IConversationStateService>();
var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
var provider = state.GetState("audio_read_llm_provider");
var model = state.GetState("audio_read_llm_provider");
if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(model))
{
return (provider, model);
}
provider = _settings?.Audio?.Reading?.LlmProvider;
model = _settings?.Audio?.Reading?.LlmModel;
if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(model))
{
return (provider, model);
}
provider = "openai";
model = "gpt-4o-mini-transcribe";
return (provider, model);
}
}

View file

@ -1,5 +1,19 @@
using BotSharp.Abstraction.Models;
namespace BotSharp.Plugin.AudioHandler.Settings; namespace BotSharp.Plugin.AudioHandler.Settings;
public class AudioHandlerSettings public class AudioHandlerSettings
{ {
public AudioSettings? Audio { get; set; }
} }
#region Audio
public class AudioSettings
{
public AudioReadSettings? Reading { get; set; }
}
public class AudioReadSettings : LlmBase
{
}
#endregion

View file

@ -14,11 +14,14 @@ global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Files; global using BotSharp.Abstraction.Files;
global using BotSharp.Abstraction.Files.Models; global using BotSharp.Abstraction.Files.Models;
global using BotSharp.Abstraction.Files.Enums; global using BotSharp.Abstraction.Files.Enums;
global using BotSharp.Abstraction.Files.Utilities;
global using BotSharp.Abstraction.Functions; global using BotSharp.Abstraction.Functions;
global using BotSharp.Abstraction.MLTasks; global using BotSharp.Abstraction.MLTasks;
global using BotSharp.Abstraction.Options; global using BotSharp.Abstraction.Options;
global using BotSharp.Abstraction.Plugins; global using BotSharp.Abstraction.Plugins;
global using BotSharp.Abstraction.Utilities; global using BotSharp.Abstraction.Utilities;
global using BotSharp.Abstraction.Routing;
global using BotSharp.Core.Infrastructures;
global using BotSharp.Plugin.AudioHandler.Enums; global using BotSharp.Plugin.AudioHandler.Enums;
global using BotSharp.Plugin.AudioHandler.Helpers; global using BotSharp.Plugin.AudioHandler.Helpers;
@ -26,6 +29,7 @@ global using BotSharp.Plugin.AudioHandler.Hooks;
global using BotSharp.Plugin.AudioHandler.Models; global using BotSharp.Plugin.AudioHandler.Models;
global using BotSharp.Plugin.AudioHandler.LlmContexts; global using BotSharp.Plugin.AudioHandler.LlmContexts;
global using BotSharp.Plugin.AudioHandler.Provider; global using BotSharp.Plugin.AudioHandler.Provider;
global using BotSharp.Plugin.AudioHandler.Settings;
global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection;

View file

@ -1,7 +1,5 @@
using BotSharp.Abstraction.Plugins; using BotSharp.Abstraction.Plugins;
using BotSharp.Abstraction.Settings; using BotSharp.Abstraction.Settings;
using BotSharp.Plugin.ExcelHandler.Helpers.MySql;
using BotSharp.Plugin.ExcelHandler.Helpers.Sqlite;
using BotSharp.Plugin.ExcelHandler.Hooks; using BotSharp.Plugin.ExcelHandler.Hooks;
using BotSharp.Plugin.ExcelHandler.Services; using BotSharp.Plugin.ExcelHandler.Services;
using BotSharp.Plugin.ExcelHandler.Settings; using BotSharp.Plugin.ExcelHandler.Settings;
@ -25,9 +23,7 @@ public class ExcelHandlerPlugin : IBotSharpPlugin
}); });
services.AddScoped<IAgentUtilityHook, ExcelHandlerUtilityHook>(); services.AddScoped<IAgentUtilityHook, ExcelHandlerUtilityHook>();
services.AddScoped<ISqliteDbHelpers, SqliteDbHelpers>(); services.AddScoped<IDbService, SqliteService>();
services.AddScoped<IMySqlDbHelper, MySqlDbHelpers>(); services.AddScoped<IDbService, MySqlService>();
services.AddScoped<ISqliteService, SqliteService>();
services.AddScoped<IMySqlService, MySqlService>();
} }
} }

View file

@ -1,62 +1,48 @@
using BotSharp.Abstraction.Files.Enums; using BotSharp.Plugin.ExcelHandler.Settings;
using BotSharp.Abstraction.Files.Models;
using BotSharp.Abstraction.Files.Utilities;
using BotSharp.Abstraction.Routing;
using BotSharp.Plugin.ExcelHandler.Models;
using BotSharp.Plugin.ExcelHandler.Services;
using NPOI.SS.UserModel; using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel; using NPOI.XSSF.UserModel;
using System.Linq.Dynamic.Core; using System.Linq.Dynamic.Core;
namespace BotSharp.Plugin.ExcelHandler.Functions; namespace BotSharp.Plugin.ExcelHandler.Functions;
public class HandleExcelRequestFn : IFunctionCallback public class ReadExcelFn : IFunctionCallback
{ {
public string Name => "util-excel-handle_excel_request"; public string Name => "util-excel-handle_excel_request";
public string Indication => "Handling excel request"; public string Indication => "Reading excel";
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _services;
private readonly IFileStorageService _fileStorage; private readonly IFileStorageService _fileStorage;
private readonly ILogger<HandleExcelRequestFn> _logger; private readonly ILogger<ReadExcelFn> _logger;
private readonly BotSharpOptions _options; private readonly BotSharpOptions _options;
private readonly IMySqlService _mySqlService; private readonly IDbService _dbService;
private readonly ExcelHandlerSettings _settings;
private HashSet<string> _excelFileTypes;
private HashSet<string> _excelMimeTypes; public ReadExcelFn(
private double _excelRowSize = 0; IServiceProvider services,
private double _excelColumnSize = 0; ILogger<ReadExcelFn> logger,
private string _tableName = "tempTable";
private string _currentFileName = string.Empty;
private List<string> _headerColumns = new List<string>();
private List<string> _columnTypes = new List<string>();
public HandleExcelRequestFn(
IServiceProvider serviceProvider,
IFileStorageService fileStorage,
ILogger<HandleExcelRequestFn> logger,
BotSharpOptions options, BotSharpOptions options,
IMySqlService mySqlService ExcelHandlerSettings settings,
) IFileStorageService fileStorage,
IEnumerable<IDbService> dbServices)
{ {
_serviceProvider = serviceProvider; _services = services;
_fileStorage = fileStorage;
_logger = logger; _logger = logger;
_options = options; _options = options;
_mySqlService = mySqlService; _settings = settings;
_fileStorage = fileStorage;
_dbService = dbServices.FirstOrDefault(x => x.Provider == _settings.DbProvider);
} }
public async Task<bool> Execute(RoleDialogModel message) public async Task<bool> Execute(RoleDialogModel message)
{ {
var args = JsonSerializer.Deserialize<LlmContextIn>(message.FunctionArgs, _options.JsonSerializerOptions); var args = JsonSerializer.Deserialize<LlmContextIn>(message.FunctionArgs, _options.JsonSerializerOptions);
var conv = _serviceProvider.GetRequiredService<IConversationService>(); var conv = _services.GetRequiredService<IConversationService>();
var states = _serviceProvider.GetRequiredService<IConversationStateService>(); var states = _services.GetRequiredService<IConversationStateService>();
var routingCtx = _serviceProvider.GetRequiredService<IRoutingContext>(); var routingCtx = _services.GetRequiredService<IRoutingContext>();
if (_excelMimeTypes.IsNullOrEmpty()) Init();
{
_excelMimeTypes = FileUtility.GetMimeFileTypes(new List<string> { "excel", "spreadsheet" }).ToHashSet<string>();
}
var dialogs = routingCtx.GetDialogs(); var dialogs = routingCtx.GetDialogs();
if (dialogs.IsNullOrEmpty()) if (dialogs.IsNullOrEmpty())
@ -71,8 +57,8 @@ public class HandleExcelRequestFn : IFunctionCallback
return true; return true;
} }
var resultList = GetResponeFromDialogs(dialogs); var results = GetResponeFromDialogs(dialogs);
message.Content = GenerateSqlExecutionSummary(resultList); message.Content = GenerateSqlExecutionSummary(results);
states.SetState("excel_import_result",message.Content); states.SetState("excel_import_result",message.Content);
dialogs.ForEach(x => x.Files = null); dialogs.ForEach(x => x.Files = null);
return true; return true;
@ -80,6 +66,14 @@ public class HandleExcelRequestFn : IFunctionCallback
#region Private Methods #region Private Methods
private void Init()
{
if (_excelFileTypes.IsNullOrEmpty())
{
_excelFileTypes = FileUtility.GetMimeFileTypes(["excel", "spreadsheet"]).ToHashSet();
}
}
private bool AssembleFiles(string conversationId, List<RoleDialogModel> dialogs) private bool AssembleFiles(string conversationId, List<RoleDialogModel> dialogs)
{ {
if (dialogs.IsNullOrEmpty()) if (dialogs.IsNullOrEmpty())
@ -88,7 +82,7 @@ public class HandleExcelRequestFn : IFunctionCallback
} }
var messageIds = dialogs.Select(x => x.MessageId).Distinct().ToList(); var messageIds = dialogs.Select(x => x.MessageId).Distinct().ToList();
var contentTypes = FileUtility.GetContentFileTypes(mimeTypes: _excelMimeTypes); var contentTypes = FileUtility.GetContentFileTypes(mimeTypes: _excelFileTypes);
var excelFiles = _fileStorage.GetMessageFiles(conversationId, messageIds, options: new() var excelFiles = _fileStorage.GetMessageFiles(conversationId, messageIds, options: new()
{ {
Sources = [FileSource.User], Sources = [FileSource.User],
@ -123,59 +117,61 @@ public class HandleExcelRequestFn : IFunctionCallback
private List<SqlContextOut> GetResponeFromDialogs(List<RoleDialogModel> dialogs) private List<SqlContextOut> GetResponeFromDialogs(List<RoleDialogModel> dialogs)
{ {
var sqlCommands = new List<SqlContextOut>();
var dialog = dialogs.Last(x => !x.Files.IsNullOrEmpty()); var dialog = dialogs.Last(x => !x.Files.IsNullOrEmpty());
var sqlCommandList = new List<SqlContextOut>();
foreach (var file in dialog.Files) foreach (var file in dialog.Files)
{ {
if (file == null || string.IsNullOrWhiteSpace(file.FileStorageUrl)) continue; if (string.IsNullOrWhiteSpace(file?.FileStorageUrl))
{
continue;
}
string extension = Path.GetExtension(file.FileStorageUrl); string extension = Path.GetExtension(file.FileStorageUrl);
if (!_excelMimeTypes.Contains(extension)) continue; if (!_excelFileTypes.Contains(extension))
{
_currentFileName = Path.GetFileName(file.FileStorageUrl); continue;
}
var binary = _fileStorage.GetFileBytes(file.FileStorageUrl); var binary = _fileStorage.GetFileBytes(file.FileStorageUrl);
var workbook = ConvertToWorkBook(binary.ToArray()); var workbook = ConvertToWorkBook(binary);
var currentCommandList = _mySqlService.WriteExcelDataToDB(workbook); var currentCommands = _dbService.WriteExcelDataToDB(workbook);
sqlCommandList.AddRange(currentCommandList); sqlCommands.AddRange(currentCommands);
} }
return sqlCommandList; return sqlCommands;
} }
private string GenerateSqlExecutionSummary(List<SqlContextOut> messageList) private string GenerateSqlExecutionSummary(List<SqlContextOut> results)
{ {
var stringBuilder = new StringBuilder(); var stringBuilder = new StringBuilder();
if (messageList.Any(x => x.isSuccessful)) if (results.Any(x => x.isSuccessful))
{ {
stringBuilder.Append("---Success---"); stringBuilder.Append("---Success---");
stringBuilder.Append("\r\n"); stringBuilder.Append("\r\n");
foreach (var message in messageList.Where(x => x.isSuccessful)) foreach (var result in results.Where(x => x.isSuccessful))
{ {
stringBuilder.Append(message.Message); stringBuilder.Append(result.Message);
stringBuilder.Append("\r\n\r\n"); stringBuilder.Append("\r\n\r\n");
} }
} }
if (messageList.Any(x => !x.isSuccessful)) if (results.Any(x => !x.isSuccessful))
{ {
stringBuilder.Append("---Failed---"); stringBuilder.Append("---Failed---");
stringBuilder.Append("\r\n"); stringBuilder.Append("\r\n");
foreach (var message in messageList.Where(x => !x.isSuccessful)) foreach (var result in results.Where(x => !x.isSuccessful))
{ {
stringBuilder.Append(message.Message); stringBuilder.Append(result.Message);
stringBuilder.Append("\r\n"); stringBuilder.Append("\r\n");
} }
} }
return stringBuilder.ToString(); return stringBuilder.ToString();
} }
private IWorkbook ConvertToWorkBook(byte[] bytes) private IWorkbook ConvertToWorkBook(BinaryData binary)
{ {
IWorkbook workbook; using var fileStream = new MemoryStream(binary.ToArray());
using (var fileStream = new MemoryStream(bytes)) IWorkbook workbook = new XSSFWorkbook(fileStream);
{
workbook = new XSSFWorkbook(fileStream);
}
return workbook; return workbook;
} }
#endregion #endregion

View file

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Data.Sqlite;
using MySql.Data.MySqlClient;
namespace BotSharp.Plugin.ExcelHandler.Helpers.MySql
{
public interface IMySqlDbHelper
{
MySqlConnection GetDbConnection();
}
}

View file

@ -1,44 +0,0 @@
using System.Text.RegularExpressions;
using BotSharp.Plugin.SqlDriver.Settings;
using MySql.Data.MySqlClient;
namespace BotSharp.Plugin.ExcelHandler.Helpers.MySql
{
public class MySqlDbHelpers : IMySqlDbHelper
{
private string _mySqlDriverConnection = "";
private readonly IServiceProvider _services;
private string _databaseName;
public MySqlDbHelpers(IServiceProvider service)
{
_services = service;
}
public MySqlConnection GetDbConnection()
{
if (string.IsNullOrEmpty(_mySqlDriverConnection))
{
InitializeDatabase();
}
var dbConnection = new MySqlConnection(_mySqlDriverConnection);
dbConnection.Open();
return dbConnection;
}
private void InitializeDatabase()
{
var settingService = _services.GetRequiredService<SqlDriverSetting>();
_mySqlDriverConnection = settingService.MySqlTempConnectionString;
_databaseName = GetDatabaseName(settingService.MySqlTempConnectionString);
}
private string GetDatabaseName(string connectionString)
{
string pattern = @"database=([^;]+)";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
Match match = regex.Match(connectionString);
return match.Success ? match.Groups[1].Value : string.Empty;
}
}
}

View file

@ -1,9 +0,0 @@
using Microsoft.Data.Sqlite;
namespace BotSharp.Plugin.ExcelHandler.Helpers.Sqlite;
public interface ISqliteDbHelpers
{
SqliteConnection GetPhysicalDbConnection();
SqliteConnection GetInMemoryDbConnection();
}

View file

@ -1,41 +0,0 @@
using BotSharp.Plugin.SqlDriver.Settings;
using Microsoft.Data.Sqlite;
namespace BotSharp.Plugin.ExcelHandler.Helpers.Sqlite;
public class SqliteDbHelpers : ISqliteDbHelpers
{
private string _dbFilePath = string.Empty;
private SqliteConnection inMemoryDbConnection = null;
private readonly IServiceProvider _services;
public SqliteDbHelpers(IServiceProvider service)
{
_services = service;
}
public SqliteConnection GetInMemoryDbConnection()
{
if (inMemoryDbConnection == null)
{
inMemoryDbConnection = new SqliteConnection("Data Source=:memory:;Mode=ReadWrite");
inMemoryDbConnection.Open();
return inMemoryDbConnection;
}
return inMemoryDbConnection;
}
public SqliteConnection GetPhysicalDbConnection()
{
if (string.IsNullOrEmpty(_dbFilePath))
{
var settingService = _services.GetRequiredService<SqlDriverSetting>();
_dbFilePath = settingService.SqlLiteConnectionString;
}
var dbConnection = new SqliteConnection($"Data Source={_dbFilePath};Mode=ReadWrite");
dbConnection.Open();
return dbConnection;
}
}

View file

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BotSharp.Plugin.ExcelHandler.Models;
using NPOI.SS.UserModel; using NPOI.SS.UserModel;
namespace BotSharp.Plugin.ExcelHandler.Services namespace BotSharp.Plugin.ExcelHandler.Services;
{
public interface IDbService public interface IDbService
{ {
string Provider { get; }
IEnumerable<SqlContextOut> WriteExcelDataToDB(IWorkbook workbook); IEnumerable<SqlContextOut> WriteExcelDataToDB(IWorkbook workbook);
} }
}

View file

@ -1,6 +0,0 @@
namespace BotSharp.Plugin.ExcelHandler.Services;
public interface IMySqlService : IDbService
{
public bool DeleteTableSqlQuery();
}

View file

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BotSharp.Plugin.ExcelHandler.Models;
using NPOI.SS.UserModel;
namespace BotSharp.Plugin.ExcelHandler.Services
{
public interface ISqliteService : IDbService
{
public void DeleteTableSqlQuery();
public string GenerateTableSchema();
}
}

View file

@ -1,18 +1,18 @@
using System.Data; using System.Data;
using BotSharp.Abstraction.Routing; using System.Text.RegularExpressions;
using BotSharp.Plugin.ExcelHandler.Helpers.MySql; using BotSharp.Plugin.SqlDriver.Settings;
using BotSharp.Plugin.ExcelHandler.Models;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using Newtonsoft.Json; using Newtonsoft.Json;
using NPOI.SS.UserModel; using NPOI.SS.UserModel;
namespace BotSharp.Plugin.ExcelHandler.Services namespace BotSharp.Plugin.ExcelHandler.Services;
public class MySqlService : IDbService
{ {
public class MySqlService : IMySqlService
{
private readonly IMySqlDbHelper _mySqlDbHelpers;
private readonly IServiceProvider _services; private readonly IServiceProvider _services;
private readonly ILogger<MySqlService> _logger;
private string _mysqlConnection = "";
private double _excelRowSize = 0; private double _excelRowSize = 0;
private double _excelColumnSize = 0; private double _excelColumnSize = 0;
private string _tableName = "tempTable"; private string _tableName = "tempTable";
@ -21,86 +21,36 @@ namespace BotSharp.Plugin.ExcelHandler.Services
private List<string> _headerColumns = new List<string>(); private List<string> _headerColumns = new List<string>();
private List<string> _columnTypes = new List<string>(); private List<string> _columnTypes = new List<string>();
public MySqlService(IMySqlDbHelper mySqlDbHelpers, IServiceProvider services) public MySqlService(
IServiceProvider services,
ILogger<MySqlService> logger)
{ {
_mySqlDbHelpers = mySqlDbHelpers;
_services = services; _services = services;
_logger = logger;
} }
public bool DeleteTableSqlQuery() public string Provider => "mysql";
{
try
{
return true;
}
catch (Exception ex)
{
return false;
}
}
/*private void ExecuteDropTableQuery(List<string> dropTableNames, MySqlConnection connection)
{
dropTableNames.ForEach(x =>
{
var dropTableQuery = $"DROP TABLE IF EXISTS {x}";
using var selectCmd = new MySqlCommand(dropTableQuery, connection);
selectCmd.ExecuteNonQuery();
});
}*/
public List<string> GetAllTableSchema(MySqlConnection mySqlDbConnection)
{
string schemaQuery = $@"
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_KEY, EXTRA
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = '{mySqlDbConnection.Database}';";
var tables = new List<string>();
using MySqlCommand cmd = new MySqlCommand(schemaQuery, mySqlDbConnection);
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
string tableName = reader.GetString("TABLE_NAME");
//string columnName = reader.GetString("COLUMN_NAME");
//string dataType = reader.GetString("DATA_TYPE");
//string isNullable = reader.GetString("IS_NULLABLE");
//string columnKey = reader.GetString("COLUMN_KEY");
//string extra = reader.GetString("EXTRA");
tables.Add(tableName);
}
}
return tables.Distinct().ToList();
}
}
public IEnumerable<SqlContextOut> WriteExcelDataToDB(IWorkbook workbook) public IEnumerable<SqlContextOut> WriteExcelDataToDB(IWorkbook workbook)
{ {
var numTables = workbook.NumberOfSheets; var numTables = workbook.NumberOfSheets;
var commandList = new List<SqlContextOut>(); var results = new List<SqlContextOut>();
var state = _services.GetRequiredService<IConversationStateService>(); var state = _services.GetRequiredService<IConversationStateService>();
for (int sheetIdx = 0; sheetIdx < numTables; sheetIdx++) for (int sheetIdx = 0; sheetIdx < numTables; sheetIdx++)
{ {
var commandResult = new SqlContextOut();
ISheet sheet = workbook.GetSheetAt(sheetIdx); ISheet sheet = workbook.GetSheetAt(sheetIdx);
var (isCreateSuccess, message) = SqlCreateTableFn(sheet); var (isCreateSuccess, message) = SqlCreateTableFn(sheet);
if (!isCreateSuccess) if (!isCreateSuccess)
{ {
commandResult = new SqlContextOut results.Add(new SqlContextOut
{ {
isSuccessful = isCreateSuccess, isSuccessful = isCreateSuccess,
Message = message, Message = message,
FileName = _currentFileName FileName = _currentFileName
}; });
commandList.Add(commandResult);
continue; continue;
} }
@ -110,16 +60,17 @@ namespace BotSharp.Plugin.ExcelHandler.Services
var (isInsertSuccess, insertMessage) = SqlInsertDataFn(sheet); var (isInsertSuccess, insertMessage) = SqlInsertDataFn(sheet);
string exampleData = GetInsertExample(table); string exampleData = GetInsertExample(table);
commandResult = new SqlContextOut results.Add(new SqlContextOut
{ {
isSuccessful = isInsertSuccess, isSuccessful = isInsertSuccess,
Message = $"{insertMessage}\r\nExample Data: {exampleData}. \r\n The remaining data contains different values. ", Message = $"{insertMessage}\r\nExample Data: {exampleData}. \r\n The remaining data contains different values. ",
FileName = _currentFileName FileName = _currentFileName
}; });
commandList.Add(commandResult);
} }
return commandList; return results;
} }
#region Private methods
private string ProcessInsertSqlQuery(string dataSql) private string ProcessInsertSqlQuery(string dataSql)
{ {
var wrapUpCols = _headerColumns.Select(x => $"`{x}`").ToList(); var wrapUpCols = _headerColumns.Select(x => $"`{x}`").ToList();
@ -127,6 +78,7 @@ namespace BotSharp.Plugin.ExcelHandler.Services
string insertSqlQuery = $"Insert into {_tableName} {transferedCols} Values {dataSql}"; string insertSqlQuery = $"Insert into {_tableName} {transferedCols} Values {dataSql}";
return insertSqlQuery; return insertSqlQuery;
} }
private (bool, string) SqlInsertDataFn(ISheet sheet) private (bool, string) SqlInsertDataFn(ISheet sheet)
{ {
try try
@ -142,6 +94,7 @@ namespace BotSharp.Plugin.ExcelHandler.Services
return (false, $"{_currentFileName}: Failed to parse excel data into `{_database}`.`{_tableName}` table. ####Error: {ex.Message}"); return (false, $"{_currentFileName}: Failed to parse excel data into `{_database}`.`{_tableName}` table. ####Error: {ex.Message}");
} }
} }
private string ParseSheetData(ISheet singleSheet) private string ParseSheetData(ISheet singleSheet)
{ {
var stringBuilder = new StringBuilder(); var stringBuilder = new StringBuilder();
@ -190,8 +143,8 @@ namespace BotSharp.Plugin.ExcelHandler.Services
{ {
try try
{ {
var routing = _services.GetRequiredService<IRoutingContext>(); var conv = _services.GetRequiredService<IConversationService>();
_tableName = $"excel_{routing.ConversationId.Split('-').Last()}_{sheet.SheetName}"; _tableName = $"excel_{conv.ConversationId.Split('-').Last()}_{sheet.SheetName}";
_headerColumns = ParseSheetColumn(sheet); _headerColumns = ParseSheetColumn(sheet);
string createTableSql = CreateDBTableSqlString(_tableName, _headerColumns, null ,true); string createTableSql = CreateDBTableSqlString(_tableName, _headerColumns, null ,true);
ExecuteSqlQueryForInsertion(createTableSql); ExecuteSqlQueryForInsertion(createTableSql);
@ -215,6 +168,7 @@ namespace BotSharp.Plugin.ExcelHandler.Services
_excelColumnSize = headerColumn.Count; _excelColumnSize = headerColumn.Count;
return headerColumn; return headerColumn;
} }
private string CreateDBTableSqlString(string tableName, List<string> headerColumns, List<string>? columnTypes = null, bool isMemory = false) private string CreateDBTableSqlString(string tableName, List<string> headerColumns, List<string>? columnTypes = null, bool isMemory = false)
{ {
_columnTypes = columnTypes.IsNullOrEmpty() ? headerColumns.Select(x => "VARCHAR(128)").ToList() : columnTypes; _columnTypes = columnTypes.IsNullOrEmpty() ? headerColumns.Select(x => "VARCHAR(128)").ToList() : columnTypes;
@ -233,18 +187,19 @@ namespace BotSharp.Plugin.ExcelHandler.Services
return createTableSql; return createTableSql;
} }
public void ExecuteSqlQueryForInsertion(string sqlQuery) private void ExecuteSqlQueryForInsertion(string sqlQuery)
{ {
using var connection = _mySqlDbHelpers.GetDbConnection(); using var connection = GetDbConnection();
_database = connection.Database; _database = connection.Database;
using (MySqlCommand cmd = new MySqlCommand(sqlQuery, connection)) using (MySqlCommand cmd = new MySqlCommand(sqlQuery, connection))
{ {
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
} }
private string GetInsertExample(string tableName) private string GetInsertExample(string tableName)
{ {
using var connection = _mySqlDbHelpers.GetDbConnection(); using var connection = GetDbConnection();
_database = connection.Database; _database = connection.Database;
var sqlQuery = $"SELECT * FROM {tableName} LIMIT 2;"; var sqlQuery = $"SELECT * FROM {tableName} LIMIT 2;";
using var cmd = new MySqlCommand(sqlQuery, connection); using var cmd = new MySqlCommand(sqlQuery, connection);
@ -255,5 +210,58 @@ namespace BotSharp.Plugin.ExcelHandler.Services
return JsonConvert.SerializeObject(dataExample); return JsonConvert.SerializeObject(dataExample);
} }
private List<string> GetAllTableSchema(MySqlConnection mySqlDbConnection)
{
string schemaQuery = $@"
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_KEY, EXTRA
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = '{mySqlDbConnection.Database}';";
var tables = new List<string>();
using MySqlCommand cmd = new MySqlCommand(schemaQuery, mySqlDbConnection);
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
string tableName = reader.GetString("TABLE_NAME");
tables.Add(tableName);
} }
} }
return tables.Distinct().ToList();
}
}
#endregion
#region Db connection
private MySqlConnection GetDbConnection()
{
if (string.IsNullOrEmpty(_mysqlConnection))
{
InitializeDatabase();
}
var dbConnection = new MySqlConnection(_mysqlConnection);
dbConnection.Open();
return dbConnection;
}
private void InitializeDatabase()
{
var sqlSettings = _services.GetRequiredService<SqlDriverSetting>();
_mysqlConnection = sqlSettings.MySqlTempConnectionString;
var databaseName = GetDatabaseName(_mysqlConnection);
_logger.LogInformation($"Connected to MySQL database {databaseName}");
}
private string GetDatabaseName(string connectionString)
{
string pattern = @"database=([^;]+)";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
Match match = regex.Match(connectionString);
return match.Success ? match.Groups[1].Value : string.Empty;
}
#endregion
}

View file

@ -1,19 +1,16 @@
using System; using BotSharp.Plugin.SqlDriver.Settings;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BotSharp.Plugin.ExcelHandler.Helpers.Sqlite;
using BotSharp.Plugin.ExcelHandler.Models;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using NPOI.SS.UserModel; using NPOI.SS.UserModel;
namespace BotSharp.Plugin.ExcelHandler.Services namespace BotSharp.Plugin.ExcelHandler.Services;
{
public class SqliteService : ISqliteService
{
private readonly ISqliteDbHelpers _sqliteDbHelpers;
public class SqliteService : IDbService
{
private readonly IServiceProvider _services;
private readonly ILogger<SqliteService> _logger;
private string _dbFilePath = string.Empty;
private SqliteConnection _inMemoryDbConnection = null;
private double _excelRowSize = 0; private double _excelRowSize = 0;
private double _excelColumnSize = 0; private double _excelColumnSize = 0;
private string _tableName = "tempTable"; private string _tableName = "tempTable";
@ -21,95 +18,49 @@ namespace BotSharp.Plugin.ExcelHandler.Services
private List<string> _headerColumns = new List<string>(); private List<string> _headerColumns = new List<string>();
private List<string> _columnTypes = new List<string>(); private List<string> _columnTypes = new List<string>();
public SqliteService(ISqliteDbHelpers sqliteDbHelpers) public SqliteService(
IServiceProvider services,
ILogger<SqliteService> logger)
{ {
_sqliteDbHelpers = sqliteDbHelpers; _services = services;
_logger = logger;
} }
public string Provider => "sqlite";
public IEnumerable<SqlContextOut> WriteExcelDataToDB(IWorkbook workbook) public IEnumerable<SqlContextOut> WriteExcelDataToDB(IWorkbook workbook)
{
{ {
var numTables = workbook.NumberOfSheets; var numTables = workbook.NumberOfSheets;
var commandList = new List<SqlContextOut>(); var results = new List<SqlContextOut>();
for (int sheetIdx = 0; sheetIdx < numTables; sheetIdx++) for (int sheetIdx = 0; sheetIdx < numTables; sheetIdx++)
{ {
var commandResult = new SqlContextOut();
ISheet sheet = workbook.GetSheetAt(sheetIdx); ISheet sheet = workbook.GetSheetAt(sheetIdx);
var (isCreateSuccess, message) = SqlCreateTableFn(sheet); var (isCreateSuccess, message) = SqlCreateTableFn(sheet);
if (!isCreateSuccess) if (!isCreateSuccess)
{ {
commandResult = new SqlContextOut results.Add(new SqlContextOut
{ {
isSuccessful = isCreateSuccess, isSuccessful = isCreateSuccess,
Message = message, Message = message,
FileName = _currentFileName FileName = _currentFileName
}; });
commandList.Add(commandResult);
continue; continue;
} }
var (isInsertSuccess, insertMessage) = SqlInsertDataFn(sheet); var (isInsertSuccess, insertMessage) = SqlInsertDataFn(sheet);
commandResult = new SqlContextOut results.Add(new SqlContextOut
{ {
isSuccessful = isInsertSuccess, isSuccessful = isInsertSuccess,
Message = insertMessage, Message = insertMessage,
FileName = _currentFileName FileName = _currentFileName
};
commandList.Add(commandResult);
}
return commandList;
}
}
public void DeleteTableSqlQuery()
{
string deleteTableSql = @"
SELECT
name
FROM
sqlite_schema
WHERE
type = 'table' AND
name NOT LIKE 'sqlite_%'
";
var physicalDbConnection = _sqliteDbHelpers.GetPhysicalDbConnection();
using var selectCmd = new SqliteCommand(deleteTableSql, physicalDbConnection);
using var reader = selectCmd.ExecuteReader();
if (reader.HasRows)
{
var dropTableQueries = new List<string>();
while (reader.Read())
{
string tableName = reader.GetString(0);
var dropTableSql = $"DROP TABLE IF EXISTS '{tableName}'";
dropTableQueries.Add(dropTableSql);
}
dropTableQueries.ForEach(query =>
{
using var dropTableCommand = new SqliteCommand(query, physicalDbConnection);
dropTableCommand.ExecuteNonQuery();
}); });
} }
physicalDbConnection.Close(); return results;
} }
public string GenerateTableSchema()
{
var sb = new StringBuilder();
sb.Append($"\nTable Schema for `{_tableName}`:");
sb.Append("\n");
sb.Append($"cid | name | type ");
sb.Append("\n");
//sb.Append("----|------------|------------");
for (int i = 0; i < _excelColumnSize; i++)
{
sb.Append($"{i,-4} | {_headerColumns[i],-10} | {_columnTypes[i],-10}");
sb.Append("\n");
}
return sb.ToString();
}
#region private methods #region Private methods
private (bool, string) SqlInsertDataFn(ISheet sheet) private (bool, string) SqlInsertDataFn(ISheet sheet)
{ {
try try
@ -219,8 +170,8 @@ namespace BotSharp.Plugin.ExcelHandler.Services
private void ExecuteSqlQueryForInsertion(string query) private void ExecuteSqlQueryForInsertion(string query)
{ {
var physicalDbConnection = _sqliteDbHelpers.GetPhysicalDbConnection(); var physicalDbConnection = GetPhysicalDbConnection();
var inMemoryDbConnection = _sqliteDbHelpers.GetInMemoryDbConnection(); var inMemoryDbConnection = GetInMemoryDbConnection();
physicalDbConnection.BackupDatabase(inMemoryDbConnection, "main", "main"); physicalDbConnection.BackupDatabase(inMemoryDbConnection, "main", "main");
physicalDbConnection.Close(); physicalDbConnection.Close();
@ -233,6 +184,80 @@ namespace BotSharp.Plugin.ExcelHandler.Services
} }
inMemoryDbConnection.BackupDatabase(physicalDbConnection); inMemoryDbConnection.BackupDatabase(physicalDbConnection);
} }
private void DeleteTableSqlQuery()
{
string deleteTableSql = @"
SELECT
name
FROM
sqlite_schema
WHERE
type = 'table' AND
name NOT LIKE 'sqlite_%'
";
var physicalDbConnection = GetPhysicalDbConnection();
using var selectCmd = new SqliteCommand(deleteTableSql, physicalDbConnection);
using var reader = selectCmd.ExecuteReader();
if (reader.HasRows)
{
var dropTableQueries = new List<string>();
while (reader.Read())
{
string tableName = reader.GetString(0);
var dropTableSql = $"DROP TABLE IF EXISTS '{tableName}'";
dropTableQueries.Add(dropTableSql);
}
dropTableQueries.ForEach(query =>
{
using var dropTableCommand = new SqliteCommand(query, physicalDbConnection);
dropTableCommand.ExecuteNonQuery();
});
}
physicalDbConnection.Close();
}
private string GenerateTableSchema()
{
var sb = new StringBuilder();
sb.Append($"\nTable Schema for `{_tableName}`:");
sb.Append("\n");
sb.Append($"cid | name | type ");
sb.Append("\n");
//sb.Append("----|------------|------------");
for (int i = 0; i < _excelColumnSize; i++)
{
sb.Append($"{i,-4} | {_headerColumns[i],-10} | {_columnTypes[i],-10}");
sb.Append("\n");
}
return sb.ToString();
}
#endregion
#region Db connection
private SqliteConnection GetInMemoryDbConnection()
{
if (_inMemoryDbConnection == null)
{
_logger.LogInformation($"Init in-memory Sqlite database connection");
_inMemoryDbConnection = new SqliteConnection("Data Source=:memory:;Mode=ReadWrite");
_inMemoryDbConnection.Open();
}
return _inMemoryDbConnection;
}
private SqliteConnection GetPhysicalDbConnection()
{
if (string.IsNullOrEmpty(_dbFilePath))
{
var sqlSettings = _services.GetRequiredService<SqlDriverSetting>();
_dbFilePath = sqlSettings.SqlLiteConnectionString;
}
var dbConnection = new SqliteConnection($"Data Source={_dbFilePath};Mode=ReadWrite");
dbConnection.Open();
return dbConnection;
}
#endregion #endregion
} }
}

View file

@ -1,11 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BotSharp.Plugin.ExcelHandler.Settings; namespace BotSharp.Plugin.ExcelHandler.Settings;
public class ExcelHandlerSettings public class ExcelHandlerSettings
{ {
public string DbProvider { get; set; } = "mysql";
} }

View file

@ -9,6 +9,9 @@ global using BotSharp.Abstraction.Agents;
global using BotSharp.Abstraction.Conversations; global using BotSharp.Abstraction.Conversations;
global using BotSharp.Abstraction.Conversations.Models; global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Files; global using BotSharp.Abstraction.Files;
global using BotSharp.Abstraction.Files.Enums;
global using BotSharp.Abstraction.Files.Models;
global using BotSharp.Abstraction.Files.Utilities;
global using BotSharp.Abstraction.Functions; global using BotSharp.Abstraction.Functions;
global using BotSharp.Abstraction.Options; global using BotSharp.Abstraction.Options;
global using BotSharp.Abstraction.Agents.Enums; global using BotSharp.Abstraction.Agents.Enums;
@ -17,9 +20,12 @@ global using BotSharp.Abstraction.Agents.Settings;
global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.Functions.Models;
global using BotSharp.Abstraction.Repositories; global using BotSharp.Abstraction.Repositories;
global using BotSharp.Abstraction.Utilities; global using BotSharp.Abstraction.Utilities;
global using BotSharp.Abstraction.Routing;
global using BotSharp.Plugin.ExcelHandler.Enums; global using BotSharp.Plugin.ExcelHandler.Enums;
global using BotSharp.Plugin.ExcelHandler.LlmContexts; global using BotSharp.Plugin.ExcelHandler.LlmContexts;
global using BotSharp.Plugin.ExcelHandler.Models;
global using BotSharp.Plugin.ExcelHandler.Services;
global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection;

View file

@ -14,7 +14,6 @@ public class ImageSettings
public ImageReadSettings? Reading { get; set; } public ImageReadSettings? Reading { get; set; }
public ImageGenerationSettings? Generation { get; set; } public ImageGenerationSettings? Generation { get; set; }
public ImageEditSettings? Edit { get; set; } public ImageEditSettings? Edit { get; set; }
public ImageVariationSettings? Variation { get; set; }
} }
public class ImageReadSettings : LlmBase public class ImageReadSettings : LlmBase
@ -31,11 +30,6 @@ public class ImageEditSettings : LlmBase
{ {
public SettingBase? ImageConverter { get; set; } public SettingBase? ImageConverter { get; set; }
} }
public class ImageVariationSettings : LlmBase
{
}
#endregion #endregion
#region Pdf #region Pdf

View file

@ -33,25 +33,47 @@ public class AudioTranscriptionProvider : IAudioTranscription
private AudioTranscriptionOptions PrepareTranscriptionOptions(string? text) private AudioTranscriptionOptions PrepareTranscriptionOptions(string? text)
{ {
var settingsService = _services.GetRequiredService<ILlmProviderService>();
var state = _services.GetRequiredService<IConversationStateService>(); var state = _services.GetRequiredService<IConversationStateService>();
var format = GetTranscriptionResponseFormat(state.GetState("audio_response_format"));
var granularity = GetGranularity(state.GetState("audio_granularity")); var settings = settingsService.GetSetting(Provider, _model)?.Audio?.Transcription;
var temperature = GetTemperature(state.GetState("audio_temperature"));
var temperature = state.GetState("audio_temperature");
var responseFormat = state.GetState("audio_response_format");
var granularity = state.GetState("audio_granularity");
if (string.IsNullOrEmpty(temperature) && settings?.Temperature != null)
{
temperature = $"{settings.Temperature}";
}
responseFormat = settings?.ResponseFormat != null ? VerifyTranscriptionParameter(responseFormat, settings.ResponseFormat.Default, settings.ResponseFormat.Options) : null;
granularity = settings?.Granularity != null ? VerifyTranscriptionParameter(granularity, settings.Granularity.Default, settings.Granularity.Options) : null;
var options = new AudioTranscriptionOptions var options = new AudioTranscriptionOptions
{ {
ResponseFormat = format,
TimestampGranularities = granularity,
Temperature = temperature,
Prompt = text Prompt = text
}; };
if (!string.IsNullOrEmpty(temperature))
{
options.Temperature = GetTemperature(temperature);
}
if (!string.IsNullOrEmpty(responseFormat))
{
options.ResponseFormat = GetTranscriptionResponseFormat(responseFormat);
}
if (!string.IsNullOrEmpty(granularity))
{
options.TimestampGranularities = GetGranularity(granularity);
}
return options; return options;
} }
private AudioTranscriptionFormat GetTranscriptionResponseFormat(string input) private AudioTranscriptionFormat GetTranscriptionResponseFormat(string input)
{ {
var value = !string.IsNullOrEmpty(input) ? input : "verbose"; var value = !string.IsNullOrEmpty(input) ? input : "json";
AudioTranscriptionFormat format; AudioTranscriptionFormat format;
switch (value) switch (value)
@ -109,4 +131,14 @@ public class AudioTranscriptionProvider : IAudioTranscription
return temperature; return temperature;
} }
private string? VerifyTranscriptionParameter(string? curVal, string? defaultVal, IEnumerable<string>? options = null)
{
if (options.IsNullOrEmpty())
{
return curVal.IfNullOrEmptyAs(defaultVal);
}
return options.Contains(curVal) ? curVal : defaultVal;
}
} }

View file

@ -36,6 +36,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\tests\BotSharp.Plugin.PizzaBot\BotSharp.Plugin.PizzaBot.csproj" /> <ProjectReference Include="..\..\tests\BotSharp.Plugin.PizzaBot\BotSharp.Plugin.PizzaBot.csproj" />
<ProjectReference Include="..\BotSharp.ServiceDefaults\BotSharp.ServiceDefaults.csproj" /> <ProjectReference Include="..\BotSharp.ServiceDefaults\BotSharp.ServiceDefaults.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.ExcelHandler\BotSharp.Plugin.ExcelHandler.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="$(SolutionName)==BotSharp"> <ItemGroup Condition="$(SolutionName)==BotSharp">

View file

@ -319,20 +319,6 @@
"Driver": "Playwright" "Driver": "Playwright"
}, },
"HttpHandler": {
"BaseAddress": "",
"Origin": ""
},
"ChartHandler": {
"ChartPlot": {
"LlmProvider": "openai",
"LlmModel": "gpt-5",
"MaxOutputTokens": 8192,
"ReasoningEffortLevel": "minimal"
}
},
"SqlDriver": { "SqlDriver": {
"MySqlConnectionString": "", "MySqlConnectionString": "",
"SqlServerConnectionString": "", "SqlServerConnectionString": "",
@ -461,10 +447,6 @@
"ImageConverter": { "ImageConverter": {
"Provider": "file-handler" "Provider": "file-handler"
} }
},
"Variation": {
"LlmProvider": "",
"LlmModel": ""
} }
}, },
"Pdf": { "Pdf": {
@ -480,6 +462,33 @@
} }
}, },
"AudioHandler": {
"Audio": {
"Reading": {
"LlmProvider": "openai",
"LlmModel": "gpt-4o-mini-transcribe"
}
}
},
"ExcelHandler": {
"DbProvider": "mysql"
},
"HttpHandler": {
"BaseAddress": "",
"Origin": ""
},
"ChartHandler": {
"ChartPlot": {
"LlmProvider": "openai",
"LlmModel": "gpt-5",
"MaxOutputTokens": 8192,
"ReasoningEffortLevel": "minimal"
}
},
"TencentCos": { "TencentCos": {
"AppId": "", "AppId": "",
"SecretId": "", "SecretId": "",
@ -591,6 +600,8 @@
"BotSharp.Plugin.EmailHandler", "BotSharp.Plugin.EmailHandler",
"BotSharp.Plugin.AudioHandler", "BotSharp.Plugin.AudioHandler",
"BotSharp.Plugin.ChartHandler", "BotSharp.Plugin.ChartHandler",
"BotSharp.Plugin.AudioHandler",
"BotSharp.Plugin.ExcelHandler",
"BotSharp.Plugin.SqlDriver", "BotSharp.Plugin.SqlDriver",
"BotSharp.Plugin.TencentCos" "BotSharp.Plugin.TencentCos"
] ]