Added batch processing settings

This commit is contained in:
Thorsten Sommer 2026-08-11 12:14:22 +02:00
parent 9ee7e5b6be
commit c8c336cf7a
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
12 changed files with 454 additions and 31 deletions

View File

@ -180,6 +180,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
this.CurrentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
this.CurrentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component);
await this.OnDefaultsAppliedAsync();
this.assistantSessionKey = new(this.Component, this.AssistantSessionInstanceId);
await this.AttachAssistantSessionIfAvailable();
await this.ConsumeMediaOutcomeAsync();
@ -312,6 +313,11 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
/// </remarks>
protected virtual Task OnFormChange() => Task.CompletedTask;
/// <summary>
/// Allows assistants to finish asynchronous work after their configured defaults were applied.
/// </summary>
protected virtual Task OnDefaultsAppliedAsync() => Task.CompletedTask;
/// <summary>
/// Add an issue to the UI.
/// </summary>
@ -668,6 +674,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
this.ResetForm();
this.ResetProviderAndProfileSelection();
await this.OnDefaultsAppliedAsync();
this.InputIsValid = false;
this.InputIssues = [];

View File

@ -1,5 +1,5 @@
@attribute [Route(Routes.ASSISTANT_BATCH_PROCESSING)]
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.NoSettingsPanel>
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogBatchProcessing>
@using AIStudio.Settings.DataModel
<MudText Typo="Typo.h5" Class="mb-3">
@ -16,7 +16,7 @@
@T("Instructions")
</MudText>
<MudSelect T="BatchProcessingPromptSource" @bind-Value="@this.promptSource" Disabled="@this.isProcessingBatch" AdornmentIcon="@Icons.Material.Filled.EditNote" Adornment="Adornment.Start" Label="@T("Source of the instructions")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3">
<MudSelect T="BatchProcessingPromptSource" Value="@this.promptSource" ValueChanged="@this.PromptSourceChanged" Disabled="@this.isProcessingBatch" AdornmentIcon="@Icons.Material.Filled.EditNote" Adornment="Adornment.Start" Label="@T("Source of the instructions")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3">
@foreach (var source in Enum.GetValues<BatchProcessingPromptSource>())
{
<MudSelectItem Value="@source">
@ -31,7 +31,17 @@
}
else if (this.promptSource is BatchProcessingPromptSource.FILE_IMPORT)
{
<ReadFileContent Text="@T("Select the file with your instructions")" @bind-FileContent="@this.importedPrompt" ShowAttachedDocumentState="@true" Disabled="@this.isProcessingBatch"/>
<ReadFileContent Text="@T("Select the file with your instructions")" @bind-FileContent="@this.ImportedPrompt" ShowAttachedDocumentState="@true" Disabled="@this.isProcessingBatch"/>
@if (!string.IsNullOrWhiteSpace(this.promptFilePath))
{
<MudText Typo="Typo.body2" Class="mb-3">@(string.Format(T("Configured instructions file: {0}"), this.promptFilePath))</MudText>
}
@if (!string.IsNullOrWhiteSpace(this.promptFileLoadIssue))
{
<MudAlert Severity="Severity.Error" Dense="true" Class="mb-3">@this.promptFileLoadIssue</MudAlert>
}
<MudJustifiedText Typo="Typo.body2" Class="mb-3">
@T("The content of the selected file is used as the instructions for every single document of the batch run.")
@ -39,6 +49,11 @@ else if (this.promptSource is BatchProcessingPromptSource.FILE_IMPORT)
}
else
{
@if (this.ConfiguredPolicyIsMissing)
{
<MudAlert Severity="Severity.Warning" Dense="true" Class="mb-3">@T("The configured default policy no longer exists. Please select another document analysis policy.")</MudAlert>
}
@if (this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Count is 0)
{
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@ -50,7 +65,7 @@ else
}
else
{
<MudSelect T="DataDocumentAnalysisPolicy" @bind-Value="@this.selectedPolicy" Disabled="@this.isProcessingBatch" AdornmentIcon="@Icons.Material.Filled.Policy" Adornment="Adornment.Start" Label="@T("Document analysis policy")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3">
<MudSelect T="DataDocumentAnalysisPolicy" Value="@this.selectedPolicy" ValueChanged="@this.SelectedPolicyChanged" Disabled="@this.isProcessingBatch" AdornmentIcon="@Icons.Material.Filled.Policy" Adornment="Adornment.Start" Label="@T("Document analysis policy")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3">
@foreach (var policy in this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies)
{
<MudSelectItem Value="@policy">

View File

@ -1,6 +1,5 @@
using AIStudio.Chat;
using AIStudio.Provider;
using AIStudio.Settings;
namespace AIStudio.Assistants.BatchProcessing;
@ -92,7 +91,7 @@ public partial class AssistantBatchProcessing
{
IncludeDateTime = false,
SelectedProvider = this.ProviderSettings.Id,
SelectedProfile = Profile.NO_PROFILE.Id,
SelectedProfile = this.CurrentProfile.Id,
SystemPrompt = this.SystemPrompt,
WorkspaceId = Guid.Empty,
ChatId = Guid.NewGuid(),

View File

@ -47,7 +47,9 @@ public partial class AssistantBatchProcessing
/// </summary>
private string? ValidateInstructionSource() => this.promptSource switch
{
BatchProcessingPromptSource.POLICY when this.ConfiguredPolicyIsMissing => T("The configured default policy no longer exists. Please select another document analysis policy."),
BatchProcessingPromptSource.POLICY when this.selectedPolicy is null => T("Please select a document analysis policy."),
BatchProcessingPromptSource.FILE_IMPORT when !string.IsNullOrWhiteSpace(this.promptFileLoadIssue) => this.promptFileLoadIssue,
BatchProcessingPromptSource.FILE_IMPORT when string.IsNullOrWhiteSpace(this.importedPrompt) => T("Please select the file which contains your instructions."),
_ => null,

View File

@ -1,17 +1,17 @@
using AIStudio.Dialogs.Settings;
using AIStudio.Provider;
using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Assistants.BatchProcessing;
public partial class AssistantBatchProcessing : AssistantBaseCore<NoSettingsPanel>
public partial class AssistantBatchProcessing : AssistantBaseCore<SettingsDialogBatchProcessing>
{
[Inject]
private IDialogService DialogService { get; init; } = null!;
private const string DEFAULT_FILE_PATTERNS = "*.pdf;*.docx;*.pptx;*.xlsx;*.md;*.txt";
private const string DEFAULT_OUTPUT_DIRECTORY_NAME = "ai-results";
private const string DEFAULT_RESULTS_FILENAME = "batch-results.csv";
private const string CSV_EXTENSION = ".csv";
@ -40,7 +40,7 @@ public partial class AssistantBatchProcessing : AssistantBaseCore<NoSettingsPane
protected override bool ShowResult => false;
protected override bool AllowProfiles => false;
protected override bool AllowProfiles => true;
protected override bool ShowSendTo => false;
@ -51,31 +51,39 @@ public partial class AssistantBatchProcessing : AssistantBaseCore<NoSettingsPane
if (this.isProcessingBatch)
return;
this.inputDirectory = string.Empty;
this.outputDirectory = string.Empty;
this.filePatterns = DEFAULT_FILE_PATTERNS;
this.includeSubdirectories = false;
this.promptSource = BatchProcessingPromptSource.FREE_PROMPT;
this.freePrompt = string.Empty;
this.ApplyFormDefaults();
this.importedPrompt = string.Empty;
this.selectedPolicy = null;
this.outputMode = BatchProcessingOutputMode.MARKDOWN_FILES;
this.resultColumnHeader = string.Empty;
this.csvFileName = string.Empty;
this.promptFileLoadIssue = string.Empty;
this.fileResults.Clear();
this.usedResultFileNames.Clear();
this.hasReportedWriteFailure = false;
this.numProcessedFiles = 0;
}
protected override bool MightPreselectValues() => false;
protected override bool MightPreselectValues()
{
if (!this.SettingsManager.ConfigurationData.BatchProcessing.PreselectOptions)
return false;
this.ApplyFormDefaults();
return true;
}
protected override async Task OnDefaultsAppliedAsync()
{
await this.LoadConfiguredPromptFileAsync();
this.ApplyPolicyPreselection();
}
private string inputDirectory = string.Empty;
private string outputDirectory = string.Empty;
private string filePatterns = DEFAULT_FILE_PATTERNS;
private string filePatterns = DataBatchProcessing.DEFAULT_FILE_PATTERNS;
private bool includeSubdirectories;
private BatchProcessingPromptSource promptSource = BatchProcessingPromptSource.FREE_PROMPT;
private string freePrompt = string.Empty;
private string importedPrompt = string.Empty;
private string promptFilePath = string.Empty;
private string promptFileLoadIssue = string.Empty;
private DataDocumentAnalysisPolicy? selectedPolicy;
private BatchProcessingOutputMode outputMode = BatchProcessingOutputMode.MARKDOWN_FILES;
private string resultColumnHeader = string.Empty;
@ -92,11 +100,163 @@ public partial class AssistantBatchProcessing : AssistantBaseCore<NoSettingsPane
/// </summary>
private string ResultColumnHeader => string.IsNullOrWhiteSpace(this.resultColumnHeader) ? T("Result") : this.resultColumnHeader.Trim();
/// <summary>
/// Updates the manually imported prompt and stops presenting an obsolete
/// configured path or load error once the user has selected another file.
/// </summary>
private string ImportedPrompt
{
get => this.importedPrompt;
set
{
this.importedPrompt = value;
this.promptFilePath = string.Empty;
this.promptFileLoadIssue = string.Empty;
}
}
private bool ConfiguredPolicyIsMissing
{
get
{
var settings = this.SettingsManager.ConfigurationData.BatchProcessing;
return settings.PreselectOptions
&& this.promptSource is BatchProcessingPromptSource.POLICY
&& !string.IsNullOrWhiteSpace(settings.PreselectedPolicyId)
&& this.selectedPolicy is null;
}
}
private ConfidenceLevel GetMinimumConfidenceLevel()
{
if (this.promptSource is BatchProcessingPromptSource.POLICY && this.selectedPolicy is not null)
return this.selectedPolicy.MinimumProviderConfidence;
var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.Component);
if (this.promptSource is BatchProcessingPromptSource.POLICY
&& this.selectedPolicy is not null
&& this.selectedPolicy.MinimumProviderConfidence > minimumLevel)
minimumLevel = this.selectedPolicy.MinimumProviderConfidence;
return ConfidenceLevel.NONE;
return minimumLevel;
}
private void ApplyFormDefaults()
{
var settings = this.SettingsManager.ConfigurationData.BatchProcessing;
if (!settings.PreselectOptions)
{
this.inputDirectory = string.Empty;
this.outputDirectory = string.Empty;
this.filePatterns = DataBatchProcessing.DEFAULT_FILE_PATTERNS;
this.includeSubdirectories = false;
this.promptSource = BatchProcessingPromptSource.FREE_PROMPT;
this.freePrompt = string.Empty;
this.promptFilePath = string.Empty;
this.selectedPolicy = null;
this.outputMode = BatchProcessingOutputMode.MARKDOWN_FILES;
this.resultColumnHeader = string.Empty;
this.csvFileName = string.Empty;
return;
}
this.inputDirectory = settings.InputDirectory;
this.outputDirectory = settings.OutputDirectory;
this.filePatterns = settings.FilePatterns;
this.includeSubdirectories = settings.IncludeSubdirectories;
this.promptSource = settings.PromptSource;
this.freePrompt = settings.FreePrompt;
this.promptFilePath = settings.PromptFilePath;
this.selectedPolicy = this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies
.FirstOrDefault(policy => policy.Id == settings.PreselectedPolicyId);
this.outputMode = settings.OutputMode;
this.resultColumnHeader = settings.ResultColumnHeader;
this.csvFileName = settings.CsvFileName;
}
private async Task LoadConfiguredPromptFileAsync()
{
this.promptFileLoadIssue = string.Empty;
if (this.promptSource is not BatchProcessingPromptSource.FILE_IMPORT || string.IsNullOrWhiteSpace(this.promptFilePath))
return;
this.importedPrompt = string.Empty;
if (!string.Equals(Path.GetExtension(this.promptFilePath), ".md", StringComparison.OrdinalIgnoreCase))
{
this.promptFileLoadIssue = T("The configured instructions file must be a Markdown file (*.md).");
return;
}
if (!File.Exists(this.promptFilePath))
{
this.promptFileLoadIssue = T("The configured instructions file no longer exists.");
return;
}
try
{
this.importedPrompt = await File.ReadAllTextAsync(this.promptFilePath);
if (string.IsNullOrWhiteSpace(this.importedPrompt))
this.promptFileLoadIssue = T("The configured instructions file is empty.");
}
catch (Exception exception)
{
this.Logger.LogError(exception, "Could not load the configured batch instructions file '{PromptFilePath}'.", this.promptFilePath);
this.promptFileLoadIssue = T("The configured instructions file could not be read.");
}
}
private void PromptSourceChanged(BatchProcessingPromptSource source)
{
this.promptSource = source;
if (source is BatchProcessingPromptSource.POLICY)
this.ApplyPolicyPreselection();
else
this.ResetProviderAndProfileSelection();
}
private void SelectedPolicyChanged(DataDocumentAnalysisPolicy? policy)
{
this.selectedPolicy = policy;
this.ApplyPolicyPreselection();
}
private void ApplyPolicyPreselection()
{
if (this.promptSource is not BatchProcessingPromptSource.POLICY || this.selectedPolicy is null)
return;
var minimumLevel = this.GetMinimumConfidenceLevel();
var policyProvider = this.SettingsManager.GetPreselectedProvider(this.Component, this.selectedPolicy.PreselectedProvider);
if (policyProvider != Settings.Provider.NONE
&& policyProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
this.ProviderSettings = policyProvider;
else
{
var fallbackProvider = this.SettingsManager.GetPreselectedProvider(this.Component, usePreselectionBeforeCurrentProvider: true);
this.ProviderSettings = fallbackProvider != Settings.Provider.NONE
&& fallbackProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel
? fallbackProvider
: Settings.Provider.NONE;
}
this.CurrentProfile = this.ResolvePolicyProfile();
}
private Profile ResolvePolicyProfile()
{
if (this.selectedPolicy is null)
return this.SettingsManager.GetPreselectedProfile(this.Component);
var policyProfile = ProfilePreselection.FromStoredValue(this.selectedPolicy.PreselectedProfile);
if (policyProfile.DoNotPreselectProfile)
return Profile.NO_PROFILE;
if (policyProfile.UseSpecificProfile)
{
var profile = this.SettingsManager.ConfigurationData.Profiles
.FirstOrDefault(candidate => candidate.Id == policyProfile.SpecificProfileId);
if (profile is not null)
return profile;
}
return this.SettingsManager.GetPreselectedProfile(this.Component);
}
}

View File

@ -0,0 +1,58 @@
@using AIStudio.Assistants.BatchProcessing
@using AIStudio.Settings
@inherits SettingsDialogBase
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6" Class="d-flex align-center">
<MudIcon Icon="@Icons.Material.Filled.BatchPrediction" Class="mr-2"/>
@T("Assistant: Batch Processing defaults")
</MudText>
</TitleContent>
<DialogContent>
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
<ConfigurationOption OptionDescription="@T("Preselect batch processing options?")" LabelOn="@T("Batch processing options are preselected")" LabelOff="@T("No batch processing options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.PreselectOptions)" StateUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.PreselectOptions = value)" OptionHelp="@T("When enabled, new batch runs start with the defaults configured below.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.PreselectOptions, out var meta) && meta.IsLocked"/>
<MudText Typo="Typo.h6" Class="mb-3">@T("Input")</MudText>
<ConfigurationText OptionDescription="@T("Default input folder")" Disabled="@this.DefaultsDisabled" Icon="@Icons.Material.Filled.Folder" Text="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.InputDirectory)" TextUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.InputDirectory = value)" OptionHelp="@T("Leave empty when an input folder should be selected for every batch run.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.InputDirectory, out var meta) && meta.IsLocked"/>
<ConfigurationText OptionDescription="@T("Default file patterns")" Disabled="@this.DefaultsDisabled" Icon="@Icons.Material.Filled.FilterAlt" Text="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.FilePatterns)" TextUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.FilePatterns = value)" OptionHelp="@T("Separate multiple file patterns with a semicolon, e.g., *.pdf;*.docx.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.FilePatterns, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Include subfolders by default?")" Disabled="@this.DefaultsDisabled" LabelOn="@T("Subfolders are included")" LabelOff="@T("Only the selected folder is processed")" State="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.IncludeSubdirectories)" StateUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.IncludeSubdirectories = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.IncludeSubdirectories, out var meta) && meta.IsLocked"/>
<MudText Typo="Typo.h6" Class="mb-3 mt-6">@T("Instructions")</MudText>
<ConfigurationSelect OptionDescription="@T("Default source of the instructions")" Disabled="@this.DefaultsDisabled" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.PromptSource)" Data="@this.PromptSourceData" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.PromptSource = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.PromptSource, out var meta) && meta.IsLocked"/>
@if (this.SettingsManager.ConfigurationData.BatchProcessing.PromptSource is BatchProcessingPromptSource.FREE_PROMPT)
{
<ConfigurationText OptionDescription="@T("Default prompt")" Disabled="@this.DefaultsDisabled" Icon="@Icons.Material.Filled.EditNote" NumLines="5" MaxLines="26" Text="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.FreePrompt)" TextUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.FreePrompt = value)" OptionHelp="@T("These instructions are applied to every document of a new batch run.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.FreePrompt, out var meta) && meta.IsLocked"/>
}
else if (this.SettingsManager.ConfigurationData.BatchProcessing.PromptSource is BatchProcessingPromptSource.FILE_IMPORT)
{
<ConfigurationText OptionDescription="@T("Default Markdown instructions file")" Disabled="@this.DefaultsDisabled" Icon="@Icons.Material.Filled.Description" Text="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.PromptFilePath)" TextUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.PromptFilePath = value)" OptionHelp="@T("The current content of this Markdown file is loaded whenever the defaults are applied.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.PromptFilePath, out var meta) && meta.IsLocked"/>
}
else
{
<ConfigurationSelect OptionDescription="@T("Default document analysis policy")" Disabled="@this.DefaultsDisabled" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.PreselectedPolicyId)" Data="@this.PolicyData" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.PreselectedPolicyId = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.PreselectedPolicyId, out var meta) && meta.IsLocked"/>
@if (this.SelectedPolicyMissing)
{
<MudAlert Severity="Severity.Warning" Dense="true" Class="mb-6">@T("The configured default policy no longer exists. Select another policy before starting a policy-based batch run.")</MudAlert>
}
}
<MudText Typo="Typo.h6" Class="mb-3 mt-6">@T("Output")</MudText>
<ConfigurationSelect OptionDescription="@T("Default output mode")" Disabled="@this.DefaultsDisabled" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.OutputMode)" Data="@this.OutputModeData" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.OutputMode = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.OutputMode, out var meta) && meta.IsLocked"/>
@if (this.SettingsManager.ConfigurationData.BatchProcessing.OutputMode is BatchProcessingOutputMode.TABLE_ONLY)
{
<ConfigurationText OptionDescription="@T("Default results table name")" Disabled="@this.DefaultsDisabled" Icon="@Icons.Material.Filled.Description" Text="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.CsvFileName)" TextUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.CsvFileName = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.CsvFileName, out var meta) && meta.IsLocked"/>
<ConfigurationText OptionDescription="@T("Default result column header")" Disabled="@this.DefaultsDisabled" Icon="@Icons.Material.Filled.TableChart" Text="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.ResultColumnHeader)" TextUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.ResultColumnHeader = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.ResultColumnHeader, out var meta) && meta.IsLocked"/>
}
<ConfigurationText OptionDescription="@T("Default output folder")" Disabled="@this.DefaultsDisabled" Icon="@Icons.Material.Filled.Folder" Text="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.OutputDirectory)" TextUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.OutputDirectory = value)" OptionHelp="@T("Leave empty to use the ai-results subfolder of the input folder.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.OutputDirectory, out var meta) && meta.IsLocked"/>
<MudText Typo="Typo.h6" Class="mb-3 mt-6">@T("AI selection")</MudText>
<ConfigurationMinConfidenceSelection Disabled="@this.DefaultsDisabled" RestrictToGlobalMinimumConfidence="true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.MinimumProviderConfidence)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.MinimumProviderConfidence = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.MinimumProviderConfidence, out var meta) && meta.IsLocked"/>
<ConfigurationProviderSelection Component="Components.BATCH_PROCESSING_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@this.DefaultsDisabled" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BatchProcessing.PreselectedProvider)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.PreselectedProvider = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.PreselectedProvider, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@T("Default profile")" Disabled="@this.DefaultsDisabled" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.BatchProcessing.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BatchProcessing.PreselectedProfile = value)" OptionHelp="@T("Choose whether batch runs should use the app default profile, no profile, or a specific profile.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.PreselectedProfile, out var meta) && meta.IsLocked"/>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Close" Variant="Variant.Filled">@T("Close")</MudButton>
</DialogActions>
</MudDialog>

View File

@ -0,0 +1,48 @@
using AIStudio.Assistants.BatchProcessing;
using AIStudio.Settings;
namespace AIStudio.Dialogs.Settings;
public partial class SettingsDialogBatchProcessing : SettingsDialogBase
{
private bool DefaultsDisabled() => !this.SettingsManager.ConfigurationData.BatchProcessing.PreselectOptions;
private IReadOnlyList<ConfigurationSelectData<BatchProcessingPromptSource>> PromptSourceData =>
[
.. Enum
.GetValues<BatchProcessingPromptSource>()
.Select(value => new ConfigurationSelectData<BatchProcessingPromptSource>(value.Name(), value))
];
private IReadOnlyList<ConfigurationSelectData<BatchProcessingOutputMode>> OutputModeData =>
[
.. Enum
.GetValues<BatchProcessingOutputMode>()
.Select(value => new ConfigurationSelectData<BatchProcessingOutputMode>(value.Name(), value))
];
private IReadOnlyList<ConfigurationSelectData<string>> PolicyData
{
get
{
var selectedPolicyId = this.SettingsManager.ConfigurationData.BatchProcessing.PreselectedPolicyId;
var policies = this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies
.Select(policy => new ConfigurationSelectData<string>(policy.PolicyName, policy.Id))
.ToList();
if (this.SelectedPolicyMissing)
policies.Add(new(string.Format(T("Missing policy ({0})"), selectedPolicyId), selectedPolicyId));
return policies;
}
}
private bool SelectedPolicyMissing
{
get
{
var selectedPolicyId = this.SettingsManager.ConfigurationData.BatchProcessing.PreselectedPolicyId;
return !string.IsNullOrWhiteSpace(selectedPolicyId) && this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.All(policy => policy.Id != selectedPolicyId);
}
}
}

View File

@ -394,6 +394,62 @@ CONFIG["SETTINGS"] = {}
-- CONFIG["SETTINGS"]["DataChat.PreselectedDataSourceIds.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataChat.SendToChatDataSourceBehavior.AllowUserOverride"] = true
-- Configure defaults for the Batch Processing Assistant.
-- Preselection must be enabled for the remaining batch settings to take effect.
-- CONFIG["SETTINGS"]["DataBatchProcessing.PreselectOptions"] = true
--
-- Configure the default input and output folders.
-- Leave the input folder empty to require a selection for every new batch run.
-- Leave the output folder empty to use the ai-results subfolder of the input folder.
-- CONFIG["SETTINGS"]["DataBatchProcessing.InputDirectory"] = ""
-- CONFIG["SETTINGS"]["DataBatchProcessing.OutputDirectory"] = ""
--
-- Configure the default file patterns and whether subfolders are included.
-- Separate multiple patterns with semicolons.
-- CONFIG["SETTINGS"]["DataBatchProcessing.FilePatterns"] = "*.pdf;*.docx;*.pptx;*.xlsx;*.md;*.txt"
-- CONFIG["SETTINGS"]["DataBatchProcessing.IncludeSubdirectories"] = false
--
-- Configure the default instruction source.
-- Allowed values are: FREE_PROMPT, FILE_IMPORT, POLICY
-- CONFIG["SETTINGS"]["DataBatchProcessing.PromptSource"] = "FREE_PROMPT"
-- CONFIG["SETTINGS"]["DataBatchProcessing.FreePrompt"] = "Summarize each document."
-- CONFIG["SETTINGS"]["DataBatchProcessing.PromptFilePath"] = ""
--
-- The policy ID must reference an entry in CONFIG["DOCUMENT_ANALYSIS_POLICIES"] or a
-- user-configured policy. It is used only when PromptSource is POLICY.
-- CONFIG["SETTINGS"]["DataBatchProcessing.PreselectedPolicyId"] = ""
--
-- Configure the default output mode.
-- Allowed values are: MARKDOWN_FILES, TABLE_ONLY
-- CONFIG["SETTINGS"]["DataBatchProcessing.OutputMode"] = "MARKDOWN_FILES"
-- CONFIG["SETTINGS"]["DataBatchProcessing.CsvFileName"] = "batch-results.csv"
-- CONFIG["SETTINGS"]["DataBatchProcessing.ResultColumnHeader"] = "Result"
--
-- Configure the minimum provider confidence and the default provider and profile.
-- Allowed confidence values are: NONE, UNTRUSTED, UNKNOWN, VERY_LOW, LOW, MODERATE, MEDIUM, HIGH
-- A policy can require a higher minimum confidence; the stricter level wins.
-- CONFIG["SETTINGS"]["DataBatchProcessing.MinimumProviderConfidence"] = "NONE"
-- CONFIG["SETTINGS"]["DataBatchProcessing.PreselectedProvider"] = "00000000-0000-0000-0000-000000000000"
-- Please note: an empty profile ID uses the app default profile; the all-zero ID uses no profile.
-- CONFIG["SETTINGS"]["DataBatchProcessing.PreselectedProfile"] = ""
--
-- Allow users to change individual managed batch defaults locally.
-- CONFIG["SETTINGS"]["DataBatchProcessing.PreselectOptions.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.InputDirectory.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.OutputDirectory.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.FilePatterns.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.IncludeSubdirectories.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.PromptSource.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.FreePrompt.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.PromptFilePath.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.PreselectedPolicyId.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.OutputMode.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.CsvFileName.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.ResultColumnHeader.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.MinimumProviderConfidence.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.PreselectedProvider.AllowUserOverride"] = true
-- CONFIG["SETTINGS"]["DataBatchProcessing.PreselectedProfile.AllowUserOverride"] = true
-- Configure the transcription provider for voice-to-text functionality.
-- It must be one of the transcription provider IDs defined in CONFIG["TRANSCRIPTION_PROVIDERS"].
-- Without a selected transcription provider, dictation and transcription features will be disabled.
@ -407,7 +463,8 @@ CONFIG["SETTINGS"] = {}
-- CODING_ASSISTANT, TEXT_SUMMARIZER_ASSISTANT, EMAIL_ASSISTANT,
-- LEGAL_CHECK_ASSISTANT, SYNONYMS_ASSISTANT, MY_TASKS_ASSISTANT,
-- JOB_POSTING_ASSISTANT, BIAS_DAY_ASSISTANT, ERI_ASSISTANT,
-- DOCUMENT_ANALYSIS_ASSISTANT, SLIDE_BUILDER_ASSISTANT, VISUAL_BRIEFING_ASSISTANT, I18N_ASSISTANT,
-- DOCUMENT_ANALYSIS_ASSISTANT, BATCH_PROCESSING_ASSISTANT, SLIDE_BUILDER_ASSISTANT,
-- VISUAL_BRIEFING_ASSISTANT, I18N_ASSISTANT,
-- LOG_VIEWER_ASSISTANT
--
-- Replaces, does not merge: a configuration with a higher priority replaces this list

View File

@ -136,6 +136,11 @@ public sealed class Data
public DataDocumentAnalysis DocumentAnalysis { get; init; } = new();
/// <summary>
/// Gets the managed Batch Processing Assistant defaults.
/// </summary>
public DataBatchProcessing BatchProcessing { get; init; } = new(x => x.BatchProcessing);
public DataMandatoryInformation MandatoryInformation { get; init; } = new();
public DataTextSummarizer TextSummarizer { get; init; } = new();

View File

@ -0,0 +1,52 @@
using System.Linq.Expressions;
using AIStudio.Assistants.BatchProcessing;
using AIStudio.Provider;
namespace AIStudio.Settings.DataModel;
/// <summary>
/// Stores managed defaults for the Batch Processing Assistant.
/// </summary>
/// <param name="configSelection">The managed-configuration selector.</param>
public sealed class DataBatchProcessing(Expression<Func<Data, DataBatchProcessing>>? configSelection = null)
{
public const string DEFAULT_FILE_PATTERNS = "*.pdf;*.docx;*.pptx;*.xlsx;*.md;*.txt";
/// <summary>
/// Initializes an unmanaged Batch Processing settings instance.
/// </summary>
public DataBatchProcessing() : this(null)
{
}
public bool PreselectOptions { get; set; } = ManagedConfiguration.Register(configSelection, value => value.PreselectOptions, false);
public string InputDirectory { get; set; } = ManagedConfiguration.Register(configSelection, value => value.InputDirectory, string.Empty);
public string OutputDirectory { get; set; } = ManagedConfiguration.Register(configSelection, value => value.OutputDirectory, string.Empty);
public string FilePatterns { get; set; } = ManagedConfiguration.Register(configSelection, value => value.FilePatterns, DEFAULT_FILE_PATTERNS);
public bool IncludeSubdirectories { get; set; } = ManagedConfiguration.Register(configSelection, value => value.IncludeSubdirectories, false);
public BatchProcessingPromptSource PromptSource { get; set; } = ManagedConfiguration.Register(configSelection, value => value.PromptSource, BatchProcessingPromptSource.FREE_PROMPT);
public string FreePrompt { get; set; } = ManagedConfiguration.Register(configSelection, value => value.FreePrompt, string.Empty);
public string PromptFilePath { get; set; } = ManagedConfiguration.Register(configSelection, value => value.PromptFilePath, string.Empty);
public string PreselectedPolicyId { get; set; } = ManagedConfiguration.Register(configSelection, value => value.PreselectedPolicyId, string.Empty);
public BatchProcessingOutputMode OutputMode { get; set; } = ManagedConfiguration.Register(configSelection, value => value.OutputMode, BatchProcessingOutputMode.MARKDOWN_FILES);
public string CsvFileName { get; set; } = ManagedConfiguration.Register(configSelection, value => value.CsvFileName, string.Empty);
public string ResultColumnHeader { get; set; } = ManagedConfiguration.Register(configSelection, value => value.ResultColumnHeader, string.Empty);
public ConfidenceLevel MinimumProviderConfidence { get; set; } = ManagedConfiguration.Register(configSelection, value => value.MinimumProviderConfidence, ConfidenceLevel.NONE);
public string PreselectedProvider { get; set; } = ManagedConfiguration.Register(configSelection, value => value.PreselectedProvider, string.Empty);
public string PreselectedProfile { get; set; } = ManagedConfiguration.Register(configSelection, value => value.PreselectedProfile, string.Empty);
}

View File

@ -157,9 +157,9 @@ public static class ComponentsExtensions
// We do this inside the Document Analysis Assistant component:
Components.DOCUMENT_ANALYSIS_ASSISTANT => ConfidenceLevel.NONE,
// The minimum confidence for the Batch Processing Assistant is set per policy
// as well. We do this inside the Batch Processing Assistant component:
Components.BATCH_PROCESSING_ASSISTANT => ConfidenceLevel.NONE,
// A policy-specific minimum is merged with this component default inside
// the Batch Processing Assistant; the stricter level wins.
Components.BATCH_PROCESSING_ASSISTANT => settingsManager.ConfigurationData.BatchProcessing.PreselectOptions ? settingsManager.ConfigurationData.BatchProcessing.MinimumProviderConfidence : default,
_ => default,
};
@ -192,6 +192,8 @@ public static class ComponentsExtensions
// The provider is selected per policy instead. We do this inside the Document Analysis Assistant component.
Components.DOCUMENT_ANALYSIS_ASSISTANT => Settings.Provider.NONE,
Components.BATCH_PROCESSING_ASSISTANT => settingsManager.ConfigurationData.BatchProcessing.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.BatchProcessing.PreselectedProvider) : null,
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedProvider) : null,
Components.AGENT_TEXT_CONTENT_CLEANER => settingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider) : null,
@ -218,6 +220,7 @@ public static class ComponentsExtensions
Components.ERI_ASSISTANT => settingsManager.ConfigurationData.ERI.PreselectOptions ? settingsManager.ConfigurationData.ERI.PreselectedProfile : string.Empty,
Components.SLIDE_BUILDER_ASSISTANT => settingsManager.ConfigurationData.SlideBuilder.PreselectOptions ? settingsManager.ConfigurationData.SlideBuilder.PreselectedProfile : string.Empty,
Components.VISUAL_BRIEFING_ASSISTANT => settingsManager.ConfigurationData.VisualBriefing.PreselectedProfile,
Components.BATCH_PROCESSING_ASSISTANT => settingsManager.ConfigurationData.BatchProcessing.PreselectOptions ? settingsManager.ConfigurationData.BatchProcessing.PreselectedProfile : string.Empty,
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.Chat.PreselectedProfile : string.Empty,
// The Document Analysis Assistant does not have a preselected profile at the component level.

View File

@ -337,6 +337,23 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
ManagedConfiguration.TryProcessConfiguration(x => x.Chat, x => x.PreselectedDataSourceIds, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.Chat, x => x.SendToChatDataSourceBehavior, this.Id, settingsTable, dryRun);
// Config: Batch Processing Assistant defaults?
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.PreselectOptions, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.InputDirectory, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.OutputDirectory, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.FilePatterns, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.IncludeSubdirectories, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.PromptSource, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.FreePrompt, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.PromptFilePath, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.PreselectedPolicyId, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.OutputMode, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.CsvFileName, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.ResultColumnHeader, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.MinimumProviderConfidence, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.PreselectedProvider, Guid.Empty, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.PreselectedProfile, this.Id, settingsTable, dryRun);
// Config: transcription provider?
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.UseTranscriptionProvider, Guid.Empty, this.Id, settingsTable, dryRun);