feat: Add Native AOT compilation and source-generated JSON serialization, streamline CLI arguments, and remove translation functionality.
This commit is contained in:
16
AppJsonSerializerContext.cs
Normal file
16
AppJsonSerializerContext.cs
Normal 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
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ public class ToakConfig
|
|||||||
public string StyleMode { get; set; } = "Professional";
|
public string StyleMode { get; set; } = "Professional";
|
||||||
public bool StructureBulletPoints { get; set; } = false;
|
public bool StructureBulletPoints { get; set; } = false;
|
||||||
public bool StructureSmartParagraphing { get; set; } = true;
|
public bool StructureSmartParagraphing { get; set; } = true;
|
||||||
public string TargetLanguage { get; set; } = string.Empty;
|
|
||||||
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; } = "openai/gpt-oss-20b";
|
||||||
public string WhisperModel { get; set; } = "whisper-large-v3-turbo";
|
public string WhisperModel { get; set; } = "whisper-large-v3-turbo";
|
||||||
@@ -33,7 +33,7 @@ public static class ConfigManager
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var json = File.ReadAllText(ConfigPath);
|
var json = File.ReadAllText(ConfigPath);
|
||||||
return JsonSerializer.Deserialize<ToakConfig>(json) ?? new ToakConfig();
|
return JsonSerializer.Deserialize(json, AppJsonSerializerContext.Default.ToakConfig) ?? new ToakConfig();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@@ -48,7 +48,7 @@ public static class ConfigManager
|
|||||||
Directory.CreateDirectory(ConfigDir);
|
Directory.CreateDirectory(ConfigDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
|
var json = JsonSerializer.Serialize(config, AppJsonSerializerContext.Default.ToakConfig);
|
||||||
File.WriteAllText(ConfigPath, json);
|
File.WriteAllText(ConfigPath, json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public class GroqApiClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
var json = await response.Content.ReadAsStringAsync();
|
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;
|
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, AppJsonSerializerContext.Default.LlamaRequest), System.Text.Encoding.UTF8, "application/json");
|
||||||
var jsonContent = new StringContent(JsonSerializer.Serialize(requestBody, jsonOptions), System.Text.Encoding.UTF8, "application/json");
|
|
||||||
|
|
||||||
var response = await _httpClient.PostAsync("chat/completions", jsonContent);
|
var response = await _httpClient.PostAsync("chat/completions", jsonContent);
|
||||||
|
|
||||||
@@ -110,7 +109,7 @@ public class GroqApiClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
var json = await response.Content.ReadAsStringAsync();
|
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;
|
return result?.Choices?.FirstOrDefault()?.Message?.Content ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
25
Program.cs
25
Program.cs
@@ -1,20 +1,13 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Toak;
|
using Toak;
|
||||||
|
|
||||||
bool pipeToStdout = args.Contains("--pipe") || Console.IsOutputRedirected;
|
bool pipeToStdout = args.Contains("--pipe") || args.Contains("-p") || Console.IsOutputRedirected;
|
||||||
bool rawOutput = args.Contains("--raw");
|
|
||||||
bool copyToClipboard = args.Contains("--copy");
|
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("Toak: High-speed Linux Dictation");
|
||||||
Console.WriteLine("Usage:");
|
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 config <key> <value> - Update a specific configuration setting");
|
||||||
Console.WriteLine(" toak show - Show current configuration");
|
Console.WriteLine(" toak show - Show current configuration");
|
||||||
Console.WriteLine("Flags:");
|
Console.WriteLine("Flags:");
|
||||||
Console.WriteLine(" --pipe - Output transcription to stdout instead of typing");
|
Console.WriteLine(" -h, --help - Show this help message");
|
||||||
Console.WriteLine(" --raw - Skip LLM refinement, output raw transcript");
|
Console.WriteLine(" -p, --pipe - Output transcription to stdout instead of typing");
|
||||||
Console.WriteLine(" --copy - Copy to clipboard instead of typing");
|
Console.WriteLine(" --copy - Copy to clipboard instead of typing");
|
||||||
Console.WriteLine(" --translate <lang> - Translate output to the specified language");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,10 +242,6 @@ if (command == "toggle")
|
|||||||
AudioRecorder.StopRecording();
|
AudioRecorder.StopRecording();
|
||||||
|
|
||||||
var config = ConfigManager.LoadConfig();
|
var config = ConfigManager.LoadConfig();
|
||||||
if (!string.IsNullOrWhiteSpace(translateTo))
|
|
||||||
{
|
|
||||||
config.TargetLanguage = translateTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(config.GroqApiKey))
|
if (string.IsNullOrWhiteSpace(config.GroqApiKey))
|
||||||
{
|
{
|
||||||
@@ -285,11 +273,8 @@ if (command == "toggle")
|
|||||||
string finalText = transcript;
|
string finalText = transcript;
|
||||||
|
|
||||||
// 2. LLM Refinement
|
// 2. LLM Refinement
|
||||||
if (!rawOutput)
|
|
||||||
{
|
|
||||||
var systemPrompt = PromptBuilder.BuildPrompt(config);
|
var systemPrompt = PromptBuilder.BuildPrompt(config);
|
||||||
finalText = await groq.RefineTextAsync(transcript, systemPrompt, config.LlmModel);
|
finalText = await groq.RefineTextAsync(transcript, systemPrompt, config.LlmModel);
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Output
|
// 3. Output
|
||||||
if (pipeToStdout)
|
if (pipeToStdout)
|
||||||
|
|||||||
@@ -21,10 +21,7 @@ public static class PromptBuilder
|
|||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
sb.AppendLine("FORMATTING RULES:");
|
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)
|
if (config.ModulePunctuation)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<PublishAot>true</PublishAot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
12
install.sh
Executable file
12
install.sh
Executable 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."
|
||||||
Reference in New Issue
Block a user