values) => this.SettingsManager.ConfigurationData.Tools.DefaultToolIdsByComponent[this.Component.ToString()] = [..values];
+}
diff --git a/app/MindWork AI Studio/Components/ToolSelection.razor b/app/MindWork AI Studio/Components/ToolSelection.razor
new file mode 100644
index 00000000..c5123d78
--- /dev/null
+++ b/app/MindWork AI Studio/Components/ToolSelection.razor
@@ -0,0 +1,64 @@
+@using AIStudio.Settings
+@using AIStudio.Tools.ToolCallingSystem
+@inherits MSGComponentBase
+
+
+
+
+
+
+
+
+
+
+
+ @T("Tool Selection")
+
+
+
+
+
+ @if (!this.SupportsTools)
+ {
+ @T("The selected provider or model does not support tool calling.")
+ }
+ else if (this.Disabled)
+ {
+
+ @T("Tool changes are locked while a response is running. Your current selection is shown below and applies again from the next message once the run is finished.")
+
+ }
+ else if (this.catalog.Count == 0)
+ {
+ @T("No tools are available in this context.")
+ }
+
+ @if (this.SupportsTools && this.catalog.Count > 0)
+ {
+ @foreach (var item in this.catalog)
+ {
+ var isSelected = this.SelectedToolIds.Contains(item.Definition.Id);
+ var isConfigured = item.ConfigurationState.IsConfigured;
+
+
+
+
+
+ @item.Definition.DisplayName
+
+
+
+ @if (!isConfigured)
+ {
+ @T("Required settings are missing. Configure this tool before enabling it.")
+ }
+
+ }
+ }
+
+
+ @T("Close")
+
+
+
+
diff --git a/app/MindWork AI Studio/Components/ToolSelection.razor.cs b/app/MindWork AI Studio/Components/ToolSelection.razor.cs
new file mode 100644
index 00000000..dca52ef4
--- /dev/null
+++ b/app/MindWork AI Studio/Components/ToolSelection.razor.cs
@@ -0,0 +1,78 @@
+using AIStudio.Dialogs.Settings;
+using AIStudio.Provider;
+using AIStudio.Settings;
+using AIStudio.Tools;
+using AIStudio.Tools.ToolCallingSystem;
+
+using Microsoft.AspNetCore.Components;
+
+namespace AIStudio.Components;
+
+public partial class ToolSelection : MSGComponentBase
+{
+ [Parameter]
+ public AIStudio.Tools.Components Component { get; set; } = AIStudio.Tools.Components.CHAT;
+
+ [Parameter]
+ public required AIStudio.Settings.Provider LLMProvider { get; set; }
+
+ [Parameter]
+ public HashSet SelectedToolIds { get; set; } = [];
+
+ [Parameter]
+ public EventCallback> SelectedToolIdsChanged { get; set; }
+
+ [Parameter]
+ public bool Disabled { get; set; }
+
+ [Parameter]
+ public string PopoverButtonClasses { get; set; } = string.Empty;
+
+ [Inject]
+ private ToolRegistry ToolRegistry { get; init; } = null!;
+
+ [Inject]
+ private IDialogService DialogService { get; init; } = null!;
+
+ private bool showSelection;
+ private IReadOnlyList catalog = [];
+
+ private bool SupportsTools =>
+ this.LLMProvider != AIStudio.Settings.Provider.NONE &&
+ this.LLMProvider.GetModelCapabilities().Contains(Capability.CHAT_COMPLETION_API) &&
+ this.LLMProvider.GetModelCapabilities().Contains(Capability.FUNCTION_CALLING);
+
+ private async Task ToggleSelection()
+ {
+ this.showSelection = !this.showSelection;
+ if (this.showSelection)
+ this.catalog = await this.ToolRegistry.GetCatalogAsync(this.Component);
+ }
+
+ private void Hide() => this.showSelection = false;
+
+ private async Task ChangeSelection(string toolId, bool isSelected)
+ {
+ var updated = new HashSet(this.SelectedToolIds, StringComparer.Ordinal);
+ if (isSelected)
+ updated.Add(toolId);
+ else
+ updated.Remove(toolId);
+
+ this.SelectedToolIds = updated;
+ await this.SelectedToolIdsChanged.InvokeAsync(updated);
+ }
+
+ private async Task OpenSettings(string toolId)
+ {
+ var parameters = new DialogParameters
+ {
+ { x => x.ToolId, toolId },
+ };
+
+ var dialog = await this.DialogService.ShowAsync(null, parameters, Dialogs.DialogOptions.FULLSCREEN);
+ await dialog.Result;
+ this.catalog = await this.ToolRegistry.GetCatalogAsync(this.Component);
+ this.StateHasChanged();
+ }
+}
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogAgenda.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogAgenda.razor
index dcaf18ff..789d7a01 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogAgenda.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogAgenda.razor
@@ -36,6 +36,7 @@
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogAssistantBias.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogAssistantBias.razor
index 40f3331f..c00fe8d4 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogAssistantBias.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogAssistantBias.razor
@@ -32,6 +32,7 @@
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChat.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChat.razor
index d9ed5a90..94f9b021 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChat.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChat.razor
@@ -22,6 +22,8 @@
+
+
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
{
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogCoding.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogCoding.razor
index 6cfed1ac..2dd954f6 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogCoding.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogCoding.razor
@@ -22,6 +22,7 @@
+
Close
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogGrammarSpelling.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogGrammarSpelling.razor
index 7130f3cf..87a37a4f 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogGrammarSpelling.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogGrammarSpelling.razor
@@ -19,10 +19,11 @@
+
@T("Close")
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogI18N.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogI18N.razor
index a64528d0..6f7d8798 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogI18N.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogI18N.razor
@@ -19,10 +19,11 @@
+
@T("Close")
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogIconFinder.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogIconFinder.razor
index 187e0523..d4cb90fc 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogIconFinder.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogIconFinder.razor
@@ -15,10 +15,11 @@
+
@T("Close")
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogJobPostings.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogJobPostings.razor
index 9d2c47bc..563a7c34 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogJobPostings.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogJobPostings.razor
@@ -26,10 +26,11 @@
+
@T("Close")
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogLegalCheck.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogLegalCheck.razor
index 71947b14..817774ab 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogLegalCheck.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogLegalCheck.razor
@@ -17,6 +17,7 @@
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogMyTasks.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogMyTasks.razor
index 1fed1f08..dacffa8c 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogMyTasks.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogMyTasks.razor
@@ -20,6 +20,7 @@
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogRewrite.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogRewrite.razor
index 6cdfc96f..5849256b 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogRewrite.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogRewrite.razor
@@ -21,10 +21,11 @@
+
@T("Close")
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogSlideBuilder.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogSlideBuilder.razor
index 18d51280..6d019598 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogSlideBuilder.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogSlideBuilder.razor
@@ -25,6 +25,7 @@
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogSynonyms.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogSynonyms.razor
index 0a78e616..9dc1fdf2 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogSynonyms.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogSynonyms.razor
@@ -19,10 +19,11 @@
+
@T("Close")
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogTextSummarizer.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogTextSummarizer.razor
index 9e1e183b..f17e57ad 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogTextSummarizer.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogTextSummarizer.razor
@@ -29,10 +29,11 @@
+
@T("Close")
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogTranslation.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogTranslation.razor
index f3db4a3c..61187dc8 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogTranslation.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogTranslation.razor
@@ -23,10 +23,11 @@
+
@T("Close")
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogWritingEMails.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogWritingEMails.razor
index ff96ced6..2ff22788 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogWritingEMails.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogWritingEMails.razor
@@ -23,6 +23,7 @@
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor b/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor
new file mode 100644
index 00000000..9f8b9d0b
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor
@@ -0,0 +1,46 @@
+@using AIStudio.Tools.ToolCallingSystem
+@inherits SettingsDialogBase
+
+
+
+
+
+ @(this.toolDefinition?.DisplayName ?? T("Tool Settings"))
+
+
+
+ @if (this.toolDefinition is null)
+ {
+ @T("The selected tool could not be loaded.")
+ }
+ else
+ {
+ @foreach (var property in this.toolDefinition.SettingsSchema.Properties)
+ {
+ var fieldName = property.Key;
+ var field = property.Value;
+ if (field.EnumValues.Count > 0)
+ {
+
+ @foreach (var option in field.EnumValues)
+ {
+ @option
+ }
+
+ }
+ else
+ {
+
+ }
+ }
+ }
+
+
+
+ @T("Cancel")
+
+
+ @T("Save")
+
+
+
diff --git a/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor.cs b/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor.cs
new file mode 100644
index 00000000..cf4c9ded
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor.cs
@@ -0,0 +1,41 @@
+using AIStudio.Tools.ToolCallingSystem;
+
+using Microsoft.AspNetCore.Components;
+
+namespace AIStudio.Dialogs.Settings;
+
+public partial class ToolSettingsDialog : SettingsDialogBase
+{
+ [Parameter]
+ public string ToolId { get; set; } = string.Empty;
+
+ [Inject]
+ private ToolRegistry ToolRegistry { get; init; } = null!;
+
+ [Inject]
+ private ToolSettingsService ToolSettingsService { get; init; } = null!;
+
+ private ToolDefinition? toolDefinition;
+ private Dictionary values = new(StringComparer.Ordinal);
+
+ protected override async Task OnInitializedAsync()
+ {
+ await base.OnInitializedAsync();
+ this.toolDefinition = this.ToolRegistry.GetDefinition(this.ToolId);
+ if (this.toolDefinition is not null)
+ this.values = await this.ToolSettingsService.GetSettingsAsync(this.toolDefinition);
+ }
+
+ private string GetValue(string fieldName) => this.values.GetValueOrDefault(fieldName, string.Empty);
+
+ private void UpdateValue(string fieldName, string? value) => this.values[fieldName] = value ?? string.Empty;
+
+ private async Task Save()
+ {
+ if (this.toolDefinition is null)
+ return;
+
+ await this.ToolSettingsService.SaveSettingsAsync(this.toolDefinition, this.values);
+ this.MudDialog.Close();
+ }
+}
diff --git a/app/MindWork AI Studio/Pages/Settings.razor b/app/MindWork AI Studio/Pages/Settings.razor
index 70201807..16542dfb 100644
--- a/app/MindWork AI Studio/Pages/Settings.razor
+++ b/app/MindWork AI Studio/Pages/Settings.razor
@@ -21,6 +21,7 @@
}
+
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
{
@@ -31,4 +32,4 @@
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Program.cs b/app/MindWork AI Studio/Program.cs
index f19344d6..3d180bde 100644
--- a/app/MindWork AI Studio/Program.cs
+++ b/app/MindWork AI Studio/Program.cs
@@ -1,5 +1,6 @@
using AIStudio.Agents;
using AIStudio.Settings;
+using AIStudio.Tools.ToolCallingSystem;
using AIStudio.Tools.Databases;
using AIStudio.Tools.Databases.Qdrant;
using AIStudio.Tools.PluginSystem;
@@ -168,6 +169,10 @@ internal sealed class Program
builder.Services.AddSingleton(rust);
builder.Services.AddMudMarkdownClipboardService();
builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
diff --git a/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs b/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs
index 7f2bf792..24a6f496 100644
--- a/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs
+++ b/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs
@@ -22,29 +22,22 @@ public sealed class ProviderAlibabaCloud() : BaseProvider(LLMProviders.ALIBABA_C
///
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
- await foreach (var content in this.StreamOpenAICompatibleChatCompletion(
+ await foreach (var content in this.StreamOpenAICompatibleChatCompletion(
"AlibabaCloud",
chatModel,
chatThread,
settingsManager,
- async (systemPrompt, apiParameters) =>
- {
- // Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
-
- return new ChatCompletionAPIRequest
+ () => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
+ (systemPrompt, messages, apiParameters, stream, tools) =>
+ Task.FromResult(new ChatCompletionAPIRequest
{
Model = chatModel.Id,
-
- // Build the messages:
- // - First of all the system prompt
- // - Then none-empty user and AI messages
Messages = [systemPrompt, ..messages],
-
- Stream = true,
+ Stream = stream,
+ Tools = tools,
+ ParallelToolCalls = tools is null ? null : true,
AdditionalApiParameters = apiParameters
- };
- },
+ }),
token: token))
yield return content;
}
diff --git a/app/MindWork AI Studio/Provider/BaseProvider.cs b/app/MindWork AI Studio/Provider/BaseProvider.cs
index 46e43843..5f3ba7a0 100644
--- a/app/MindWork AI Studio/Provider/BaseProvider.cs
+++ b/app/MindWork AI Studio/Provider/BaseProvider.cs
@@ -10,11 +10,14 @@ using AIStudio.Provider.Anthropic;
using AIStudio.Provider.OpenAI;
using AIStudio.Provider.SelfHosted;
using AIStudio.Settings;
+using AIStudio.Tools.ToolCallingSystem;
using AIStudio.Tools.MIME;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;
+using Microsoft.Extensions.DependencyInjection;
+
using Host = AIStudio.Provider.SelfHosted.Host;
namespace AIStudio.Provider;
@@ -572,6 +575,7 @@ public abstract class BaseProvider : IProvider, ISecretId
/// The selected chat model.
/// The current chat thread.
/// The settings manager.
+ /// Builds the provider-specific base messages.
/// Builds the provider-specific request body.
/// The secret store type.
/// Whether the API key is optional.
@@ -579,16 +583,16 @@ public abstract class BaseProvider : IProvider, ISecretId
/// The request path, relative to the provider base URL.
/// Optional additional headers to add.
/// The cancellation token.
- /// The request DTO type.
/// The delta stream line type.
/// The annotation stream line type.
/// The streamed content chunks.
- protected async IAsyncEnumerable StreamOpenAICompatibleChatCompletion(
+ protected async IAsyncEnumerable StreamOpenAICompatibleChatCompletion(
string providerName,
Model chatModel,
ChatThread chatThread,
SettingsManager settingsManager,
- Func, Task> requestFactory,
+ Func>> messagesFactory,
+ Func, IDictionary, bool, IList