2026-01-09 11:45:21 +00:00
using System.Text.Json.Serialization ;
using AIStudio.Provider ;
2026-01-09 14:49:44 +00:00
using AIStudio.Tools.PluginSystem ;
using Lua ;
2026-01-09 11:45:21 +00:00
using Host = AIStudio . Provider . SelfHosted . Host ;
namespace AIStudio.Settings ;
2026-01-09 14:49:44 +00:00
public sealed record TranscriptionProvider (
2026-01-09 11:45:21 +00:00
uint Num ,
string Id ,
string Name ,
LLMProviders UsedLLMProvider ,
Model Model ,
bool IsSelfHosted = false ,
2026-01-09 14:49:44 +00:00
bool IsEnterpriseConfiguration = false ,
Guid EnterpriseConfigurationPluginId = default ,
2026-01-09 11:45:21 +00:00
string Hostname = "http://localhost:1234" ,
2026-01-09 14:49:44 +00:00
Host Host = Host . NONE ) : ConfigurationBaseObject , ISecretId
2026-01-09 11:45:21 +00:00
{
2026-01-09 14:49:44 +00:00
private static readonly ILogger < TranscriptionProvider > LOGGER = Program . LOGGER_FACTORY . CreateLogger < TranscriptionProvider > ( ) ;
public static readonly TranscriptionProvider NONE = new ( ) ;
public TranscriptionProvider ( ) : this (
0 ,
Guid . Empty . ToString ( ) ,
string . Empty ,
LLMProviders . NONE ,
default ,
false ,
false ,
Guid . Empty )
{
}
2026-01-09 11:45:21 +00:00
public override string ToString ( ) = > this . Name ;
2026-01-09 14:49:44 +00:00
2026-01-09 11:45:21 +00:00
#region Implementation of ISecretId
2026-01-09 14:49:44 +00:00
2026-01-09 11:45:21 +00:00
/// <inheritdoc />
[JsonIgnore]
2026-02-07 17:02:39 +00:00
public string SecretId = > this . IsEnterpriseConfiguration ? $"{ISecretId.ENTERPRISE_KEY_PREFIX}::{this.UsedLLMProvider.ToName()}" : this . UsedLLMProvider . ToName ( ) ;
2026-01-09 14:49:44 +00:00
2026-01-09 11:45:21 +00:00
/// <inheritdoc />
[JsonIgnore]
public string SecretName = > this . Name ;
2026-01-09 14:49:44 +00:00
2026-01-09 11:45:21 +00:00
#endregion
2026-01-09 14:49:44 +00:00
public static bool TryParseTranscriptionProviderTable ( int idx , LuaTable table , Guid configPluginId , out ConfigurationBaseObject provider )
{
provider = NONE ;
if ( ! table . TryGetValue ( "Id" , out var idValue ) | | ! idValue . TryRead < string > ( 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 < string > ( 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 < string > ( out var usedLLMProviderText ) | | ! Enum . TryParse < LLMProviders > ( 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 < string > ( out var hostText ) | | ! Enum . TryParse < Host > ( 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 < string > ( 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 < LuaTable > ( 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
{
2026-02-01 13:50:19 +00:00
Num = 0 , // will be set later by the PluginConfigurationObject
2026-01-09 14:49:44 +00:00
Id = id . ToString ( ) ,
Name = name ,
UsedLLMProvider = usedLLMProvider ,
Model = model ,
IsSelfHosted = usedLLMProvider is LLMProviders . SELF_HOSTED ,
IsEnterpriseConfiguration = true ,
EnterpriseConfigurationPluginId = configPluginId ,
Hostname = hostname ,
Host = host ,
} ;
2026-02-05 20:03:12 +00:00
// Handle encrypted API key if present:
if ( table . TryGetValue ( "APIKey" , out var apiKeyValue ) & & apiKeyValue . TryRead < string > ( 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 (
2026-02-07 17:02:39 +00:00
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToName()}" ,
2026-02-05 20:03:12 +00:00
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." ) ;
}
}
2026-01-09 14:49:44 +00:00
return true ;
}
private static bool TryReadModelTable ( int idx , LuaTable table , out Model model )
{
model = default ;
if ( ! table . TryGetValue ( "Id" , out var idValue ) | | ! idValue . TryRead < string > ( 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 < string > ( 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 ;
}
2026-02-05 09:59:59 +00:00
2026-02-05 20:03:12 +00:00
/// <summary>
/// Exports the transcription provider configuration as a Lua configuration section.
/// </summary>
/// <param name="encryptedApiKey">Optional encrypted API key to include in the export.</param>
/// <returns>A Lua configuration section string.</returns>
public string ExportAsConfigurationSection ( string? encryptedApiKey = null )
2026-02-05 09:59:59 +00:00
{
2026-02-05 20:03:12 +00:00
var apiKeyLine = string . Empty ;
if ( ! string . IsNullOrWhiteSpace ( encryptedApiKey ) )
{
apiKeyLine = $"" "
["APIKey"] = "{LuaTools.EscapeLuaString(encryptedApiKey)}" ,
"" ";
}
2026-02-05 09:59:59 +00:00
return $ $"" "
CONFIG [ "TRANSCRIPTION_PROVIDERS" ] [ # CONFIG [ "TRANSCRIPTION_PROVIDERS" ] + 1 ] = {
2026-02-07 18:00:33 +00:00
["Id"] = "{{Guid.NewGuid().ToString()}}" ,
2026-02-05 10:10:34 +00:00
["Name"] = "{{LuaTools.EscapeLuaString(this.Name)}}" ,
["UsedLLMProvider"] = "{{this.UsedLLMProvider}}" ,
2026-02-05 20:03:12 +00:00
2026-02-05 10:10:34 +00:00
["Host"] = "{{this.Host}}" ,
["Hostname"] = "{{LuaTools.EscapeLuaString(this.Hostname)}}" ,
2026-02-05 20:03:12 +00:00
{ { apiKeyLine } }
2026-02-05 10:10:34 +00:00
["Model"] = {
["Id"] = "{{LuaTools.EscapeLuaString(this.Model.Id)}}" ,
["DisplayName"] = "{{LuaTools.EscapeLuaString(this.Model.DisplayName ?? string.Empty)}}" ,
} ,
}
"" ";
2026-02-05 09:59:59 +00:00
}
}