Merge pull request #2 from cjpais/llm

Llm
This commit is contained in:
CJ Pais 2025-02-17 10:09:21 -08:00 committed by GitHub
commit 45e9c3a3ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 811 additions and 85 deletions

502
src-tauri/Cargo.lock generated
View file

@ -78,6 +78,56 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "anstream"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [
"anstyle",
"once_cell",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.95" version = "1.0.95"
@ -228,6 +278,28 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "async-stream"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
dependencies = [
"async-stream-impl",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-stream-impl"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
]
[[package]] [[package]]
name = "async-task" name = "async-task"
version = "4.7.1" version = "4.7.1"
@ -693,6 +765,12 @@ dependencies = [
"objc", "objc",
] ]
[[package]]
name = "colorchoice"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.6.7" version = "4.6.7"
@ -1176,6 +1254,15 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "endi" name = "endi"
version = "1.1.0" version = "1.1.0"
@ -1223,6 +1310,29 @@ dependencies = [
"syn 2.0.96", "syn 2.0.96",
] ]
[[package]]
name = "env_filter"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
]
[[package]] [[package]]
name = "epoll" name = "epoll"
version = "4.3.3" version = "4.3.3"
@ -1394,6 +1504,21 @@ dependencies = [
"new_debug_unreachable", "new_debug_unreachable",
] ]
[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.31" version = "0.3.31"
@ -1401,6 +1526,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink",
] ]
[[package]] [[package]]
@ -1468,6 +1594,7 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [ dependencies = [
"futures-channel",
"futures-core", "futures-core",
"futures-io", "futures-io",
"futures-macro", "futures-macro",
@ -1814,13 +1941,35 @@ dependencies = [
"syn 2.0.96", "syn 2.0.96",
] ]
[[package]]
name = "h2"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http 0.2.12",
"indexmap 2.7.1",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "handy-app" name = "handy-app"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cpal", "cpal",
"env_logger",
"log",
"rdev", "rdev",
"rig-core",
"rubato", "rubato",
"samplerate", "samplerate",
"serde", "serde",
@ -1833,6 +1982,7 @@ dependencies = [
"tauri-plugin-macos-permissions", "tauri-plugin-macos-permissions",
"tauri-plugin-opener", "tauri-plugin-opener",
"tauri-plugin-single-instance", "tauri-plugin-single-instance",
"tokio",
"whisper-rs", "whisper-rs",
] ]
@ -1895,6 +2045,17 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "http"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [
"bytes",
"fnv",
"itoa 1.0.14",
]
[[package]] [[package]]
name = "http" name = "http"
version = "1.2.0" version = "1.2.0"
@ -1906,6 +2067,17 @@ dependencies = [
"itoa 1.0.14", "itoa 1.0.14",
] ]
[[package]]
name = "http-body"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
"http 0.2.12",
"pin-project-lite",
]
[[package]] [[package]]
name = "http-body" name = "http-body"
version = "1.0.1" version = "1.0.1"
@ -1913,7 +2085,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [ dependencies = [
"bytes", "bytes",
"http", "http 1.2.0",
] ]
[[package]] [[package]]
@ -1924,8 +2096,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-util", "futures-util",
"http", "http 1.2.0",
"http-body", "http-body 1.0.1",
"pin-project-lite", "pin-project-lite",
] ]
@ -1935,6 +2107,42 @@ version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http 0.2.12",
"http-body 0.4.6",
"httparse",
"httpdate",
"itoa 1.0.14",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.6.0" version = "1.6.0"
@ -1944,8 +2152,8 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"http", "http 1.2.0",
"http-body", "http-body 1.0.1",
"httparse", "httparse",
"itoa 1.0.14", "itoa 1.0.14",
"pin-project-lite", "pin-project-lite",
@ -1961,8 +2169,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"http", "http 1.2.0",
"hyper", "hyper 1.6.0",
"hyper-util", "hyper-util",
"rustls", "rustls",
"rustls-pki-types", "rustls-pki-types",
@ -1972,6 +2180,19 @@ dependencies = [
"webpki-roots", "webpki-roots",
] ]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper 0.14.32",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.10" version = "0.1.10"
@ -1981,9 +2202,9 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"http", "http 1.2.0",
"http-body", "http-body 1.0.1",
"hyper", "hyper 1.6.0",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"tokio", "tokio",
@ -2258,6 +2479,12 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.12.1" version = "0.12.1"
@ -2647,6 +2874,23 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "native-tls"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]] [[package]]
name = "ndk" name = "ndk"
version = "0.8.0" version = "0.8.0"
@ -3077,12 +3321,65 @@ dependencies = [
"pathdiff", "pathdiff",
] ]
[[package]]
name = "openssl"
version = "0.10.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd"
dependencies = [
"bitflags 2.8.0",
"cfg-if",
"foreign-types 0.3.2",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
]
[[package]]
name = "openssl-probe"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "option-ext" name = "option-ext"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordered-float"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "ordered-stream" name = "ordered-stream"
version = "0.2.0" version = "0.2.0"
@ -3720,6 +4017,48 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "reqwest"
version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [
"base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.32",
"hyper-tls",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls-pemfile 1.0.4",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper 0.1.2",
"system-configuration",
"tokio",
"tokio-native-tls",
"tokio-util",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"winreg 0.50.0",
]
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.12.12" version = "0.12.12"
@ -3730,10 +4069,10 @@ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
"futures-util", "futures-util",
"http", "http 1.2.0",
"http-body", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper", "hyper 1.6.0",
"hyper-rustls", "hyper-rustls",
"hyper-util", "hyper-util",
"ipnet", "ipnet",
@ -3745,12 +4084,12 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"quinn", "quinn",
"rustls", "rustls",
"rustls-pemfile", "rustls-pemfile 2.2.0",
"rustls-pki-types", "rustls-pki-types",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"sync_wrapper", "sync_wrapper 1.0.2",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls",
"tokio-util", "tokio-util",
@ -3765,6 +4104,25 @@ dependencies = [
"windows-registry", "windows-registry",
] ]
[[package]]
name = "rig-core"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aaf53f8631db9cc12287b5595b14964ae36a213f0d247055efcd26714f2da2b"
dependencies = [
"async-stream",
"bytes",
"futures",
"glob",
"ordered-float",
"reqwest 0.11.27",
"schemars",
"serde",
"serde_json",
"thiserror 1.0.69",
"tracing",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.8" version = "0.17.8"
@ -3861,6 +4219,15 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "rustls-pemfile"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64 0.21.7",
]
[[package]] [[package]]
name = "rustls-pemfile" name = "rustls-pemfile"
version = "2.2.0" version = "2.2.0"
@ -3920,6 +4287,15 @@ dependencies = [
"libsamplerate-sys", "libsamplerate-sys",
] ]
[[package]]
name = "schannel"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
dependencies = [
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "schemars" name = "schemars"
version = "0.8.21" version = "0.8.21"
@ -3953,6 +4329,29 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.8.0",
"core-foundation 0.9.4",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "selectors" name = "selectors"
version = "0.22.0" version = "0.22.0"
@ -4361,6 +4760,12 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]] [[package]]
name = "sync_wrapper" name = "sync_wrapper"
version = "1.0.2" version = "1.0.2"
@ -4381,6 +4786,27 @@ dependencies = [
"syn 2.0.96", "syn 2.0.96",
] ]
[[package]]
name = "system-configuration"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "system-deps" name = "system-deps"
version = "6.2.2" version = "6.2.2"
@ -4465,7 +4891,7 @@ dependencies = [
"glob", "glob",
"gtk", "gtk",
"heck 0.5.0", "heck 0.5.0",
"http", "http 1.2.0",
"jni", "jni",
"libc", "libc",
"log", "log",
@ -4477,7 +4903,7 @@ dependencies = [
"percent-encoding", "percent-encoding",
"plist", "plist",
"raw-window-handle", "raw-window-handle",
"reqwest", "reqwest 0.12.12",
"serde", "serde",
"serde_json", "serde_json",
"serde_repr", "serde_repr",
@ -4681,7 +5107,7 @@ checksum = "2274ef891ccc0a8d318deffa9d70053f947664d12d58b9c0d1ae5e89237e01f7"
dependencies = [ dependencies = [
"dpi", "dpi",
"gtk", "gtk",
"http", "http 1.2.0",
"jni", "jni",
"raw-window-handle", "raw-window-handle",
"serde", "serde",
@ -4699,7 +5125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3707b40711d3b9f6519150869e358ffbde7c57567fb9b5a8b51150606939b2a0" checksum = "3707b40711d3b9f6519150869e358ffbde7c57567fb9b5a8b51150606939b2a0"
dependencies = [ dependencies = [
"gtk", "gtk",
"http", "http 1.2.0",
"jni", "jni",
"log", "log",
"objc2", "objc2",
@ -4730,7 +5156,7 @@ dependencies = [
"dunce", "dunce",
"glob", "glob",
"html5ever", "html5ever",
"http", "http 1.2.0",
"infer", "infer",
"json-patch", "json-patch",
"kuchikiki", "kuchikiki",
@ -4918,6 +5344,16 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.26.1" version = "0.26.1"
@ -5020,7 +5456,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-util", "futures-util",
"pin-project-lite", "pin-project-lite",
"sync_wrapper", "sync_wrapper 1.0.2",
"tokio", "tokio",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
@ -5230,6 +5666,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.12.1" version = "1.12.1"
@ -5240,6 +5682,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version-compare" name = "version-compare"
version = "0.2.0" version = "0.2.0"
@ -6032,6 +6480,16 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "winreg"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "winreg" name = "winreg"
version = "0.52.0" version = "0.52.0"
@ -6078,7 +6536,7 @@ dependencies = [
"gdkx11", "gdkx11",
"gtk", "gtk",
"html5ever", "html5ever",
"http", "http 1.2.0",
"javascriptcore-rs", "javascriptcore-rs",
"jni", "jni",
"kuchikiki", "kuchikiki",

View file

@ -30,6 +30,10 @@ whisper-rs = { version = "0.13.2", features = ["whisper-cpp-log"] }
anyhow = "1.0.95" anyhow = "1.0.95"
rubato = "0.16.1" rubato = "0.16.1"
samplerate = "0.2.4" samplerate = "0.2.4"
rig-core = "0.8.0"
env_logger = "0.11.6"
log = "0.4.25"
tokio = "1.43.0"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
whisper-rs = { version = "0.13.2", features = ["metal"] } whisper-rs = { version = "0.13.2", features = ["metal"] }

View file

@ -1,62 +1,252 @@
mod managers; mod managers;
use log::{info, Level};
use managers::keybinding::KeyBindingManager; use managers::keybinding::KeyBindingManager;
use managers::transcription::TranscriptionManager; use managers::transcription::TranscriptionManager;
use managers::{audio::AudioRecordingManager, transcription}; use managers::{audio::AudioRecordingManager, transcription};
use rdev::Key; use rdev::{simulate, EventType, Key, SimulateError};
use rig::streaming::StreamingChoice;
use rig::{completion::Prompt, providers::anthropic};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::{thread, time};
use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_clipboard_manager::ClipboardExt;
#[cfg_attr(mobile, tauri::mobile_entry_point)] fn try_send_event(event: &EventType) {
pub fn run() { if let Err(SimulateError) = simulate(event) {
let recording_manager = println!("We could not send {:?}", event);
Arc::new(AudioRecordingManager::new().expect("Failed to initialize recording manager")); }
let transcription_manager = }
Arc::new(TranscriptionManager::new().expect("Failed to initialize transcription manager"));
// let transcription_manager = Arc::new(TranscriptionManager::new());
let manager = Arc::new(Mutex::new(KeyBindingManager::new(
recording_manager.clone(),
transcription_manager.clone(),
)));
// Register your key bindings fn send(event: EventType) {
{ try_send_event(&event);
let mut manager = manager.lock().unwrap(); thread::sleep(time::Duration::from_millis(20));
}
// Example: Register Ctrl+Shift combination fn send_multiple(events: &[EventType]) {
manager.register( events.iter().for_each(try_send_event);
"ctrl-meta".to_string(), thread::sleep(time::Duration::from_millis(20));
vec![Key::ControlRight, Key::MetaRight], }
|ctx| {
println!("Ctrl+Meta pressed!"); fn send_paste() {
ctx.recording_manager.try_start_recording("ctrl-meta"); // Determine the modifier key based on the OS
}, #[cfg(target_os = "macos")]
|ctx| { let modifier_key = Key::MetaLeft; // Command key on macOS
println!("Ctrl+Meta released!"); #[cfg(not(target_os = "macos"))]
let modifier_key = Key::ControlLeft; // Control key on other systems
// Press both keys
send(EventType::KeyPress(modifier_key));
send(EventType::KeyPress(Key::KeyV));
// Release both keys simultaneously
send_multiple(&[
EventType::KeyRelease(Key::KeyV),
EventType::KeyRelease(modifier_key),
]);
// Additional delay after the complete combination
thread::sleep(time::Duration::from_millis(20));
}
fn send_copy() {
// Determine the modifier key based on the OS
#[cfg(target_os = "macos")]
let modifier_key = Key::MetaLeft; // Command key on macOS
#[cfg(not(target_os = "macos"))]
let modifier_key = Key::ControlLeft; // Control key on other systems
// Press both keys
send(EventType::KeyPress(modifier_key));
send(EventType::KeyPress(Key::KeyC));
// Release both keys simultaneously
send_multiple(&[
EventType::KeyRelease(Key::KeyC),
EventType::KeyRelease(modifier_key),
]);
}
fn paste(text: String, app_handle: tauri::AppHandle) {
let clipboard = app_handle.clipboard();
// get the current clipboard content
let clipboard_content = clipboard.read_text().unwrap_or_default();
clipboard.write_text(&text).unwrap();
send_paste();
// restore the clipboard
clipboard.write_text(&clipboard_content).unwrap();
}
fn get_highlighted_text(app_handle: tauri::AppHandle) -> String {
let clipboard = app_handle.clipboard();
// save the clipboard content
let clipboard_content = clipboard.read_text().unwrap_or_default();
// empty the clipboard
clipboard.write_text("").unwrap();
// issue 'copy'
send_copy();
// get the highlighted text
let highlighted_text = clipboard.read_text().unwrap_or_default();
// restore the clipboard content
clipboard.write_text(&clipboard_content).unwrap();
highlighted_text
}
const INSTRUCT_SYS: &str = r#"
You are a helpful assistant. You will receive voice transcriptions
from a user that may include both a command/question and some
minimal context to help you respond appropriately.
For example, the user might say:
- A direct question with no context: "What is the capital of France?"
- A command with context: "get commit message I fixed the bug in the login system"
"#;
const CODE_SYS: &str = r#"
You are a code-only assistant. I will provide you with selected text or clipboard content along with instructions. If I request code, output only the exact code implementation. If I request a terminal command, provide only the valid command syntax. Never use markdown, explanations, or additional text.
When I share selected text or clipboard content, use that as context for generating your response. The output should be ready to copy and paste directly, with no formatting or commentary. For terminal commands, ensure they are valid for the specified environment. Note for terminal commands, I typically use lowercase instead of uppercase. You may also be given them directly, but need to translate them into a way that can actually be executed in the terminal because the transcription you are given might be poor.
Output only:
- Raw code implementation when code is requested
- Terminal command syntax when a command is requested
- No markdown, no backticks, no explanations
- No additional text or descriptions
"#;
fn register_bindings(manager: &mut KeyBindingManager) {
manager.register(
"ctrl-meta".to_string(),
vec![Key::ControlRight, Key::MetaRight],
|ctx| {
info!("Ctrl+Meta pressed!");
ctx.recording_manager.try_start_recording("ctrl-meta");
None
},
|ctx| {
info!("release being called from ctrl-meta");
let ctx = ctx.clone();
Some(tauri::async_runtime::spawn(async move {
if let Some(samples) = ctx.recording_manager.stop_recording("ctrl-meta") { if let Some(samples) = ctx.recording_manager.stop_recording("ctrl-meta") {
match ctx.transcription_manager.transcribe(samples) {
Ok(transcription) => {
println!("Transcription: {}", transcription);
paste(transcription, ctx.app_handle.clone());
}
Err(err) => println!("Transcription error: {}", err),
}
}
}))
},
);
// Register LLM Call after Transcription
manager.register(
"shift-alt".to_string(),
vec![Key::ShiftLeft, Key::Alt],
|ctx| {
info!("Shift+Alt pressed!");
ctx.recording_manager.try_start_recording("shift-alt");
None
},
|ctx| {
info!("release being called from shift-alt");
let ctx = ctx.clone();
Some(tauri::async_runtime::spawn(async move {
if let Some(samples) = ctx.recording_manager.stop_recording("shift-alt") {
if let Ok(transcription) = ctx.transcription_manager.transcribe(samples) {
println!("Transcription: {}", transcription);
let instruct = ctx
.anthropic
.agent(anthropic::CLAUDE_3_5_SONNET)
.preamble(INSTRUCT_SYS)
.temperature(0.5)
.build();
let highlighted_text = get_highlighted_text(ctx.app_handle.clone());
println!("Highlighted Text: {}", highlighted_text);
let prompt = format!("{}\n\ncontext:{}\n", transcription, highlighted_text);
match instruct.prompt(prompt).await {
Ok(response) => {
println!("Sonnet response: {}", response);
paste(response, ctx.app_handle.clone());
}
Err(err) => println!("Sonnet error: {}", err),
}
}
}
}))
},
);
manager.register(
"ctrl-alt-meta".to_string(),
vec![Key::ControlLeft, Key::Alt, Key::MetaLeft],
|ctx| {
info!("Ctrl+Alt+Meta pressed!");
ctx.recording_manager.try_start_recording("ctrl-alt-meta");
None
},
|ctx| {
info!("release being called from ctrl-alt-meta");
let ctx = ctx.clone();
Some(tauri::async_runtime::spawn(async move {
if let Some(samples) = ctx.recording_manager.stop_recording("ctrl-alt-meta") {
let samples: Vec<f32> = samples; // explicit type annotation let samples: Vec<f32> = samples; // explicit type annotation
match ctx.transcription_manager.transcribe(samples) { match ctx.transcription_manager.transcribe(samples) {
Ok(transcription) => println!("Transcription: {}", transcription), Ok(transcription) => {
println!("Transcription: {}", transcription);
let code = ctx
.anthropic
.agent(anthropic::CLAUDE_3_5_SONNET)
.preamble(CODE_SYS)
.temperature(0.5)
.build();
let highlighted_text = get_highlighted_text(ctx.app_handle.clone());
let prompt =
format!("{}\n\ncontext:{}\n", transcription, highlighted_text);
match code.prompt(prompt).await {
Ok(response) => {
println!("Sonnet response: {}", response);
paste(response, ctx.app_handle.clone());
}
Err(err) => println!("Sonnet error: {}", err),
}
}
Err(err) => println!("Transcription error: {}", err), Err(err) => println!("Transcription error: {}", err),
} }
} else { } else {
println!("No samples recorded"); println!("No samples recorded");
} }
}, }))
); },
} );
}
tauri::async_runtime::spawn({ #[cfg_attr(mobile, tauri::mobile_entry_point)]
let manager = manager.clone(); pub fn run() {
async move { env_logger::init();
rdev::listen(move |event| {
if let Ok(manager) = manager.lock() { let recording_manager =
manager.handle_event(&event); Arc::new(AudioRecordingManager::new().expect("Failed to initialize recording manager"));
} let transcription_manager =
}) Arc::new(TranscriptionManager::new().expect("Failed to initialize transcription manager"));
.unwrap(); // let transcription_manager = Arc::new(TranscriptionManager::new());
} let claude_client = Arc::new(anthropic::Client::from_env());
});
tauri::Builder::default() tauri::Builder::default()
.plugin(tauri_plugin_global_shortcut::Builder::new().build()) .plugin(tauri_plugin_global_shortcut::Builder::new().build())
@ -67,6 +257,32 @@ pub fn run() {
)) ))
.plugin(tauri_plugin_macos_permissions::init()) .plugin(tauri_plugin_macos_permissions::init())
.plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_opener::init())
.setup(move |app| {
let app_handle = app.handle().clone();
let manager = Arc::new(Mutex::new(KeyBindingManager::new(
recording_manager.clone(),
transcription_manager.clone(),
claude_client.clone(),
app_handle.clone(),
)));
{
let mut manager = manager.lock().unwrap();
register_bindings(&mut manager);
}
let manager_clone = manager.clone();
tauri::async_runtime::spawn(async move {
rdev::listen(move |event| {
if let Ok(manager) = manager_clone.lock() {
manager.handle_event(&event);
}
})
.unwrap();
});
Ok(())
})
.invoke_handler(tauri::generate_handler![]) .invoke_handler(tauri::generate_handler![])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

View file

@ -133,6 +133,7 @@ impl AudioRecordingManager {
pub fn stop_recording(&self, binding_id: &str) -> Option<Vec<f32>> { pub fn stop_recording(&self, binding_id: &str) -> Option<Vec<f32>> {
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
println!("Stop recording called from binding {}", binding_id);
match *state { match *state {
RecordingState::Recording { RecordingState::Recording {
binding_id: ref active_id, binding_id: ref active_id,
@ -141,10 +142,29 @@ impl AudioRecordingManager {
println!("Stopped recording for binding {}", binding_id); println!("Stopped recording for binding {}", binding_id);
let mut buffer = self.buffer.lock().unwrap(); let mut buffer = self.buffer.lock().unwrap();
Some(buffer.drain(..).collect()) let audio_data: Vec<f32> = buffer.drain(..).collect();
// Calculate duration in milliseconds
// 16000 is our target sample rate after resampling
let duration_ms = (audio_data.len() as f32 / 16000.0) * 1000.0;
if duration_ms < 300.0 {
// Discard the audio if it's too short
Some(Vec::new())
} else {
// Pad to minimum 1000ms if needed
if duration_ms < 1000.0 {
let target_samples = (16000.0 * (1000.0 / 1000.0)) as usize; // 16000 samples for 1 second
let mut padded_audio = audio_data;
padded_audio.resize(target_samples, 0.0); // Pad with silence (zeros)
Some(padded_audio)
} else {
Some(audio_data)
}
}
} }
_ => { _ => {
println!("Cannot stop recording: not recording or wrong binding"); // println!("Cannot stop recording: not recording or wrong binding");
None None
} }
} }

View file

@ -1,35 +1,38 @@
// First, let's define some types for our audio recording functionality
use super::audio::AudioRecordingManager; use super::audio::AudioRecordingManager;
use super::transcription::TranscriptionManager; use super::transcription::TranscriptionManager;
use rdev::EventType; use rdev::EventType;
use rig::agent::Agent;
use rig::providers::anthropic;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
type KeySet = HashSet<rdev::Key>; type KeySet = HashSet<rdev::Key>;
// Now let's modify the KeyBinding structure to work with our new system
#[derive(Clone)] #[derive(Clone)]
pub struct BindingContext { pub struct BindingContext {
pub recording_manager: Arc<AudioRecordingManager>, pub recording_manager: Arc<AudioRecordingManager>,
pub transcription_manager: Arc<TranscriptionManager>, pub transcription_manager: Arc<TranscriptionManager>,
// Add other managers here as needed pub anthropic: Arc<anthropic::Client>,
// pub transcription_manager: Arc<TranscriptionManager>, pub app_handle: tauri::AppHandle,
// pub llm_manager: Arc<LLMManager>,
} }
pub struct KeyBinding { pub struct KeyBinding {
id: String, id: String,
keys: KeySet, keys: KeySet,
on_press: Box<dyn Fn(&BindingContext) + Send + 'static>, on_press: Box<
on_release: Box<dyn Fn(&BindingContext) + Send + 'static>, dyn Fn(&BindingContext) -> Option<tauri::async_runtime::JoinHandle<()>> + Send + 'static,
>,
on_release: Box<
dyn Fn(&BindingContext) -> Option<tauri::async_runtime::JoinHandle<()>> + Send + 'static,
>,
currently_pressed: Arc<Mutex<KeySet>>, currently_pressed: Arc<Mutex<KeySet>>,
} }
impl KeyBinding { impl KeyBinding {
fn new<F, G>(id: String, keys: Vec<rdev::Key>, on_press: F, on_release: G) -> Self fn new<F, G>(id: String, keys: Vec<rdev::Key>, on_press: F, on_release: G) -> Self
where where
F: Fn(&BindingContext) + Send + 'static, F: Fn(&BindingContext) -> Option<tauri::async_runtime::JoinHandle<()>> + Send + 'static,
G: Fn(&BindingContext) + Send + 'static, G: Fn(&BindingContext) -> Option<tauri::async_runtime::JoinHandle<()>> + Send + 'static,
{ {
Self { Self {
id, id,
@ -41,22 +44,39 @@ impl KeyBinding {
} }
fn handle_event(&self, key: rdev::Key, is_press: bool, context: &BindingContext) -> bool { fn handle_event(&self, key: rdev::Key, is_press: bool, context: &BindingContext) -> bool {
if !self.keys.contains(&key) {
return false;
}
let mut pressed = self.currently_pressed.lock().unwrap(); let mut pressed = self.currently_pressed.lock().unwrap();
if is_press { if is_press {
if !self.keys.contains(&key) {
return false; // Ignore keys that aren't part of this binding
}
if pressed.contains(&key) {
return false;
}
pressed.insert(key); pressed.insert(key);
if pressed.len() == self.keys.len() && pressed.is_subset(&self.keys) { if pressed.len() == self.keys.len() && pressed.is_subset(&self.keys) {
(self.on_press)(context); let _ = (self.on_press)(context);
return true; return true;
} }
} else { } else {
pressed.remove(&key); // Release event
if pressed.len() == self.keys.len() - 1 { if !self.keys.contains(&key) {
(self.on_release)(context); return false;
return true; }
if pressed.remove(&key) {
if pressed.len() == self.keys.len() - 1 {
let _ = (self.on_release)(context);
// Clear the currently_pressed set if all keys are released
if pressed.is_empty() {
pressed.clear();
}
return true;
}
} }
} }
false false
@ -72,20 +92,24 @@ impl KeyBindingManager {
pub fn new( pub fn new(
recording_manager: Arc<AudioRecordingManager>, recording_manager: Arc<AudioRecordingManager>,
transcription_manager: Arc<TranscriptionManager>, transcription_manager: Arc<TranscriptionManager>,
anthropic: Arc<anthropic::Client>,
app_handle: tauri::AppHandle,
) -> Self { ) -> Self {
Self { Self {
bindings: Vec::new(), bindings: Vec::new(),
context: BindingContext { context: BindingContext {
recording_manager, recording_manager,
transcription_manager, transcription_manager,
anthropic,
app_handle,
}, },
} }
} }
pub fn register<F, G>(&mut self, id: String, keys: Vec<rdev::Key>, on_press: F, on_release: G) pub fn register<F, G>(&mut self, id: String, keys: Vec<rdev::Key>, on_press: F, on_release: G)
where where
F: Fn(&BindingContext) + Send + 'static, F: Fn(&BindingContext) -> Option<tauri::async_runtime::JoinHandle<()>> + Send + 'static,
G: Fn(&BindingContext) + Send + 'static, G: Fn(&BindingContext) -> Option<tauri::async_runtime::JoinHandle<()>> + Send + 'static,
{ {
self.bindings self.bindings
.push(KeyBinding::new(id, keys, on_press, on_release)); .push(KeyBinding::new(id, keys, on_press, on_release));
@ -94,8 +118,12 @@ impl KeyBindingManager {
pub fn handle_event(&self, event: &rdev::Event) { pub fn handle_event(&self, event: &rdev::Event) {
if let EventType::KeyPress(key) | EventType::KeyRelease(key) = event.event_type { if let EventType::KeyPress(key) | EventType::KeyRelease(key) = event.event_type {
let is_press = matches!(event.event_type, EventType::KeyPress(_)); let is_press = matches!(event.event_type, EventType::KeyPress(_));
// Only process the first binding that successfully handles the event
for binding in &self.bindings { for binding in &self.bindings {
binding.handle_event(key, is_press, &self.context); if binding.handle_event(key, is_press, &self.context) {
break; // Exit after first successful handling
}
} }
} }
} }