mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-09-27 02:53:38 +00:00
Fix document analysis provider selection freezes
This commit is contained in:
parent
6e735523c6
commit
894c509f4d
@ -184,7 +184,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
this.formChangeTimer.Elapsed += (_, _) =>
|
||||
{
|
||||
this.formChangeTimer.Stop();
|
||||
this.OnFormChange().Observe($"{nameof(AssistantBase<TSettings>)}: handling a form change");
|
||||
this.InvokeAsync(this.OnFormChange).Observe($"{nameof(AssistantBase<TSettings>)}: handling a form change");
|
||||
};
|
||||
|
||||
this.MightPreselectValues();
|
||||
|
||||
@ -104,13 +104,13 @@ else
|
||||
@T("Note: This setting only takes effect when this policy is exported and distributed via a configuration plugin to other users. When enabled, users will only see the document selection interface and cannot view or modify the policy details. This setting does NOT affect your local view - you will always see the full policy definition for policies you create.")
|
||||
</MudJustifiedText>
|
||||
|
||||
<ConfigurationMinConfidenceSelection Disabled="@(() => this.IsNoPolicySelectedOrProtected)" RestrictToGlobalMinimumConfidence="true" SelectedValue="@(() => this.policyMinimumProviderConfidence)" SelectionUpdateAsync="@(async level => await this.PolicyMinimumConfidenceWasChangedAsync(level))" />
|
||||
<ConfigurationMinConfidenceSelection Disabled="@(() => this.IsNoPolicySelectedOrProtected)" RestrictToGlobalMinimumConfidence="true" SelectedValue="@(() => this.policyMinimumProviderConfidence)" SelectionUpdate="@this.PolicyMinimumConfidenceWasChanged" />
|
||||
|
||||
<ToolSelectionField Component="@this.Component" SelectedToolIds="@this.policyAllowedToolIds" SelectedToolIdsChanged="@this.PolicyAllowedToolsWasChangedAsync" Disabled="@this.IsNoPolicySelectedOrProtected" Label="@T("Tools this policy permits")" Help="@T("Only the tools selected here can be used by the AI for an analysis with this policy. Every tool still has to meet the confidence requirements of the selected provider, so a tool may remain unavailable even when this policy permits it.")"/>
|
||||
<ToolSelectionField Component="@this.Component" SelectedToolIds="@this.policyAllowedToolIds" SelectedToolIdsChanged="@this.PolicyAllowedToolsWasChanged" Disabled="@this.IsNoPolicySelectedOrProtected" Label="@T("Tools this policy permits")" Help="@T("Only the tools selected here can be used by the AI for an analysis with this policy. Every tool still has to meet the confidence requirements of the selected provider, so a tool may remain unavailable even when this policy permits it.")"/>
|
||||
|
||||
<ConfigurationProviderSelection Component="Components.DOCUMENT_ANALYSIS_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => this.IsNoPolicySelectedOrProtected)" SelectedValue="@(() => this.policyPreselectedProviderId)" SelectionUpdate="@this.PolicyPreselectedProviderWasChanged" ExplicitMinimumConfidence="@this.GetPolicyMinimumConfidenceLevel()"/>
|
||||
|
||||
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => this.IsNoPolicySelected)" SelectedValue="@(() => this.policyPreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdateAsync="@(async selection => await this.PolicyPreselectedProfileWasChangedAsync(selection))" OptionHelp="@T("Choose whether the policy should use the app default profile, no profile, or a specific profile.")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => this.IsNoPolicySelected)" SelectedValue="@(() => this.policyPreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@this.PolicyPreselectedProfileWasChanged" OptionHelp="@T("Choose whether the policy should use the app default profile, no profile, or a specific profile.")"/>
|
||||
|
||||
<MudTextSwitch Disabled="@(this.IsNoPolicySelected || (this.selectedPolicy?.IsEnterpriseConfiguration ?? true))" Label="@T("Would you like to protect this policy so that you cannot accidentally edit or delete it?")" Value="@this.policyIsProtected" ValueChanged="async state => await this.PolicyProtectionWasChanged(state)" LabelOn="@T("Yes, protect this policy")" LabelOff="@T("No, the policy can be edited")" />
|
||||
|
||||
|
||||
@ -180,6 +180,7 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
protected override void ResetForm()
|
||||
{
|
||||
this.loadedDocumentPaths.Clear();
|
||||
this.policyNameWasEdited = false;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.policyName = string.Empty;
|
||||
@ -220,6 +221,7 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
this.policyAllowedToolIds = [..this.selectedPolicy.AllowedToolIds];
|
||||
this.policyPreselectedProviderId = this.selectedPolicy.PreselectedProvider;
|
||||
this.policyPreselectedProfile = ProfilePreselection.FromStoredValue(this.selectedPolicy.PreselectedProfile);
|
||||
this.policyNameWasEdited = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -259,6 +261,7 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
return;
|
||||
|
||||
// The preselected profile is always user-adjustable, even for protected policies and enterprise configurations:
|
||||
var hasChanges = this.selectedPolicy.PreselectedProfile != this.policyPreselectedProfile;
|
||||
this.selectedPolicy.PreselectedProfile = this.policyPreselectedProfile;
|
||||
|
||||
// Enterprise configurations cannot be modified at all:
|
||||
@ -268,6 +271,17 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
var canEditProtectedFields = force || (!this.selectedPolicy.IsProtected && !this.policyIsProtected);
|
||||
if (canEditProtectedFields)
|
||||
{
|
||||
hasChanges = hasChanges
|
||||
|| this.policyNameWasEdited
|
||||
|| this.selectedPolicy.PreselectedProvider != this.policyPreselectedProviderId
|
||||
|| this.selectedPolicy.PolicyDescription != this.policyDescription
|
||||
|| this.selectedPolicy.IsProtected != this.policyIsProtected
|
||||
|| this.selectedPolicy.HidePolicyDefinition != this.policyHidePolicyDefinition
|
||||
|| this.selectedPolicy.AnalysisRules != this.policyAnalysisRules
|
||||
|| this.selectedPolicy.OutputRules != this.policyOutputRules
|
||||
|| this.selectedPolicy.MinimumProviderConfidence != this.policyMinimumProviderConfidence
|
||||
|| !this.selectedPolicy.AllowedToolIds.SetEquals(this.policyAllowedToolIds);
|
||||
|
||||
this.selectedPolicy.PreselectedProvider = this.policyPreselectedProviderId;
|
||||
this.selectedPolicy.PolicyName = this.policyName;
|
||||
this.selectedPolicy.PolicyDescription = this.policyDescription;
|
||||
@ -279,7 +293,11 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
this.selectedPolicy.AllowedToolIds = [..this.policyAllowedToolIds];
|
||||
}
|
||||
|
||||
if (!hasChanges)
|
||||
return;
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
this.policyNameWasEdited = false;
|
||||
}
|
||||
|
||||
private DataDocumentAnalysisPolicy? selectedPolicy;
|
||||
@ -298,6 +316,7 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
/// </remarks>
|
||||
private bool documentSelectionExpanded;
|
||||
private string policyName = string.Empty;
|
||||
private bool policyNameWasEdited;
|
||||
private string policyDescription = string.Empty;
|
||||
private string policyAnalysisRules = string.Empty;
|
||||
private string policyOutputRules = string.Empty;
|
||||
@ -477,6 +496,7 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
return;
|
||||
|
||||
this.selectedPolicy.PolicyName = this.policyName;
|
||||
this.policyNameWasEdited = true;
|
||||
}
|
||||
|
||||
private async Task PolicyProtectionWasChanged(bool state)
|
||||
@ -488,7 +508,6 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
return;
|
||||
|
||||
this.policyIsProtected = state;
|
||||
this.selectedPolicy.IsProtected = state;
|
||||
this.policyDefinitionExpanded = !state;
|
||||
this.documentSelectionExpanded = state;
|
||||
await this.AutoSave(true);
|
||||
@ -503,7 +522,6 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
return;
|
||||
|
||||
this.policyHidePolicyDefinition = state;
|
||||
this.selectedPolicy.HidePolicyDefinition = state;
|
||||
await this.AutoSave(true);
|
||||
}
|
||||
|
||||
@ -580,16 +598,18 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
/// <summary>
|
||||
/// Takes over the tools this policy permits.
|
||||
/// </summary>
|
||||
private async Task PolicyAllowedToolsWasChangedAsync(HashSet<string> allowedToolIds)
|
||||
private void PolicyAllowedToolsWasChanged(HashSet<string> allowedToolIds)
|
||||
{
|
||||
this.policyAllowedToolIds = allowedToolIds;
|
||||
await this.AutoSave();
|
||||
if (this.selectedPolicy is not null)
|
||||
this.selectedPolicy.AllowedToolIds = [..allowedToolIds];
|
||||
}
|
||||
|
||||
private async Task PolicyMinimumConfidenceWasChangedAsync(ConfidenceLevel level)
|
||||
private void PolicyMinimumConfidenceWasChanged(ConfidenceLevel level)
|
||||
{
|
||||
this.policyMinimumProviderConfidence = level;
|
||||
await this.AutoSave();
|
||||
if (this.selectedPolicy is not null)
|
||||
this.selectedPolicy.MinimumProviderConfidence = level;
|
||||
|
||||
this.ApplyPolicyPreselection();
|
||||
}
|
||||
@ -605,14 +625,13 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
this.ApplyPolicyPreselection();
|
||||
}
|
||||
|
||||
private async Task PolicyPreselectedProfileWasChangedAsync(ProfilePreselection selection)
|
||||
private void PolicyPreselectedProfileWasChanged(ProfilePreselection selection)
|
||||
{
|
||||
this.policyPreselectedProfile = selection;
|
||||
if (this.selectedPolicy is not null)
|
||||
this.selectedPolicy.PreselectedProfile = this.policyPreselectedProfile;
|
||||
|
||||
this.CurrentProfile = this.ResolveProfileSelection();
|
||||
await this.AutoSave();
|
||||
}
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
@ -4213,6 +4213,9 @@ 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"
|
||||
|
||||
-- No LLM providers meet the confidence requirements. Configure an eligible provider in the app settings.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T1220991024"] = "No LLM providers meet the confidence requirements. Configure an eligible provider in the app settings."
|
||||
|
||||
-- Audio input possible
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T1742581112"] = "Audio input possible"
|
||||
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits MSGComponentBase
|
||||
@{
|
||||
var availableProviderItems = this.GetAvailableProviderSelectionItems().ToList();
|
||||
}
|
||||
<MudSelect T="Provider" Value="@this.ProviderSettings" ValueChanged="@this.SelectionChanged" Validation="@this.ValidateProvider" Margin="Margin.Dense" Label="@T("Provider")" Class="mb-3 rounded-lg" OuterClass="flex-grow-0" Variant="Variant.Outlined" Disabled="@this.Disabled">
|
||||
@foreach (var providerItem in this.GetAvailableProviderSelectionItems())
|
||||
@foreach (var providerItem in availableProviderItems)
|
||||
{
|
||||
<MudSelectItem Value="@providerItem.Provider">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Class="w-100" Wrap="Wrap.NoWrap">
|
||||
@ -21,3 +24,9 @@
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
@if (availableProviderItems.Count is 0)
|
||||
{
|
||||
<MudText Typo="Typo.body2" Color="Color.Error" Class="mb-3">
|
||||
@T("No LLM providers meet the confidence requirements. Configure an eligible provider in the app settings.")
|
||||
</MudText>
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ public sealed class SettingsManager
|
||||
|
||||
private readonly ILogger<SettingsManager> logger;
|
||||
private readonly RustService rustService;
|
||||
private readonly SemaphoreSlim settingsWriteSemaphore = new(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// The settings manager.
|
||||
@ -293,6 +294,9 @@ public sealed class SettingsManager
|
||||
/// Stores the settings to the file system.
|
||||
/// </summary>
|
||||
public async Task StoreSettings()
|
||||
{
|
||||
await this.settingsWriteSemaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
if(!this.IsSetUp)
|
||||
{
|
||||
@ -306,29 +310,41 @@ public sealed class SettingsManager
|
||||
return;
|
||||
}
|
||||
|
||||
var settingsJson = JsonSerializer.Serialize(this.ConfigurationData, JSON_OPTIONS);
|
||||
var settingsPath = Path.Combine(ConfigDirectory!, SETTINGS_FILENAME);
|
||||
await this.StoreSettingsSnapshot(this.ConfigurationData, settingsPath);
|
||||
await this.StoreCurrentVersionBackup(this.ConfigurationData);
|
||||
await this.StoreSettingsSnapshot(settingsJson, settingsPath);
|
||||
await this.StoreCurrentVersionBackup(this.ConfigurationData.Version, settingsJson);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.settingsWriteSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBackupSettingsFilename(Version version) => $"settings.{version.ToString().ToLowerInvariant()}.json";
|
||||
|
||||
private static string GetBackupSettingsPath(Version version) => Path.Combine(ConfigDirectory!, GetBackupSettingsFilename(version));
|
||||
|
||||
private async Task StoreCurrentVersionBackup(Data settingsData)
|
||||
private Task StoreCurrentVersionBackup(Data settingsData) =>
|
||||
this.StoreCurrentVersionBackup(settingsData.Version, JsonSerializer.Serialize(settingsData, JSON_OPTIONS));
|
||||
|
||||
private async Task StoreCurrentVersionBackup(Version settingsVersion, string settingsJson)
|
||||
{
|
||||
if(settingsData.Version != CURRENT_SETTINGS_VERSION)
|
||||
if(settingsVersion != CURRENT_SETTINGS_VERSION)
|
||||
{
|
||||
this.logger.LogWarning($"Skipping settings backup because the settings version '{settingsData.Version}' is not the current version '{CURRENT_SETTINGS_VERSION}'.");
|
||||
this.logger.LogWarning($"Skipping settings backup because the settings version '{settingsVersion}' is not the current version '{CURRENT_SETTINGS_VERSION}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
var backupSettingsPath = GetBackupSettingsPath(CURRENT_SETTINGS_VERSION);
|
||||
await this.StoreSettingsSnapshot(settingsData, backupSettingsPath);
|
||||
await this.StoreSettingsSnapshot(settingsJson, backupSettingsPath);
|
||||
this.logger.LogInformation($"Stored the settings backup file '{backupSettingsPath}'.");
|
||||
}
|
||||
|
||||
private async Task StoreSettingsSnapshot(Data settingsData, string settingsPath)
|
||||
private Task StoreSettingsSnapshot(Data settingsData, string settingsPath) =>
|
||||
this.StoreSettingsSnapshot(JsonSerializer.Serialize(settingsData, JSON_OPTIONS), settingsPath);
|
||||
|
||||
private async Task StoreSettingsSnapshot(string settingsJson, string settingsPath)
|
||||
{
|
||||
if(!Directory.Exists(ConfigDirectory))
|
||||
{
|
||||
@ -336,8 +352,6 @@ public sealed class SettingsManager
|
||||
Directory.CreateDirectory(ConfigDirectory!);
|
||||
}
|
||||
|
||||
var settingsJson = JsonSerializer.Serialize(settingsData, JSON_OPTIONS);
|
||||
|
||||
//
|
||||
// We write the new settings next to the previous ones and replace them afterwards, so that
|
||||
// no crash can leave a half-written settings file behind. The temporary file has to live in
|
||||
@ -349,7 +363,7 @@ public sealed class SettingsManager
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(tempFile, settingsJson);
|
||||
File.Move(tempFile, settingsPath, true);
|
||||
await Task.Run(() => File.Move(tempFile, settingsPath, true));
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
- Improved the list of your attached files: every file now appears under the folder it came from, and each folder is named only once, no matter in which order you attached your files.
|
||||
- Changed how provider trust and provider confidence work together. Marking a provider as trustworthy in a configuration no longer also satisfies a required confidence level: one says who runs the provider, the other how confidential it is. Organizations raise a provider's level in their own confidence scheme instead. This applies beyond local data sources, for example, when a model reads a page from your intranet.
|
||||
- Fixed the abilities AI Studio assumed for many models. We checked the families against their documentation: some models gained image input, reasoning, or tool calling, others lost an ability they never had.
|
||||
- Fixed the Document Analysis assistant becoming unresponsive when choosing an LLM provider.
|
||||
- Fixed model names that a provider writes in its own way not being recognized at all, such as the colon Ollama puts before the variant. Those models were treated as plain text models and lost every other ability.
|
||||
- Fixed a model resold under a plain name not getting the abilities it really has.
|
||||
- Fixed image and video generation models showing up among the chat models.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user