Add support for document analysis policy configuration

This commit is contained in:
Thorsten Sommer 2026-01-31 20:50:10 +01:00
parent cac7f70b16
commit f6091be1b0
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
10 changed files with 145 additions and 10 deletions

View File

@ -64,7 +64,7 @@ else
<ProfileFormSelection Disabled="@this.IsNoPolicySelected" Profile="@this.currentProfile" ProfileChanged="@this.PolicyPreselectedProfileWasChangedAsync" /> <ProfileFormSelection Disabled="@this.IsNoPolicySelected" Profile="@this.currentProfile" ProfileChanged="@this.PolicyPreselectedProfileWasChangedAsync" />
<MudTextSwitch Disabled="@(this.IsNoPolicySelected || (this.selectedPolicy?.IsManaged ?? 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")" /> <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")" />
<MudText Typo="Typo.h5" Class="mt-6 mb-1"> <MudText Typo="Typo.h5" Class="mt-6 mb-1">
@T("Analysis and output rules") @T("Analysis and output rules")

View File

@ -283,6 +283,8 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
{ {
this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Add(new () this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Add(new ()
{ {
Id = Guid.NewGuid().ToString(),
Num = this.SettingsManager.ConfigurationData.NextDocumentAnalysisPolicyNum++,
PolicyName = string.Format(T("Policy {0}"), DateTimeOffset.UtcNow), PolicyName = string.Format(T("Policy {0}"), DateTimeOffset.UtcNow),
}); });

View File

@ -213,6 +213,35 @@ CONFIG["CHAT_TEMPLATES"][#CONFIG["CHAT_TEMPLATES"]+1] = {
} }
} }
} }
-- Document analysis policies for this configuration:
CONFIG["DOCUMENT_ANALYSIS_POLICIES"] = {}
-- An example document analysis policy:
-- CONFIG["DOCUMENT_ANALYSIS_POLICIES"][#CONFIG["DOCUMENT_ANALYSIS_POLICIES"]+1] = {
-- ["Id"] = "00000000-0000-0000-0000-000000000000",
-- ["PolicyName"] = "Compliance Summary Policy",
-- ["PolicyDescription"] = "Summarizes compliance-relevant clauses, obligations, and deadlines found in provided documents.",
--
-- ["AnalysisRules"] = [===[
-- Focus on compliance obligations, deadlines, and required actions.
-- Ignore marketing content and high-level summaries.
-- Flag any ambiguous or missing information.
-- ]===],
--
-- ["OutputRules"] = [===[
-- Provide a Markdown report with headings for Obligations, Deadlines,
-- and Open Questions.
-- ]===],
--
-- -- Optional: minimum provider confidence required for this policy.
-- -- Allowed values are: NONE, VERY_LOW, LOW, MODERATE, MEDIUM, HIGH
-- ["MinimumProviderConfidence"] = "MEDIUM",
--
-- -- Optional: preselect a provider or profile by ID.
-- -- The IDs must exist in CONFIG["LLM_PROVIDERS"] or CONFIG["PROFILES"].
-- ["PreselectedProvider"] = "00000000-0000-0000-0000-000000000000",
-- ["PreselectedProfile"] = "00000000-0000-0000-0000-000000000000"
-- }
-- Profiles for this configuration: -- Profiles for this configuration:
CONFIG["PROFILES"] = {} CONFIG["PROFILES"] = {}

View File

@ -80,6 +80,11 @@ public sealed class Data
/// The next chat template number to use. /// The next chat template number to use.
/// </summary> /// </summary>
public uint NextChatTemplateNum { get; set; } = 1; public uint NextChatTemplateNum { get; set; } = 1;
/// <summary>
/// The next document analysis policy number to use.
/// </summary>
public uint NextDocumentAnalysisPolicyNum { get; set; } = 1;
public DataApp App { get; init; } = new(x => x.App); public DataApp App { get; init; } = new(x => x.App);

View File

@ -1,16 +1,37 @@
using AIStudio.Provider; using AIStudio.Provider;
using AIStudio.Tools.PluginSystem;
using Lua;
namespace AIStudio.Settings.DataModel; namespace AIStudio.Settings.DataModel;
public sealed class DataDocumentAnalysisPolicy public sealed record DataDocumentAnalysisPolicy : ConfigurationBaseObject
{ {
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger<DataDocumentAnalysisPolicy>();
/// <inheritdoc />
public override string Id { get; init; } = string.Empty;
/// <inheritdoc />
public override uint Num { get; init; }
/// <inheritdoc />
public override string Name
{
get => this.PolicyName;
init => this.PolicyName = value;
}
/// <inheritdoc />
public override bool IsEnterpriseConfiguration { get; init; }
/// <summary> /// <summary>
/// Preselect the policy name? /// The name of the document analysis policy.
/// </summary> /// </summary>
public string PolicyName { get; set; } = string.Empty; public string PolicyName { get; set; } = string.Empty;
/// <summary> /// <summary>
/// Preselect the policy description? /// The description of the document analysis policy.
/// </summary> /// </summary>
public string PolicyDescription { get; set; } = string.Empty; public string PolicyDescription { get; set; } = string.Empty;
@ -18,12 +39,9 @@ public sealed class DataDocumentAnalysisPolicy
/// Is this policy protected? If so, it cannot be deleted or modified by the user. /// Is this policy protected? If so, it cannot be deleted or modified by the user.
/// </summary> /// </summary>
public bool IsProtected { get; set; } public bool IsProtected { get; set; }
/// <summary> /// <inheritdoc />
/// Is this a managed policy? Managed policies are created and managed by the organization public override Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty;
/// and cannot be modified or deleted by the user.
/// </summary>
public bool IsManaged { get; set; }
/// <summary> /// <summary>
/// The rules for the document analysis policy. /// The rules for the document analysis policy.
@ -49,4 +67,74 @@ public sealed class DataDocumentAnalysisPolicy
/// Preselect a profile? /// Preselect a profile?
/// </summary> /// </summary>
public string PreselectedProfile { get; set; } = string.Empty; public string PreselectedProfile { get; set; } = string.Empty;
public static bool TryProcessConfiguration(int idx, LuaTable table, Guid configPluginId, out ConfigurationBaseObject policy)
{
policy = new DataDocumentAnalysisPolicy();
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
{
LOG.LogWarning("The configured document analysis policy {PolicyIndex} does not contain a valid ID. The ID must be a valid GUID.", idx);
return false;
}
if (!table.TryGetValue("PolicyName", out var nameValue) || !nameValue.TryRead<string>(out var name) || string.IsNullOrWhiteSpace(name))
{
LOG.LogWarning("The configured document analysis policy {PolicyIndex} does not contain a valid PolicyName field.", idx);
return false;
}
if (!table.TryGetValue("PolicyDescription", out var descriptionValue) || !descriptionValue.TryRead<string>(out var description) || string.IsNullOrWhiteSpace(description))
{
LOG.LogWarning("The configured document analysis policy {PolicyIndex} does not contain a valid PolicyDescription field.", idx);
return false;
}
if (!table.TryGetValue("AnalysisRules", out var analysisRulesValue) || !analysisRulesValue.TryRead<string>(out var analysisRules) || string.IsNullOrWhiteSpace(analysisRules))
{
LOG.LogWarning("The configured document analysis policy {PolicyIndex} does not contain valid AnalysisRules field.", idx);
return false;
}
if (!table.TryGetValue("OutputRules", out var outputRulesValue) || !outputRulesValue.TryRead<string>(out var outputRules) || string.IsNullOrWhiteSpace(outputRules))
{
LOG.LogWarning("The configured document analysis policy {PolicyIndex} does not contain valid OutputRules field.", idx);
return false;
}
var minimumConfidence = ConfidenceLevel.NONE;
if (table.TryGetValue("MinimumProviderConfidence", out var minConfValue) && minConfValue.TryRead<string>(out var minConfText))
{
if (!Enum.TryParse(minConfText, true, out minimumConfidence))
{
LOG.LogWarning("The configured document analysis policy {PolicyIndex} contains an invalid MinimumProviderConfidence: {ConfidenceLevel}.", idx, minConfText);
minimumConfidence = ConfidenceLevel.NONE;
}
}
var preselectedProvider = string.Empty;
if (table.TryGetValue("PreselectedProvider", out var providerValue) && providerValue.TryRead<string>(out var providerId))
preselectedProvider = providerId;
var preselectedProfile = string.Empty;
if (table.TryGetValue("PreselectedProfile", out var profileValue) && profileValue.TryRead<string>(out var profileId))
preselectedProfile = profileId;
policy = new DataDocumentAnalysisPolicy
{
Id = id.ToString(),
Num = 0, // will be set later by the PluginConfigurationObject
PolicyName = name,
PolicyDescription = description,
AnalysisRules = analysisRules,
OutputRules = outputRules,
MinimumProviderConfidence = minimumConfidence,
PreselectedProvider = preselectedProvider,
PreselectedProfile = preselectedProfile,
IsProtected = true,
IsEnterpriseConfiguration = true,
EnterpriseConfigurationPluginId = configPluginId,
};
return true;
}
} }

View File

@ -88,6 +88,9 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
// Handle configured profiles: // Handle configured profiles:
PluginConfigurationObject.TryParse(PluginConfigurationObjectType.PROFILE, x => x.Profiles, x => x.NextProfileNum, mainTable, this.Id, ref this.configObjects, dryRun); PluginConfigurationObject.TryParse(PluginConfigurationObjectType.PROFILE, x => x.Profiles, x => x.NextProfileNum, mainTable, this.Id, ref this.configObjects, dryRun);
// Handle configured document analysis policies:
PluginConfigurationObject.TryParse(PluginConfigurationObjectType.DOCUMENT_ANALYSIS_POLICY, x => x.DocumentAnalysis.Policies, x => x.NextDocumentAnalysisPolicyNum, mainTable, this.Id, ref this.configObjects, dryRun);
// Config: preselected profile? // Config: preselected profile?
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.PreselectedProfile, Guid.Empty, this.Id, settingsTable, dryRun); ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.PreselectedProfile, Guid.Empty, this.Id, settingsTable, dryRun);

View File

@ -70,6 +70,7 @@ public sealed record PluginConfigurationObject
PluginConfigurationObjectType.EMBEDDING_PROVIDER => "EMBEDDING_PROVIDERS", PluginConfigurationObjectType.EMBEDDING_PROVIDER => "EMBEDDING_PROVIDERS",
PluginConfigurationObjectType.TRANSCRIPTION_PROVIDER => "TRANSCRIPTION_PROVIDERS", PluginConfigurationObjectType.TRANSCRIPTION_PROVIDER => "TRANSCRIPTION_PROVIDERS",
PluginConfigurationObjectType.PROFILE => "PROFILES", PluginConfigurationObjectType.PROFILE => "PROFILES",
PluginConfigurationObjectType.DOCUMENT_ANALYSIS_POLICY => "DOCUMENT_ANALYSIS_POLICIES",
_ => null, _ => null,
}; };
@ -105,6 +106,7 @@ public sealed record PluginConfigurationObject
PluginConfigurationObjectType.PROFILE => (Profile.TryParseProfileTable(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject != Profile.NO_PROFILE, configurationObject), PluginConfigurationObjectType.PROFILE => (Profile.TryParseProfileTable(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject != Profile.NO_PROFILE, configurationObject),
PluginConfigurationObjectType.TRANSCRIPTION_PROVIDER => (TranscriptionProvider.TryParseTranscriptionProviderTable(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject != TranscriptionProvider.NONE, configurationObject), PluginConfigurationObjectType.TRANSCRIPTION_PROVIDER => (TranscriptionProvider.TryParseTranscriptionProviderTable(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject != TranscriptionProvider.NONE, configurationObject),
PluginConfigurationObjectType.EMBEDDING_PROVIDER => (EmbeddingProvider.TryParseEmbeddingProviderTable(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject != EmbeddingProvider.NONE, configurationObject), PluginConfigurationObjectType.EMBEDDING_PROVIDER => (EmbeddingProvider.TryParseEmbeddingProviderTable(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject != EmbeddingProvider.NONE, configurationObject),
PluginConfigurationObjectType.DOCUMENT_ANALYSIS_POLICY => (DataDocumentAnalysisPolicy.TryProcessConfiguration(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject is DataDocumentAnalysisPolicy, configurationObject),
_ => (false, NoConfigurationObject.INSTANCE) _ => (false, NoConfigurationObject.INSTANCE)
}; };

View File

@ -11,4 +11,5 @@ public enum PluginConfigurationObjectType
CHAT_TEMPLATE, CHAT_TEMPLATE,
EMBEDDING_PROVIDER, EMBEDDING_PROVIDER,
TRANSCRIPTION_PROVIDER, TRANSCRIPTION_PROVIDER,
DOCUMENT_ANALYSIS_POLICY,
} }

View File

@ -148,6 +148,10 @@ public static partial class PluginFactory
// Check profiles: // Check profiles:
if(PluginConfigurationObject.CleanLeftOverConfigurationObjects(PluginConfigurationObjectType.PROFILE, x => x.Profiles, AVAILABLE_PLUGINS, configObjectList)) if(PluginConfigurationObject.CleanLeftOverConfigurationObjects(PluginConfigurationObjectType.PROFILE, x => x.Profiles, AVAILABLE_PLUGINS, configObjectList))
wasConfigurationChanged = true; wasConfigurationChanged = true;
// Check document analysis policies:
if(PluginConfigurationObject.CleanLeftOverConfigurationObjects(PluginConfigurationObjectType.DOCUMENT_ANALYSIS_POLICY, x => x.DocumentAnalysis.Policies, AVAILABLE_PLUGINS, configObjectList))
wasConfigurationChanged = true;
// Check for a preselected profile: // Check for a preselected profile:
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.PreselectedProfile, AVAILABLE_PLUGINS)) if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.PreselectedProfile, AVAILABLE_PLUGINS))

View File

@ -1,3 +1,4 @@
# v26.2.1, build 233 (2026-02-xx xx:xx UTC) # v26.2.1, build 233 (2026-02-xx xx:xx UTC)
- Added the ability to individually configure the minimum confidence level, standard profile, and default provider for each policy in the Document Analysis Assistant (in preview). - Added the ability to individually configure the minimum confidence level, standard profile, and default provider for each policy in the Document Analysis Assistant (in preview).
- Added support for defining document analysis policies (in preview) by configuration plugins, enabling centralized management of policies across entire departments or organizations.
- Fixed a bug where the global minimum confidence level was not being applied to the assistants. - Fixed a bug where the global minimum confidence level was not being applied to the assistants.