Fixed the web content URL not being reset in the assistants (#964)
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 / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
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

This commit is contained in:
Thorsten Sommer 2026-09-13 16:24:36 +02:00 committed by GitHub
parent a65c1162a5
commit 8a735c2bbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 62 additions and 27 deletions

View File

@ -140,6 +140,7 @@ else
var webState = this.assistantState.WebContent[webContent.Name]; var webState = this.assistantState.WebContent[webContent.Name];
<div class="@webContent.Class" style="@GetOptionalStyle(webContent.Style)"> <div class="@webContent.Class" style="@GetOptionalStyle(webContent.Style)">
<ReadWebContent @bind-Content="@webState.Content" <ReadWebContent @bind-Content="@webState.Content"
@bind-URL="@webState.URL"
ProviderSettings="@this.ProviderSettings" ProviderSettings="@this.ProviderSettings"
@bind-AgentIsRunning="@webState.AgentIsRunning" @bind-AgentIsRunning="@webState.AgentIsRunning"
@bind-Preselect="@webState.Preselect" @bind-Preselect="@webState.Preselect"

View File

@ -3,6 +3,7 @@ namespace AIStudio.Assistants.Dynamic;
public sealed class WebContentState public sealed class WebContentState
{ {
public string Content { get; set; } = string.Empty; public string Content { get; set; } = string.Empty;
public string URL { get; set; } = string.Empty;
public bool Preselect { get; set; } public bool Preselect { get; set; }
public bool PreselectContentCleanerAgent { get; set; } public bool PreselectContentCleanerAgent { get; set; }
public bool AgentIsRunning { get; set; } public bool AgentIsRunning { get; set; }

View File

@ -3,7 +3,7 @@
@if (!this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader) @if (!this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)
{ {
<ReadWebContent @bind-Content="@this.inputLegalDocument" ProviderSettings="@this.ProviderSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/> <ReadWebContent @bind-Content="@this.inputLegalDocument" @bind-URL="@this.webContentURL" ProviderSettings="@this.ProviderSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/>
} }
@* Two zones, so no default target: the user has to aim at the one they mean. *@ @* Two zones, so no default target: the user has to aim at the one they mean. *@

View File

@ -36,6 +36,7 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
{ {
this.inputLegalDocument = string.Empty; this.inputLegalDocument = string.Empty;
this.inputQuestions = string.Empty; this.inputQuestions = string.Empty;
this.webContentURL = string.Empty;
if (!this.MightPreselectValues()) if (!this.MightPreselectValues())
{ {
this.showWebContentReader = false; this.showWebContentReader = false;
@ -58,11 +59,13 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
private bool showWebContentReader; private bool showWebContentReader;
private bool useContentCleanerAgent; private bool useContentCleanerAgent;
private bool isAgentRunning; private bool isAgentRunning;
private string webContentURL = string.Empty;
private string inputLegalDocument = string.Empty; private string inputLegalDocument = string.Empty;
private string inputQuestions = string.Empty; private string inputQuestions = string.Empty;
private static readonly AssistantSessionStateKey<bool> SHOW_WEB_CONTENT_READER_STATE_KEY = new(nameof(showWebContentReader)); private static readonly AssistantSessionStateKey<bool> SHOW_WEB_CONTENT_READER_STATE_KEY = new(nameof(showWebContentReader));
private static readonly AssistantSessionStateKey<bool> USE_CONTENT_CLEANER_AGENT_STATE_KEY = new(nameof(useContentCleanerAgent)); private static readonly AssistantSessionStateKey<bool> USE_CONTENT_CLEANER_AGENT_STATE_KEY = new(nameof(useContentCleanerAgent));
private static readonly AssistantSessionStateKey<bool> IS_AGENT_RUNNING_STATE_KEY = new(nameof(isAgentRunning)); private static readonly AssistantSessionStateKey<bool> IS_AGENT_RUNNING_STATE_KEY = new(nameof(isAgentRunning));
private static readonly AssistantSessionStateKey<string> WEB_CONTENT_URL_STATE_KEY = new(nameof(webContentURL));
private static readonly AssistantSessionStateKey<string> INPUT_LEGAL_DOCUMENT_STATE_KEY = new(nameof(inputLegalDocument)); private static readonly AssistantSessionStateKey<string> INPUT_LEGAL_DOCUMENT_STATE_KEY = new(nameof(inputLegalDocument));
private static readonly AssistantSessionStateKey<string> INPUT_QUESTIONS_STATE_KEY = new(nameof(inputQuestions)); private static readonly AssistantSessionStateKey<string> INPUT_QUESTIONS_STATE_KEY = new(nameof(inputQuestions));
@ -72,6 +75,7 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
state.Set(SHOW_WEB_CONTENT_READER_STATE_KEY, this.showWebContentReader); state.Set(SHOW_WEB_CONTENT_READER_STATE_KEY, this.showWebContentReader);
state.Set(USE_CONTENT_CLEANER_AGENT_STATE_KEY, this.useContentCleanerAgent); state.Set(USE_CONTENT_CLEANER_AGENT_STATE_KEY, this.useContentCleanerAgent);
state.Set(IS_AGENT_RUNNING_STATE_KEY, this.isAgentRunning); state.Set(IS_AGENT_RUNNING_STATE_KEY, this.isAgentRunning);
state.Set(WEB_CONTENT_URL_STATE_KEY, this.webContentURL);
state.Set(INPUT_LEGAL_DOCUMENT_STATE_KEY, this.inputLegalDocument); state.Set(INPUT_LEGAL_DOCUMENT_STATE_KEY, this.inputLegalDocument);
state.Set(INPUT_QUESTIONS_STATE_KEY, this.inputQuestions); state.Set(INPUT_QUESTIONS_STATE_KEY, this.inputQuestions);
} }
@ -82,6 +86,7 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
state.Restore(SHOW_WEB_CONTENT_READER_STATE_KEY, value => this.showWebContentReader = value); state.Restore(SHOW_WEB_CONTENT_READER_STATE_KEY, value => this.showWebContentReader = value);
state.Restore(USE_CONTENT_CLEANER_AGENT_STATE_KEY, value => this.useContentCleanerAgent = value); state.Restore(USE_CONTENT_CLEANER_AGENT_STATE_KEY, value => this.useContentCleanerAgent = value);
state.Restore(IS_AGENT_RUNNING_STATE_KEY, value => this.isAgentRunning = value); state.Restore(IS_AGENT_RUNNING_STATE_KEY, value => this.isAgentRunning = value);
state.Restore(WEB_CONTENT_URL_STATE_KEY, value => this.webContentURL = value);
state.Restore(INPUT_LEGAL_DOCUMENT_STATE_KEY, value => this.inputLegalDocument = value); state.Restore(INPUT_LEGAL_DOCUMENT_STATE_KEY, value => this.inputLegalDocument = value);
state.Restore(INPUT_QUESTIONS_STATE_KEY, value => this.inputQuestions = value); state.Restore(INPUT_QUESTIONS_STATE_KEY, value => this.inputQuestions = value);
} }

View File

@ -3,7 +3,7 @@
@if (!this.SettingsManager.ConfigurationData.TextSummarizer.HideWebContentReader) @if (!this.SettingsManager.ConfigurationData.TextSummarizer.HideWebContentReader)
{ {
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.ProviderSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/> <ReadWebContent @bind-Content="@this.inputText" @bind-URL="@this.webContentURL" ProviderSettings="@this.ProviderSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/>
} }
<ReadFileContent @bind-FileContent="@this.inputText" EnableDragDrop="true" CatchAllDocuments="true"/> <ReadFileContent @bind-FileContent="@this.inputText" EnableDragDrop="true" CatchAllDocuments="true"/>

View File

@ -35,6 +35,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
protected override void ResetForm() protected override void ResetForm()
{ {
this.inputText = string.Empty; this.inputText = string.Empty;
this.webContentURL = string.Empty;
if(!this.MightPreselectValues()) if(!this.MightPreselectValues())
{ {
this.showWebContentReader = false; this.showWebContentReader = false;
@ -66,6 +67,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
private bool showWebContentReader; private bool showWebContentReader;
private bool useContentCleanerAgent; private bool useContentCleanerAgent;
private string webContentURL = string.Empty;
private string inputText = string.Empty; private string inputText = string.Empty;
private bool isAgentRunning; private bool isAgentRunning;
private CommonLanguages selectedTargetLanguage; private CommonLanguages selectedTargetLanguage;
@ -75,6 +77,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
private string importantAspects = string.Empty; private string importantAspects = string.Empty;
private static readonly AssistantSessionStateKey<bool> SHOW_WEB_CONTENT_READER_STATE_KEY = new(nameof(showWebContentReader)); private static readonly AssistantSessionStateKey<bool> SHOW_WEB_CONTENT_READER_STATE_KEY = new(nameof(showWebContentReader));
private static readonly AssistantSessionStateKey<bool> USE_CONTENT_CLEANER_AGENT_STATE_KEY = new(nameof(useContentCleanerAgent)); private static readonly AssistantSessionStateKey<bool> USE_CONTENT_CLEANER_AGENT_STATE_KEY = new(nameof(useContentCleanerAgent));
private static readonly AssistantSessionStateKey<string> WEB_CONTENT_URL_STATE_KEY = new(nameof(webContentURL));
private static readonly AssistantSessionStateKey<string> INPUT_TEXT_STATE_KEY = new(nameof(inputText)); private static readonly AssistantSessionStateKey<string> INPUT_TEXT_STATE_KEY = new(nameof(inputText));
private static readonly AssistantSessionStateKey<bool> IS_AGENT_RUNNING_STATE_KEY = new(nameof(isAgentRunning)); private static readonly AssistantSessionStateKey<bool> IS_AGENT_RUNNING_STATE_KEY = new(nameof(isAgentRunning));
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage)); private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
@ -88,6 +91,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
{ {
state.Set(SHOW_WEB_CONTENT_READER_STATE_KEY, this.showWebContentReader); state.Set(SHOW_WEB_CONTENT_READER_STATE_KEY, this.showWebContentReader);
state.Set(USE_CONTENT_CLEANER_AGENT_STATE_KEY, this.useContentCleanerAgent); state.Set(USE_CONTENT_CLEANER_AGENT_STATE_KEY, this.useContentCleanerAgent);
state.Set(WEB_CONTENT_URL_STATE_KEY, this.webContentURL);
state.Set(INPUT_TEXT_STATE_KEY, this.inputText); state.Set(INPUT_TEXT_STATE_KEY, this.inputText);
state.Set(IS_AGENT_RUNNING_STATE_KEY, this.isAgentRunning); state.Set(IS_AGENT_RUNNING_STATE_KEY, this.isAgentRunning);
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage); state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
@ -102,6 +106,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
{ {
state.Restore(SHOW_WEB_CONTENT_READER_STATE_KEY, value => this.showWebContentReader = value); state.Restore(SHOW_WEB_CONTENT_READER_STATE_KEY, value => this.showWebContentReader = value);
state.Restore(USE_CONTENT_CLEANER_AGENT_STATE_KEY, value => this.useContentCleanerAgent = value); state.Restore(USE_CONTENT_CLEANER_AGENT_STATE_KEY, value => this.useContentCleanerAgent = value);
state.Restore(WEB_CONTENT_URL_STATE_KEY, value => this.webContentURL = value);
state.Restore(INPUT_TEXT_STATE_KEY, value => this.inputText = value); state.Restore(INPUT_TEXT_STATE_KEY, value => this.inputText = value);
state.Restore(IS_AGENT_RUNNING_STATE_KEY, value => this.isAgentRunning = value); state.Restore(IS_AGENT_RUNNING_STATE_KEY, value => this.isAgentRunning = value);
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value); state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);

View File

@ -3,7 +3,7 @@
@if (!this.SettingsManager.ConfigurationData.Translation.HideWebContentReader) @if (!this.SettingsManager.ConfigurationData.Translation.HideWebContentReader)
{ {
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.ProviderSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/> <ReadWebContent @bind-Content="@this.inputText" @bind-URL="@this.webContentURL" ProviderSettings="@this.ProviderSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/>
} }
<ReadFileContent @bind-FileContent="@this.inputText" EnableDragDrop="true" CatchAllDocuments="true"/> <ReadFileContent @bind-FileContent="@this.inputText" EnableDragDrop="true" CatchAllDocuments="true"/>

View File

@ -47,6 +47,7 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
{ {
this.inputText = string.Empty; this.inputText = string.Empty;
this.inputTextLastTranslation = string.Empty; this.inputTextLastTranslation = string.Empty;
this.webContentURL = string.Empty;
if (!this.MightPreselectValues()) if (!this.MightPreselectValues())
{ {
this.showWebContentReader = false; this.showWebContentReader = false;
@ -76,6 +77,7 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
private bool useContentCleanerAgent; private bool useContentCleanerAgent;
private bool liveTranslation; private bool liveTranslation;
private bool isAgentRunning; private bool isAgentRunning;
private string webContentURL = string.Empty;
private string inputText = string.Empty; private string inputText = string.Empty;
private string inputTextLastTranslation = string.Empty; private string inputTextLastTranslation = string.Empty;
private CommonLanguages selectedTargetLanguage; private CommonLanguages selectedTargetLanguage;
@ -84,6 +86,7 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
private static readonly AssistantSessionStateKey<bool> USE_CONTENT_CLEANER_AGENT_STATE_KEY = new(nameof(useContentCleanerAgent)); private static readonly AssistantSessionStateKey<bool> USE_CONTENT_CLEANER_AGENT_STATE_KEY = new(nameof(useContentCleanerAgent));
private static readonly AssistantSessionStateKey<bool> LIVE_TRANSLATION_STATE_KEY = new(nameof(liveTranslation)); private static readonly AssistantSessionStateKey<bool> LIVE_TRANSLATION_STATE_KEY = new(nameof(liveTranslation));
private static readonly AssistantSessionStateKey<bool> IS_AGENT_RUNNING_STATE_KEY = new(nameof(isAgentRunning)); private static readonly AssistantSessionStateKey<bool> IS_AGENT_RUNNING_STATE_KEY = new(nameof(isAgentRunning));
private static readonly AssistantSessionStateKey<string> WEB_CONTENT_URL_STATE_KEY = new(nameof(webContentURL));
private static readonly AssistantSessionStateKey<string> INPUT_TEXT_STATE_KEY = new(nameof(inputText)); private static readonly AssistantSessionStateKey<string> INPUT_TEXT_STATE_KEY = new(nameof(inputText));
private static readonly AssistantSessionStateKey<string> INPUT_TEXT_LAST_TRANSLATION_STATE_KEY = new(nameof(inputTextLastTranslation)); private static readonly AssistantSessionStateKey<string> INPUT_TEXT_LAST_TRANSLATION_STATE_KEY = new(nameof(inputTextLastTranslation));
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage)); private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
@ -96,6 +99,7 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
state.Set(USE_CONTENT_CLEANER_AGENT_STATE_KEY, this.useContentCleanerAgent); state.Set(USE_CONTENT_CLEANER_AGENT_STATE_KEY, this.useContentCleanerAgent);
state.Set(LIVE_TRANSLATION_STATE_KEY, this.liveTranslation); state.Set(LIVE_TRANSLATION_STATE_KEY, this.liveTranslation);
state.Set(IS_AGENT_RUNNING_STATE_KEY, this.isAgentRunning); state.Set(IS_AGENT_RUNNING_STATE_KEY, this.isAgentRunning);
state.Set(WEB_CONTENT_URL_STATE_KEY, this.webContentURL);
state.Set(INPUT_TEXT_STATE_KEY, this.inputText); state.Set(INPUT_TEXT_STATE_KEY, this.inputText);
state.Set(INPUT_TEXT_LAST_TRANSLATION_STATE_KEY, this.inputTextLastTranslation); state.Set(INPUT_TEXT_LAST_TRANSLATION_STATE_KEY, this.inputTextLastTranslation);
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage); state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
@ -109,6 +113,7 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
state.Restore(USE_CONTENT_CLEANER_AGENT_STATE_KEY, value => this.useContentCleanerAgent = value); state.Restore(USE_CONTENT_CLEANER_AGENT_STATE_KEY, value => this.useContentCleanerAgent = value);
state.Restore(LIVE_TRANSLATION_STATE_KEY, value => this.liveTranslation = value); state.Restore(LIVE_TRANSLATION_STATE_KEY, value => this.liveTranslation = value);
state.Restore(IS_AGENT_RUNNING_STATE_KEY, value => this.isAgentRunning = value); state.Restore(IS_AGENT_RUNNING_STATE_KEY, value => this.isAgentRunning = value);
state.Restore(WEB_CONTENT_URL_STATE_KEY, value => this.webContentURL = value);
state.Restore(INPUT_TEXT_STATE_KEY, value => this.inputText = value); state.Restore(INPUT_TEXT_STATE_KEY, value => this.inputText = value);
state.Restore(INPUT_TEXT_LAST_TRANSLATION_STATE_KEY, value => this.inputTextLastTranslation = value); state.Restore(INPUT_TEXT_LAST_TRANSLATION_STATE_KEY, value => this.inputTextLastTranslation = value);
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value); state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);

View File

@ -5,8 +5,8 @@
{ {
<MudTextSwitch Label="@T("Cleanup content by using an LLM agent?")" Value="@this.PreselectContentCleanerAgent" ValueChanged="@this.UseContentCleanerAgentChanged" Validation="@this.ValidateProvider" Disabled="@this.AgentIsRunning" LabelOn="@T("The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.")" LabelOff="@T("No content cleaning")" /> <MudTextSwitch Label="@T("Cleanup content by using an LLM agent?")" Value="@this.PreselectContentCleanerAgent" ValueChanged="@this.UseContentCleanerAgentChanged" Validation="@this.ValidateProvider" Disabled="@this.AgentIsRunning" LabelOn="@T("The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.")" LabelOff="@T("No content cleaning")" />
<MudStack Row="@true" AlignItems="@AlignItems.Baseline" Class="mb-3"> <MudStack Row="@true" AlignItems="@AlignItems.Baseline" Class="mb-3">
<MudTextField T="string" Label="@T("URL from which to load the content")" @bind-Value="@this.providedURL" Validation="@this.ValidateURL" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Link" Placeholder="https://..." HelperText="@T("Loads the content from your URL. Does not work when the content is hidden behind a paywall.")" Variant="Variant.Outlined" Immediate="@true" Disabled="@this.AgentIsRunning"/> <MudTextField T="string" Label="@T("URL from which to load the content")" Value="@this.URL" ValueChanged="@this.URLValueChanged" Validation="@this.ValidateURL" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Link" Placeholder="https://..." HelperText="@T("Loads the content from your URL. Does not work when the content is hidden behind a paywall.")" Variant="Variant.Outlined" Immediate="@true" Disabled="@this.AgentIsRunning"/>
<MudButton Disabled="@(!this.IsReady || this.AgentIsRunning)" Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Download" OnClick="() => this.LoadFromWeb()"> <MudButton Disabled="@(!this.IsReady || this.AgentIsRunning)" Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Download" OnClick="@this.LoadFromWeb">
@T("Fetch") @T("Fetch")
</MudButton> </MudButton>
</MudStack> </MudStack>

View File

@ -36,6 +36,19 @@ public partial class ReadWebContent : MSGComponentBase
[Parameter] [Parameter]
public EventCallback<string> ContentChanged { get; set; } public EventCallback<string> ContentChanged { get; set; }
/// <summary>
/// The URL the content is loaded from.
/// </summary>
/// <remarks>
/// The URL belongs to the parent, so that it is cleared when the parent resets its form and
/// is kept when the parent stores its state.
/// </remarks>
[Parameter]
public string URL { get; set; } = string.Empty;
[Parameter]
public EventCallback<string> URLChanged { get; set; }
[Parameter] [Parameter]
public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE; public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
@ -60,8 +73,6 @@ public partial class ReadWebContent : MSGComponentBase
private readonly Process<ReadWebContentSteps> process = Process<ReadWebContentSteps>.INSTANCE; private readonly Process<ReadWebContentSteps> process = Process<ReadWebContentSteps>.INSTANCE;
private ProcessStepValue processStep; private ProcessStepValue processStep;
private string providedURL = string.Empty;
private bool urlIsValid;
private bool isProviderValid; private bool isProviderValid;
private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE; private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE;
@ -105,7 +116,7 @@ public partial class ReadWebContent : MSGComponentBase
// the URL, so their own network is not off limits. // the URL, so their own network is not off limits.
// //
var retrievedPage = await this.WebPageRetrievalService.RetrieveAsync( var retrievedPage = await this.WebPageRetrievalService.RetrieveAsync(
new Uri(this.providedURL), new Uri(this.URL),
new WebPageRetrievalOptions new WebPageRetrievalOptions
{ {
TimeoutSeconds = TIMEOUT_SECONDS, TimeoutSeconds = TIMEOUT_SECONDS,
@ -115,14 +126,14 @@ public partial class ReadWebContent : MSGComponentBase
this.processStep = this.process[ReadWebContentSteps.PARSING]; this.processStep = this.process[ReadWebContentSteps.PARSING];
this.StateHasChanged(); this.StateHasChanged();
markdown = retrievedPage.ExtractedPage.Markdown; markdown = retrievedPage.ExtractedPage.Markdown;
markdown = await this.PromptInjectionGuardService.SanitizeAsync(markdown, PromptInjectionSource.WebContent(this.providedURL)); markdown = await this.PromptInjectionGuardService.SanitizeAsync(markdown, PromptInjectionSource.WebContent(this.URL));
if (this.PreselectContentCleanerAgent && this.providerSettings != AIStudio.Settings.Provider.NONE) if (this.PreselectContentCleanerAgent && this.providerSettings != AIStudio.Settings.Provider.NONE)
{ {
this.AgentTextContentCleaner.ProviderSettings = this.providerSettings; this.AgentTextContentCleaner.ProviderSettings = this.providerSettings;
var additionalData = new Dictionary<string, string> var additionalData = new Dictionary<string, string>
{ {
{ "sourceURL", this.providedURL }, { "sourceURL", this.URL },
}; };
this.processStep = this.process[ReadWebContentSteps.CLEANING]; this.processStep = this.process[ReadWebContentSteps.CLEANING];
@ -164,8 +175,8 @@ public partial class ReadWebContent : MSGComponentBase
// and the reasons a page cannot be read are things the user can act on: a link to a // and the reasons a page cannot be read are things the user can act on: a link to a
// PDF rather than a page, a host that does not answer, a server refusing the request. // PDF rather than a page, a host that does not answer, a server refusing the request.
// //
this.Logger.LogWarning(exception, "Could not load the web content from '{ProvidedUrl}'.", this.providedURL); this.Logger.LogWarning(exception, "Could not load the web content from '{ProvidedUrl}'.", this.URL);
await this.MessageBus.SendError(new(Icons.Material.Filled.CloudOff, string.Format(this.T("The content of '{0}' could not be loaded: {1}"), this.providedURL, exception.Message))); await this.MessageBus.SendError(new(Icons.Material.Filled.CloudOff, string.Format(this.T("The content of '{0}' could not be loaded: {1}"), this.URL, exception.Message)));
} }
this.Content = markdown; this.Content = markdown;
@ -176,7 +187,7 @@ public partial class ReadWebContent : MSGComponentBase
{ {
get get
{ {
if(!this.urlIsValid) if(!this.UrlIsValid)
return false; return false;
if(this.PreselectContentCleanerAgent && !this.isProviderValid) if(this.PreselectContentCleanerAgent && !this.isProviderValid)
@ -186,6 +197,21 @@ public partial class ReadWebContent : MSGComponentBase
} }
} }
/// <summary>
/// Whether the current URL can be loaded.
/// </summary>
/// <remarks>
/// Asked of the current value instead of remembered from the last validation run: the parent
/// clears the URL when it resets its form, and the form validation does not run again at that
/// point. The fetch button would otherwise stay enabled with an empty field.
/// </remarks>
private bool UrlIsValid => this.ValidateURL(this.URL) is null;
private async Task URLValueChanged(string url)
{
await this.URLChanged.InvokeAsync(url);
}
private async Task ShowWebContentReaderChanged(bool state) private async Task ShowWebContentReaderChanged(bool state)
{ {
await this.PreselectChanged.InvokeAsync(state); await this.PreselectChanged.InvokeAsync(state);
@ -211,25 +237,15 @@ public partial class ReadWebContent : MSGComponentBase
private string? ValidateURL(string url) private string? ValidateURL(string url)
{ {
if(string.IsNullOrWhiteSpace(url)) if(string.IsNullOrWhiteSpace(url))
{
this.urlIsValid = false;
return T("Please provide a URL to load the content from."); return T("Please provide a URL to load the content from.");
}
var urlParsingResult = Uri.TryCreate(url, UriKind.Absolute, out var uriResult); var urlParsingResult = Uri.TryCreate(url, UriKind.Absolute, out var uriResult);
if(!urlParsingResult) if(!urlParsingResult)
{
this.urlIsValid = false;
return T("Please provide a valid URL."); return T("Please provide a valid URL.");
}
if(uriResult is not { Scheme: "http" or "https" }) if(uriResult is not { Scheme: "http" or "https" })
{
this.urlIsValid = false;
return T("Please provide a valid HTTP or HTTPS URL."); return T("Please provide a valid HTTP or HTTPS URL.");
}
this.urlIsValid = true;
return null; return null;
} }
} }

View File

@ -29,5 +29,7 @@
- Fixed a model resold under a plain name not getting the abilities it really has. - 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. - Fixed image and video generation models showing up among the chat models.
- Fixed a dropped file being processed several times, e.g., after the computer woke up from sleep. - Fixed a dropped file being processed several times, e.g., after the computer woke up from sleep.
- Fixed the web address staying in the field when you reset an assistant that loads content from a web page.
- Fixed the web address being gone when you leave such an assistant and come back to it later.
- Fixed the Visual Briefing Assistant (in preview) not scrolling, which put everything below the window edge out of reach and made the assistant unusable. The briefing preview is now shown at its intended size inside its frame, and switching between the desktop, tablet, and mobile view changes its width as it should. - Fixed the Visual Briefing Assistant (in preview) not scrolling, which put everything below the window edge out of reach and made the assistant unusable. The briefing preview is now shown at its intended size inside its frame, and switching between the desktop, tablet, and mobile view changes its width as it should.
- Upgraded the Visual Briefing Assistant (in preview) from the prototype to the beta state. The assistant is now completely implemented and is undergoing a deeper testing phase in preparation for release. To try it, open the app settings, allow preview features down to beta, and then enable the Visual Briefing Assistant there. - Upgraded the Visual Briefing Assistant (in preview) from the prototype to the beta state. The assistant is now completely implemented and is undergoing a deeper testing phase in preparation for release. To try it, open the app settings, allow preview features down to beta, and then enable the Visual Briefing Assistant there.