using System.Text.Json.Serialization; using AIStudio.Provider; using AIStudio.Tools.PluginSystem; using Lua; using Host = AIStudio.Provider.SelfHosted.Host; namespace AIStudio.Settings; public sealed record TranscriptionProvider( uint Num, string Id, string Name, LLMProviders UsedLLMProvider, Model Model, bool IsSelfHosted = false, bool IsEnterpriseConfiguration = false, Guid EnterpriseConfigurationPluginId = default, string Hostname = "http://localhost:1234", Host Host = Host.NONE) : ConfigurationBaseObject, ISecretId { private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(); public static readonly TranscriptionProvider NONE = new(); public TranscriptionProvider() : this( 0, Guid.Empty.ToString(), string.Empty, LLMProviders.NONE, default, false, false, Guid.Empty) { } public override string ToString() => this.Name; #region Implementation of ISecretId /// [JsonIgnore] public string SecretId => this.IsEnterpriseConfiguration ? $"{ISecretId.ENTERPRISE_KEY_PREFIX}::{this.UsedLLMProvider.ToName()}" : this.UsedLLMProvider.ToName(); /// [JsonIgnore] public string SecretName => this.Name; #endregion public static bool TryParseTranscriptionProviderTable(int idx, LuaTable table, Guid configPluginId, out ConfigurationBaseObject provider) { provider = NONE; if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead(out var idText) || !Guid.TryParse(idText, out var id)) { LOGGER.LogWarning($"The configured transcription provider {idx} does not contain a valid ID. The ID must be a valid GUID."); return false; } if (!table.TryGetValue("Name", out var nameValue) || !nameValue.TryRead(out var name)) { LOGGER.LogWarning($"The configured transcription provider {idx} does not contain a valid name."); return false; } if (!table.TryGetValue("UsedLLMProvider", out var usedLLMProviderValue) || !usedLLMProviderValue.TryRead(out var usedLLMProviderText) || !Enum.TryParse(usedLLMProviderText, true, out var usedLLMProvider)) { LOGGER.LogWarning($"The configured transcription provider {idx} does not contain a valid LLM provider enum value."); return false; } if (!table.TryGetValue("Host", out var hostValue) || !hostValue.TryRead(out var hostText) || !Enum.TryParse(hostText, true, out var host)) { LOGGER.LogWarning($"The configured transcription provider {idx} does not contain a valid host enum value."); return false; } if (!table.TryGetValue("Hostname", out var hostnameValue) || !hostnameValue.TryRead(out var hostname)) { LOGGER.LogWarning($"The configured transcription provider {idx} does not contain a valid hostname."); return false; } if (!table.TryGetValue("Model", out var modelValue) || !modelValue.TryRead(out var modelTable)) { LOGGER.LogWarning($"The configured transcription provider {idx} does not contain a valid model table."); return false; } if (!TryReadModelTable(idx, modelTable, out var model)) { LOGGER.LogWarning($"The configured transcription provider {idx} does not contain a valid model configuration."); return false; } provider = new TranscriptionProvider { Num = 0, // will be set later by the PluginConfigurationObject Id = id.ToString(), Name = name, UsedLLMProvider = usedLLMProvider, Model = model, IsSelfHosted = usedLLMProvider is LLMProviders.SELF_HOSTED, IsEnterpriseConfiguration = true, EnterpriseConfigurationPluginId = configPluginId, Hostname = hostname, Host = host, }; // Handle encrypted API key if present: if (table.TryGetValue("APIKey", out var apiKeyValue) && apiKeyValue.TryRead(out var apiKeyText) && !string.IsNullOrWhiteSpace(apiKeyText)) { if (!EnterpriseEncryption.IsEncrypted(apiKeyText)) LOGGER.LogWarning($"The configured transcription provider {idx} contains a plaintext API key. Only encrypted API keys (starting with 'ENC:v1:') are supported."); else { var encryption = PluginFactory.EnterpriseEncryption; if (encryption?.IsAvailable == true) { 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.ToName()}", name, decryptedApiKey, SecretStoreType.TRANSCRIPTION_PROVIDER)); LOGGER.LogDebug($"Successfully decrypted API key for transcription provider {idx}. It will be stored in the OS keyring."); } else LOGGER.LogWarning($"Failed to decrypt API key for transcription provider {idx}. The encryption secret may be incorrect."); } else LOGGER.LogWarning($"The configured transcription provider {idx} contains an encrypted API key, but no encryption secret is configured."); } } return true; } private static bool TryReadModelTable(int idx, LuaTable table, out Model model) { model = default; if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead(out var id)) { LOGGER.LogWarning($"The configured transcription provider {idx} does not contain a valid model ID."); return false; } if (!table.TryGetValue("DisplayName", out var displayNameValue) || !displayNameValue.TryRead(out var displayName)) { LOGGER.LogWarning($"The configured transcription provider {idx} does not contain a valid model display name."); return false; } model = new(id, displayName); return true; } /// /// Exports the transcription provider configuration as a Lua configuration section. /// /// Optional encrypted API key to include in the export. /// A Lua configuration section string. public string ExportAsConfigurationSection(string? encryptedApiKey = null) { var apiKeyLine = string.Empty; if (!string.IsNullOrWhiteSpace(encryptedApiKey)) { apiKeyLine = $""" ["APIKey"] = "{LuaTools.EscapeLuaString(encryptedApiKey)}", """; } return $$""" CONFIG["TRANSCRIPTION_PROVIDERS"][#CONFIG["TRANSCRIPTION_PROVIDERS"]+1] = { ["Id"] = "{{Guid.NewGuid().ToString()}}", ["Name"] = "{{LuaTools.EscapeLuaString(this.Name)}}", ["UsedLLMProvider"] = "{{this.UsedLLMProvider}}", ["Host"] = "{{this.Host}}", ["Hostname"] = "{{LuaTools.EscapeLuaString(this.Hostname)}}", {{apiKeyLine}} ["Model"] = { ["Id"] = "{{LuaTools.EscapeLuaString(this.Model.Id)}}", ["DisplayName"] = "{{LuaTools.EscapeLuaString(this.Model.DisplayName ?? string.Empty)}}", }, } """; } }