// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System.CommandLine; using System.CommandLine.Parsing; using Azure.AI.VoiceLive.Samples; using Azure.Identity; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using NAudio.Wave; namespace Azure.AI.VoiceLive.Samples { /// /// FILE: SampleProgram.cs /// /// /// DESCRIPTION: /// Demonstrates how to use the VoiceLive SDK with Azure AI Foundry agents (Agent V2 API). /// The agent session connects to a pre-configured agent rather than using inline instructions, /// enabling voice access to agents with tools, knowledge bases, and custom behaviors. /// /// USAGE: /// dotnet run /// /// Required environment variables: /// 1) AZURE_VOICELIVE_ENDPOINT - The Azure VoiceLive endpoint /// 2) AGENT_NAME - The Azure AI Foundry agent name /// 3) AGENT_PROJECT_NAME - The Azure AI Foundry project name /// /// Optional environment variables: /// 4) AGENT_VERSION - Specific agent version (defaults to latest) /// 5) AGENT_VOICE - Voice for the assistant (default: en-US-AvaNeural) /// 6) FOUNDRY_RESOURCE_OVERRIDE - Override Foundry resource endpoint /// 7) AGENT_AUTH_IDENTITY_CLIENT_ID - Managed identity client ID for authentication /// /// Note: Agent sessions require DefaultAzureCredential — API key authentication is not supported. /// /// REQUIREMENTS: /// - Azure.AI.VoiceLive /// - Azure.Identity /// - NAudio /// - Microsoft.Extensions.Configuration /// - System.CommandLine /// public class SampleProgram { public static async Task Main(string[] args) { var rootCommand = new RootCommand("Agent Voice Assistant using Azure VoiceLive SDK"); var endpointOption = new Option("--endpoint") { Description = "Azure VoiceLive endpoint (or set AZURE_VOICELIVE_ENDPOINT)" }; var agentNameOption = new Option("--agent-name") { Description = "Azure AI Foundry agent name (or set AGENT_NAME)" }; var agentProjectOption = new Option("--agent-project") { Description = "Azure AI Foundry project name (or set AGENT_PROJECT_NAME)" }; var agentVersionOption = new Option("--agent-version") { Description = "Agent version (or set AGENT_VERSION)" }; var voiceOption = new Option("--voice") { Description = "Voice for the assistant (or set AGENT_VOICE)" }; var foundryOverrideOption = new Option("--foundry-resource-override") { Description = "Override Foundry resource endpoint (or set FOUNDRY_RESOURCE_OVERRIDE)" }; var authIdentityOption = new Option("--auth-identity-client-id") { Description = "Managed identity client ID (or set AGENT_AUTH_IDENTITY_CLIENT_ID)" }; var verboseOption = new Option("--verbose") { Description = "Enable verbose logging" }; rootCommand.Add(endpointOption); rootCommand.Add(agentNameOption); rootCommand.Add(agentProjectOption); rootCommand.Add(agentVersionOption); rootCommand.Add(voiceOption); rootCommand.Add(foundryOverrideOption); rootCommand.Add(authIdentityOption); rootCommand.Add(verboseOption); var parseResult = rootCommand.Parse(args); if (parseResult.Errors.Count > 0) { foreach (var error in parseResult.Errors) Console.WriteLine(error.Message); return 1; } var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: true) .AddEnvironmentVariables() .Build(); var endpoint = parseResult.GetValue(endpointOption) ?? configuration["VoiceLive:Endpoint"] ?? Environment.GetEnvironmentVariable("AZURE_VOICELIVE_ENDPOINT"); var agentName = parseResult.GetValue(agentNameOption) ?? configuration["Agent:Name"] ?? Environment.GetEnvironmentVariable("AGENT_NAME"); var agentProject = parseResult.GetValue(agentProjectOption) ?? configuration["Agent:ProjectName"] ?? Environment.GetEnvironmentVariable("AGENT_PROJECT_NAME"); var agentVersion = parseResult.GetValue(agentVersionOption) ?? configuration["Agent:Version"] ?? Environment.GetEnvironmentVariable("AGENT_VERSION"); var voice = parseResult.GetValue(voiceOption) ?? configuration["Agent:Voice"] ?? Environment.GetEnvironmentVariable("AGENT_VOICE") ?? "en-US-AvaNeural"; var foundryOverride = parseResult.GetValue(foundryOverrideOption) ?? configuration["Agent:FoundryResourceOverride"] ?? Environment.GetEnvironmentVariable("FOUNDRY_RESOURCE_OVERRIDE"); var authIdentityClientId = parseResult.GetValue(authIdentityOption) ?? configuration["Agent:AuthIdentityClientId"] ?? Environment.GetEnvironmentVariable("AGENT_AUTH_IDENTITY_CLIENT_ID"); var verbose = parseResult.GetValue(verboseOption); if (string.IsNullOrEmpty(endpoint)) { Console.WriteLine("Error: No endpoint provided. Set AZURE_VOICELIVE_ENDPOINT or use --endpoint."); return 1; } if (string.IsNullOrEmpty(agentName) || string.IsNullOrEmpty(agentProject)) { Console.WriteLine("Error: Agent name and project name are required."); Console.WriteLine("Set AGENT_NAME and AGENT_PROJECT_NAME, or use --agent-name and --agent-project."); return 1; } using var loggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); builder.SetMinimumLevel(verbose ? LogLevel.Debug : LogLevel.Information); }); var logger = loggerFactory.CreateLogger(); if (!CheckAudioSystem(logger)) return 1; try { var credential = new DefaultAzureCredential(); var client = new VoiceLiveClient(new Uri(endpoint), credential, new VoiceLiveClientOptions()); var agentConfig = new AgentSessionConfig(agentName, agentProject) { AgentVersion = agentVersion, FoundryResourceOverride = string.IsNullOrEmpty(foundryOverride) ? null : foundryOverride, AuthenticationIdentityClientId = authIdentityClientId }; using var assistant = new AgentVoiceAssistant(client, agentConfig, voice, loggerFactory); using var cts = new CancellationTokenSource(); Console.CancelKeyPress += (_, e) => { e.Cancel = true; cts.Cancel(); }; await assistant.StartAsync(cts.Token).ConfigureAwait(false); } catch (OperationCanceledException) { Console.WriteLine("\nVoice assistant shut down. Goodbye!"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); return 1; } return 0; } private static bool CheckAudioSystem(ILogger logger) { try { using (var waveIn = new WaveInEvent { WaveFormat = new WaveFormat(24000, 16, 1), BufferMilliseconds = 50 }) { waveIn.DataAvailable += (_, __) => { }; waveIn.StartRecording(); waveIn.StopRecording(); } var buffer = new BufferedWaveProvider(new WaveFormat(24000, 16, 1)) { BufferDuration = TimeSpan.FromMilliseconds(200) }; using (var waveOut = new WaveOutEvent { DesiredLatency = 100 }) { waveOut.Init(buffer); waveOut.Play(); waveOut.Stop(); } logger.LogInformation("Audio system check passed."); return true; } catch (Exception ex) { Console.WriteLine($"Audio system check failed: {ex.Message}"); return false; } } } }