diff --git a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor
index c1c1d8f1..4f57754d 100644
--- a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor
+++ b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor
@@ -64,7 +64,7 @@ else
-
+
@T("Analysis and output rules")
diff --git a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor.cs b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor.cs
index 94ad313a..bbdcb994 100644
--- a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor.cs
+++ b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor.cs
@@ -283,6 +283,8 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore
public uint NextChatTemplateNum { get; set; } = 1;
+
+ ///
+ /// The next document analysis policy number to use.
+ ///
+ public uint NextDocumentAnalysisPolicyNum { get; set; } = 1;
public DataApp App { get; init; } = new(x => x.App);
diff --git a/app/MindWork AI Studio/Settings/DataModel/DataDocumentAnalysisPolicy.cs b/app/MindWork AI Studio/Settings/DataModel/DataDocumentAnalysisPolicy.cs
index aae8ede1..bbfee658 100644
--- a/app/MindWork AI Studio/Settings/DataModel/DataDocumentAnalysisPolicy.cs
+++ b/app/MindWork AI Studio/Settings/DataModel/DataDocumentAnalysisPolicy.cs
@@ -1,16 +1,37 @@
using AIStudio.Provider;
+using AIStudio.Tools.PluginSystem;
+
+using Lua;
namespace AIStudio.Settings.DataModel;
-public sealed class DataDocumentAnalysisPolicy
+public sealed record DataDocumentAnalysisPolicy : ConfigurationBaseObject
{
+ private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger();
+
+ ///
+ public override string Id { get; init; } = string.Empty;
+
+ ///
+ public override uint Num { get; init; }
+
+ ///
+ public override string Name
+ {
+ get => this.PolicyName;
+ init => this.PolicyName = value;
+ }
+
+ ///
+ public override bool IsEnterpriseConfiguration { get; init; }
+
///
- /// Preselect the policy name?
+ /// The name of the document analysis policy.
///
public string PolicyName { get; set; } = string.Empty;
///
- /// Preselect the policy description?
+ /// The description of the document analysis policy.
///
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.
///
public bool IsProtected { get; set; }
-
- ///
- /// Is this a managed policy? Managed policies are created and managed by the organization
- /// and cannot be modified or deleted by the user.
- ///
- public bool IsManaged { get; set; }
+
+ ///
+ public override Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty;
///
/// The rules for the document analysis policy.
@@ -49,4 +67,74 @@ public sealed class DataDocumentAnalysisPolicy
/// Preselect a profile?
///
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(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(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(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(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(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(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(out var providerId))
+ preselectedProvider = providerId;
+
+ var preselectedProfile = string.Empty;
+ if (table.TryGetValue("PreselectedProfile", out var profileValue) && profileValue.TryRead(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;
+ }
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs
index e85c8eba..29d95e76 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs
@@ -88,6 +88,9 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
// Handle configured profiles:
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?
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.PreselectedProfile, Guid.Empty, this.Id, settingsTable, dryRun);
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObject.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObject.cs
index da5c46c2..647c79bf 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObject.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObject.cs
@@ -70,6 +70,7 @@ public sealed record PluginConfigurationObject
PluginConfigurationObjectType.EMBEDDING_PROVIDER => "EMBEDDING_PROVIDERS",
PluginConfigurationObjectType.TRANSCRIPTION_PROVIDER => "TRANSCRIPTION_PROVIDERS",
PluginConfigurationObjectType.PROFILE => "PROFILES",
+ PluginConfigurationObjectType.DOCUMENT_ANALYSIS_POLICY => "DOCUMENT_ANALYSIS_POLICIES",
_ => 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.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.DOCUMENT_ANALYSIS_POLICY => (DataDocumentAnalysisPolicy.TryProcessConfiguration(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject is DataDocumentAnalysisPolicy, configurationObject),
_ => (false, NoConfigurationObject.INSTANCE)
};
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObjectType.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObjectType.cs
index 82931873..4236af12 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObjectType.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObjectType.cs
@@ -11,4 +11,5 @@ public enum PluginConfigurationObjectType
CHAT_TEMPLATE,
EMBEDDING_PROVIDER,
TRANSCRIPTION_PROVIDER,
+ DOCUMENT_ANALYSIS_POLICY,
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs
index bdfdba81..b2b45aba 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs
@@ -148,6 +148,10 @@ public static partial class PluginFactory
// Check profiles:
if(PluginConfigurationObject.CleanLeftOverConfigurationObjects(PluginConfigurationObjectType.PROFILE, x => x.Profiles, AVAILABLE_PLUGINS, configObjectList))
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:
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.PreselectedProfile, AVAILABLE_PLUGINS))
diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.2.1.md b/app/MindWork AI Studio/wwwroot/changelog/v26.2.1.md
index e1c8b7c2..9d465ece 100644
--- a/app/MindWork AI Studio/wwwroot/changelog/v26.2.1.md
+++ b/app/MindWork AI Studio/wwwroot/changelog/v26.2.1.md
@@ -1,3 +1,4 @@
# 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 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.
\ No newline at end of file