diff --git a/app/MindWork AI Studio/Components/ReadWebContent.razor b/app/MindWork AI Studio/Components/ReadWebContent.razor
index 10ebc52d..0a5bb3d0 100644
--- a/app/MindWork AI Studio/Components/ReadWebContent.razor
+++ b/app/MindWork AI Studio/Components/ReadWebContent.razor
@@ -3,7 +3,13 @@
@if (this.Preselect)
{
-
+
+ @if (this.ContentCleanerHint is { } contentCleanerHint)
+ {
+
+ @contentCleanerHint
+
+ }
diff --git a/app/MindWork AI Studio/Components/ReadWebContent.razor.cs b/app/MindWork AI Studio/Components/ReadWebContent.razor.cs
index 22721a1d..38c88aa8 100644
--- a/app/MindWork AI Studio/Components/ReadWebContent.razor.cs
+++ b/app/MindWork AI Studio/Components/ReadWebContent.razor.cs
@@ -72,33 +72,66 @@ public partial class ReadWebContent : MSGComponentBase
private readonly Process process = Process.INSTANCE;
private ProcessStepValue processStep;
-
- private bool isProviderValid;
+ ///
+ /// The model the content cleaner runs with.
+ ///
+ ///
+ /// This is a resolved value, not a chosen one: the reader has no model selection of its own,
+ /// it takes what the assistant around it uses, unless a dedicated one for the cleaner or an
+ /// app-wide default takes precedence. Because the assistant's model can change at any moment,
+ /// this is resolved again on every render instead of being remembered from the first one.
+ ///
private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE;
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
{
- this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_TEXT_CONTENT_CLEANER, this.ProviderSettings.Id, true);
- this.providerSettings = this.ProviderSettings;
- this.ValidateProvider(this.PreselectContentCleanerAgent);
-
+ this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED ]);
+ this.ResolveProvider();
+
await base.OnInitializedAsync();
}
protected override async Task OnParametersSetAsync()
{
- if (!this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)
- this.providerSettings = this.ProviderSettings;
-
- this.ValidateProvider(this.PreselectContentCleanerAgent);
+ this.ResolveProvider();
await base.OnParametersSetAsync();
}
#endregion
+ #region Overrides of MSGComponentBase
+
+ protected override Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
+ {
+ if (triggeredEvent is Event.CONFIGURATION_CHANGED)
+ {
+ //
+ // A dedicated model for the cleaner, or the app-wide default, may be set while this
+ // assistant is open. Nothing about that reaches us as a parameter, so without this the
+ // user would have to leave the assistant and come back for it to take effect.
+ //
+ this.ResolveProvider();
+ this.StateHasChanged();
+ }
+
+ return Task.CompletedTask;
+ }
+
+ #endregion
+
+ ///
+ /// Determines the model the content cleaner runs with.
+ ///
+ ///
+ /// Called from both lifecycle methods, and with the same arguments: the assistant's model is a
+ /// parameter, and a parameter arrives whenever the parent renders. Resolving only once would
+ /// leave the cleaner with whatever was set the first time this component was built.
+ ///
+ private void ResolveProvider() => this.providerSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_TEXT_CONTENT_CLEANER, this.ProviderSettings.Id, true);
+
private async Task LoadFromWeb()
{
if(!this.IsReady)
@@ -128,6 +161,16 @@ public partial class ReadWebContent : MSGComponentBase
markdown = retrievedPage.ExtractedPage.Markdown;
markdown = await this.PromptInjectionGuardService.SanitizeAsync(markdown, PromptInjectionSource.WebContent(this.URL));
+ if (this.PreselectContentCleanerAgent && this.providerSettings == AIStudio.Settings.Provider.NONE)
+ {
+ //
+ // Say that the cleaning did not happen. The user asked for it, the page arrives,
+ // and without a word they would take the raw markdown -- navigation, cookie banner
+ // and advertising included -- for the cleaned result.
+ //
+ await this.MessageBus.SendError(new(Icons.Material.Filled.SettingsSuggest, T("The content was loaded, but not cleaned: no model is available for the content cleaner.")));
+ }
+
if (this.PreselectContentCleanerAgent && this.providerSettings != AIStudio.Settings.Provider.NONE)
{
this.AgentTextContentCleaner.ProviderSettings = this.providerSettings;
@@ -183,19 +226,16 @@ public partial class ReadWebContent : MSGComponentBase
await this.ContentChanged.InvokeAsync(this.Content);
}
- private bool IsReady
- {
- get
- {
- if(!this.UrlIsValid)
- return false;
-
- if(this.PreselectContentCleanerAgent && !this.isProviderValid)
- return false;
-
- return true;
- }
- }
+ ///
+ /// Whether the content can be fetched.
+ ///
+ ///
+ /// A missing model for the content cleaner is deliberately not part of this. Cleaning is an
+ /// option of the fetch, not a condition for it: making it one would leave the user with a
+ /// switch they turned on, no way to get their page, and a dead button to explain it. The page
+ /// is fetched, and LoadFromWeb says that it arrived uncleaned.
+ ///
+ private bool IsReady => this.UrlIsValid;
///
/// Whether the current URL can be loaded.
@@ -222,18 +262,30 @@ public partial class ReadWebContent : MSGComponentBase
await this.PreselectContentCleanerAgentChanged.InvokeAsync(state);
}
- private string? ValidateProvider(bool shouldUseAgent)
+ ///
+ /// Says why the content cleaner has no model, or nothing when it has one.
+ ///
+ ///
+ /// This is a hint, not a validation: the cleaner is an option of the reader, and an option
+ /// nobody can use yet must not keep the assistant around it from running. It is also stated
+ /// rather than remembered, so that choosing a model below makes it disappear at once.
+ /// The two causes lead to different places, which is why they are told apart: either no model
+ /// was chosen at all, or the chosen one is not trusted enough for this agent.
+ ///
+ private string? ContentCleanerHint
{
- if(shouldUseAgent && this.providerSettings == AIStudio.Settings.Provider.NONE)
+ get
{
- this.isProviderValid = false;
- return T("Please select a provider to use the cleanup agent.");
- }
+ if(!this.PreselectContentCleanerAgent || this.providerSettings != AIStudio.Settings.Provider.NONE)
+ return null;
- this.isProviderValid = true;
- return null;
+ if(this.ProviderSettings == AIStudio.Settings.Provider.NONE)
+ return T("The content cleaner uses the model of this assistant. Please select one below.");
+
+ return T("The selected model does not meet the confidence requirements of the content cleaner. Please select another model, or configure an eligible one in the app settings.");
+ }
}
-
+
private string? ValidateURL(string url)
{
if(string.IsNullOrWhiteSpace(url))