mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
langtool: Add Claude as an alternative to OpenAI for AI translation
New claude.rs talks to the Anthropic Messages API, and ai.rs picks between the two providers. The provider is chosen with --provider, or guessed from the model name, or from whichever of ANTHROPIC_API_KEY/OPENAI_API_KEY is set. Also update the dependencies (and bump the clap requirement to 4.6). Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_0164UKoXpz9r155TRfQsoc3H
This commit is contained in:
1 parent
a67ae4e7f6
commit
77e356961d
7 files changed
+332
-105
No files matched your search
Generated
+94
-93
@@ -4,9 +4,9 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -81,9 +81,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-rs"
|
||||
version = "1.17.3"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1"
|
||||
checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e"
|
||||
dependencies = [
|
||||
"aws-lc-sys",
|
||||
"zeroize",
|
||||
@@ -91,9 +91,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-sys"
|
||||
version = "0.43.0"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c"
|
||||
checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cmake",
|
||||
@@ -140,9 +140,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.4.0"
|
||||
version = "1.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
|
||||
checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@@ -175,9 +175,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.5"
|
||||
version = "4.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301b56658598e48f3648647ac6fc887be7e7108eddfa4e9b63fcf3ec58c0cadf"
|
||||
checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -185,9 +185,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.6.5"
|
||||
version = "4.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94a65403d1a1bd28f7dc68eb8506e8874808ee5eecb59298de588e2e1407a078"
|
||||
checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -375,9 +375,9 @@ checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
@@ -414,9 +414,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.33"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
|
||||
checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -424,33 +424,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.33"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
|
||||
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.33"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
|
||||
checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.33"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
|
||||
checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.33"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
|
||||
checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.33"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
|
||||
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
@@ -500,9 +500,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.15"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155"
|
||||
checksum = "839c0e8a181239723652be9062bb56ca5bf5f64011f73b623f6f4fc59086a228"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -560,9 +560,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
|
||||
checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -640,9 +640,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
|
||||
checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
@@ -654,9 +654,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
|
||||
checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
@@ -667,9 +667,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
|
||||
checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
@@ -681,16 +681,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
|
||||
checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
|
||||
checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_properties_data",
|
||||
@@ -701,15 +702,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
|
||||
checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.2.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
|
||||
checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
@@ -781,7 +782,7 @@ dependencies = [
|
||||
"jni-sys",
|
||||
"log",
|
||||
"simd_cesu8",
|
||||
"thiserror 2.0.19",
|
||||
"thiserror 2.0.20",
|
||||
"walkdir",
|
||||
"windows-link",
|
||||
]
|
||||
@@ -830,9 +831,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.103"
|
||||
version = "0.3.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
|
||||
checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
@@ -866,15 +867,15 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
|
||||
checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.33"
|
||||
version = "0.4.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
@@ -1026,15 +1027,15 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.33"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
||||
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
|
||||
checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
@@ -1062,7 +1063,7 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror 2.0.19",
|
||||
"thiserror 2.0.20",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"web-time",
|
||||
@@ -1070,9 +1071,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.16"
|
||||
version = "0.11.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560"
|
||||
checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"bytes",
|
||||
@@ -1085,7 +1086,7 @@ dependencies = [
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.19",
|
||||
"thiserror 2.0.20",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
@@ -1160,9 +1161,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.16"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
|
||||
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1324,9 +1325,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.13"
|
||||
version = "0.103.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
|
||||
checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"ring",
|
||||
@@ -1596,11 +1597,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.19"
|
||||
version = "2.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
|
||||
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.19",
|
||||
"thiserror-impl 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1616,9 +1617,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.19"
|
||||
version = "2.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
|
||||
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1627,9 +1628,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
|
||||
checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
@@ -1832,9 +1833,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.126"
|
||||
version = "0.2.127"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
|
||||
checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -1845,9 +1846,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.76"
|
||||
version = "0.4.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
|
||||
checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -1855,9 +1856,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.126"
|
||||
version = "0.2.127"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
|
||||
checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1865,9 +1866,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.126"
|
||||
version = "0.2.127"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
|
||||
checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
@@ -1878,9 +1879,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.126"
|
||||
version = "0.2.127"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
|
||||
checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1946,9 +1947,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.103"
|
||||
version = "0.3.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
|
||||
checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -2150,9 +2151,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
|
||||
checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
|
||||
|
||||
[[package]]
|
||||
name = "x11-clipboard"
|
||||
@@ -2187,9 +2188,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.28"
|
||||
version = "0.8.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
|
||||
checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
@@ -2243,9 +2244,9 @@ checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
|
||||
checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
@@ -2254,9 +2255,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.6"
|
||||
version = "0.11.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
|
||||
checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
@@ -2265,13 +2266,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.3"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
|
||||
checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -5,7 +5,7 @@ name = "langtool"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
clap = { version = "4.6", features = ["derive"] }
|
||||
reqwest = { version = "0.13", features = ["blocking", "json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
@@ -16,6 +16,25 @@ To see command line usage, type:
|
||||
cargo run -- --help
|
||||
```
|
||||
|
||||
## AI translation
|
||||
|
||||
Some commands (`add-new-key-ai`, `add-new-key-value-ai`, `finish-language-with-ai`) use an LLM to
|
||||
generate translations. Two providers are supported, pick one by setting its API key in the
|
||||
environment:
|
||||
|
||||
* Claude (Anthropic): `ANTHROPIC_API_KEY`
|
||||
* OpenAI: `OPENAI_API_KEY`
|
||||
|
||||
If both are set, Claude is used unless you say otherwise with `--provider openai`. To pick a
|
||||
specific model, use `--model`, for example:
|
||||
|
||||
```bash
|
||||
cargo run -- --model claude-sonnet-5 finish-language-with-ai sv_SE
|
||||
cargo run -- --model gpt-4o finish-language-with-ai sv_SE
|
||||
```
|
||||
|
||||
The provider is inferred from the model name, so `--provider` is only needed to disambiguate.
|
||||
|
||||
To autoformat the code, use:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
use clap::ValueEnum;
|
||||
use std::error::Error;
|
||||
|
||||
use crate::chatgpt::ChatGPT;
|
||||
use crate::claude::Claude;
|
||||
|
||||
// gpt-5, gpt-5-mini, gpt-5-nano, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, o3, o4-mini, gpt-4o
|
||||
const DEFAULT_OPENAI_MODEL: &str = "gpt-4o-mini";
|
||||
// claude-opus-5, claude-sonnet-5, claude-haiku-4-5
|
||||
const DEFAULT_CLAUDE_MODEL: &str = "claude-opus-5";
|
||||
|
||||
const OPENAI_KEY_VAR: &str = "OPENAI_API_KEY";
|
||||
const CLAUDE_KEY_VAR: &str = "ANTHROPIC_API_KEY";
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
|
||||
pub enum Provider {
|
||||
/// OpenAI, needs OPENAI_API_KEY in the environment.
|
||||
#[value(name = "openai")]
|
||||
OpenAI,
|
||||
/// Anthropic, needs ANTHROPIC_API_KEY in the environment.
|
||||
Claude,
|
||||
}
|
||||
|
||||
pub enum Ai {
|
||||
OpenAI(ChatGPT),
|
||||
Claude(Claude),
|
||||
}
|
||||
|
||||
impl Ai {
|
||||
/// Picks a provider and creates a client for it, if we have an API key for it.
|
||||
///
|
||||
/// If no provider was specified on the command line, we guess from the model name if we
|
||||
/// have one, otherwise we just use whichever API key is available (Claude wins if both are).
|
||||
pub fn create(provider: Option<Provider>, model: Option<String>) -> Option<Ai> {
|
||||
let openai_key = std::env::var(OPENAI_KEY_VAR).ok();
|
||||
let claude_key = std::env::var(CLAUDE_KEY_VAR).ok();
|
||||
|
||||
let provider = match provider {
|
||||
Some(provider) => provider,
|
||||
None => match model.as_deref() {
|
||||
Some(model) if model.starts_with("claude") => Provider::Claude,
|
||||
Some(model) if model.starts_with("gpt") || model.starts_with('o') => {
|
||||
Provider::OpenAI
|
||||
}
|
||||
_ => {
|
||||
if claude_key.is_some() {
|
||||
Provider::Claude
|
||||
} else {
|
||||
Provider::OpenAI
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// If we don't have a key for the chosen provider, we return None here and the AI
|
||||
// commands will report it and skip themselves.
|
||||
match provider {
|
||||
Provider::OpenAI => openai_key.map(|key| {
|
||||
let model = model.unwrap_or_else(|| DEFAULT_OPENAI_MODEL.to_string());
|
||||
Ai::OpenAI(ChatGPT::new(key, model))
|
||||
}),
|
||||
Provider::Claude => claude_key.map(|key| {
|
||||
let model = model.unwrap_or_else(|| DEFAULT_CLAUDE_MODEL.to_string());
|
||||
Ai::Claude(Claude::new(key, model))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat(&self, request: &str) -> Result<String, Box<dyn Error>> {
|
||||
match self {
|
||||
Ai::OpenAI(chatgpt) => chatgpt.chat(request),
|
||||
Ai::Claude(claude) => claude.chat(request),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn description(&self) -> String {
|
||||
match self {
|
||||
Ai::OpenAI(chatgpt) => format!("OpenAI ({})", chatgpt.model()),
|
||||
Ai::Claude(claude) => format!("Claude ({})", claude.model()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,10 @@ impl ChatGPT {
|
||||
ChatGPT { api_key, model }
|
||||
}
|
||||
|
||||
pub fn model(&self) -> &str {
|
||||
&self.model
|
||||
}
|
||||
|
||||
pub fn chat(&self, request: &str) -> Result<String, Box<dyn Error>> {
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.timeout(Duration::from_secs(300))
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
use anyhow::{Context, anyhow};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{error::Error, time::Duration};
|
||||
|
||||
// The Anthropic API version header. Not the model version, this basically never changes.
|
||||
const API_VERSION: &str = "2023-06-01";
|
||||
|
||||
// Plenty for a section's worth of translated strings. If we ever hit it, we get a
|
||||
// "max_tokens" stop reason and complain loudly instead of silently truncating.
|
||||
const MAX_TOKENS: u32 = 16000;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MessagesRequest<'a> {
|
||||
pub model: &'a str,
|
||||
pub max_tokens: u32,
|
||||
pub messages: Vec<Message<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Message<'a> {
|
||||
pub role: &'a str,
|
||||
pub content: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MessagesResponse {
|
||||
pub content: Vec<ContentBlock>,
|
||||
pub stop_reason: Option<String>,
|
||||
}
|
||||
|
||||
// Content comes back as a list of blocks. We only care about the text ones, but there can
|
||||
// be others (thinking blocks, tool use), so we can't just assume block 0 is text.
|
||||
#[derive(Deserialize)]
|
||||
pub struct ContentBlock {
|
||||
#[serde(rename = "type")]
|
||||
pub block_type: String,
|
||||
#[serde(default)]
|
||||
pub text: Option<String>,
|
||||
}
|
||||
|
||||
pub struct Claude {
|
||||
api_key: String,
|
||||
model: String,
|
||||
}
|
||||
|
||||
impl Claude {
|
||||
pub fn new(api_key: String, model: String) -> Self {
|
||||
Claude { api_key, model }
|
||||
}
|
||||
|
||||
pub fn model(&self) -> &str {
|
||||
&self.model
|
||||
}
|
||||
|
||||
pub fn chat(&self, request: &str) -> Result<String, Box<dyn Error>> {
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.timeout(Duration::from_secs(300))
|
||||
.build()?;
|
||||
|
||||
let req_body = MessagesRequest {
|
||||
model: &self.model,
|
||||
max_tokens: MAX_TOKENS,
|
||||
messages: vec![Message {
|
||||
role: "user",
|
||||
content: request,
|
||||
}],
|
||||
};
|
||||
|
||||
let res = client
|
||||
.post("https://api.anthropic.com/v1/messages")
|
||||
.header("x-api-key", &self.api_key)
|
||||
.header("anthropic-version", API_VERSION)
|
||||
.json(&req_body)
|
||||
.send()
|
||||
.context("response")?;
|
||||
|
||||
// Errors come back as a JSON body with a useful message in it, so print it as-is
|
||||
// rather than just the status code.
|
||||
let status = res.status();
|
||||
if !status.is_success() {
|
||||
let body = res.text().unwrap_or_default();
|
||||
return Err(anyhow!("Claude API error ({status}): {body}").into());
|
||||
}
|
||||
|
||||
let res: MessagesResponse = res.json().context("json")?;
|
||||
|
||||
match res.stop_reason.as_deref() {
|
||||
Some("max_tokens") => {
|
||||
return Err(
|
||||
anyhow!("Response hit the {MAX_TOKENS} token limit, it got cut off").into(),
|
||||
);
|
||||
}
|
||||
Some("refusal") => {
|
||||
return Err(anyhow!("Claude declined to answer this request").into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let text = res
|
||||
.content
|
||||
.iter()
|
||||
.filter(|block| block.block_type == "text")
|
||||
.filter_map(|block| block.text.as_deref())
|
||||
.collect::<Vec<_>>()
|
||||
.join("");
|
||||
|
||||
if text.is_empty() {
|
||||
return Err(anyhow!("No text in the response from Claude").into());
|
||||
}
|
||||
|
||||
Ok(text)
|
||||
}
|
||||
}
|
||||
+19
-11
@@ -8,12 +8,18 @@ use section::{Section, line_value};
|
||||
mod inifile;
|
||||
use inifile::IniFile;
|
||||
|
||||
mod ai;
|
||||
mod chatgpt;
|
||||
mod claude;
|
||||
use clap::Parser;
|
||||
|
||||
mod util;
|
||||
|
||||
use crate::{chatgpt::ChatGPT, section::split_line, util::ask_letter};
|
||||
use crate::{
|
||||
ai::{Ai, Provider},
|
||||
section::split_line,
|
||||
util::ask_letter,
|
||||
};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Args {
|
||||
@@ -23,9 +29,12 @@ struct Args {
|
||||
dry_run: bool,
|
||||
#[arg(short, long)]
|
||||
verbose: bool,
|
||||
// gpt-5, gpt-5-mini, gpt-5-nano, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, o3, o4-mini, gpt-4o, gpt-4o-realtime-preview
|
||||
#[arg(short, long, default_value = "gpt-4o-mini")]
|
||||
model: String,
|
||||
/// Which AI service to use for translation. Defaults to whichever one we have an API key for.
|
||||
#[arg(short, long, value_enum)]
|
||||
provider: Option<Provider>,
|
||||
/// Model to use, see ai.rs for examples. Defaults to a reasonable one for the provider.
|
||||
#[arg(short, long)]
|
||||
model: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
@@ -391,10 +400,10 @@ fn finish_language_with_ai(
|
||||
target_ini: &mut IniFile,
|
||||
ref_ini: &IniFile,
|
||||
section: Option<&str>,
|
||||
ai: &ChatGPT,
|
||||
ai: &Ai,
|
||||
dry_run: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
println!("Finishing language with AI");
|
||||
println!("Finishing language with {}", ai.description());
|
||||
println!(
|
||||
"Step 1: Compare all strings in the section with the matching strings from the reference."
|
||||
);
|
||||
@@ -653,15 +662,14 @@ fn parse_response(response: &str) -> Option<BTreeMap<String, String>> {
|
||||
fn main() {
|
||||
let opt = Args::parse();
|
||||
|
||||
let api_key = std::env::var("OPENAI_API_KEY").ok();
|
||||
let ai = api_key.map(|key| chatgpt::ChatGPT::new(key, opt.model));
|
||||
let ai = Ai::create(opt.provider, opt.model);
|
||||
|
||||
// TODO: Grab extra arguments from opt somehow.
|
||||
// let args: Vec<String> = vec![]; //std::env::args().skip(1).collect();
|
||||
execute_command(opt.cmd, ai.as_ref(), opt.dry_run, opt.verbose);
|
||||
}
|
||||
|
||||
fn execute_command(cmd: Command, ai: Option<&ChatGPT>, dry_run: bool, verbose: bool) {
|
||||
fn execute_command(cmd: Command, ai: Option<&Ai>, dry_run: bool, verbose: bool) {
|
||||
let root = "../../assets/lang";
|
||||
let reference_ini_filename = "en_US.ini";
|
||||
|
||||
@@ -1002,7 +1010,7 @@ fn execute_command(cmd: Command, ai: Option<&ChatGPT>, dry_run: bool, verbose: b
|
||||
}
|
||||
|
||||
fn generate_ai_response(
|
||||
ai: Option<&ChatGPT>,
|
||||
ai: Option<&Ai>,
|
||||
filenames: &[String],
|
||||
section: &str,
|
||||
key: &str,
|
||||
@@ -1016,7 +1024,7 @@ fn generate_ai_response(
|
||||
);
|
||||
println!("generated prompt:\n{prompt}");
|
||||
Some(if let Some(ai) = &ai {
|
||||
println!("Using AI for translation...");
|
||||
println!("Using {} for translation...", ai.description());
|
||||
let response = ai
|
||||
.chat(&prompt)
|
||||
.map_err(|e| anyhow::anyhow!("chat failed: {e}"))
|
||||
|
||||
Reference in new issue
Block a user