1
0

feat: Add Native AOT compilation and source-generated JSON serialization, streamline CLI arguments, and remove translation functionality.

This commit is contained in:
2026-02-26 21:40:17 +01:00
parent c2f4cbbfb2
commit fbff8c98ff
7 changed files with 43 additions and 33 deletions

View File

@@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Toak;
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(ToakConfig))]
[JsonSerializable(typeof(WhisperResponse))]
[JsonSerializable(typeof(LlamaRequest))]
[JsonSerializable(typeof(LlamaRequestMessage))]
[JsonSerializable(typeof(LlamaResponse))]
[JsonSerializable(typeof(LlamaChoice))]
[JsonSerializable(typeof(LlamaRequestMessage[]))]
[JsonSerializable(typeof(LlamaChoice[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext
{
}

View File

@@ -12,7 +12,7 @@ public class ToakConfig
public string StyleMode { get; set; } = "Professional";
public bool StructureBulletPoints { get; set; } = false;
public bool StructureSmartParagraphing { get; set; } = true;
public string TargetLanguage { get; set; } = string.Empty;
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";
@@ -33,7 +33,7 @@ public static class ConfigManager
try
{
var json = File.ReadAllText(ConfigPath);
return JsonSerializer.Deserialize<ToakConfig>(json) ?? new ToakConfig();
return JsonSerializer.Deserialize(json, AppJsonSerializerContext.Default.ToakConfig) ?? new ToakConfig();
}
catch (Exception)
{
@@ -48,7 +48,7 @@ public static class ConfigManager
Directory.CreateDirectory(ConfigDir);
}
var json = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
var json = JsonSerializer.Serialize(config, AppJsonSerializerContext.Default.ToakConfig);
File.WriteAllText(ConfigPath, json);
}
}

View File

@@ -81,7 +81,7 @@ public class GroqApiClient
}
var json = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<WhisperResponse>(json);
var result = JsonSerializer.Deserialize(json, AppJsonSerializerContext.Default.WhisperResponse);
return result?.Text ?? string.Empty;
}
@@ -98,8 +98,7 @@ public class GroqApiClient
}
};
var jsonOptions = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
var jsonContent = new StringContent(JsonSerializer.Serialize(requestBody, jsonOptions), System.Text.Encoding.UTF8, "application/json");
var jsonContent = new StringContent(JsonSerializer.Serialize(requestBody, AppJsonSerializerContext.Default.LlamaRequest), System.Text.Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("chat/completions", jsonContent);
@@ -110,7 +109,7 @@ public class GroqApiClient
}
var json = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<LlamaResponse>(json);
var result = JsonSerializer.Deserialize(json, AppJsonSerializerContext.Default.LlamaResponse);
return result?.Choices?.FirstOrDefault()?.Message?.Content ?? string.Empty;
}

View File

@@ -1,20 +1,13 @@
using System.Diagnostics;
using Toak;
bool pipeToStdout = args.Contains("--pipe") || Console.IsOutputRedirected;
bool rawOutput = args.Contains("--raw");
bool pipeToStdout = args.Contains("--pipe") || args.Contains("-p") || Console.IsOutputRedirected;
bool copyToClipboard = args.Contains("--copy");
string translateTo = "";
int translateIndex = Array.IndexOf(args, "--translate");
if (translateIndex >= 0 && translateIndex < args.Length - 1)
{
translateTo = args[translateIndex + 1];
}
string command = args.FirstOrDefault(a => !a.StartsWith("--")) ?? "";
string command = args.FirstOrDefault(a => !a.StartsWith("-")) ?? "";
if (string.IsNullOrEmpty(command) && args.Length == 0)
if (args.Contains("-h") || args.Contains("--help") || (string.IsNullOrEmpty(command) && args.Length == 0))
{
Console.WriteLine("Toak: High-speed Linux Dictation");
Console.WriteLine("Usage:");
@@ -25,10 +18,9 @@ if (string.IsNullOrEmpty(command) && args.Length == 0)
Console.WriteLine(" toak config <key> <value> - Update a specific configuration setting");
Console.WriteLine(" toak show - Show current configuration");
Console.WriteLine("Flags:");
Console.WriteLine(" --pipe - Output transcription to stdout instead of typing");
Console.WriteLine(" --raw - Skip LLM refinement, output raw transcript");
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(" --translate <lang> - Translate output to the specified language");
return;
}
@@ -250,10 +242,6 @@ if (command == "toggle")
AudioRecorder.StopRecording();
var config = ConfigManager.LoadConfig();
if (!string.IsNullOrWhiteSpace(translateTo))
{
config.TargetLanguage = translateTo;
}
if (string.IsNullOrWhiteSpace(config.GroqApiKey))
{
@@ -285,11 +273,8 @@ if (command == "toggle")
string finalText = transcript;
// 2. LLM Refinement
if (!rawOutput)
{
var systemPrompt = PromptBuilder.BuildPrompt(config);
finalText = await groq.RefineTextAsync(transcript, systemPrompt, config.LlmModel);
}
var systemPrompt = PromptBuilder.BuildPrompt(config);
finalText = await groq.RefineTextAsync(transcript, systemPrompt, config.LlmModel);
// 3. Output
if (pipeToStdout)

View File

@@ -21,10 +21,7 @@ public static class PromptBuilder
sb.AppendLine();
sb.AppendLine("FORMATTING RULES:");
if (!string.IsNullOrWhiteSpace(config.TargetLanguage))
{
sb.AppendLine($"- CRITICAL: You must translate the text to {config.TargetLanguage} while applying all other formatting rules.");
}
if (config.ModulePunctuation)
{

View File

@@ -5,6 +5,7 @@
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
</PropertyGroup>
</Project>

12
install.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# Exit immediately if a command exits with a non-zero status
set -e
echo "Building Toak Native AOT executable..."
dotnet publish -c Release -r linux-x64
echo "Installing to /usr/bin/toak... (This may prompt for your sudo password)"
sudo cp bin/Release/net10.0/linux-x64/publish/Toak /usr/bin/toak
echo "Installation complete! You can now run 'toak' from anywhere."