Added an expert option to override model capabilities (#830)
Some checks are pending
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions

Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
Peer Hogeterp 2026-07-05 12:42:55 +02:00 committed by GitHub
parent 431f0b78d8
commit e3afe7ff9b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 726 additions and 27 deletions

View File

@ -4378,9 +4378,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROMPTINGGUIDELINEDIALOG::T384594633"] = "Th
-- Prompting Guideline
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROMPTINGGUIDELINEDIALOG::T4250996615"] = "Prompting Guideline"
-- Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1017509792"] = "Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model."
-- Hugging Face Inference Provider
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1085481431"] = "Hugging Face Inference Provider"
@ -4399,30 +4396,57 @@ 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)"
-- 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"] = "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."
-- 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"
-- Update
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1847791252"] = "Update"
-- Failed to load the API key from the operating system. The message was: {0}. You might ignore this message and provide the API key again.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1870831108"] = "Failed to load the API key from the operating system. The message was: {0}. You might ignore this message and provide the API key again."
-- Speech input
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1874348907"] = "Speech input"
-- Please enter a model name.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1936099896"] = "Please enter a model name."
-- Enabled (Auto)
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2001330464"] = "Enabled (Auto)"
-- The current model uses the {0}.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2029870721"] = "The current model uses the {0}."
-- Additional API parameters must form a JSON object.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2051143391"] = "Additional API parameters must form a JSON object."
-- Use detected model behavior: {0}.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2141072961"] = "Use detected model behavior: {0}."
-- Model
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2189814010"] = "Model"
-- (Optional) API Key
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2331453405"] = "(Optional) API Key"
-- Enabled
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2626085950"] = "Enabled"
-- Add
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2646845972"] = "Add"
@ -4435,12 +4459,42 @@ 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"
-- Audio input
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"
@ -4450,18 +4504,36 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3783329915"] = "This host u
-- Duplicate key '{0}' found.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3804472591"] = "Duplicate key '{0}' found."
-- Override Model Capabilities
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3904244586"] = "Override Model Capabilities"
-- Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4116737656"] = "Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually."
-- Model selection
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T416738168"] = "Model selection"
-- Stored default model capabilities may not reflect its full range. Override them here if needed.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4217532151"] = "Stored default model capabilities may not reflect its full range. Override them here if needed."
-- Video input
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4289835208"] = "Video input"
-- We are currently unable to communicate with the provider to load models. Please try again later.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T504465522"] = "We are currently unable to communicate with the provider to load models. Please try again later."
-- Always reasoning
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T641757736"] = "Always reasoning"
-- Host
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"

View File

@ -72,6 +72,7 @@ public partial class SettingsPanelProviders : SettingsPanelProviderBase
{ x => x.DataHost, provider.Host },
{ x => x.HFInferenceProviderId, provider.HFInferenceProvider },
{ x => x.AdditionalJsonApiParameters, provider.AdditionalJsonApiParameters },
{ x => x.DataCapabilityOverrides, provider.CapabilityOverrides },
};
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>(T("Edit LLM Provider"), dialogParameters, DialogOptions.FULLSCREEN);

View File

@ -105,7 +105,9 @@
}
else
{
<MudSelect @bind-Value="@this.DataModel"
<MudSelect T="Provider.Model"
Value="@this.DataModel"
ValueChanged="@(async model => await this.OnModelChanged(model))"
OpenIcon="@Icons.Material.Filled.FaceRetouchingNatural" AdornmentColor="Color.Info"
Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingModel">
@foreach (var model in this.availableModels)
@ -157,8 +159,67 @@
</MudButton>
<MudDivider />
<MudCollapse Expanded="@this.showExpertSettings" Class="@this.GetExpertStyles">
<MudJustifiedText Class="mb-5">
@T("Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.")
<MudAlert Severity="Severity.Warning" Class="mb-4">
@T("Please be careful: wrong expert settings can break model usage, disable supported features, or make unsupported features appear available.")
</MudAlert>
<MudText Typo="Typo.subtitle2" Class="mb-2">
@T("Override Model Capabilities")
</MudText>
<MudJustifiedText Typo="Typo.body2" Class="mb-4">
@T("Stored default model capabilities may not reflect its full range. Override them here if needed.")
</MudJustifiedText>
<MudStack Class="mb-4" Spacing="2">
@foreach (var capability in SWITCH_CAPABILITY_OVERRIDES)
{
<MudPaper Outlined="@true" Class="pa-3">
<MudStack Row="@true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Wrap="Wrap.Wrap">
<MudStack Spacing="0">
<MudText Typo="Typo.body2">
@this.GetCapabilityOverrideLabel(capability)
</MudText>
<MudText Typo="Typo.caption" Color="Color.Secondary">
@this.GetCapabilityEffectiveLabel(capability)
</MudText>
</MudStack>
<MudStack Row="@true" AlignItems="AlignItems.Center" Spacing="2">
<MudSwitch T="bool"
Value="@this.IsCapabilityEnabled(capability)"
ValueChanged="@(value => this.OnCapabilitySwitchChanged(capability, value))"
Color="Color.Primary" />
<MudButton Variant="Variant.Outlined"
Color="Color.Default"
Size="Size.Small"
StartIcon="@Icons.Material.Filled.RestartAlt"
Disabled="@(!this.HasCapabilityOverride(capability))"
OnClick="@(() => this.ResetCapabilityOverride(capability))">
@T("Reset")
</MudButton>
</MudStack>
</MudStack>
</MudPaper>
}
<MudPaper Outlined="@true" Class="pa-3">
<MudSelect T="ReasoningOverrideMode"
Value="@this.GetReasoningOverrideMode()"
ValueChanged="@this.SetReasoningOverrideMode"
Label="@T("Reasoning (thinking) behavior")"
HelperText="@this.GetReasoningOverrideModeDescription(this.GetReasoningOverrideMode())"
Variant="Variant.Outlined"
Margin="Margin.Dense"
OpenIcon="@Icons.Material.Filled.Psychology"
AdornmentColor="Color.Info"
Adornment="Adornment.Start">
@foreach (var mode in REASONING_OVERRIDE_MODES)
{
<MudSelectItem Value="@mode">
@this.GetReasoningOverrideModeLabel(mode)
</MudSelectItem>
}
</MudSelect>
</MudPaper>
</MudStack>
<MudJustifiedText Typo="Typo.body2" Class="mb-4">
@string.Format(T("The current model uses the {0}."), this.GetCurrentModelApiLabel())
</MudJustifiedText>
<MudTextField T="string" Label=@T("Additional API parameters") Variant="Variant.Outlined" Lines="4" AutoGrow="true" MaxLines="10" HelperText=@T("""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.""") Placeholder="@GetPlaceholderExpertSettings" @bind-Value="@this.AdditionalJsonApiParameters" Immediate="true" Validation="@this.ValidateAdditionalJsonApiParameters" OnBlur="@this.OnInputChangeExpertSettings"/>
</MudCollapse>

View File

@ -4,6 +4,7 @@ using System.Text.Json;
using AIStudio.Components;
using AIStudio.Provider;
using AIStudio.Provider.HuggingFace;
using AIStudio.Settings;
using AIStudio.Tools.Services;
using AIStudio.Tools.Validation;
@ -18,6 +19,15 @@ namespace AIStudio.Dialogs;
/// </summary>
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!;
@ -84,6 +94,9 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
[Parameter]
public string AdditionalJsonApiParameters { get; set; } = string.Empty;
[Parameter]
public ProviderCapabilityOverrides? DataCapabilityOverrides { get; set; }
[Inject]
private RustService RustService { get; init; } = null!;
@ -91,6 +104,22 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
private ILogger<ProviderDialog> Logger { get; init; } = null!;
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
private static readonly IReadOnlyList<Capability> SWITCH_CAPABILITY_OVERRIDES =
[
Capability.AUDIO_INPUT,
Capability.MULTIPLE_IMAGE_INPUT,
Capability.SPEECH_INPUT,
Capability.VIDEO_INPUT
];
private static readonly IReadOnlyList<ReasoningOverrideMode> REASONING_OVERRIDE_MODES =
[
ReasoningOverrideMode.AUTOMATIC,
ReasoningOverrideMode.NO_REASONING,
ReasoningOverrideMode.CAN_BE_ENABLED,
ReasoningOverrideMode.ON_BY_DEFAULT,
ReasoningOverrideMode.ALWAYS_ON
];
/// <summary>
/// The list of used instance names. We need this to check for uniqueness.
@ -106,6 +135,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
private string dataLoadingModelsIssue = string.Empty;
private bool usesLegacySystemModelFallback;
private bool showExpertSettings;
private ProviderCapabilityOverrides capabilityOverrides = new();
// We get the form reference from Blazor code to validate it manually:
private MudForm form = null!;
@ -160,6 +190,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
Host = this.DataHost,
HFInferenceProvider = this.HFInferenceProviderId,
AdditionalJsonApiParameters = this.AdditionalJsonApiParameters,
CapabilityOverrides = this.capabilityOverrides.HasOverrides ? this.capabilityOverrides : null,
};
}
@ -178,7 +209,8 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
this.UsedInstanceNames = this.SettingsManager.ConfigurationData.Providers.Select(x => x.InstanceName.ToLowerInvariant()).ToList();
#pragma warning restore MWAIS0001
this.showExpertSettings = !string.IsNullOrWhiteSpace(this.AdditionalJsonApiParameters);
this.capabilityOverrides = this.DataCapabilityOverrides ?? new();
this.showExpertSettings = !string.IsNullOrWhiteSpace(this.AdditionalJsonApiParameters) || this.capabilityOverrides.HasOverrides;
// When editing, we need to load the data:
if(this.IsEditing)
@ -300,11 +332,19 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
this.DataHost = selectedHost;
this.DataModel = default;
this.dataManuallyModel = string.Empty;
this.capabilityOverrides = new();
this.availableModels.Clear();
this.dataLoadingModelsIssue = string.Empty;
this.usesLegacySystemModelFallback = false;
}
private Task OnModelChanged(Model selectedModel)
{
this.DataModel = selectedModel;
this.capabilityOverrides = new();
return Task.CompletedTask;
}
private async Task ReloadModels()
{
this.dataLoadingModelsIssue = string.Empty;
@ -369,6 +409,156 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
private void ToggleExpertSettings() => this.showExpertSettings = !this.showExpertSettings;
private void SetCapabilityOverride(Capability capability, bool value)
{
this.capabilityOverrides = this.capabilityOverrides.SetOverride(capability, value);
}
private Task OnCapabilitySwitchChanged(Capability capability, bool value)
{
this.SetCapabilityOverride(capability, value);
return Task.CompletedTask;
}
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 ReasoningOverrideMode GetAutomaticReasoningOverrideMode()
{
var capabilities = this.GetAutomaticModelCapabilities();
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 => string.Format(T("Use detected model behavior: {0}."), this.GetReasoningOverrideModeLabel(this.GetAutomaticReasoningOverrideMode())),
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)
{
var capabilities = this.GetCurrentModelCapabilities();
return capabilities.Contains(capability);
}
private string GetCapabilityEffectiveLabel(Capability capability)
{
var isEnabled = this.IsCapabilityEnabled(capability);
if (this.HasCapabilityOverride(capability))
return isEnabled ? T("Enabled") : T("Disabled");
return isEnabled ? T("Enabled (Auto)") : T("Disabled (Auto)");
}
private List<Capability> GetCurrentModelCapabilities()
{
var currentProviderSettings = this.CreateProviderSettings();
return currentProviderSettings.GetModelCapabilities();
}
private List<Capability> GetAutomaticModelCapabilities() => this.DataLLMProvider.GetModelCapabilities(this.DataModel);
private string GetCurrentModelApiLabel()
{
var capabilities = this.GetCurrentModelCapabilities();
if (capabilities.Contains(Capability.RESPONSES_API))
return "Responses API";
if (capabilities.Contains(Capability.CHAT_COMPLETION_API))
return "Chat Completions API";
return "Unknown";
}
private string GetCapabilityOverrideLabel(Capability capability) => capability switch
{
Capability.AUDIO_INPUT => T("Audio input"),
Capability.MULTIPLE_IMAGE_INPUT => T("Multiple image input"),
Capability.SPEECH_INPUT => T("Speech input"),
Capability.VIDEO_INPUT => T("Video input"),
Capability.ALWAYS_REASONING => T("Always reasoning"),
_ => capability.ToString()
};
private void OnInputChangeExpertSettings()
{
this.AdditionalJsonApiParameters = NormalizeAdditionalJsonApiParameters(this.AdditionalJsonApiParameters)

View File

@ -74,6 +74,18 @@ CONFIG["LLM_PROVIDERS"] = {}
-- -- Please do not add the enclosing curly braces {} here. Also, no trailing comma is allowed.
-- ["AdditionalJsonApiParameters"] = "",
--
-- -- Optional: expert capability overrides.
-- -- Allowed keys are exactly:
-- -- 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,
-- -- },
--
-- -- Optional: Hugging Face inference provider. Only relevant for UsedLLMProvider = HUGGINGFACE.
-- -- Allowed values are: CEREBRAS, NEBIUS_AI_STUDIO, SAMBANOVA, NOVITA, HYPERBOLIC, TOGETHER_AI, FIREWORKS, HF_INFERENCE_API
-- -- ["HFInferenceProvider"] = "NOVITA",

View File

@ -4380,9 +4380,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROMPTINGGUIDELINEDIALOG::T384594633"] = "De
-- Prompting Guideline
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROMPTINGGUIDELINEDIALOG::T4250996615"] = "Prompting-Leitfaden"
-- Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1017509792"] = "Bitte beachten Sie: Dieser Bereich ist nur für Expertinnen und Experten. Sie sind dafür verantwortlich, die Korrektheit der zusätzlichen Parameter zu überprüfen, die Sie beim APIAufruf angeben. Standardmäßig verwendet AI Studio die OpenAIkompatible Chat Completions-API, sofern diese vom zugrunde liegenden Dienst und Modell unterstützt wird."
-- Hugging Face Inference Provider
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1085481431"] = "Hugging Face Inferenz-Anbieter"
@ -4401,30 +4398,57 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1356621346"] = "Konto erste
-- Load models
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T15352225"] = "Modelle laden"
-- Automatic
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1634363268"] = "Automatisch"
-- Disabled (Auto)
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."
-- Hostname
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1727440780"] = "Hostname"
-- Always on
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1761671861"] = "Immer an"
-- Reset
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T180921696"] = "Zurücksetzen"
-- Update
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1847791252"] = "Aktualisieren"
-- Failed to load the API key from the operating system. The message was: {0}. You might ignore this message and provide the API key again.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1870831108"] = "Der API-Schlüssel konnte nicht vom Betriebssystem geladen werden. Die Meldung war: {0}. Sie können diese Meldung ignorieren und den API-Schlüssel erneut eingeben."
-- Speech input
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1874348907"] = "Spracheingabe"
-- Please enter a model name.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1936099896"] = "Bitte geben Sie einen Modellnamen ein."
-- Enabled (Auto)
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}."
-- Additional API parameters must form a JSON object.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2051143391"] = "Zusätzliche API-Parameter müssen ein JSON-Objekt bilden."
-- Use detected model behavior: {0}.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2141072961"] = "Erkanntes Modellverhalten verwenden: {0}"
-- Model
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2189814010"] = "Modell"
-- (Optional) API Key
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2331453405"] = "(Optional) API-Schlüssel"
-- Enabled
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2626085950"] = "Aktiviert"
-- Add
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2646845972"] = "Hinzufügen"
@ -4437,12 +4461,42 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "Keine Model
-- Instance Name
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2842060373"] = "Instanzname"
-- On by default
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2843289040"] = "Standardmäßig aktiviert"
-- No reasoning (thinking) capability.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T301695429"] = "Keine Fähigkeit für Schlussfolgerungen (Denken)."
-- 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"] = "Achtung: Fehlerhafte Experteneinstellungen können die Modellnutzung beeinträchtigen, unterstützte Funktionen deaktivieren oder nicht unterstützte Funktionen als verfügbar erscheinen lassen."
-- Reasoning (thinking) is available and on unless additional API parameters disable it.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T310420667"] = "Schlussfolgerungen (Denken) sind verfügbar und aktiviert, sofern es nicht durch zusätzliche API-Parameter deaktiviert wird."
-- Disabled
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3217987877"] = "Deaktiviert"
-- The model always uses reasoning (thinking); it cannot be disabled.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3294757333"] = "Das Modell verwendet immer Schlussfolgerungen (Denken); es kann nicht deaktiviert werden."
-- Can be enabled
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3299454847"] = "Kann aktiviert werden"
-- Show Expert Settings
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Experten-Einstellungen anzeigen"
-- Audio input
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3404621481"] = "Audioeingabe"
-- 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"] = "Ungültiges JSON: Fügen Sie die Parameter in korrektem JSON-Format hinzu, z. B. \"temperature\": 0.5. Entfernen Sie abschließende Kommas. Die üblichen umgebenden geschweiften Klammern {} dürfen jedoch nicht verwendet werden."
-- Reasoning (thinking) behavior
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3546126752"] = "Verhalten bezüglich Schlussfolgerungen (Denken)"
-- Reasoning (thinking) is available, but off unless additional API parameters enable it.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3548835672"] = "Schlussfolgerungen (Denken) sind verfügbar, aber ausgeschaltet, sofern sie nicht durch zusätzliche API-Parameter aktiviert werden."
-- Show available models
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Verfügbare Modelle anzeigen"
@ -4452,18 +4506,36 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3783329915"] = "Dieser Host
-- Duplicate key '{0}' found.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3804472591"] = "Doppelter Schlüssel '{0}' gefunden."
-- Override Model Capabilities
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3904244586"] = "Modellfähigkeiten überschreiben"
-- Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4116737656"] = "Derzeit können wir die Modelle für den ausgewählten Anbieter und/oder Host nicht abfragen. Bitte geben Sie daher den Modellnamen manuell ein."
-- Model selection
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T416738168"] = "Modellauswahl"
-- Stored default model capabilities may not reflect its full range. Override them here if needed.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4217532151"] = "Die gespeicherten Standardfähigkeiten des Modells entsprechen möglicherweise nicht dessen vollständigem Funktionsumfang. Überschreiben Sie sie hier bei Bedarf."
-- Video input
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4289835208"] = "Videoeingabe"
-- We are currently unable to communicate with the provider to load models. Please try again later.
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"] = "Immer schlussfolgernd (Denken)"
-- Host
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T808120719"] = "Host"
-- Multiple image input
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T858529900"] = "Bildeingabe"
-- No reasoning (thinking)
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T87434533"] = "Keine Schlussfolgerungen (Denken)"
-- Provider
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T900237532"] = "Anbieter"

View File

@ -2436,23 +2436,23 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROFILESELECTION::T3654011106"] = "Open P
-- You can switch between your profiles here
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROFILESELECTION::T918741365"] = "You can switch between your profiles here"
-- Uses reasoning
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T163305901"] = "Uses reasoning"
-- Uses reasoning from provider settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T1646870133"] = "Uses reasoning from provider settings"
-- Audio input possible
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T1742581112"] = "Audio input possible"
-- Uses reasoning (thinking)
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T2196970948"] = "Uses reasoning (thinking)"
-- Image input possible
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T2685487365"] = "Image input possible"
-- Speech input possible
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T3005724142"] = "Speech input possible"
-- Uses reasoning by default
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T3202628203"] = "Uses reasoning by default"
-- Uses reasoning (thinking) by default
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T3891860124"] = "Uses reasoning (thinking) by default"
-- Uses reasoning (thinking) configured by settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T4279448758"] = "Uses reasoning (thinking) configured by settings"
-- Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T900237532"] = "Provider"
@ -4380,9 +4380,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROMPTINGGUIDELINEDIALOG::T384594633"] = "Th
-- Prompting Guideline
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROMPTINGGUIDELINEDIALOG::T4250996615"] = "Prompting Guideline"
-- Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1017509792"] = "Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model."
-- Hugging Face Inference Provider
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1085481431"] = "Hugging Face Inference Provider"
@ -4401,30 +4398,57 @@ 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)"
-- 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"] = "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."
-- 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"
-- Update
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1847791252"] = "Update"
-- Failed to load the API key from the operating system. The message was: {0}. You might ignore this message and provide the API key again.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1870831108"] = "Failed to load the API key from the operating system. The message was: {0}. You might ignore this message and provide the API key again."
-- Speech input
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1874348907"] = "Speech input"
-- Please enter a model name.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1936099896"] = "Please enter a model name."
-- Enabled (Auto)
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2001330464"] = "Enabled (Auto)"
-- The current model uses the {0}.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2029870721"] = "The current model uses the {0}."
-- Additional API parameters must form a JSON object.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2051143391"] = "Additional API parameters must form a JSON object."
-- Use detected model behavior: {0}.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2141072961"] = "Use detected model behavior: {0}."
-- Model
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2189814010"] = "Model"
-- (Optional) API Key
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2331453405"] = "(Optional) API Key"
-- Enabled
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2626085950"] = "Enabled"
-- Add
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2646845972"] = "Add"
@ -4437,12 +4461,42 @@ 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"
-- Audio input
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"
@ -4452,18 +4506,36 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3783329915"] = "This host u
-- Duplicate key '{0}' found.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3804472591"] = "Duplicate key '{0}' found."
-- Override Model Capabilities
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3904244586"] = "Override Model Capabilities"
-- Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4116737656"] = "Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually."
-- Model selection
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T416738168"] = "Model selection"
-- Stored default model capabilities may not reflect its full range. Override them here if needed.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4217532151"] = "Stored default model capabilities may not reflect its full range. Override them here if needed."
-- Video input
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4289835208"] = "Video input"
-- We are currently unable to communicate with the provider to load models. Please try again later.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T504465522"] = "We are currently unable to communicate with the provider to load models. Please try again later."
-- Always reasoning
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T641757736"] = "Always reasoning"
-- Host
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"

View File

@ -33,7 +33,8 @@ public sealed record Provider(
string Hostname = "http://localhost:1234",
Host Host = Host.NONE,
HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE,
string AdditionalJsonApiParameters = "") : ConfigurationBaseObject, ISecretId
string AdditionalJsonApiParameters = "",
ProviderCapabilityOverrides? CapabilityOverrides = null) : ConfigurationBaseObject, ISecretId
{
private static readonly ILogger<Provider> LOGGER = Program.LOGGER_FACTORY.CreateLogger<Provider>();
@ -152,6 +153,8 @@ public sealed record Provider(
additionalJsonApiParameters = string.Empty;
}
var capabilityOverrides = ProviderCapabilityOverrides.TryParseFromLuaTable(idx, table, configPluginId, LOGGER);
provider = new Provider
{
Num = 0, // will be set later by the PluginConfigurationObject
@ -166,6 +169,7 @@ public sealed record Provider(
Host = host,
HFInferenceProvider = hfInferenceProvider,
AdditionalJsonApiParameters = additionalJsonApiParameters,
CapabilityOverrides = capabilityOverrides,
};
// Handle encrypted API key if present:
@ -241,6 +245,8 @@ public sealed record Provider(
""";
}
var capabilityOverridesLine = this.CapabilityOverrides?.ExportAsLuaTable(" ") ?? string.Empty;
return $$"""
CONFIG["LLM_PROVIDERS"][#CONFIG["LLM_PROVIDERS"]+1] = {
["Id"] = "{{Guid.NewGuid().ToString()}}",
@ -252,6 +258,7 @@ public sealed record Provider(
{{hfInferenceProviderLine}}
{{apiKeyLine}}
["AdditionalJsonApiParameters"] = "{{LuaTools.EscapeLuaString(this.AdditionalJsonApiParameters)}}",
{{capabilityOverridesLine}}
["Model"] = {
["Id"] = "{{LuaTools.EscapeLuaString(this.Model.Id)}}",
["DisplayName"] = "{{LuaTools.EscapeLuaString(this.Model.DisplayName ?? this.Model.Id)}}",

View File

@ -0,0 +1,207 @@
using System.Text;
using System.Text.Json.Serialization;
using AIStudio.Provider;
using Lua;
using LuaTable = Lua.LuaTable;
namespace AIStudio.Settings;
/// <summary>
/// Optional expert capability overrides for a configured LLM provider.
/// Missing values keep the automatic capability detection result.
/// </summary>
public sealed record ProviderCapabilityOverrides
{
private static readonly IReadOnlyList<Capability> SUPPORTED_CAPABILITIES =
[
Capability.AUDIO_INPUT,
Capability.MULTIPLE_IMAGE_INPUT,
Capability.SPEECH_INPUT,
Capability.VIDEO_INPUT,
Capability.OPTIONAL_REASONING,
Capability.ALWAYS_REASONING,
Capability.REASONING_BY_DEFAULT
];
[JsonPropertyName("AUDIO_INPUT")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public bool? AudioInput { get; init; }
[JsonPropertyName("MULTIPLE_IMAGE_INPUT")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public bool? MultipleImageInput { get; init; }
[JsonPropertyName("SPEECH_INPUT")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public bool? SpeechInput { get; init; }
[JsonPropertyName("VIDEO_INPUT")]
[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.OptionalReasoning is not null ||
this.AlwaysReasoning is not null ||
this.ReasoningByDefault is not null;
public bool? GetOverride(Capability capability) => capability switch
{
Capability.AUDIO_INPUT => this.AudioInput,
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
};
public ProviderCapabilityOverrides SetOverride(Capability capability, bool? value) => capability switch
{
Capability.AUDIO_INPUT => this with { AudioInput = value },
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
};
public List<Capability> ApplyTo(IEnumerable<Capability> automaticCapabilities)
{
var mergedCapabilities = automaticCapabilities.Distinct().ToList();
foreach (var capability in SUPPORTED_CAPABILITIES)
{
var overrideValue = this.GetOverride(capability);
if (overrideValue == true && !mergedCapabilities.Contains(capability))
mergedCapabilities.Add(capability);
else if (overrideValue == false)
mergedCapabilities.Remove(capability);
}
this.NormalizeReasoningCapabilities(mergedCapabilities);
return mergedCapabilities;
}
private void NormalizeReasoningCapabilities(List<Capability> 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)
return string.Empty;
var builder = new StringBuilder();
builder.AppendLine($@"{indentation}[""CapabilityOverrides""] = {{");
foreach (var capability in SUPPORTED_CAPABILITIES)
{
var overrideValue = this.GetOverride(capability);
if (overrideValue is null)
continue;
builder.AppendLine($@"{indentation} [""{capability}""] = {overrideValue.Value.ToString().ToLowerInvariant()},");
}
builder.Append($@"{indentation}}},");
return builder.ToString();
}
public static ProviderCapabilityOverrides? TryParseFromLuaTable(int idx, LuaTable providerTable, Guid configPluginId, ILogger logger)
{
if (!providerTable.TryGetValue("CapabilityOverrides", out var capabilityOverridesValue))
return null;
if (capabilityOverridesValue.Type is not LuaValueType.Table || !capabilityOverridesValue.TryRead<LuaTable>(out var capabilityOverridesTable))
{
logger.LogWarning("The configured provider {ProviderIndex} contains an invalid CapabilityOverrides table. Automatic capability detection will be used instead. (Plugin ID: {PluginId})", idx, configPluginId);
return null;
}
var result = new ProviderCapabilityOverrides();
var previousKey = LuaValue.Nil;
while (capabilityOverridesTable.TryGetNext(previousKey, out var pair))
{
previousKey = pair.Key;
if (!pair.Key.TryRead<string>(out var keyText))
{
logger.LogWarning("The configured provider {ProviderIndex} contains a CapabilityOverrides entry with a non-string key. The entry will be ignored. (Plugin ID: {PluginId})", idx, configPluginId);
continue;
}
if (!TryParseSupportedCapability(keyText, out var capability))
{
logger.LogWarning("The configured provider {ProviderIndex} contains an unsupported capability override '{CapabilityKey}'. The entry will be ignored. (Plugin ID: {PluginId})", idx, keyText, configPluginId);
continue;
}
if (!pair.Value.TryRead<bool>(out var overrideValue))
{
logger.LogWarning("The configured provider {ProviderIndex} contains a non-boolean capability override for '{CapabilityKey}'. Automatic capability detection will be used for that capability. (Plugin ID: {PluginId})", idx, keyText, configPluginId);
continue;
}
result = result.SetOverride(capability, overrideValue);
}
return result.HasOverrides ? result : null;
}
private static bool TryParseSupportedCapability(string capabilityKey, out Capability capability)
{
capability = Capability.NONE;
if (!Enum.TryParse(capabilityKey, true, out capability))
return false;
return SUPPORTED_CAPABILITIES.Contains(capability);
}
}

View File

@ -9,7 +9,11 @@ public static partial class ProviderExtensions
/// </summary>
/// <param name="provider">The configured provider.</param>
/// <returns>The capabilities of the configured model.</returns>
public static List<Capability> GetModelCapabilities(this Provider provider) => provider.UsedLLMProvider.GetModelCapabilities(provider.Model);
public static List<Capability> GetModelCapabilities(this Provider provider)
{
var automaticCapabilities = provider.UsedLLMProvider.GetModelCapabilities(provider.Model);
return provider.CapabilityOverrides?.ApplyTo(automaticCapabilities) ?? automaticCapabilities;
}
/// <summary>
/// Get the capabilities of a model for a specific provider.

View File

@ -1,4 +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 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.