ElevenLabs speaks for the voice tool when ELEVENLABS_API_KEY and ELEVENLABS_VOICE_ID are set (TTS_PROVIDER=elevenlabs|openai picks, the other is the fallback). Opus output (opus_48000_64) goes to Telegram as is; voice names resolve through /v2/voices; instructions become audio tags on eleven_v3 and are ignored on other models. The key is redacted from tool output and never reaches agent processes.
45 lines
2.4 KiB
C#
45 lines
2.4 KiB
C#
namespace Volcanic.AgenticCore.Telegram.Speech;
|
|
|
|
/// <summary>
|
|
/// The <c>Telegram:Speech:ElevenLabs</c> section: ElevenLabs text to speech for the <c>voice</c> tool. Configured exactly
|
|
/// when both <see cref="ApiKey"/> and <see cref="VoiceId"/> are set.
|
|
/// </summary>
|
|
public sealed class ElevenLabsOptions
|
|
{
|
|
public const string DefaultBaseUrl = "https://api.elevenlabs.io";
|
|
public const string DefaultModel = "eleven_multilingual_v2";
|
|
|
|
/// <summary>OGG/Opus, which Telegram shows as a voice note.</summary>
|
|
public const string DefaultOutputFormat = "opus_48000_64";
|
|
|
|
/// <summary>Falls back to <c>ELEVENLABS_API_KEY</c>.</summary>
|
|
public string ApiKey { get; set; } = "";
|
|
|
|
/// <summary>Falls back to <c>ELEVENLABS_VOICE_ID</c>. A voice id, or the name of a voice in the account's library.</summary>
|
|
public string VoiceId { get; set; } = "";
|
|
|
|
/// <summary>Falls back to <c>ELEVENLABS_MODEL</c>, then <see cref="DefaultModel"/>.</summary>
|
|
public string Model { get; set; } = "";
|
|
|
|
public string BaseUrl { get; set; } = DefaultBaseUrl;
|
|
|
|
/// <summary>An <c>opus_48000_*</c> format; others aren't OGG/Opus and are refused.</summary>
|
|
public string OutputFormat { get; set; } = DefaultOutputFormat;
|
|
|
|
public bool IsConfigured => !string.IsNullOrWhiteSpace(ApiKey) && !string.IsNullOrWhiteSpace(VoiceId);
|
|
|
|
public string ResolvedModel => string.IsNullOrWhiteSpace(Model) ? DefaultModel : Model.Trim();
|
|
|
|
/// <summary>Only v3 models read audio tags such as <c>[whispers]</c>; older models would speak them aloud.</summary>
|
|
public bool SupportsAudioTags => ResolvedModel.StartsWith("eleven_v3", StringComparison.OrdinalIgnoreCase);
|
|
|
|
/// <summary>Reads the environment shortcuts; the environment fills only empty settings, and an empty variable counts as unset.</summary>
|
|
public void ApplyEnvironment(Func<string, string?> getEnv)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(getEnv);
|
|
if (string.IsNullOrWhiteSpace(ApiKey) && getEnv("ELEVENLABS_API_KEY") is { } key && !string.IsNullOrWhiteSpace(key)) ApiKey = key.Trim();
|
|
if (string.IsNullOrWhiteSpace(VoiceId) && getEnv("ELEVENLABS_VOICE_ID") is { } voice && !string.IsNullOrWhiteSpace(voice)) VoiceId = voice.Trim();
|
|
if (string.IsNullOrWhiteSpace(Model) && getEnv("ELEVENLABS_MODEL") is { } model && !string.IsNullOrWhiteSpace(model)) Model = model.Trim();
|
|
}
|
|
}
|