From b2a9957dc582e1b9ce4a311c785d3a7c3dc708b4 Mon Sep 17 00:00:00 2001 From: uliboooo Date: Tue, 31 Mar 2026 20:15:52 +0900 Subject: [PATCH] support gemini3 as fixing this issue --- Cargo.lock | 461 ++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 + src/llm.rs | 136 ++++++++++-- src/main.rs | 6 +- tools/api_test/test.sh | 26 +++ 5 files changed, 601 insertions(+), 31 deletions(-) create mode 100755 tools/api_test/test.sh diff --git a/Cargo.lock b/Cargo.lock index fc71f83..71ab18c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,6 +123,28 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "base64" version = "0.22.1" @@ -159,12 +181,24 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.44" @@ -218,12 +252,31 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "console" version = "0.16.3" @@ -284,6 +337,12 @@ dependencies = [ "syn", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -381,6 +440,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures-channel" version = "0.3.32" @@ -427,8 +492,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -438,9 +505,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 5.3.0", "wasip2", + "wasm-bindgen", ] [[package]] @@ -470,7 +539,9 @@ dependencies = [ "indicatif", "llm-api-rs", "ollama-rs", + "reqwest 0.13.2", "serde", + "serde_json", "tokio", "unicode-width", "url", @@ -852,6 +923,50 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -943,7 +1058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2caadd5d8b4be5d532af35f898161b86412d3e3fb50eeff97d30650af18710f2" dependencies = [ "async-trait", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", "thiserror 1.0.69", @@ -965,6 +1080,12 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "memchr" version = "2.8.0" @@ -1022,7 +1143,7 @@ checksum = "f647d8676b95a6b6205e11453c9fac338d73c9cdcc011c94d1ba9c9bfea582cd" dependencies = [ "async-stream", "log", - "reqwest", + "reqwest 0.12.28", "schemars", "serde", "serde_json", @@ -1155,6 +1276,15 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -1174,6 +1304,62 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.45" @@ -1195,6 +1381,35 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -1264,6 +1479,46 @@ dependencies = [ "web-sys", ] +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "ring" version = "0.17.14" @@ -1278,6 +1533,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + [[package]] name = "rustix" version = "1.1.4" @@ -1297,6 +1558,7 @@ version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ + "aws-lc-rs", "once_cell", "rustls-pki-types", "rustls-webpki", @@ -1304,21 +1566,62 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe 0.2.1", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ + "web-time", "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -1336,6 +1639,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.29" @@ -1658,6 +1970,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.49.0" @@ -1888,6 +2215,16 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2034,6 +2371,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2050,6 +2396,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2126,13 +2481,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2144,34 +2508,67 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2184,24 +2581,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -2337,6 +2758,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zerofrom" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index 7f096bd..8dec82c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ ollama-rs = "0.3.*" tokio = { version = "1.49.*", features = ["rt", "rt-multi-thread"] } llm-api-rs = "0.1.*" serde = { version = "1.0.*", features = ["derive"] } +serde_json = "1.0.149" derive-getters = "0.5.*" chrono = "0.4.*" home = "0.5.*" @@ -34,3 +35,5 @@ indicatif = "0.18.*" unicode-width = "0.2.1" url = "2.5.7" atty = "0.2.14" +reqwest = { version = "0.13.2", features = ["json"] } + diff --git a/src/llm.rs b/src/llm.rs index 0a299ed..9f432f2 100644 --- a/src/llm.rs +++ b/src/llm.rs @@ -2,11 +2,13 @@ use crate::cli_helper::Spinner; use crate::config; use derive_getters::Getters; use llm_api_rs::{ - self, LlmProvider, + LlmProvider, core::{ChatCompletionRequest, ChatMessage}, - providers::{Anthropic, DeepSeek, Gemini, OpenAI}, + providers::{Anthropic, DeepSeek, OpenAI}, }; use ollama_rs::{Ollama, generation::completion::request::GenerationRequest}; +use reqwest::header::CONTENT_TYPE; +use serde_json::json; use std::fmt::Display; #[derive(Debug)] @@ -18,6 +20,8 @@ pub enum Error { // CliHelper(String), OllamaE(ollama_rs::error::OllamaError), Conf(config::Error), + InvalidResponse, + ApiErr(reqwest::Error), } impl Display for Error { @@ -27,20 +31,14 @@ impl Display for Error { Error::FailedGetAPIKey => write!(f, "failed to get api key"), Error::FailedGetBaseURL => write!(f, "failed to get base url"), Error::ChatCompletion(e) => write!(f, "chat completion error: {}", e), - // Error::CliHelper(e) => write!(f, "cli helper error: {}", e), Error::OllamaE(e) => write!(f, "ollama error: {}", e), - Error::Conf(error) => write!(f, "config error {error}"), + Error::Conf(e) => write!(f, "config error {e}"), + Error::InvalidResponse => write!(f, "invalid response"), + Error::ApiErr(e) => write!(f, "api error: {e}"), } } } -// impl From for Error { -// fn from(e: cli_helper::Error) -> Self { -// match e { -// cli_helper::Error::Io(io_err) => Error::CliHelper(io_err.to_string()), -// } -// } -// } impl From for Error { fn from(value: ollama_rs::error::OllamaError) -> Self { Self::OllamaE(value) @@ -140,11 +138,87 @@ impl LlmReqInfo { } } +fn make_gemini_body(model: String, prompt: String) -> serde_json::Value { + json!({ + "model": model, + "contents": [{ + "parts": [ + { + "text": prompt + } + ] + }] + }) +} + +// { +// "candidates": [ +// { +// "content": { +// "parts": [ +// { +// "text": "text", +// "thoughtSignature": "" +// } +// ], +// "role": "model" +// }, +// "finishReason": "STOP", +// "index": 0 +// } +// ], +// "usageMetadata": { +// "promptTokenCount": 11853, +// "candidatesTokenCount": 415, +// "totalTokenCount": 12986, +// "promptTokensDetails": [ +// { +// "modality": "TEXT", +// "tokenCount": 11853 +// } +// ], +// "thoughtsTokenCount": 718 +// }, +// "modelVersion": "gemini-3-flash-preview", +// "responseId": "96TLaaPKOKnY2roPx9DxCQ" +// } + +use serde::{Deserialize, Serialize}; + +// Discard any content other than text. +#[derive(Debug, Serialize, Deserialize)] +pub struct GeminiResp { + pub candidates: Vec, +} + +impl GeminiResp { + fn get_resp_text(&self) -> Option { + self.candidates + .first() + .and_then(|f| f.content.parts.first().map(|f| f.text.to_string())) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Candidate { + pub content: Content, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Content { + pub parts: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Part { + pub text: String, +} + pub async fn call_llm>(llm_info: LlmReqInfo, prompt: T) -> Result { let spinner = Spinner::new("Calling LLM..."); - let result = async { - // llm_api_rs isn't support ollama - if llm_info.provider() == &Provider::Ollama { + let api = llm_info.resolve_api_key()?; + let res = match llm_info.provider { + Provider::Ollama => { let base_url = llm_info.clone().base_url.ok_or(Error::FailedGetBaseURL)?; let o_res = Ollama::new(base_url.0, base_url.1); let res = o_res @@ -157,11 +231,35 @@ pub async fn call_llm>(llm_info: LlmReqInfo, prompt: T) -> Result< Ok(v) => Ok(v.response.to_string()), Err(e) => Err(Error::OllamaE(e)), } - } else { - let api = llm_info.resolve_api_key()?; + } + Provider::Gemini => { + let client = reqwest::Client::new(); + + // let url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent".to_string(); + let url = format!( + "https://generativelanguage.googleapis.com/v1beta/models/{}:generateContent", + llm_info.model + ); + let body = make_gemini_body(llm_info.model, prompt.as_ref().to_string()); + + let resp = client + .post(url) + .header("x-goog-api-key", api) + .header(CONTENT_TYPE, "application/json") + .json(&body) + .send() + .await + .map_err(Error::ApiErr)? + // .unwrap() + .json::() + .await + .map_err(Error::ApiErr)?; + + resp.get_resp_text().ok_or(Error::InvalidResponse) + } + _ => { let client: Box = match llm_info.provider { Provider::OpenAI => Box::new(OpenAI::new(api)), - Provider::Gemini => Box::new(Gemini::new(api)), Provider::Anthropic => Box::new(Anthropic::new(api)), Provider::DeepSeek => Box::new(DeepSeek::new(api)), _ => return Err(Error::NotSuppoeredProvider), @@ -188,8 +286,8 @@ pub async fn call_llm>(llm_info: LlmReqInfo, prompt: T) -> Result< Err(e) => Err(Error::ChatCompletion(e.to_string())), } } - } - .await; + }; + let result = async { res }.await; spinner.stop("LLM call finished."); result } diff --git a/src/main.rs b/src/main.rs index be6c72f..ed1a58a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -227,7 +227,8 @@ fn resolve_model( None => Err(Error::NotFoundConfig( "A config file is required to resolve model aliases. \ Please create ~/.config/ggw/config.toml, or pass the model as \ - 'provider/model' (e.g. `-m gemini/gemini-2.0-flash`).".to_string() + 'provider/model' (e.g. `-m gemini/gemini-2.0-flash`)." + .to_string(), )), }? .get_model(v) @@ -239,7 +240,8 @@ fn resolve_model( None => Err(Error::NotFoundConfig( "No config file found and no model specified. \ Please create ~/.config/ggw/config.toml with a default_model, \ - or specify a model with `-m provider/model` (e.g. `-m gemini/gemini-2.0-flash`).".to_string() + or specify a model with `-m provider/model` (e.g. `-m gemini/gemini-2.0-flash`)." + .to_string(), )), }? .get_default() diff --git a/tools/api_test/test.sh b/tools/api_test/test.sh new file mode 100755 index 0000000..19786ae --- /dev/null +++ b/tools/api_test/test.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +call() { + model_name="$1" + url="https://generativelanguage.googleapis.com/v1beta/models/${model_name}:generateContent" + echo "$url" + + curl "${url}" \ + -H "x-goog-api-key: $GEMINI_API_KEY" \ + -H 'Content-Type: application/json' \ + -X POST \ + -d '{ + "contents": [ + { + "parts": [ + { + "text": "Explain how AI works in a few words" + } + ] + } + ] + }' +} + +# call "gemini-3-flash-preview" +call "gemini-2.5-flash"