add voxtype
This commit is contained in:
22
flake.lock
generated
22
flake.lock
generated
@@ -675,6 +675,7 @@
|
||||
"tonearm": "tonearm",
|
||||
"turny": "turny",
|
||||
"voidauth": "voidauth",
|
||||
"voxtype": "voxtype",
|
||||
"workout-sync": "workout-sync"
|
||||
}
|
||||
},
|
||||
@@ -872,6 +873,27 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"voxtype": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_7",
|
||||
"nixpkgs": [
|
||||
"nixpkgs-unstable"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1772443545,
|
||||
"narHash": "sha256-oD3lameQXilKcgxQORR2l0+iDbnCO61+mjYD3MEVbuQ=",
|
||||
"owner": "peteonrails",
|
||||
"repo": "voxtype",
|
||||
"rev": "d011f3ff074a6a14c14e75fefb375a408e9e8887",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "peteonrails",
|
||||
"repo": "voxtype",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"workout-sync": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
|
||||
@@ -56,6 +56,10 @@
|
||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
inputs.flake-parts.follows = "flake-parts";
|
||||
};
|
||||
voxtype = {
|
||||
url = "github:peteonrails/voxtype";
|
||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
};
|
||||
};
|
||||
outputs =
|
||||
{
|
||||
|
||||
@@ -12,9 +12,11 @@
|
||||
../gnome
|
||||
./dnote.nix
|
||||
../modules/zed
|
||||
../modules/voxtype
|
||||
./secrets.nix
|
||||
inputs.hastebin.nixosModules.hm
|
||||
inputs.agenix.homeManagerModules.default
|
||||
inputs.voxtype.homeManagerModules.default
|
||||
];
|
||||
|
||||
# This value determines the Home Manager release that your configuration is
|
||||
@@ -94,6 +96,18 @@
|
||||
'')
|
||||
pkgs-unstable.tidal-hifi
|
||||
inputs.tonearm.packages.${pkgs.stdenv.hostPlatform.system}.tonearm
|
||||
(writeShellScriptBin "voxtoggle" ''
|
||||
status=$(${lib.getExe config.programs.voxtype.package} status)
|
||||
pid=$(cat ''${XDG_RUNTIME_DIR}/voxtype/pid)
|
||||
|
||||
if [[ "$status" == "stopped" ]]; then
|
||||
exit 1
|
||||
elif [[ "$status" == "recording" ]]; then
|
||||
kill -SIGUSR2 "$pid"
|
||||
else
|
||||
kill -SIGUSR1 "$pid"
|
||||
fi
|
||||
'')
|
||||
];
|
||||
|
||||
programs = {
|
||||
@@ -427,6 +441,51 @@
|
||||
enable = true;
|
||||
defaultEditor = true;
|
||||
};
|
||||
|
||||
voxtype = {
|
||||
enable = true;
|
||||
package = inputs.voxtype.packages.${pkgs.stdenv.hostPlatform.system}.vulkan;
|
||||
model.name = "large-v3-turbo";
|
||||
service.enable = true;
|
||||
settings = {
|
||||
hotkey.enabled = false;
|
||||
whisper.language = "auto";
|
||||
output.notification = {
|
||||
on_recording_start = false;
|
||||
on_recording_stop = false;
|
||||
on_transcription = false;
|
||||
};
|
||||
};
|
||||
postProcessing = {
|
||||
enable = true;
|
||||
settings = {
|
||||
model = "qwen3:4b-instruct";
|
||||
commonInstructions = "no quotes, no emojis, no explanations";
|
||||
prompts = [
|
||||
{
|
||||
title = "Clean up";
|
||||
instructions = "Clean up this dictation. Remove filler words, fix grammar and punctuation. Output ONLY the cleaned text";
|
||||
}
|
||||
{
|
||||
title = "Make a title";
|
||||
instructions = "Make a concise and descriptive title for this dictation. Output ONLY the title";
|
||||
}
|
||||
{
|
||||
title = "Summarize";
|
||||
instructions = "Summarize this dictation in a few sentences. Output ONLY the summary";
|
||||
}
|
||||
{
|
||||
title = "Commit message";
|
||||
instructions = "Write a concise and descriptive git commit message for this dictation. Output ONLY the commit message";
|
||||
}
|
||||
{
|
||||
title = "Translate to English";
|
||||
instructions = "Translate this dictation to English. Remove filler words, fix grammar and punctuation. Output ONLY the translation";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
gnome = {
|
||||
|
||||
92
home/modules/voxtype/default.nix
Normal file
92
home/modules/voxtype/default.nix
Normal file
@@ -0,0 +1,92 @@
|
||||
{
|
||||
config,
|
||||
osConfig,
|
||||
lib,
|
||||
pkgs,
|
||||
pkgs-unstable,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.programs.voxtype;
|
||||
postProcessUnwrapped =
|
||||
pkgs.runCommand "voxtype-post-process-unwrapped"
|
||||
{
|
||||
code = ./post-process.cr;
|
||||
nativeBuildInputs = [
|
||||
pkgs-unstable.crystal
|
||||
];
|
||||
}
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
crystal build $code -o $out/bin/voxtype-post-process
|
||||
'';
|
||||
|
||||
postProcess = pkgs.symlinkJoin {
|
||||
name = "voxtype-post-process";
|
||||
paths = [ postProcessUnwrapped ];
|
||||
nativeBuildInputs = [ pkgs.makeBinaryWrapper ];
|
||||
postBuild = ''
|
||||
wrapProgram $out/bin/voxtype-post-process \
|
||||
--set OLLAMA_PORT ${toString osConfig.services.ollama.port} \
|
||||
--set WALKER_BIN ${lib.getExe config.services.walker.package}
|
||||
'';
|
||||
meta.mainProgram = "voxtype-post-process";
|
||||
};
|
||||
in
|
||||
{
|
||||
options.programs.voxtype = {
|
||||
postProcessing = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
enable = lib.mkEnableOption "Enable post-processing of transcriptions";
|
||||
settings = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
model = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The ollama model to use for post-processing";
|
||||
};
|
||||
commonInstructions = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "no quotes, no emojis, no explanations";
|
||||
description = "Instructions to include in every post-processing prompt";
|
||||
};
|
||||
prompts = lib.mkOption {
|
||||
type = lib.types.listOf (
|
||||
lib.types.submodule {
|
||||
options = {
|
||||
title = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "A title for this prompt, used in the selector";
|
||||
};
|
||||
instructions = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Instructions to include in the post-processing prompt, in addition to the common instructions";
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
default = [
|
||||
{
|
||||
title = "Clean up";
|
||||
instructions = "Clean up this dictation. Remove filler words, fix grammar and punctuation. Output ONLY the cleaned text";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.postProcessing.enable {
|
||||
xdg.configFile."voxtype/post-processing.json".text = builtins.toJSON cfg.postProcessing.settings;
|
||||
programs.voxtype.settings.output.post_process = {
|
||||
command = lib.getExe postProcess;
|
||||
timeout_ms = 5 * 60 * 1000; # 5 minutes
|
||||
};
|
||||
};
|
||||
}
|
||||
54
home/modules/voxtype/post-process.cr
Normal file
54
home/modules/voxtype/post-process.cr
Normal file
@@ -0,0 +1,54 @@
|
||||
require "json"
|
||||
require "http/client"
|
||||
|
||||
struct OllamaResponse
|
||||
include JSON::Serializable
|
||||
|
||||
getter response : String
|
||||
end
|
||||
|
||||
struct Prompt
|
||||
include JSON::Serializable
|
||||
|
||||
getter title : String
|
||||
getter instructions : String
|
||||
end
|
||||
|
||||
struct Config
|
||||
include JSON::Serializable
|
||||
|
||||
getter model : String
|
||||
getter prompts : Array(Prompt)
|
||||
@[JSON::Field(key: "commonInstructions")]
|
||||
getter common_instructions : String
|
||||
end
|
||||
|
||||
config_path = "#{ENV.fetch("XDG_CONFIG_HOME", "~/.config")}/voxtype/post-processing.json"
|
||||
config = File.open(config_path) { |file| Config.from_json(file) }
|
||||
client = HTTP::Client.new("localhost", ENV.fetch("OLLAMA_PORT", "11434").to_i)
|
||||
|
||||
prompt_selection = Process.run(ENV["WALKER_BIN"], ["--dmenu"]) do |process|
|
||||
config.prompts.each do |prompt|
|
||||
process.input.puts prompt.title
|
||||
end
|
||||
|
||||
process.input.close
|
||||
process.output.gets_to_end.chomp
|
||||
end
|
||||
|
||||
instructions = config.prompts.find { |prompt| prompt.title == prompt_selection }.try(&.instructions) || prompt_selection
|
||||
|
||||
payload = {
|
||||
model: config.model,
|
||||
prompt: "#{instructions} - #{config.common_instructions}:\n\n#{STDIN.gets_to_end.chomp}",
|
||||
think: false,
|
||||
stream: false,
|
||||
}
|
||||
|
||||
client.post("/api/generate", body: payload.to_json) do |response|
|
||||
if response.status_code == 200
|
||||
puts OllamaResponse.from_json(response.body_io).response.strip
|
||||
else
|
||||
abort "Ollama API error: #{response.status_code} #{response.body}"
|
||||
end
|
||||
end
|
||||
@@ -51,10 +51,12 @@ in
|
||||
|
||||
ollama = {
|
||||
enable = true;
|
||||
acceleration = "rocm";
|
||||
environmentVariables = {
|
||||
HSA_OVERRIDE_GFX_VERSION = "11.0.3";
|
||||
};
|
||||
package = pkgs-unstable.ollama-vulkan;
|
||||
syncModels = true;
|
||||
loadModels = [
|
||||
"qwen3:4b-instruct"
|
||||
"qwen3:8b"
|
||||
];
|
||||
};
|
||||
|
||||
borgbackup.jobs.root = {
|
||||
@@ -139,4 +141,31 @@ in
|
||||
environment.etc."1password/custom_allowed_browsers".text = ''
|
||||
vivaldi
|
||||
'';
|
||||
|
||||
systemd.services.ollama-keep-alive =
|
||||
let
|
||||
ollamaURL = "http://localhost:${toString config.services.ollama.port}/api/generate";
|
||||
payload = {
|
||||
model = lib.elemAt config.services.ollama.loadModels 0;
|
||||
keep_alive = -1;
|
||||
};
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
description = "Keep Ollama primary model loaded by pinging it";
|
||||
after = [
|
||||
"ollama.service"
|
||||
"network-online.target"
|
||||
];
|
||||
wants = [ "network-online.target" ];
|
||||
bindsTo = [ "ollama.service" ];
|
||||
wantedBy = [
|
||||
"multi-user.target"
|
||||
"ollama.service"
|
||||
];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${pkgs.curl}/bin/curl -s '${ollamaURL}' -d '${builtins.toJSON payload}'";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user