mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-09-01 17:49:14 +00:00
Fixed issues when multiple configurations intro texts or mandatory infos share the same ID (#932)
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
parent
7d9a4f5ab1
commit
afdcc0455d
@ -385,18 +385,89 @@ public static partial class PluginFactory
|
||||
|
||||
public static IReadOnlyList<DataMandatoryInfo> GetMandatoryInfos()
|
||||
{
|
||||
return RUNNING_PLUGINS
|
||||
.OfType<PluginConfiguration>()
|
||||
.SelectMany(plugin => plugin.MandatoryInfos)
|
||||
.ToList();
|
||||
return ResolveLivePluginContent<DataMandatoryInfo>("mandatory info", plugin => plugin.MandatoryInfos).ToList();
|
||||
}
|
||||
|
||||
public static IReadOnlyList<DataIntroduction> GetIntroductions()
|
||||
{
|
||||
return RUNNING_PLUGINS
|
||||
.OfType<PluginConfiguration>()
|
||||
.SelectMany(plugin => plugin.Introductions)
|
||||
return ResolveLivePluginContent<DataIntroduction>("introduction", plugin => plugin.Introductions)
|
||||
.OrderBy(introduction => introduction.Index)
|
||||
.ThenBy(introduction => introduction.Id, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects live content from all running configuration plugins, so that each content ID appears exactly once.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The IDs of live content are chosen by whoever writes the configuration, so two configuration
|
||||
/// plugins may use the same ID. We resolve such a collision the same way a collision on a setting
|
||||
/// is resolved: a configuration which acts on behalf of the organization wins, so nobody can push
|
||||
/// aside what an organization deployed. Among configurations of the same origin, the declared
|
||||
/// priority decides, and when even that is equal, the plugin which started later wins.<br/><br/>
|
||||
/// Duplicates are not merely a cosmetic problem: the home page keys its panels by the introduction
|
||||
/// ID, and the acceptance of a mandatory info is stored per ID as well.
|
||||
/// </remarks>
|
||||
/// <param name="contentKind">The kind of content, used to report a collision in the log.</param>
|
||||
/// <param name="selector">Selects the content of one configuration plugin.</param>
|
||||
/// <typeparam name="T">The type of the live plugin content.</typeparam>
|
||||
/// <returns>The content of all configuration plugins, with every ID resolved to one winner.</returns>
|
||||
private static IEnumerable<T> ResolveLivePluginContent<T>(string contentKind, Func<PluginConfiguration, IEnumerable<T>> selector) where T : ILivePluginContent
|
||||
{
|
||||
var contentById = new Dictionary<string, (T Content, int Authority, int Priority)>(StringComparer.Ordinal);
|
||||
foreach (var plugin in RUNNING_PLUGINS.OfType<PluginConfiguration>())
|
||||
{
|
||||
var authority = GetConfigurationAuthority(plugin.PluginPath);
|
||||
foreach (var content in selector(plugin))
|
||||
{
|
||||
if (contentById.TryGetValue(content.Id, out var currentWinner))
|
||||
{
|
||||
//
|
||||
// The candidate needs the higher authority to take over. Within the same
|
||||
// authority, the higher priority wins, and an equal priority falls back to the
|
||||
// start order, where the plugin processed later wins:
|
||||
//
|
||||
var isTakingOver = authority > currentWinner.Authority || (authority == currentWinner.Authority && plugin.Priority >= currentWinner.Priority);
|
||||
var winnerPluginId = isTakingOver ? content.EnterpriseConfigurationPluginId : currentWinner.Content.EnterpriseConfigurationPluginId;
|
||||
var ignoredPluginId = isTakingOver ? currentWinner.Content.EnterpriseConfigurationPluginId : content.EnterpriseConfigurationPluginId;
|
||||
|
||||
if (winnerPluginId == ignoredPluginId)
|
||||
LOG.LogWarning($"The configuration plugin '{winnerPluginId}' defines the {contentKind} ID '{content.Id}' more than once. Using its last definition and ignoring the earlier one. Please use each ID only once.");
|
||||
else
|
||||
{
|
||||
var reason = isTakingOver
|
||||
? DescribeConfigurationPrecedence(authority, plugin.Priority, currentWinner.Authority, currentWinner.Priority)
|
||||
: DescribeConfigurationPrecedence(currentWinner.Authority, currentWinner.Priority, authority, plugin.Priority);
|
||||
|
||||
LOG.LogWarning($"Multiple configuration plugins define the {contentKind} ID '{content.Id}'. Using the one from the configuration plugin '{winnerPluginId}' and ignoring the one from the configuration plugin '{ignoredPluginId}', because {reason}.");
|
||||
}
|
||||
|
||||
if (!isTakingOver)
|
||||
continue;
|
||||
}
|
||||
|
||||
contentById[content.Id] = (content, authority, plugin.Priority);
|
||||
}
|
||||
}
|
||||
|
||||
return contentById.Values.Select(entry => entry.Content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explains in one phrase why one configuration plugin won a collision against another.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Administrators read this in the log while they are testing their configuration. Naming the
|
||||
/// deciding rule saves them from guessing why their change had no effect.
|
||||
/// </remarks>
|
||||
private static string DescribeConfigurationPrecedence(int winnerAuthority, int winnerPriority, int ignoredAuthority, int ignoredPriority)
|
||||
{
|
||||
if (winnerAuthority != ignoredAuthority)
|
||||
return "a configuration which acts on behalf of your organization takes precedence over a locally placed one";
|
||||
|
||||
if (winnerPriority != ignoredPriority)
|
||||
return $"it declares the higher priority ({winnerPriority} instead of {ignoredPriority})";
|
||||
|
||||
return $"both declare the same priority ({winnerPriority}), so the configuration plugin which started later wins";
|
||||
}
|
||||
}
|
||||
@ -4,4 +4,5 @@
|
||||
- Improved the preview for large documents. It now shows you the beginning of your document instead of loading all of it, so the dialog opens right away. Your complete document still goes to the AI.
|
||||
- Fixed AI Studio reading a document to the end even after you closed its preview. Closing the dialog now stops that work immediately.
|
||||
- Fixed AI Studio holding on to finished chats, presentation images, and plugin data. It releases them now, so memory no longer grows the longer you keep the app running.
|
||||
- Fixed assistants handing an outdated result to the chat. When you sent several results without opening the chat in between, you now receive the one you sent last.
|
||||
- Fixed assistants handing an outdated result to the chat. When you sent several results without opening the chat in between, you now receive the one you sent last.
|
||||
- Fixed rare issues when multiple configurations provided introduction texts or mandatory information under the same ID.
|
||||
Loading…
Reference in New Issue
Block a user