using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Files.Utilities; using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Instructs.Models; using BotSharp.Core.Infrastructures; using BotSharp.OpenAPI.ViewModels.Instructs; namespace BotSharp.OpenAPI.Controllers; [Authorize] [ApiController] public class InstructModeController : ControllerBase { private readonly IServiceProvider _services; private readonly ILogger _logger; public InstructModeController(IServiceProvider services, ILogger logger) { _services = services; _logger = logger; } [HttpPost("/instruct/{agentId}")] public async Task InstructCompletion([FromRoute] string agentId, [FromBody] InstructMessageModel input) { var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); state.SetState("provider", input.Provider, source: StateSource.External) .SetState("model", input.Model, source: StateSource.External) .SetState("model_id", input.ModelId, source: StateSource.External) .SetState("instruction", input.Instruction, source: StateSource.External) .SetState("input_text", input.Text, source: StateSource.External) .SetState("template_name", input.Template, source: StateSource.External); var instructor = _services.GetRequiredService(); var result = await instructor.Execute(agentId, new RoleDialogModel(AgentRole.User, input.Text), templateName: input.Template, instruction: input.Instruction); result.States = state.GetStates(); return result; } [HttpPost("/instruct/text-completion")] public async Task TextCompletion([FromBody] IncomingInstructRequest input) { var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); state.SetState("provider", input.Provider ?? "azure-openai", source: StateSource.External) .SetState("model", input.Model, source: StateSource.External) .SetState("model_id", input.ModelId, source: StateSource.External); var textCompletion = CompletionProvider.GetTextCompletion(_services); return await textCompletion.GetCompletion(input.Text, input.AgentId ?? Guid.Empty.ToString(), Guid.NewGuid().ToString()); } #region Chat [HttpPost("/instruct/chat-completion")] public async Task ChatCompletion([FromBody] IncomingInstructRequest input) { var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); state.SetState("provider", input.Provider, source: StateSource.External) .SetState("model", input.Model, source: StateSource.External) .SetState("model_id", input.ModelId, source: StateSource.External); var completion = CompletionProvider.GetChatCompletion(_services); var message = await completion.GetChatCompletions(new Agent() { Id = input.AgentId ?? Guid.Empty.ToString(), Instruction = input.Instruction }, new List { new RoleDialogModel(AgentRole.User, input.Text) }); var hooks = _services.GetServices(); foreach (var hook in hooks) { await hook.OnResponseGenerated(new InstructResponseModel { AgentId = input.AgentId, Provider = input.Provider, Model = input.Model }); } return message.Content; } #endregion #region Read image [HttpPost("/instruct/multi-modal")] public async Task MultiModalCompletion([FromBody] MultiModalRequest input) { var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); try { var fileInstruct = _services.GetRequiredService(); var content = await fileInstruct.ReadImages(input.Provider, input.Model, input.Text, input.Files, input.AgentId); return content; } catch (Exception ex) { var error = $"Error in reading images. {ex.Message}"; _logger.LogError(error); return error; } } [HttpPost("/instruct/multi-modal/upload")] public async Task MultiModalCompletion(IFormFile file, [FromForm] string text, [FromForm] string? provider = null, [FromForm] string? model = null, [FromForm] List? states = null, [FromForm] string? agentId = null) { var state = _services.GetRequiredService(); states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var viewModel = new MultiModalViewModel(); try { var data = FileUtility.BuildFileDataFromFile(file); var files = new List { new InstructFileModel { FileData = data } }; var fileInstruct = _services.GetRequiredService(); var content = await fileInstruct.ReadImages(provider, model, text, files, agentId); viewModel.Content = content; return viewModel; } catch (Exception ex) { var error = $"Error in reading image upload. {ex.Message}"; _logger.LogError(error); viewModel.Message = error; return viewModel; } } #endregion #region Generate image [HttpPost("/instruct/image-generation")] public async Task ImageGeneration([FromBody] ImageGenerationRequest input) { var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var imageViewModel = new ImageGenerationViewModel(); try { var fileInstruct = _services.GetRequiredService(); var message = await fileInstruct.GenerateImage(input.Provider, input.Model, input.Text, input.AgentId); imageViewModel.Content = message.Content; imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList(); return imageViewModel; } catch (Exception ex) { var error = $"Error in image generation. {ex.Message}"; _logger.LogError(error); imageViewModel.Message = error; return imageViewModel; } } #endregion #region Edit image [HttpPost("/instruct/image-variation")] public async Task ImageVariation([FromBody] ImageVariationRequest input) { var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var imageViewModel = new ImageGenerationViewModel(); try { if (input.File == null) { return new ImageGenerationViewModel { Message = "Error! Cannot find an image!" }; } var fileInstruct = _services.GetRequiredService(); var message = await fileInstruct.VaryImage(input.Provider, input.Model, input.File, input.AgentId); imageViewModel.Content = message.Content; imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList(); return imageViewModel; } catch (Exception ex) { var error = $"Error in image variation. {ex.Message}"; _logger.LogError(error); imageViewModel.Message = error; return imageViewModel; } } [HttpPost("/instruct/image-variation/upload")] public async Task ImageVariation(IFormFile file, [FromForm] string? provider = null, [FromForm] string? model = null, [FromForm] List? states = null, [FromForm] string? agentId = null) { var state = _services.GetRequiredService(); states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var imageViewModel = new ImageGenerationViewModel(); try { using var stream = new MemoryStream(); file.CopyTo(stream); stream.Position = 0; var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2"); var message = await completion.GetImageVariation(new Agent() { Id = agentId ?? Guid.Empty.ToString() }, new RoleDialogModel(AgentRole.User, string.Empty), stream, file.FileName); imageViewModel.Content = message.Content; imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList(); stream.Close(); return imageViewModel; } catch (Exception ex) { var error = $"Error in image variation upload. {ex.Message}"; _logger.LogError(error); imageViewModel.Message = error; return imageViewModel; } } [HttpPost("/instruct/image-edit")] public async Task ImageEdit([FromBody] ImageEditRequest input) { var fileInstruct = _services.GetRequiredService(); var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var imageViewModel = new ImageGenerationViewModel(); try { if (input.File == null) { return new ImageGenerationViewModel { Message = "Error! Cannot find a valid image file!" }; } var message = await fileInstruct.EditImage(input.Provider, input.Model, input.Text, input.File, input.AgentId); imageViewModel.Content = message.Content; imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList(); return imageViewModel; } catch (Exception ex) { var error = $"Error in image edit. {ex.Message}"; _logger.LogError(error); imageViewModel.Message = error; return imageViewModel; } } [HttpPost("/instruct/image-edit/upload")] public async Task ImageEdit(IFormFile file, [FromForm] string text, [FromForm] string? provider = null, [FromForm] string? model = null, [FromForm] List? states = null, [FromForm] string? agentId = null) { var fileInstruct = _services.GetRequiredService(); var state = _services.GetRequiredService(); states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var imageViewModel = new ImageGenerationViewModel(); try { using var stream = new MemoryStream(); file.CopyTo(stream); stream.Position = 0; var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2"); var message = await completion.GetImageEdits(new Agent() { Id = agentId ?? Guid.Empty.ToString() }, new RoleDialogModel(AgentRole.User, text), stream, file.FileName); imageViewModel.Content = message.Content; imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList(); stream.Close(); return imageViewModel; } catch (Exception ex) { var error = $"Error in image edit upload. {ex.Message}"; _logger.LogError(error); imageViewModel.Message = error; return imageViewModel; } } [HttpPost("/instruct/image-mask-edit")] public async Task ImageMaskEdit([FromBody] ImageMaskEditRequest input) { var fileInstruct = _services.GetRequiredService(); var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var imageViewModel = new ImageGenerationViewModel(); try { var image = input.File; var mask = input.Mask; if (image == null || mask == null) { return new ImageGenerationViewModel { Message = "Error! Cannot find a valid image or mask!" }; } var message = await fileInstruct.EditImage(input.Provider, input.Model, input.Text, image, mask, input.AgentId); imageViewModel.Content = message.Content; imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList(); return imageViewModel; } catch (Exception ex) { var error = $"Error in image mask edit. {ex.Message}"; _logger.LogError(error); imageViewModel.Message = error; return imageViewModel; } } [HttpPost("/instruct/image-mask-edit/upload")] public async Task ImageMaskEdit(IFormFile image, IFormFile mask, [FromForm] string text, [FromForm] string? provider = null, [FromForm] string? model = null, [FromForm] List? states = null, [FromForm] string? agentId = null) { var fileInstruct = _services.GetRequiredService(); var state = _services.GetRequiredService(); states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var imageViewModel = new ImageGenerationViewModel(); try { using var imageStream = new MemoryStream(); image.CopyTo(imageStream); imageStream.Position = 0; using var maskStream = new MemoryStream(); mask.CopyTo(maskStream); maskStream.Position = 0; var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2"); var message = await completion.GetImageEdits(new Agent() { Id = agentId ?? Guid.Empty.ToString() }, new RoleDialogModel(AgentRole.User, text), imageStream, image.FileName, maskStream, mask.FileName); imageViewModel.Content = message.Content; imageViewModel.Images = message.GeneratedImages.Select(x => ImageViewModel.ToViewModel(x)).ToList(); imageStream.Close(); maskStream.Close(); return imageViewModel; } catch (Exception ex) { var error = $"Error in image mask edit upload. {ex.Message}"; _logger.LogError(error); imageViewModel.Message = error; return imageViewModel; } } #endregion #region Pdf [HttpPost("/instruct/pdf-completion")] public async Task PdfCompletion([FromBody] MultiModalRequest input) { var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var viewModel = new PdfCompletionViewModel(); try { var fileInstruct = _services.GetRequiredService(); var content = await fileInstruct.ReadPdf(input.Provider, input.Model, input.ModelId, input.Text, input.Files, input.AgentId); viewModel.Content = content; return viewModel; } catch (Exception ex) { var error = $"Error in pdf completion. {ex.Message}"; _logger.LogError(error); viewModel.Message = error; return viewModel; } } [HttpPost("/instruct/pdf-completion/upload")] public async Task PdfCompletion(IFormFile file, [FromForm] string text, [FromForm] string? provider = null, [FromForm] string? model = null, [FromForm] string? modelId = null, [FromForm] List? states = null, [FromForm] string? agentId = null) { var state = _services.GetRequiredService(); states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var viewModel = new PdfCompletionViewModel(); try { var data = FileUtility.BuildFileDataFromFile(file); var files = new List { new InstructFileModel { FileData = data } }; var fileInstruct = _services.GetRequiredService(); var content = await fileInstruct.ReadPdf(provider, model, modelId, text, files, agentId); viewModel.Content = content; return viewModel; } catch (Exception ex) { var error = $"Error in pdf completion upload. {ex.Message}"; _logger.LogError(error); viewModel.Message = error; return viewModel; } } #endregion #region Audio [HttpPost("/instruct/speech-to-text")] public async Task SpeechToText([FromBody] SpeechToTextRequest input) { var fileInstruct = _services.GetRequiredService(); var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var viewModel = new SpeechToTextViewModel(); try { var audio = input.File; if (audio == null) { return new SpeechToTextViewModel { Message = "Error! Cannot find a valid audio file!" }; } var content = await fileInstruct.SpeechToText(input.Provider, input.Model, audio); viewModel.Content = content; return viewModel; } catch (Exception ex) { var error = $"Error in speech to text. {ex.Message}"; _logger.LogError(error); viewModel.Message = error; return viewModel; } } [HttpPost("/instruct/speech-to-text/upload")] public async Task SpeechToText(IFormFile file, [FromForm] string? provider = null, [FromForm] string? model = null, [FromForm] string? text = null, [FromForm] List? states = null) { var fileInstruct = _services.GetRequiredService(); var state = _services.GetRequiredService(); states?.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var viewModel = new SpeechToTextViewModel(); try { using var stream = new MemoryStream(); file.CopyTo(stream); stream.Position = 0; var completion = CompletionProvider.GetAudioCompletion(_services, provider: provider ?? "openai", model: model ?? "whisper-1"); var content = await completion.GenerateTextFromAudioAsync(stream, file.FileName, text); viewModel.Content = content; stream.Close(); return viewModel; } catch (Exception ex) { var error = $"Error in speech-to-text upload. {ex.Message}"; _logger.LogError(error); viewModel.Message = error; return viewModel; } } [HttpPost("/instruct/text-to-speech")] public async Task TextToSpeech([FromBody] TextToSpeechRequest input) { var state = _services.GetRequiredService(); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds, source: StateSource.External)); var completion = CompletionProvider.GetAudioCompletion(_services, provider: input.Provider ?? "openai", model: input.Model ?? "tts-1"); var binaryData = await completion.GenerateAudioFromTextAsync(input.Text); var stream = binaryData.ToStream(); stream.Position = 0; return new FileStreamResult(stream, "audio/mpeg") { FileDownloadName = "output.mp3" }; } #endregion }