mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-17 04:17:59 +00:00
Fixed an incomplete plugin start with extensive enterprise configurations (#1033)
This commit is contained in:
1 parent
bdf828fa4f
commit
56215aa6dd
16 files changed
+493
-70
No files matched your search
@@ -186,7 +186,7 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool TryParseConfiguration(int idx, LuaTable table, Guid configPluginId, out DataSourceERI_V1 dataSource)
|
||||
public static bool TryParseConfiguration(int idx, LuaTable table, Guid configPluginId, bool dryRun, out DataSourceERI_V1 dataSource)
|
||||
{
|
||||
dataSource = default;
|
||||
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
|
||||
@@ -301,7 +301,7 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
|
||||
EnterpriseConfigurationPluginId = configPluginId,
|
||||
};
|
||||
|
||||
return TryQueueEnterpriseSecret(idx, table, configPluginId, dataSource);
|
||||
return TryQueueEnterpriseSecret(idx, table, configPluginId, dataSource, dryRun);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -360,7 +360,7 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
|
||||
""";
|
||||
}
|
||||
|
||||
private static bool TryQueueEnterpriseSecret(int idx, LuaTable table, Guid configPluginId, DataSourceERI_V1 dataSource)
|
||||
private static bool TryQueueEnterpriseSecret(int idx, LuaTable table, Guid configPluginId, DataSourceERI_V1 dataSource, bool dryRun)
|
||||
{
|
||||
var secretFieldName = dataSource.AuthMethod switch
|
||||
{
|
||||
@@ -397,6 +397,13 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// A dry run only checks the configuration, so it queues nothing: the start which follows it
|
||||
// would otherwise store the secret once more for every dry run before it:
|
||||
//
|
||||
if (dryRun)
|
||||
return true;
|
||||
|
||||
PendingEnterpriseSecrets.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{dataSource.Id}",
|
||||
dataSource.Name,
|
||||
|
||||
@@ -61,7 +61,7 @@ public sealed record EmbeddingProvider(
|
||||
|
||||
#endregion
|
||||
|
||||
public static bool TryParseEmbeddingProviderTable(int idx, LuaTable table, Guid configPluginId, string pluginPath, out ConfigurationBaseObject provider)
|
||||
public static bool TryParseEmbeddingProviderTable(int idx, LuaTable table, Guid configPluginId, string pluginPath, bool dryRun, out ConfigurationBaseObject provider)
|
||||
{
|
||||
provider = NONE;
|
||||
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
|
||||
@@ -189,13 +189,20 @@ public sealed record EmbeddingProvider(
|
||||
{
|
||||
if (encryption.TryDecrypt(apiKeyText, out var decryptedApiKey))
|
||||
{
|
||||
// Queue the API key for storage in the OS keyring:
|
||||
PendingEnterpriseApiKeys.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToSecretId()}",
|
||||
name,
|
||||
decryptedApiKey,
|
||||
SecretStoreType.EMBEDDING_PROVIDER));
|
||||
LOGGER.LogDebug($"Successfully decrypted API key for embedding provider {idx}. It will be stored in the OS keyring. (Plugin ID: {configPluginId})");
|
||||
//
|
||||
// Queue the API key for storage in the OS keyring. A dry run only checks the
|
||||
// configuration, so it queues nothing: the start which follows it would
|
||||
// otherwise store the key once more for every dry run before it:
|
||||
//
|
||||
if (!dryRun)
|
||||
{
|
||||
PendingEnterpriseApiKeys.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToSecretId()}",
|
||||
name,
|
||||
decryptedApiKey,
|
||||
SecretStoreType.EMBEDDING_PROVIDER));
|
||||
LOGGER.LogDebug($"Successfully decrypted API key for embedding provider {idx}. It will be stored in the OS keyring. (Plugin ID: {configPluginId})");
|
||||
}
|
||||
}
|
||||
else
|
||||
LOGGER.LogWarning($"Failed to decrypt API key for embedding provider {idx}. The encryption secret may be incorrect. (Plugin ID: {configPluginId})");
|
||||
|
||||
@@ -88,7 +88,7 @@ public sealed record Provider(
|
||||
|
||||
#endregion
|
||||
|
||||
public static bool TryParseProviderTable(int idx, LuaTable table, Guid configPluginId, string pluginPath, out ConfigurationBaseObject provider)
|
||||
public static bool TryParseProviderTable(int idx, LuaTable table, Guid configPluginId, string pluginPath, bool dryRun, out ConfigurationBaseObject provider)
|
||||
{
|
||||
provider = NONE;
|
||||
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
|
||||
@@ -210,13 +210,20 @@ public sealed record Provider(
|
||||
{
|
||||
if (encryption.TryDecrypt(apiKeyText, out var decryptedApiKey))
|
||||
{
|
||||
// Queue the API key for storage in the OS keyring:
|
||||
PendingEnterpriseApiKeys.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToSecretId()}",
|
||||
instanceName,
|
||||
decryptedApiKey,
|
||||
SecretStoreType.LLM_PROVIDER));
|
||||
LOGGER.LogDebug($"Successfully decrypted API key for provider {idx}. It will be stored in the OS keyring. (Plugin ID: {configPluginId})");
|
||||
//
|
||||
// Queue the API key for storage in the OS keyring. A dry run only checks the
|
||||
// configuration, so it queues nothing: the start which follows it would
|
||||
// otherwise store the key once more for every dry run before it:
|
||||
//
|
||||
if (!dryRun)
|
||||
{
|
||||
PendingEnterpriseApiKeys.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToSecretId()}",
|
||||
instanceName,
|
||||
decryptedApiKey,
|
||||
SecretStoreType.LLM_PROVIDER));
|
||||
LOGGER.LogDebug($"Successfully decrypted API key for provider {idx}. It will be stored in the OS keyring. (Plugin ID: {configPluginId})");
|
||||
}
|
||||
}
|
||||
else
|
||||
LOGGER.LogWarning($"Failed to decrypt API key for provider {idx}. The encryption secret may be incorrect. (Plugin ID: {configPluginId})");
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Provider;
|
||||
@@ -30,6 +32,16 @@ public sealed class SettingsManager
|
||||
Converters = { new TolerantEnumConverter() },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// From this duration on, storing the settings is reported as slow.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The settings file is a few hundred kilobytes at most, so storing it usually takes some
|
||||
/// milliseconds. Far beyond that, something is blocking: on one machine, every store took 18
|
||||
/// seconds, and each one held up the start of AI Studio.
|
||||
/// </remarks>
|
||||
private static readonly TimeSpan SLOW_STORE_THRESHOLD = TimeSpan.FromSeconds(1);
|
||||
|
||||
private readonly ILogger<SettingsManager> logger;
|
||||
private readonly RustService rustService;
|
||||
|
||||
@@ -337,10 +349,24 @@ public sealed class SettingsManager
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Serializing and writing are measured on their own, because a slow store has two
|
||||
// different causes: a property which asks the file system or the network while it is
|
||||
// serialized, or a file system which is slow to write, e.g., because of a virus scanner
|
||||
// or a synchronized profile folder. Only the two durations tell them apart:
|
||||
//
|
||||
var serializingStartedAt = Stopwatch.GetTimestamp();
|
||||
var settingsJson = JsonSerializer.Serialize(this.ConfigurationData, JSON_OPTIONS);
|
||||
var serializingDuration = Stopwatch.GetElapsedTime(serializingStartedAt);
|
||||
|
||||
var writingStartedAt = Stopwatch.GetTimestamp();
|
||||
var settingsPath = Path.Combine(ConfigDirectory!, SETTINGS_FILENAME);
|
||||
await this.StoreSerializedSettings(settingsJson, settingsPath);
|
||||
await this.StoreSerializedVersionBackup(this.ConfigurationData.Version, settingsJson);
|
||||
var writingDuration = Stopwatch.GetElapsedTime(writingStartedAt);
|
||||
|
||||
if (serializingDuration + writingDuration >= SLOW_STORE_THRESHOLD)
|
||||
this.logger.LogWarning($"Storing the settings took {(serializingDuration + writingDuration).TotalMilliseconds:F0} ms, which is unusually long: serializing took {serializingDuration.TotalMilliseconds:F0} ms, writing took {writingDuration.TotalMilliseconds:F0} ms. The settings are {Encoding.UTF8.GetByteCount(settingsJson) / 1024} KB. A slow serialization points to a setting which reaches out to the file system or the network while it is serialized; slow writing points to the file system itself, e.g., a virus scanner or a synchronized profile folder.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@ public sealed record TranscriptionProvider(
|
||||
|
||||
#endregion
|
||||
|
||||
public static bool TryParseTranscriptionProviderTable(int idx, LuaTable table, Guid configPluginId, string pluginPath, out ConfigurationBaseObject provider)
|
||||
public static bool TryParseTranscriptionProviderTable(int idx, LuaTable table, Guid configPluginId, string pluginPath, bool dryRun, out ConfigurationBaseObject provider)
|
||||
{
|
||||
provider = NONE;
|
||||
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
|
||||
@@ -160,13 +160,20 @@ public sealed record TranscriptionProvider(
|
||||
{
|
||||
if (encryption.TryDecrypt(apiKeyText, out var decryptedApiKey))
|
||||
{
|
||||
// Queue the API key for storage in the OS keyring:
|
||||
PendingEnterpriseApiKeys.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToSecretId()}",
|
||||
name,
|
||||
decryptedApiKey,
|
||||
SecretStoreType.TRANSCRIPTION_PROVIDER));
|
||||
LOGGER.LogDebug($"Successfully decrypted API key for transcription provider {idx}. It will be stored in the OS keyring. (Plugin ID: {configPluginId})");
|
||||
//
|
||||
// Queue the API key for storage in the OS keyring. A dry run only checks the
|
||||
// configuration, so it queues nothing: the start which follows it would
|
||||
// otherwise store the key once more for every dry run before it:
|
||||
//
|
||||
if (!dryRun)
|
||||
{
|
||||
PendingEnterpriseApiKeys.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToSecretId()}",
|
||||
name,
|
||||
decryptedApiKey,
|
||||
SecretStoreType.TRANSCRIPTION_PROVIDER));
|
||||
LOGGER.LogDebug($"Successfully decrypted API key for transcription provider {idx}. It will be stored in the OS keyring. (Plugin ID: {configPluginId})");
|
||||
}
|
||||
}
|
||||
else
|
||||
LOGGER.LogWarning($"Failed to decrypt API key for transcription provider {idx}. The encryption secret may be incorrect. (Plugin ID: {configPluginId})");
|
||||
|
||||
Reference in new issue
Block a user