diff --git a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor index 2cefb7ac..df3d902a 100644 --- a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor +++ b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor @@ -91,7 +91,7 @@ else @T("Documents for the analysis") - + diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index c2b1cc0b..941a05dc 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -1495,12 +1495,18 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2928927510"] = "Videos -- Images are not supported yet UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T298062956"] = "Images are not supported yet" +-- Click to attach files +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T3521845090"] = "Click to attach files" + -- Clear file list UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T3759696136"] = "Clear file list" -- Executables are not allowed UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T4167762413"] = "Executables are not allowed" +-- Attach file +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T572534842"] = "Attach file" + -- Select a file to attach UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T595772870"] = "Select a file to attach" diff --git a/app/MindWork AI Studio/Chat/ContentImage.cs b/app/MindWork AI Studio/Chat/ContentImage.cs index 467d3e5f..6026e554 100644 --- a/app/MindWork AI Studio/Chat/ContentImage.cs +++ b/app/MindWork AI Studio/Chat/ContentImage.cs @@ -30,6 +30,9 @@ public sealed class ContentImage : IContent, IImageSource /// public List Sources { get; set; } = []; + /// + public List FileAttachments { get; set; } = []; + /// public Task CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatChatThread, CancellationToken token = default) { diff --git a/app/MindWork AI Studio/Chat/ContentText.cs b/app/MindWork AI Studio/Chat/ContentText.cs index ef47f808..d7e0a2a3 100644 --- a/app/MindWork AI Studio/Chat/ContentText.cs +++ b/app/MindWork AI Studio/Chat/ContentText.cs @@ -1,3 +1,4 @@ +using System.Text; using System.Text.Json.Serialization; using AIStudio.Provider; @@ -39,6 +40,9 @@ public sealed class ContentText : IContent /// public List Sources { get; set; } = []; + + /// + public List FileAttachments { get; set; } = []; /// public async Task CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatThread, CancellationToken token = default) @@ -139,9 +143,34 @@ public sealed class ContentText : IContent Text = this.Text, InitialRemoteWait = this.InitialRemoteWait, IsStreaming = this.IsStreaming, + Sources = [..this.Sources], + FileAttachments = [..this.FileAttachments], }; #endregion + + public async Task PrepareContentForAI() + { + var sb = new StringBuilder(); + sb.AppendLine(this.Text); + + if(this.FileAttachments.Count > 0) + { + sb.AppendLine(); + sb.AppendLine("The following files are attached to this message:"); + foreach(var file in this.FileAttachments) + { + sb.AppendLine(); + sb.AppendLine("---------------------------------------"); + sb.AppendLine($"File path: {file}"); + sb.AppendLine("File content:"); + sb.AppendLine("````"); + sb.AppendLine(await Program.RUST_SERVICE.ReadArbitraryFileData(file, int.MaxValue)); + sb.AppendLine("````"); + } + } + return sb.ToString(); + } /// /// The text content. diff --git a/app/MindWork AI Studio/Chat/IContent.cs b/app/MindWork AI Studio/Chat/IContent.cs index 2400e14e..883a1b6c 100644 --- a/app/MindWork AI Studio/Chat/IContent.cs +++ b/app/MindWork AI Studio/Chat/IContent.cs @@ -47,6 +47,13 @@ public interface IContent /// [JsonIgnore] public List Sources { get; set; } + + /// + /// Represents a collection of file attachments associated with the content. + /// This property contains a list of file paths that are appended + /// to the content to provide additional context or resources. + /// + public List FileAttachments { get; set; } /// /// Uses the provider to create the content. diff --git a/app/MindWork AI Studio/Chat/ListContentBlockExtensions.cs b/app/MindWork AI Studio/Chat/ListContentBlockExtensions.cs new file mode 100644 index 00000000..72b08754 --- /dev/null +++ b/app/MindWork AI Studio/Chat/ListContentBlockExtensions.cs @@ -0,0 +1,25 @@ +namespace AIStudio.Chat; + +public static class ListContentBlockExtensions +{ + /// + /// Processes a list of content blocks by transforming them into a collection of message results asynchronously. + /// + /// The list of content blocks to process. + /// A function that transforms each content block into a message result asynchronously. + /// The type of the result produced by the transformation function. + /// An asynchronous task that resolves to a list of transformed results. + public static async Task> BuildMessages(this List blocks, Func> transformer) + { + var messages = blocks + .Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)) + .Select(transformer) + .ToList(); + + // Await all messages: + await Task.WhenAll(messages); + + // Select all results: + return (IList)messages.Select(n => n.Result); + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/AttachDocuments.razor b/app/MindWork AI Studio/Components/AttachDocuments.razor index aa89ebe3..d0cf8d6b 100644 --- a/app/MindWork AI Studio/Components/AttachDocuments.razor +++ b/app/MindWork AI Studio/Components/AttachDocuments.razor @@ -1,20 +1,52 @@ @inherits MSGComponentBase -
- - - - @T("Drag and drop files here or click to attach documents.") - - @foreach (var fileInfo in this.DocumentPaths.Select(file => new FileInfo(file))) +@if (this.UseSmallForm) +{ +
+ @{ + var fileInfos = this.DocumentPaths.Select(file => new FileInfo(file)).ToList(); + } + @if (fileInfos.Any()) { - - + + } - - - - @T("Clear file list") - -
\ No newline at end of file + else + { + + + + } +
+} +else +{ +
+ + + + @T("Drag and drop files here or click to attach documents.") + + @foreach (var fileInfo in this.DocumentPaths.Select(file => new FileInfo(file))) + { + + + + } + + + + @T("Clear file list") + +
+} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs index 7b885842..d7e65aba 100644 --- a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs +++ b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs @@ -28,6 +28,9 @@ public partial class AttachDocuments : MSGComponentBase [Parameter] public bool CatchAllDocuments { get; set; } + [Parameter] + public bool UseSmallForm { get; set; } + [Inject] private ILogger Logger { get; set; } = null!; @@ -36,6 +39,7 @@ public partial class AttachDocuments : MSGComponentBase [Inject] private IDialogService DialogService { get; init; } = null!; + private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Top; #region Overrides of MSGComponentBase diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor b/app/MindWork AI Studio/Components/ChatComponent.razor index 48420522..b68ef369 100644 --- a/app/MindWork AI Studio/Components/ChatComponent.razor +++ b/app/MindWork AI Studio/Components/ChatComponent.razor @@ -83,6 +83,8 @@ + + @if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY) { diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor.cs b/app/MindWork AI Studio/Components/ChatComponent.razor.cs index 94ee2385..4d192f4f 100644 --- a/app/MindWork AI Studio/Components/ChatComponent.razor.cs +++ b/app/MindWork AI Studio/Components/ChatComponent.razor.cs @@ -57,6 +57,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable private string currentWorkspaceName = string.Empty; private Guid currentWorkspaceId = Guid.Empty; private CancellationTokenSource? cancellationTokenSource; + private List fileAttachments = new(); + private HashSet chatDocumentPaths = []; // Unfortunately, we need the input field reference to blur the focus away. Without // this, we cannot clear the input field. @@ -462,6 +464,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable lastUserPrompt = new ContentText { Text = this.userInput, + FileAttachments = this.fileAttachments.ToList(), // Create a copy }; // @@ -507,6 +510,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable // Clear the input field: await this.inputField.FocusAsync(); this.userInput = string.Empty; + this.fileAttachments.Clear(); await this.inputField.BlurAsync(); // Enable the stream state for the chat component: @@ -575,6 +579,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable this.hasUnsavedChanges = false; } + private async Task StartNewChat(bool useSameWorkspace = false, bool deletePreviousChat = false) { // diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogBase.cs b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogBase.cs index 1dd94c1c..0dd1af1b 100644 --- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogBase.cs +++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogBase.cs @@ -23,7 +23,7 @@ public abstract class SettingsDialogBase : MSGComponentBase protected readonly List> availableEmbeddingProviders = new(); #region Overrides of ComponentBase - + /// protected override async Task OnInitializedAsync() { diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs index 93084866..2708ab4f 100644 --- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs +++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs @@ -13,7 +13,7 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase public ChatThread? ExistingChatThread { get; set; } #region Overrides of ComponentBase - + /// protected override async Task OnInitializedAsync() { diff --git a/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs b/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs index e7d3e523..78618db2 100644 --- a/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs +++ b/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs @@ -39,6 +39,26 @@ public sealed class ProviderAlibabaCloud() : BaseProvider("https://dashscope-int // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the AlibabaCloud HTTP chat request: var alibabaCloudChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest { @@ -47,24 +67,8 @@ public sealed class ProviderAlibabaCloud() : BaseProvider("https://dashscope-int // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], + Stream = true, AdditionalApiParameters = apiParameters }, JSON_SERIALIZER_OPTIONS); diff --git a/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs b/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs index f2c88f52..4ea73e77 100644 --- a/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs +++ b/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs @@ -30,29 +30,32 @@ public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.co // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters("system"); + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the Anthropic HTTP chat request: var chatRequest = JsonSerializer.Serialize(new ChatRequest { Model = chatModel.Id, // Build the messages: - Messages = [..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [..messages], System = chatThread.PrepareSystemPrompt(settingsManager, chatThread), MaxTokens = apiParameters.TryGetValue("max_tokens", out var value) && value is int intValue ? intValue : 4_096, diff --git a/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs b/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs index 1063262c..991d6a2e 100644 --- a/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs +++ b/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs @@ -39,6 +39,26 @@ public sealed class ProviderDeepSeek() : BaseProvider("https://api.deepseek.com/ // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the DeepSeek HTTP chat request: var deepSeekChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest { @@ -47,24 +67,8 @@ public sealed class ProviderDeepSeek() : BaseProvider("https://api.deepseek.com/ // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], + Stream = true, AdditionalApiParameters = apiParameters }, JSON_SERIALIZER_OPTIONS); diff --git a/app/MindWork AI Studio/Provider/Fireworks/ProviderFireworks.cs b/app/MindWork AI Studio/Provider/Fireworks/ProviderFireworks.cs index a02c692c..20c79188 100644 --- a/app/MindWork AI Studio/Provider/Fireworks/ProviderFireworks.cs +++ b/app/MindWork AI Studio/Provider/Fireworks/ProviderFireworks.cs @@ -39,6 +39,26 @@ public class ProviderFireworks() : BaseProvider("https://api.fireworks.ai/infere // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the Fireworks HTTP chat request: var fireworksChatRequest = JsonSerializer.Serialize(new ChatRequest { @@ -47,24 +67,7 @@ public class ProviderFireworks() : BaseProvider("https://api.fireworks.ai/infere // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], // Right now, we only support streaming completions: Stream = true, diff --git a/app/MindWork AI Studio/Provider/GWDG/ProviderGWDG.cs b/app/MindWork AI Studio/Provider/GWDG/ProviderGWDG.cs index 2a56bfd4..b1cb291c 100644 --- a/app/MindWork AI Studio/Provider/GWDG/ProviderGWDG.cs +++ b/app/MindWork AI Studio/Provider/GWDG/ProviderGWDG.cs @@ -39,6 +39,26 @@ public sealed class ProviderGWDG() : BaseProvider("https://chat-ai.academiccloud // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the GWDG HTTP chat request: var gwdgChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest { @@ -47,24 +67,8 @@ public sealed class ProviderGWDG() : BaseProvider("https://chat-ai.academiccloud // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], + Stream = true, AdditionalApiParameters = apiParameters }, JSON_SERIALIZER_OPTIONS); diff --git a/app/MindWork AI Studio/Provider/Google/ProviderGoogle.cs b/app/MindWork AI Studio/Provider/Google/ProviderGoogle.cs index 8dcf0c96..7ce3f24e 100644 --- a/app/MindWork AI Studio/Provider/Google/ProviderGoogle.cs +++ b/app/MindWork AI Studio/Provider/Google/ProviderGoogle.cs @@ -39,6 +39,26 @@ public class ProviderGoogle() : BaseProvider("https://generativelanguage.googlea // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the Google HTTP chat request: var geminiChatRequest = JsonSerializer.Serialize(new ChatRequest { @@ -47,24 +67,7 @@ public class ProviderGoogle() : BaseProvider("https://generativelanguage.googlea // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], // Right now, we only support streaming completions: Stream = true, diff --git a/app/MindWork AI Studio/Provider/Groq/ProviderGroq.cs b/app/MindWork AI Studio/Provider/Groq/ProviderGroq.cs index 5cc7b3df..45473d82 100644 --- a/app/MindWork AI Studio/Provider/Groq/ProviderGroq.cs +++ b/app/MindWork AI Studio/Provider/Groq/ProviderGroq.cs @@ -39,6 +39,26 @@ public class ProviderGroq() : BaseProvider("https://api.groq.com/openai/v1/", LO // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the OpenAI HTTP chat request: var groqChatRequest = JsonSerializer.Serialize(new ChatRequest { @@ -47,24 +67,7 @@ public class ProviderGroq() : BaseProvider("https://api.groq.com/openai/v1/", LO // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], // Right now, we only support streaming completions: Stream = true, diff --git a/app/MindWork AI Studio/Provider/Helmholtz/ProviderHelmholtz.cs b/app/MindWork AI Studio/Provider/Helmholtz/ProviderHelmholtz.cs index f0b69bb4..3f7b405b 100644 --- a/app/MindWork AI Studio/Provider/Helmholtz/ProviderHelmholtz.cs +++ b/app/MindWork AI Studio/Provider/Helmholtz/ProviderHelmholtz.cs @@ -38,6 +38,26 @@ public sealed class ProviderHelmholtz() : BaseProvider("https://api.helmholtz-bl // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); // Prepare the Helmholtz HTTP chat request: var helmholtzChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest @@ -47,24 +67,8 @@ public sealed class ProviderHelmholtz() : BaseProvider("https://api.helmholtz-bl // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], + Stream = true, AdditionalApiParameters = apiParameters }, JSON_SERIALIZER_OPTIONS); diff --git a/app/MindWork AI Studio/Provider/HuggingFace/ProviderHuggingFace.cs b/app/MindWork AI Studio/Provider/HuggingFace/ProviderHuggingFace.cs index 6cfb8027..31522b5c 100644 --- a/app/MindWork AI Studio/Provider/HuggingFace/ProviderHuggingFace.cs +++ b/app/MindWork AI Studio/Provider/HuggingFace/ProviderHuggingFace.cs @@ -44,6 +44,26 @@ public sealed class ProviderHuggingFace : BaseProvider // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + // Build the list of messages: + var message = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the HuggingFace HTTP chat request: var huggingfaceChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest { @@ -52,24 +72,8 @@ public sealed class ProviderHuggingFace : BaseProvider // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..message], + Stream = true, AdditionalApiParameters = apiParameters }, JSON_SERIALIZER_OPTIONS); diff --git a/app/MindWork AI Studio/Provider/Mistral/ProviderMistral.cs b/app/MindWork AI Studio/Provider/Mistral/ProviderMistral.cs index 01b0db11..bd999b92 100644 --- a/app/MindWork AI Studio/Provider/Mistral/ProviderMistral.cs +++ b/app/MindWork AI Studio/Provider/Mistral/ProviderMistral.cs @@ -36,6 +36,26 @@ public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/ // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new RegularMessage + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); // Prepare the Mistral HTTP chat request: var mistralChatRequest = JsonSerializer.Serialize(new ChatRequest @@ -45,24 +65,7 @@ public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/ // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new RegularMessage - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], // Right now, we only support streaming completions: Stream = true, @@ -70,6 +73,7 @@ public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/ AdditionalApiParameters = apiParameters }, JSON_SERIALIZER_OPTIONS); + async Task RequestBuilder() { // Build the HTTP post request: diff --git a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs index be38cedc..89da7b7d 100644 --- a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs +++ b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs @@ -88,6 +88,26 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/" // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters("input", "store", "tools"); + + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => systemPromptRole, + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); // // Create the request: either for the Responses API or the Chat Completion API @@ -102,24 +122,7 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/" // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => systemPromptRole, - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], // Right now, we only support streaming completions: Stream = true, diff --git a/app/MindWork AI Studio/Provider/Perplexity/ProviderPerplexity.cs b/app/MindWork AI Studio/Provider/Perplexity/ProviderPerplexity.cs index a15a7e3a..3687ad7b 100644 --- a/app/MindWork AI Studio/Provider/Perplexity/ProviderPerplexity.cs +++ b/app/MindWork AI Studio/Provider/Perplexity/ProviderPerplexity.cs @@ -48,6 +48,26 @@ public sealed class ProviderPerplexity() : BaseProvider("https://api.perplexity. // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message() + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the Perplexity HTTP chat request: var perplexityChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest { @@ -56,24 +76,7 @@ public sealed class ProviderPerplexity() : BaseProvider("https://api.perplexity. // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], Stream = true, AdditionalApiParameters = apiParameters }, JSON_SERIALIZER_OPTIONS); diff --git a/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs b/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs index abb15532..4389099e 100644 --- a/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs +++ b/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs @@ -35,6 +35,26 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the OpenAI HTTP chat request: var providerChatRequest = JsonSerializer.Serialize(new ChatRequest { @@ -43,24 +63,7 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], // Right now, we only support streaming completions: Stream = true, diff --git a/app/MindWork AI Studio/Provider/X/ProviderX.cs b/app/MindWork AI Studio/Provider/X/ProviderX.cs index b1743c53..e8a0b2e7 100644 --- a/app/MindWork AI Studio/Provider/X/ProviderX.cs +++ b/app/MindWork AI Studio/Provider/X/ProviderX.cs @@ -39,6 +39,26 @@ public sealed class ProviderX() : BaseProvider("https://api.x.ai/v1/", LOGGER) // Parse the API parameters: var apiParameters = this.ParseAdditionalApiParameters(); + // Build the list of messages: + var messages = await chatThread.Blocks.BuildMessages(async n => new Message() + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => await text.PrepareContentForAI(), + _ => string.Empty, + } + }); + // Prepare the xAI HTTP chat request: var xChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest { @@ -47,24 +67,7 @@ public sealed class ProviderX() : BaseProvider("https://api.x.ai/v1/", LOGGER) // Build the messages: // - First of all the system prompt // - Then none-empty user and AI messages - Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message - { - Role = n.Role switch - { - ChatRole.USER => "user", - ChatRole.AI => "assistant", - ChatRole.AGENT => "assistant", - ChatRole.SYSTEM => "system", - - _ => "user", - }, - - Content = n.Content switch - { - ContentText text => text.Text, - _ => string.Empty, - } - }).ToList()], + Messages = [systemPrompt, ..messages], // Right now, we only support streaming completions: Stream = true,