refactor: centralize common strings, paths, and default values into a new Constants class.
This commit is contained in:
@@ -20,7 +20,7 @@ public class GroqApiClient : ISpeechClient, ILlmClient
|
|||||||
_httpClient.BaseAddress = new Uri("https://api.groq.com/openai/v1/");
|
_httpClient.BaseAddress = new Uri("https://api.groq.com/openai/v1/");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> TranscribeAsync(string filePath, string language = "", string model = "whisper-large-v3-turbo")
|
public async Task<string> TranscribeAsync(string filePath, string language = "", string model = Toak.Core.Constants.Defaults.WhisperModel)
|
||||||
{
|
{
|
||||||
using var content = new MultipartFormDataContent();
|
using var content = new MultipartFormDataContent();
|
||||||
using var fileStream = File.OpenRead(filePath);
|
using var fileStream = File.OpenRead(filePath);
|
||||||
@@ -29,7 +29,7 @@ public class GroqApiClient : ISpeechClient, ILlmClient
|
|||||||
streamContent.Headers.ContentType = new MediaTypeHeaderValue("audio/wav"); // or mpeg
|
streamContent.Headers.ContentType = new MediaTypeHeaderValue("audio/wav"); // or mpeg
|
||||||
content.Add(streamContent, "file", Path.GetFileName(filePath));
|
content.Add(streamContent, "file", Path.GetFileName(filePath));
|
||||||
|
|
||||||
string modelToUse = string.IsNullOrWhiteSpace(model) ? "whisper-large-v3-turbo" : model;
|
string modelToUse = string.IsNullOrWhiteSpace(model) ? Toak.Core.Constants.Defaults.WhisperModel : model;
|
||||||
|
|
||||||
// according to docs whisper-large-v3-turbo requires the language to be provided if it is to be translated later potentially or if we need the most accurate behavior
|
// according to docs whisper-large-v3-turbo requires the language to be provided if it is to be translated later potentially or if we need the most accurate behavior
|
||||||
// Actually, if we want language param, we can pass it to either model
|
// Actually, if we want language param, we can pass it to either model
|
||||||
@@ -56,11 +56,11 @@ public class GroqApiClient : ISpeechClient, ILlmClient
|
|||||||
return result?.Text ?? string.Empty;
|
return result?.Text ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> RefineTextAsync(string rawTranscript, string systemPrompt, string model = "openai/gpt-oss-20b")
|
public async Task<string> RefineTextAsync(string rawTranscript, string systemPrompt, string model = Toak.Core.Constants.Defaults.LlmModel)
|
||||||
{
|
{
|
||||||
var requestBody = new LlamaRequest
|
var requestBody = new LlamaRequest
|
||||||
{
|
{
|
||||||
Model = string.IsNullOrWhiteSpace(model) ? "openai/gpt-oss-20b" : model,
|
Model = string.IsNullOrWhiteSpace(model) ? Toak.Core.Constants.Defaults.LlmModel : model,
|
||||||
Temperature = 0.0,
|
Temperature = 0.0,
|
||||||
Messages = new[]
|
Messages = new[]
|
||||||
{
|
{
|
||||||
@@ -87,11 +87,11 @@ public class GroqApiClient : ISpeechClient, ILlmClient
|
|||||||
return result?.Choices?.FirstOrDefault()?.Message?.Content ?? string.Empty;
|
return result?.Choices?.FirstOrDefault()?.Message?.Content ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async IAsyncEnumerable<string> RefineTextStreamAsync(string rawTranscript, string systemPrompt, string model = "openai/gpt-oss-20b")
|
public async IAsyncEnumerable<string> RefineTextStreamAsync(string rawTranscript, string systemPrompt, string model = Toak.Core.Constants.Defaults.LlmModel)
|
||||||
{
|
{
|
||||||
var requestBody = new LlamaRequest
|
var requestBody = new LlamaRequest
|
||||||
{
|
{
|
||||||
Model = string.IsNullOrWhiteSpace(model) ? "openai/gpt-oss-20b" : model,
|
Model = string.IsNullOrWhiteSpace(model) ? Toak.Core.Constants.Defaults.LlmModel : model,
|
||||||
Temperature = 0.0,
|
Temperature = 0.0,
|
||||||
Stream = true,
|
Stream = true,
|
||||||
Messages = new[]
|
Messages = new[]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Toak.Audio;
|
|||||||
|
|
||||||
public class AudioRecorder : IAudioRecorder
|
public class AudioRecorder : IAudioRecorder
|
||||||
{
|
{
|
||||||
private readonly string WavPath = Path.Combine(Path.GetTempPath(), "toak_recording.wav");
|
private readonly string WavPath = Constants.Paths.RecordingWavFile;
|
||||||
private readonly IRecordingStateTracker _stateTracker;
|
private readonly IRecordingStateTracker _stateTracker;
|
||||||
private readonly INotifications _notifications;
|
private readonly INotifications _notifications;
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ public class AudioRecorder : IAudioRecorder
|
|||||||
|
|
||||||
var pInfo = new ProcessStartInfo
|
var pInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "pw-record",
|
FileName = Constants.Commands.AudioRecord,
|
||||||
Arguments = $"--rate=16000 --channels=1 --format=s16 \"{WavPath}\"",
|
Arguments = $"--rate=16000 --channels=1 --format=s16 \"{WavPath}\"",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
@@ -63,7 +63,7 @@ public class AudioRecorder : IAudioRecorder
|
|||||||
// Gracefully stop pw-record using SIGINT to ensure WAV headers are finalizing cleanly
|
// Gracefully stop pw-record using SIGINT to ensure WAV headers are finalizing cleanly
|
||||||
Process.Start(new ProcessStartInfo
|
Process.Start(new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "kill",
|
FileName = Constants.Commands.ProcessKill,
|
||||||
Arguments = $"-INT {pid.Value}",
|
Arguments = $"-INT {pid.Value}",
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
UseShellExecute = false
|
UseShellExecute = false
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ public static class LatencyTestCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
AnsiConsole.MarkupLine("Generating 1-second silent audio file for testing...");
|
AnsiConsole.MarkupLine("Generating 1-second silent audio file for testing...");
|
||||||
var testWavPath = Path.Combine(Path.GetTempPath(), "toak_latency_test.wav");
|
var testWavPath = Constants.Paths.LatencyTestWavFile;
|
||||||
|
|
||||||
var pInfo = new ProcessStartInfo
|
var pInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "ffmpeg",
|
FileName = Constants.Commands.AudioFfmpeg,
|
||||||
Arguments = $"-f lavfi -i anullsrc=r=44100:cl=mono -t 1 -y {testWavPath}",
|
Arguments = $"-f lavfi -i anullsrc=r=44100:cl=mono -t 1 -y {testWavPath}",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
using Toak.Core;
|
||||||
using Toak.Serialization;
|
using Toak.Serialization;
|
||||||
using Toak.Core.Interfaces;
|
using Toak.Core.Interfaces;
|
||||||
|
|
||||||
@@ -8,12 +9,11 @@ namespace Toak.Configuration;
|
|||||||
|
|
||||||
public class ConfigManager : IConfigProvider
|
public class ConfigManager : IConfigProvider
|
||||||
{
|
{
|
||||||
private readonly string ConfigDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "toak");
|
private readonly string ConfigDir = Constants.Paths.ConfigDir;
|
||||||
private readonly string ConfigPath;
|
private readonly string ConfigPath = Constants.Paths.ConfigFile;
|
||||||
|
|
||||||
public ConfigManager()
|
public ConfigManager()
|
||||||
{
|
{
|
||||||
ConfigPath = Path.Combine(ConfigDir, "config.json");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ToakConfig LoadConfig()
|
public ToakConfig LoadConfig()
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ public class ToakConfig
|
|||||||
public bool ModuleTechnicalSanitization { get; set; } = true;
|
public bool ModuleTechnicalSanitization { get; set; } = true;
|
||||||
|
|
||||||
public string WhisperLanguage { get; set; } = string.Empty;
|
public string WhisperLanguage { get; set; } = string.Empty;
|
||||||
public string LlmModel { get; set; } = "openai/gpt-oss-20b";
|
public string LlmModel { get; set; } = Toak.Core.Constants.Defaults.LlmModel;
|
||||||
public string WhisperModel { get; set; } = "whisper-large-v3-turbo";
|
public string WhisperModel { get; set; } = Toak.Core.Constants.Defaults.WhisperModel;
|
||||||
public string StartSoundPath { get; set; } = "Assets/Audio/beep.wav";
|
public string StartSoundPath { get; set; } = "Assets/Audio/beep.wav";
|
||||||
public string StopSoundPath { get; set; } = "Assets/Audio/beep.wav";
|
public string StopSoundPath { get; set; } = "Assets/Audio/beep.wav";
|
||||||
public List<string> ActiveSkills { get; set; } = new List<string> { "Terminal", "Translate" };
|
public List<string> ActiveSkills { get; set; } = new List<string> { "Terminal", "Translate" };
|
||||||
|
|||||||
48
Core/Constants.cs
Normal file
48
Core/Constants.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Toak.Core;
|
||||||
|
|
||||||
|
public static class Constants
|
||||||
|
{
|
||||||
|
public const string AppName = "toak";
|
||||||
|
|
||||||
|
public static class Paths
|
||||||
|
{
|
||||||
|
public static readonly string AppDataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), AppName);
|
||||||
|
public static readonly string ConfigDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", AppName);
|
||||||
|
|
||||||
|
public static readonly string ConfigFile = Path.Combine(ConfigDir, "config.json");
|
||||||
|
public static readonly string HistoryFile = Path.Combine(AppDataDir, "history.jsonl");
|
||||||
|
public static readonly string DaemonLockFile = Path.Combine(AppDataDir, "daemon.lock");
|
||||||
|
|
||||||
|
public static readonly string StateFile = Path.Combine(Path.GetTempPath(), "toak_state.pid");
|
||||||
|
public static readonly string RecordingWavFile = Path.Combine(Path.GetTempPath(), "toak_recording.wav");
|
||||||
|
public static readonly string LatencyTestWavFile = Path.Combine(Path.GetTempPath(), "toak_latency_test.wav");
|
||||||
|
|
||||||
|
public static string GetSocketPath()
|
||||||
|
{
|
||||||
|
var runtimeDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
|
||||||
|
return Path.Combine(string.IsNullOrEmpty(runtimeDir) ? Path.GetTempPath() : runtimeDir, "toak.sock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Commands
|
||||||
|
{
|
||||||
|
public const string AudioRecord = "pw-record";
|
||||||
|
public const string AudioFfmpeg = "ffmpeg";
|
||||||
|
public const string ProcessKill = "kill";
|
||||||
|
public const string TypeX11 = "xdotool";
|
||||||
|
public const string TypeWayland = "wtype";
|
||||||
|
public const string Notify = "notify-send";
|
||||||
|
public const string PlaySound = "paplay";
|
||||||
|
public const string ClipboardX11 = "xclip";
|
||||||
|
public const string ClipboardWayland = "wl-copy";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Defaults
|
||||||
|
{
|
||||||
|
public const string LlmModel = "openai/gpt-oss-20b";
|
||||||
|
public const string WhisperModel = "whisper-large-v3-turbo";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,19 +15,14 @@ public static class DaemonService
|
|||||||
{
|
{
|
||||||
public static string GetSocketPath()
|
public static string GetSocketPath()
|
||||||
{
|
{
|
||||||
var runtimeDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
|
return Constants.Paths.GetSocketPath();
|
||||||
if (string.IsNullOrEmpty(runtimeDir))
|
|
||||||
{
|
|
||||||
runtimeDir = Path.GetTempPath();
|
|
||||||
}
|
|
||||||
return Path.Combine(runtimeDir, "toak.sock");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FileStream? _lockFile;
|
private static FileStream? _lockFile;
|
||||||
|
|
||||||
public static async Task StartAsync(bool verbose)
|
public static async Task StartAsync(bool verbose)
|
||||||
{
|
{
|
||||||
var lockPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "toak", "daemon.lock");
|
var lockPath = Constants.Paths.DaemonLockFile;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(lockPath)!);
|
Directory.CreateDirectory(Path.GetDirectoryName(lockPath)!);
|
||||||
|
|||||||
@@ -10,12 +10,11 @@ namespace Toak.Core;
|
|||||||
|
|
||||||
public class HistoryManager : IHistoryManager
|
public class HistoryManager : IHistoryManager
|
||||||
{
|
{
|
||||||
private readonly string HistoryDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "toak");
|
private readonly string HistoryDir = Constants.Paths.AppDataDir;
|
||||||
private readonly string HistoryFile;
|
private readonly string HistoryFile = Constants.Paths.HistoryFile;
|
||||||
|
|
||||||
public HistoryManager()
|
public HistoryManager()
|
||||||
{
|
{
|
||||||
HistoryFile = Path.Combine(HistoryDir, "history.jsonl");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveEntry(string rawTranscript, string refinedText, string? skillName, long durationMs)
|
public void SaveEntry(string rawTranscript, string refinedText, string? skillName, long durationMs)
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ public interface IConfigProvider
|
|||||||
|
|
||||||
public interface ISpeechClient
|
public interface ISpeechClient
|
||||||
{
|
{
|
||||||
Task<string> TranscribeAsync(string filePath, string language = "", string model = "whisper-large-v3-turbo");
|
Task<string> TranscribeAsync(string filePath, string language = "", string model = Toak.Core.Constants.Defaults.WhisperModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ILlmClient
|
public interface ILlmClient
|
||||||
{
|
{
|
||||||
Task<string> RefineTextAsync(string rawTranscript, string systemPrompt, string model = "openai/gpt-oss-20b");
|
Task<string> RefineTextAsync(string rawTranscript, string systemPrompt, string model = Toak.Core.Constants.Defaults.LlmModel);
|
||||||
IAsyncEnumerable<string> RefineTextStreamAsync(string rawTranscript, string systemPrompt, string model = "openai/gpt-oss-20b");
|
IAsyncEnumerable<string> RefineTextStreamAsync(string rawTranscript, string systemPrompt, string model = Toak.Core.Constants.Defaults.LlmModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IAudioRecorder
|
public interface IAudioRecorder
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Toak.Core;
|
|||||||
|
|
||||||
public class StateTracker : IRecordingStateTracker
|
public class StateTracker : IRecordingStateTracker
|
||||||
{
|
{
|
||||||
private readonly string StateFilePath = Path.Combine(Path.GetTempPath(), "toak_state.pid");
|
private readonly string StateFilePath = Constants.Paths.StateFile;
|
||||||
|
|
||||||
public bool IsRecording()
|
public bool IsRecording()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class ClipboardManager : IClipboardManager
|
|||||||
{
|
{
|
||||||
pInfo = new ProcessStartInfo
|
pInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "wl-copy",
|
FileName = Toak.Core.Constants.Commands.ClipboardWayland,
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
RedirectStandardInput = true
|
RedirectStandardInput = true
|
||||||
@@ -35,7 +35,7 @@ public class ClipboardManager : IClipboardManager
|
|||||||
{
|
{
|
||||||
pInfo = new ProcessStartInfo
|
pInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "xclip",
|
FileName = Toak.Core.Constants.Commands.ClipboardX11,
|
||||||
Arguments = "-selection clipboard",
|
Arguments = "-selection clipboard",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class Notifications : INotifications
|
|||||||
{
|
{
|
||||||
var pInfo = new ProcessStartInfo
|
var pInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "notify-send",
|
FileName = Toak.Core.Constants.Commands.Notify,
|
||||||
Arguments = $"-a \"Toak\" \"{summary}\" \"{body}\"",
|
Arguments = $"-a \"Toak\" \"{summary}\" \"{body}\"",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true
|
CreateNoWindow = true
|
||||||
@@ -66,7 +66,7 @@ public class Notifications : INotifications
|
|||||||
|
|
||||||
var pInfo = new ProcessStartInfo
|
var pInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "paplay",
|
FileName = Toak.Core.Constants.Commands.PlaySound,
|
||||||
Arguments = $"\"{absolutePath}\"",
|
Arguments = $"\"{absolutePath}\"",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true
|
CreateNoWindow = true
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class TextInjector : ITextInjector
|
|||||||
Logger.LogDebug($"Injecting text using wtype...");
|
Logger.LogDebug($"Injecting text using wtype...");
|
||||||
pInfo = new ProcessStartInfo
|
pInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "wtype",
|
FileName = Toak.Core.Constants.Commands.TypeWayland,
|
||||||
Arguments = $"\"{text.Replace("\"", "\\\"")}\"",
|
Arguments = $"\"{text.Replace("\"", "\\\"")}\"",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true
|
CreateNoWindow = true
|
||||||
@@ -39,7 +39,7 @@ public class TextInjector : ITextInjector
|
|||||||
Logger.LogDebug($"Injecting text using xdotool...");
|
Logger.LogDebug($"Injecting text using xdotool...");
|
||||||
pInfo = new ProcessStartInfo
|
pInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "xdotool",
|
FileName = Toak.Core.Constants.Commands.TypeX11,
|
||||||
Arguments = $"type --clearmodifiers --delay 0 \"{text.Replace("\"", "\\\"")}\"",
|
Arguments = $"type --clearmodifiers --delay 0 \"{text.Replace("\"", "\\\"")}\"",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true
|
CreateNoWindow = true
|
||||||
@@ -67,7 +67,7 @@ public class TextInjector : ITextInjector
|
|||||||
Logger.LogDebug($"Setting up stream injection using wtype...");
|
Logger.LogDebug($"Setting up stream injection using wtype...");
|
||||||
pInfo = new ProcessStartInfo
|
pInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "wtype",
|
FileName = Toak.Core.Constants.Commands.TypeWayland,
|
||||||
Arguments = "-",
|
Arguments = "-",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
@@ -79,7 +79,7 @@ public class TextInjector : ITextInjector
|
|||||||
Logger.LogDebug($"Setting up stream injection using xdotool...");
|
Logger.LogDebug($"Setting up stream injection using xdotool...");
|
||||||
pInfo = new ProcessStartInfo
|
pInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "xdotool",
|
FileName = Toak.Core.Constants.Commands.TypeX11,
|
||||||
Arguments = "type --clearmodifiers --delay 0 --file -",
|
Arguments = "type --clearmodifiers --delay 0 --file -",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
|
|||||||
Reference in New Issue
Block a user