using System.Diagnostics; using Spectre.Console; using Toak.Api; using Toak.Configuration; using Toak.Core; namespace Toak.Commands; public static class LatencyTestCommand { public static async Task ExecuteAsync(bool verbose) { Logger.Verbose = verbose; var config = new ConfigManager().LoadConfig(); if (string.IsNullOrWhiteSpace(config.GroqApiKey)) { AnsiConsole.MarkupLine("[red]Groq API Key is not configured.[/] Run 'toak onboard'."); return; } AnsiConsole.MarkupLine("Generating 1-second silent audio file for testing..."); var testWavPath = Constants.Paths.LatencyTestWavFile; var pInfo = new ProcessStartInfo { FileName = Constants.Commands.AudioFfmpeg, Arguments = $"-f lavfi -i anullsrc=r=44100:cl=mono -t 1 -y {testWavPath}", UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true, RedirectStandardOutput = true }; var proc = Process.Start(pInfo); if (proc != null) await proc.WaitForExitAsync(); if (!File.Exists(testWavPath)) { AnsiConsole.MarkupLine("[red]Failed to generate test audio file using ffmpeg.[/]"); return; } var client = new OpenAiCompatibleClient(config.GroqApiKey); try { await AnsiConsole.Status() .StartAsync("Running latency test...", async ctx => { ctx.Status("Testing STT (Whisper)..."); var sttWatch = Stopwatch.StartNew(); await client.TranscribeAsync(testWavPath, config.WhisperLanguage, config.WhisperModel); sttWatch.Stop(); ctx.Status("Testing LLM (Llama)..."); var systemPrompt = PromptBuilder.BuildPrompt(config); var llmWatch = Stopwatch.StartNew(); await client.RefineTextAsync("Hello world, this is a latency test.", systemPrompt, config.LlmModel); llmWatch.Stop(); var total = sttWatch.ElapsedMilliseconds + llmWatch.ElapsedMilliseconds; AnsiConsole.WriteLine(); var table = new Table(); table.AddColumn("Operation"); table.AddColumn("Latency (ms)"); table.AddRow("STT", sttWatch.ElapsedMilliseconds.ToString()); table.AddRow("LLM", llmWatch.ElapsedMilliseconds.ToString()); table.AddRow("[bold]Total[/]", $"[bold]{total}ms[/]"); AnsiConsole.Write(table); AnsiConsole.MarkupLine(total < 1500 ? $"[green]Status: OK (under 1.5s target). Total time: {(total / 1000.0):0.0}s.[/]" : $"[yellow]Status: SLOW (over 1.5s target). Total time: {(total / 1000.0):0.0}s.[/]"); }); } catch (Exception ex) { AnsiConsole.MarkupLine($"[red]Error during test: {ex.Message}[/]"); } finally { if (File.Exists(testWavPath)) File.Delete(testWavPath); } } }