From a3654483999081696dd108fc9510c4fced9a3b6b Mon Sep 17 00:00:00 2001 From: TomiEckert Date: Fri, 27 Feb 2026 00:39:32 +0100 Subject: [PATCH] feat: Implement a modular skill system with hotword detection, streaming text output, and enhanced logging. --- Api/GroqApiClient.cs | 56 ++++++++++++ Api/Models/LlamaModels.cs | 21 +++++ Assets/Audio/beep.wav | Bin 0 -> 150058 bytes Audio/AudioRecorder.cs | 4 + Configuration/ToakConfig.cs | 3 + Core/Logger.cs | 15 ++++ Core/PromptBuilder.cs | 1 + Core/Skills/ISkill.cs | 13 +++ Core/Skills/SkillRegistry.cs | 29 +++++++ Core/Skills/TerminalSkill.cs | 41 +++++++++ Core/Skills/TranslateSkill.cs | 23 +++++ Core/StateTracker.cs | 3 + IDEAS.md | 55 ++++++++++++ IO/Notifications.cs | 43 +++++++++ IO/TextInjector.cs | 58 +++++++++++++ Program.cs | 101 +++++++++++++++++----- Serialization/AppJsonSerializerContext.cs | 4 + Toak.csproj | 4 + 18 files changed, 451 insertions(+), 23 deletions(-) create mode 100644 Assets/Audio/beep.wav create mode 100644 Core/Logger.cs create mode 100644 Core/Skills/ISkill.cs create mode 100644 Core/Skills/SkillRegistry.cs create mode 100644 Core/Skills/TerminalSkill.cs create mode 100644 Core/Skills/TranslateSkill.cs diff --git a/Api/GroqApiClient.cs b/Api/GroqApiClient.cs index 077eaff..ba33606 100644 --- a/Api/GroqApiClient.cs +++ b/Api/GroqApiClient.cs @@ -4,6 +4,7 @@ using System.Text.Json.Serialization; using Toak.Api.Models; using Toak.Serialization; +using Toak.Core; namespace Toak.Api; @@ -39,7 +40,9 @@ public class GroqApiClient content.Add(new StringContent(firstLang), "language"); } + Logger.LogDebug($"Sending Whisper API request ({modelToUse})..."); var response = await _httpClient.PostAsync("audio/transcriptions", content); + Logger.LogDebug($"Whisper API response status: {response.StatusCode}"); if (!response.IsSuccessStatusCode) { @@ -67,7 +70,9 @@ public class GroqApiClient var jsonContent = new StringContent(JsonSerializer.Serialize(requestBody, AppJsonSerializerContext.Default.LlamaRequest), System.Text.Encoding.UTF8, "application/json"); + Logger.LogDebug($"Sending Llama API request (model: {requestBody.Model})..."); var response = await _httpClient.PostAsync("chat/completions", jsonContent); + Logger.LogDebug($"Llama API response status: {response.StatusCode}"); if (!response.IsSuccessStatusCode) { @@ -80,4 +85,55 @@ public class GroqApiClient return result?.Choices?.FirstOrDefault()?.Message?.Content ?? string.Empty; } + + public async IAsyncEnumerable RefineTextStreamAsync(string rawTranscript, string systemPrompt, string model = "openai/gpt-oss-20b") + { + var requestBody = new LlamaRequest + { + Model = string.IsNullOrWhiteSpace(model) ? "openai/gpt-oss-20b" : model, + Temperature = 0.0, + Stream = true, + Messages = new[] + { + new LlamaRequestMessage { Role = "system", Content = systemPrompt }, + new LlamaRequestMessage { Role = "user", Content = $"{rawTranscript}" } + } + }; + + var jsonContent = new StringContent(JsonSerializer.Serialize(requestBody, AppJsonSerializerContext.Default.LlamaRequest), System.Text.Encoding.UTF8, "application/json"); + + using var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions") { Content = jsonContent }; + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream")); + + Logger.LogDebug($"Sending Llama Steam API request (model: {requestBody.Model})..."); + using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + Logger.LogDebug($"Llama Stream API response status: {response.StatusCode}"); + + if (!response.IsSuccessStatusCode) + { + var error = await response.Content.ReadAsStringAsync(); + throw new Exception($"Llama API Error: {response.StatusCode} - {error}"); + } + + using var stream = await response.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(stream); + + string? line; + while ((line = await reader.ReadLineAsync()) != null) + { + if (string.IsNullOrWhiteSpace(line)) continue; + if (line.StartsWith("data: ")) + { + var data = line.Substring("data: ".Length).Trim(); + if (data == "[DONE]") break; + + var chunk = JsonSerializer.Deserialize(data, AppJsonSerializerContext.Default.LlamaStreamResponse); + var content = chunk?.Choices?.FirstOrDefault()?.Delta?.Content; + if (!string.IsNullOrEmpty(content)) + { + yield return content; + } + } + } + } } diff --git a/Api/Models/LlamaModels.cs b/Api/Models/LlamaModels.cs index 405bfac..a84cbac 100644 --- a/Api/Models/LlamaModels.cs +++ b/Api/Models/LlamaModels.cs @@ -18,6 +18,8 @@ public class LlamaRequest public LlamaRequestMessage[] Messages { get; set; } = Array.Empty(); [JsonPropertyName("temperature")] public double Temperature { get; set; } = 0.0; + [JsonPropertyName("stream")] + public bool? Stream { get; set; } } public class LlamaResponse @@ -31,3 +33,22 @@ public class LlamaChoice [JsonPropertyName("message")] public LlamaRequestMessage Message { get; set; } = new(); } + +public class LlamaStreamResponse +{ + [JsonPropertyName("choices")] + public LlamaStreamChoice[] Choices { get; set; } = Array.Empty(); +} + +public class LlamaStreamChoice +{ + [JsonPropertyName("delta")] + public LlamaStreamDelta Delta { get; set; } = new(); +} + +public class LlamaStreamDelta +{ + [JsonPropertyName("content")] + public string? Content { get; set; } +} + diff --git a/Assets/Audio/beep.wav b/Assets/Audio/beep.wav new file mode 100644 index 0000000000000000000000000000000000000000..aab932d14fa3005815293c3dfde6e971229683e3 GIT binary patch literal 150058 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 W1L8qv0zZNcZC8<8OZVvdvHuU=m9R$u literal 0 HcmV?d00001 diff --git a/Audio/AudioRecorder.cs b/Audio/AudioRecorder.cs index 2c02142..0a76180 100644 --- a/Audio/AudioRecorder.cs +++ b/Audio/AudioRecorder.cs @@ -15,9 +15,12 @@ public static class AudioRecorder { if (File.Exists(WavPath)) { + Logger.LogDebug($"Deleting old audio file: {WavPath}"); File.Delete(WavPath); } + Logger.LogDebug("Starting ffmpeg to record audio..."); + var pInfo = new ProcessStartInfo { FileName = "ffmpeg", @@ -41,6 +44,7 @@ public static class AudioRecorder var pid = StateTracker.GetRecordingPid(); if (pid.HasValue) { + Logger.LogDebug($"Found active recording process with PID {pid.Value}. Attempting to stop..."); try { var process = Process.GetProcessById(pid.Value); diff --git a/Configuration/ToakConfig.cs b/Configuration/ToakConfig.cs index 2c54f2c..108009e 100644 --- a/Configuration/ToakConfig.cs +++ b/Configuration/ToakConfig.cs @@ -13,4 +13,7 @@ public class ToakConfig public string WhisperLanguage { get; set; } = string.Empty; public string LlmModel { get; set; } = "openai/gpt-oss-20b"; public string WhisperModel { get; set; } = "whisper-large-v3-turbo"; + public string StartSoundPath { get; set; } = "Assets/Audio/beep.wav"; + public string StopSoundPath { get; set; } = "Assets/Audio/beep.wav"; + public List ActiveSkills { get; set; } = new List { "Terminal", "Translate" }; } diff --git a/Core/Logger.cs b/Core/Logger.cs new file mode 100644 index 0000000..a7ae4dc --- /dev/null +++ b/Core/Logger.cs @@ -0,0 +1,15 @@ +namespace Toak.Core; + +public static class Logger +{ + public static bool Verbose { get; set; } = false; + + public static void LogDebug(string message) + { + if (Verbose) + { + var logLine = $"[DEBUG] {DateTime.Now:HH:mm:ss.fff} - {message}"; + Console.WriteLine(logLine); + } + } +} diff --git a/Core/PromptBuilder.cs b/Core/PromptBuilder.cs index eafe4f0..f4366ec 100644 --- a/Core/PromptBuilder.cs +++ b/Core/PromptBuilder.cs @@ -23,6 +23,7 @@ public static class PromptBuilder sb.AppendLine(); sb.AppendLine("FORMATTING RULES:"); sb.AppendLine("- CRITICAL: If the contains nothing, or very short gibberish, output NOTHING AT ALL (an empty string)."); + sb.AppendLine("- LANGUAGE DETECT: The transcript may be in English or a different language (e.g., Hungarian, Spanish). Detect the language and ensure your output and grammar corrections are STRICTLY in that same language."); diff --git a/Core/Skills/ISkill.cs b/Core/Skills/ISkill.cs new file mode 100644 index 0000000..6a8f911 --- /dev/null +++ b/Core/Skills/ISkill.cs @@ -0,0 +1,13 @@ +namespace Toak.Core.Skills; + +public interface ISkill +{ + string Name { get; } + string Description { get; } + string[] Hotwords { get; } + + bool HandlesExecution { get; } + + string GetSystemPrompt(string rawTranscript); + void Execute(string llmResult); +} diff --git a/Core/Skills/SkillRegistry.cs b/Core/Skills/SkillRegistry.cs new file mode 100644 index 0000000..3798946 --- /dev/null +++ b/Core/Skills/SkillRegistry.cs @@ -0,0 +1,29 @@ +namespace Toak.Core.Skills; + +public static class SkillRegistry +{ + public static readonly ISkill[] AllSkills = new ISkill[] + { + new TerminalSkill(), + new TranslateSkill() + }; + + public static ISkill? DetectSkill(string transcript, IEnumerable activeSkillNames) + { + var activeSkills = AllSkills.Where(s => activeSkillNames.Contains(s.Name, StringComparer.OrdinalIgnoreCase)).ToList(); + + string normalizedTranscript = transcript.Trim(); + + foreach (var skill in activeSkills) + { + foreach (var hotword in skill.Hotwords) + { + if (normalizedTranscript.StartsWith(hotword, StringComparison.OrdinalIgnoreCase)) + { + return skill; + } + } + } + return null; + } +} diff --git a/Core/Skills/TerminalSkill.cs b/Core/Skills/TerminalSkill.cs new file mode 100644 index 0000000..2e5c476 --- /dev/null +++ b/Core/Skills/TerminalSkill.cs @@ -0,0 +1,41 @@ +using System.Diagnostics; + +namespace Toak.Core.Skills; + +public class TerminalSkill : ISkill +{ + public string Name => "Terminal"; + public string Description => "Translates an intent into a bash command and runs it in the background."; + public string[] Hotwords => new[] { "System terminal", "System command" }; + + public bool HandlesExecution => true; + + public string GetSystemPrompt(string rawTranscript) + { + return @"You are a command-line assistant. The user will ask you to perform a task. +Translate the request into a single bash command. +Output ONLY the raw bash command to achieve this task. Do not include markdown formatting, backticks, or explanations."; + } + + public void Execute(string llmResult) + { + try + { + Console.WriteLine($"[TerminalSkill] Executing: {llmResult}"); + var escapedCmd = llmResult.Replace("\"", "\\\""); + var pInfo = new ProcessStartInfo + { + FileName = "bash", + Arguments = $"-c \"{escapedCmd}\"", + UseShellExecute = false, + CreateNoWindow = true + }; + Process.Start(pInfo); + IO.Notifications.Notify("Toak Terminal Executed", llmResult); + } + catch (Exception ex) + { + Console.WriteLine($"[TerminalSkill Error] {ex.Message}"); + } + } +} diff --git a/Core/Skills/TranslateSkill.cs b/Core/Skills/TranslateSkill.cs new file mode 100644 index 0000000..3017b31 --- /dev/null +++ b/Core/Skills/TranslateSkill.cs @@ -0,0 +1,23 @@ +namespace Toak.Core.Skills; + +public class TranslateSkill : ISkill +{ + public string Name => "Translate"; + public string Description => "Translates the spoken text into another language on the fly."; + public string[] Hotwords => new[] { "System translate to", "System translate into" }; + + public bool HandlesExecution => false; + + public string GetSystemPrompt(string rawTranscript) + { + return @"You are an expert translator. The user wants to translate the following text. +The first few words identify the target language (e.g. 'Translate to Spanish:', 'Translate into Hungarian:'). +Translate the REST of the transcript into that target language. +Output ONLY the final translated text. Do not include markdown, explanations, or quotes."; + } + + public void Execute(string llmResult) + { + // Not used since HandlesExecution is false + } +} diff --git a/Core/StateTracker.cs b/Core/StateTracker.cs index 5d22d44..8cc55b3 100644 --- a/Core/StateTracker.cs +++ b/Core/StateTracker.cs @@ -11,6 +11,7 @@ public static class StateTracker public static void SetRecording(int ffmpegPid) { + Logger.LogDebug($"Setting recording state with PID {ffmpegPid}"); File.WriteAllText(StateFilePath, ffmpegPid.ToString()); } @@ -21,6 +22,7 @@ public static class StateTracker var content = File.ReadAllText(StateFilePath).Trim(); if (int.TryParse(content, out var pid)) { + Logger.LogDebug($"Read recording PID {pid} from state file"); return pid; } } @@ -31,6 +33,7 @@ public static class StateTracker { if (File.Exists(StateFilePath)) { + Logger.LogDebug("Clearing recording state file"); File.Delete(StateFilePath); } } diff --git a/IDEAS.md b/IDEAS.md index c99bb69..98d04f9 100644 --- a/IDEAS.md +++ b/IDEAS.md @@ -197,6 +197,61 @@ toak status # Check if daemon is running --- +## Future Innovations + +### Hotword Commands (LLM Routing) +Instruct the LLM in `PromptBuilder` to output a specific JSON structure if given a command phrase. If a specific hotword like "System command" or "Computer dictate" is detected at the start of the audio, Toak parses the JSON, skips typng out via `xdotool`/`wtype`, and instead executes a pre-defined background action. + +If it doesn't hear a command phrase, it simply returns the text normally and types it. + +**How it works (Under the Hood):** +The LLM is prompted to always return JSON in the background when a command is directed at the assistant. +```json +{ + "is_command": true, + "action": "append_to_notes", + "content": "Buy milk and eggs", + "meta": {} +} +``` + +**Alternative Hotword Ideas:** +Since "Toak" is not a real English word, Whisper might transcribe it as "talk", "toke", or "oak." It is highly recommended to use distinct, phonetically clear hotwords such as: +- **"System..."** (e.g. "System note:") +- **"Computer..."** (e.g. "Computer search:") +- **"Action..."** (e.g. "Action commit:") +- **"Dictate..."** (e.g. "Dictate terminal:") +- **"Listen up..."** (e.g. "Listen up translate...") + +**Prompt Ideas & Use Cases:** + +1. **Quick Notes / Brainstorming:** + - *Hotword:* `"System note:"` or `"Drop this in my notes:"` + - *Action:* Appends the spoken text to a configured `~/notes.md` file in the background without interrupting your current window. + - *Example:* "System note: I need to remember to check the database migrations later today." + +2. **Terminal / CLI Execution:** + - *Hotword:* `"Computer terminal:"` or `"Command:"` + - *Action:* Takes the natural language command, asks the LLM to translate it into a bash command, and types it into a new tmux window or background process. + - *Example:* "Computer terminal: find all python files modified in the last 2 days." + +3. **Git Commit Messages:** + - *Hotword:* `"Action commit:"` + - *Action:* Automatically formats the dictated text into a standard conventional commit message, stages all files, and commits them. + - *Example:* "Action commit: I refactored the audio recorder to use native processes instead of the old library." -> LLM outputs `refactor(audio): migrate to native processes` and runs `git commit -am "..."`. + +4. **Web Search / Lookup:** + - *Hotword:* `"System search:"` or `"Look up:"` + - *Action:* Opens your default browser and performs a search for the spoken phrase. + - *Example:* "System search: MDN documentation for grid layout." + +5. **Translating on the fly:** + - *Hotword:* `"Translate to Spanish:"` + - *Action:* Instead of typing English, it types the translated version of the rest of the sentence. + - *Example:* "Translate to Spanish: Hello, how are you today?" -> Types out `Hola, ¿cómo estás hoy?`. + +--- + ## Implementation Priority ### Tier 1: High Impact, Low Effort diff --git a/IO/Notifications.cs b/IO/Notifications.cs index 2397b40..f7113ba 100644 --- a/IO/Notifications.cs +++ b/IO/Notifications.cs @@ -22,4 +22,47 @@ public static class Notifications Console.WriteLine($"[Notifications] Failed to send notification: {ex.Message}"); } } + + public static void PlaySound(string soundPath) + { + if (string.IsNullOrWhiteSpace(soundPath)) return; + try + { + var absolutePath = soundPath; + if (!Path.IsPathRooted(absolutePath)) + absolutePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, absolutePath); + + if (!File.Exists(absolutePath)) + { + var resourceName = "Toak." + soundPath.Replace("/", ".").Replace("\\", "."); + using var stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); + if (stream != null) + { + absolutePath = Path.Combine(Path.GetTempPath(), "toak_" + Path.GetFileName(soundPath)); + if (!File.Exists(absolutePath)) + { + using var fileStream = File.Create(absolutePath); + stream.CopyTo(fileStream); + } + } + else + { + return; + } + } + + var pInfo = new ProcessStartInfo + { + FileName = "paplay", + Arguments = $"\"{absolutePath}\"", + UseShellExecute = false, + CreateNoWindow = true + }; + Process.Start(pInfo); + } + catch (Exception ex) + { + Console.WriteLine($"[Notifications] Failed to play sound: {ex.Message}"); + } + } } diff --git a/IO/TextInjector.cs b/IO/TextInjector.cs index 1844358..33678c7 100644 --- a/IO/TextInjector.cs +++ b/IO/TextInjector.cs @@ -1,11 +1,14 @@ using System.Diagnostics; +using Toak.Core; + namespace Toak.IO; public static class TextInjector { public static void Inject(string text, string backend) { + Logger.LogDebug($"Injecting text: '{text}' with {backend}"); if (string.IsNullOrWhiteSpace(text)) return; try @@ -13,6 +16,7 @@ public static class TextInjector ProcessStartInfo pInfo; if (backend.ToLowerInvariant() == "wtype") { + Logger.LogDebug($"Injecting text using wtype..."); pInfo = new ProcessStartInfo { FileName = "wtype", @@ -23,6 +27,7 @@ public static class TextInjector } else // xdotool { + Logger.LogDebug($"Injecting text using xdotool..."); pInfo = new ProcessStartInfo { FileName = "xdotool", @@ -40,4 +45,57 @@ public static class TextInjector Notifications.Notify("Injection Error", "Could not type text into window."); } } + + public static async Task InjectStreamAsync(IAsyncEnumerable tokenStream, string backend) + { + try + { + ProcessStartInfo pInfo; + if (backend.ToLowerInvariant() == "wtype") + { + Logger.LogDebug($"Setting up stream injection using wtype..."); + pInfo = new ProcessStartInfo + { + FileName = "wtype", + Arguments = "-", + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardInput = true + }; + } + else // xdotool + { + Logger.LogDebug($"Setting up stream injection using xdotool..."); + pInfo = new ProcessStartInfo + { + FileName = "xdotool", + Arguments = "type --clearmodifiers --delay 0 --file -", + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardInput = true + }; + } + + using var process = Process.Start(pInfo); + if (process == null) return; + + Logger.LogDebug("Started stream injection process, waiting for tokens..."); + + await foreach (var token in tokenStream) + { + Logger.LogDebug($"Injecting token: '{token}'"); + await process.StandardInput.WriteAsync(token); + await process.StandardInput.FlushAsync(); + } + + Logger.LogDebug("Stream injection complete. Closing standard input."); + process.StandardInput.Close(); + await process.WaitForExitAsync(); + } + catch (Exception ex) + { + Console.WriteLine($"[TextInjector] Error injecting text stream: {ex.Message}"); + Notifications.Notify("Injection Error", "Could not type text stream into window."); + } + } } diff --git a/Program.cs b/Program.cs index 3eb41cc..8dd017c 100644 --- a/Program.cs +++ b/Program.cs @@ -5,8 +5,11 @@ using Toak.Api; using Toak.Core; using Toak.IO; -bool pipeToStdout = args.Contains("--pipe") || args.Contains("-p") || Console.IsOutputRedirected; +bool pipeToStdout = args.Contains("--pipe") || args.Contains("-p"); bool copyToClipboard = args.Contains("--copy"); +bool verbose = args.Contains("-v") || args.Contains("--verbose"); + +Logger.Verbose = verbose; string command = ""; @@ -29,6 +32,7 @@ if (args.Contains("-h") || args.Contains("--help") || (string.IsNullOrEmpty(comm Console.WriteLine(" -h, --help - Show this help message"); Console.WriteLine(" -p, --pipe - Output transcription to stdout instead of typing"); Console.WriteLine(" --copy - Copy to clipboard instead of typing"); + Console.WriteLine(" -v, --verbose - Enable detailed debug logging"); return; } @@ -71,6 +75,17 @@ if (command == "onboard") var backend = Console.ReadLine(); if (!string.IsNullOrWhiteSpace(backend)) config.TypingBackend = backend.ToLowerInvariant(); + Console.WriteLine(); + var availableSkills = Toak.Core.Skills.SkillRegistry.AllSkills.Select(s => s.Name); + Console.WriteLine($"Active Skills (comma separated) [{string.Join(", ", config.ActiveSkills)}]:"); + Console.WriteLine($" Available: {string.Join(", ", availableSkills)}"); + Console.Write("Selection: "); + var skillsInput = Console.ReadLine(); + if (!string.IsNullOrWhiteSpace(skillsInput)) + { + config.ActiveSkills = skillsInput.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList(); + } + ConfigManager.SaveConfig(config); Console.WriteLine("Configuration saved."); return; @@ -85,6 +100,7 @@ if (command == "show") Console.WriteLine($" Whisper Model: {config.WhisperModel}"); Console.WriteLine($" Spoken Language: {(string.IsNullOrEmpty(config.WhisperLanguage) ? "Auto" : config.WhisperLanguage)}"); Console.WriteLine($" Typing Backend: {config.TypingBackend}"); + Console.WriteLine($" Active Skills: {string.Join(", ", config.ActiveSkills)}"); Console.WriteLine($" Style Mode: {config.StyleMode}"); Console.WriteLine($" Punctuation Module: {config.ModulePunctuation}"); Console.WriteLine($" Technical Sanitization: {config.ModuleTechnicalSanitization}"); @@ -244,12 +260,15 @@ if (command == "toggle") { if (StateTracker.IsRecording()) { + var config = ConfigManager.LoadConfig(); + Notifications.PlaySound(config.StopSoundPath); + if (!pipeToStdout) Console.WriteLine("Stopping recording and transcribing..."); if (!pipeToStdout) Notifications.Notify("Toak", "Transcribing..."); AudioRecorder.StopRecording(); - var config = ConfigManager.LoadConfig(); + Logger.LogDebug($"Loaded configuration: LLM={config.LlmModel}, Whisper={config.WhisperModel}, Typing={config.TypingBackend}"); if (string.IsNullOrWhiteSpace(config.GroqApiKey)) { @@ -271,7 +290,9 @@ if (command == "toggle") var stopWatch = Stopwatch.StartNew(); // 1. STT + Logger.LogDebug($"Starting STT transcription via Whisper for {wavPath}..."); var transcript = await groq.TranscribeAsync(wavPath, config.WhisperLanguage, config.WhisperModel); + Logger.LogDebug($"Raw transcript received: '{transcript}'"); if (string.IsNullOrWhiteSpace(transcript)) { @@ -279,32 +300,64 @@ if (command == "toggle") return; } - string finalText = transcript; - // 2. LLM Refinement - var systemPrompt = PromptBuilder.BuildPrompt(config); - finalText = await groq.RefineTextAsync(transcript, systemPrompt, config.LlmModel); - - if (string.IsNullOrWhiteSpace(finalText)) + var detectedSkill = Toak.Core.Skills.SkillRegistry.DetectSkill(transcript, config.ActiveSkills); + string systemPrompt; + if (detectedSkill != null) { - if (!pipeToStdout) Notifications.Notify("Toak", "Dropped short or empty audio."); - return; - } - - // 3. Output - if (pipeToStdout) - { - Console.WriteLine(finalText); - } - else if (copyToClipboard) - { - ClipboardManager.Copy(finalText); - stopWatch.Stop(); - Notifications.Notify("Toak", $"Copied to clipboard in {stopWatch.ElapsedMilliseconds}ms"); + Logger.LogDebug($"Skill detected: {detectedSkill.Name}"); + if (!pipeToStdout) Notifications.Notify("Toak Skill Detected", detectedSkill.Name); + systemPrompt = detectedSkill.GetSystemPrompt(transcript); } else { - TextInjector.Inject(finalText, config.TypingBackend); + systemPrompt = PromptBuilder.BuildPrompt(config); + } + + // 3. Output + if (detectedSkill != null && detectedSkill.HandlesExecution) + { + Logger.LogDebug($"Executing skill synchronously: {detectedSkill.Name}"); + string finalText = await groq.RefineTextAsync(transcript, systemPrompt, config.LlmModel); + Logger.LogDebug($"Skill refined text: '{finalText}'"); + if (string.IsNullOrWhiteSpace(finalText)) + { + if (!pipeToStdout) Notifications.Notify("Toak", "Dropped short or empty audio."); + return; + } + + detectedSkill.Execute(finalText); + stopWatch.Stop(); + Notifications.Notify("Toak", $"Skill executed in {stopWatch.ElapsedMilliseconds}ms"); + } + else if (pipeToStdout || copyToClipboard) + { + Logger.LogDebug("Starting LLM text refinement (synchronous)..."); + string finalText = await groq.RefineTextAsync(transcript, systemPrompt, config.LlmModel); + Logger.LogDebug($"Refined text received: '{finalText}'"); + if (string.IsNullOrWhiteSpace(finalText)) + { + if (!pipeToStdout) Notifications.Notify("Toak", "Dropped short or empty audio."); + return; + } + + if (pipeToStdout) + { + Console.WriteLine(finalText); + } + else + { + ClipboardManager.Copy(finalText); + stopWatch.Stop(); + Notifications.Notify("Toak", $"Copied to clipboard in {stopWatch.ElapsedMilliseconds}ms"); + } + } + else + { + Logger.LogDebug("Starting LLM text refinement (streaming)..."); + var tokenStream = groq.RefineTextStreamAsync(transcript, systemPrompt, config.LlmModel); + Logger.LogDebug("Starting to inject text..."); + await TextInjector.InjectStreamAsync(tokenStream, config.TypingBackend); stopWatch.Stop(); Notifications.Notify("Toak", $"Done in {stopWatch.ElapsedMilliseconds}ms"); } @@ -323,6 +376,8 @@ if (command == "toggle") { // Start recording if (!pipeToStdout) Console.WriteLine("Starting recording..."); + var config = ConfigManager.LoadConfig(); + Notifications.PlaySound(config.StartSoundPath); AudioRecorder.StartRecording(); } } diff --git a/Serialization/AppJsonSerializerContext.cs b/Serialization/AppJsonSerializerContext.cs index b06ab97..f47df36 100644 --- a/Serialization/AppJsonSerializerContext.cs +++ b/Serialization/AppJsonSerializerContext.cs @@ -14,6 +14,10 @@ namespace Toak.Serialization; [JsonSerializable(typeof(LlamaChoice))] [JsonSerializable(typeof(LlamaRequestMessage[]))] [JsonSerializable(typeof(LlamaChoice[]))] +[JsonSerializable(typeof(LlamaStreamResponse))] +[JsonSerializable(typeof(LlamaStreamChoice))] +[JsonSerializable(typeof(LlamaStreamDelta))] +[JsonSerializable(typeof(LlamaStreamChoice[]))] internal partial class AppJsonSerializerContext : JsonSerializerContext { } diff --git a/Toak.csproj b/Toak.csproj index 218df61..1b7c082 100644 --- a/Toak.csproj +++ b/Toak.csproj @@ -8,4 +8,8 @@ true + + + +