diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor.cs b/app/MindWork AI Studio/Components/ChatComponent.razor.cs
index 88e1ec20..45fabf86 100644
--- a/app/MindWork AI Studio/Components/ChatComponent.razor.cs
+++ b/app/MindWork AI Studio/Components/ChatComponent.razor.cs
@@ -81,7 +81,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
protected override async Task OnInitializedAsync()
{
// Apply the filters for the message bus:
- this.ApplyFilters([], [ Event.HAS_CHAT_UNSAVED_CHANGES, Event.RESET_CHAT_STATE, Event.CHAT_STREAMING_DONE, Event.WORKSPACE_LOADED_CHAT_CHANGED ]);
+ this.ApplyFilters([], [ Event.HAS_CHAT_UNSAVED_CHANGES, Event.RESET_CHAT_STATE, Event.CHAT_STREAMING_DONE, Event.WORKSPACE_LOADED_CHAT_CHANGED, Event.CONFIGURATION_CHANGED ]);
// Configure the spellchecking for the user input:
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
@@ -1007,6 +1007,10 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
case Event.WORKSPACE_LOADED_CHAT_CHANGED:
await this.LoadedChatChanged();
break;
+
+ case Event.CONFIGURATION_CHANGED:
+ await this.InvokeAsync(this.StateHasChanged);
+ break;
}
}
diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor b/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor
index c10c1983..238b0133 100644
--- a/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor
+++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor
@@ -1,3 +1,4 @@
+@using AIStudio.Provider
@using AIStudio.Tools.ToolCallingSystem
@inherits SettingsPanelBase
@@ -11,6 +12,7 @@
@T("Icon")
@T("Name")
@T("Description")
+ @T("Minimum provider confidence")
@T("State")
@T("Settings")
@@ -24,6 +26,16 @@
@context.Implementation.GetDescription()
+
+
+ @foreach (var confidenceLevel in this.GetSelectableConfidenceLevels())
+ {
+
+ @this.GetConfidenceLevelName(confidenceLevel)
+
+ }
+
+
@if (context.ConfigurationState.IsConfigured)
{
diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor.cs b/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor.cs
index c978d16c..36b0e7a8 100644
--- a/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor.cs
+++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor.cs
@@ -1,4 +1,6 @@
+using AIStudio.Provider;
using AIStudio.Dialogs.Settings;
+using AIStudio.Settings;
using AIStudio.Tools;
using AIStudio.Tools.ToolCallingSystem;
@@ -15,8 +17,9 @@ public partial class SettingsPanelTools : SettingsPanelBase
protected override async Task OnInitializedAsync()
{
- await base.OnInitializedAsync();
+ this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED ]);
this.items = await this.ToolRegistry.GetCatalogAsync(this.ToolRegistry.GetAllDefinitions());
+ await base.OnInitializedAsync();
}
private async Task OpenSettings(string toolId)
@@ -47,4 +50,40 @@ public partial class SettingsPanelTools : SettingsPanelBase
return item.Implementation.GetSettingsFieldLabel(fieldName, fieldDefinition);
}
+
+ private IEnumerable GetSelectableConfidenceLevels() =>
+ Enum.GetValues().OrderBy(x => x).Where(x => x is not ConfidenceLevel.UNKNOWN);
+
+ private string GetCurrentConfidenceLevelName(ToolCatalogItem item) => this.GetConfidenceLevelName(this.GetMinimumProviderConfidence(item));
+
+ private string GetConfidenceLevelName(ConfidenceLevel confidenceLevel) => confidenceLevel is ConfidenceLevel.NONE
+ ? this.T("No minimum confidence level chosen")
+ : confidenceLevel.GetName();
+
+ private string SetCurrentConfidenceLevelColorStyle(ToolCatalogItem item) =>
+ $"background-color: {this.GetMinimumProviderConfidence(item).GetColor(this.SettingsManager)};";
+
+ private bool IsToolConfidenceManaged() =>
+ ManagedConfiguration.TryGet(x => x.Tools, x => x.MinimumProviderConfidenceByToolId, out var meta) && meta.IsLocked;
+
+ private ConfidenceLevel GetMinimumProviderConfidence(ToolCatalogItem item) => this.SettingsManager.GetMinimumProviderConfidenceForTool(item.Definition.Id);
+
+ private async Task ChangeMinimumProviderConfidence(ToolCatalogItem item, ConfidenceLevel confidenceLevel)
+ {
+ this.SettingsManager.SetMinimumProviderConfidenceForTool(item.Definition.Id, confidenceLevel);
+ await this.SettingsManager.StoreSettings();
+ this.items = await this.ToolRegistry.GetCatalogAsync(this.ToolRegistry.GetAllDefinitions());
+ await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED);
+ }
+
+ protected override async Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
+ {
+ switch (triggeredEvent)
+ {
+ case Event.CONFIGURATION_CHANGED:
+ this.items = await this.ToolRegistry.GetCatalogAsync(this.ToolRegistry.GetAllDefinitions());
+ await this.InvokeAsync(this.StateHasChanged);
+ break;
+ }
+ }
}
diff --git a/app/MindWork AI Studio/Components/ToolSelection.razor b/app/MindWork AI Studio/Components/ToolSelection.razor
index a98f02e2..23613c26 100644
--- a/app/MindWork AI Studio/Components/ToolSelection.razor
+++ b/app/MindWork AI Studio/Components/ToolSelection.razor
@@ -40,10 +40,12 @@
var isSelected = this.SelectedToolIds.Contains(item.Definition.Id);
var isConfigured = item.ConfigurationState.IsConfigured;
var dependencyHint = this.GetDependencyHint(item.Definition.Id);
+ var providerConfidenceHint = this.GetProviderConfidenceHint(item);
+ var isBlockedByProviderConfidence = this.IsBlockedByProviderConfidence(item);
-
+
@item.Implementation.GetDisplayName()
@@ -59,6 +61,10 @@
{
@dependencyHint
}
+ @if (!string.IsNullOrWhiteSpace(providerConfidenceHint))
+ {
+ @providerConfidenceHint
+ }
}
}
diff --git a/app/MindWork AI Studio/Components/ToolSelection.razor.cs b/app/MindWork AI Studio/Components/ToolSelection.razor.cs
index 7f96fb06..0f81eb0c 100644
--- a/app/MindWork AI Studio/Components/ToolSelection.razor.cs
+++ b/app/MindWork AI Studio/Components/ToolSelection.razor.cs
@@ -43,11 +43,21 @@ public partial class ToolSelection : MSGComponentBase
base.OnParametersSet();
}
+ protected override async Task OnInitializedAsync()
+ {
+ this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED ]);
+ await base.OnInitializedAsync();
+ }
+
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 ConfidenceLevel ProviderConfidence => this.LLMProvider == AIStudio.Settings.Provider.NONE
+ ? ConfidenceLevel.NONE
+ : this.LLMProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level;
+
private async Task ToggleSelection()
{
this.showSelection = !this.showSelection;
@@ -72,6 +82,10 @@ public partial class ToolSelection : MSGComponentBase
private bool IsSelectionLockedByDependency(string toolId) => ToolSelectionRules.IsRequiredBySelectedTools(toolId, this.SelectedToolIds);
+ private ConfidenceLevel GetMinimumProviderConfidence(ToolCatalogItem item) => this.SettingsManager.GetMinimumProviderConfidenceForTool(item.Definition.Id);
+
+ private bool IsBlockedByProviderConfidence(ToolCatalogItem item) => !ToolSelectionRules.IsProviderConfidenceAllowed(this.ProviderConfidence, this.GetMinimumProviderConfidence(item));
+
private string? GetDependencyHint(string toolId)
{
if (toolId == ToolSelectionRules.WEB_SEARCH_TOOL_ID)
@@ -83,6 +97,17 @@ public partial class ToolSelection : MSGComponentBase
return null;
}
+ private string? GetProviderConfidenceHint(ToolCatalogItem item)
+ {
+ if (!this.IsBlockedByProviderConfidence(item))
+ return null;
+
+ return string.Format(
+ this.T("This tool requires provider confidence {0}. The selected provider has {1}."),
+ this.GetMinimumProviderConfidence(item).GetName(),
+ this.ProviderConfidence.GetName());
+ }
+
private async Task OpenSettings(string toolId)
{
var parameters = new DialogParameters
@@ -95,4 +120,15 @@ public partial class ToolSelection : MSGComponentBase
this.catalog = await this.ToolRegistry.GetCatalogAsync(this.Component);
this.StateHasChanged();
}
+
+ protected override async Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
+ {
+ switch (triggeredEvent)
+ {
+ case Event.CONFIGURATION_CHANGED when this.showSelection:
+ this.catalog = await this.ToolRegistry.GetCatalogAsync(this.Component);
+ await this.InvokeAsync(this.StateHasChanged);
+ break;
+ }
+ }
}
diff --git a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
index 5f717d8b..3d7d280e 100644
--- a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
+++ b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
@@ -5,6 +5,7 @@ using System.Text.Json;
using AIStudio.Chat;
using AIStudio.Settings;
+using AIStudio.Tools.ToolCallingSystem;
namespace AIStudio.Provider.OpenAI;
@@ -78,11 +79,12 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, "https
//
// Prepare the tools we want to use:
//
- IList providerTools = modelCapabilities.Contains(Capability.WEB_SEARCH) switch
- {
- true => [ ProviderTools.WEB_SEARCH ],
- _ => []
- };
+ var providerConfidence = this.Provider.GetConfidence(settingsManager).Level;
+ var minimumWebSearchConfidence = settingsManager.GetMinimumProviderConfidenceForTool(ToolSelectionRules.WEB_SEARCH_TOOL_ID);
+ var isWebSearchAllowed = ToolSelectionRules.IsProviderConfidenceAllowed(providerConfidence, minimumWebSearchConfidence);
+ IList providerTools = modelCapabilities.Contains(Capability.WEB_SEARCH) && isWebSearchAllowed
+ ? [ ProviderTools.WEB_SEARCH ]
+ : [];
// Parse the API parameters:
diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutionModels.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutionModels.cs
index f90cf538..05f718c0 100644
--- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutionModels.cs
+++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutionModels.cs
@@ -1,6 +1,7 @@
using System.Text.Json;
using System.Text.Json.Nodes;
+using AIStudio.Provider;
using AIStudio.Settings;
namespace AIStudio.Tools.ToolCallingSystem;
@@ -90,6 +91,8 @@ public sealed class ToolCatalogItem
public required IToolImplementation Implementation { get; init; }
public required ToolConfigurationState ConfigurationState { get; init; }
+
+ public ConfidenceLevel MinimumProviderConfidence { get; init; } = ConfidenceLevel.NONE;
}
public sealed class ToolSelectionState
diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs
index ea4732d2..9b95162f 100644
--- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs
+++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs
@@ -1,6 +1,7 @@
using System.Text.Json;
using AIStudio.Provider;
+using AIStudio.Settings;
using Microsoft.AspNetCore.Hosting;
@@ -9,6 +10,7 @@ namespace AIStudio.Tools.ToolCallingSystem;
public sealed class ToolRegistry
{
private readonly ILogger logger;
+ private readonly SettingsManager settingsManager;
private readonly ToolSettingsService toolSettingsService;
private readonly Dictionary definitionsById = new(StringComparer.Ordinal);
private readonly Dictionary implementationsByKey = new(StringComparer.Ordinal);
@@ -16,10 +18,12 @@ public sealed class ToolRegistry
public ToolRegistry(
IWebHostEnvironment webHostEnvironment,
IEnumerable implementations,
+ SettingsManager settingsManager,
ToolSettingsService toolSettingsService,
ILogger logger)
{
this.logger = logger;
+ this.settingsManager = settingsManager;
this.toolSettingsService = toolSettingsService;
foreach (var implementation in implementations)
@@ -101,6 +105,7 @@ public sealed class ToolRegistry
Definition = definition,
Implementation = implementation,
ConfigurationState = await this.toolSettingsService.GetConfigurationStateAsync(definition, implementation),
+ MinimumProviderConfidence = this.settingsManager.GetMinimumProviderConfidenceForTool(definition.Id),
});
}
@@ -111,6 +116,7 @@ public sealed class ToolRegistry
AIStudio.Tools.Components component,
IEnumerable selectedToolIds,
IReadOnlyCollection modelCapabilities,
+ ConfidenceLevel providerConfidence,
bool isToolSelectionVisible)
{
if (!isToolSelectionVisible)
@@ -131,6 +137,10 @@ public sealed class ToolRegistry
if (!configurationState.IsConfigured)
continue;
+ var minimumToolConfidence = this.settingsManager.GetMinimumProviderConfidenceForTool(definition.Id);
+ if (!ToolSelectionRules.IsProviderConfidenceAllowed(providerConfidence, minimumToolConfidence))
+ continue;
+
result.Add((definition, implementation));
}
diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs
index fc5b9d39..fcd34580 100644
--- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs
+++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Linq;
+using AIStudio.Provider;
+
namespace AIStudio.Tools.ToolCallingSystem;
public static class ToolSelectionRules
@@ -22,4 +24,14 @@ public static class ToolSelectionRules
var normalized = NormalizeSelection(selectedToolIds);
return toolId == READ_WEB_PAGE_TOOL_ID && normalized.Contains(WEB_SEARCH_TOOL_ID);
}
+
+ public static ConfidenceLevel GetDefaultMinimumProviderConfidence(string toolId) => toolId switch
+ {
+ WEB_SEARCH_TOOL_ID => ConfidenceLevel.MEDIUM,
+ READ_WEB_PAGE_TOOL_ID => ConfidenceLevel.MEDIUM,
+ _ => ConfidenceLevel.NONE,
+ };
+
+ public static bool IsProviderConfidenceAllowed(ConfidenceLevel providerConfidence, ConfidenceLevel minimumToolConfidence) =>
+ minimumToolConfidence is ConfidenceLevel.NONE || providerConfidence >= minimumToolConfidence;
}
diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.3.1.md b/app/MindWork AI Studio/wwwroot/changelog/v26.3.1.md
index f4d5274d..ddf9a890 100644
--- a/app/MindWork AI Studio/wwwroot/changelog/v26.3.1.md
+++ b/app/MindWork AI Studio/wwwroot/changelog/v26.3.1.md
@@ -7,6 +7,7 @@
- Added a start-page setting, so AI Studio can now open directly on your preferred page when the app starts. Configuration plugins can also provide and optionally lock this default for organizations.
- Added math rendering in chats for LaTeX display formulas, including block formats such as `$$ ... $$` and `\[ ... \]`.
- Added the latest OpenAI models.
+- Added minimum provider confidence settings for tools, so sensitive tools such as web search can be limited to trusted providers. Configuration plugins can also manage these defaults for organizations.
- Released the document analysis assistant after an intense testing phase.
- Improved enterprise deployment for organizations: administrators can now provide up to 10 centrally managed enterprise configuration slots, use policy files on Linux and macOS, and continue using older configuration formats as a fallback during migration.
- Improved the profile selection for assistants and the chat. You can now explicitly choose between the app default profile, no profile, or a specific profile.
@@ -27,4 +28,4 @@
- Fixed an issue where the app could turn white or appear invisible in certain chats after HTML-like content was shown. Thanks, Inga, for reporting this issue and providing some context on how to reproduce it.
- Fixed security issues in the native app runtime by strengthening how AI Studio creates and protects the secret values used for its internal secure connection.
- Updated several security-sensitive Rust dependencies in the native runtime to address known vulnerabilities.
-- Updated .NET to v9.0.14
\ No newline at end of file
+- Updated .NET to v9.0.14