Files
VolcanicAgenticCore/hosts/Volcanic.AgenticCore.Telegram/TelegramProcessEnvironment.cs
EugeneTes 7fef3ce94e feat(telegram): ElevenLabs text to speech for the voice tool
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.
2026-10-14 13:07:36 +00:00

42 lines
2.7 KiB
C#

using Volcanic.AgenticCore.Extensions.GoogleCalendar;
using Volcanic.AgenticCore.Features.Processes;
namespace Volcanic.AgenticCore.Telegram;
/// <summary>The Telegram host's process environment: section, <c>PROCESS_ENV_ALLOW</c> and the host's secret values.</summary>
public static class TelegramProcessEnvironment
{
public const string AllowVariable = "PROCESS_ENV_ALLOW";
/// <summary>Let through by default: skills (gmail, gdrive) need it. Its value stays in the secret values, so tool output shows <c>[REDACTED]</c>.</summary>
public const string GoogleClientSecretVariable = "GOOGLE_CLIENT_SECRET";
/// <summary>Adds the section's lists, <c>PROCESS_ENV_ALLOW</c>, <c>GOOGLE_CLIENT_SECRET</c> to the allow-list, and the bot token, the OpenAI, ElevenLabs and OpenRouter API keys and the Google client secret to the secret values of <paramref name="target"/>.</summary>
public static void Configure(ProcessEnvironmentOptions target, TelegramOptions telegram, ProcessEnvironmentSection section, Func<string, string?> getEnv,
GoogleCalendarOptions? calendar = null)
{
ArgumentNullException.ThrowIfNull(target);
ArgumentNullException.ThrowIfNull(telegram);
ArgumentNullException.ThrowIfNull(section);
ArgumentNullException.ThrowIfNull(getEnv);
target.Deny.AddRange(section.Deny);
target.Allow.AddRange(section.Allow);
if (getEnv(AllowVariable) is { Length: > 0 } allow)
target.Allow.AddRange(allow.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
if (!target.Allow.Contains(GoogleClientSecretVariable, StringComparer.OrdinalIgnoreCase)) target.Allow.Add(GoogleClientSecretVariable);
target.HardenHostProcess = section.HardenHostProcess;
if (!string.IsNullOrWhiteSpace(telegram.BotToken)) target.SecretValues.Add(telegram.BotToken);
if (!string.IsNullOrWhiteSpace(telegram.Speech?.ApiKey)) target.SecretValues.Add(telegram.Speech.ApiKey);
if (!string.IsNullOrWhiteSpace(telegram.Speech?.ElevenLabs?.ApiKey)) target.SecretValues.Add(telegram.Speech.ElevenLabs.ApiKey);
if (!string.IsNullOrWhiteSpace(telegram.Triggers?.ApiKey)) target.SecretValues.Add(telegram.Triggers.ApiKey);
if (!string.IsNullOrWhiteSpace(calendar?.ClientSecret)) target.SecretValues.Add(calendar.ClientSecret);
}
/// <summary>The startup log line: names only, never values.</summary>
public static string Describe(ProcessEnvironmentOptions options)
{
ArgumentNullException.ThrowIfNull(options);
return $"Process environment: {options.Deny.Count} deny rules, allow: {(options.Allow.Count == 0 ? "none" : string.Join(", ", options.Allow))}";
}
}