diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index 78dca7d9..226857eb 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -4396,6 +4396,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1356621346"] = "Create acco -- Load models UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T15352225"] = "Load models" +-- Automatic +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1634363268"] = "Automatic" + -- Disabled (Auto) UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1671157437"] = "Disabled (Auto)" @@ -4405,6 +4408,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1689135032"] = "Add the par -- Hostname UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1727440780"] = "Hostname" +-- Always on +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1761671861"] = "Always on" + -- Reset UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T180921696"] = "Reset" @@ -4450,12 +4456,27 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "No models l -- Instance Name UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2842060373"] = "Instance Name" +-- On by default +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2843289040"] = "On by default" + +-- No reasoning (thinking) capability. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T301695429"] = "No reasoning (thinking) capability." + -- Please be careful: wrong expert settings can break model usage, disable supported features, or make unsupported features appear available. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3079061205"] = "Please be careful: wrong expert settings can break model usage, disable supported features, or make unsupported features appear available." +-- Reasoning (thinking) is available and on unless additional API parameters disable it. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T310420667"] = "Reasoning (thinking) is available and on unless additional API parameters disable it." + -- Disabled UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3217987877"] = "Disabled" +-- The model always uses reasoning (thinking); it cannot be disabled. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3294757333"] = "The model always uses reasoning (thinking); it cannot be disabled." + +-- Can be enabled +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3299454847"] = "Can be enabled" + -- Show Expert Settings UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Show Expert Settings" @@ -4465,12 +4486,21 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3404621481"] = "Audio input -- Invalid JSON: Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3502745319"] = "Invalid JSON: Add the parameters in proper JSON formatting, e.g., \\\"temperature\\\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though." +-- Reasoning (thinking) behavior +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3546126752"] = "Reasoning (thinking) behavior" + +-- Reasoning (thinking) is available, but off unless additional API parameters enable it. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3548835672"] = "Reasoning (thinking) is available, but off unless additional API parameters enable it." + -- Show available models UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Show available models" -- This host uses the model configured at the provider level. No model selection is available. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3783329915"] = "This host uses the model configured at the provider level. No model selection is available." +-- Use detected model behavior. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3790739687"] = "Use detected model behavior." + -- Duplicate key '{0}' found. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3804472591"] = "Duplicate key '{0}' found." @@ -4501,6 +4531,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T808120719"] = "Host" -- Multiple image input UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T858529900"] = "Multiple image input" +-- No reasoning (thinking) +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T87434533"] = "No reasoning (thinking)" + -- Provider UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T900237532"] = "Provider" diff --git a/app/MindWork AI Studio/Dialogs/ProviderDialog.razor b/app/MindWork AI Studio/Dialogs/ProviderDialog.razor index 180968c4..fa6e2afd 100644 --- a/app/MindWork AI Studio/Dialogs/ProviderDialog.razor +++ b/app/MindWork AI Studio/Dialogs/ProviderDialog.razor @@ -170,7 +170,7 @@ @T("Stored default model capabilities may not reflect its full range. Override them here if needed.") - @foreach (var capability in ProviderCapabilityOverrides.SUPPORTED_CAPABILITIES) + @foreach (var capability in SWITCH_CAPABILITY_OVERRIDES) { @@ -199,6 +199,25 @@ } + + + @foreach (var mode in REASONING_OVERRIDE_MODES) + { + + @this.GetReasoningOverrideModeLabel(mode) + + } + + @string.Format(T("The current model uses the {0}."), this.GetCurrentModelApiLabel()) diff --git a/app/MindWork AI Studio/Dialogs/ProviderDialog.razor.cs b/app/MindWork AI Studio/Dialogs/ProviderDialog.razor.cs index 7e1e39da..ecc3c178 100644 --- a/app/MindWork AI Studio/Dialogs/ProviderDialog.razor.cs +++ b/app/MindWork AI Studio/Dialogs/ProviderDialog.razor.cs @@ -19,6 +19,15 @@ namespace AIStudio.Dialogs; /// public partial class ProviderDialog : MSGComponentBase, ISecretId { + private enum ReasoningOverrideMode + { + AUTOMATIC, + NO_REASONING, + CAN_BE_ENABLED, + ON_BY_DEFAULT, + ALWAYS_ON + } + [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!; @@ -95,6 +104,22 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId private ILogger Logger { get; init; } = null!; private static readonly Dictionary SPELLCHECK_ATTRIBUTES = new(); + private static readonly IReadOnlyList SWITCH_CAPABILITY_OVERRIDES = + [ + Capability.AUDIO_INPUT, + Capability.MULTIPLE_IMAGE_INPUT, + Capability.SPEECH_INPUT, + Capability.VIDEO_INPUT + ]; + + private static readonly IReadOnlyList REASONING_OVERRIDE_MODES = + [ + ReasoningOverrideMode.AUTOMATIC, + ReasoningOverrideMode.NO_REASONING, + ReasoningOverrideMode.CAN_BE_ENABLED, + ReasoningOverrideMode.ON_BY_DEFAULT, + ReasoningOverrideMode.ALWAYS_ON + ]; /// /// The list of used instance names. We need this to check for uniqueness. @@ -398,6 +423,80 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId private void ResetCapabilityOverride(Capability capability) => this.capabilityOverrides = this.capabilityOverrides.SetOverride(capability, null); + private ReasoningOverrideMode GetReasoningOverrideMode() + { + var alwaysReasoning = this.capabilityOverrides.GetOverride(Capability.ALWAYS_REASONING); + var optionalReasoning = this.capabilityOverrides.GetOverride(Capability.OPTIONAL_REASONING); + var reasoningByDefault = this.capabilityOverrides.GetOverride(Capability.REASONING_BY_DEFAULT); + if (alwaysReasoning is null && optionalReasoning is null && reasoningByDefault is null) + return ReasoningOverrideMode.AUTOMATIC; + + var capabilities = this.GetCurrentModelCapabilities(); + if (capabilities.Contains(Capability.ALWAYS_REASONING)) + return ReasoningOverrideMode.ALWAYS_ON; + + if (capabilities.Contains(Capability.REASONING_BY_DEFAULT)) + return ReasoningOverrideMode.ON_BY_DEFAULT; + + if (capabilities.Contains(Capability.OPTIONAL_REASONING)) + return ReasoningOverrideMode.CAN_BE_ENABLED; + + return ReasoningOverrideMode.NO_REASONING; + } + + private void SetReasoningOverrideMode(ReasoningOverrideMode mode) + { + this.capabilityOverrides = mode switch + { + ReasoningOverrideMode.AUTOMATIC => this.capabilityOverrides + .SetOverride(Capability.ALWAYS_REASONING, null) + .SetOverride(Capability.OPTIONAL_REASONING, null) + .SetOverride(Capability.REASONING_BY_DEFAULT, null), + + ReasoningOverrideMode.NO_REASONING => this.capabilityOverrides + .SetOverride(Capability.ALWAYS_REASONING, false) + .SetOverride(Capability.OPTIONAL_REASONING, false) + .SetOverride(Capability.REASONING_BY_DEFAULT, false), + + ReasoningOverrideMode.CAN_BE_ENABLED => this.capabilityOverrides + .SetOverride(Capability.ALWAYS_REASONING, false) + .SetOverride(Capability.OPTIONAL_REASONING, true) + .SetOverride(Capability.REASONING_BY_DEFAULT, false), + + ReasoningOverrideMode.ON_BY_DEFAULT => this.capabilityOverrides + .SetOverride(Capability.ALWAYS_REASONING, false) + .SetOverride(Capability.OPTIONAL_REASONING, true) + .SetOverride(Capability.REASONING_BY_DEFAULT, true), + + ReasoningOverrideMode.ALWAYS_ON => this.capabilityOverrides + .SetOverride(Capability.ALWAYS_REASONING, true) + .SetOverride(Capability.OPTIONAL_REASONING, false) + .SetOverride(Capability.REASONING_BY_DEFAULT, false), + + _ => this.capabilityOverrides + }; + } + + private string GetReasoningOverrideModeLabel(ReasoningOverrideMode mode) => mode switch + { + ReasoningOverrideMode.AUTOMATIC => T("Automatic"), + ReasoningOverrideMode.NO_REASONING => T("No reasoning (thinking)"), + ReasoningOverrideMode.CAN_BE_ENABLED => T("Can be enabled"), + ReasoningOverrideMode.ON_BY_DEFAULT => T("On by default"), + ReasoningOverrideMode.ALWAYS_ON => T("Always on"), + _ => mode.ToString() + }; + + private string GetReasoningOverrideModeDescription(ReasoningOverrideMode mode) => mode switch + { + ReasoningOverrideMode.AUTOMATIC => T("Use detected model behavior."), + ReasoningOverrideMode.NO_REASONING => T("No reasoning (thinking) capability."), + ReasoningOverrideMode.CAN_BE_ENABLED => T("Reasoning (thinking) is available, but off unless additional API parameters enable it."), + ReasoningOverrideMode.ON_BY_DEFAULT => T("Reasoning (thinking) is available and on unless additional API parameters disable it."), + ReasoningOverrideMode.ALWAYS_ON => T("The model always uses reasoning (thinking); it cannot be disabled."), + _ => string.Empty + }; + private bool HasCapabilityOverride(Capability capability) => this.capabilityOverrides.GetOverride(capability) is not null; private bool IsCapabilityEnabled(Capability capability) diff --git a/app/MindWork AI Studio/Plugins/configuration/plugin.lua b/app/MindWork AI Studio/Plugins/configuration/plugin.lua index 425ba561..156bb05d 100644 --- a/app/MindWork AI Studio/Plugins/configuration/plugin.lua +++ b/app/MindWork AI Studio/Plugins/configuration/plugin.lua @@ -76,8 +76,11 @@ CONFIG["LLM_PROVIDERS"] = {} -- -- -- Optional: expert capability overrides. -- -- Allowed keys are exactly: --- -- AUDIO_INPUT, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, VIDEO_INPUT, ALWAYS_REASONING +-- -- AUDIO_INPUT, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, VIDEO_INPUT, +-- -- OPTIONAL_REASONING, ALWAYS_REASONING, REASONING_BY_DEFAULT -- -- Allowed values are booleans only. +-- -- For default-on reasoning (rhinking), set OPTIONAL_REASONING and REASONING_BY_DEFAULT to true. +-- -- ALWAYS_REASONING means the model cannot disable reasoning (thinking). -- -- Missing keys keep the automatic capability detection result. -- -- ["CapabilityOverrides"] = { -- -- ["VIDEO_INPUT"] = false, diff --git a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua index 70374d80..53d0937e 100644 --- a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua +++ b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua @@ -4399,7 +4399,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1356621346"] = "Konto erste UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T15352225"] = "Modelle laden" -- Disabled (Auto) -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1671157437"] = "Deaktiviert (Automatisch)" +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1671157437"] = "Deaktiviert (automatisch)" -- Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1689135032"] = "Fügen Sie die Parameter in korrekter JSON-Formatierung hinzu, z. B. \"temperature\": 0.5. Entfernen Sie abschließende Kommas. Die üblichen äußeren geschweiften Klammern {} dürfen dabei jedoch nicht verwendet werden." @@ -4423,7 +4423,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1874348907"] = "Spracheinga UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1936099896"] = "Bitte geben Sie einen Modellnamen ein." -- Enabled (Auto) -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2001330464"] = "Aktiviert (Automatisch)" +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2001330464"] = "Aktiviert (automatisch)" -- The current model uses the {0}. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2029870721"] = "Das aktuelle Modell nutzt die {0}." @@ -4495,7 +4495,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4289835208"] = "Videoeingab UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T504465522"] = "Wir können derzeit nicht mit dem Anbieter kommunizieren, um Modelle zu laden. Bitte versuchen Sie es später erneut." -- Always reasoning -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T641757736"] = "Nachdenkend" +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T641757736"] = "Immer schlussfolgernd (Denken)" -- Host UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T808120719"] = "Host" diff --git a/app/MindWork AI Studio/Settings/ProviderCapabilityOverrides.cs b/app/MindWork AI Studio/Settings/ProviderCapabilityOverrides.cs index 6645da59..6741000b 100644 --- a/app/MindWork AI Studio/Settings/ProviderCapabilityOverrides.cs +++ b/app/MindWork AI Studio/Settings/ProviderCapabilityOverrides.cs @@ -15,13 +15,15 @@ namespace AIStudio.Settings; /// public sealed record ProviderCapabilityOverrides { - public static readonly IReadOnlyList SUPPORTED_CAPABILITIES = + private static readonly IReadOnlyList SUPPORTED_CAPABILITIES = [ Capability.AUDIO_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.SPEECH_INPUT, Capability.VIDEO_INPUT, - Capability.ALWAYS_REASONING + Capability.OPTIONAL_REASONING, + Capability.ALWAYS_REASONING, + Capability.REASONING_BY_DEFAULT ]; [JsonPropertyName("AUDIO_INPUT")] @@ -40,17 +42,27 @@ public sealed record ProviderCapabilityOverrides [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool? VideoInput { get; init; } + [JsonPropertyName("OPTIONAL_REASONING")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? OptionalReasoning { get; init; } + [JsonPropertyName("ALWAYS_REASONING")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool? AlwaysReasoning { get; init; } + [JsonPropertyName("REASONING_BY_DEFAULT")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? ReasoningByDefault { get; init; } + [JsonIgnore] public bool HasOverrides => this.AudioInput is not null || this.MultipleImageInput is not null || this.SpeechInput is not null || this.VideoInput is not null || - this.AlwaysReasoning is not null; + this.OptionalReasoning is not null || + this.AlwaysReasoning is not null || + this.ReasoningByDefault is not null; public bool? GetOverride(Capability capability) => capability switch { @@ -58,7 +70,9 @@ public sealed record ProviderCapabilityOverrides Capability.MULTIPLE_IMAGE_INPUT => this.MultipleImageInput, Capability.SPEECH_INPUT => this.SpeechInput, Capability.VIDEO_INPUT => this.VideoInput, + Capability.OPTIONAL_REASONING => this.OptionalReasoning, Capability.ALWAYS_REASONING => this.AlwaysReasoning, + Capability.REASONING_BY_DEFAULT => this.ReasoningByDefault, _ => null }; @@ -68,7 +82,9 @@ public sealed record ProviderCapabilityOverrides Capability.MULTIPLE_IMAGE_INPUT => this with { MultipleImageInput = value }, Capability.SPEECH_INPUT => this with { SpeechInput = value }, Capability.VIDEO_INPUT => this with { VideoInput = value }, + Capability.OPTIONAL_REASONING => this with { OptionalReasoning = value }, Capability.ALWAYS_REASONING => this with { AlwaysReasoning = value }, + Capability.REASONING_BY_DEFAULT => this with { ReasoningByDefault = value }, _ => this }; @@ -84,9 +100,41 @@ public sealed record ProviderCapabilityOverrides mergedCapabilities.Remove(capability); } + this.NormalizeReasoningCapabilities(mergedCapabilities); return mergedCapabilities; } + private void NormalizeReasoningCapabilities(List capabilities) + { + if (this.AlwaysReasoning == true || + this.AlwaysReasoning is not false && + this.OptionalReasoning is not true && + this.ReasoningByDefault is not true && + capabilities.Contains(Capability.ALWAYS_REASONING)) + { + capabilities.Remove(Capability.OPTIONAL_REASONING); + capabilities.Remove(Capability.REASONING_BY_DEFAULT); + return; + } + + if (this.AlwaysReasoning == false || + this.OptionalReasoning == true || + this.ReasoningByDefault == true) + capabilities.Remove(Capability.ALWAYS_REASONING); + + if (this.OptionalReasoning == false) + { + capabilities.Remove(Capability.REASONING_BY_DEFAULT); + return; + } + + if (this.ReasoningByDefault == true && !capabilities.Contains(Capability.OPTIONAL_REASONING)) + capabilities.Add(Capability.OPTIONAL_REASONING); + + if (!capabilities.Contains(Capability.OPTIONAL_REASONING)) + capabilities.Remove(Capability.REASONING_BY_DEFAULT); + } + public string ExportAsLuaTable(string indentation) { if (!this.HasOverrides) diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.6.3.md b/app/MindWork AI Studio/wwwroot/changelog/v26.6.3.md index 86d23f03..a9371b96 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v26.6.3.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v26.6.3.md @@ -1,5 +1,5 @@ # v26.6.3, build 243 (2026-06-xx xx:xx UTC) -- Added expert capability overrides for providers, so advanced users and configuration plugins can manually enable or disable selected model capabilities when automatic detection needs adjustment. +- Added expert capability overrides for providers, so advanced users and configuration plugins can manually adjust selected model capabilities, including reasoning (thinking) behavior, when automatic detection needs adjustment. - Improved the provider selection by showing small capability icons for supported audio, image, speech, and reasoning features of the selected model. - Improved source links in chat answers when file or document names contained spaces, umlauts, or other special characters. Source entries now open much more reliably for documents with names such as PDFs from shared portals or internal knowledge bases. - Improved all assistants, so running tasks can continue when you leave the assistant and return later.