2024-08-17 03:39:32 +00:00
|
|
|
using BotSharp.Core.Infrastructures;
|
|
|
|
|
using Microsoft.AspNetCore.StaticFiles;
|
|
|
|
|
|
|
|
|
|
namespace BotSharp.Plugin.AudioHandler.Functions;
|
|
|
|
|
|
|
|
|
|
public class HandleAudioRequestFn : IFunctionCallback
|
|
|
|
|
{
|
|
|
|
|
public string Name => "handle_audio_request";
|
|
|
|
|
public string Indication => "Handling audio request";
|
|
|
|
|
|
|
|
|
|
private readonly IServiceProvider _serviceProvider;
|
|
|
|
|
private readonly ILogger<HandleAudioRequestFn> _logger;
|
|
|
|
|
private readonly BotSharpOptions _options;
|
|
|
|
|
|
|
|
|
|
private readonly IEnumerable<string> _audioContentType = new List<string>
|
|
|
|
|
{
|
|
|
|
|
AudioType.mp3.ToFileType(),
|
|
|
|
|
AudioType.wav.ToFileType(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public HandleAudioRequestFn(
|
|
|
|
|
IServiceProvider serviceProvider,
|
|
|
|
|
ILogger<HandleAudioRequestFn> logger,
|
2024-08-26 22:24:07 +00:00
|
|
|
BotSharpOptions options)
|
2024-08-17 03:39:32 +00:00
|
|
|
{
|
|
|
|
|
_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 wholeDialogs = conv.GetDialogHistory();
|
2024-08-26 22:24:07 +00:00
|
|
|
var dialogs = AssembleFiles(conv.ConversationId, wholeDialogs);
|
2024-08-17 03:39:32 +00:00
|
|
|
|
2024-08-26 22:24:07 +00:00
|
|
|
var response = await GetResponeFromDialogs(dialogs);
|
2024-08-17 03:39:32 +00:00
|
|
|
message.Content = response;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-26 22:24:07 +00:00
|
|
|
private List<RoleDialogModel> AssembleFiles(string convId, List<RoleDialogModel> dialogs)
|
2024-08-17 03:39:32 +00:00
|
|
|
{
|
2024-08-26 22:24:07 +00:00
|
|
|
if (dialogs.IsNullOrEmpty()) return new List<RoleDialogModel>();
|
2024-08-17 03:39:32 +00:00
|
|
|
|
|
|
|
|
var fileService = _serviceProvider.GetRequiredService<IFileStorageService>();
|
|
|
|
|
var messageId = dialogs.Select(x => x.MessageId).Distinct().ToList();
|
|
|
|
|
var audioMessageFiles = fileService.GetMessageFiles(convId, messageId, FileSourceType.User, _audioContentType);
|
|
|
|
|
|
|
|
|
|
audioMessageFiles = audioMessageFiles.Where(x => x.ContentType.Contains("audio")).ToList();
|
|
|
|
|
|
|
|
|
|
foreach (var dialog in dialogs)
|
|
|
|
|
{
|
|
|
|
|
var found = audioMessageFiles.Where(x => x.MessageId == dialog.MessageId).ToList();
|
2024-08-26 22:24:07 +00:00
|
|
|
if (found.IsNullOrEmpty()) continue;
|
2024-08-17 03:39:32 +00:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2024-08-26 22:24:07 +00:00
|
|
|
var speech2Text = await PrepareModel("native");
|
2024-08-17 03:39:32 +00:00
|
|
|
var dialog = dialogs.Where(x => !x.Files.IsNullOrEmpty()).Last();
|
|
|
|
|
int transcribedCount = 0;
|
2024-08-26 22:24:07 +00:00
|
|
|
|
2024-08-17 03:39:32 +00:00
|
|
|
foreach (var file in dialog.Files)
|
|
|
|
|
{
|
2024-08-26 22:24:07 +00:00
|
|
|
if (file == null) continue;
|
2024-08-17 03:39:32 +00:00
|
|
|
|
|
|
|
|
string extension = Path.GetExtension(file?.FileStorageUrl);
|
|
|
|
|
if (ParseAudioFileType(extension) && File.Exists(file.FileStorageUrl))
|
|
|
|
|
{
|
2024-08-26 22:24:07 +00:00
|
|
|
file.FileData = await speech2Text.GenerateTextFromAudioAsync(file.FileStorageUrl);
|
2024-08-17 03:39:32 +00:00
|
|
|
transcribedCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (transcribedCount == 0)
|
|
|
|
|
{
|
|
|
|
|
throw new FileNotFoundException($"No audio files found in the dialog. MessageId: {dialog.MessageId}");
|
|
|
|
|
}
|
2024-08-26 22:24:07 +00:00
|
|
|
|
2024-08-17 03:39:32 +00:00
|
|
|
var resList = dialog.Files.Select(x => $"{x.FileName} \r\n {x.FileData}").ToList();
|
|
|
|
|
return string.Join("\n\r", resList);
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-26 22:24:07 +00:00
|
|
|
private async Task<ISpeechToText> PrepareModel(string provider = "native")
|
2024-08-17 03:39:32 +00:00
|
|
|
{
|
2024-08-26 22:24:07 +00:00
|
|
|
var speech2Text = _serviceProvider.GetServices<ISpeechToText>().FirstOrDefault(x => x.Provider == provider.ToLower());
|
|
|
|
|
if (speech2Text == null)
|
2024-08-17 03:39:32 +00:00
|
|
|
{
|
2024-08-26 22:24:07 +00:00
|
|
|
throw new Exception($"Can't resolve speech2text provider by {provider}");
|
2024-08-17 03:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
2024-08-26 22:24:07 +00:00
|
|
|
if (provider.IsEqualTo("openai"))
|
2024-08-17 03:39:32 +00:00
|
|
|
{
|
|
|
|
|
return CompletionProvider.GetSpeechToText(_serviceProvider, provider: "openai", model: "whisper-1");
|
|
|
|
|
}
|
2024-08-26 22:24:07 +00:00
|
|
|
|
|
|
|
|
await speech2Text.SetModelName("Tiny");
|
|
|
|
|
return speech2Text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ParseAudioFileType(string fileType)
|
|
|
|
|
{
|
|
|
|
|
fileType = fileType.ToLower();
|
|
|
|
|
var provider = new FileExtensionContentTypeProvider();
|
|
|
|
|
bool canParse = Enum.TryParse<AudioType>(fileType, out _) || provider.TryGetContentType(fileType, out _);
|
|
|
|
|
return canParse;
|
2024-08-17 03:39:32 +00:00
|
|
|
}
|
|
|
|
|
}
|