From 0e65370ca23c542536123ef6d4eaf042b2392dac Mon Sep 17 00:00:00 2001 From: wisdgod Date: Mon, 24 Feb 2025 08:50:37 +0800 Subject: [PATCH] v0.1.3-rc.5 --- .env.example | 15 +- .gitignore | 2 +- Cargo.lock | 309 +- Cargo.toml | 71 +- Dockerfile | 10 +- Dockerfile.cross | 2 +- Dockerfile.cross.arm64 | 2 +- README.md | 102 +- build.rs | 21 +- src/app.rs | 1 + src/app/config.rs | 14 +- src/app/constant.rs | 175 +- src/app/lazy.rs | 48 +- src/app/model.rs | 511 ++-- src/app/model/build_key.rs | 9 +- src/app/model/config.rs | 286 +- src/app/model/proxies.rs | 2 +- src/app/model/usage_check.rs | 22 +- src/app/model/vision_ability.rs | 32 + src/chat/adapter.rs | 152 +- src/chat/aiserver/v1.rs | 8 +- src/chat/aiserver/v1/aiserver.v1.rs | 4149 ++++++++++++++++++++++++++ src/chat/aiserver/v1/lite.proto | 2197 ++++++++------ src/chat/aiserver/v1/timestamp.proto | 144 + src/chat/constant.rs | 354 +-- src/chat/constant/models.rs | 118 - src/chat/error.rs | 6 +- src/chat/middleware/auth.rs | 2 +- src/chat/model.rs | 33 +- src/chat/route.rs | 9 +- src/chat/route/api.rs | 13 +- src/chat/route/checksum.rs | 59 + src/chat/route/config.rs | 61 +- src/chat/route/health.rs | 29 +- src/chat/route/logs.rs | 21 +- src/chat/route/profile.rs | 13 +- src/chat/route/token.rs | 99 + src/chat/route/tokens.rs | 400 +-- src/chat/service.rs | 332 ++- src/chat/stream/decoder.rs | 78 +- src/common.rs | 2 +- src/common/client.rs | 57 +- src/common/model.rs | 6 +- src/common/model/config.rs | 2 +- src/common/model/error.rs | 8 +- src/common/model/health.rs | 2 +- src/common/model/userinfo.rs | 8 +- src/common/utils.rs | 106 +- src/common/utils/checksum.rs | 6 +- src/common/utils/token.rs | 106 +- src/main.rs | 31 +- static/logs.html | 108 + static/tokens.html | 5 +- tools/get-token/Cargo.lock | 84 +- tools/get-token/Cargo.toml | 5 +- tools/get-token/src/main.rs | 146 +- tools/set-token/Cargo.lock | 219 ++ tools/set-token/Cargo.toml | 16 + tools/set-token/src/main.rs | 538 ++++ 59 files changed, 8861 insertions(+), 2505 deletions(-) create mode 100644 src/app/model/vision_ability.rs create mode 100644 src/chat/aiserver/v1/aiserver.v1.rs create mode 100644 src/chat/aiserver/v1/timestamp.proto delete mode 100644 src/chat/constant/models.rs create mode 100644 src/chat/route/checksum.rs create mode 100644 src/chat/route/token.rs create mode 100644 tools/set-token/Cargo.lock create mode 100644 tools/set-token/Cargo.toml create mode 100644 tools/set-token/src/main.rs diff --git a/.env.example b/.env.example index 235777d..b1187e4 100644 --- a/.env.example +++ b/.env.example @@ -22,8 +22,8 @@ SHARED_TOKEN= # 令牌文件路径(已弃用) # TOKEN_FILE=.token -# 令牌列表文件路径 -TOKEN_LIST_FILE=.tokens +# 令牌列表文件路径(已弃用) +# TOKEN_LIST_FILE=.tokens # (实验性)是否启用慢速池(true/false) ENABLE_SLOW_POOL=false @@ -93,8 +93,11 @@ SERVICE_TIMEOUT=30 # 包含网络引用 INCLUDE_WEB_REFERENCES=false -# 持久化日志文件路径 -LOGS_FILE_PATH=logs.bin +# 持久化日志文件路径(已弃用) +# LOGS_FILE_PATH=logs.bin -# 持久化页面配置文件路径 -PAGES_FILE_PATH=pages.bin \ No newline at end of file +# 持久化页面配置文件路径(已弃用) +# PAGES_FILE_PATH=pages.bin + +# 程序数据目录 +DATA_DIR=data diff --git a/.gitignore b/.gitignore index e843bf7..cfd38f6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ node_modules /cursor-api /cursor-api.exe /release - +/data /*.py /logs /dev* diff --git a/Cargo.lock b/Cargo.lock index 55973c1..142bc52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", ] @@ -69,9 +69,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "async-compression" @@ -220,9 +220,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytecheck" @@ -276,15 +276,15 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "cc" -version = "1.2.10" +version = "1.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" dependencies = [ "shlex", ] @@ -333,9 +333,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -361,7 +361,7 @@ dependencies = [ [[package]] name = "cursor-api" -version = "0.1.3-rc.4.3" +version = "0.1.3-rc.5" dependencies = [ "axum", "base64", @@ -378,6 +378,7 @@ dependencies = [ "paste", "prost", "prost-build", + "prost-types", "rand", "regex", "reqwest", @@ -412,7 +413,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -438,9 +439,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -460,9 +461,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "faststr" -version = "0.2.27" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9154486833a83cb5d99de8c4d831314b8ae810dd4ef18d89ceb7a9c7c728dd74" +checksum = "403ebc0cd0c6dbff1cae7098168eff6bac83fad5928b6e91f29388b8dbb61653" dependencies = [ "bytes", "rkyv 0.8.10", @@ -481,9 +482,9 @@ dependencies = [ [[package]] name = "fixedbitset" -version = "0.4.2" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" @@ -575,7 +576,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -626,7 +627,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", ] [[package]] @@ -647,9 +660,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", @@ -727,9 +740,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -739,9 +752,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -948,7 +961,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1017,9 +1030,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -1070,9 +1083,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "matchit" @@ -1103,9 +1116,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", "simd-adler32", @@ -1118,7 +1131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1130,29 +1143,29 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "munge" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" +checksum = "8743b8dfaf66acac79aca9ff2440e8680fef745b6260e6a31d1772b14cfa2862" dependencies = [ "munge_macro", ] [[package]] name = "munge_macro" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" +checksum = "66191390a55bb9830fa8468c12634442ea4199c6e390ddf08ddcace35b3cd5da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -1194,15 +1207,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ "bitflags 2.8.0", "cfg-if", @@ -1221,20 +1234,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -1279,9 +1292,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" -version = "0.6.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", "indexmap", @@ -1324,7 +1337,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -1334,7 +1347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1348,9 +1361,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", @@ -1358,9 +1371,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck", "itertools", @@ -1372,28 +1385,28 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.96", + "syn 2.0.98", "tempfile", ] [[package]] name = "prost-derive" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "prost-types" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" dependencies = [ "prost", ] @@ -1435,7 +1448,7 @@ checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1470,20 +1483,20 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ - "libc", "rand_chacha", "rand_core", + "zerocopy 0.8.20", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", @@ -1491,18 +1504,19 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "7a509b1a2ffbe92afab0e55c8fd99dea1c280e8171bd2d88682bb20bc41cbc2c" dependencies = [ - "getrandom", + "getrandom 0.3.1", + "zerocopy 0.8.20", ] [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ "bitflags 2.8.0", ] @@ -1524,7 +1538,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1621,15 +1635,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -1689,7 +1702,7 @@ checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1700,9 +1713,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.8.0", "errno", @@ -1713,9 +1726,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.21" +version = "0.23.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ "once_cell", "rustls-pki-types", @@ -1735,9 +1748,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" @@ -1758,9 +1771,9 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "schannel" @@ -1808,29 +1821,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "serde_json" -version = "1.0.137" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "itoa", "memchr", @@ -1909,9 +1922,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" @@ -1961,12 +1974,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1992,9 +1999,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -2018,7 +2025,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2063,13 +2070,13 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.15.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", - "getrandom", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -2101,7 +2108,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2112,7 +2119,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2165,7 +2172,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2296,15 +2303,15 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "untrusted" @@ -2337,11 +2344,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.12.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +checksum = "93d59ca99a559661b96bf898d8fce28ed87935fd2bea9f05983c1464dd6c71b1" dependencies = [ - "getrandom", + "getrandom 0.3.1", ] [[package]] @@ -2371,6 +2378,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -2393,7 +2409,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -2428,7 +2444,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2532,7 +2548,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2543,7 +2559,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2667,6 +2683,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -2708,7 +2733,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure", ] @@ -2719,7 +2744,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c" +dependencies = [ + "zerocopy-derive 0.8.20", ] [[package]] @@ -2730,7 +2764,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] @@ -2750,7 +2795,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure", ] @@ -2779,7 +2824,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8989bd2..f2c0d8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,47 +1,48 @@ [package] name = "cursor-api" -version = "0.1.3-rc.4.3" -edition = "2021" +version = "0.1.3-rc.5" +edition = "2024" authors = ["wisdgod "] description = "OpenAI format compatibility layer for the Cursor API" repository = "https://github.com/wisdgod/cursor-api" [build-dependencies] -prost-build = "0.13.4" -sha2 = { version = "0.10.8", default-features = false } -serde_json = "1.0.134" +prost-build = "^0.13" +sha2 = { version = "^0.10.8", default-features = false } +serde_json = "^1.0" [dependencies] -axum = { version = "0.8.1", features = ["json"] } -base64 = { version = "0.22.1", default-features = false, features = ["std"] } -# brotli = { version = "7.0.0", default-features = false, features = ["std"] } -bytes = "1.9.0" -chrono = { version = "0.4.39", default-features = false, features = ["std", "clock", "now", "serde", "rkyv-64"] } -dotenvy = "0.15.7" -flate2 = { version = "1.0.35", default-features = false, features = ["rust_backend"] } -futures = { version = "0.3.31", default-features = false, features = ["std"] } -gif = { version = "0.13.1", default-features = false, features = ["std"] } -hex = { version = "0.4.3", default-features = false, features = ["std"] } -image = { version = "0.25.5", default-features = false, features = ["jpeg", "png", "gif", "webp"] } -memmap2 = "0.9.5" -# openssl = { version = "0.10.68", features = ["vendored"] } -parking_lot = "0.12.3" -paste = "1.0.15" -prost = "0.13.4" -rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"] } -regex = { version = "1.11.1", default-features = false, features = ["std", "perf"] } -reqwest = { version = "0.12.12", default-features = false, features = ["gzip", "brotli", "json", "stream", "socks", "__tls", "charset", "default-tls", "h2", "http2", "macos-system-configuration"] } -rkyv = { version = "0.7.45", default-features = false, features = ["alloc", "std", "bytecheck", "size_64", "validation", "std"] } -serde = { version = "1.0.217", default-features = false, features = ["std", "derive"] } -serde_json = { package = "sonic-rs", version = "0.3.17" } -# serde_json = "1.0.137" -sha2 = { version = "0.10.8", default-features = false } -sysinfo = { version = "0.33.1", default-features = false, features = ["system"] } -tokio = { version = "1.43.0", features = ["rt-multi-thread", "macros", "net", "sync", "time", "fs", "signal"] } -tokio-stream = { version = "0.1.17", features = ["time"] } -tower-http = { version = "0.6.2", features = ["cors", "limit"] } -url = { version = "2.5.4", default-features = false } -uuid = { version = "1.12.1", features = ["v4"] } +axum = { version = "^0.8", features = ["json"] } +base64 = { version = "^0.22", default-features = false, features = ["std"] } +# brotli = { version = "^7.0", default-features = false, features = ["std"] } +bytes = "^1.10" +chrono = { version = "^0.4", default-features = false, features = ["std", "clock", "now", "serde", "rkyv-64"] } +dotenvy = "^0.15" +flate2 = { version = "^1.0", default-features = false, features = ["rust_backend"] } +futures = { version = "^0.3", default-features = false, features = ["std"] } +gif = { version = "^0.13", default-features = false, features = ["std"] } +hex = { version = "^0.4", default-features = false, features = ["std"] } +image = { version = "^0.25", default-features = false, features = ["jpeg", "png", "gif", "webp"] } +memmap2 = "^0.9" +# openssl = { version = "^0.10", features = ["vendored"] } +parking_lot = "^0.12" +paste = "^1.0" +prost = "^0.13" +prost-types = "^0.13" +rand = { version = "^0.9", default-features = false, features = ["thread_rng"] } +regex = { version = "^1.11", default-features = false, features = ["std", "perf"] } +reqwest = { version = "^0.12", default-features = false, features = ["gzip", "brotli", "json", "stream", "socks", "__tls", "charset", "default-tls", "h2", "http2", "macos-system-configuration"] } +rkyv = { version = "^0.7", default-features = false, features = ["alloc", "std", "bytecheck", "size_64", "validation", "std"] } +serde = { version = "^1.0", default-features = false, features = ["std", "derive"] } +serde_json = { package = "sonic-rs", version = "^0.3" } +# serde_json = "^1.0" +sha2 = { version = "^0.10", default-features = false } +sysinfo = { version = "^0.33", default-features = false, features = ["system"] } +tokio = { version = "^1.43", features = ["rt-multi-thread", "macros", "net", "sync", "time", "fs", "signal"] } +tokio-stream = { version = "^0.1", features = ["time"] } +tower-http = { version = "^0.6", features = ["cors", "limit"] } +url = { version = "^2.5", default-features = false } +uuid = { version = "^1.14", features = ["v4"] } [profile.release] lto = true diff --git a/Dockerfile b/Dockerfile index da4219b..6f5e6c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ARG TARGETARCH -FROM --platform=linux/${TARGETARCH} rust:1.84.0-slim-bookworm as builder +FROM --platform=linux/${TARGETARCH} rust:1-slim-bookworm as builder ARG TARGETARCH @@ -10,13 +10,7 @@ RUN apt-get update && \ && rm -rf /var/lib/apt/lists/* COPY . . -RUN case "$TARGETARCH" in \ - amd64) TARGET_CPU="x86-64-v3" ;; \ - arm64) TARGET_CPU="neoverse-n1" ;; \ - *) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; \ - esac && \ - RUSTFLAGS="-C link-arg=-s -C target-cpu=$TARGET_CPU" cargo build --release && \ - cp target/release/cursor-api /app/cursor-api +RUN case "$TARGETARCH" in amd64) TARGET_CPU="x86-64-v3" ;; arm64) TARGET_CPU="neoverse-n1" ;; *) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; esac && RUSTFLAGS="-C link-arg=-s -C target-cpu=$TARGET_CPU" cargo build --release && cp target/release/cursor-api /app/cursor-api # 运行阶段 FROM --platform=linux/${TARGETARCH} debian:bookworm-slim diff --git a/Dockerfile.cross b/Dockerfile.cross index d93a731..ee34eb7 100644 --- a/Dockerfile.cross +++ b/Dockerfile.cross @@ -1,6 +1,6 @@ # Dockerfile.cross -FROM --platform=linux/amd64 rust:1.84.0-slim-bookworm +FROM --platform=linux/amd64 rust:1-slim-bookworm WORKDIR /app diff --git a/Dockerfile.cross.arm64 b/Dockerfile.cross.arm64 index 6ec6d19..e2f7bc2 100644 --- a/Dockerfile.cross.arm64 +++ b/Dockerfile.cross.arm64 @@ -1,6 +1,6 @@ # Dockerfile.cross -FROM --platform=linux/arm64 rust:1.84.0-slim-bookworm +FROM --platform=linux/arm64 rust:1-slim-bookworm WORKDIR /app diff --git a/README.md b/README.md index e0906d0..2cca955 100644 --- a/README.md +++ b/README.md @@ -59,17 +59,18 @@ gpt-4o-128k gemini-1.5-flash-500k claude-3-haiku-200k claude-3-5-sonnet-200k -claude-3-5-sonnet-20241022 gpt-4o-mini o1-mini o1-preview o1 claude-3.5-haiku -gemini-exp-1206 +gemini-2.0-pro-exp gemini-2.0-flash-thinking-exp -gemini-2.0-flash-exp +gemini-2.0-flash deepseek-v3 deepseek-r1 +o3-mini +grok-2 ``` ## 接口说明 @@ -148,6 +149,32 @@ data: {"id":"string","object":"chat.completion.chunk","created":number,"model":" data: [DONE] ``` +### 获取模型列表 + +* 接口地址: `/v1/models` +* 请求方法: GET +* 认证方式: Bearer Token + +#### 响应格式 + +```json +{ + "object": "list", + "data": [ + { + "id": "string", + "object": "model", + "created": number, + "owned_by": "string" + } + ] +} +``` + +#### 更新模型列表说明 + +每次携带Token时都会拉取最新的模型列表,与上次更新需距离至少30分钟。 + ### Token管理接口 #### 简易Token信息管理页面 @@ -194,7 +221,8 @@ data: [DONE] "tokens": number, "max_requests": number, "max_tokens": number - } + }, + "start_of_month": "string" }, "user": { "email": "string", @@ -260,12 +288,15 @@ data: [DONE] * 请求格式: ```json -[ - { - "token": "string", - "checksum": "string" // 可选,如果不提供将自动生成 - } -] +{ + "tokens": [ + { + "token": "string", + "checksum": "string" // 可选,如果不提供将自动生成 + } + ], + "tags": ["string"] +} ``` * 响应格式: @@ -308,6 +339,29 @@ data: [DONE] - failed_tokens: 返回未找到的token列表 - detailed: 返回完整信息(包括updated_tokens和failed_tokens) +#### 更新Tokens标签 + +* 接口地址: `/tokens/tags/update` +* 请求方法: POST +* 认证方式: Bearer Token +* 请求格式: + +```json +{ + "tokens": ["string"], + "tags": ["string"] +} +``` + +* 响应格式: + +```json +{ + "status": "success", + "message": "string" // "标签更新成功" +} +``` + #### 构建API Key * 接口地址: `/build-key` @@ -354,7 +408,7 @@ data: [DONE] |------|------| | 提取关键信息,生成更短的密钥 | 可能存在版本兼容性问题 | | 支持携带自定义配置 | 增加了程序复杂度 | -| 采用非常规编码方式,提升安全性 | | +| 采用非常规编码方式,提升安全性 | 项目是开源的,安全性的提升相当于没有 | | 更容易验证Key的合法性 | | | 取消预校验带来轻微性能提升 | | @@ -470,26 +524,6 @@ data: [DONE] ### 其他接口 -#### 获取模型列表 - -* 接口地址: `/v1/models` -* 请求方法: GET -* 响应格式: - -```json -{ - "object": "list", - "data": [ - { - "id": "string", - "object": "model", - "created": number, - "owned_by": "string" - } - ] -} -``` - #### 获取一个随机hash * 接口地址: `/get-hash` @@ -605,7 +639,8 @@ string "tokens": number, "max_requests": number, "max_tokens": number - } + }, + "start_of_month": "string" }, "user": { "email": "string", @@ -673,7 +708,8 @@ string "tokens": number, "max_requests": number, "max_tokens": number - } + }, + "start_of_month": "string" }, "user": { "email": "string", diff --git a/build.rs b/build.rs index 0af7697..482c0a1 100644 --- a/build.rs +++ b/build.rs @@ -116,7 +116,7 @@ fn minify_assets() -> Result<()> { let files_to_update: Vec<_> = current_hashes .iter() .filter(|(path, current_hash)| { - let is_readme = path.file_name().map_or(false, |f| f == "README.md"); + let is_readme = path.file_name().is_some_and(|f| f == "README.md"); let ext = path.extension().and_then(|e| e.to_str()).unwrap_or(""); // 为 README.md 和其他文件使用不同的输出路径检查 @@ -136,9 +136,7 @@ fn minify_assets() -> Result<()> { } // 检查原始文件是否发生变化 - saved_hashes - .get(*path) - .map_or(true, |saved_hash| saved_hash != *current_hash) + saved_hashes.get(*path) != Some(*current_hash) }) .map(|(path, _)| path.file_name().unwrap().to_string_lossy().into_owned()) .collect(); @@ -168,7 +166,7 @@ fn minify_assets() -> Result<()> { fn main() -> Result<()> { // Proto 文件处理 - println!("cargo:rerun-if-changed=src/chat/aiserver/v1/lite.proto"); + // println!("cargo:rerun-if-changed=src/chat/aiserver/v1/lite.proto"); println!("cargo:rerun-if-changed=src/chat/config/key.proto"); // 获取环境变量 PROTOC let protoc_path = match std::env::var_os("PROTOC") { @@ -185,12 +183,13 @@ fn main() -> Result<()> { config.protoc_executable(protoc_path); } // config.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]"); - config - .compile_protos( - &["src/chat/aiserver/v1/lite.proto"], - &["src/chat/aiserver/v1/"], - ) - .unwrap(); + // config.enum_attribute(".aiserver.v1", "#[allow(clippy::enum_variant_names)]"); + // config + // .compile_protos( + // &["src/chat/aiserver/v1/lite.proto"], + // &["src/chat/aiserver/v1/"], + // ) + // .unwrap(); config .compile_protos(&["src/chat/config/key.proto"], &["src/chat/config/"]) .unwrap(); diff --git a/src/app.rs b/src/app.rs index a08e33d..ddf59be 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,3 +2,4 @@ pub mod config; pub mod constant; pub mod model; pub mod lazy; +// pub mod rule; diff --git a/src/app/config.rs b/src/app/config.rs index 2154fa5..6d4734e 100644 --- a/src/app/config.rs +++ b/src/app/config.rs @@ -1,11 +1,11 @@ use super::{constant::AUTHORIZATION_BEARER_PREFIX, lazy::AUTH_TOKEN, model::AppConfig}; use crate::common::model::{ - config::{ConfigData, ConfigUpdateRequest}, ApiStatus, ErrorResponse, NormalResponse, + config::{ConfigData, ConfigUpdateRequest}, }; use axum::{ - http::{header::AUTHORIZATION, HeaderMap, StatusCode}, Json, + http::{HeaderMap, StatusCode, header::AUTHORIZATION}, }; // 定义处理更新操作的宏 @@ -41,7 +41,7 @@ pub async fn handle_config_update( .ok_or(( StatusCode::UNAUTHORIZED, Json(ErrorResponse { - status: ApiStatus::Failed, + status: ApiStatus::Failure, code: Some(401), error: Some("未提供认证令牌".to_string()), message: None, @@ -52,7 +52,7 @@ pub async fn handle_config_update( return Err(( StatusCode::UNAUTHORIZED, Json(ErrorResponse { - status: ApiStatus::Failed, + status: ApiStatus::Failure, code: Some(401), error: Some("无效的认证令牌".to_string()), message: None, @@ -85,7 +85,7 @@ pub async fn handle_config_update( return Err(( StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { - status: ApiStatus::Failed, + status: ApiStatus::Failure, code: Some(500), error: Some(format!("更新页面内容失败: {}", e)), message: None, @@ -119,7 +119,7 @@ pub async fn handle_config_update( return Err(( StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { - status: ApiStatus::Failed, + status: ApiStatus::Failure, code: Some(500), error: Some(format!("重置页面内容失败: {}", e)), message: None, @@ -149,7 +149,7 @@ pub async fn handle_config_update( _ => Err(( StatusCode::BAD_REQUEST, Json(ErrorResponse { - status: ApiStatus::Failed, + status: ApiStatus::Failure, code: Some(400), error: Some("无效的操作类型".to_string()), message: None, diff --git a/src/app/constant.rs b/src/app/constant.rs index 7b3d3db..77200fe 100644 --- a/src/app/constant.rs +++ b/src/app/constant.rs @@ -1,83 +1,122 @@ macro_rules! def_pub_const { - ($name:ident, $value:expr) => { - pub const $name: &'static str = $value; + // 单个常量定义 + // ($name:ident, $value:expr) => { + // pub const $name: &'static str = $value; + // }; + + // 批量常量定义 + ($($name:ident => $value:expr),+ $(,)?) => { + $( + pub const $name: &'static str = $value; + )+ }; } pub const COMMA: char = ','; -def_pub_const!(PKG_VERSION, env!("CARGO_PKG_VERSION")); -// def_pub_const!(PKG_NAME, env!("CARGO_PKG_NAME")); -// def_pub_const!(PKG_DESCRIPTION, env!("CARGO_PKG_DESCRIPTION")); -// def_pub_const!(PKG_AUTHORS, env!("CARGO_PKG_AUTHORS")); -// def_pub_const!(PKG_REPOSITORY, env!("CARGO_PKG_REPOSITORY")); - -def_pub_const!(EMPTY_STRING, ""); - -def_pub_const!(COMMA_STRING, ","); - -def_pub_const!(ROUTE_ROOT_PATH, "/"); -def_pub_const!(ROUTE_HEALTH_PATH, "/health"); -def_pub_const!(ROUTE_GET_HASH, "/get-hash"); -def_pub_const!(ROUTE_GET_CHECKSUM, "/get-checksum"); -def_pub_const!(ROUTE_GET_TIMESTAMP_HEADER, "/get-tsheader"); -def_pub_const!(ROUTE_USER_INFO_PATH, "/userinfo"); -def_pub_const!(ROUTE_API_PATH, "/api"); -def_pub_const!(ROUTE_LOGS_PATH, "/logs"); -def_pub_const!(ROUTE_CONFIG_PATH, "/config"); -def_pub_const!(ROUTE_TOKENS_PATH, "/tokens"); -def_pub_const!(ROUTE_TOKENS_GET_PATH, "/tokens/get"); -def_pub_const!(ROUTE_TOKENS_RELOAD_PATH, "/tokens/reload"); -def_pub_const!(ROUTE_TOKENS_UPDATE_PATH, "/tokens/update"); -def_pub_const!(ROUTE_TOKENS_ADD_PATH, "/tokens/add"); -def_pub_const!(ROUTE_TOKENS_DELETE_PATH, "/tokens/delete"); -def_pub_const!(ROUTE_ENV_EXAMPLE_PATH, "/env-example"); -def_pub_const!(ROUTE_STATIC_PATH, "/static/{path}"); -def_pub_const!(ROUTE_SHARED_STYLES_PATH, "/static/shared-styles.css"); -def_pub_const!(ROUTE_SHARED_JS_PATH, "/static/shared.js"); -def_pub_const!(ROUTE_ABOUT_PATH, "/about"); -def_pub_const!(ROUTE_README_PATH, "/readme"); -def_pub_const!(ROUTE_BASIC_CALIBRATION_PATH, "/basic-calibration"); -def_pub_const!(ROUTE_BUILD_KEY_PATH, "/build-key"); - -def_pub_const!(DEFAULT_TOKEN_LIST_FILE_NAME, ".tokens"); - -def_pub_const!(STATUS_PENDING, "pending"); -def_pub_const!(STATUS_SUCCESS, "success"); -def_pub_const!(STATUS_FAILED, "failed"); - -def_pub_const!(HEADER_NAME_GHOST_MODE, "x-ghost-mode"); - -def_pub_const!(TRUE, "true"); -def_pub_const!(FALSE, "false"); - -// def_pub_const!(CONTENT_TYPE_PROTO, "application/proto"); -def_pub_const!(CONTENT_TYPE_CONNECT_PROTO, "application/connect+proto"); -def_pub_const!(CONTENT_TYPE_TEXT_HTML_WITH_UTF8, "text/html;charset=utf-8"); +// Package related constants def_pub_const!( - CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, - "text/plain;charset=utf-8" -); -def_pub_const!(CONTENT_TYPE_TEXT_CSS_WITH_UTF8, "text/css;charset=utf-8"); -def_pub_const!( - CONTENT_TYPE_TEXT_JS_WITH_UTF8, - "text/javascript;charset=utf-8" + PKG_VERSION => env!("CARGO_PKG_VERSION") + // PKG_NAME => env!("CARGO_PKG_NAME"), + // PKG_DESCRIPTION => env!("CARGO_PKG_DESCRIPTION"), + // PKG_AUTHORS => env!("CARGO_PKG_AUTHORS"), + // PKG_REPOSITORY => env!("CARGO_PKG_REPOSITORY") ); -def_pub_const!(AUTHORIZATION_BEARER_PREFIX, "Bearer "); +// Basic string constants +def_pub_const!( + EMPTY_STRING => "", + COMMA_STRING => "," +); -def_pub_const!(CURSOR_API2_HOST, "api2.cursor.sh"); -def_pub_const!(CURSOR_HOST, "www.cursor.com"); -def_pub_const!(CURSOR_SETTINGS_URL, "https://www.cursor.com/settings"); +// Route related constants +def_pub_const!( + ROUTE_ROOT_PATH => "/", + ROUTE_HEALTH_PATH => "/health", + ROUTE_GET_HASH => "/get-hash", + ROUTE_GET_CHECKSUM => "/get-checksum", + ROUTE_GET_TIMESTAMP_HEADER => "/get-tsheader", + ROUTE_USER_INFO_PATH => "/userinfo", + ROUTE_API_PATH => "/api", + ROUTE_LOGS_PATH => "/logs", + ROUTE_CONFIG_PATH => "/config", + ROUTE_TOKENS_PATH => "/tokens", + ROUTE_TOKENS_GET_PATH => "/tokens/get", + ROUTE_TOKENS_UPDATE_PATH => "/tokens/update", + ROUTE_TOKENS_ADD_PATH => "/tokens/add", + ROUTE_TOKENS_DELETE_PATH => "/tokens/delete", + ROUTE_TOKEN_TAGS_UPDATE_PATH => "/tokens/tags/update", + ROUTE_ENV_EXAMPLE_PATH => "/env-example", + ROUTE_STATIC_PATH => "/static/{path}", + ROUTE_SHARED_STYLES_PATH => "/static/shared-styles.css", + ROUTE_SHARED_JS_PATH => "/static/shared.js", + ROUTE_ABOUT_PATH => "/about", + ROUTE_README_PATH => "/readme", + ROUTE_BASIC_CALIBRATION_PATH => "/basic-calibration", + ROUTE_BUILD_KEY_PATH => "/build-key" +); -def_pub_const!(OBJECT_CHAT_COMPLETION, "chat.completion"); -def_pub_const!(OBJECT_CHAT_COMPLETION_CHUNK, "chat.completion.chunk"); +// def_pub_const!(DEFAULT_TOKEN_LIST_FILE_NAME => ".tokens"); -// def_pub_const!(CURSOR_API2_STREAM_CHAT, "StreamChat"); -// def_pub_const!(CURSOR_API2_GET_USER_INFO, "GetUserInfo"); +// Status constants +def_pub_const!( + STATUS_PENDING => "pending", + STATUS_SUCCESS => "success", + STATUS_FAILED => "failed" +); -def_pub_const!(FINISH_REASON_STOP, "stop"); +// Header constants +def_pub_const!( + HEADER_NAME_GHOST_MODE => "x-ghost-mode" +); -def_pub_const!(ERR_INVALID_PATH, "无效的路径"); +// Boolean constants +def_pub_const!( + TRUE => "true", + FALSE => "false" +); -// def_pub_const!(ERR_CHECKSUM_NO_GOOD, "checksum no good"); +// Content type constants +def_pub_const!( + CONTENT_TYPE_PROTO => "application/proto", + CONTENT_TYPE_CONNECT_PROTO => "application/connect+proto", + CONTENT_TYPE_TEXT_HTML_WITH_UTF8 => "text/html;charset=utf-8", + CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8 => "text/plain;charset=utf-8", + CONTENT_TYPE_TEXT_CSS_WITH_UTF8 => "text/css;charset=utf-8", + CONTENT_TYPE_TEXT_JS_WITH_UTF8 => "text/javascript;charset=utf-8" +); + +// Authorization constants +def_pub_const!( + AUTHORIZATION_BEARER_PREFIX => "Bearer " +); + +// Cursor related constants +def_pub_const!( + CURSOR_API2_HOST => "api2.cursor.sh", + CURSOR_HOST => "www.cursor.com", + CURSOR_SETTINGS_URL => "https://www.cursor.com/settings" +); + +// Object type constants +def_pub_const!( + OBJECT_CHAT_COMPLETION => "chat.completion", + OBJECT_CHAT_COMPLETION_CHUNK => "chat.completion.chunk" +); + +// def_pub_const!( +// CURSOR_API2_STREAM_CHAT => "StreamChat", +// CURSOR_API2_GET_USER_INFO => "GetUserInfo" +// ); + +// Finish reason constants +def_pub_const!( + FINISH_REASON_STOP => "stop" +); + +// Error message constants +def_pub_const!( + ERR_INVALID_PATH => "无效的路径" +); + +// def_pub_const!(ERR_CHECKSUM_NO_GOOD => "checksum no good"); diff --git a/src/app/lazy.rs b/src/app/lazy.rs index 8f659dd..544ed5f 100644 --- a/src/app/lazy.rs +++ b/src/app/lazy.rs @@ -1,10 +1,9 @@ -use super::constant::{ - COMMA, CURSOR_API2_HOST, CURSOR_HOST, DEFAULT_TOKEN_LIST_FILE_NAME, EMPTY_STRING, -}; +use super::constant::{COMMA, CURSOR_API2_HOST, CURSOR_HOST, EMPTY_STRING}; use crate::common::utils::{ parse_ascii_char_from_env, parse_bool_from_env, parse_string_from_env, parse_usize_from_env, }; -use std::sync::LazyLock; +use chrono::Local; +use std::{path::PathBuf, sync::LazyLock}; use tokio::sync::{Mutex, OnceCell}; macro_rules! def_pub_static { @@ -32,17 +31,15 @@ macro_rules! def_pub_static { def_pub_static!(ROUTE_PREFIX, env: "ROUTE_PREFIX", default: EMPTY_STRING); def_pub_static!(AUTH_TOKEN, env: "AUTH_TOKEN", default: EMPTY_STRING); -def_pub_static!(TOKEN_LIST_FILE, env: "TOKEN_LIST_FILE", default: DEFAULT_TOKEN_LIST_FILE_NAME); def_pub_static!(ROUTE_MODELS_PATH, format!("{}/v1/models", *ROUTE_PREFIX)); def_pub_static!( ROUTE_CHAT_PATH, format!("{}/v1/chat/completions", *ROUTE_PREFIX) ); -pub static START_TIME: LazyLock> = - LazyLock::new(chrono::Local::now); +pub static START_TIME: LazyLock> = LazyLock::new(Local::now); -pub fn get_start_time() -> chrono::DateTime { +pub fn get_start_time() -> chrono::DateTime { *START_TIME } @@ -114,6 +111,12 @@ def_cursor_api_url!( "/aiserver.v1.AiService/StreamChatWeb" ); +def_cursor_api_url!( + CURSOR_API2_CHAT_MODELS_URL, + CURSOR_API2_HOST, + "/aiserver.v1.AiService/AvailableModels" +); + def_cursor_api_url!( CURSOR_API2_STRIPE_URL, CURSOR_API2_HOST, @@ -124,11 +127,26 @@ def_cursor_api_url!(CURSOR_USAGE_API_URL, CURSOR_HOST, "/api/usage"); def_cursor_api_url!(CURSOR_USER_API_URL, CURSOR_HOST, "/api/auth/me"); -pub(super) static LOGS_FILE_PATH: LazyLock = - LazyLock::new(|| parse_string_from_env("LOGS_FILE_PATH", "logs.bin")); +static DATA_DIR: LazyLock = LazyLock::new(|| { + let data_dir = parse_string_from_env("DATA_DIR", "data"); + let path = std::env::current_exe() + .ok() + .and_then(|exe_path| exe_path.parent().map(|p| p.to_path_buf())) + .unwrap_or_else(|| PathBuf::from(".")) + .join(data_dir); + if !path.exists() { + std::fs::create_dir_all(&path).expect("无法创建数据目录"); + } + path +}); -pub(super) static PAGES_FILE_PATH: LazyLock = - LazyLock::new(|| parse_string_from_env("PAGES_FILE_PATH", "pages.bin")); +pub(super) static CONFIG_FILE_PATH: LazyLock = + LazyLock::new(|| DATA_DIR.join("config.bin")); + +pub(super) static LOGS_FILE_PATH: LazyLock = LazyLock::new(|| DATA_DIR.join("logs.bin")); + +pub(super) static TOKENS_FILE_PATH: LazyLock = + LazyLock::new(|| DATA_DIR.join("tokens.bin")); pub static DEBUG: LazyLock = LazyLock::new(|| parse_bool_from_env("DEBUG", false)); @@ -157,14 +175,14 @@ pub(crate) async fn get_log_file() -> &'static Mutex { #[macro_export] macro_rules! debug_println { ($($arg:tt)*) => { - if *crate::app::lazy::DEBUG { + if *$crate::app::lazy::DEBUG { let time = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); let log_message = format!("{} - {}", time, format!($($arg)*)); use tokio::io::AsyncWriteExt as _; // 使用 tokio 的 spawn 在后台异步写入日志 tokio::spawn(async move { - let log_file = crate::app::lazy::get_log_file().await; + let log_file = $crate::app::lazy::get_log_file().await; // 使用 MutexGuard 获取可变引用 let mut file = log_file.lock().await; if let Err(err) = file.write_all(log_message.as_bytes()).await { @@ -183,6 +201,8 @@ macro_rules! debug_println { pub static REQUEST_LOGS_LIMIT: LazyLock = LazyLock::new(|| std::cmp::min(parse_usize_from_env("REQUEST_LOGS_LIMIT", 100), 2000)); +pub static IS_UNLIMITED_REQUEST_LOGS: LazyLock = LazyLock::new(|| *REQUEST_LOGS_LIMIT == 0); + pub static SERVICE_TIMEOUT: LazyLock = LazyLock::new(|| { let timeout = parse_usize_from_env("SERVICE_TIMEOUT", 30); u64::try_from(timeout).map(|t| t.min(600)).unwrap_or(30) diff --git a/src/app/model.rs b/src/app/model.rs index 1bf836b..05d34d5 100644 --- a/src/app/model.rs +++ b/src/app/model.rs @@ -1,30 +1,30 @@ use crate::{ - app::constant::{ - EMPTY_STRING, ERR_INVALID_PATH, ROUTE_ABOUT_PATH, ROUTE_API_PATH, ROUTE_BUILD_KEY_PATH, - ROUTE_CONFIG_PATH, ROUTE_LOGS_PATH, ROUTE_README_PATH, ROUTE_ROOT_PATH, - ROUTE_SHARED_JS_PATH, ROUTE_SHARED_STYLES_PATH, ROUTE_TOKENS_PATH, - }, chat::model::Message, common::{ - client::rebuild_http_client, - model::{userinfo::TokenProfile, ApiStatus}, - utils::{generate_checksum_with_repair, parse_bool_from_env, parse_string_from_env}, + model::{ApiStatus, userinfo::TokenProfile}, + utils::{generate_checksum_with_repair, get_token_profile}, }, }; -use parking_lot::RwLock; +use memmap2::{MmapMut, MmapOptions}; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; use serde::{Deserialize, Serialize}; -use std::sync::LazyLock; +use std::{collections::HashSet, fs::OpenOptions}; mod usage_check; pub use usage_check::UsageCheck; +mod vision_ability; +pub use vision_ability::VisionAbility; mod config; +pub use config::AppConfig; mod proxies; pub use proxies::Proxies; mod build_key; pub use build_key::*; -use super::constant::{STATUS_FAILED, STATUS_PENDING, STATUS_SUCCESS}; +use super::{ + constant::{STATUS_FAILED, STATUS_PENDING, STATUS_SUCCESS}, + lazy::{LOGS_FILE_PATH, TOKENS_FILE_PATH}, +}; // 页面内容类型枚举 #[derive(Clone, Serialize, Deserialize, Archive, RkyvDeserialize, RkyvSerialize)] @@ -44,52 +44,6 @@ impl Default for PageContent { } } -// 静态配置 -#[derive(Default, Clone)] -pub struct AppConfig { - vision_ability: VisionAbility, - slow_pool: bool, - allow_claude: bool, - pages: Pages, - usage_check: UsageCheck, - dynamic_key: bool, - share_token: String, - is_share: bool, - proxies: Proxies, - web_refs: bool, -} - -#[derive(Serialize, Deserialize, Clone, Copy, PartialEq)] -pub enum VisionAbility { - #[serde(rename = "none", alias = "disabled")] - None, - #[serde(rename = "base64", alias = "base64-only")] - Base64, - #[serde(rename = "all", alias = "base64-http")] - All, -} - -impl VisionAbility { - pub fn from_str(s: &str) -> Self { - match s.to_lowercase().as_str() { - "none" | "disabled" => Self::None, - "base64" | "base64-only" => Self::Base64, - "all" | "base64-http" => Self::All, - _ => Self::default(), - } - } - - pub fn is_none(&self) -> bool { - matches!(self, VisionAbility::None) - } -} - -impl Default for VisionAbility { - fn default() -> Self { - Self::Base64 - } -} - #[derive(Clone, Default, Archive, RkyvDeserialize, RkyvSerialize)] pub struct Pages { pub root_content: PageContent, @@ -104,232 +58,140 @@ pub struct Pages { pub build_key_content: PageContent, } -// 运行时状态 -pub struct AppState { +#[derive(Serialize, Clone, Archive, RkyvDeserialize, RkyvSerialize)] +pub struct TokenGroup { + pub index: u16, + pub name: String, + pub tokens: Vec, + #[serde(default)] + pub enabled: bool, +} + +// Token管理器 +#[derive(Clone, Archive, RkyvDeserialize, RkyvSerialize)] +pub struct TokenManager { + pub tokens: Vec, + pub tags: HashSet, // 存储所有已使用的标签 +} + +// 请求统计管理器 +#[derive(Clone, Archive, RkyvDeserialize, RkyvSerialize)] +pub struct RequestStatsManager { pub total_requests: u64, pub active_requests: u64, pub error_requests: u64, pub request_logs: Vec, - pub token_infos: Vec, } -// 全局配置实例 -pub static APP_CONFIG: LazyLock> = - LazyLock::new(|| RwLock::new(AppConfig::default())); +#[derive(Clone, Archive, RkyvDeserialize, RkyvSerialize)] +pub struct AppState { + pub token_manager: TokenManager, + pub request_manager: RequestStatsManager, +} -macro_rules! config_methods { - ($($field:ident: $type:ty, $default:expr;)*) => { - $( - paste::paste! { - pub fn []() -> $type - where - $type: Copy + PartialEq, - { - APP_CONFIG.read().$field - } - - pub fn [](value: $type) - where - $type: Copy + PartialEq, - { - let current = Self::[](); - if current != value { - APP_CONFIG.write().$field = value; - } - } - - pub fn []() - where - $type: Copy + PartialEq, - { - let default_value = $default; - let current = Self::[](); - if current != default_value { - APP_CONFIG.write().$field = default_value; - } - } +impl TokenManager { + pub fn new(tokens: Vec) -> Self { + let mut tags = HashSet::new(); + for token in &tokens { + if let Some(token_tags) = &token.tags { + tags.extend(token_tags.iter().cloned()); } - )* - }; -} + } -macro_rules! config_methods_clone { - ($($field:ident: $type:ty, $default:expr;)*) => { - $( - paste::paste! { - pub fn []() -> $type - where - $type: Clone + PartialEq, - { - APP_CONFIG.read().$field.clone() - } + Self { tokens, tags } + } - pub fn [](value: $type) - where - $type: Clone + PartialEq, - { - let current = Self::[](); - if current != value { - APP_CONFIG.write().$field = value; - } - } + pub fn update_global_tags(&mut self, new_tags: &[String]) { + // 将新标签添加到全局标签集合中 + self.tags.extend(new_tags.iter().cloned()); + } - pub fn []() - where - $type: Clone + PartialEq, - { - let default_value = $default; - let current = Self::[](); - if current != default_value { - APP_CONFIG.write().$field = default_value; - } - } + pub fn update_tokens_tags( + &mut self, + tokens: Vec, + new_tags: Vec, + ) -> Result<(), &'static str> { + // 创建tokens的HashSet用于快速查找 + let tokens_set: HashSet<_> = tokens.iter().collect(); + + // 更新指定tokens的标签 + for token_info in &mut self.tokens { + if tokens_set.contains(&token_info.token) { + token_info.tags = Some(new_tags.clone()); } - )* - }; -} + } -impl AppConfig { - pub fn init() { - let mut config = APP_CONFIG.write(); - config.vision_ability = - VisionAbility::from_str(&parse_string_from_env("VISION_ABILITY", EMPTY_STRING)); - config.slow_pool = parse_bool_from_env("ENABLE_SLOW_POOL", false); - config.allow_claude = parse_bool_from_env("PASS_ANY_CLAUDE", false); - config.usage_check = - UsageCheck::from_str(&parse_string_from_env("USAGE_CHECK", EMPTY_STRING)); - config.dynamic_key = parse_bool_from_env("DYNAMIC_KEY", false); - config.share_token = parse_string_from_env("SHARED_TOKEN", EMPTY_STRING); - config.is_share = !config.share_token.is_empty(); - config.proxies = match std::env::var("PROXIES") { - Ok(proxies) => Proxies::from_str(proxies.as_str()), - Err(_) => Proxies::default(), + // 更新全局标签集合 + self.tags = self + .tokens + .iter() + .filter_map(|t| t.tags.clone()) + .flatten() + .collect(); + + Ok(()) + } + + pub fn get_tokens_by_tag(&self, tag: &str) -> Vec<&TokenInfo> { + self.tokens + .iter() + .filter(|t| { + t.tags + .as_ref() + .is_some_and(|tags| tags.contains(&tag.to_string())) + }) + .collect() + } + + pub fn update_checksum(&mut self) { + for token_info in self.tokens.iter_mut() { + token_info.checksum = generate_checksum_with_repair(&token_info.checksum); + } + } + + pub async fn save_tokens(&self) -> Result<(), Box> { + let bytes = rkyv::to_bytes::<_, 256>(self)?; + + let file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&*TOKENS_FILE_PATH)?; + + if bytes.len() > usize::MAX / 2 { + return Err("Token数据过大".into()); + } + + file.set_len(bytes.len() as u64)?; + let mut mmap = unsafe { MmapMut::map_mut(&file)? }; + mmap.copy_from_slice(&bytes); + mmap.flush()?; + + Ok(()) + } + + pub async fn load_tokens() -> Result> { + let file = match OpenOptions::new().read(true).open(&*TOKENS_FILE_PATH) { + Ok(file) => file, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + return Ok(Self::new(Vec::new())); + } + Err(e) => return Err(Box::new(e)), }; - config.web_refs = parse_bool_from_env("INCLUDE_WEB_REFERENCES", false) - } - config_methods! { - slow_pool: bool, false; - allow_claude: bool, false; - dynamic_key: bool, false; - web_refs: bool, false; - } - - config_methods_clone! { - vision_ability: VisionAbility, VisionAbility::default(); - usage_check: UsageCheck, UsageCheck::default(); - } - - pub fn get_share_token() -> String { - APP_CONFIG.read().share_token.clone() - } - - pub fn update_share_token(value: String) { - let current = Self::get_share_token(); - if current != value { - let mut config = APP_CONFIG.write(); - config.share_token = value; - config.is_share = !config.share_token.is_empty(); + if file.metadata()?.len() > usize::MAX as u64 { + return Err("Token文件过大".into()); } - } - pub fn reset_share_token() { - let current = Self::get_share_token(); - if !current.is_empty() { - let mut config = APP_CONFIG.write(); - config.share_token = String::new(); - config.is_share = false; - } - } - - pub fn get_proxies() -> Proxies { - APP_CONFIG.read().proxies.clone() - } - - pub fn update_proxies(value: Proxies) { - let current = Self::get_proxies(); - if current != value { - let mut config = APP_CONFIG.write(); - config.proxies = value; - rebuild_http_client(); - } - } - - pub fn reset_proxies() { - let default_value = Proxies::default(); - let current = Self::get_proxies(); - if current != default_value { - let mut config = APP_CONFIG.write(); - config.proxies = default_value; - rebuild_http_client(); - } - } - - pub fn get_page_content(path: &str) -> Option { - match path { - ROUTE_ROOT_PATH => Some(APP_CONFIG.read().pages.root_content.clone()), - ROUTE_LOGS_PATH => Some(APP_CONFIG.read().pages.logs_content.clone()), - ROUTE_CONFIG_PATH => Some(APP_CONFIG.read().pages.config_content.clone()), - ROUTE_TOKENS_PATH => Some(APP_CONFIG.read().pages.tokeninfo_content.clone()), - ROUTE_SHARED_STYLES_PATH => Some(APP_CONFIG.read().pages.shared_styles_content.clone()), - ROUTE_SHARED_JS_PATH => Some(APP_CONFIG.read().pages.shared_js_content.clone()), - ROUTE_ABOUT_PATH => Some(APP_CONFIG.read().pages.about_content.clone()), - ROUTE_README_PATH => Some(APP_CONFIG.read().pages.readme_content.clone()), - ROUTE_API_PATH => Some(APP_CONFIG.read().pages.api_content.clone()), - ROUTE_BUILD_KEY_PATH => Some(APP_CONFIG.read().pages.build_key_content.clone()), - _ => None, - } - } - - pub fn update_page_content(path: &str, content: PageContent) -> Result<(), &'static str> { - let mut config = APP_CONFIG.write(); - match path { - ROUTE_ROOT_PATH => config.pages.root_content = content, - ROUTE_LOGS_PATH => config.pages.logs_content = content, - ROUTE_CONFIG_PATH => config.pages.config_content = content, - ROUTE_TOKENS_PATH => config.pages.tokeninfo_content = content, - ROUTE_SHARED_STYLES_PATH => config.pages.shared_styles_content = content, - ROUTE_SHARED_JS_PATH => config.pages.shared_js_content = content, - ROUTE_ABOUT_PATH => config.pages.about_content = content, - ROUTE_README_PATH => config.pages.readme_content = content, - ROUTE_API_PATH => config.pages.api_content = content, - ROUTE_BUILD_KEY_PATH => config.pages.build_key_content = content, - _ => return Err(ERR_INVALID_PATH), - } - Ok(()) - } - - pub fn reset_page_content(path: &str) -> Result<(), &'static str> { - let mut config = APP_CONFIG.write(); - match path { - ROUTE_ROOT_PATH => config.pages.root_content = PageContent::default(), - ROUTE_LOGS_PATH => config.pages.logs_content = PageContent::default(), - ROUTE_CONFIG_PATH => config.pages.config_content = PageContent::default(), - ROUTE_TOKENS_PATH => config.pages.tokeninfo_content = PageContent::default(), - ROUTE_SHARED_STYLES_PATH => config.pages.shared_styles_content = PageContent::default(), - ROUTE_SHARED_JS_PATH => config.pages.shared_js_content = PageContent::default(), - ROUTE_ABOUT_PATH => config.pages.about_content = PageContent::default(), - ROUTE_README_PATH => config.pages.readme_content = PageContent::default(), - ROUTE_API_PATH => config.pages.api_content = PageContent::default(), - ROUTE_BUILD_KEY_PATH => config.pages.build_key_content = PageContent::default(), - _ => return Err(ERR_INVALID_PATH), - } - Ok(()) - } - - pub fn is_share() -> bool { - APP_CONFIG.read().is_share + let mmap = unsafe { MmapOptions::new().map(&file)? }; + let archived = unsafe { rkyv::archived_root::(&mmap) }; + Ok(archived.deserialize(&mut rkyv::Infallible)?) } } -impl AppState { - pub fn new(token_infos: Vec) -> Self { - // 尝试加载保存的日志 - let request_logs = tokio::task::block_in_place(|| { - tokio::runtime::Handle::current() - .block_on(async { Self::load_saved_logs().await.unwrap_or_default() }) - }); - +impl RequestStatsManager { + pub fn new(request_logs: Vec) -> Self { Self { total_requests: request_logs.len() as u64, active_requests: 0, @@ -338,14 +200,96 @@ impl AppState { .filter(|log| matches!(log.status, LogStatus::Failed)) .count() as u64, request_logs, - token_infos, } } - pub fn update_checksum(&mut self) { - for token_info in self.token_infos.iter_mut() { - token_info.checksum = generate_checksum_with_repair(&token_info.checksum); + pub async fn save_logs(&self) -> Result<(), Box> { + let bytes = rkyv::to_bytes::<_, 256>(&self.request_logs)?; + + let file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&*LOGS_FILE_PATH)?; + + if bytes.len() > usize::MAX / 2 { + return Err("日志数据过大".into()); } + + file.set_len(bytes.len() as u64)?; + let mut mmap = unsafe { MmapMut::map_mut(&file)? }; + mmap.copy_from_slice(&bytes); + mmap.flush()?; + + Ok(()) + } + + pub async fn load_logs() -> Result, Box> { + let file = match OpenOptions::new().read(true).open(&*LOGS_FILE_PATH) { + Ok(file) => file, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + return Ok(Vec::new()); + } + Err(e) => return Err(Box::new(e)), + }; + + if file.metadata()?.len() > usize::MAX as u64 { + return Err("日志文件过大".into()); + } + + let mmap = unsafe { MmapOptions::new().map(&file)? }; + let archived = unsafe { rkyv::archived_root::>(&mmap) }; + Ok(archived.deserialize(&mut rkyv::Infallible)?) + } +} + +impl Default for AppState { + fn default() -> Self { + Self::new() + } +} + +impl AppState { + pub fn new() -> Self { + // 尝试加载保存的数据 + let (request_logs, mut token_manager) = tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + let logs = RequestStatsManager::load_logs().await.unwrap_or_default(); + let token_manager = TokenManager::load_tokens() + .await + .unwrap_or_else(|_| TokenManager::new(Vec::new())); + (logs, token_manager) + }) + }); + + // 查询缺失的 token profiles + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + for token_info in token_manager.tokens.iter_mut() { + if token_info.profile.is_none() { + token_info.profile = get_token_profile(&token_info.token).await; + } + } + }) + }); + + Self { + token_manager, + request_manager: RequestStatsManager::new(request_logs), + } + } + + pub async fn save_state(&self) -> Result<(), Box> { + // 并行保存 logs 和 tokens + let (logs_result, tokens_result) = tokio::join!( + self.request_manager.save_logs(), + self.token_manager.save_tokens() + ); + + logs_result?; + tokens_result?; + Ok(()) } } @@ -417,12 +361,13 @@ pub struct ChatRequest { } // 用于存储 token 信息 -#[derive(Serialize, Clone, Archive, RkyvDeserialize, RkyvSerialize)] +#[derive(Clone, Serialize, Archive, RkyvSerialize, RkyvDeserialize)] pub struct TokenInfo { pub token: String, pub checksum: String, #[serde(skip_serializing_if = "Option::is_none")] pub profile: Option, + pub tags: Option>, } // TokenUpdateRequest 结构体 @@ -431,6 +376,13 @@ pub struct TokenUpdateRequest { pub tokens: String, } +#[derive(Deserialize)] +pub struct TokenAddRequest { + pub tokens: Vec, + #[serde(default)] + pub tags: Option>, +} + #[derive(Deserialize)] pub struct TokenAddRequestTokenInfo { pub token: String, @@ -484,3 +436,26 @@ pub struct TokensDeleteResponse { #[serde(skip_serializing_if = "Option::is_none")] pub failed_tokens: Option>, } + +#[derive(Serialize)] +pub struct TokenInfoResponse { + pub status: ApiStatus, + #[serde(skip_serializing_if = "Option::is_none")] + pub tokens: Option>, + pub tokens_count: usize, + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, +} + +// 标签相关的请求/响应结构体 +#[derive(Deserialize)] +pub struct TokenTagsUpdateRequest { + pub tokens: Vec, + pub tags: Vec, +} + +#[derive(Serialize)] +pub struct TokenTagsResponse { + pub status: ApiStatus, + pub message: Option, +} diff --git a/src/app/model/build_key.rs b/src/app/model/build_key.rs index 46f3f31..3882d35 100644 --- a/src/app/model/build_key.rs +++ b/src/app/model/build_key.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::{app::constant::COMMA, chat::constant::AVAILABLE_MODELS}; +use crate::{app::constant::COMMA, chat::constant::Models}; #[derive(Deserialize)] pub struct BuildKeyRequest { @@ -16,7 +16,7 @@ pub struct BuildKeyRequest { } pub struct UsageCheckModelConfig { pub model_type: UsageCheckModelType, - pub model_ids: Vec<&'static str>, + pub model_ids: Vec, } impl<'de> Deserialize<'de> for UsageCheckModelConfig { @@ -42,10 +42,7 @@ impl<'de> Deserialize<'de> for UsageCheckModelConfig { .split(COMMA) .filter_map(|model| { let model = model.trim(); - AVAILABLE_MODELS - .iter() - .find(|m| m.id == model) - .map(|m| m.id) + Models::find_id(model) }) .collect() }; diff --git a/src/app/model/config.rs b/src/app/model/config.rs index f4f5244..500977d 100644 --- a/src/app/model/config.rs +++ b/src/app/model/config.rs @@ -1,69 +1,248 @@ use memmap2::{MmapMut, MmapOptions}; -use rkyv::{archived_root, Deserialize as _}; -use std::fs::OpenOptions; +use parking_lot::RwLock; +use rkyv::{Deserialize as _, archived_root}; +use std::{fs::OpenOptions, sync::LazyLock}; -use crate::app::lazy::{LOGS_FILE_PATH, PAGES_FILE_PATH}; +use crate::{ + app::{ + constant::{ + EMPTY_STRING, ERR_INVALID_PATH, ROUTE_ABOUT_PATH, ROUTE_API_PATH, ROUTE_BUILD_KEY_PATH, + ROUTE_CONFIG_PATH, ROUTE_LOGS_PATH, ROUTE_README_PATH, ROUTE_ROOT_PATH, + ROUTE_SHARED_JS_PATH, ROUTE_SHARED_STYLES_PATH, ROUTE_TOKENS_PATH, + }, + lazy::CONFIG_FILE_PATH, + }, + common::{ + client::rebuild_http_client, + utils::{parse_bool_from_env, parse_string_from_env}, + }, +}; -use super::{AppConfig, AppState, Pages, RequestLog, APP_CONFIG}; +use super::{PageContent, Pages, Proxies, UsageCheck, VisionAbility}; -impl AppState { - // 保存日志的方法 - pub(crate) async fn save_logs(&self) -> Result<(), Box> { - // 序列化日志 - let bytes = rkyv::to_bytes::<_, 256>(&self.request_logs)?; +// 静态配置 +#[derive(Default, Clone)] +pub struct AppConfig { + vision_ability: VisionAbility, + slow_pool: bool, + allow_claude: bool, + pages: Pages, + usage_check: UsageCheck, + dynamic_key: bool, + share_token: String, + is_share: bool, + proxies: Proxies, + web_refs: bool, +} - // 创建或打开文件 - let file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(LOGS_FILE_PATH.as_str())?; +// 全局配置实例 +static APP_CONFIG: LazyLock> = + LazyLock::new(|| RwLock::new(AppConfig::default())); - // 添加大小检查 - if bytes.len() > usize::MAX / 2 { - return Err("日志数据过大".into()); - } +macro_rules! config_methods { + ($($field:ident: $type:ty, $default:expr;)*) => { + $( + paste::paste! { + pub fn []() -> $type + where + $type: Copy + PartialEq, + { + APP_CONFIG.read().$field + } - // 设置文件大小 - file.set_len(bytes.len() as u64)?; + pub fn [](value: $type) + where + $type: Copy + PartialEq, + { + let current = Self::[](); + if current != value { + APP_CONFIG.write().$field = value; + } + } - // 创建可写入的内存映射 - let mut mmap = unsafe { MmapMut::map_mut(&file)? }; - - // 写入数据 - mmap.copy_from_slice(&bytes); - - // 同步到磁盘 - mmap.flush()?; - - Ok(()) - } - - // 加载日志的方法 - pub(super) async fn load_saved_logs() -> Result, Box> { - let file = match OpenOptions::new().read(true).open(LOGS_FILE_PATH.as_str()) { - Ok(file) => file, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => { - return Ok(Vec::new()); + pub fn []() + where + $type: Copy + PartialEq, + { + let default_value = $default; + let current = Self::[](); + if current != default_value { + APP_CONFIG.write().$field = default_value; + } + } } - Err(e) => return Err(Box::new(e)), - }; + )* + }; +} - // 添加文件大小检查 - if file.metadata()?.len() > usize::MAX as u64 { - return Err("日志文件过大".into()); - } +macro_rules! config_methods_clone { + ($($field:ident: $type:ty, $default:expr;)*) => { + $( + paste::paste! { + pub fn []() -> $type + where + $type: Clone + PartialEq, + { + APP_CONFIG.read().$field.clone() + } - // 创建只读内存映射 - let mmap = unsafe { MmapOptions::new().map(&file)? }; + pub fn [](value: $type) + where + $type: Clone + PartialEq, + { + let current = Self::[](); + if current != value { + APP_CONFIG.write().$field = value; + } + } - // 验证并反序列化数据 - let archived = unsafe { archived_root::>(&mmap) }; - Ok(archived.deserialize(&mut rkyv::Infallible)?) - } + pub fn []() + where + $type: Clone + PartialEq, + { + let default_value = $default; + let current = Self::[](); + if current != default_value { + APP_CONFIG.write().$field = default_value; + } + } + } + )* + }; } impl AppConfig { + pub fn init() { + let mut config = APP_CONFIG.write(); + config.vision_ability = + VisionAbility::from_str(&parse_string_from_env("VISION_ABILITY", EMPTY_STRING)); + config.slow_pool = parse_bool_from_env("ENABLE_SLOW_POOL", false); + config.allow_claude = parse_bool_from_env("PASS_ANY_CLAUDE", false); + config.usage_check = + UsageCheck::from_str(&parse_string_from_env("USAGE_CHECK", EMPTY_STRING)); + config.dynamic_key = parse_bool_from_env("DYNAMIC_KEY", false); + config.share_token = parse_string_from_env("SHARED_TOKEN", EMPTY_STRING); + config.is_share = !config.share_token.is_empty(); + config.proxies = match std::env::var("PROXIES") { + Ok(proxies) => Proxies::from_str(proxies.as_str()), + Err(_) => Proxies::default(), + }; + config.web_refs = parse_bool_from_env("INCLUDE_WEB_REFERENCES", false) + } + + config_methods! { + slow_pool: bool, false; + allow_claude: bool, false; + dynamic_key: bool, false; + web_refs: bool, false; + } + + config_methods_clone! { + vision_ability: VisionAbility, VisionAbility::default(); + usage_check: UsageCheck, UsageCheck::default(); + } + + pub fn get_share_token() -> String { + APP_CONFIG.read().share_token.clone() + } + + pub fn update_share_token(value: String) { + let current = Self::get_share_token(); + if current != value { + let mut config = APP_CONFIG.write(); + config.share_token = value; + config.is_share = !config.share_token.is_empty(); + } + } + + pub fn reset_share_token() { + let current = Self::get_share_token(); + if !current.is_empty() { + let mut config = APP_CONFIG.write(); + config.share_token = String::new(); + config.is_share = false; + } + } + + pub fn get_proxies() -> Proxies { + APP_CONFIG.read().proxies.clone() + } + + pub fn update_proxies(value: Proxies) { + let current = Self::get_proxies(); + if current != value { + let mut config = APP_CONFIG.write(); + config.proxies = value; + rebuild_http_client(); + } + } + + pub fn reset_proxies() { + let default_value = Proxies::default(); + let current = Self::get_proxies(); + if current != default_value { + let mut config = APP_CONFIG.write(); + config.proxies = default_value; + rebuild_http_client(); + } + } + + pub fn get_page_content(path: &str) -> Option { + match path { + ROUTE_ROOT_PATH => Some(APP_CONFIG.read().pages.root_content.clone()), + ROUTE_LOGS_PATH => Some(APP_CONFIG.read().pages.logs_content.clone()), + ROUTE_CONFIG_PATH => Some(APP_CONFIG.read().pages.config_content.clone()), + ROUTE_TOKENS_PATH => Some(APP_CONFIG.read().pages.tokeninfo_content.clone()), + ROUTE_SHARED_STYLES_PATH => Some(APP_CONFIG.read().pages.shared_styles_content.clone()), + ROUTE_SHARED_JS_PATH => Some(APP_CONFIG.read().pages.shared_js_content.clone()), + ROUTE_ABOUT_PATH => Some(APP_CONFIG.read().pages.about_content.clone()), + ROUTE_README_PATH => Some(APP_CONFIG.read().pages.readme_content.clone()), + ROUTE_API_PATH => Some(APP_CONFIG.read().pages.api_content.clone()), + ROUTE_BUILD_KEY_PATH => Some(APP_CONFIG.read().pages.build_key_content.clone()), + _ => None, + } + } + + pub fn update_page_content(path: &str, content: PageContent) -> Result<(), &'static str> { + let mut config = APP_CONFIG.write(); + match path { + ROUTE_ROOT_PATH => config.pages.root_content = content, + ROUTE_LOGS_PATH => config.pages.logs_content = content, + ROUTE_CONFIG_PATH => config.pages.config_content = content, + ROUTE_TOKENS_PATH => config.pages.tokeninfo_content = content, + ROUTE_SHARED_STYLES_PATH => config.pages.shared_styles_content = content, + ROUTE_SHARED_JS_PATH => config.pages.shared_js_content = content, + ROUTE_ABOUT_PATH => config.pages.about_content = content, + ROUTE_README_PATH => config.pages.readme_content = content, + ROUTE_API_PATH => config.pages.api_content = content, + ROUTE_BUILD_KEY_PATH => config.pages.build_key_content = content, + _ => return Err(ERR_INVALID_PATH), + } + Ok(()) + } + + pub fn reset_page_content(path: &str) -> Result<(), &'static str> { + let mut config = APP_CONFIG.write(); + match path { + ROUTE_ROOT_PATH => config.pages.root_content = PageContent::default(), + ROUTE_LOGS_PATH => config.pages.logs_content = PageContent::default(), + ROUTE_CONFIG_PATH => config.pages.config_content = PageContent::default(), + ROUTE_TOKENS_PATH => config.pages.tokeninfo_content = PageContent::default(), + ROUTE_SHARED_STYLES_PATH => config.pages.shared_styles_content = PageContent::default(), + ROUTE_SHARED_JS_PATH => config.pages.shared_js_content = PageContent::default(), + ROUTE_ABOUT_PATH => config.pages.about_content = PageContent::default(), + ROUTE_README_PATH => config.pages.readme_content = PageContent::default(), + ROUTE_API_PATH => config.pages.api_content = PageContent::default(), + ROUTE_BUILD_KEY_PATH => config.pages.build_key_content = PageContent::default(), + _ => return Err(ERR_INVALID_PATH), + } + Ok(()) + } + + pub fn is_share() -> bool { + APP_CONFIG.read().is_share + } + pub fn save_config() -> Result<(), Box> { let pages = APP_CONFIG.read().pages.clone(); let bytes = rkyv::to_bytes::<_, 256>(&pages)?; @@ -72,7 +251,8 @@ impl AppConfig { .read(true) .write(true) .create(true) - .open(PAGES_FILE_PATH.as_str())?; + .truncate(true) + .open(&*CONFIG_FILE_PATH)?; // 添加大小检查 if bytes.len() > usize::MAX / 2 { @@ -89,7 +269,7 @@ impl AppConfig { } pub fn load_saved_config() -> Result<(), Box> { - let file = match OpenOptions::new().read(true).open(PAGES_FILE_PATH.as_str()) { + let file = match OpenOptions::new().read(true).open(&*CONFIG_FILE_PATH) { Ok(file) => file, Err(e) if e.kind() == std::io::ErrorKind::NotFound => { return Ok(()); diff --git a/src/app/model/proxies.rs b/src/app/model/proxies.rs index b117e4a..ba8cefd 100644 --- a/src/app/model/proxies.rs +++ b/src/app/model/proxies.rs @@ -1,6 +1,6 @@ use reqwest::{Client, Proxy}; -use serde::{Serialize, Serializer}; use serde::{Deserialize, Deserializer}; +use serde::{Serialize, Serializer}; // use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; use crate::app::constant::COMMA_STRING; diff --git a/src/app/model/usage_check.rs b/src/app/model/usage_check.rs index 7e04498..18aa8e4 100644 --- a/src/app/model/usage_check.rs +++ b/src/app/model/usage_check.rs @@ -1,6 +1,6 @@ use crate::{ app::constant::{COMMA, COMMA_STRING}, - chat::{config::key_config, constant::AVAILABLE_MODELS}, + chat::{config::key_config, constant::Models}, }; use serde::{Deserialize, Serialize}; // use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; @@ -10,7 +10,7 @@ pub enum UsageCheck { None, Default, All, - Custom(Vec<&'static str>), + Custom(Vec), } impl UsageCheck { @@ -21,10 +21,10 @@ impl UsageCheck { Type::Default | Type::Disabled => Self::None, Type::All => Self::All, Type::Custom => { - let models: Vec<&'static str> = model + let models: Vec<_> = model .model_ids .iter() - .filter_map(|id| AVAILABLE_MODELS.iter().find(|m| m.id == id).map(|m| m.id)) + .filter_map(|id| Models::find_id(id)) .collect(); if models.is_empty() { Self::None @@ -119,14 +119,11 @@ impl<'de> Deserialize<'de> for UsageCheck { return Ok(UsageCheck::None); } - let models: Vec<&'static str> = list + let models: Vec<_> = list .split(COMMA) .filter_map(|model| { let model = model.trim(); - AVAILABLE_MODELS - .iter() - .find(|m| m.id == model) - .map(|m| m.id) + Models::find_id(model) }) .collect(); @@ -150,14 +147,11 @@ impl UsageCheck { if list.is_empty() { return Self::default(); } - let models: Vec<&'static str> = list + let models: Vec<_> = list .split(COMMA) .filter_map(|model| { let model = model.trim(); - AVAILABLE_MODELS - .iter() - .find(|m| m.id == model) - .map(|m| m.id) + Models::find_id(model) }) .collect(); diff --git a/src/app/model/vision_ability.rs b/src/app/model/vision_ability.rs new file mode 100644 index 0000000..35e5494 --- /dev/null +++ b/src/app/model/vision_ability.rs @@ -0,0 +1,32 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Copy, PartialEq)] +pub enum VisionAbility { + #[serde(rename = "none", alias = "disabled")] + None, + #[serde(rename = "base64", alias = "base64-only")] + Base64, + #[serde(rename = "all", alias = "base64-http")] + All, +} + +impl VisionAbility { + pub fn from_str(s: &str) -> Self { + match s.to_lowercase().as_str() { + "none" | "disabled" => Self::None, + "base64" | "base64-only" => Self::Base64, + "all" | "base64-http" => Self::All, + _ => Self::default(), + } + } + + pub fn is_none(&self) -> bool { + matches!(self, VisionAbility::None) + } +} + +impl Default for VisionAbility { + fn default() -> Self { + Self::Base64 + } +} diff --git a/src/chat/adapter.rs b/src/chat/adapter.rs index 2499451..2c9c5e3 100644 --- a/src/chat/adapter.rs +++ b/src/chat/adapter.rs @@ -1,6 +1,6 @@ -use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; +use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64}; use image::guess_format; -use prost::Message as _; +use rand::Rng as _; use reqwest::Client; use uuid::Uuid; @@ -10,17 +10,79 @@ use crate::{ lazy::DEFAULT_INSTRUCTIONS, model::{AppConfig, VisionAbility}, }, - common::client::HTTP_CLIENT, + common::{client::HTTP_CLIENT, utils::encode_message}, }; use super::{ aiserver::v1::{ - conversation_message, image_proto, AzureState, ChatExternalLink, ConversationMessage, ExplicitContext, GetChatRequest, ImageProto, ModelDetails + AzureState, ChatExternalLink, ConversationMessage, ExplicitContext, GetChatRequest, + ImageProto, ModelDetails, WebReference, conversation_message, image_proto, }, constant::{ERR_UNSUPPORTED_GIF, ERR_UNSUPPORTED_IMAGE_FORMAT, LONG_CONTEXT_MODELS}, model::{Message, MessageContent, Role}, }; +fn parse_web_references(text: &str) -> Vec { + let mut web_refs = Vec::new(); + let lines = text.lines().skip(1); // 跳过 "WebReferences:" 行 + + for line in lines { + let line = line.trim(); + if line.is_empty() { + break; + } + + // 跳过序号和空格 + let mut chars = line.chars(); + for c in chars.by_ref() { + if c == '.' { + break; + } + } + let remaining = chars.as_str().trim_start(); + + // 解析 [title](url) 部分 + if !remaining.starts_with('[') { + continue; + } + + let mut title = String::new(); + let mut url = String::new(); + let mut chunk = String::new(); + let mut current = &mut title; + let mut state = 0; // 0: title, 1: url, 2: chunk + + let mut chars = remaining.chars(); + chars.next(); // 跳过 '[' + + while let Some(c) = chars.next() { + match (state, c) { + (0, ']') => { + state = 1; + if chars.next() != Some('(') { + break; + } + current = &mut url; + } + (1, ')') => { + state = 2; + if chars.next() == Some('<') { + current = &mut chunk; + } else { + break; + } + } + (2, '>') => break, + (_, c) => current.push(c), + } + } + + web_refs.push(WebReference { title, url, chunk }); + } + + web_refs +} + async fn process_chat_inputs( inputs: Vec, disable_vision: bool, @@ -96,6 +158,17 @@ async fn process_chat_inputs( is_agentic: false, file_diff_trajectories: vec![], conversation_summary: None, + existed_subsequent_terminal_command: false, + existed_previous_terminal_command: false, + docs_references: vec![], + web_references: vec![], + git_context: None, + attached_folders_list_dir_results: vec![], + cached_conversation_summary: None, + human_changes: vec![], + attached_human_changes: false, + summarized_composers: vec![], + cursor_rules: vec![], }], vec![], ); @@ -119,7 +192,7 @@ async fn process_chat_inputs( // 如果第一条是 assistant,插入空的 user 消息 if chat_inputs .first() - .map_or(false, |input| input.role == Role::Assistant) + .is_some_and(|input| input.role == Role::Assistant) { chat_inputs.insert( 0, @@ -153,7 +226,7 @@ async fn process_chat_inputs( // 确保最后一条是 user if chat_inputs .last() - .map_or(false, |input| input.role == Role::Assistant) + .is_some_and(|input| input.role == Role::Assistant) { chat_inputs.push(Message { role: Role::User, @@ -201,6 +274,21 @@ async fn process_chat_inputs( } }; + let (text, web_references) = + if input.role == Role::Assistant && text.starts_with("WebReferences:") { + if let Some(pos) = text.find("\n\n") { + let (web_refs_text, content_text) = text.split_at(pos); + ( + content_text[2..].to_string(), // 跳过 "\n\n" + parse_web_references(web_refs_text), + ) + } else { + (text, vec![]) + } + } else { + (text, vec![]) + }; + messages.push(ConversationMessage { text, r#type: if input.role == Role::User { @@ -238,6 +326,17 @@ async fn process_chat_inputs( is_agentic: false, file_diff_trajectories: vec![], conversation_summary: None, + existed_subsequent_terminal_command: false, + existed_previous_terminal_command: false, + docs_references: vec![], + web_references, + git_context: None, + attached_folders_list_dir_results: vec![], + cached_conversation_summary: None, + human_changes: vec![], + attached_human_changes: false, + summarized_composers: vec![], + cursor_rules: vec![], }); } @@ -246,7 +345,7 @@ async fn process_chat_inputs( if last_msg.r#type == conversation_message::MessageType::Human as i32 { let text = &last_msg.text; let mut chars = text.chars().peekable(); - + while let Some(c) = chars.next() { if c == '@' { let mut url = String::new(); @@ -387,13 +486,7 @@ pub async fn encode_chat_message( is_search: bool, ) -> Result, Box> { // 在进入异步操作前获取并释放锁 - let enable_slow_pool = { - if enable_slow_pool { - Some(true) - } else { - None - } - }; + let enable_slow_pool = { if enable_slow_pool { Some(true) } else { None } }; let (instructions, messages, urls) = process_chat_inputs(inputs, disable_vision).await; @@ -401,19 +494,24 @@ pub async fn encode_chat_message( Some(ExplicitContext { context: instructions, repo_context: None, + rules: vec![], }) } else { None }; - let base_uuid = rand::random::(); - let external_links = urls.into_iter().enumerate().map(|(i, url)| { - let uuid = base_uuid.wrapping_add(i as u16); - ChatExternalLink { - url, - uuid: uuid.to_string(), - } - }).collect(); + let base_uuid = rand::rng().random::(); + let external_links = urls + .into_iter() + .enumerate() + .map(|(i, url)| { + let uuid = base_uuid.wrapping_add(i as u16); + ChatExternalLink { + url, + uuid: uuid.to_string(), + } + }) + .collect(); let chat = GetChatRequest { current_file: None, @@ -461,13 +559,9 @@ pub async fn encode_chat_message( is_composer: None, runnable_code_blocks: Some(false), should_cache: Some(false), + allow_model_fallbacks: None, + number_of_times_shown_fallback_model_warning: None, }; - let mut encoded = Vec::new(); - chat.encode(&mut encoded)?; - - let len_prefix = format!("{:010x}", encoded.len()).to_uppercase(); - let content = hex::encode_upper(&encoded); - - Ok(hex::decode(len_prefix + &content)?) + encode_message(&chat, true) } diff --git a/src/chat/aiserver/v1.rs b/src/chat/aiserver/v1.rs index 5660b2d..711cca0 100644 --- a/src/chat/aiserver/v1.rs +++ b/src/chat/aiserver/v1.rs @@ -1,4 +1,5 @@ -include!(concat!(env!("OUT_DIR"), "/aiserver.v1.rs")); +// include!(concat!(env!("OUT_DIR"), "/aiserver.v1.rs")); +include!("v1/aiserver.v1.rs"); use error_details::Error; impl ErrorDetails { @@ -7,6 +8,7 @@ impl ErrorDetails { Ok(error) => match error { Error::Unspecified => 500, Error::BadApiKey + | Error::BadUserApiKey | Error::InvalidAuthId | Error::AuthTokenNotFound | Error::AuthTokenExpired @@ -33,7 +35,9 @@ impl ErrorDetails { | Error::BadModelName | Error::SlashEditFileTooLong | Error::FileUnsupported - | Error::ClaudeImageTooLarge => 400, + | Error::ClaudeImageTooLarge + | Error::ConversationTooLong => 400, + Error::Timeout => 504, Error::Deprecated | Error::FreeUserUsageLimit | Error::ProUserUsageLimit diff --git a/src/chat/aiserver/v1/aiserver.v1.rs b/src/chat/aiserver/v1/aiserver.v1.rs new file mode 100644 index 0000000..ddf6daf --- /dev/null +++ b/src/chat/aiserver/v1/aiserver.v1.rs @@ -0,0 +1,4149 @@ +// This file is @generated by prost-build. +/// aiserver.v1.AvailableModelsRequest +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct AvailableModelsRequest { + #[prost(bool, tag = "1")] + pub is_nightly: bool, + #[prost(bool, tag = "2")] + pub include_long_context_models: bool, +} +/// aiserver.v1.AvailableModelsResponse +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AvailableModelsResponse { + #[prost(message, repeated, tag = "2")] + pub models: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag = "1")] + pub model_names: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +/// Nested message and enum types in `AvailableModelsResponse`. +pub mod available_models_response { + /// aiserver.v1.AvailableModelsResponse.AvailableModel + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct AvailableModel { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub default_on: bool, + #[prost(bool, optional, tag = "3")] + pub is_long_context_only: ::core::option::Option, + #[prost(bool, optional, tag = "4")] + pub is_chat_only: ::core::option::Option, + } +} +/// aiserver.v1.ErrorDetails +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ErrorDetails { + #[prost(enumeration = "error_details::Error", tag = "1")] + pub error: i32, + #[prost(message, optional, tag = "2")] + pub details: ::core::option::Option, + #[prost(bool, optional, tag = "3")] + pub is_expected: ::core::option::Option, +} +/// Nested message and enum types in `ErrorDetails`. +pub mod error_details { + /// aiserver.v1.ErrorDetails.Error + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Error { + Unspecified = 0, + BadApiKey = 1, + BadUserApiKey = 42, + NotLoggedIn = 2, + InvalidAuthId = 3, + NotHighEnoughPermissions = 4, + AgentRequiresLogin = 18, + BadModelName = 5, + NotFound = 39, + Deprecated = 40, + UserNotFound = 6, + FreeUserRateLimitExceeded = 7, + ProUserRateLimitExceeded = 8, + FreeUserUsageLimit = 9, + ProUserUsageLimit = 10, + ResourceExhausted = 41, + AuthTokenNotFound = 11, + AuthTokenExpired = 12, + Openai = 13, + OpenaiRateLimitExceeded = 14, + OpenaiAccountLimitExceeded = 15, + TaskUuidNotFound = 16, + TaskNoPermissions = 17, + AgentEngineNotFound = 19, + MaxTokens = 20, + ProUserOnly = 23, + ApiKeyNotSupported = 24, + UserAbortedRequest = 21, + Timeout = 25, + GenericRateLimitExceeded = 22, + SlashEditFileTooLong = 26, + FileUnsupported = 27, + Gpt4VisionPreviewRateLimit = 28, + CustomMessage = 29, + OutdatedClient = 30, + ClaudeImageTooLarge = 31, + GitgraphNotFound = 32, + FileNotFound = 33, + ApiKeyRateLimit = 34, + Debounced = 35, + BadRequest = 36, + RepositoryServiceRepositoryIsNotInitialized = 37, + Unauthorized = 38, + ConversationTooLong = 43, + } + impl Error { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "ERROR_UNSPECIFIED", + Self::BadApiKey => "ERROR_BAD_API_KEY", + Self::BadUserApiKey => "ERROR_BAD_USER_API_KEY", + Self::NotLoggedIn => "ERROR_NOT_LOGGED_IN", + Self::InvalidAuthId => "ERROR_INVALID_AUTH_ID", + Self::NotHighEnoughPermissions => "ERROR_NOT_HIGH_ENOUGH_PERMISSIONS", + Self::AgentRequiresLogin => "ERROR_AGENT_REQUIRES_LOGIN", + Self::BadModelName => "ERROR_BAD_MODEL_NAME", + Self::NotFound => "ERROR_NOT_FOUND", + Self::Deprecated => "ERROR_DEPRECATED", + Self::UserNotFound => "ERROR_USER_NOT_FOUND", + Self::FreeUserRateLimitExceeded => "ERROR_FREE_USER_RATE_LIMIT_EXCEEDED", + Self::ProUserRateLimitExceeded => "ERROR_PRO_USER_RATE_LIMIT_EXCEEDED", + Self::FreeUserUsageLimit => "ERROR_FREE_USER_USAGE_LIMIT", + Self::ProUserUsageLimit => "ERROR_PRO_USER_USAGE_LIMIT", + Self::ResourceExhausted => "ERROR_RESOURCE_EXHAUSTED", + Self::AuthTokenNotFound => "ERROR_AUTH_TOKEN_NOT_FOUND", + Self::AuthTokenExpired => "ERROR_AUTH_TOKEN_EXPIRED", + Self::Openai => "ERROR_OPENAI", + Self::OpenaiRateLimitExceeded => "ERROR_OPENAI_RATE_LIMIT_EXCEEDED", + Self::OpenaiAccountLimitExceeded => "ERROR_OPENAI_ACCOUNT_LIMIT_EXCEEDED", + Self::TaskUuidNotFound => "ERROR_TASK_UUID_NOT_FOUND", + Self::TaskNoPermissions => "ERROR_TASK_NO_PERMISSIONS", + Self::AgentEngineNotFound => "ERROR_AGENT_ENGINE_NOT_FOUND", + Self::MaxTokens => "ERROR_MAX_TOKENS", + Self::ProUserOnly => "ERROR_PRO_USER_ONLY", + Self::ApiKeyNotSupported => "ERROR_API_KEY_NOT_SUPPORTED", + Self::UserAbortedRequest => "ERROR_USER_ABORTED_REQUEST", + Self::Timeout => "ERROR_TIMEOUT", + Self::GenericRateLimitExceeded => "ERROR_GENERIC_RATE_LIMIT_EXCEEDED", + Self::SlashEditFileTooLong => "ERROR_SLASH_EDIT_FILE_TOO_LONG", + Self::FileUnsupported => "ERROR_FILE_UNSUPPORTED", + Self::Gpt4VisionPreviewRateLimit => { + "ERROR_GPT_4_VISION_PREVIEW_RATE_LIMIT" + } + Self::CustomMessage => "ERROR_CUSTOM_MESSAGE", + Self::OutdatedClient => "ERROR_OUTDATED_CLIENT", + Self::ClaudeImageTooLarge => "ERROR_CLAUDE_IMAGE_TOO_LARGE", + Self::GitgraphNotFound => "ERROR_GITGRAPH_NOT_FOUND", + Self::FileNotFound => "ERROR_FILE_NOT_FOUND", + Self::ApiKeyRateLimit => "ERROR_API_KEY_RATE_LIMIT", + Self::Debounced => "ERROR_DEBOUNCED", + Self::BadRequest => "ERROR_BAD_REQUEST", + Self::RepositoryServiceRepositoryIsNotInitialized => { + "ERROR_REPOSITORY_SERVICE_REPOSITORY_IS_NOT_INITIALIZED" + } + Self::Unauthorized => "ERROR_UNAUTHORIZED", + Self::ConversationTooLong => "ERROR_CONVERSATION_TOO_LONG", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ERROR_UNSPECIFIED" => Some(Self::Unspecified), + "ERROR_BAD_API_KEY" => Some(Self::BadApiKey), + "ERROR_BAD_USER_API_KEY" => Some(Self::BadUserApiKey), + "ERROR_NOT_LOGGED_IN" => Some(Self::NotLoggedIn), + "ERROR_INVALID_AUTH_ID" => Some(Self::InvalidAuthId), + "ERROR_NOT_HIGH_ENOUGH_PERMISSIONS" => { + Some(Self::NotHighEnoughPermissions) + } + "ERROR_AGENT_REQUIRES_LOGIN" => Some(Self::AgentRequiresLogin), + "ERROR_BAD_MODEL_NAME" => Some(Self::BadModelName), + "ERROR_NOT_FOUND" => Some(Self::NotFound), + "ERROR_DEPRECATED" => Some(Self::Deprecated), + "ERROR_USER_NOT_FOUND" => Some(Self::UserNotFound), + "ERROR_FREE_USER_RATE_LIMIT_EXCEEDED" => { + Some(Self::FreeUserRateLimitExceeded) + } + "ERROR_PRO_USER_RATE_LIMIT_EXCEEDED" => { + Some(Self::ProUserRateLimitExceeded) + } + "ERROR_FREE_USER_USAGE_LIMIT" => Some(Self::FreeUserUsageLimit), + "ERROR_PRO_USER_USAGE_LIMIT" => Some(Self::ProUserUsageLimit), + "ERROR_RESOURCE_EXHAUSTED" => Some(Self::ResourceExhausted), + "ERROR_AUTH_TOKEN_NOT_FOUND" => Some(Self::AuthTokenNotFound), + "ERROR_AUTH_TOKEN_EXPIRED" => Some(Self::AuthTokenExpired), + "ERROR_OPENAI" => Some(Self::Openai), + "ERROR_OPENAI_RATE_LIMIT_EXCEEDED" => Some(Self::OpenaiRateLimitExceeded), + "ERROR_OPENAI_ACCOUNT_LIMIT_EXCEEDED" => { + Some(Self::OpenaiAccountLimitExceeded) + } + "ERROR_TASK_UUID_NOT_FOUND" => Some(Self::TaskUuidNotFound), + "ERROR_TASK_NO_PERMISSIONS" => Some(Self::TaskNoPermissions), + "ERROR_AGENT_ENGINE_NOT_FOUND" => Some(Self::AgentEngineNotFound), + "ERROR_MAX_TOKENS" => Some(Self::MaxTokens), + "ERROR_PRO_USER_ONLY" => Some(Self::ProUserOnly), + "ERROR_API_KEY_NOT_SUPPORTED" => Some(Self::ApiKeyNotSupported), + "ERROR_USER_ABORTED_REQUEST" => Some(Self::UserAbortedRequest), + "ERROR_TIMEOUT" => Some(Self::Timeout), + "ERROR_GENERIC_RATE_LIMIT_EXCEEDED" => { + Some(Self::GenericRateLimitExceeded) + } + "ERROR_SLASH_EDIT_FILE_TOO_LONG" => Some(Self::SlashEditFileTooLong), + "ERROR_FILE_UNSUPPORTED" => Some(Self::FileUnsupported), + "ERROR_GPT_4_VISION_PREVIEW_RATE_LIMIT" => { + Some(Self::Gpt4VisionPreviewRateLimit) + } + "ERROR_CUSTOM_MESSAGE" => Some(Self::CustomMessage), + "ERROR_OUTDATED_CLIENT" => Some(Self::OutdatedClient), + "ERROR_CLAUDE_IMAGE_TOO_LARGE" => Some(Self::ClaudeImageTooLarge), + "ERROR_GITGRAPH_NOT_FOUND" => Some(Self::GitgraphNotFound), + "ERROR_FILE_NOT_FOUND" => Some(Self::FileNotFound), + "ERROR_API_KEY_RATE_LIMIT" => Some(Self::ApiKeyRateLimit), + "ERROR_DEBOUNCED" => Some(Self::Debounced), + "ERROR_BAD_REQUEST" => Some(Self::BadRequest), + "ERROR_REPOSITORY_SERVICE_REPOSITORY_IS_NOT_INITIALIZED" => { + Some(Self::RepositoryServiceRepositoryIsNotInitialized) + } + "ERROR_UNAUTHORIZED" => Some(Self::Unauthorized), + "ERROR_CONVERSATION_TOO_LONG" => Some(Self::ConversationTooLong), + _ => None, + } + } + } +} +/// aiserver.v1.CustomErrorDetails +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CustomErrorDetails { + #[prost(string, tag = "1")] + pub title: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub detail: ::prost::alloc::string::String, + #[prost(bool, optional, tag = "3")] + pub allow_command_links_potentially_unsafe_please_only_use_for_handwritten_trusted_markdown: ::core::option::Option< + bool, + >, + #[prost(bool, optional, tag = "4")] + pub is_retryable: ::core::option::Option, + #[prost(bool, optional, tag = "5")] + pub show_request_id: ::core::option::Option, + #[prost(bool, optional, tag = "6")] + pub should_show_immediate_error: ::core::option::Option, +} +/// aiserver.v1.GetChatRequest +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetChatRequest { + #[prost(message, optional, tag = "1")] + pub current_file: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub conversation: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub repositories: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "4")] + pub explicit_context: ::core::option::Option, + #[prost(string, optional, tag = "5")] + pub workspace_root_path: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "6")] + pub code_blocks: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "7")] + pub model_details: ::core::option::Option, + #[prost(string, repeated, tag = "8")] + pub documentation_identifiers: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, + #[prost(string, tag = "9")] + pub request_id: ::prost::alloc::string::String, + #[prost(message, optional, tag = "10")] + pub linter_errors: ::core::option::Option, + #[prost(string, optional, tag = "11")] + pub summary: ::core::option::Option<::prost::alloc::string::String>, + #[prost(int32, optional, tag = "12")] + pub summary_up_until_index: ::core::option::Option, + #[prost(bool, optional, tag = "13")] + pub allow_long_file_scan: ::core::option::Option, + #[prost(bool, optional, tag = "14")] + pub is_bash: ::core::option::Option, + #[prost(string, tag = "15")] + pub conversation_id: ::prost::alloc::string::String, + #[prost(bool, optional, tag = "16")] + pub can_handle_filenames_after_language_ids: ::core::option::Option, + #[prost(string, optional, tag = "17")] + pub use_web: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "18")] + pub quotes: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "19")] + pub debug_info: ::core::option::Option, + #[prost(string, optional, tag = "20")] + pub workspace_id: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "21")] + pub external_links: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "23")] + pub commit_notes: ::prost::alloc::vec::Vec, + #[prost(bool, optional, tag = "22")] + pub long_context_mode: ::core::option::Option, + #[prost(bool, optional, tag = "24")] + pub is_eval: ::core::option::Option, + #[prost(int32, optional, tag = "26")] + pub desired_max_tokens: ::core::option::Option, + #[prost(message, optional, tag = "25")] + pub context_ast: ::core::option::Option, + #[prost(bool, optional, tag = "27")] + pub is_composer: ::core::option::Option, + #[prost(bool, optional, tag = "28")] + pub runnable_code_blocks: ::core::option::Option, + #[prost(bool, optional, tag = "29")] + pub should_cache: ::core::option::Option, + #[prost(bool, optional, tag = "30")] + pub allow_model_fallbacks: ::core::option::Option, + #[prost(int32, optional, tag = "31")] + pub number_of_times_shown_fallback_model_warning: ::core::option::Option, +} +/// aiserver.v1.RepositoryInfo +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryInfo { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "2")] + pub remote_urls: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "3")] + pub remote_names: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, tag = "4")] + pub repo_name: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub repo_owner: ::prost::alloc::string::String, + #[prost(bool, tag = "6")] + pub is_tracked: bool, + #[prost(bool, tag = "7")] + pub is_local: bool, + #[prost(int32, optional, tag = "8")] + pub num_files: ::core::option::Option, + #[prost(double, optional, tag = "9")] + pub orthogonal_transform_seed: ::core::option::Option, + #[prost(enumeration = "EmbeddingModel", optional, tag = "10")] + pub preferred_embedding_model: ::core::option::Option, +} +/// aiserver.v1.ModelDetails +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ModelDetails { + #[prost(string, optional, tag = "1")] + pub model_name: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "2")] + pub api_key: ::core::option::Option<::prost::alloc::string::String>, + #[prost(bool, optional, tag = "3")] + pub enable_ghost_mode: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub azure_state: ::core::option::Option, + #[prost(bool, optional, tag = "5")] + pub enable_slow_pool: ::core::option::Option, + #[prost(string, optional, tag = "6")] + pub openai_api_base_url: ::core::option::Option<::prost::alloc::string::String>, +} +/// aiserver.v1.AzureState +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AzureState { + #[prost(string, tag = "1")] + pub api_key: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub base_url: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub deployment: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + pub use_azure: bool, +} +/// aiserver.v1.LinterErrors +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LinterErrors { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub errors: ::prost::alloc::vec::Vec, + #[prost(string, tag = "3")] + pub file_contents: ::prost::alloc::string::String, +} +/// aiserver.v1.LinterError +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LinterError { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub range: ::core::option::Option, + #[prost(string, optional, tag = "3")] + pub source: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "4")] + pub related_information: ::prost::alloc::vec::Vec, + #[prost(enumeration = "diagnostic::DiagnosticSeverity", optional, tag = "5")] + pub severity: ::core::option::Option, +} +/// aiserver.v1.CursorRange +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct CursorRange { + #[prost(message, optional, tag = "1")] + pub start_position: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub end_position: ::core::option::Option, +} +/// aiserver.v1.CursorPosition +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct CursorPosition { + #[prost(int32, tag = "1")] + pub line: i32, + #[prost(int32, tag = "2")] + pub column: i32, +} +/// aiserver.v1.ExplicitContext +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExplicitContext { + #[prost(string, tag = "1")] + pub context: ::prost::alloc::string::String, + #[prost(string, optional, tag = "2")] + pub repo_context: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "3")] + pub rules: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.CursorRule +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CursorRule { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub description: ::prost::alloc::string::String, + #[prost(string, optional, tag = "3")] + pub body: ::core::option::Option<::prost::alloc::string::String>, + #[prost(bool, optional, tag = "4")] + pub is_from_glob: ::core::option::Option, +} +/// aiserver.v1.DebugInfo +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DebugInfo { + #[prost(message, optional, tag = "1")] + pub breakpoint: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub call_stack: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub history: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `DebugInfo`. +pub mod debug_info { + /// aiserver.v1.DebugInfo.Variable + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Variable { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub value: ::prost::alloc::string::String, + #[prost(string, optional, tag = "3")] + pub r#type: ::core::option::Option<::prost::alloc::string::String>, + } + /// aiserver.v1.DebugInfo.Scope + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Scope { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub variables: ::prost::alloc::vec::Vec, + } + /// aiserver.v1.DebugInfo.CallStackFrame + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct CallStackFrame { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub line_number: i32, + #[prost(string, tag = "3")] + pub function_name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub scopes: ::prost::alloc::vec::Vec, + } + /// aiserver.v1.DebugInfo.Breakpoint + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Breakpoint { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub line_number: i32, + #[prost(string, repeated, tag = "3")] + pub lines_before_breakpoint: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, + #[prost(string, repeated, tag = "4")] + pub lines_after_breakpoint: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, + #[prost(string, optional, tag = "5")] + pub exception_info: ::core::option::Option<::prost::alloc::string::String>, + } +} +/// aiserver.v1.CodeBlock +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CodeBlock { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(string, optional, tag = "2")] + pub file_contents: ::core::option::Option<::prost::alloc::string::String>, + #[prost(int32, optional, tag = "9")] + pub file_contents_length: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub range: ::core::option::Option, + #[prost(string, tag = "4")] + pub contents: ::prost::alloc::string::String, + #[prost(message, optional, tag = "5")] + pub signatures: ::core::option::Option, + #[prost(string, optional, tag = "6")] + pub override_contents: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "7")] + pub original_contents: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "8")] + pub detailed_lines: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `CodeBlock`. +pub mod code_block { + /// aiserver.v1.CodeBlock.Signatures + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Signatures { + #[prost(message, repeated, tag = "1")] + pub ranges: ::prost::alloc::vec::Vec, + } +} +/// aiserver.v1.DetailedLine +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DetailedLine { + #[prost(string, tag = "1")] + pub text: ::prost::alloc::string::String, + #[prost(float, tag = "2")] + pub line_number: f32, + #[prost(bool, tag = "3")] + pub is_signature: bool, +} +/// aiserver.v1.CurrentFileInfo +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CurrentFileInfo { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub contents: ::prost::alloc::string::String, + #[prost(bool, tag = "18")] + pub rely_on_filesync: bool, + #[prost(string, optional, tag = "17")] + pub sha_256_hash: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "16")] + pub cells: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "10")] + pub top_chunks: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "9")] + pub contents_start_at_line: i32, + #[prost(message, optional, tag = "3")] + pub cursor_position: ::core::option::Option, + #[prost(message, repeated, tag = "4")] + pub dataframes: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "8")] + pub total_number_of_lines: i32, + #[prost(string, tag = "5")] + pub language_id: ::prost::alloc::string::String, + #[prost(message, optional, tag = "6")] + pub selection: ::core::option::Option, + #[prost(int32, optional, tag = "11")] + pub alternative_version_id: ::core::option::Option, + #[prost(message, repeated, tag = "7")] + pub diagnostics: ::prost::alloc::vec::Vec, + #[prost(int32, optional, tag = "14")] + pub file_version: ::core::option::Option, + #[prost(int32, repeated, tag = "15")] + pub cell_start_lines: ::prost::alloc::vec::Vec, + #[prost(string, tag = "19")] + pub workspace_root_path: ::prost::alloc::string::String, +} +/// Nested message and enum types in `CurrentFileInfo`. +pub mod current_file_info { + /// aiserver.v1.CurrentFileInfo.NotebookCell + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct NotebookCell {} +} +/// aiserver.v1.Diagnostic +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Diagnostic { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub range: ::core::option::Option, + #[prost(enumeration = "diagnostic::DiagnosticSeverity", tag = "3")] + pub severity: i32, + #[prost(message, repeated, tag = "4")] + pub related_information: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `Diagnostic`. +pub mod diagnostic { + /// aiserver.v1.Diagnostic.RelatedInformation + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct RelatedInformation { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub range: ::core::option::Option, + } + /// aiserver.v1.Diagnostic.DiagnosticSeverity + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum DiagnosticSeverity { + Unspecified = 0, + Error = 1, + Warning = 2, + Information = 3, + Hint = 4, + } + impl DiagnosticSeverity { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "DIAGNOSTIC_SEVERITY_UNSPECIFIED", + Self::Error => "DIAGNOSTIC_SEVERITY_ERROR", + Self::Warning => "DIAGNOSTIC_SEVERITY_WARNING", + Self::Information => "DIAGNOSTIC_SEVERITY_INFORMATION", + Self::Hint => "DIAGNOSTIC_SEVERITY_HINT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DIAGNOSTIC_SEVERITY_UNSPECIFIED" => Some(Self::Unspecified), + "DIAGNOSTIC_SEVERITY_ERROR" => Some(Self::Error), + "DIAGNOSTIC_SEVERITY_WARNING" => Some(Self::Warning), + "DIAGNOSTIC_SEVERITY_INFORMATION" => Some(Self::Information), + "DIAGNOSTIC_SEVERITY_HINT" => Some(Self::Hint), + _ => None, + } + } + } +} +/// aiserver.v1.DataframeInfo +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DataframeInfo { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub shape: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub data_dimensionality: i32, + #[prost(message, repeated, tag = "6")] + pub columns: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "7")] + pub row_count: i32, + #[prost(string, tag = "8")] + pub index_column: ::prost::alloc::string::String, +} +/// Nested message and enum types in `DataframeInfo`. +pub mod dataframe_info { + /// aiserver.v1.DataframeInfo.Column + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Column { + #[prost(string, tag = "1")] + pub key: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub r#type: ::prost::alloc::string::String, + } +} +/// aiserver.v1.BM25Chunk +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Bm25Chunk { + #[prost(string, tag = "1")] + pub content: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub range: ::core::option::Option, + #[prost(int32, tag = "3")] + pub score: i32, + #[prost(string, tag = "4")] + pub relative_path: ::prost::alloc::string::String, +} +/// aiserver.v1.SimplestRange +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct SimplestRange { + #[prost(int32, tag = "1")] + pub start_line: i32, + #[prost(int32, tag = "2")] + pub end_line_inclusive: i32, +} +/// aiserver.v1.ConversationMessage +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConversationMessage { + #[prost(string, tag = "1")] + pub text: ::prost::alloc::string::String, + #[prost(enumeration = "conversation_message::MessageType", tag = "2")] + pub r#type: i32, + #[prost(message, repeated, tag = "3")] + pub attached_code_chunks: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub codebase_context_chunks: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub commits: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub pull_requests: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "7")] + pub git_diffs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "8")] + pub assistant_suggested_diffs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "9")] + pub interpreter_results: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "10")] + pub images: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag = "11")] + pub attached_folders: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "12")] + pub approximate_lint_errors: ::prost::alloc::vec::Vec< + conversation_message::ApproximateLintError, + >, + #[prost(string, tag = "13")] + pub bubble_id: ::prost::alloc::string::String, + #[prost(string, optional, tag = "32")] + pub server_bubble_id: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "14")] + pub attached_folders_new: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "15")] + pub lints: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "16")] + pub user_responses_to_suggested_code_blocks: ::prost::alloc::vec::Vec< + UserResponseToSuggestedCodeBlock, + >, + #[prost(string, repeated, tag = "17")] + pub relevant_files: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "18")] + pub tool_results: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "19")] + pub notepads: ::prost::alloc::vec::Vec, + #[prost(bool, optional, tag = "20")] + pub is_capability_iteration: ::core::option::Option, + #[prost(message, repeated, tag = "21")] + pub capabilities: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "22")] + pub edit_trail_contexts: ::prost::alloc::vec::Vec< + conversation_message::EditTrailContext, + >, + #[prost(message, repeated, tag = "23")] + pub suggested_code_blocks: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "24")] + pub diffs_for_compressing_files: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "25")] + pub multi_file_linter_errors: ::prost::alloc::vec::Vec< + LinterErrorsWithoutFileContents, + >, + #[prost(message, repeated, tag = "26")] + pub diff_histories: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "27")] + pub recently_viewed_files: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "28")] + pub recent_locations_history: ::prost::alloc::vec::Vec< + conversation_message::RecentLocation, + >, + #[prost(bool, tag = "29")] + pub is_agentic: bool, + #[prost(message, repeated, tag = "30")] + pub file_diff_trajectories: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "31")] + pub conversation_summary: ::core::option::Option, + #[prost(bool, tag = "33")] + pub existed_subsequent_terminal_command: bool, + #[prost(bool, tag = "34")] + pub existed_previous_terminal_command: bool, + #[prost(message, repeated, tag = "35")] + pub docs_references: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "36")] + pub web_references: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "37")] + pub git_context: ::core::option::Option, + #[prost(message, repeated, tag = "38")] + pub attached_folders_list_dir_results: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "39")] + pub cached_conversation_summary: ::core::option::Option, + #[prost(message, repeated, tag = "40")] + pub human_changes: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "41")] + pub attached_human_changes: bool, + #[prost(message, repeated, tag = "42")] + pub summarized_composers: ::prost::alloc::vec::Vec< + conversation_message::ComposerContext, + >, + #[prost(message, repeated, tag = "43")] + pub cursor_rules: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `ConversationMessage`. +pub mod conversation_message { + /// aiserver.v1.ConversationMessage.CodeChunk + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct CodeChunk { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub start_line_number: i32, + #[prost(string, repeated, tag = "3")] + pub lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(enumeration = "code_chunk::SummarizationStrategy", optional, tag = "4")] + pub summarization_strategy: ::core::option::Option, + #[prost(string, tag = "5")] + pub language_identifier: ::prost::alloc::string::String, + #[prost(enumeration = "code_chunk::Intent", optional, tag = "6")] + pub intent: ::core::option::Option, + #[prost(bool, optional, tag = "7")] + pub is_final_version: ::core::option::Option, + #[prost(bool, optional, tag = "8")] + pub is_first_version: ::core::option::Option, + #[prost(bool, optional, tag = "9")] + pub contents_are_missing: ::core::option::Option, + } + /// Nested message and enum types in `CodeChunk`. + pub mod code_chunk { + /// aiserver.v1.ConversationMessage.CodeChunk.Intent + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Intent { + Unspecified = 0, + ComposerFile = 1, + CompressedComposerFile = 2, + RecentlyViewedFile = 3, + Outline = 4, + MentionedFile = 5, + CodeSelection = 6, + } + impl Intent { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "INTENT_UNSPECIFIED", + Self::ComposerFile => "INTENT_COMPOSER_FILE", + Self::CompressedComposerFile => "INTENT_COMPRESSED_COMPOSER_FILE", + Self::RecentlyViewedFile => "INTENT_RECENTLY_VIEWED_FILE", + Self::Outline => "INTENT_OUTLINE", + Self::MentionedFile => "INTENT_MENTIONED_FILE", + Self::CodeSelection => "INTENT_CODE_SELECTION", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "INTENT_UNSPECIFIED" => Some(Self::Unspecified), + "INTENT_COMPOSER_FILE" => Some(Self::ComposerFile), + "INTENT_COMPRESSED_COMPOSER_FILE" => { + Some(Self::CompressedComposerFile) + } + "INTENT_RECENTLY_VIEWED_FILE" => Some(Self::RecentlyViewedFile), + "INTENT_OUTLINE" => Some(Self::Outline), + "INTENT_MENTIONED_FILE" => Some(Self::MentionedFile), + "INTENT_CODE_SELECTION" => Some(Self::CodeSelection), + _ => None, + } + } + } + /// aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum SummarizationStrategy { + NoneUnspecified = 0, + Summarized = 1, + Embedded = 2, + } + impl SummarizationStrategy { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::NoneUnspecified => "SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED", + Self::Summarized => "SUMMARIZATION_STRATEGY_SUMMARIZED", + Self::Embedded => "SUMMARIZATION_STRATEGY_EMBEDDED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED" => { + Some(Self::NoneUnspecified) + } + "SUMMARIZATION_STRATEGY_SUMMARIZED" => Some(Self::Summarized), + "SUMMARIZATION_STRATEGY_EMBEDDED" => Some(Self::Embedded), + _ => None, + } + } + } + } + /// aiserver.v1.ConversationMessage.ToolResult + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ToolResult { + #[prost(string, tag = "1")] + pub tool_call_id: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub tool_name: ::prost::alloc::string::String, + #[prost(uint32, tag = "3")] + pub tool_index: u32, + #[prost(string, tag = "4")] + pub args: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub raw_args: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "6")] + pub attached_code_chunks: ::prost::alloc::vec::Vec, + #[prost(string, optional, tag = "7")] + pub content: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, optional, tag = "8")] + pub result: ::core::option::Option, + #[prost(message, optional, tag = "9")] + pub error: ::core::option::Option, + #[prost(message, repeated, tag = "10")] + pub images: ::prost::alloc::vec::Vec, + } + /// aiserver.v1.ConversationMessage.MultiRangeCodeChunk + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct MultiRangeCodeChunk { + #[prost(message, repeated, tag = "1")] + pub ranges: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + pub content: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub relative_workspace_path: ::prost::alloc::string::String, + } + /// Nested message and enum types in `MultiRangeCodeChunk`. + pub mod multi_range_code_chunk { + /// aiserver.v1.ConversationMessage.MultiRangeCodeChunk.RangeWithPriority + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct RangeWithPriority { + #[prost(message, optional, tag = "1")] + pub range: ::core::option::Option, + #[prost(double, tag = "2")] + pub priority: f64, + } + } + /// aiserver.v1.ConversationMessage.NotepadContext + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct NotepadContext { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub text: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub attached_code_chunks: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag = "4")] + pub attached_folders: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "5")] + pub commits: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub pull_requests: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "7")] + pub git_diffs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "8")] + pub images: ::prost::alloc::vec::Vec, + } + /// aiserver.v1.ConversationMessage.ComposerContext + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ComposerContext { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub conversation_summary: ::core::option::Option, + } + /// aiserver.v1.ConversationMessage.EditLocation + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct EditLocation { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, optional, tag = "3")] + pub range: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub initial_range: ::core::option::Option, + #[prost(string, tag = "5")] + pub context_lines: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub text: ::prost::alloc::string::String, + #[prost(message, optional, tag = "7")] + pub text_range: ::core::option::Option, + } + /// aiserver.v1.ConversationMessage.EditTrailContext + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct EditTrailContext { + #[prost(string, tag = "1")] + pub unique_id: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub edit_trail_sorted: ::prost::alloc::vec::Vec, + } + /// aiserver.v1.ConversationMessage.ApproximateLintError + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ApproximateLintError { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub value: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub start_line: i32, + #[prost(int32, tag = "4")] + pub end_line: i32, + #[prost(int32, tag = "5")] + pub start_column: i32, + #[prost(int32, tag = "6")] + pub end_column: i32, + } + /// aiserver.v1.ConversationMessage.Lints + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Lints { + #[prost(message, optional, tag = "1")] + pub lints: ::core::option::Option, + #[prost(string, tag = "2")] + pub chat_codeblock_model_value: ::prost::alloc::string::String, + } + /// aiserver.v1.ConversationMessage.RecentLocation + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct RecentLocation { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub line_number: i32, + } + /// aiserver.v1.ConversationMessage.RenderedDiff + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct RenderedDiff { + #[prost(int32, tag = "1")] + pub start_line_number: i32, + #[prost(int32, tag = "2")] + pub end_line_number_exclusive: i32, + #[prost(string, repeated, tag = "3")] + pub before_context_lines: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, + #[prost(string, repeated, tag = "4")] + pub removed_lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "5")] + pub added_lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "6")] + pub after_context_lines: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, + } + /// aiserver.v1.ConversationMessage.HumanChange + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct HumanChange { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub rendered_diffs: ::prost::alloc::vec::Vec, + } + /// aiserver.v1.ConversationMessage.MessageType + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum MessageType { + Unspecified = 0, + Human = 1, + Ai = 2, + } + impl MessageType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "MESSAGE_TYPE_UNSPECIFIED", + Self::Human => "MESSAGE_TYPE_HUMAN", + Self::Ai => "MESSAGE_TYPE_AI", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "MESSAGE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "MESSAGE_TYPE_HUMAN" => Some(Self::Human), + "MESSAGE_TYPE_AI" => Some(Self::Ai), + _ => None, + } + } + } +} +/// aiserver.v1.WebReference +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WebReference { + #[prost(string, tag = "2")] + pub title: ::prost::alloc::string::String, + #[prost(string, tag = "1")] + pub url: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub chunk: ::prost::alloc::string::String, +} +/// aiserver.v1.ViewableGitContext +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ViewableGitContext { + #[prost(message, optional, tag = "1")] + pub commit_data: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub pull_request_data: ::core::option::Option, + #[prost(message, repeated, tag = "3")] + pub diff_data: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.ViewablePRProps +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ViewablePrProps { + #[prost(string, tag = "1")] + pub title: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub body: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub files: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.DiffFile +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DiffFile { + #[prost(string, tag = "1")] + pub file_details: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub file_name: ::prost::alloc::string::String, +} +/// aiserver.v1.ViewableDiffProps +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ViewableDiffProps { + #[prost(message, repeated, tag = "1")] + pub files: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + pub diff_preface: ::prost::alloc::string::String, +} +/// aiserver.v1.ViewableCommitProps +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ViewableCommitProps { + #[prost(string, tag = "1")] + pub description: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub message: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub files: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.UserResponseToSuggestedCodeBlock +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UserResponseToSuggestedCodeBlock { + #[prost( + enumeration = "user_response_to_suggested_code_block::UserResponseType", + tag = "1" + )] + pub user_response_type: i32, + #[prost(string, tag = "2")] + pub file_path: ::prost::alloc::string::String, + #[prost(message, optional, tag = "3")] + pub user_modifications_to_suggested_code_blocks: ::core::option::Option, +} +/// Nested message and enum types in `UserResponseToSuggestedCodeBlock`. +pub mod user_response_to_suggested_code_block { + /// aiserver.v1.UserResponseToSuggestedCodeBlock.UserResponseType + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum UserResponseType { + Unspecified = 0, + Accept = 1, + Reject = 2, + Modify = 3, + } + impl UserResponseType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "USER_RESPONSE_TYPE_UNSPECIFIED", + Self::Accept => "USER_RESPONSE_TYPE_ACCEPT", + Self::Reject => "USER_RESPONSE_TYPE_REJECT", + Self::Modify => "USER_RESPONSE_TYPE_MODIFY", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "USER_RESPONSE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "USER_RESPONSE_TYPE_ACCEPT" => Some(Self::Accept), + "USER_RESPONSE_TYPE_REJECT" => Some(Self::Reject), + "USER_RESPONSE_TYPE_MODIFY" => Some(Self::Modify), + _ => None, + } + } + } +} +/// aiserver.v1.FileDiff +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FileDiff { + #[prost(string, tag = "1")] + pub from: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub to: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub chunks: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `FileDiff`. +pub mod file_diff { + /// aiserver.v1.FileDiff.Chunk + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Chunk { + #[prost(string, tag = "1")] + pub content: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "2")] + pub lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(int32, tag = "3")] + pub old_start: i32, + #[prost(int32, tag = "4")] + pub old_lines: i32, + #[prost(int32, tag = "5")] + pub new_start: i32, + #[prost(int32, tag = "6")] + pub new_lines: i32, + } +} +/// aiserver.v1.ToolResultError +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ToolResultError { + #[prost(string, tag = "1")] + pub client_visible_error_message: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub model_visible_error_message: ::prost::alloc::string::String, + #[prost(string, optional, tag = "3")] + pub actual_error_message_only_send_from_client_to_server_never_the_other_way_around_because_that_may_be_a_security_risk: ::core::option::Option< + ::prost::alloc::string::String, + >, +} +/// aiserver.v1.ToolResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ToolResult { + #[prost(oneof = "tool_result::ToolResult", tags = "1, 2, 3")] + pub tool_result: ::core::option::Option, +} +/// Nested message and enum types in `ToolResult`. +pub mod tool_result { + #[allow(clippy::enum_variant_names)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum ToolResult { + #[prost(message, tag = "1")] + BuiltinToolResult(super::BuiltinToolResult), + #[prost(message, tag = "2")] + CustomToolResult(super::CustomToolResult), + #[prost(message, tag = "3")] + ErrorToolResult(super::ErrorToolResult), + } +} +/// aiserver.v1.ErrorToolResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ErrorToolResult { + #[prost(string, tag = "1")] + pub error_message: ::prost::alloc::string::String, +} +/// aiserver.v1.CustomToolResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CustomToolResult { + #[prost(string, tag = "1")] + pub tool_id: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub result: ::prost::alloc::string::String, +} +/// aiserver.v1.BuiltinToolResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BuiltinToolResult { + #[prost(enumeration = "BuiltinTool", tag = "1")] + pub tool: i32, + #[prost( + oneof = "builtin_tool_result::Result", + tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24" + )] + pub result: ::core::option::Option, +} +/// Nested message and enum types in `BuiltinToolResult`. +pub mod builtin_tool_result { + #[allow(clippy::enum_variant_names)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Result { + #[prost(message, tag = "2")] + SearchResult(super::SearchResult), + #[prost(message, tag = "3")] + ReadChunkResult(super::ReadChunkResult), + #[prost(message, tag = "4")] + GotodefResult(super::GotodefResult), + #[prost(message, tag = "5")] + EditResult(super::EditResult), + #[prost(message, tag = "6")] + UndoEditResult(super::UndoEditResult), + #[prost(message, tag = "7")] + EndResult(super::EndResult), + #[prost(message, tag = "8")] + NewFileResult(super::NewFileResult), + #[prost(message, tag = "9")] + AddTestResult(super::AddTestResult), + #[prost(message, tag = "10")] + RunTestResult(super::RunTestResult), + #[prost(message, tag = "11")] + DeleteTestResult(super::DeleteTestResult), + #[prost(message, tag = "12")] + SaveFileResult(super::SaveFileResult), + #[prost(message, tag = "13")] + GetTestsResult(super::GetTestsResult), + #[prost(message, tag = "14")] + GetSymbolsResult(super::GetSymbolsResult), + #[prost(message, tag = "15")] + SemanticSearchResult(super::SemanticSearchResult), + #[prost(message, tag = "16")] + GetProjectStructureResult(super::GetProjectStructureResult), + #[prost(message, tag = "17")] + CreateRmFilesResult(super::CreateRmFilesResult), + #[prost(message, tag = "18")] + RunTerminalCommandsResult(super::RunTerminalCommandsResult), + #[prost(message, tag = "19")] + NewEditResult(super::NewEditResult), + #[prost(message, tag = "20")] + ReadWithLinterResult(super::ReadWithLinterResult), + #[prost(message, tag = "21")] + AddUiStepResult(super::AddUiStepResult), + #[prost(message, tag = "22")] + ReadSemsearchFilesResult(super::ReadSemsearchFilesResult), + #[prost(message, tag = "23")] + CreateFileResult(super::CreateFileResult), + #[prost(message, tag = "24")] + DeleteFileResult(super::DeleteFileResult), + } +} +/// aiserver.v1.UndoEditResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UndoEditResult { + #[prost(string, repeated, tag = "1")] + pub feedback: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, tag = "4")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub context_start_line_number: i32, + #[prost(string, repeated, tag = "3")] + pub context_lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(int32, tag = "5")] + pub context_total_num_lines: i32, + #[prost(int32, tag = "6")] + pub file_total_lines: i32, +} +/// aiserver.v1.SemanticSearchResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SemanticSearchResult { + #[prost(message, repeated, tag = "1")] + pub results: ::prost::alloc::vec::Vec, + #[prost(map = "string, string", tag = "2")] + pub files: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, +} +/// Nested message and enum types in `SemanticSearchResult`. +pub mod semantic_search_result { + /// aiserver.v1.SemanticSearchResult.Item + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Item { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(float, tag = "2")] + pub score: f32, + #[prost(string, tag = "3")] + pub content: ::prost::alloc::string::String, + #[prost(message, optional, tag = "4")] + pub range: ::core::option::Option, + #[prost(string, optional, tag = "5")] + pub original_content: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "6")] + pub detailed_lines: ::prost::alloc::vec::Vec, + } +} +/// aiserver.v1.SimpleRange +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct SimpleRange { + #[prost(int32, tag = "1")] + pub start_line_number: i32, + #[prost(int32, tag = "2")] + pub start_column: i32, + #[prost(int32, tag = "3")] + pub end_line_number_inclusive: i32, + #[prost(int32, tag = "4")] + pub end_column: i32, +} +/// aiserver.v1.SearchResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SearchResult { + #[prost(message, repeated, tag = "1")] + pub file_results: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "2")] + pub num_total_matches: i32, + #[prost(int32, tag = "3")] + pub num_total_matched_files: i32, + #[prost(bool, tag = "4")] + pub num_total_may_be_incomplete: bool, + #[prost(bool, tag = "5")] + pub files_only: bool, +} +/// aiserver.v1.SearchToolFileSearchResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SearchToolFileSearchResult { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub num_matches: i32, + #[prost(message, repeated, tag = "3")] + pub potentially_relevant_lines: ::prost::alloc::vec::Vec< + search_tool_file_search_result::Line, + >, + #[prost(bool, tag = "4")] + pub cropped: bool, +} +/// Nested message and enum types in `SearchToolFileSearchResult`. +pub mod search_tool_file_search_result { + /// aiserver.v1.SearchToolFileSearchResult.Line + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Line { + #[prost(int32, tag = "1")] + pub line_number: i32, + #[prost(string, tag = "2")] + pub text: ::prost::alloc::string::String, + } +} +/// aiserver.v1.SaveFileResult +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct SaveFileResult {} +/// aiserver.v1.RunTestResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RunTestResult { + #[prost(string, tag = "1")] + pub result: ::prost::alloc::string::String, +} +/// aiserver.v1.RunTerminalCommandsResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RunTerminalCommandsResult { + #[prost(string, repeated, tag = "1")] + pub outputs: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +/// aiserver.v1.ReadWithLinterResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReadWithLinterResult { + #[prost(string, tag = "1")] + pub contents: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub diagnostics: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.ReadSemsearchFilesResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReadSemsearchFilesResult { + #[prost(message, repeated, tag = "1")] + pub code_results: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub all_files: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub missing_files: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.MissingFile +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MissingFile { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(enumeration = "missing_file::MissingReason", tag = "2")] + pub missing_reason: i32, + #[prost(int32, optional, tag = "3")] + pub num_lines: ::core::option::Option, +} +/// Nested message and enum types in `MissingFile`. +pub mod missing_file { + /// aiserver.v1.MissingFile.MissingReason + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum MissingReason { + Unspecified = 0, + TooLarge = 1, + NotFound = 2, + } + impl MissingReason { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "MISSING_REASON_UNSPECIFIED", + Self::TooLarge => "MISSING_REASON_TOO_LARGE", + Self::NotFound => "MISSING_REASON_NOT_FOUND", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "MISSING_REASON_UNSPECIFIED" => Some(Self::Unspecified), + "MISSING_REASON_TOO_LARGE" => Some(Self::TooLarge), + "MISSING_REASON_NOT_FOUND" => Some(Self::NotFound), + _ => None, + } + } + } +} +/// aiserver.v1.File +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct File { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub contents: ::prost::alloc::string::String, +} +/// aiserver.v1.CodeResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CodeResult { + #[prost(message, optional, tag = "1")] + pub code_block: ::core::option::Option, + #[prost(float, tag = "2")] + pub score: f32, +} +/// aiserver.v1.ReadChunkResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReadChunkResult { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub start_line_number: i32, + #[prost(string, repeated, tag = "3")] + pub lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(int32, tag = "4")] + pub total_num_lines: i32, + #[prost(bool, tag = "5")] + pub cropped: bool, +} +/// aiserver.v1.NewFileResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NewFileResult { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub file_total_lines: i32, +} +/// aiserver.v1.NewEditResult +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct NewEditResult {} +/// aiserver.v1.GotodefResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GotodefResult { + #[prost(message, repeated, tag = "1")] + pub results: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.GotodefToolFileSearchResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GotodefToolFileSearchResult { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub potentially_relevant_lines: ::prost::alloc::vec::Vec< + gotodef_tool_file_search_result::Line, + >, +} +/// Nested message and enum types in `GotodefToolFileSearchResult`. +pub mod gotodef_tool_file_search_result { + /// aiserver.v1.GotodefToolFileSearchResult.Line + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Line { + #[prost(int32, tag = "1")] + pub line_number: i32, + #[prost(string, tag = "2")] + pub text: ::prost::alloc::string::String, + } +} +/// aiserver.v1.GetTestsResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetTestsResult { + #[prost(message, repeated, tag = "1")] + pub tests: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `GetTestsResult`. +pub mod get_tests_result { + /// aiserver.v1.GetTestsResult.Test + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Test { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "2")] + pub lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } +} +/// aiserver.v1.GetSymbolsResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetSymbolsResult { + #[prost(message, repeated, tag = "1")] + pub symbols: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.DocumentSymbol +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DocumentSymbol { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub detail: ::prost::alloc::string::String, + #[prost(enumeration = "document_symbol::SymbolKind", tag = "3")] + pub kind: i32, + #[prost(string, tag = "5")] + pub container_name: ::prost::alloc::string::String, + #[prost(message, optional, tag = "6")] + pub range: ::core::option::Option, + #[prost(message, optional, tag = "7")] + pub selection_range: ::core::option::Option, + #[prost(message, repeated, tag = "8")] + pub children: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `DocumentSymbol`. +pub mod document_symbol { + /// aiserver.v1.DocumentSymbol.Range + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct Range { + #[prost(int32, tag = "1")] + pub start_line_number: i32, + #[prost(int32, tag = "2")] + pub start_column: i32, + #[prost(int32, tag = "3")] + pub end_line_number: i32, + #[prost(int32, tag = "4")] + pub end_column: i32, + } + /// aiserver.v1.DocumentSymbol.SymbolKind + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum SymbolKind { + Unspecified = 0, + File = 1, + Module = 2, + Namespace = 3, + Package = 4, + Class = 5, + Method = 6, + Property = 7, + Field = 8, + Constructor = 9, + Enum = 10, + Interface = 11, + Function = 12, + Variable = 13, + Constant = 14, + String = 15, + Number = 16, + Boolean = 17, + Array = 18, + Object = 19, + Key = 20, + Null = 21, + EnumMember = 22, + Struct = 23, + Event = 24, + Operator = 25, + TypeParameter = 26, + } + impl SymbolKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "SYMBOL_KIND_UNSPECIFIED", + Self::File => "SYMBOL_KIND_FILE", + Self::Module => "SYMBOL_KIND_MODULE", + Self::Namespace => "SYMBOL_KIND_NAMESPACE", + Self::Package => "SYMBOL_KIND_PACKAGE", + Self::Class => "SYMBOL_KIND_CLASS", + Self::Method => "SYMBOL_KIND_METHOD", + Self::Property => "SYMBOL_KIND_PROPERTY", + Self::Field => "SYMBOL_KIND_FIELD", + Self::Constructor => "SYMBOL_KIND_CONSTRUCTOR", + Self::Enum => "SYMBOL_KIND_ENUM", + Self::Interface => "SYMBOL_KIND_INTERFACE", + Self::Function => "SYMBOL_KIND_FUNCTION", + Self::Variable => "SYMBOL_KIND_VARIABLE", + Self::Constant => "SYMBOL_KIND_CONSTANT", + Self::String => "SYMBOL_KIND_STRING", + Self::Number => "SYMBOL_KIND_NUMBER", + Self::Boolean => "SYMBOL_KIND_BOOLEAN", + Self::Array => "SYMBOL_KIND_ARRAY", + Self::Object => "SYMBOL_KIND_OBJECT", + Self::Key => "SYMBOL_KIND_KEY", + Self::Null => "SYMBOL_KIND_NULL", + Self::EnumMember => "SYMBOL_KIND_ENUM_MEMBER", + Self::Struct => "SYMBOL_KIND_STRUCT", + Self::Event => "SYMBOL_KIND_EVENT", + Self::Operator => "SYMBOL_KIND_OPERATOR", + Self::TypeParameter => "SYMBOL_KIND_TYPE_PARAMETER", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SYMBOL_KIND_UNSPECIFIED" => Some(Self::Unspecified), + "SYMBOL_KIND_FILE" => Some(Self::File), + "SYMBOL_KIND_MODULE" => Some(Self::Module), + "SYMBOL_KIND_NAMESPACE" => Some(Self::Namespace), + "SYMBOL_KIND_PACKAGE" => Some(Self::Package), + "SYMBOL_KIND_CLASS" => Some(Self::Class), + "SYMBOL_KIND_METHOD" => Some(Self::Method), + "SYMBOL_KIND_PROPERTY" => Some(Self::Property), + "SYMBOL_KIND_FIELD" => Some(Self::Field), + "SYMBOL_KIND_CONSTRUCTOR" => Some(Self::Constructor), + "SYMBOL_KIND_ENUM" => Some(Self::Enum), + "SYMBOL_KIND_INTERFACE" => Some(Self::Interface), + "SYMBOL_KIND_FUNCTION" => Some(Self::Function), + "SYMBOL_KIND_VARIABLE" => Some(Self::Variable), + "SYMBOL_KIND_CONSTANT" => Some(Self::Constant), + "SYMBOL_KIND_STRING" => Some(Self::String), + "SYMBOL_KIND_NUMBER" => Some(Self::Number), + "SYMBOL_KIND_BOOLEAN" => Some(Self::Boolean), + "SYMBOL_KIND_ARRAY" => Some(Self::Array), + "SYMBOL_KIND_OBJECT" => Some(Self::Object), + "SYMBOL_KIND_KEY" => Some(Self::Key), + "SYMBOL_KIND_NULL" => Some(Self::Null), + "SYMBOL_KIND_ENUM_MEMBER" => Some(Self::EnumMember), + "SYMBOL_KIND_STRUCT" => Some(Self::Struct), + "SYMBOL_KIND_EVENT" => Some(Self::Event), + "SYMBOL_KIND_OPERATOR" => Some(Self::Operator), + "SYMBOL_KIND_TYPE_PARAMETER" => Some(Self::TypeParameter), + _ => None, + } + } + } +} +/// aiserver.v1.GetProjectStructureResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetProjectStructureResult { + #[prost(message, repeated, tag = "1")] + pub files: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + pub root_workspace_path: ::prost::alloc::string::String, +} +/// Nested message and enum types in `GetProjectStructureResult`. +pub mod get_project_structure_result { + /// aiserver.v1.GetProjectStructureResult.File + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct File { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub outline: ::prost::alloc::string::String, + } +} +/// aiserver.v1.EndResult +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct EndResult {} +/// aiserver.v1.EditResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EditResult { + #[prost(string, repeated, tag = "1")] + pub feedback: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(int32, tag = "2")] + pub context_start_line_number: i32, + #[prost(string, repeated, tag = "3")] + pub context_lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, tag = "4")] + pub file: ::prost::alloc::string::String, + #[prost(int32, tag = "5")] + pub file_total_lines: i32, + #[prost(message, repeated, tag = "6")] + pub structured_feedback: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `EditResult`. +pub mod edit_result { + /// aiserver.v1.EditResult.RelatedInformation + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct RelatedInformation { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub start_line_number: i32, + #[prost(int32, tag = "3")] + pub end_line_number: i32, + #[prost(string, tag = "4")] + pub relative_workspace_path: ::prost::alloc::string::String, + } + /// aiserver.v1.EditResult.Feedback + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Feedback { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub severity: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub start_line_number: i32, + #[prost(int32, tag = "4")] + pub end_line_number: i32, + #[prost(message, repeated, tag = "5")] + pub related_information: ::prost::alloc::vec::Vec, + } +} +/// aiserver.v1.DeleteTestResult +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct DeleteTestResult {} +/// aiserver.v1.DeleteFileResult +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct DeleteFileResult { + #[prost(bool, tag = "1")] + pub rejected: bool, + #[prost(bool, tag = "2")] + pub file_non_existent: bool, + #[prost(bool, tag = "3")] + pub file_deleted_successfully: bool, +} +/// aiserver.v1.CreateRmFilesResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateRmFilesResult { + #[prost(string, repeated, tag = "1")] + pub created_file_paths: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "2")] + pub removed_file_paths: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +/// aiserver.v1.CreateFileResult +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct CreateFileResult { + #[prost(bool, tag = "1")] + pub file_created_successfully: bool, + #[prost(bool, tag = "2")] + pub file_already_exists: bool, +} +/// aiserver.v1.AddUiStepResult +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct AddUiStepResult {} +/// aiserver.v1.AddTestResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AddTestResult { + #[prost(message, repeated, tag = "1")] + pub feedback: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `AddTestResult`. +pub mod add_test_result { + /// aiserver.v1.AddTestResult.RelatedInformation + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct RelatedInformation { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub start_line_number: i32, + #[prost(int32, tag = "3")] + pub end_line_number: i32, + #[prost(string, tag = "4")] + pub relative_workspace_path: ::prost::alloc::string::String, + } + /// aiserver.v1.AddTestResult.Feedback + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Feedback { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub severity: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub start_line_number: i32, + #[prost(int32, tag = "4")] + pub end_line_number: i32, + #[prost(message, repeated, tag = "5")] + pub related_information: ::prost::alloc::vec::Vec, + } +} +/// aiserver.v1.SuggestedCodeBlock +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SuggestedCodeBlock { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, +} +/// aiserver.v1.SimpleFileDiff +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SimpleFileDiff { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub chunks: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `SimpleFileDiff`. +pub mod simple_file_diff { + /// aiserver.v1.SimpleFileDiff.Chunk + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Chunk { + #[prost(string, repeated, tag = "1")] + pub old_lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "2")] + pub new_lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(message, optional, tag = "3")] + pub old_range: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub new_range: ::core::option::Option, + } +} +/// aiserver.v1.LineRange +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct LineRange { + #[prost(int32, tag = "1")] + pub start_line_number: i32, + #[prost(int32, tag = "2")] + pub end_line_number_inclusive: i32, +} +/// aiserver.v1.RedDiff +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RedDiff { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub red_ranges: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub red_ranges_reversed: ::prost::alloc::vec::Vec, + #[prost(string, tag = "4")] + pub start_hash: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub end_hash: ::prost::alloc::string::String, +} +/// aiserver.v1.PullRequest +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PullRequest { + #[prost(string, tag = "1")] + pub title: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub body: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub diff: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.ListDirResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListDirResult { + #[prost(message, repeated, tag = "1")] + pub files: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + pub directory_relative_workspace_path: ::prost::alloc::string::String, +} +/// Nested message and enum types in `ListDirResult`. +pub mod list_dir_result { + /// aiserver.v1.ListDirResult.File + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct File { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub is_directory: bool, + #[prost(int64, optional, tag = "3")] + pub size: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub last_modified: ::core::option::Option<::prost_types::Timestamp>, + #[prost(int32, optional, tag = "5")] + pub num_children: ::core::option::Option, + #[prost(int32, optional, tag = "6")] + pub num_lines: ::core::option::Option, + } +} +/// aiserver.v1.LinterErrorsWithoutFileContents +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LinterErrorsWithoutFileContents { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub errors: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.InterpreterResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct InterpreterResult { + #[prost(string, tag = "1")] + pub output: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub success: bool, +} +/// aiserver.v1.ImageProto +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ImageProto { + #[prost(bytes = "vec", tag = "1")] + pub data: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub dimension: ::core::option::Option, +} +/// Nested message and enum types in `ImageProto`. +pub mod image_proto { + /// aiserver.v1.ImageProto.Dimension + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct Dimension { + #[prost(int32, tag = "1")] + pub width: i32, + #[prost(int32, tag = "2")] + pub height: i32, + } +} +/// aiserver.v1.GitDiff +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GitDiff { + #[prost(message, repeated, tag = "1")] + pub diffs: ::prost::alloc::vec::Vec, + #[prost(enumeration = "git_diff::DiffType", tag = "2")] + pub diff_type: i32, +} +/// Nested message and enum types in `GitDiff`. +pub mod git_diff { + /// aiserver.v1.GitDiff.DiffType + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum DiffType { + Unspecified = 0, + DiffToHead = 1, + DiffFromBranchToMain = 2, + } + impl DiffType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "DIFF_TYPE_UNSPECIFIED", + Self::DiffToHead => "DIFF_TYPE_DIFF_TO_HEAD", + Self::DiffFromBranchToMain => "DIFF_TYPE_DIFF_FROM_BRANCH_TO_MAIN", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DIFF_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "DIFF_TYPE_DIFF_TO_HEAD" => Some(Self::DiffToHead), + "DIFF_TYPE_DIFF_FROM_BRANCH_TO_MAIN" => Some(Self::DiffFromBranchToMain), + _ => None, + } + } + } +} +/// aiserver.v1.GetLintsForChangeResponse +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetLintsForChangeResponse { + #[prost(message, repeated, tag = "1")] + pub lints: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `GetLintsForChangeResponse`. +pub mod get_lints_for_change_response { + /// aiserver.v1.GetLintsForChangeResponse.Lint + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Lint { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub severity: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "4")] + pub start_line_number_one_indexed: i32, + #[prost(int32, tag = "5")] + pub start_column_one_indexed: i32, + #[prost(int32, tag = "6")] + pub end_line_number_inclusive_one_indexed: i32, + #[prost(int32, tag = "7")] + pub end_column_one_indexed: i32, + #[prost(message, repeated, tag = "9")] + pub quick_fixes: ::prost::alloc::vec::Vec, + } + /// Nested message and enum types in `Lint`. + pub mod lint { + /// aiserver.v1.GetLintsForChangeResponse.Lint.QuickFix + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct QuickFix { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub kind: ::prost::alloc::string::String, + #[prost(bool, tag = "3")] + pub is_preferred: bool, + #[prost(message, repeated, tag = "4")] + pub edits: ::prost::alloc::vec::Vec, + } + /// Nested message and enum types in `QuickFix`. + pub mod quick_fix { + /// aiserver.v1.GetLintsForChangeResponse.Lint.QuickFix.Edit + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Edit { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub text: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub start_line_number_one_indexed: i32, + #[prost(int32, tag = "4")] + pub start_column_one_indexed: i32, + #[prost(int32, tag = "5")] + pub end_line_number_inclusive_one_indexed: i32, + #[prost(int32, tag = "6")] + pub end_column_one_indexed: i32, + } + } + } +} +/// aiserver.v1.Lint +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Lint { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub range: ::core::option::Option, + #[prost(enumeration = "LintSeverity", tag = "3")] + pub severity: i32, +} +/// aiserver.v1.Edit +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Edit { + #[prost(string, tag = "1")] + pub text: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub range: ::core::option::Option, +} +/// aiserver.v1.IRange +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct IRange { + #[prost(int32, tag = "1")] + pub start_line_number: i32, + #[prost(int32, tag = "2")] + pub start_column: i32, + #[prost(int32, tag = "3")] + pub end_line_number: i32, + #[prost(int32, tag = "4")] + pub end_column: i32, +} +/// aiserver.v1.FolderInfo +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FolderInfo { + #[prost(string, tag = "1")] + pub relative_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub files: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.FolderFileInfo +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FolderFileInfo { + #[prost(string, tag = "1")] + pub relative_path: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub content: ::prost::alloc::string::String, + #[prost(bool, tag = "3")] + pub truncated: bool, + #[prost(float, tag = "4")] + pub score: f32, +} +/// aiserver.v1.DocsReference +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DocsReference { + #[prost(string, tag = "1")] + pub title: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub url: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub chunk: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub name: ::prost::alloc::string::String, +} +/// aiserver.v1.DiffHistoryData +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DiffHistoryData { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub diffs: ::prost::alloc::vec::Vec, + #[prost(double, tag = "3")] + pub timestamp: f64, + #[prost(string, tag = "4")] + pub unique_id: ::prost::alloc::string::String, + #[prost(message, optional, tag = "5")] + pub start_to_end_diff: ::core::option::Option, +} +/// aiserver.v1.ComposerFileDiff +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ComposerFileDiff { + #[prost(message, repeated, tag = "1")] + pub chunks: ::prost::alloc::vec::Vec, + #[prost(enumeration = "composer_file_diff::Editor", tag = "2")] + pub editor: i32, + #[prost(bool, tag = "3")] + pub hit_timeout: bool, +} +/// Nested message and enum types in `ComposerFileDiff`. +pub mod composer_file_diff { + /// aiserver.v1.ComposerFileDiff.ChunkDiff + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ChunkDiff { + #[prost(string, tag = "1")] + pub diff_string: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub old_start: i32, + #[prost(int32, tag = "3")] + pub new_start: i32, + #[prost(int32, tag = "4")] + pub old_lines: i32, + #[prost(int32, tag = "5")] + pub new_lines: i32, + #[prost(int32, tag = "6")] + pub lines_removed: i32, + #[prost(int32, tag = "7")] + pub lines_added: i32, + } + /// aiserver.v1.ComposerFileDiff.Editor + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Editor { + Unspecified = 0, + Ai = 1, + Human = 2, + } + impl Editor { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "EDITOR_UNSPECIFIED", + Self::Ai => "EDITOR_AI", + Self::Human => "EDITOR_HUMAN", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "EDITOR_UNSPECIFIED" => Some(Self::Unspecified), + "EDITOR_AI" => Some(Self::Ai), + "EDITOR_HUMAN" => Some(Self::Human), + _ => None, + } + } + } +} +/// aiserver.v1.ConversationSummary +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConversationSummary { + #[prost(string, tag = "1")] + pub summary: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub truncation_last_bubble_id_inclusive: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub client_should_start_sending_from_inclusive_bubble_id: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub previous_conversation_summary_bubble_id: ::prost::alloc::string::String, + #[prost(bool, tag = "5")] + pub includes_tool_results: bool, +} +/// aiserver.v1.ComposerFileDiffHistory +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ComposerFileDiffHistory { + #[prost(string, tag = "1")] + pub file_name: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "2")] + pub diff_history: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(double, repeated, tag = "3")] + pub diff_history_timestamps: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.ComposerCapabilityRequest +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ComposerCapabilityRequest { + #[prost( + enumeration = "composer_capability_request::ComposerCapabilityType", + tag = "1" + )] + pub r#type: i32, + #[prost( + oneof = "composer_capability_request::Data", + tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14" + )] + pub data: ::core::option::Option, +} +/// Nested message and enum types in `ComposerCapabilityRequest`. +pub mod composer_capability_request { + /// aiserver.v1.ComposerCapabilityRequest.ToolSchema + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ToolSchema { + #[prost(enumeration = "ToolType", tag = "1")] + pub r#type: i32, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + #[prost(map = "string, message", tag = "3")] + pub properties: ::std::collections::HashMap< + ::prost::alloc::string::String, + SchemaProperty, + >, + #[prost(string, repeated, tag = "4")] + pub required: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } + /// aiserver.v1.ComposerCapabilityRequest.SchemaProperty + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct SchemaProperty { + #[prost(string, tag = "1")] + pub r#type: ::prost::alloc::string::String, + #[prost(string, optional, tag = "2")] + pub description: ::core::option::Option<::prost::alloc::string::String>, + } + /// aiserver.v1.ComposerCapabilityRequest.LoopOnLintsCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct LoopOnLintsCapability { + #[prost(message, repeated, tag = "1")] + pub linter_errors: ::prost::alloc::vec::Vec, + #[prost(string, optional, tag = "2")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + } + /// aiserver.v1.ComposerCapabilityRequest.LoopOnTestsCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct LoopOnTestsCapability { + #[prost(string, repeated, tag = "1")] + pub test_names: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, optional, tag = "2")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + } + /// aiserver.v1.ComposerCapabilityRequest.MegaPlannerCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct MegaPlannerCapability { + #[prost(string, optional, tag = "1")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + } + /// aiserver.v1.ComposerCapabilityRequest.LoopOnCommandCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct LoopOnCommandCapability { + #[prost(string, tag = "1")] + pub command: ::prost::alloc::string::String, + #[prost(string, optional, tag = "2")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "3")] + pub output: ::core::option::Option<::prost::alloc::string::String>, + #[prost(int32, optional, tag = "4")] + pub exit_code: ::core::option::Option, + } + /// aiserver.v1.ComposerCapabilityRequest.ToolCallCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ToolCallCapability { + #[prost(string, optional, tag = "1")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "2")] + pub tool_schemas: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag = "3")] + pub relevant_files: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "4")] + pub files_in_context: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "5")] + pub semantic_search_files: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, + } + /// aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct DiffReviewCapability { + #[prost(string, optional, tag = "1")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "2")] + pub diffs: ::prost::alloc::vec::Vec, + } + /// Nested message and enum types in `DiffReviewCapability`. + pub mod diff_review_capability { + /// aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability.SimpleFileDiff + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct SimpleFileDiff { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub chunks: ::prost::alloc::vec::Vec, + } + /// Nested message and enum types in `SimpleFileDiff`. + pub mod simple_file_diff { + /// aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability.SimpleFileDiff.Chunk + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Chunk { + #[prost(string, repeated, tag = "1")] + pub old_lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "2")] + pub new_lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(message, optional, tag = "3")] + pub old_range: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub new_range: ::core::option::Option, + } + } + } + /// aiserver.v1.ComposerCapabilityRequest.DecomposerCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct DecomposerCapability { + #[prost(string, optional, tag = "1")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + } + /// aiserver.v1.ComposerCapabilityRequest.ContextPickingCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ContextPickingCapability { + #[prost(string, optional, tag = "1")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "2")] + pub potential_context_files: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, + #[prost(message, repeated, tag = "3")] + pub potential_context_code_chunks: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag = "4")] + pub files_in_context: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } + /// aiserver.v1.ComposerCapabilityRequest.EditTrailCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct EditTrailCapability { + #[prost(string, optional, tag = "1")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + } + /// aiserver.v1.ComposerCapabilityRequest.AutoContextCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct AutoContextCapability { + #[prost(string, optional, tag = "1")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "2")] + pub additional_files: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } + /// aiserver.v1.ComposerCapabilityRequest.ContextPlannerCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ContextPlannerCapability { + #[prost(string, optional, tag = "1")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "2")] + pub attached_code_chunks: ::prost::alloc::vec::Vec, + } + /// aiserver.v1.ComposerCapabilityRequest.RememberThisCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct RememberThisCapability { + #[prost(string, optional, tag = "1")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, tag = "2")] + pub memory: ::prost::alloc::string::String, + } + /// aiserver.v1.ComposerCapabilityRequest.CursorRulesCapability + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct CursorRulesCapability { + #[prost(string, optional, tag = "1")] + pub custom_instructions: ::core::option::Option<::prost::alloc::string::String>, + } + /// aiserver.v1.ComposerCapabilityRequest.ComposerCapabilityType + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum ComposerCapabilityType { + Unspecified = 0, + LoopOnLints = 1, + LoopOnTests = 2, + MegaPlanner = 3, + LoopOnCommand = 4, + ToolCall = 5, + DiffReview = 6, + ContextPicking = 7, + EditTrail = 8, + AutoContext = 9, + ContextPlanner = 10, + DiffHistory = 11, + RememberThis = 12, + Decomposer = 13, + UsesCodebase = 14, + ToolFormer = 15, + CursorRules = 16, + } + impl ComposerCapabilityType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "COMPOSER_CAPABILITY_TYPE_UNSPECIFIED", + Self::LoopOnLints => "COMPOSER_CAPABILITY_TYPE_LOOP_ON_LINTS", + Self::LoopOnTests => "COMPOSER_CAPABILITY_TYPE_LOOP_ON_TESTS", + Self::MegaPlanner => "COMPOSER_CAPABILITY_TYPE_MEGA_PLANNER", + Self::LoopOnCommand => "COMPOSER_CAPABILITY_TYPE_LOOP_ON_COMMAND", + Self::ToolCall => "COMPOSER_CAPABILITY_TYPE_TOOL_CALL", + Self::DiffReview => "COMPOSER_CAPABILITY_TYPE_DIFF_REVIEW", + Self::ContextPicking => "COMPOSER_CAPABILITY_TYPE_CONTEXT_PICKING", + Self::EditTrail => "COMPOSER_CAPABILITY_TYPE_EDIT_TRAIL", + Self::AutoContext => "COMPOSER_CAPABILITY_TYPE_AUTO_CONTEXT", + Self::ContextPlanner => "COMPOSER_CAPABILITY_TYPE_CONTEXT_PLANNER", + Self::DiffHistory => "COMPOSER_CAPABILITY_TYPE_DIFF_HISTORY", + Self::RememberThis => "COMPOSER_CAPABILITY_TYPE_REMEMBER_THIS", + Self::Decomposer => "COMPOSER_CAPABILITY_TYPE_DECOMPOSER", + Self::UsesCodebase => "COMPOSER_CAPABILITY_TYPE_USES_CODEBASE", + Self::ToolFormer => "COMPOSER_CAPABILITY_TYPE_TOOL_FORMER", + Self::CursorRules => "COMPOSER_CAPABILITY_TYPE_CURSOR_RULES", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "COMPOSER_CAPABILITY_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "COMPOSER_CAPABILITY_TYPE_LOOP_ON_LINTS" => Some(Self::LoopOnLints), + "COMPOSER_CAPABILITY_TYPE_LOOP_ON_TESTS" => Some(Self::LoopOnTests), + "COMPOSER_CAPABILITY_TYPE_MEGA_PLANNER" => Some(Self::MegaPlanner), + "COMPOSER_CAPABILITY_TYPE_LOOP_ON_COMMAND" => Some(Self::LoopOnCommand), + "COMPOSER_CAPABILITY_TYPE_TOOL_CALL" => Some(Self::ToolCall), + "COMPOSER_CAPABILITY_TYPE_DIFF_REVIEW" => Some(Self::DiffReview), + "COMPOSER_CAPABILITY_TYPE_CONTEXT_PICKING" => Some(Self::ContextPicking), + "COMPOSER_CAPABILITY_TYPE_EDIT_TRAIL" => Some(Self::EditTrail), + "COMPOSER_CAPABILITY_TYPE_AUTO_CONTEXT" => Some(Self::AutoContext), + "COMPOSER_CAPABILITY_TYPE_CONTEXT_PLANNER" => Some(Self::ContextPlanner), + "COMPOSER_CAPABILITY_TYPE_DIFF_HISTORY" => Some(Self::DiffHistory), + "COMPOSER_CAPABILITY_TYPE_REMEMBER_THIS" => Some(Self::RememberThis), + "COMPOSER_CAPABILITY_TYPE_DECOMPOSER" => Some(Self::Decomposer), + "COMPOSER_CAPABILITY_TYPE_USES_CODEBASE" => Some(Self::UsesCodebase), + "COMPOSER_CAPABILITY_TYPE_TOOL_FORMER" => Some(Self::ToolFormer), + "COMPOSER_CAPABILITY_TYPE_CURSOR_RULES" => Some(Self::CursorRules), + _ => None, + } + } + } + /// aiserver.v1.ComposerCapabilityRequest.ToolType + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum ToolType { + Unspecified = 0, + AddFileToContext = 1, + RunTerminalCommand = 2, + Iterate = 3, + RemoveFileFromContext = 4, + SemanticSearchCodebase = 5, + } + impl ToolType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "TOOL_TYPE_UNSPECIFIED", + Self::AddFileToContext => "TOOL_TYPE_ADD_FILE_TO_CONTEXT", + Self::RunTerminalCommand => "TOOL_TYPE_RUN_TERMINAL_COMMAND", + Self::Iterate => "TOOL_TYPE_ITERATE", + Self::RemoveFileFromContext => "TOOL_TYPE_REMOVE_FILE_FROM_CONTEXT", + Self::SemanticSearchCodebase => "TOOL_TYPE_SEMANTIC_SEARCH_CODEBASE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TOOL_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "TOOL_TYPE_ADD_FILE_TO_CONTEXT" => Some(Self::AddFileToContext), + "TOOL_TYPE_RUN_TERMINAL_COMMAND" => Some(Self::RunTerminalCommand), + "TOOL_TYPE_ITERATE" => Some(Self::Iterate), + "TOOL_TYPE_REMOVE_FILE_FROM_CONTEXT" => Some(Self::RemoveFileFromContext), + "TOOL_TYPE_SEMANTIC_SEARCH_CODEBASE" => { + Some(Self::SemanticSearchCodebase) + } + _ => None, + } + } + } + #[allow(clippy::enum_variant_names)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Data { + #[prost(message, tag = "2")] + LoopOnLints(LoopOnLintsCapability), + #[prost(message, tag = "3")] + LoopOnTests(LoopOnTestsCapability), + #[prost(message, tag = "4")] + MegaPlanner(MegaPlannerCapability), + #[prost(message, tag = "5")] + LoopOnCommand(LoopOnCommandCapability), + #[prost(message, tag = "6")] + ToolCall(ToolCallCapability), + #[prost(message, tag = "7")] + DiffReview(DiffReviewCapability), + #[prost(message, tag = "8")] + ContextPicking(ContextPickingCapability), + #[prost(message, tag = "9")] + EditTrail(EditTrailCapability), + #[prost(message, tag = "10")] + AutoContext(AutoContextCapability), + #[prost(message, tag = "11")] + ContextPlanner(ContextPlannerCapability), + #[prost(message, tag = "12")] + RememberThis(RememberThisCapability), + #[prost(message, tag = "13")] + Decomposer(DecomposerCapability), + #[prost(message, tag = "14")] + CursorRules(CursorRulesCapability), + } +} +/// aiserver.v1.CodeChunk +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CodeChunk { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub start_line_number: i32, + #[prost(string, repeated, tag = "3")] + pub lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(enumeration = "code_chunk::SummarizationStrategy", optional, tag = "4")] + pub summarization_strategy: ::core::option::Option, + #[prost(string, tag = "5")] + pub language_identifier: ::prost::alloc::string::String, + #[prost(enumeration = "code_chunk::Intent", optional, tag = "6")] + pub intent: ::core::option::Option, + #[prost(bool, optional, tag = "7")] + pub is_final_version: ::core::option::Option, + #[prost(bool, optional, tag = "8")] + pub is_first_version: ::core::option::Option, +} +/// Nested message and enum types in `CodeChunk`. +pub mod code_chunk { + /// aiserver.v1.CodeChunk.Intent + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Intent { + Unspecified = 0, + ComposerFile = 1, + CompressedComposerFile = 2, + } + impl Intent { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "INTENT_UNSPECIFIED", + Self::ComposerFile => "INTENT_COMPOSER_FILE", + Self::CompressedComposerFile => "INTENT_COMPRESSED_COMPOSER_FILE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "INTENT_UNSPECIFIED" => Some(Self::Unspecified), + "INTENT_COMPOSER_FILE" => Some(Self::ComposerFile), + "INTENT_COMPRESSED_COMPOSER_FILE" => Some(Self::CompressedComposerFile), + _ => None, + } + } + } + /// aiserver.v1.CodeChunk.SummarizationStrategy + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum SummarizationStrategy { + NoneUnspecified = 0, + Summarized = 1, + Embedded = 2, + } + impl SummarizationStrategy { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::NoneUnspecified => "SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED", + Self::Summarized => "SUMMARIZATION_STRATEGY_SUMMARIZED", + Self::Embedded => "SUMMARIZATION_STRATEGY_EMBEDDED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED" => Some(Self::NoneUnspecified), + "SUMMARIZATION_STRATEGY_SUMMARIZED" => Some(Self::Summarized), + "SUMMARIZATION_STRATEGY_EMBEDDED" => Some(Self::Embedded), + _ => None, + } + } + } +} +/// aiserver.v1.Commit +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Commit { + #[prost(string, tag = "1")] + pub sha: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub message: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub description: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub diff: ::prost::alloc::vec::Vec, + #[prost(string, tag = "5")] + pub author: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub date: ::prost::alloc::string::String, +} +/// aiserver.v1.ClientSideToolV2Result +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ClientSideToolV2Result { + #[prost(enumeration = "ClientSideToolV2", tag = "1")] + pub tool: i32, + #[prost(message, optional, tag = "8")] + pub error: ::core::option::Option, + #[prost( + oneof = "client_side_tool_v2_result::Result", + tags = "2, 3, 4, 5, 6, 9, 10, 11, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30" + )] + pub result: ::core::option::Option, +} +/// Nested message and enum types in `ClientSideToolV2Result`. +pub mod client_side_tool_v2_result { + #[allow(clippy::enum_variant_names)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Result { + #[prost(message, tag = "2")] + ReadSemsearchFilesResult(super::ReadSemsearchFilesResult), + #[prost(message, tag = "3")] + ReadFileForImportsResult(super::ReadFileForImportsResult), + #[prost(message, tag = "4")] + RipgrepSearchResult(super::RipgrepSearchResult), + #[prost(message, tag = "5")] + RunTerminalCommandResult(super::RunTerminalCommandResult), + #[prost(message, tag = "6")] + ReadFileResult(super::ReadFileResult), + #[prost(message, tag = "9")] + ListDirResult(super::ListDirResult), + #[prost(message, tag = "10")] + EditFileResult(super::EditFileResult), + #[prost(message, tag = "11")] + FileSearchResult(super::ToolCallFileSearchResult), + #[prost(message, tag = "18")] + SemanticSearchFullResult(super::SemanticSearchFullResult), + #[prost(message, tag = "19")] + CreateFileResult(super::CreateFileResult), + #[prost(message, tag = "20")] + DeleteFileResult(super::DeleteFileResult), + #[prost(message, tag = "21")] + ReapplyResult(super::ReapplyResult), + #[prost(message, tag = "22")] + GetRelatedFilesResult(super::GetRelatedFilesResult), + #[prost(message, tag = "23")] + ParallelApplyResult(super::ParallelApplyResult), + #[prost(message, tag = "24")] + RunTerminalCommandV2Result(super::RunTerminalCommandV2Result), + #[prost(message, tag = "25")] + FetchRulesResult(super::FetchRulesResult), + #[prost(message, tag = "26")] + PlannerResult(super::PlannerResult), + #[prost(message, tag = "27")] + WebSearchResult(super::WebSearchResult), + #[prost(message, tag = "28")] + McpResult(super::McpResult), + #[prost(message, tag = "29")] + WebViewerResult(super::WebViewerResult), + #[prost(message, tag = "30")] + DiffHistoryResult(super::DiffHistoryResult), + } +} +/// aiserver.v1.WebViewerResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WebViewerResult { + #[prost(string, tag = "1")] + pub url: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub screenshot: ::core::option::Option, +} +/// aiserver.v1.WebSearchResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WebSearchResult { + #[prost(message, repeated, tag = "1")] + pub references: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `WebSearchResult`. +pub mod web_search_result { + /// aiserver.v1.WebSearchResult.WebReference + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct WebReference { + #[prost(string, tag = "1")] + pub title: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub url: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub chunk: ::prost::alloc::string::String, + } +} +/// aiserver.v1.ToolCallFileSearchResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ToolCallFileSearchResult { + #[prost(message, repeated, tag = "1")] + pub files: ::prost::alloc::vec::Vec, + #[prost(bool, optional, tag = "2")] + pub limit_hit: ::core::option::Option, + #[prost(int32, tag = "3")] + pub num_results: i32, +} +/// Nested message and enum types in `ToolCallFileSearchResult`. +pub mod tool_call_file_search_result { + /// aiserver.v1.ToolCallFileSearchResult.File + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct File { + #[prost(string, tag = "1")] + pub uri: ::prost::alloc::string::String, + } +} +/// aiserver.v1.SemanticSearchFullResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SemanticSearchFullResult { + #[prost(message, repeated, tag = "1")] + pub code_results: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub all_files: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub missing_files: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.RunTerminalCommandV2Result +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RunTerminalCommandV2Result { + #[prost(string, tag = "1")] + pub output: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub exit_code: i32, + #[prost(bool, optional, tag = "3")] + pub rejected: ::core::option::Option, + #[prost(bool, tag = "4")] + pub popped_out_into_background: bool, + #[prost(bool, tag = "5")] + pub is_running_in_background: bool, + #[prost(bool, tag = "6")] + pub not_interrupted: bool, + #[prost(string, tag = "7")] + pub resulting_working_directory: ::prost::alloc::string::String, + #[prost(bool, tag = "8")] + pub did_user_change: bool, + #[prost(enumeration = "RunTerminalCommandEndedReason", tag = "9")] + pub ended_reason: i32, + #[prost(int32, optional, tag = "10")] + pub exit_code_v2: ::core::option::Option, +} +/// aiserver.v1.RunTerminalCommandResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RunTerminalCommandResult { + #[prost(string, tag = "1")] + pub output: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub exit_code: i32, + #[prost(bool, optional, tag = "3")] + pub rejected: ::core::option::Option, + #[prost(bool, tag = "4")] + pub popped_out_into_background: bool, +} +/// aiserver.v1.RipgrepSearchResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RipgrepSearchResult { + #[prost(message, optional, tag = "1")] + pub internal: ::core::option::Option, +} +/// aiserver.v1.RipgrepSearchResultInternal +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RipgrepSearchResultInternal { + #[prost(message, repeated, tag = "1")] + pub results: ::prost::alloc::vec::Vec, + #[prost( + enumeration = "ripgrep_search_result_internal::SearchCompletionExitCode", + optional, + tag = "2" + )] + pub exit: ::core::option::Option, + #[prost(bool, optional, tag = "3")] + pub limit_hit: ::core::option::Option, + #[prost(message, repeated, tag = "4")] + pub messages: ::prost::alloc::vec::Vec< + ripgrep_search_result_internal::ITextSearchCompleteMessage, + >, + #[prost(oneof = "ripgrep_search_result_internal::Stats", tags = "5, 6")] + pub stats: ::core::option::Option, +} +/// Nested message and enum types in `RipgrepSearchResultInternal`. +pub mod ripgrep_search_result_internal { + /// aiserver.v1.RipgrepSearchResultInternal.IFileMatch + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct IFileMatch { + #[prost(string, tag = "1")] + pub resource: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub results: ::prost::alloc::vec::Vec, + } + /// aiserver.v1.RipgrepSearchResultInternal.ITextSearchResult + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ITextSearchResult { + #[prost(oneof = "i_text_search_result::Result", tags = "1, 2")] + pub result: ::core::option::Option, + } + /// Nested message and enum types in `ITextSearchResult`. + pub mod i_text_search_result { + #[allow(clippy::enum_variant_names)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Result { + #[prost(message, tag = "1")] + Match(super::ITextSearchMatch), + #[prost(message, tag = "2")] + Context(super::ITextSearchContext), + } + } + /// aiserver.v1.RipgrepSearchResultInternal.ITextSearchMatch + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ITextSearchMatch { + #[prost(string, optional, tag = "1")] + pub uri: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "2")] + pub range_locations: ::prost::alloc::vec::Vec, + #[prost(string, tag = "3")] + pub preview_text: ::prost::alloc::string::String, + #[prost(int32, optional, tag = "4")] + pub webview_index: ::core::option::Option, + #[prost(string, optional, tag = "5")] + pub cell_fragment: ::core::option::Option<::prost::alloc::string::String>, + } + /// aiserver.v1.RipgrepSearchResultInternal.ITextSearchContext + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ITextSearchContext { + #[prost(string, optional, tag = "1")] + pub uri: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, tag = "2")] + pub text: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub line_number: i32, + } + /// aiserver.v1.RipgrepSearchResultInternal.ISearchRangeSetPairing + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct ISearchRangeSetPairing { + #[prost(message, optional, tag = "1")] + pub source: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub preview: ::core::option::Option, + } + /// aiserver.v1.RipgrepSearchResultInternal.ISearchRange + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct ISearchRange { + #[prost(int32, tag = "1")] + pub start_line_number: i32, + #[prost(int32, tag = "2")] + pub start_column: i32, + #[prost(int32, tag = "3")] + pub end_line_number: i32, + #[prost(int32, tag = "4")] + pub end_column: i32, + } + /// aiserver.v1.RipgrepSearchResultInternal.ITextSearchCompleteMessage + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ITextSearchCompleteMessage { + #[prost(string, tag = "1")] + pub text: ::prost::alloc::string::String, + #[prost(enumeration = "TextSearchCompleteMessageType", tag = "2")] + pub r#type: i32, + #[prost(bool, optional, tag = "3")] + pub trusted: ::core::option::Option, + } + /// aiserver.v1.RipgrepSearchResultInternal.IFileSearchStats + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct IFileSearchStats { + #[prost(bool, tag = "1")] + pub from_cache: bool, + #[prost(int32, tag = "5")] + pub result_count: i32, + #[prost(enumeration = "i_file_search_stats::FileSearchProviderType", tag = "6")] + pub r#type: i32, + #[prost(int32, optional, tag = "7")] + pub sorting_time: ::core::option::Option, + #[prost(oneof = "i_file_search_stats::DetailStats", tags = "2, 3, 4")] + pub detail_stats: ::core::option::Option, + } + /// Nested message and enum types in `IFileSearchStats`. + pub mod i_file_search_stats { + /// aiserver.v1.RipgrepSearchResultInternal.IFileSearchStats.FileSearchProviderType + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum FileSearchProviderType { + Unspecified = 0, + FileSearchProvider = 1, + SearchProcess = 2, + } + impl FileSearchProviderType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "FILE_SEARCH_PROVIDER_TYPE_UNSPECIFIED", + Self::FileSearchProvider => { + "FILE_SEARCH_PROVIDER_TYPE_FILE_SEARCH_PROVIDER" + } + Self::SearchProcess => "FILE_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "FILE_SEARCH_PROVIDER_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "FILE_SEARCH_PROVIDER_TYPE_FILE_SEARCH_PROVIDER" => { + Some(Self::FileSearchProvider) + } + "FILE_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS" => { + Some(Self::SearchProcess) + } + _ => None, + } + } + } + #[allow(clippy::enum_variant_names)] + #[derive(Clone, Copy, PartialEq, ::prost::Oneof)] + pub enum DetailStats { + #[prost(message, tag = "2")] + SearchEngineStats(super::ISearchEngineStats), + #[prost(message, tag = "3")] + CachedSearchStats(super::ICachedSearchStats), + #[prost(message, tag = "4")] + FileSearchProviderStats(super::IFileSearchProviderStats), + } + } + /// aiserver.v1.RipgrepSearchResultInternal.ITextSearchStats + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct ITextSearchStats { + #[prost(enumeration = "i_text_search_stats::TextSearchProviderType", tag = "1")] + pub r#type: i32, + } + /// Nested message and enum types in `ITextSearchStats`. + pub mod i_text_search_stats { + /// aiserver.v1.RipgrepSearchResultInternal.ITextSearchStats.TextSearchProviderType + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum TextSearchProviderType { + Unspecified = 0, + TextSearchProvider = 1, + SearchProcess = 2, + AiTextSearchProvider = 3, + } + impl TextSearchProviderType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "TEXT_SEARCH_PROVIDER_TYPE_UNSPECIFIED", + Self::TextSearchProvider => { + "TEXT_SEARCH_PROVIDER_TYPE_TEXT_SEARCH_PROVIDER" + } + Self::SearchProcess => "TEXT_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS", + Self::AiTextSearchProvider => { + "TEXT_SEARCH_PROVIDER_TYPE_AI_TEXT_SEARCH_PROVIDER" + } + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TEXT_SEARCH_PROVIDER_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "TEXT_SEARCH_PROVIDER_TYPE_TEXT_SEARCH_PROVIDER" => { + Some(Self::TextSearchProvider) + } + "TEXT_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS" => { + Some(Self::SearchProcess) + } + "TEXT_SEARCH_PROVIDER_TYPE_AI_TEXT_SEARCH_PROVIDER" => { + Some(Self::AiTextSearchProvider) + } + _ => None, + } + } + } + } + /// aiserver.v1.RipgrepSearchResultInternal.ISearchEngineStats + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct ISearchEngineStats { + #[prost(int32, tag = "1")] + pub file_walk_time: i32, + #[prost(int32, tag = "2")] + pub directories_walked: i32, + #[prost(int32, tag = "3")] + pub files_walked: i32, + #[prost(int32, tag = "4")] + pub cmd_time: i32, + #[prost(int32, optional, tag = "5")] + pub cmd_result_count: ::core::option::Option, + } + /// aiserver.v1.RipgrepSearchResultInternal.ICachedSearchStats + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct ICachedSearchStats { + #[prost(bool, tag = "1")] + pub cache_was_resolved: bool, + #[prost(int32, tag = "2")] + pub cache_lookup_time: i32, + #[prost(int32, tag = "3")] + pub cache_filter_time: i32, + #[prost(int32, tag = "4")] + pub cache_entry_count: i32, + } + /// aiserver.v1.RipgrepSearchResultInternal.IFileSearchProviderStats + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct IFileSearchProviderStats { + #[prost(int32, tag = "1")] + pub provider_time: i32, + #[prost(int32, tag = "2")] + pub post_process_time: i32, + } + /// aiserver.v1.RipgrepSearchResultInternal.TextSearchCompleteMessageType + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum TextSearchCompleteMessageType { + Unspecified = 0, + Information = 1, + Warning = 2, + } + impl TextSearchCompleteMessageType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_UNSPECIFIED", + Self::Information => "TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_INFORMATION", + Self::Warning => "TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_WARNING", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_UNSPECIFIED" => { + Some(Self::Unspecified) + } + "TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_INFORMATION" => { + Some(Self::Information) + } + "TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_WARNING" => Some(Self::Warning), + _ => None, + } + } + } + /// aiserver.v1.RipgrepSearchResultInternal.SearchCompletionExitCode + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum SearchCompletionExitCode { + Unspecified = 0, + Normal = 1, + NewSearchStarted = 2, + } + impl SearchCompletionExitCode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "SEARCH_COMPLETION_EXIT_CODE_UNSPECIFIED", + Self::Normal => "SEARCH_COMPLETION_EXIT_CODE_NORMAL", + Self::NewSearchStarted => { + "SEARCH_COMPLETION_EXIT_CODE_NEW_SEARCH_STARTED" + } + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SEARCH_COMPLETION_EXIT_CODE_UNSPECIFIED" => Some(Self::Unspecified), + "SEARCH_COMPLETION_EXIT_CODE_NORMAL" => Some(Self::Normal), + "SEARCH_COMPLETION_EXIT_CODE_NEW_SEARCH_STARTED" => { + Some(Self::NewSearchStarted) + } + _ => None, + } + } + } + #[allow(clippy::enum_variant_names)] + #[derive(Clone, Copy, PartialEq, ::prost::Oneof)] + pub enum Stats { + #[prost(message, tag = "5")] + FileSearchStats(IFileSearchStats), + #[prost(message, tag = "6")] + TextSearchStats(ITextSearchStats), + } +} +/// aiserver.v1.ReapplyResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReapplyResult { + #[prost(message, optional, tag = "1")] + pub diff: ::core::option::Option, + #[prost(bool, tag = "2")] + pub is_applied: bool, + #[prost(bool, tag = "3")] + pub apply_failed: bool, + #[prost(message, repeated, tag = "4")] + pub linter_errors: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.ReadFileResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReadFileResult { + #[prost(string, tag = "1")] + pub contents: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub did_downgrade_to_line_range: bool, + #[prost(bool, tag = "3")] + pub did_shorten_line_range: bool, + #[prost(bool, tag = "4")] + pub did_set_default_line_range: bool, + #[prost(string, optional, tag = "5")] + pub full_file_contents: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "6")] + pub outline: ::core::option::Option<::prost::alloc::string::String>, + #[prost(int32, optional, tag = "7")] + pub start_line_one_indexed: ::core::option::Option, + #[prost(int32, optional, tag = "8")] + pub end_line_one_indexed_inclusive: ::core::option::Option, + #[prost(string, tag = "9")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(bool, tag = "10")] + pub did_shorten_char_range: bool, +} +/// aiserver.v1.ReadFileForImportsResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReadFileForImportsResult { + #[prost(string, tag = "1")] + pub contents: ::prost::alloc::string::String, +} +/// aiserver.v1.PlannerResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PlannerResult { + #[prost(string, tag = "1")] + pub plan: ::prost::alloc::string::String, +} +/// aiserver.v1.ParallelApplyResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ParallelApplyResult { + #[prost(message, repeated, tag = "1")] + pub file_results: ::prost::alloc::vec::Vec, + #[prost(string, optional, tag = "2")] + pub error: ::core::option::Option<::prost::alloc::string::String>, +} +/// Nested message and enum types in `ParallelApplyResult`. +pub mod parallel_apply_result { + /// aiserver.v1.ParallelApplyResult.FileResult + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct FileResult { + #[prost(string, tag = "1")] + pub file_path: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub diff: ::core::option::Option, + #[prost(bool, tag = "3")] + pub is_applied: bool, + #[prost(bool, tag = "4")] + pub apply_failed: bool, + #[prost(string, optional, tag = "5")] + pub error: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "6")] + pub linter_errors: ::prost::alloc::vec::Vec, + } +} +/// aiserver.v1.FileResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FileResult { + #[prost(message, optional, tag = "1")] + pub file: ::core::option::Option, + #[prost(float, tag = "2")] + pub score: f32, +} +/// aiserver.v1.MCPResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct McpResult { + #[prost(string, tag = "1")] + pub selected_tool: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub result: ::prost::alloc::string::String, +} +/// aiserver.v1.GetRelatedFilesResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetRelatedFilesResult { + #[prost(message, repeated, tag = "1")] + pub files: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `GetRelatedFilesResult`. +pub mod get_related_files_result { + /// aiserver.v1.GetRelatedFilesResult.File + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct File { + #[prost(string, tag = "1")] + pub uri: ::prost::alloc::string::String, + #[prost(float, tag = "2")] + pub score: f32, + } +} +/// aiserver.v1.FetchRulesResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FetchRulesResult { + #[prost(message, repeated, tag = "1")] + pub rules: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.EditFileResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EditFileResult { + #[prost(message, optional, tag = "1")] + pub diff: ::core::option::Option, + #[prost(bool, tag = "2")] + pub is_applied: bool, + #[prost(bool, tag = "3")] + pub apply_failed: bool, + #[prost(message, repeated, tag = "4")] + pub linter_errors: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `EditFileResult`. +pub mod edit_file_result { + /// aiserver.v1.EditFileResult.FileDiff + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct FileDiff { + #[prost(message, repeated, tag = "1")] + pub chunks: ::prost::alloc::vec::Vec, + #[prost(enumeration = "file_diff::Editor", tag = "2")] + pub editor: i32, + #[prost(bool, tag = "3")] + pub hit_timeout: bool, + } + /// Nested message and enum types in `FileDiff`. + pub mod file_diff { + /// aiserver.v1.EditFileResult.FileDiff.ChunkDiff + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ChunkDiff { + #[prost(string, tag = "1")] + pub diff_string: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub old_start: i32, + #[prost(int32, tag = "3")] + pub new_start: i32, + #[prost(int32, tag = "4")] + pub old_lines: i32, + #[prost(int32, tag = "5")] + pub new_lines: i32, + #[prost(int32, tag = "6")] + pub lines_removed: i32, + #[prost(int32, tag = "7")] + pub lines_added: i32, + } + /// aiserver.v1.EditFileResult.FileDiff.Editor + #[allow(clippy::enum_variant_names)] + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Editor { + Unspecified = 0, + Ai = 1, + Human = 2, + } + impl Editor { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "EDITOR_UNSPECIFIED", + Self::Ai => "EDITOR_AI", + Self::Human => "EDITOR_HUMAN", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "EDITOR_UNSPECIFIED" => Some(Self::Unspecified), + "EDITOR_AI" => Some(Self::Ai), + "EDITOR_HUMAN" => Some(Self::Human), + _ => None, + } + } + } + } +} +/// aiserver.v1.DiffHistoryResult +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DiffHistoryResult { + #[prost(message, repeated, tag = "40")] + pub human_changes: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `DiffHistoryResult`. +pub mod diff_history_result { + /// aiserver.v1.DiffHistoryResult.RenderedDiff + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct RenderedDiff { + #[prost(int32, tag = "1")] + pub start_line_number: i32, + #[prost(int32, tag = "2")] + pub end_line_number_exclusive: i32, + #[prost(string, repeated, tag = "3")] + pub before_context_lines: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, + #[prost(string, repeated, tag = "4")] + pub removed_lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "5")] + pub added_lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "6")] + pub after_context_lines: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, + } + /// aiserver.v1.DiffHistoryResult.HumanChange + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct HumanChange { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub rendered_diffs: ::prost::alloc::vec::Vec, + } +} +/// aiserver.v1.ContextAST +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContextAst { + #[prost(message, repeated, tag = "1")] + pub files: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.ContainerTree +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContainerTree { + #[prost(string, tag = "1")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub nodes: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.ContainerTreeNode +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContainerTreeNode { + #[prost(oneof = "container_tree_node::Node", tags = "1, 2, 3")] + pub node: ::core::option::Option, +} +/// Nested message and enum types in `ContainerTreeNode`. +pub mod container_tree_node { + /// aiserver.v1.ContainerTreeNode.Symbol + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Symbol { + #[prost(string, tag = "1")] + pub doc_string: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub value: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "6")] + pub references: ::prost::alloc::vec::Vec, + #[prost(double, tag = "7")] + pub score: f64, + } + /// aiserver.v1.ContainerTreeNode.Container + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Container { + #[prost(string, tag = "1")] + pub doc_string: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub header: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub trailer: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "5")] + pub children: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub references: ::prost::alloc::vec::Vec, + #[prost(double, tag = "7")] + pub score: f64, + } + /// aiserver.v1.ContainerTreeNode.Blob + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Blob { + #[prost(string, optional, tag = "1")] + pub value: ::core::option::Option<::prost::alloc::string::String>, + } + /// aiserver.v1.ContainerTreeNode.Reference + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Reference { + #[prost(string, tag = "1")] + pub value: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub relative_workspace_path: ::prost::alloc::string::String, + } + #[allow(clippy::enum_variant_names)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Node { + #[prost(message, tag = "1")] + Container(Container), + #[prost(message, tag = "2")] + Blob(Blob), + #[prost(message, tag = "3")] + Symbol(Symbol), + } +} +/// aiserver.v1.CommitNote +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CommitNote { + #[prost(string, tag = "1")] + pub note: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub commit_hash: ::prost::alloc::string::String, +} +/// aiserver.v1.ChatQuote +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ChatQuote { + #[prost(string, tag = "1")] + pub markdown: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub bubble_id: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub section_index: i32, +} +/// aiserver.v1.ChatExternalLink +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ChatExternalLink { + #[prost(string, tag = "1")] + pub url: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub uuid: ::prost::alloc::string::String, +} +/// aiserver.v1.StreamChatResponse +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StreamChatResponse { + #[prost(string, tag = "1")] + pub text: ::prost::alloc::string::String, + #[prost(string, optional, tag = "22")] + pub server_bubble_id: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "2")] + pub debugging_only_chat_prompt: ::core::option::Option< + ::prost::alloc::string::String, + >, + #[prost(int32, optional, tag = "3")] + pub debugging_only_token_count: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub document_citation: ::core::option::Option, + #[prost(string, optional, tag = "5")] + pub filled_prompt: ::core::option::Option<::prost::alloc::string::String>, + #[prost(bool, optional, tag = "6")] + pub is_big_file: ::core::option::Option, + #[prost(string, optional, tag = "7")] + pub intermediate_text: ::core::option::Option<::prost::alloc::string::String>, + #[prost(bool, optional, tag = "10")] + pub is_using_slow_request: ::core::option::Option, + #[prost(message, optional, tag = "8")] + pub chunk_identity: ::core::option::Option, + #[prost(message, optional, tag = "9")] + pub docs_reference: ::core::option::Option, + #[prost(message, optional, tag = "11")] + pub web_citation: ::core::option::Option, + #[prost(message, optional, tag = "12")] + pub status_updates: ::core::option::Option, + #[prost(message, optional, tag = "13")] + pub timing_info: ::core::option::Option, + #[prost(message, optional, tag = "14")] + pub symbol_link: ::core::option::Option, + #[prost(message, optional, tag = "15")] + pub file_link: ::core::option::Option, + #[prost(message, optional, tag = "16")] + pub conversation_summary: ::core::option::Option, + #[prost(message, optional, tag = "17")] + pub service_status_update: ::core::option::Option, +} +/// Nested message and enum types in `StreamChatResponse`. +pub mod stream_chat_response { + /// aiserver.v1.StreamChatResponse.ChunkIdentity + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ChunkIdentity { + #[prost(string, tag = "1")] + pub file_name: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub start_line: i32, + #[prost(int32, tag = "3")] + pub end_line: i32, + #[prost(string, tag = "4")] + pub text: ::prost::alloc::string::String, + #[prost(enumeration = "super::ChunkType", tag = "5")] + pub chunk_type: i32, + } +} +/// aiserver.v1.WebCitation +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WebCitation { + #[prost(message, repeated, tag = "1")] + pub references: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.SymbolLink +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SymbolLink { + #[prost(string, tag = "1")] + pub symbol_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub symbol_search_string: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub relative_workspace_path: ::prost::alloc::string::String, + #[prost(int32, tag = "4")] + pub rough_line_number: i32, +} +/// aiserver.v1.StatusUpdates +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StatusUpdates { + #[prost(message, repeated, tag = "1")] + pub updates: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.StatusUpdate +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StatusUpdate { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(string, optional, tag = "2")] + pub metadata: ::core::option::Option<::prost::alloc::string::String>, +} +/// aiserver.v1.ServiceStatusUpdate +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ServiceStatusUpdate { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub codicon: ::prost::alloc::string::String, + #[prost(bool, optional, tag = "3")] + pub allow_command_links_potentially_unsafe_please_only_use_for_handwritten_trusted_markdown: ::core::option::Option< + bool, + >, + #[prost(string, optional, tag = "4")] + pub action_to_run_on_status_update: ::core::option::Option< + ::prost::alloc::string::String, + >, +} +/// aiserver.v1.ServerTimingInfo +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct ServerTimingInfo { + #[prost(double, tag = "1")] + pub server_start_time: f64, + #[prost(double, tag = "2")] + pub server_first_token_time: f64, + #[prost(double, tag = "3")] + pub server_request_sent_time: f64, + #[prost(double, tag = "4")] + pub server_end_time: f64, +} +/// aiserver.v1.FileLink +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FileLink { + #[prost(string, tag = "1")] + pub display_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub relative_workspace_path: ::prost::alloc::string::String, +} +/// aiserver.v1.DocumentationCitation +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DocumentationCitation { + #[prost(message, repeated, tag = "1")] + pub chunks: ::prost::alloc::vec::Vec, +} +/// aiserver.v1.DocumentationChunk +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DocumentationChunk { + #[prost(string, tag = "1")] + pub doc_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub page_url: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub documentation_chunk: ::prost::alloc::string::String, + #[prost(float, tag = "4")] + pub score: f32, + #[prost(string, tag = "5")] + pub page_title: ::prost::alloc::string::String, +} +/// aiserver.v1.EmbeddingModel +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum EmbeddingModel { + Unspecified = 0, + VoyageCode2 = 1, + TextEmbeddingsLarge3 = 2, + Qwen15bCustom = 3, +} +impl EmbeddingModel { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "EMBEDDING_MODEL_UNSPECIFIED", + Self::VoyageCode2 => "EMBEDDING_MODEL_VOYAGE_CODE_2", + Self::TextEmbeddingsLarge3 => "EMBEDDING_MODEL_TEXT_EMBEDDINGS_LARGE_3", + Self::Qwen15bCustom => "EMBEDDING_MODEL_QWEN_1_5B_CUSTOM", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "EMBEDDING_MODEL_UNSPECIFIED" => Some(Self::Unspecified), + "EMBEDDING_MODEL_VOYAGE_CODE_2" => Some(Self::VoyageCode2), + "EMBEDDING_MODEL_TEXT_EMBEDDINGS_LARGE_3" => Some(Self::TextEmbeddingsLarge3), + "EMBEDDING_MODEL_QWEN_1_5B_CUSTOM" => Some(Self::Qwen15bCustom), + _ => None, + } + } +} +/// aiserver.v1.BuiltinTool +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum BuiltinTool { + Unspecified = 0, + Search = 1, + ReadChunk = 2, + Gotodef = 3, + Edit = 4, + UndoEdit = 5, + End = 6, + NewFile = 7, + AddTest = 8, + RunTest = 9, + DeleteTest = 10, + SaveFile = 11, + GetTests = 12, + GetSymbols = 13, + SemanticSearch = 14, + GetProjectStructure = 15, + CreateRmFiles = 16, + RunTerminalCommands = 17, + NewEdit = 18, + ReadWithLinter = 19, +} +impl BuiltinTool { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "BUILTIN_TOOL_UNSPECIFIED", + Self::Search => "BUILTIN_TOOL_SEARCH", + Self::ReadChunk => "BUILTIN_TOOL_READ_CHUNK", + Self::Gotodef => "BUILTIN_TOOL_GOTODEF", + Self::Edit => "BUILTIN_TOOL_EDIT", + Self::UndoEdit => "BUILTIN_TOOL_UNDO_EDIT", + Self::End => "BUILTIN_TOOL_END", + Self::NewFile => "BUILTIN_TOOL_NEW_FILE", + Self::AddTest => "BUILTIN_TOOL_ADD_TEST", + Self::RunTest => "BUILTIN_TOOL_RUN_TEST", + Self::DeleteTest => "BUILTIN_TOOL_DELETE_TEST", + Self::SaveFile => "BUILTIN_TOOL_SAVE_FILE", + Self::GetTests => "BUILTIN_TOOL_GET_TESTS", + Self::GetSymbols => "BUILTIN_TOOL_GET_SYMBOLS", + Self::SemanticSearch => "BUILTIN_TOOL_SEMANTIC_SEARCH", + Self::GetProjectStructure => "BUILTIN_TOOL_GET_PROJECT_STRUCTURE", + Self::CreateRmFiles => "BUILTIN_TOOL_CREATE_RM_FILES", + Self::RunTerminalCommands => "BUILTIN_TOOL_RUN_TERMINAL_COMMANDS", + Self::NewEdit => "BUILTIN_TOOL_NEW_EDIT", + Self::ReadWithLinter => "BUILTIN_TOOL_READ_WITH_LINTER", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "BUILTIN_TOOL_UNSPECIFIED" => Some(Self::Unspecified), + "BUILTIN_TOOL_SEARCH" => Some(Self::Search), + "BUILTIN_TOOL_READ_CHUNK" => Some(Self::ReadChunk), + "BUILTIN_TOOL_GOTODEF" => Some(Self::Gotodef), + "BUILTIN_TOOL_EDIT" => Some(Self::Edit), + "BUILTIN_TOOL_UNDO_EDIT" => Some(Self::UndoEdit), + "BUILTIN_TOOL_END" => Some(Self::End), + "BUILTIN_TOOL_NEW_FILE" => Some(Self::NewFile), + "BUILTIN_TOOL_ADD_TEST" => Some(Self::AddTest), + "BUILTIN_TOOL_RUN_TEST" => Some(Self::RunTest), + "BUILTIN_TOOL_DELETE_TEST" => Some(Self::DeleteTest), + "BUILTIN_TOOL_SAVE_FILE" => Some(Self::SaveFile), + "BUILTIN_TOOL_GET_TESTS" => Some(Self::GetTests), + "BUILTIN_TOOL_GET_SYMBOLS" => Some(Self::GetSymbols), + "BUILTIN_TOOL_SEMANTIC_SEARCH" => Some(Self::SemanticSearch), + "BUILTIN_TOOL_GET_PROJECT_STRUCTURE" => Some(Self::GetProjectStructure), + "BUILTIN_TOOL_CREATE_RM_FILES" => Some(Self::CreateRmFiles), + "BUILTIN_TOOL_RUN_TERMINAL_COMMANDS" => Some(Self::RunTerminalCommands), + "BUILTIN_TOOL_NEW_EDIT" => Some(Self::NewEdit), + "BUILTIN_TOOL_READ_WITH_LINTER" => Some(Self::ReadWithLinter), + _ => None, + } + } +} +/// aiserver.v1.LintSeverity +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum LintSeverity { + Unspecified = 0, + Error = 1, + Warning = 2, + Info = 3, + Hint = 4, + Ai = 5, +} +impl LintSeverity { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "LINT_SEVERITY_UNSPECIFIED", + Self::Error => "LINT_SEVERITY_ERROR", + Self::Warning => "LINT_SEVERITY_WARNING", + Self::Info => "LINT_SEVERITY_INFO", + Self::Hint => "LINT_SEVERITY_HINT", + Self::Ai => "LINT_SEVERITY_AI", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "LINT_SEVERITY_UNSPECIFIED" => Some(Self::Unspecified), + "LINT_SEVERITY_ERROR" => Some(Self::Error), + "LINT_SEVERITY_WARNING" => Some(Self::Warning), + "LINT_SEVERITY_INFO" => Some(Self::Info), + "LINT_SEVERITY_HINT" => Some(Self::Hint), + "LINT_SEVERITY_AI" => Some(Self::Ai), + _ => None, + } + } +} +/// aiserver.v1.ClientSideToolV2 +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ClientSideToolV2 { + Unspecified = 0, + ReadSemsearchFiles = 1, + ReadFileForImports = 2, + RipgrepSearch = 3, + RunTerminalCommand = 4, + ReadFile = 5, + ListDir = 6, + EditFile = 7, + FileSearch = 8, + SemanticSearchFull = 9, + CreateFile = 10, + DeleteFile = 11, + Reapply = 12, + GetRelatedFiles = 13, + ParallelApply = 14, + RunTerminalCommandV2 = 15, + FetchRules = 16, + Planner = 17, + WebSearch = 18, + Mcp = 19, + WebViewer = 20, + DiffHistory = 21, +} +impl ClientSideToolV2 { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "CLIENT_SIDE_TOOL_V2_UNSPECIFIED", + Self::ReadSemsearchFiles => "CLIENT_SIDE_TOOL_V2_READ_SEMSEARCH_FILES", + Self::ReadFileForImports => "CLIENT_SIDE_TOOL_V2_READ_FILE_FOR_IMPORTS", + Self::RipgrepSearch => "CLIENT_SIDE_TOOL_V2_RIPGREP_SEARCH", + Self::RunTerminalCommand => "CLIENT_SIDE_TOOL_V2_RUN_TERMINAL_COMMAND", + Self::ReadFile => "CLIENT_SIDE_TOOL_V2_READ_FILE", + Self::ListDir => "CLIENT_SIDE_TOOL_V2_LIST_DIR", + Self::EditFile => "CLIENT_SIDE_TOOL_V2_EDIT_FILE", + Self::FileSearch => "CLIENT_SIDE_TOOL_V2_FILE_SEARCH", + Self::SemanticSearchFull => "CLIENT_SIDE_TOOL_V2_SEMANTIC_SEARCH_FULL", + Self::CreateFile => "CLIENT_SIDE_TOOL_V2_CREATE_FILE", + Self::DeleteFile => "CLIENT_SIDE_TOOL_V2_DELETE_FILE", + Self::Reapply => "CLIENT_SIDE_TOOL_V2_REAPPLY", + Self::GetRelatedFiles => "CLIENT_SIDE_TOOL_V2_GET_RELATED_FILES", + Self::ParallelApply => "CLIENT_SIDE_TOOL_V2_PARALLEL_APPLY", + Self::RunTerminalCommandV2 => "CLIENT_SIDE_TOOL_V2_RUN_TERMINAL_COMMAND_V2", + Self::FetchRules => "CLIENT_SIDE_TOOL_V2_FETCH_RULES", + Self::Planner => "CLIENT_SIDE_TOOL_V2_PLANNER", + Self::WebSearch => "CLIENT_SIDE_TOOL_V2_WEB_SEARCH", + Self::Mcp => "CLIENT_SIDE_TOOL_V2_MCP", + Self::WebViewer => "CLIENT_SIDE_TOOL_V2_WEB_VIEWER", + Self::DiffHistory => "CLIENT_SIDE_TOOL_V2_DIFF_HISTORY", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CLIENT_SIDE_TOOL_V2_UNSPECIFIED" => Some(Self::Unspecified), + "CLIENT_SIDE_TOOL_V2_READ_SEMSEARCH_FILES" => Some(Self::ReadSemsearchFiles), + "CLIENT_SIDE_TOOL_V2_READ_FILE_FOR_IMPORTS" => Some(Self::ReadFileForImports), + "CLIENT_SIDE_TOOL_V2_RIPGREP_SEARCH" => Some(Self::RipgrepSearch), + "CLIENT_SIDE_TOOL_V2_RUN_TERMINAL_COMMAND" => Some(Self::RunTerminalCommand), + "CLIENT_SIDE_TOOL_V2_READ_FILE" => Some(Self::ReadFile), + "CLIENT_SIDE_TOOL_V2_LIST_DIR" => Some(Self::ListDir), + "CLIENT_SIDE_TOOL_V2_EDIT_FILE" => Some(Self::EditFile), + "CLIENT_SIDE_TOOL_V2_FILE_SEARCH" => Some(Self::FileSearch), + "CLIENT_SIDE_TOOL_V2_SEMANTIC_SEARCH_FULL" => Some(Self::SemanticSearchFull), + "CLIENT_SIDE_TOOL_V2_CREATE_FILE" => Some(Self::CreateFile), + "CLIENT_SIDE_TOOL_V2_DELETE_FILE" => Some(Self::DeleteFile), + "CLIENT_SIDE_TOOL_V2_REAPPLY" => Some(Self::Reapply), + "CLIENT_SIDE_TOOL_V2_GET_RELATED_FILES" => Some(Self::GetRelatedFiles), + "CLIENT_SIDE_TOOL_V2_PARALLEL_APPLY" => Some(Self::ParallelApply), + "CLIENT_SIDE_TOOL_V2_RUN_TERMINAL_COMMAND_V2" => { + Some(Self::RunTerminalCommandV2) + } + "CLIENT_SIDE_TOOL_V2_FETCH_RULES" => Some(Self::FetchRules), + "CLIENT_SIDE_TOOL_V2_PLANNER" => Some(Self::Planner), + "CLIENT_SIDE_TOOL_V2_WEB_SEARCH" => Some(Self::WebSearch), + "CLIENT_SIDE_TOOL_V2_MCP" => Some(Self::Mcp), + "CLIENT_SIDE_TOOL_V2_WEB_VIEWER" => Some(Self::WebViewer), + "CLIENT_SIDE_TOOL_V2_DIFF_HISTORY" => Some(Self::DiffHistory), + _ => None, + } + } +} +/// aiserver.v1.RunTerminalCommandEndedReason +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RunTerminalCommandEndedReason { + Unspecified = 0, + ExecutionCompleted = 1, + ExecutionAborted = 2, + ExecutionFailed = 3, + ErrorOccurredCheckingReason = 4, +} +impl RunTerminalCommandEndedReason { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "RUN_TERMINAL_COMMAND_ENDED_REASON_UNSPECIFIED", + Self::ExecutionCompleted => { + "RUN_TERMINAL_COMMAND_ENDED_REASON_EXECUTION_COMPLETED" + } + Self::ExecutionAborted => { + "RUN_TERMINAL_COMMAND_ENDED_REASON_EXECUTION_ABORTED" + } + Self::ExecutionFailed => "RUN_TERMINAL_COMMAND_ENDED_REASON_EXECUTION_FAILED", + Self::ErrorOccurredCheckingReason => { + "RUN_TERMINAL_COMMAND_ENDED_REASON_ERROR_OCCURRED_CHECKING_REASON" + } + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "RUN_TERMINAL_COMMAND_ENDED_REASON_UNSPECIFIED" => Some(Self::Unspecified), + "RUN_TERMINAL_COMMAND_ENDED_REASON_EXECUTION_COMPLETED" => { + Some(Self::ExecutionCompleted) + } + "RUN_TERMINAL_COMMAND_ENDED_REASON_EXECUTION_ABORTED" => { + Some(Self::ExecutionAborted) + } + "RUN_TERMINAL_COMMAND_ENDED_REASON_EXECUTION_FAILED" => { + Some(Self::ExecutionFailed) + } + "RUN_TERMINAL_COMMAND_ENDED_REASON_ERROR_OCCURRED_CHECKING_REASON" => { + Some(Self::ErrorOccurredCheckingReason) + } + _ => None, + } + } +} +/// aiserver.v1.ChunkType +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ChunkType { + Unspecified = 0, + Codebase = 1, + LongFile = 2, + Docs = 3, +} +impl ChunkType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "CHUNK_TYPE_UNSPECIFIED", + Self::Codebase => "CHUNK_TYPE_CODEBASE", + Self::LongFile => "CHUNK_TYPE_LONG_FILE", + Self::Docs => "CHUNK_TYPE_DOCS", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CHUNK_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "CHUNK_TYPE_CODEBASE" => Some(Self::Codebase), + "CHUNK_TYPE_LONG_FILE" => Some(Self::LongFile), + "CHUNK_TYPE_DOCS" => Some(Self::Docs), + _ => None, + } + } +} diff --git a/src/chat/aiserver/v1/lite.proto b/src/chat/aiserver/v1/lite.proto index 7a6cc24..3bbb490 100644 --- a/src/chat/aiserver/v1/lite.proto +++ b/src/chat/aiserver/v1/lite.proto @@ -1,186 +1,79 @@ syntax = "proto3"; package aiserver.v1; -enum ClientSideToolV2 { // aiserver.v1.ClientSideToolV2 - CLIENT_SIDE_TOOL_V2_UNSPECIFIED = 0; - CLIENT_SIDE_TOOL_V2_READ_SEMSEARCH_FILES = 1; - CLIENT_SIDE_TOOL_V2_READ_FILE_FOR_IMPORTS = 2; - CLIENT_SIDE_TOOL_V2_RIPGREP_SEARCH = 3; - CLIENT_SIDE_TOOL_V2_RUN_TERMINAL_COMMAND = 4; - CLIENT_SIDE_TOOL_V2_READ_FILE = 5; - CLIENT_SIDE_TOOL_V2_LIST_DIR = 6; - CLIENT_SIDE_TOOL_V2_EDIT_FILE = 7; - CLIENT_SIDE_TOOL_V2_FILE_SEARCH = 8; - CLIENT_SIDE_TOOL_V2_SEMANTIC_SEARCH_FULL = 9; - CLIENT_SIDE_TOOL_V2_CREATE_FILE = 10; - CLIENT_SIDE_TOOL_V2_DELETE_FILE = 11; +import "timestamp.proto"; +message AvailableModelsRequest { // aiserver.v1.AvailableModelsRequest + bool is_nightly = 1; + bool include_long_context_models = 2; } -enum EmbeddingModel { // aiserver.v1.EmbeddingModel - EMBEDDING_MODEL_UNSPECIFIED = 0; - EMBEDDING_MODEL_VOYAGE_CODE_2 = 1; - EMBEDDING_MODEL_TEXT_EMBEDDINGS_LARGE_3 = 2; - EMBEDDING_MODEL_QWEN_1_5B_CUSTOM = 3; -} -enum ChunkType { // aiserver.v1.ChunkType - CHUNK_TYPE_UNSPECIFIED = 0; - CHUNK_TYPE_CODEBASE = 1; - CHUNK_TYPE_LONG_FILE = 2; - CHUNK_TYPE_DOCS = 3; -} -enum FastApplySource { // aiserver.v1.FastApplySource - FAST_APPLY_SOURCE_UNSPECIFIED = 0; - FAST_APPLY_SOURCE_COMPOSER = 1; - FAST_APPLY_SOURCE_CLICKED_APPLY = 2; - FAST_APPLY_SOURCE_CACHED_APPLY = 3; -} -enum BuiltinTool { // aiserver.v1.BuiltinTool - BUILTIN_TOOL_UNSPECIFIED = 0; - BUILTIN_TOOL_SEARCH = 1; - BUILTIN_TOOL_READ_CHUNK = 2; - BUILTIN_TOOL_GOTODEF = 3; - BUILTIN_TOOL_EDIT = 4; - BUILTIN_TOOL_UNDO_EDIT = 5; - BUILTIN_TOOL_END = 6; - BUILTIN_TOOL_NEW_FILE = 7; - BUILTIN_TOOL_ADD_TEST = 8; - BUILTIN_TOOL_RUN_TEST = 9; - BUILTIN_TOOL_DELETE_TEST = 10; - BUILTIN_TOOL_SAVE_FILE = 11; - BUILTIN_TOOL_GET_TESTS = 12; - BUILTIN_TOOL_GET_SYMBOLS = 13; - BUILTIN_TOOL_SEMANTIC_SEARCH = 14; - BUILTIN_TOOL_GET_PROJECT_STRUCTURE = 15; - BUILTIN_TOOL_CREATE_RM_FILES = 16; - BUILTIN_TOOL_RUN_TERMINAL_COMMANDS = 17; - BUILTIN_TOOL_NEW_EDIT = 18; - BUILTIN_TOOL_READ_WITH_LINTER = 19; -} -enum FeatureType { // aiserver.v1.FeatureType - FEATURE_TYPE_UNSPECIFIED = 0; - FEATURE_TYPE_EDIT = 1; - FEATURE_TYPE_GENERATE = 2; - FEATURE_TYPE_INLINE_LONG_COMPLETION = 3; -} -enum TaskStatus { // aiserver.v1.TaskStatus - TASK_STATUS_UNSPECIFIED = 0; - TASK_STATUS_RUNNING = 1; - TASK_STATUS_PAUSED = 2; - TASK_STATUS_DONE = 3; - TASK_STATUS_NOT_STARTED = 4; -} -enum RerankerAlgorithm { // aiserver.v1.RerankerAlgorithm - RERANKER_ALGORITHM_UNSPECIFIED = 0; - RERANKER_ALGORITHM_LULEA = 1; - RERANKER_ALGORITHM_UMEA = 2; - RERANKER_ALGORITHM_NONE = 3; - RERANKER_ALGORITHM_LLAMA = 4; - RERANKER_ALGORITHM_STARCODER_V1 = 5; - RERANKER_ALGORITHM_GPT_3_5_LOGPROBS = 6; - RERANKER_ALGORITHM_LULEA_HAIKU = 7; - RERANKER_ALGORITHM_COHERE = 8; - RERANKER_ALGORITHM_VOYAGE = 9; - RERANKER_ALGORITHM_VOYAGE_EMBEDS = 10; - RERANKER_ALGORITHM_IDENTITY = 11; - RERANKER_ALGORITHM_ADA_EMBEDS = 12; -} -enum RechunkerChoice { // aiserver.v1.RechunkerChoice - RECHUNKER_CHOICE_UNSPECIFIED = 0; - RECHUNKER_CHOICE_IDENTITY = 1; - RECHUNKER_CHOICE_600_TOKS = 2; - RECHUNKER_CHOICE_2400_TOKS = 3; - RECHUNKER_CHOICE_4000_TOKS = 4; -} -enum LintGenerator { // aiserver.v1.LintGenerator - LINT_GENERATOR_UNSPECIFIED = 0; - LINT_GENERATOR_NAIVE = 1; - LINT_GENERATOR_COMMENT_PIPELINE = 2; - LINT_GENERATOR_SIMPLE_BUG = 3; - LINT_GENERATOR_SIMPLE_LINT_RULES = 4; -} -enum LintDiscriminator { // aiserver.v1.LintDiscriminator - LINT_DISCRIMINATOR_UNSPECIFIED = 0; - LINT_DISCRIMINATOR_SPECIFIC_RULES = 1; - LINT_DISCRIMINATOR_COMPILE_ERRORS = 2; - LINT_DISCRIMINATOR_CHANGE_BEHAVIOR = 3; - LINT_DISCRIMINATOR_RELEVANCE = 4; - LINT_DISCRIMINATOR_USER_AWARENESS = 5; - LINT_DISCRIMINATOR_CORRECTNESS = 6; - LINT_DISCRIMINATOR_CHUNKING = 7; - LINT_DISCRIMINATOR_TYPO = 8; - LINT_DISCRIMINATOR_CONFIDENCE = 9; - LINT_DISCRIMINATOR_DISMISSED_BUGS = 10; -} -enum CppSource { // aiserver.v1.CppSource - CPP_SOURCE_UNSPECIFIED = 0; - CPP_SOURCE_LINE_CHANGE = 1; - CPP_SOURCE_TYPING = 2; - CPP_SOURCE_OPTION_HOLD = 3; - CPP_SOURCE_LINTER_ERRORS = 4; - CPP_SOURCE_PARAMETER_HINTS = 5; - CPP_SOURCE_CURSOR_PREDICTION = 6; - CPP_SOURCE_MANUAL_TRIGGER = 7; - CPP_SOURCE_EDITOR_CHANGE = 8; -} -enum ChunkingStrategy { // aiserver.v1.ChunkingStrategy - CHUNKING_STRATEGY_UNSPECIFIED = 0; - CHUNKING_STRATEGY_DEFAULT = 1; +message AvailableModelsResponse { // aiserver.v1.AvailableModelsResponse + message AvailableModel { // aiserver.v1.AvailableModelsResponse.AvailableModel + string name = 1; + bool default_on = 2; + optional bool is_long_context_only = 3; + optional bool is_chat_only = 4; + } + repeated AvailableModel models = 2; + repeated string model_names = 1; } message ErrorDetails { // aiserver.v1.ErrorDetails - enum Error { // aiserver.v1.ErrorDetails.Error - ERROR_UNSPECIFIED = 0; - ERROR_BAD_API_KEY = 1; - ERROR_NOT_LOGGED_IN = 2; - ERROR_INVALID_AUTH_ID = 3; - ERROR_NOT_HIGH_ENOUGH_PERMISSIONS = 4; - ERROR_BAD_MODEL_NAME = 5; - ERROR_USER_NOT_FOUND = 6; - ERROR_FREE_USER_RATE_LIMIT_EXCEEDED = 7; - ERROR_PRO_USER_RATE_LIMIT_EXCEEDED = 8; - ERROR_FREE_USER_USAGE_LIMIT = 9; - ERROR_PRO_USER_USAGE_LIMIT = 10; - ERROR_AUTH_TOKEN_NOT_FOUND = 11; - ERROR_AUTH_TOKEN_EXPIRED = 12; - ERROR_OPENAI = 13; - ERROR_OPENAI_RATE_LIMIT_EXCEEDED = 14; - ERROR_OPENAI_ACCOUNT_LIMIT_EXCEEDED = 15; - ERROR_TASK_UUID_NOT_FOUND = 16; - ERROR_TASK_NO_PERMISSIONS = 17; - ERROR_AGENT_REQUIRES_LOGIN = 18; - ERROR_AGENT_ENGINE_NOT_FOUND = 19; - ERROR_MAX_TOKENS = 20; - ERROR_USER_ABORTED_REQUEST = 21; - ERROR_GENERIC_RATE_LIMIT_EXCEEDED = 22; - ERROR_PRO_USER_ONLY = 23; - ERROR_API_KEY_NOT_SUPPORTED = 24; - ERROR_SLASH_EDIT_FILE_TOO_LONG = 26; - ERROR_FILE_UNSUPPORTED = 27; - ERROR_GPT_4_VISION_PREVIEW_RATE_LIMIT = 28; - ERROR_CUSTOM_MESSAGE = 29; - ERROR_OUTDATED_CLIENT = 30; - ERROR_CLAUDE_IMAGE_TOO_LARGE = 31; - ERROR_GITGRAPH_NOT_FOUND = 32; - ERROR_FILE_NOT_FOUND = 33; - ERROR_API_KEY_RATE_LIMIT = 34; - ERROR_DEBOUNCED = 35; - ERROR_BAD_REQUEST = 36; - ERROR_REPOSITORY_SERVICE_REPOSITORY_IS_NOT_INITIALIZED = 37; - ERROR_UNAUTHORIZED = 38; - ERROR_NOT_FOUND = 39; - ERROR_DEPRECATED = 40; - ERROR_RESOURCE_EXHAUSTED = 41; - } + enum Error { // aiserver.v1.ErrorDetails.Error + ERROR_UNSPECIFIED = 0; + ERROR_BAD_API_KEY = 1; + ERROR_BAD_USER_API_KEY = 42; + ERROR_NOT_LOGGED_IN = 2; + ERROR_INVALID_AUTH_ID = 3; + ERROR_NOT_HIGH_ENOUGH_PERMISSIONS = 4; + ERROR_AGENT_REQUIRES_LOGIN = 18; + ERROR_BAD_MODEL_NAME = 5; + ERROR_NOT_FOUND = 39; + ERROR_DEPRECATED = 40; + ERROR_USER_NOT_FOUND = 6; + ERROR_FREE_USER_RATE_LIMIT_EXCEEDED = 7; + ERROR_PRO_USER_RATE_LIMIT_EXCEEDED = 8; + ERROR_FREE_USER_USAGE_LIMIT = 9; + ERROR_PRO_USER_USAGE_LIMIT = 10; + ERROR_RESOURCE_EXHAUSTED = 41; + ERROR_AUTH_TOKEN_NOT_FOUND = 11; + ERROR_AUTH_TOKEN_EXPIRED = 12; + ERROR_OPENAI = 13; + ERROR_OPENAI_RATE_LIMIT_EXCEEDED = 14; + ERROR_OPENAI_ACCOUNT_LIMIT_EXCEEDED = 15; + ERROR_TASK_UUID_NOT_FOUND = 16; + ERROR_TASK_NO_PERMISSIONS = 17; + ERROR_AGENT_ENGINE_NOT_FOUND = 19; + ERROR_MAX_TOKENS = 20; + ERROR_PRO_USER_ONLY = 23; + ERROR_API_KEY_NOT_SUPPORTED = 24; + ERROR_USER_ABORTED_REQUEST = 21; + ERROR_TIMEOUT = 25; + ERROR_GENERIC_RATE_LIMIT_EXCEEDED = 22; + ERROR_SLASH_EDIT_FILE_TOO_LONG = 26; + ERROR_FILE_UNSUPPORTED = 27; + ERROR_GPT_4_VISION_PREVIEW_RATE_LIMIT = 28; + ERROR_CUSTOM_MESSAGE = 29; + ERROR_OUTDATED_CLIENT = 30; + ERROR_CLAUDE_IMAGE_TOO_LARGE = 31; + ERROR_GITGRAPH_NOT_FOUND = 32; + ERROR_FILE_NOT_FOUND = 33; + ERROR_API_KEY_RATE_LIMIT = 34; + ERROR_DEBOUNCED = 35; + ERROR_BAD_REQUEST = 36; + ERROR_REPOSITORY_SERVICE_REPOSITORY_IS_NOT_INITIALIZED = 37; + ERROR_UNAUTHORIZED = 38; + ERROR_CONVERSATION_TOO_LONG = 43; + } Error error = 1; CustomErrorDetails details = 2; optional bool is_expected = 3; } - message CustomErrorDetails { // aiserver.v1.CustomErrorDetails string title = 1; string detail = 2; optional bool allow_command_links_potentially_unsafe_please_only_use_for_handwritten_trusted_markdown = 3; optional bool is_retryable = 4; optional bool show_request_id = 5; + optional bool should_show_immediate_error = 6; } - message GetChatRequest { // aiserver.v1.GetChatRequest CurrentFileInfo current_file = 1; repeated ConversationMessage conversation = 2; @@ -211,10 +104,121 @@ message GetChatRequest { // aiserver.v1.GetChatRequest optional bool is_composer = 27; optional bool runnable_code_blocks = 28; optional bool should_cache = 29; + optional bool allow_model_fallbacks = 30; + optional int32 number_of_times_shown_fallback_model_warning = 31; +} +message RepositoryInfo { // aiserver.v1.RepositoryInfo + string relative_workspace_path = 1; + repeated string remote_urls = 2; + repeated string remote_names = 3; + string repo_name = 4; + string repo_owner = 5; + bool is_tracked = 6; + bool is_local = 7; + optional int32 num_files = 8; + optional double orthogonal_transform_seed = 9; + optional EmbeddingModel preferred_embedding_model = 10; +} +enum EmbeddingModel { // aiserver.v1.EmbeddingModel + EMBEDDING_MODEL_UNSPECIFIED = 0; + EMBEDDING_MODEL_VOYAGE_CODE_2 = 1; + EMBEDDING_MODEL_TEXT_EMBEDDINGS_LARGE_3 = 2; + EMBEDDING_MODEL_QWEN_1_5B_CUSTOM = 3; +} +message ModelDetails { // aiserver.v1.ModelDetails + optional string model_name = 1; + optional string api_key = 2; + optional bool enable_ghost_mode = 3; + optional AzureState azure_state = 4; + optional bool enable_slow_pool = 5; + optional string openai_api_base_url = 6; +} +message AzureState { // aiserver.v1.AzureState + string api_key = 1; + string base_url = 2; + string deployment = 3; + bool use_azure = 4; +} +message LinterErrors { // aiserver.v1.LinterErrors + string relative_workspace_path = 1; + repeated LinterError errors = 2; + string file_contents = 3; +} +message LinterError { // aiserver.v1.LinterError + string message = 1; + CursorRange range = 2; + optional string source = 3; + repeated Diagnostic.RelatedInformation related_information = 4; + optional Diagnostic.DiagnosticSeverity severity = 5; +} +message CursorRange { // aiserver.v1.CursorRange + CursorPosition start_position = 1; + CursorPosition end_position = 2; +} +message CursorPosition { // aiserver.v1.CursorPosition + int32 line = 1; + int32 column = 2; +} +message ExplicitContext { // aiserver.v1.ExplicitContext + string context = 1; + optional string repo_context = 2; + repeated CursorRule rules = 3; +} +message CursorRule { // aiserver.v1.CursorRule + string name = 1; + string description = 2; + optional string body = 3; + optional bool is_from_glob = 4; +} +message DebugInfo { // aiserver.v1.DebugInfo + message Variable { // aiserver.v1.DebugInfo.Variable + string name = 1; + string value = 2; + optional string type = 3; + } + message Scope { // aiserver.v1.DebugInfo.Scope + string name = 1; + repeated Variable variables = 2; + } + message CallStackFrame { // aiserver.v1.DebugInfo.CallStackFrame + string relative_workspace_path = 1; + int32 line_number = 2; + string function_name = 3; + repeated Scope scopes = 4; + } + message Breakpoint { // aiserver.v1.DebugInfo.Breakpoint + string relative_workspace_path = 1; + int32 line_number = 2; + repeated string lines_before_breakpoint = 3; + repeated string lines_after_breakpoint = 4; + optional string exception_info = 5; + } + Breakpoint breakpoint = 1; + repeated CallStackFrame call_stack = 2; + repeated CodeBlock history = 3; +} +message CodeBlock { // aiserver.v1.CodeBlock + message Signatures { // aiserver.v1.CodeBlock.Signatures + repeated CursorRange ranges = 1; + } + string relative_workspace_path = 1; + optional string file_contents = 2; + optional int32 file_contents_length = 9; + CursorRange range = 3; + string contents = 4; + Signatures signatures = 5; + optional string override_contents = 6; + optional string original_contents = 7; + repeated DetailedLine detailed_lines = 8; +} +message DetailedLine { // aiserver.v1.DetailedLine + string text = 1; + float line_number = 2; + bool is_signature = 3; } message CurrentFileInfo { // aiserver.v1.CurrentFileInfo - message NotebookCell { // aiserver.v1.CurrentFileInfo.NotebookCell - } + message NotebookCell { // aiserver.v1.CurrentFileInfo.NotebookCell + } string relative_workspace_path = 1; string contents = 2; bool rely_on_filesync = 18; @@ -233,6 +237,35 @@ message CurrentFileInfo { // aiserver.v1.CurrentFileInfo repeated int32 cell_start_lines = 15; string workspace_root_path = 19; } +message Diagnostic { // aiserver.v1.Diagnostic + enum DiagnosticSeverity { // aiserver.v1.Diagnostic.DiagnosticSeverity + DIAGNOSTIC_SEVERITY_UNSPECIFIED = 0; + DIAGNOSTIC_SEVERITY_ERROR = 1; + DIAGNOSTIC_SEVERITY_WARNING = 2; + DIAGNOSTIC_SEVERITY_INFORMATION = 3; + DIAGNOSTIC_SEVERITY_HINT = 4; + } + message RelatedInformation { // aiserver.v1.Diagnostic.RelatedInformation + string message = 1; + CursorRange range = 2; + } + string message = 1; + CursorRange range = 2; + DiagnosticSeverity severity = 3; + repeated RelatedInformation related_information = 4; +} +message DataframeInfo { // aiserver.v1.DataframeInfo + message Column { // aiserver.v1.DataframeInfo.Column + string key = 1; + string type = 2; + } + string name = 1; + string shape = 2; + int32 data_dimensionality = 3; + repeated Column columns = 6; + int32 row_count = 7; + string index_column = 8; +} message BM25Chunk { // aiserver.v1.BM25Chunk string content = 1; SimplestRange range = 2; @@ -243,170 +276,112 @@ message SimplestRange { // aiserver.v1.SimplestRange int32 start_line = 1; int32 end_line_inclusive = 2; } -message CursorPosition { // aiserver.v1.CursorPosition - int32 line = 1; - int32 column = 2; -} -message DataframeInfo { // aiserver.v1.DataframeInfo - message Column { // aiserver.v1.DataframeInfo.Column - string key = 1; - string type = 2; - } - string name = 1; - string shape = 2; - int32 data_dimensionality = 3; - repeated Column columns = 6; - int32 row_count = 7; - string index_column = 8; -} -message CursorRange { // aiserver.v1.CursorRange - CursorPosition start_position = 1; - CursorPosition end_position = 2; -} -message Diagnostic { // aiserver.v1.Diagnostic - enum DiagnosticSeverity { // aiserver.v1.Diagnostic.DiagnosticSeverity - DIAGNOSTIC_SEVERITY_UNSPECIFIED = 0; - DIAGNOSTIC_SEVERITY_ERROR = 1; - DIAGNOSTIC_SEVERITY_WARNING = 2; - DIAGNOSTIC_SEVERITY_INFORMATION = 3; - DIAGNOSTIC_SEVERITY_HINT = 4; - } - message RelatedInformation { // aiserver.v1.Diagnostic.RelatedInformation - string message = 1; - CursorRange range = 2; - } - string message = 1; - CursorRange range = 2; - DiagnosticSeverity severity = 3; - repeated RelatedInformation related_information = 4; -} message ConversationMessage { // aiserver.v1.ConversationMessage - enum MessageType { // aiserver.v1.ConversationMessage.MessageType - MESSAGE_TYPE_UNSPECIFIED = 0; - MESSAGE_TYPE_HUMAN = 1; - MESSAGE_TYPE_AI = 2; + enum MessageType { // aiserver.v1.ConversationMessage.MessageType + MESSAGE_TYPE_UNSPECIFIED = 0; + MESSAGE_TYPE_HUMAN = 1; + MESSAGE_TYPE_AI = 2; + } + message CodeChunk { // aiserver.v1.ConversationMessage.CodeChunk + enum Intent { // aiserver.v1.ConversationMessage.CodeChunk.Intent + INTENT_UNSPECIFIED = 0; + INTENT_COMPOSER_FILE = 1; + INTENT_COMPRESSED_COMPOSER_FILE = 2; + INTENT_RECENTLY_VIEWED_FILE = 3; + INTENT_OUTLINE = 4; + INTENT_MENTIONED_FILE = 5; + INTENT_CODE_SELECTION = 6; } - message CodeChunk { // aiserver.v1.ConversationMessage.CodeChunk - enum SummarizationStrategy { // aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy - SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0; - SUMMARIZATION_STRATEGY_SUMMARIZED = 1; - SUMMARIZATION_STRATEGY_EMBEDDED = 2; - } - enum Intent { // aiserver.v1.ConversationMessage.CodeChunk.Intent - INTENT_UNSPECIFIED = 0; - INTENT_COMPOSER_FILE = 1; - INTENT_COMPRESSED_COMPOSER_FILE = 2; - INTENT_RECENTLY_VIEWED_FILE = 3; - INTENT_OUTLINE = 4; - INTENT_MENTIONED_FILE = 5; - } - string relative_workspace_path = 1; - int32 start_line_number = 2; - repeated string lines = 3; - optional SummarizationStrategy summarization_strategy = 4; - string language_identifier = 5; - optional Intent intent = 6; - optional bool is_final_version = 7; - optional bool is_first_version = 8; - optional bool contents_are_missing = 9; + enum SummarizationStrategy { // aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy + SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0; + SUMMARIZATION_STRATEGY_SUMMARIZED = 1; + SUMMARIZATION_STRATEGY_EMBEDDED = 2; } - message ApproximateLintError { // aiserver.v1.ConversationMessage.ApproximateLintError - string message = 1; - string value = 2; - int32 start_line = 3; - int32 end_line = 4; - int32 start_column = 5; - int32 end_column = 6; - } - message Lints { // aiserver.v1.ConversationMessage.Lints - GetLintsForChangeResponse lints = 1; - string chat_codeblock_model_value = 2; - } - message ToolResult { // aiserver.v1.ConversationMessage.ToolResult - message CodeChunk { // aiserver.v1.ConversationMessage.CodeChunk - enum SummarizationStrategy { // aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy - SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0; - SUMMARIZATION_STRATEGY_SUMMARIZED = 1; - SUMMARIZATION_STRATEGY_EMBEDDED = 2; - } - enum Intent { // aiserver.v1.ConversationMessage.CodeChunk.Intent - INTENT_UNSPECIFIED = 0; - INTENT_COMPOSER_FILE = 1; - INTENT_COMPRESSED_COMPOSER_FILE = 2; - INTENT_RECENTLY_VIEWED_FILE = 3; - INTENT_OUTLINE = 4; - INTENT_MENTIONED_FILE = 5; - } - string relative_workspace_path = 1; - int32 start_line_number = 2; - repeated string lines = 3; - optional SummarizationStrategy summarization_strategy = 4; - string language_identifier = 5; - optional Intent intent = 6; - optional bool is_final_version = 7; - optional bool is_first_version = 8; - optional bool contents_are_missing = 9; - } - string tool_call_id = 1; - string tool_name = 2; - uint32 tool_index = 3; - string args = 4; - string raw_args = 5; - repeated CodeChunk attached_code_chunks = 6; - optional string content = 7; - ClientSideToolV2Result result = 8; - optional ToolResultError error = 9; - } - message NotepadContext { // aiserver.v1.ConversationMessage.NotepadContext - message CodeChunk { // aiserver.v1.ConversationMessage.CodeChunk - enum SummarizationStrategy { // aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy - SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0; - SUMMARIZATION_STRATEGY_SUMMARIZED = 1; - SUMMARIZATION_STRATEGY_EMBEDDED = 2; - } - enum Intent { // aiserver.v1.ConversationMessage.CodeChunk.Intent - INTENT_UNSPECIFIED = 0; - INTENT_COMPOSER_FILE = 1; - INTENT_COMPRESSED_COMPOSER_FILE = 2; - INTENT_RECENTLY_VIEWED_FILE = 3; - INTENT_OUTLINE = 4; - INTENT_MENTIONED_FILE = 5; - } - string relative_workspace_path = 1; - int32 start_line_number = 2; - repeated string lines = 3; - optional SummarizationStrategy summarization_strategy = 4; - string language_identifier = 5; - optional Intent intent = 6; - optional bool is_final_version = 7; - optional bool is_first_version = 8; - optional bool contents_are_missing = 9; - } - string name = 1; - string text = 2; - repeated CodeChunk attached_code_chunks = 3; - repeated string attached_folders = 4; - repeated Commit commits = 5; - repeated PullRequest pull_requests = 6; - repeated GitDiff git_diffs = 7; - repeated ImageProto images = 8; - } - message EditTrailContext { // aiserver.v1.ConversationMessage.EditTrailContext - message EditLocation { // aiserver.v1.ConversationMessage.EditLocation - string relative_workspace_path = 1; - SimplestRange range = 3; - SimplestRange initial_range = 4; - string context_lines = 5; - string text = 6; - SimplestRange text_range = 7; - } - string unique_id = 1; - repeated EditLocation edit_trail_sorted = 2; - } - message RecentLocation { // aiserver.v1.ConversationMessage.RecentLocation - string relative_workspace_path = 1; - int32 line_number = 2; + string relative_workspace_path = 1; + int32 start_line_number = 2; + repeated string lines = 3; + optional SummarizationStrategy summarization_strategy = 4; + string language_identifier = 5; + optional Intent intent = 6; + optional bool is_final_version = 7; + optional bool is_first_version = 8; + optional bool contents_are_missing = 9; + } + message ToolResult { // aiserver.v1.ConversationMessage.ToolResult + string tool_call_id = 1; + string tool_name = 2; + uint32 tool_index = 3; + string args = 4; + string raw_args = 5; + repeated CodeChunk attached_code_chunks = 6; + optional string content = 7; + ClientSideToolV2Result result = 8; + optional ToolResultError error = 9; + repeated ImageProto images = 10; + } + message MultiRangeCodeChunk { // aiserver.v1.ConversationMessage.MultiRangeCodeChunk + message RangeWithPriority { // aiserver.v1.ConversationMessage.MultiRangeCodeChunk.RangeWithPriority + SimplestRange range = 1; + double priority = 2; } + repeated RangeWithPriority ranges = 1; + string content = 2; + string relative_workspace_path = 3; + } + message NotepadContext { // aiserver.v1.ConversationMessage.NotepadContext + string name = 1; + string text = 2; + repeated CodeChunk attached_code_chunks = 3; + repeated string attached_folders = 4; + repeated Commit commits = 5; + repeated PullRequest pull_requests = 6; + repeated GitDiff git_diffs = 7; + repeated ImageProto images = 8; + } + message ComposerContext { // aiserver.v1.ConversationMessage.ComposerContext + string name = 1; + ConversationSummary conversation_summary = 2; + } + message EditLocation { // aiserver.v1.ConversationMessage.EditLocation + string relative_workspace_path = 1; + SimplestRange range = 3; + SimplestRange initial_range = 4; + string context_lines = 5; + string text = 6; + SimplestRange text_range = 7; + } + message EditTrailContext { // aiserver.v1.ConversationMessage.EditTrailContext + string unique_id = 1; + repeated EditLocation edit_trail_sorted = 2; + } + message ApproximateLintError { // aiserver.v1.ConversationMessage.ApproximateLintError + string message = 1; + string value = 2; + int32 start_line = 3; + int32 end_line = 4; + int32 start_column = 5; + int32 end_column = 6; + } + message Lints { // aiserver.v1.ConversationMessage.Lints + GetLintsForChangeResponse lints = 1; + string chat_codeblock_model_value = 2; + } + message RecentLocation { // aiserver.v1.ConversationMessage.RecentLocation + string relative_workspace_path = 1; + int32 line_number = 2; + } + message RenderedDiff { // aiserver.v1.ConversationMessage.RenderedDiff + int32 start_line_number = 1; + int32 end_line_number_exclusive = 2; + repeated string before_context_lines = 3; + repeated string removed_lines = 4; + repeated string added_lines = 5; + repeated string after_context_lines = 6; + } + message HumanChange { // aiserver.v1.ConversationMessage.HumanChange + string relative_workspace_path = 1; + repeated RenderedDiff rendered_diffs = 2; + } string text = 1; MessageType type = 2; repeated CodeChunk attached_code_chunks = 3; @@ -439,67 +414,369 @@ message ConversationMessage { // aiserver.v1.ConversationMessage bool is_agentic = 29; repeated ComposerFileDiffHistory file_diff_trajectories = 30; optional ConversationSummary conversation_summary = 31; + bool existed_subsequent_terminal_command = 33; + bool existed_previous_terminal_command = 34; + repeated DocsReference docs_references = 35; + repeated WebReference web_references = 36; + optional ViewableGitContext git_context = 37; + repeated ListDirResult attached_folders_list_dir_results = 38; + optional ConversationSummary cached_conversation_summary = 39; + repeated HumanChange human_changes = 40; + bool attached_human_changes = 41; + repeated ComposerContext summarized_composers = 42; + repeated CursorRule cursor_rules = 43; } -message CodeBlock { // aiserver.v1.CodeBlock - message Signatures { // aiserver.v1.CodeBlock.Signatures - repeated CursorRange ranges = 1; - } - string relative_workspace_path = 1; - optional string file_contents = 2; - CursorRange range = 3; - string contents = 4; - Signatures signatures = 5; - optional string override_contents = 6; - optional string original_contents = 7; - repeated DetailedLine detailed_lines = 8; +message WebReference { // aiserver.v1.WebReference + string title = 2; + string url = 1; + string chunk = 3; } -message DetailedLine { // aiserver.v1.DetailedLine - string text = 1; - float line_number = 2; - bool is_signature = 3; +message ViewableGitContext { // aiserver.v1.ViewableGitContext + optional ViewableCommitProps commit_data = 1; + optional ViewablePRProps pull_request_data = 2; + repeated ViewableDiffProps diff_data = 3; } -message Commit { // aiserver.v1.Commit - string sha = 1; +message ViewablePRProps { // aiserver.v1.ViewablePRProps + string title = 1; + string body = 2; + repeated DiffFile files = 3; +} +message DiffFile { // aiserver.v1.DiffFile + string file_details = 1; + string file_name = 2; +} +message ViewableDiffProps { // aiserver.v1.ViewableDiffProps + repeated DiffFile files = 1; + string diff_preface = 2; +} +message ViewableCommitProps { // aiserver.v1.ViewableCommitProps + string description = 1; string message = 2; - string description = 3; - repeated FileDiff diff = 4; - string author = 5; - string date = 6; + repeated DiffFile files = 3; +} +message UserResponseToSuggestedCodeBlock { // aiserver.v1.UserResponseToSuggestedCodeBlock + enum UserResponseType { // aiserver.v1.UserResponseToSuggestedCodeBlock.UserResponseType + USER_RESPONSE_TYPE_UNSPECIFIED = 0; + USER_RESPONSE_TYPE_ACCEPT = 1; + USER_RESPONSE_TYPE_REJECT = 2; + USER_RESPONSE_TYPE_MODIFY = 3; + } + UserResponseType user_response_type = 1; + string file_path = 2; + optional FileDiff user_modifications_to_suggested_code_blocks = 3; } message FileDiff { // aiserver.v1.FileDiff - message Chunk { // aiserver.v1.FileDiff.Chunk - string content = 1; - repeated string lines = 2; - int32 old_start = 3; - int32 old_lines = 4; - int32 new_start = 5; - int32 new_lines = 6; - } + message Chunk { // aiserver.v1.FileDiff.Chunk + string content = 1; + repeated string lines = 2; + int32 old_start = 3; + int32 old_lines = 4; + int32 new_start = 5; + int32 new_lines = 6; + } string from = 1; string to = 2; repeated Chunk chunks = 3; } -message PullRequest { // aiserver.v1.PullRequest - string title = 1; - string body = 2; - repeated FileDiff diff = 3; +message ToolResultError { // aiserver.v1.ToolResultError + string client_visible_error_message = 1; + string model_visible_error_message = 2; + optional string actual_error_message_only_send_from_client_to_server_never_the_other_way_around_because_that_may_be_a_security_risk = 3; } -message GitDiff { // aiserver.v1.GitDiff - enum DiffType { // aiserver.v1.GitDiff.DiffType - DIFF_TYPE_UNSPECIFIED = 0; - DIFF_TYPE_DIFF_TO_HEAD = 1; - DIFF_TYPE_DIFF_FROM_BRANCH_TO_MAIN = 2; - } - repeated FileDiff diffs = 1; - DiffType diff_type = 2; +message ToolResult { // aiserver.v1.ToolResult + oneof tool_result { + BuiltinToolResult builtin_tool_result = 1; + CustomToolResult custom_tool_result = 2; + ErrorToolResult error_tool_result = 3; + } +} +message ErrorToolResult { // aiserver.v1.ErrorToolResult + string error_message = 1; +} +message CustomToolResult { // aiserver.v1.CustomToolResult + string tool_id = 1; + string result = 2; +} +message BuiltinToolResult { // aiserver.v1.BuiltinToolResult + BuiltinTool tool = 1; + oneof result { + SearchResult search_result = 2; + ReadChunkResult read_chunk_result = 3; + GotodefResult gotodef_result = 4; + EditResult edit_result = 5; + UndoEditResult undo_edit_result = 6; + EndResult end_result = 7; + NewFileResult new_file_result = 8; + AddTestResult add_test_result = 9; + RunTestResult run_test_result = 10; + DeleteTestResult delete_test_result = 11; + SaveFileResult save_file_result = 12; + GetTestsResult get_tests_result = 13; + GetSymbolsResult get_symbols_result = 14; + SemanticSearchResult semantic_search_result = 15; + GetProjectStructureResult get_project_structure_result = 16; + CreateRmFilesResult create_rm_files_result = 17; + RunTerminalCommandsResult run_terminal_commands_result = 18; + NewEditResult new_edit_result = 19; + ReadWithLinterResult read_with_linter_result = 20; + AddUiStepResult add_ui_step_result = 21; + ReadSemsearchFilesResult read_semsearch_files_result = 22; + CreateFileResult create_file_result = 23; + DeleteFileResult delete_file_result = 24; + } +} +enum BuiltinTool { // aiserver.v1.BuiltinTool + BUILTIN_TOOL_UNSPECIFIED = 0; + BUILTIN_TOOL_SEARCH = 1; + BUILTIN_TOOL_READ_CHUNK = 2; + BUILTIN_TOOL_GOTODEF = 3; + BUILTIN_TOOL_EDIT = 4; + BUILTIN_TOOL_UNDO_EDIT = 5; + BUILTIN_TOOL_END = 6; + BUILTIN_TOOL_NEW_FILE = 7; + BUILTIN_TOOL_ADD_TEST = 8; + BUILTIN_TOOL_RUN_TEST = 9; + BUILTIN_TOOL_DELETE_TEST = 10; + BUILTIN_TOOL_SAVE_FILE = 11; + BUILTIN_TOOL_GET_TESTS = 12; + BUILTIN_TOOL_GET_SYMBOLS = 13; + BUILTIN_TOOL_SEMANTIC_SEARCH = 14; + BUILTIN_TOOL_GET_PROJECT_STRUCTURE = 15; + BUILTIN_TOOL_CREATE_RM_FILES = 16; + BUILTIN_TOOL_RUN_TERMINAL_COMMANDS = 17; + BUILTIN_TOOL_NEW_EDIT = 18; + BUILTIN_TOOL_READ_WITH_LINTER = 19; +} +message UndoEditResult { // aiserver.v1.UndoEditResult + repeated string feedback = 1; + string relative_workspace_path = 4; + int32 context_start_line_number = 2; + repeated string context_lines = 3; + int32 context_total_num_lines = 5; + int32 file_total_lines = 6; +} +message SemanticSearchResult { // aiserver.v1.SemanticSearchResult + message Item { // aiserver.v1.SemanticSearchResult.Item + string relative_workspace_path = 1; + float score = 2; + string content = 3; + SimpleRange range = 4; + optional string original_content = 5; + repeated DetailedLine detailed_lines = 6; + } + repeated Item results = 1; + map files = 2; +} +message SimpleRange { // aiserver.v1.SimpleRange + int32 start_line_number = 1; + int32 start_column = 2; + int32 end_line_number_inclusive = 3; + int32 end_column = 4; +} +message SearchResult { // aiserver.v1.SearchResult + repeated SearchToolFileSearchResult file_results = 1; + int32 num_total_matches = 2; + int32 num_total_matched_files = 3; + bool num_total_may_be_incomplete = 4; + bool files_only = 5; +} +message SearchToolFileSearchResult { // aiserver.v1.SearchToolFileSearchResult + message Line { // aiserver.v1.SearchToolFileSearchResult.Line + int32 line_number = 1; + string text = 2; + } + string relative_workspace_path = 1; + int32 num_matches = 2; + repeated Line potentially_relevant_lines = 3; + bool cropped = 4; +} +message SaveFileResult { // aiserver.v1.SaveFileResult +} +message RunTestResult { // aiserver.v1.RunTestResult + string result = 1; +} +message RunTerminalCommandsResult { // aiserver.v1.RunTerminalCommandsResult + repeated string outputs = 1; +} +message ReadWithLinterResult { // aiserver.v1.ReadWithLinterResult + string contents = 1; + repeated Diagnostic diagnostics = 2; +} +message ReadSemsearchFilesResult { // aiserver.v1.ReadSemsearchFilesResult + repeated CodeResult code_results = 1; + repeated File all_files = 2; + repeated MissingFile missing_files = 3; +} +message MissingFile { // aiserver.v1.MissingFile + enum MissingReason { // aiserver.v1.MissingFile.MissingReason + MISSING_REASON_UNSPECIFIED = 0; + MISSING_REASON_TOO_LARGE = 1; + MISSING_REASON_NOT_FOUND = 2; + } + string relative_workspace_path = 1; + MissingReason missing_reason = 2; + optional int32 num_lines = 3; +} +message File { // aiserver.v1.File + string relative_workspace_path = 1; + string contents = 2; +} +message CodeResult { // aiserver.v1.CodeResult + CodeBlock code_block = 1; + float score = 2; +} +message ReadChunkResult { // aiserver.v1.ReadChunkResult + string relative_workspace_path = 1; + int32 start_line_number = 2; + repeated string lines = 3; + int32 total_num_lines = 4; + bool cropped = 5; +} +message NewFileResult { // aiserver.v1.NewFileResult + string relative_workspace_path = 1; + int32 file_total_lines = 2; +} +message NewEditResult { // aiserver.v1.NewEditResult +} +message GotodefResult { // aiserver.v1.GotodefResult + repeated GotodefToolFileSearchResult results = 1; +} +message GotodefToolFileSearchResult { // aiserver.v1.GotodefToolFileSearchResult + message Line { // aiserver.v1.GotodefToolFileSearchResult.Line + int32 line_number = 1; + string text = 2; + } + string relative_workspace_path = 1; + repeated Line potentially_relevant_lines = 3; +} +message GetTestsResult { // aiserver.v1.GetTestsResult + message Test { // aiserver.v1.GetTestsResult.Test + string name = 1; + repeated string lines = 2; + } + repeated Test tests = 1; +} +message GetSymbolsResult { // aiserver.v1.GetSymbolsResult + repeated DocumentSymbol symbols = 1; +} +message DocumentSymbol { // aiserver.v1.DocumentSymbol + enum SymbolKind { // aiserver.v1.DocumentSymbol.SymbolKind + SYMBOL_KIND_UNSPECIFIED = 0; + SYMBOL_KIND_FILE = 1; + SYMBOL_KIND_MODULE = 2; + SYMBOL_KIND_NAMESPACE = 3; + SYMBOL_KIND_PACKAGE = 4; + SYMBOL_KIND_CLASS = 5; + SYMBOL_KIND_METHOD = 6; + SYMBOL_KIND_PROPERTY = 7; + SYMBOL_KIND_FIELD = 8; + SYMBOL_KIND_CONSTRUCTOR = 9; + SYMBOL_KIND_ENUM = 10; + SYMBOL_KIND_INTERFACE = 11; + SYMBOL_KIND_FUNCTION = 12; + SYMBOL_KIND_VARIABLE = 13; + SYMBOL_KIND_CONSTANT = 14; + SYMBOL_KIND_STRING = 15; + SYMBOL_KIND_NUMBER = 16; + SYMBOL_KIND_BOOLEAN = 17; + SYMBOL_KIND_ARRAY = 18; + SYMBOL_KIND_OBJECT = 19; + SYMBOL_KIND_KEY = 20; + SYMBOL_KIND_NULL = 21; + SYMBOL_KIND_ENUM_MEMBER = 22; + SYMBOL_KIND_STRUCT = 23; + SYMBOL_KIND_EVENT = 24; + SYMBOL_KIND_OPERATOR = 25; + SYMBOL_KIND_TYPE_PARAMETER = 26; + } + message Range { // aiserver.v1.DocumentSymbol.Range + int32 start_line_number = 1; + int32 start_column = 2; + int32 end_line_number = 3; + int32 end_column = 4; + } + string name = 1; + string detail = 2; + SymbolKind kind = 3; + string container_name = 5; + Range range = 6; + Range selection_range = 7; + repeated DocumentSymbol children = 8; +} +message GetProjectStructureResult { // aiserver.v1.GetProjectStructureResult + message File { // aiserver.v1.GetProjectStructureResult.File + string relative_workspace_path = 1; + string outline = 2; + } + repeated File files = 1; + string root_workspace_path = 2; +} +message EndResult { // aiserver.v1.EndResult +} +message EditResult { // aiserver.v1.EditResult + message RelatedInformation { // aiserver.v1.EditResult.RelatedInformation + string message = 1; + int32 start_line_number = 2; + int32 end_line_number = 3; + string relative_workspace_path = 4; + } + message Feedback { // aiserver.v1.EditResult.Feedback + string message = 1; + string severity = 2; + int32 start_line_number = 3; + int32 end_line_number = 4; + repeated RelatedInformation related_information = 5; + } + repeated string feedback = 1; + int32 context_start_line_number = 2; + repeated string context_lines = 3; + string file = 4; + int32 file_total_lines = 5; + repeated Feedback structured_feedback = 6; +} +message DeleteTestResult { // aiserver.v1.DeleteTestResult +} +message DeleteFileResult { // aiserver.v1.DeleteFileResult + bool rejected = 1; + bool file_non_existent = 2; + bool file_deleted_successfully = 3; +} +message CreateRmFilesResult { // aiserver.v1.CreateRmFilesResult + repeated string created_file_paths = 1; + repeated string removed_file_paths = 2; +} +message CreateFileResult { // aiserver.v1.CreateFileResult + bool file_created_successfully = 1; + bool file_already_exists = 2; +} +message AddUiStepResult { // aiserver.v1.AddUiStepResult +} +message AddTestResult { // aiserver.v1.AddTestResult + message RelatedInformation { // aiserver.v1.AddTestResult.RelatedInformation + string message = 1; + int32 start_line_number = 2; + int32 end_line_number = 3; + string relative_workspace_path = 4; + } + message Feedback { // aiserver.v1.AddTestResult.Feedback + string message = 1; + string severity = 2; + int32 start_line_number = 3; + int32 end_line_number = 4; + repeated RelatedInformation related_information = 5; + } + repeated Feedback feedback = 1; +} +message SuggestedCodeBlock { // aiserver.v1.SuggestedCodeBlock + string relative_workspace_path = 1; } message SimpleFileDiff { // aiserver.v1.SimpleFileDiff - message Chunk { // aiserver.v1.SimpleFileDiff.Chunk - repeated string old_lines = 1; - repeated string new_lines = 2; - LineRange old_range = 3; - LineRange new_range = 4; - } + message Chunk { // aiserver.v1.SimpleFileDiff.Chunk + repeated string old_lines = 1; + repeated string new_lines = 2; + LineRange old_range = 3; + LineRange new_range = 4; + } string relative_workspace_path = 1; repeated Chunk chunks = 3; } @@ -507,18 +784,105 @@ message LineRange { // aiserver.v1.LineRange int32 start_line_number = 1; int32 end_line_number_inclusive = 2; } +message RedDiff { // aiserver.v1.RedDiff + string relative_workspace_path = 1; + repeated SimplestRange red_ranges = 2; + repeated SimplestRange red_ranges_reversed = 3; + string start_hash = 4; + string end_hash = 5; +} +message PullRequest { // aiserver.v1.PullRequest + string title = 1; + string body = 2; + repeated FileDiff diff = 3; +} +message ListDirResult { // aiserver.v1.ListDirResult + message File { // aiserver.v1.ListDirResult.File + string name = 1; + bool is_directory = 2; + optional int64 size = 3; + optional google.protobuf.Timestamp last_modified = 4; + optional int32 num_children = 5; + optional int32 num_lines = 6; + } + repeated File files = 1; + string directory_relative_workspace_path = 2; +} +message LinterErrorsWithoutFileContents { // aiserver.v1.LinterErrorsWithoutFileContents + string relative_workspace_path = 1; + repeated LinterError errors = 2; +} message InterpreterResult { // aiserver.v1.InterpreterResult string output = 1; bool success = 2; } message ImageProto { // aiserver.v1.ImageProto - message Dimension { // aiserver.v1.ImageProto.Dimension - int32 width = 1; - int32 height = 2; - } + message Dimension { // aiserver.v1.ImageProto.Dimension + int32 width = 1; + int32 height = 2; + } bytes data = 1; Dimension dimension = 2; } +message GitDiff { // aiserver.v1.GitDiff + enum DiffType { // aiserver.v1.GitDiff.DiffType + DIFF_TYPE_UNSPECIFIED = 0; + DIFF_TYPE_DIFF_TO_HEAD = 1; + DIFF_TYPE_DIFF_FROM_BRANCH_TO_MAIN = 2; + } + repeated FileDiff diffs = 1; + DiffType diff_type = 2; +} +message GetLintsForChangeResponse { // aiserver.v1.GetLintsForChangeResponse + message Lint { // aiserver.v1.GetLintsForChangeResponse.Lint + message QuickFix { // aiserver.v1.GetLintsForChangeResponse.Lint.QuickFix + message Edit { // aiserver.v1.GetLintsForChangeResponse.Lint.QuickFix.Edit + string relative_workspace_path = 1; + string text = 2; + int32 start_line_number_one_indexed = 3; + int32 start_column_one_indexed = 4; + int32 end_line_number_inclusive_one_indexed = 5; + int32 end_column_one_indexed = 6; + } + string message = 1; + string kind = 2; + bool is_preferred = 3; + repeated Edit edits = 4; + } + string message = 1; + string severity = 2; + string relative_workspace_path = 3; + int32 start_line_number_one_indexed = 4; + int32 start_column_one_indexed = 5; + int32 end_line_number_inclusive_one_indexed = 6; + int32 end_column_one_indexed = 7; + repeated QuickFix quick_fixes = 9; + } + repeated Lint lints = 1; +} +message Lint { // aiserver.v1.Lint + string message = 1; + SimpleRange range = 2; + LintSeverity severity = 3; +} +enum LintSeverity { // aiserver.v1.LintSeverity + LINT_SEVERITY_UNSPECIFIED = 0; + LINT_SEVERITY_ERROR = 1; + LINT_SEVERITY_WARNING = 2; + LINT_SEVERITY_INFO = 3; + LINT_SEVERITY_HINT = 4; + LINT_SEVERITY_AI = 5; +} +message Edit { // aiserver.v1.Edit + string text = 1; + IRange range = 2; +} +message IRange { // aiserver.v1.IRange + int32 start_line_number = 1; + int32 start_column = 2; + int32 end_line_number = 3; + int32 end_column = 4; +} message FolderInfo { // aiserver.v1.FolderInfo string relative_path = 1; repeated FolderFileInfo files = 2; @@ -529,164 +893,292 @@ message FolderFileInfo { // aiserver.v1.FolderFileInfo bool truncated = 3; float score = 4; } -message GetLintsForChangeResponse { // aiserver.v1.GetLintsForChangeResponse - message Lint { // aiserver.v1.GetLintsForChangeResponse.Lint - message QuickFix { // aiserver.v1.GetLintsForChangeResponse.Lint.QuickFix - message Edit { // aiserver.v1.GetLintsForChangeResponse.Lint.QuickFix.Edit - string relative_workspace_path = 1; - string text = 2; - int32 start_line_number_one_indexed = 3; - int32 start_column_one_indexed = 4; - int32 end_line_number_inclusive_one_indexed = 5; - int32 end_column_one_indexed = 6; - } - string message = 1; - string kind = 2; - bool is_preferred = 3; - repeated Edit edits = 4; - } - string message = 1; - string severity = 2; - string relative_workspace_path = 3; - int32 start_line_number_one_indexed = 4; - int32 start_column_one_indexed = 5; - int32 end_line_number_inclusive_one_indexed = 6; - int32 end_column_one_indexed = 7; - repeated QuickFix quick_fixes = 9; - } - repeated Lint lints = 1; +message DocsReference { // aiserver.v1.DocsReference + string title = 1; + string url = 2; + string chunk = 3; + string name = 4; } -message UserResponseToSuggestedCodeBlock { // aiserver.v1.UserResponseToSuggestedCodeBlock - enum UserResponseType { // aiserver.v1.UserResponseToSuggestedCodeBlock.UserResponseType - USER_RESPONSE_TYPE_UNSPECIFIED = 0; - USER_RESPONSE_TYPE_ACCEPT = 1; - USER_RESPONSE_TYPE_REJECT = 2; - USER_RESPONSE_TYPE_MODIFY = 3; +message DiffHistoryData { // aiserver.v1.DiffHistoryData + string relative_workspace_path = 1; + repeated ComposerFileDiff diffs = 2; + double timestamp = 3; + string unique_id = 4; + ComposerFileDiff start_to_end_diff = 5; +} +message ComposerFileDiff { // aiserver.v1.ComposerFileDiff + enum Editor { // aiserver.v1.ComposerFileDiff.Editor + EDITOR_UNSPECIFIED = 0; + EDITOR_AI = 1; + EDITOR_HUMAN = 2; + } + message ChunkDiff { // aiserver.v1.ComposerFileDiff.ChunkDiff + string diff_string = 1; + int32 old_start = 2; + int32 new_start = 3; + int32 old_lines = 4; + int32 new_lines = 5; + int32 lines_removed = 6; + int32 lines_added = 7; + } + repeated ChunkDiff chunks = 1; + Editor editor = 2; + bool hit_timeout = 3; +} +message ConversationSummary { // aiserver.v1.ConversationSummary + string summary = 1; + string truncation_last_bubble_id_inclusive = 2; + string client_should_start_sending_from_inclusive_bubble_id = 3; + string previous_conversation_summary_bubble_id = 4; + bool includes_tool_results = 5; +} +message ComposerFileDiffHistory { // aiserver.v1.ComposerFileDiffHistory + string file_name = 1; + repeated string diff_history = 2; + repeated double diff_history_timestamps = 3; +} +message ComposerCapabilityRequest { // aiserver.v1.ComposerCapabilityRequest + enum ComposerCapabilityType { // aiserver.v1.ComposerCapabilityRequest.ComposerCapabilityType + COMPOSER_CAPABILITY_TYPE_UNSPECIFIED = 0; + COMPOSER_CAPABILITY_TYPE_LOOP_ON_LINTS = 1; + COMPOSER_CAPABILITY_TYPE_LOOP_ON_TESTS = 2; + COMPOSER_CAPABILITY_TYPE_MEGA_PLANNER = 3; + COMPOSER_CAPABILITY_TYPE_LOOP_ON_COMMAND = 4; + COMPOSER_CAPABILITY_TYPE_TOOL_CALL = 5; + COMPOSER_CAPABILITY_TYPE_DIFF_REVIEW = 6; + COMPOSER_CAPABILITY_TYPE_CONTEXT_PICKING = 7; + COMPOSER_CAPABILITY_TYPE_EDIT_TRAIL = 8; + COMPOSER_CAPABILITY_TYPE_AUTO_CONTEXT = 9; + COMPOSER_CAPABILITY_TYPE_CONTEXT_PLANNER = 10; + COMPOSER_CAPABILITY_TYPE_DIFF_HISTORY = 11; + COMPOSER_CAPABILITY_TYPE_REMEMBER_THIS = 12; + COMPOSER_CAPABILITY_TYPE_DECOMPOSER = 13; + COMPOSER_CAPABILITY_TYPE_USES_CODEBASE = 14; + COMPOSER_CAPABILITY_TYPE_TOOL_FORMER = 15; + COMPOSER_CAPABILITY_TYPE_CURSOR_RULES = 16; + } + enum ToolType { // aiserver.v1.ComposerCapabilityRequest.ToolType + TOOL_TYPE_UNSPECIFIED = 0; + TOOL_TYPE_ADD_FILE_TO_CONTEXT = 1; + TOOL_TYPE_RUN_TERMINAL_COMMAND = 2; + TOOL_TYPE_ITERATE = 3; + TOOL_TYPE_REMOVE_FILE_FROM_CONTEXT = 4; + TOOL_TYPE_SEMANTIC_SEARCH_CODEBASE = 5; + } + message ToolSchema { // aiserver.v1.ComposerCapabilityRequest.ToolSchema + ToolType type = 1; + string name = 2; + map properties = 3; + repeated string required = 4; + } + message SchemaProperty { // aiserver.v1.ComposerCapabilityRequest.SchemaProperty + string type = 1; + optional string description = 2; + } + message LoopOnLintsCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnLintsCapability + repeated LinterErrors linter_errors = 1; + optional string custom_instructions = 2; + } + message LoopOnTestsCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnTestsCapability + repeated string test_names = 1; + optional string custom_instructions = 2; + } + message MegaPlannerCapability { // aiserver.v1.ComposerCapabilityRequest.MegaPlannerCapability + optional string custom_instructions = 1; + } + message LoopOnCommandCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnCommandCapability + string command = 1; + optional string custom_instructions = 2; + optional string output = 3; + optional int32 exit_code = 4; + } + message ToolCallCapability { // aiserver.v1.ComposerCapabilityRequest.ToolCallCapability + optional string custom_instructions = 1; + repeated ToolSchema tool_schemas = 2; + repeated string relevant_files = 3; + repeated string files_in_context = 4; + repeated string semantic_search_files = 5; + } + message DiffReviewCapability { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability + message SimpleFileDiff { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability.SimpleFileDiff + message Chunk { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability.SimpleFileDiff.Chunk + repeated string old_lines = 1; + repeated string new_lines = 2; + LineRange old_range = 3; + LineRange new_range = 4; + } + string relative_workspace_path = 1; + repeated Chunk chunks = 3; } - UserResponseType user_response_type = 1; - string file_path = 2; - optional FileDiff user_modifications_to_suggested_code_blocks = 3; + optional string custom_instructions = 1; + repeated SimpleFileDiff diffs = 2; + } + message DecomposerCapability { // aiserver.v1.ComposerCapabilityRequest.DecomposerCapability + optional string custom_instructions = 1; + } + message ContextPickingCapability { // aiserver.v1.ComposerCapabilityRequest.ContextPickingCapability + optional string custom_instructions = 1; + repeated string potential_context_files = 2; + repeated CodeChunk potential_context_code_chunks = 3; + repeated string files_in_context = 4; + } + message EditTrailCapability { // aiserver.v1.ComposerCapabilityRequest.EditTrailCapability + optional string custom_instructions = 1; + } + message AutoContextCapability { // aiserver.v1.ComposerCapabilityRequest.AutoContextCapability + optional string custom_instructions = 1; + repeated string additional_files = 2; + } + message ContextPlannerCapability { // aiserver.v1.ComposerCapabilityRequest.ContextPlannerCapability + optional string custom_instructions = 1; + repeated CodeChunk attached_code_chunks = 2; + } + message RememberThisCapability { // aiserver.v1.ComposerCapabilityRequest.RememberThisCapability + optional string custom_instructions = 1; + string memory = 2; + } + message CursorRulesCapability { // aiserver.v1.ComposerCapabilityRequest.CursorRulesCapability + optional string custom_instructions = 1; + } + ComposerCapabilityType type = 1; + oneof data { + LoopOnLintsCapability loop_on_lints = 2; + LoopOnTestsCapability loop_on_tests = 3; + MegaPlannerCapability mega_planner = 4; + LoopOnCommandCapability loop_on_command = 5; + ToolCallCapability tool_call = 6; + DiffReviewCapability diff_review = 7; + ContextPickingCapability context_picking = 8; + EditTrailCapability edit_trail = 9; + AutoContextCapability auto_context = 10; + ContextPlannerCapability context_planner = 11; + RememberThisCapability remember_this = 12; + DecomposerCapability decomposer = 13; + CursorRulesCapability cursor_rules = 14; + } +} +message CodeChunk { // aiserver.v1.CodeChunk + enum Intent { // aiserver.v1.CodeChunk.Intent + INTENT_UNSPECIFIED = 0; + INTENT_COMPOSER_FILE = 1; + INTENT_COMPRESSED_COMPOSER_FILE = 2; + } + enum SummarizationStrategy { // aiserver.v1.CodeChunk.SummarizationStrategy + SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0; + SUMMARIZATION_STRATEGY_SUMMARIZED = 1; + SUMMARIZATION_STRATEGY_EMBEDDED = 2; + } + string relative_workspace_path = 1; + int32 start_line_number = 2; + repeated string lines = 3; + optional SummarizationStrategy summarization_strategy = 4; + string language_identifier = 5; + optional Intent intent = 6; + optional bool is_final_version = 7; + optional bool is_first_version = 8; +} +message Commit { // aiserver.v1.Commit + string sha = 1; + string message = 2; + string description = 3; + repeated FileDiff diff = 4; + string author = 5; + string date = 6; } message ClientSideToolV2Result { // aiserver.v1.ClientSideToolV2Result ClientSideToolV2 tool = 1; - ReadSemsearchFilesResult read_semsearch_files_result = 2; - ReadFileForImportsResult read_file_for_imports_result = 3; - RipgrepSearchResult ripgrep_search_result = 4; - RunTerminalCommandResult run_terminal_command_result = 5; - ReadFileResult read_file_result = 6; - ListDirResult list_dir_result = 9; - EditFileResult edit_file_result = 10; - ToolCallFileSearchResult file_search_result = 11; - SemanticSearchFullResult semantic_search_full_result = 18; - CreateFileResult create_file_result = 19; - DeleteFileResult delete_file_result = 20; + oneof result { + ReadSemsearchFilesResult read_semsearch_files_result = 2; + ReadFileForImportsResult read_file_for_imports_result = 3; + RipgrepSearchResult ripgrep_search_result = 4; + RunTerminalCommandResult run_terminal_command_result = 5; + ReadFileResult read_file_result = 6; + ListDirResult list_dir_result = 9; + EditFileResult edit_file_result = 10; + ToolCallFileSearchResult file_search_result = 11; + SemanticSearchFullResult semantic_search_full_result = 18; + CreateFileResult create_file_result = 19; + DeleteFileResult delete_file_result = 20; + ReapplyResult reapply_result = 21; + GetRelatedFilesResult get_related_files_result = 22; + ParallelApplyResult parallel_apply_result = 23; + RunTerminalCommandV2Result run_terminal_command_v2_result = 24; + FetchRulesResult fetch_rules_result = 25; + PlannerResult planner_result = 26; + WebSearchResult web_search_result = 27; + MCPResult mcp_result = 28; + WebViewerResult web_viewer_result = 29; + DiffHistoryResult diff_history_result = 30; + } optional ToolResultError error = 8; } -message ReadSemsearchFilesResult { // aiserver.v1.ReadSemsearchFilesResult +enum ClientSideToolV2 { // aiserver.v1.ClientSideToolV2 + CLIENT_SIDE_TOOL_V2_UNSPECIFIED = 0; + CLIENT_SIDE_TOOL_V2_READ_SEMSEARCH_FILES = 1; + CLIENT_SIDE_TOOL_V2_READ_FILE_FOR_IMPORTS = 2; + CLIENT_SIDE_TOOL_V2_RIPGREP_SEARCH = 3; + CLIENT_SIDE_TOOL_V2_RUN_TERMINAL_COMMAND = 4; + CLIENT_SIDE_TOOL_V2_READ_FILE = 5; + CLIENT_SIDE_TOOL_V2_LIST_DIR = 6; + CLIENT_SIDE_TOOL_V2_EDIT_FILE = 7; + CLIENT_SIDE_TOOL_V2_FILE_SEARCH = 8; + CLIENT_SIDE_TOOL_V2_SEMANTIC_SEARCH_FULL = 9; + CLIENT_SIDE_TOOL_V2_CREATE_FILE = 10; + CLIENT_SIDE_TOOL_V2_DELETE_FILE = 11; + CLIENT_SIDE_TOOL_V2_REAPPLY = 12; + CLIENT_SIDE_TOOL_V2_GET_RELATED_FILES = 13; + CLIENT_SIDE_TOOL_V2_PARALLEL_APPLY = 14; + CLIENT_SIDE_TOOL_V2_RUN_TERMINAL_COMMAND_V2 = 15; + CLIENT_SIDE_TOOL_V2_FETCH_RULES = 16; + CLIENT_SIDE_TOOL_V2_PLANNER = 17; + CLIENT_SIDE_TOOL_V2_WEB_SEARCH = 18; + CLIENT_SIDE_TOOL_V2_MCP = 19; + CLIENT_SIDE_TOOL_V2_WEB_VIEWER = 20; + CLIENT_SIDE_TOOL_V2_DIFF_HISTORY = 21; +} +message WebViewerResult { // aiserver.v1.WebViewerResult + string url = 1; + ImageProto screenshot = 2; +} +message WebSearchResult { // aiserver.v1.WebSearchResult + message WebReference { // aiserver.v1.WebSearchResult.WebReference + string title = 1; + string url = 2; + string chunk = 3; + } + repeated WebReference references = 1; +} +message ToolCallFileSearchResult { // aiserver.v1.ToolCallFileSearchResult + message File { // aiserver.v1.ToolCallFileSearchResult.File + string uri = 1; + } + repeated File files = 1; + optional bool limit_hit = 2; + int32 num_results = 3; +} +message SemanticSearchFullResult { // aiserver.v1.SemanticSearchFullResult repeated CodeResult code_results = 1; + repeated File all_files = 2; + repeated MissingFile missing_files = 3; } -message CodeResult { // aiserver.v1.CodeResult - CodeBlock code_block = 1; - float score = 2; +message RunTerminalCommandV2Result { // aiserver.v1.RunTerminalCommandV2Result + string output = 1; + int32 exit_code = 2; + optional bool rejected = 3; + bool popped_out_into_background = 4; + bool is_running_in_background = 5; + bool not_interrupted = 6; + string resulting_working_directory = 7; + bool did_user_change = 8; + RunTerminalCommandEndedReason ended_reason = 9; + optional int32 exit_code_v2 = 10; } -message ReadFileForImportsResult { // aiserver.v1.ReadFileForImportsResult - string contents = 1; -} -message RipgrepSearchResult { // aiserver.v1.RipgrepSearchResult - RipgrepSearchResultInternal internal = 1; -} -message RipgrepSearchResultInternal { // aiserver.v1.RipgrepSearchResultInternal - message IFileMatch { // aiserver.v1.RipgrepSearchResultInternal.IFileMatch - message ITextSearchResult { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchResult - message ITextSearchMatch { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchMatch - message ISearchRangeSetPairing { // aiserver.v1.RipgrepSearchResultInternal.ISearchRangeSetPairing - message ISearchRange { // aiserver.v1.RipgrepSearchResultInternal.ISearchRange - int32 start_line_number = 1; - int32 start_column = 2; - int32 end_line_number = 3; - int32 end_column = 4; - } - ISearchRange source = 1; - ISearchRange preview = 2; - } - optional string uri = 1; - repeated ISearchRangeSetPairing range_locations = 2; - string preview_text = 3; - optional int32 webview_index = 4; - optional string cell_fragment = 5; - } - message ITextSearchContext { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchContext - optional string uri = 1; - string text = 2; - int32 line_number = 3; - } - ITextSearchMatch match = 1; - ITextSearchContext context = 2; - } - string resource = 1; - repeated ITextSearchResult results = 2; - } - enum SearchCompletionExitCode { // aiserver.v1.RipgrepSearchResultInternal.SearchCompletionExitCode - SEARCH_COMPLETION_EXIT_CODE_UNSPECIFIED = 0; - SEARCH_COMPLETION_EXIT_CODE_NORMAL = 1; - SEARCH_COMPLETION_EXIT_CODE_NEW_SEARCH_STARTED = 2; - } - message ITextSearchCompleteMessage { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchCompleteMessage - enum TextSearchCompleteMessageType { // aiserver.v1.RipgrepSearchResultInternal.TextSearchCompleteMessageType - TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_UNSPECIFIED = 0; - TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_INFORMATION = 1; - TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_WARNING = 2; - } - string text = 1; - TextSearchCompleteMessageType type = 2; - optional bool trusted = 3; - } - message IFileSearchStats { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchStats - message ISearchEngineStats { // aiserver.v1.RipgrepSearchResultInternal.ISearchEngineStats - int32 file_walk_time = 1; - int32 directories_walked = 2; - int32 files_walked = 3; - int32 cmd_time = 4; - optional int32 cmd_result_count = 5; - } - message ICachedSearchStats { // aiserver.v1.RipgrepSearchResultInternal.ICachedSearchStats - bool cache_was_resolved = 1; - int32 cache_lookup_time = 2; - int32 cache_filter_time = 3; - int32 cache_entry_count = 4; - } - message IFileSearchProviderStats { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchProviderStats - int32 provider_time = 1; - int32 post_process_time = 2; - } - enum FileSearchProviderType { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchStats.FileSearchProviderType - FILE_SEARCH_PROVIDER_TYPE_UNSPECIFIED = 0; - FILE_SEARCH_PROVIDER_TYPE_FILE_SEARCH_PROVIDER = 1; - FILE_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS = 2; - } - bool from_cache = 1; - ISearchEngineStats search_engine_stats = 2; - ICachedSearchStats cached_search_stats = 3; - IFileSearchProviderStats file_search_provider_stats = 4; - int32 result_count = 5; - FileSearchProviderType type = 6; - optional int32 sorting_time = 7; - } - message ITextSearchStats { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchStats - enum TextSearchProviderType { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchStats.TextSearchProviderType - TEXT_SEARCH_PROVIDER_TYPE_UNSPECIFIED = 0; - TEXT_SEARCH_PROVIDER_TYPE_TEXT_SEARCH_PROVIDER = 1; - TEXT_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS = 2; - TEXT_SEARCH_PROVIDER_TYPE_AI_TEXT_SEARCH_PROVIDER = 3; - } - TextSearchProviderType type = 1; - } - repeated IFileMatch results = 1; - optional SearchCompletionExitCode exit = 2; - optional bool limit_hit = 3; - repeated ITextSearchCompleteMessage messages = 4; - IFileSearchStats file_search_stats = 5; - ITextSearchStats text_search_stats = 6; +enum RunTerminalCommandEndedReason { // aiserver.v1.RunTerminalCommandEndedReason + RUN_TERMINAL_COMMAND_ENDED_REASON_UNSPECIFIED = 0; + RUN_TERMINAL_COMMAND_ENDED_REASON_EXECUTION_COMPLETED = 1; + RUN_TERMINAL_COMMAND_ENDED_REASON_EXECUTION_ABORTED = 2; + RUN_TERMINAL_COMMAND_ENDED_REASON_EXECUTION_FAILED = 3; + RUN_TERMINAL_COMMAND_ENDED_REASON_ERROR_OCCURRED_CHECKING_REASON = 4; } message RunTerminalCommandResult { // aiserver.v1.RunTerminalCommandResult string output = 1; @@ -694,6 +1186,114 @@ message RunTerminalCommandResult { // aiserver.v1.RunTerminalCommandResult optional bool rejected = 3; bool popped_out_into_background = 4; } +message RipgrepSearchResult { // aiserver.v1.RipgrepSearchResult + RipgrepSearchResultInternal internal = 1; +} +message RipgrepSearchResultInternal { // aiserver.v1.RipgrepSearchResultInternal + enum TextSearchCompleteMessageType { // aiserver.v1.RipgrepSearchResultInternal.TextSearchCompleteMessageType + TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_UNSPECIFIED = 0; + TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_INFORMATION = 1; + TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_WARNING = 2; + } + enum SearchCompletionExitCode { // aiserver.v1.RipgrepSearchResultInternal.SearchCompletionExitCode + SEARCH_COMPLETION_EXIT_CODE_UNSPECIFIED = 0; + SEARCH_COMPLETION_EXIT_CODE_NORMAL = 1; + SEARCH_COMPLETION_EXIT_CODE_NEW_SEARCH_STARTED = 2; + } + message IFileMatch { // aiserver.v1.RipgrepSearchResultInternal.IFileMatch + string resource = 1; + repeated ITextSearchResult results = 2; + } + message ITextSearchResult { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchResult + oneof result { + ITextSearchMatch match = 1; + ITextSearchContext context = 2; + } + } + message ITextSearchMatch { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchMatch + optional string uri = 1; + repeated ISearchRangeSetPairing range_locations = 2; + string preview_text = 3; + optional int32 webview_index = 4; + optional string cell_fragment = 5; + } + message ITextSearchContext { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchContext + optional string uri = 1; + string text = 2; + int32 line_number = 3; + } + message ISearchRangeSetPairing { // aiserver.v1.RipgrepSearchResultInternal.ISearchRangeSetPairing + ISearchRange source = 1; + ISearchRange preview = 2; + } + message ISearchRange { // aiserver.v1.RipgrepSearchResultInternal.ISearchRange + int32 start_line_number = 1; + int32 start_column = 2; + int32 end_line_number = 3; + int32 end_column = 4; + } + message ITextSearchCompleteMessage { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchCompleteMessage + string text = 1; + TextSearchCompleteMessageType type = 2; + optional bool trusted = 3; + } + message IFileSearchStats { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchStats + enum FileSearchProviderType { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchStats.FileSearchProviderType + FILE_SEARCH_PROVIDER_TYPE_UNSPECIFIED = 0; + FILE_SEARCH_PROVIDER_TYPE_FILE_SEARCH_PROVIDER = 1; + FILE_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS = 2; + } + bool from_cache = 1; + oneof detail_stats { + ISearchEngineStats search_engine_stats = 2; + ICachedSearchStats cached_search_stats = 3; + IFileSearchProviderStats file_search_provider_stats = 4; + } + int32 result_count = 5; + FileSearchProviderType type = 6; + optional int32 sorting_time = 7; + } + message ITextSearchStats { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchStats + enum TextSearchProviderType { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchStats.TextSearchProviderType + TEXT_SEARCH_PROVIDER_TYPE_UNSPECIFIED = 0; + TEXT_SEARCH_PROVIDER_TYPE_TEXT_SEARCH_PROVIDER = 1; + TEXT_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS = 2; + TEXT_SEARCH_PROVIDER_TYPE_AI_TEXT_SEARCH_PROVIDER = 3; + } + TextSearchProviderType type = 1; + } + message ISearchEngineStats { // aiserver.v1.RipgrepSearchResultInternal.ISearchEngineStats + int32 file_walk_time = 1; + int32 directories_walked = 2; + int32 files_walked = 3; + int32 cmd_time = 4; + optional int32 cmd_result_count = 5; + } + message ICachedSearchStats { // aiserver.v1.RipgrepSearchResultInternal.ICachedSearchStats + bool cache_was_resolved = 1; + int32 cache_lookup_time = 2; + int32 cache_filter_time = 3; + int32 cache_entry_count = 4; + } + message IFileSearchProviderStats { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchProviderStats + int32 provider_time = 1; + int32 post_process_time = 2; + } + repeated IFileMatch results = 1; + optional SearchCompletionExitCode exit = 2; + optional bool limit_hit = 3; + repeated ITextSearchCompleteMessage messages = 4; + oneof stats { + IFileSearchStats file_search_stats = 5; + ITextSearchStats text_search_stats = 6; + } +} +message ReapplyResult { // aiserver.v1.ReapplyResult + EditFileResult.FileDiff diff = 1; + bool is_applied = 2; + bool apply_failed = 3; + repeated LinterError linter_errors = 4; +} message ReadFileResult { // aiserver.v1.ReadFileResult string contents = 1; bool did_downgrade_to_line_range = 2; @@ -706,244 +1306,50 @@ message ReadFileResult { // aiserver.v1.ReadFileResult string relative_workspace_path = 9; bool did_shorten_char_range = 10; } -message ListDirResult { // aiserver.v1.ListDirResult - message File { // aiserver.v1.ListDirResult.File - message Timestamp { // google.protobuf.Timestamp - int64 seconds = 1; - int32 nanos = 2; - } - string name = 1; - bool is_directory = 2; - optional int64 size = 3; - optional Timestamp last_modified = 4; - optional int32 num_children = 5; - optional int32 num_lines = 6; - } +message ReadFileForImportsResult { // aiserver.v1.ReadFileForImportsResult + string contents = 1; +} +message PlannerResult { // aiserver.v1.PlannerResult + string plan = 1; +} +message ParallelApplyResult { // aiserver.v1.ParallelApplyResult + message FileResult { // aiserver.v1.ParallelApplyResult.FileResult + string file_path = 1; + EditFileResult.FileDiff diff = 2; + bool is_applied = 3; + bool apply_failed = 4; + optional string error = 5; + repeated LinterError linter_errors = 6; + } + repeated FileResult file_results = 1; + optional string error = 2; +} +message FileResult { // aiserver.v1.FileResult + File file = 1; + float score = 2; +} +message MCPResult { // aiserver.v1.MCPResult + string selected_tool = 1; + string result = 2; +} +message GetRelatedFilesResult { // aiserver.v1.GetRelatedFilesResult + message File { // aiserver.v1.GetRelatedFilesResult.File + string uri = 1; + float score = 2; + } repeated File files = 1; - string directory_relative_workspace_path = 2; +} +message FetchRulesResult { // aiserver.v1.FetchRulesResult + repeated CursorRule rules = 1; } message EditFileResult { // aiserver.v1.EditFileResult - message FileDiff { // aiserver.v1.EditFileResult.FileDiff - message ChunkDiff { // aiserver.v1.EditFileResult.FileDiff.ChunkDiff - string diff_string = 1; - int32 old_start = 2; - int32 new_start = 3; - int32 old_lines = 4; - int32 new_lines = 5; - int32 lines_removed = 6; - int32 lines_added = 7; - } - enum Editor { // aiserver.v1.EditFileResult.FileDiff.Editor - EDITOR_UNSPECIFIED = 0; - EDITOR_AI = 1; - EDITOR_HUMAN = 2; - } - repeated ChunkDiff chunks = 1; - Editor editor = 2; - bool hit_timeout = 3; + message FileDiff { // aiserver.v1.EditFileResult.FileDiff + enum Editor { // aiserver.v1.EditFileResult.FileDiff.Editor + EDITOR_UNSPECIFIED = 0; + EDITOR_AI = 1; + EDITOR_HUMAN = 2; } - FileDiff diff = 1; - bool is_applied = 2; - bool apply_failed = 3; -} -message ToolCallFileSearchResult { // aiserver.v1.ToolCallFileSearchResult - message File { // aiserver.v1.ToolCallFileSearchResult.File - string uri = 1; - } - repeated File files = 1; - optional bool limit_hit = 2; - int32 num_results = 3; -} -message SemanticSearchFullResult { // aiserver.v1.SemanticSearchFullResult - repeated CodeResult code_results = 1; -} -message CreateFileResult { // aiserver.v1.CreateFileResult - bool file_created_successfully = 1; - bool file_already_exists = 2; -} -message DeleteFileResult { // aiserver.v1.DeleteFileResult - bool rejected = 1; - bool file_non_existent = 2; - bool file_deleted_successfully = 3; -} -message ToolResultError { // aiserver.v1.ToolResultError - string client_visible_error_message = 1; - string model_visible_error_message = 2; -} -message ComposerCapabilityRequest { // aiserver.v1.ComposerCapabilityRequest - enum ComposerCapabilityType { // aiserver.v1.ComposerCapabilityRequest.ComposerCapabilityType - COMPOSER_CAPABILITY_TYPE_UNSPECIFIED = 0; - COMPOSER_CAPABILITY_TYPE_LOOP_ON_LINTS = 1; - COMPOSER_CAPABILITY_TYPE_LOOP_ON_TESTS = 2; - COMPOSER_CAPABILITY_TYPE_MEGA_PLANNER = 3; - COMPOSER_CAPABILITY_TYPE_LOOP_ON_COMMAND = 4; - COMPOSER_CAPABILITY_TYPE_TOOL_CALL = 5; - COMPOSER_CAPABILITY_TYPE_DIFF_REVIEW = 6; - COMPOSER_CAPABILITY_TYPE_CONTEXT_PICKING = 7; - COMPOSER_CAPABILITY_TYPE_EDIT_TRAIL = 8; - COMPOSER_CAPABILITY_TYPE_AUTO_CONTEXT = 9; - COMPOSER_CAPABILITY_TYPE_CONTEXT_PLANNER = 10; - COMPOSER_CAPABILITY_TYPE_DIFF_HISTORY = 11; - COMPOSER_CAPABILITY_TYPE_REMEMBER_THIS = 12; - COMPOSER_CAPABILITY_TYPE_DECOMPOSER = 13; - COMPOSER_CAPABILITY_TYPE_USES_CODEBASE = 14; - COMPOSER_CAPABILITY_TYPE_TOOL_FORMER = 15; - } - message LoopOnLintsCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnLintsCapability - repeated LinterErrors linter_errors = 1; - optional string custom_instructions = 2; - } - message LoopOnTestsCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnTestsCapability - repeated string test_names = 1; - optional string custom_instructions = 2; - } - message MegaPlannerCapability { // aiserver.v1.ComposerCapabilityRequest.MegaPlannerCapability - optional string custom_instructions = 1; - } - message LoopOnCommandCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnCommandCapability - string command = 1; - optional string custom_instructions = 2; - optional string output = 3; - optional int32 exit_code = 4; - } - message ToolCallCapability { // aiserver.v1.ComposerCapabilityRequest.ToolCallCapability - message ToolSchema { // aiserver.v1.ComposerCapabilityRequest.ToolSchema - enum ToolType { // aiserver.v1.ComposerCapabilityRequest.ToolType - TOOL_TYPE_UNSPECIFIED = 0; - TOOL_TYPE_ADD_FILE_TO_CONTEXT = 1; - TOOL_TYPE_RUN_TERMINAL_COMMAND = 2; - TOOL_TYPE_ITERATE = 3; - TOOL_TYPE_REMOVE_FILE_FROM_CONTEXT = 4; - TOOL_TYPE_SEMANTIC_SEARCH_CODEBASE = 5; - } - ToolType type = 1; - string name = 2; - repeated string required = 4; - } - optional string custom_instructions = 1; - repeated ToolSchema tool_schemas = 2; - repeated string relevant_files = 3; - repeated string files_in_context = 4; - repeated string semantic_search_files = 5; - } - message DiffReviewCapability { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability - message SimpleFileDiff { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability.SimpleFileDiff - message Chunk { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability.SimpleFileDiff.Chunk - repeated string old_lines = 1; - repeated string new_lines = 2; - LineRange old_range = 3; - LineRange new_range = 4; - } - string relative_workspace_path = 1; - repeated Chunk chunks = 3; - } - optional string custom_instructions = 1; - repeated SimpleFileDiff diffs = 2; - } - message ContextPickingCapability { // aiserver.v1.ComposerCapabilityRequest.ContextPickingCapability - optional string custom_instructions = 1; - repeated string potential_context_files = 2; - repeated CodeChunk potential_context_code_chunks = 3; - repeated string files_in_context = 4; - } - message EditTrailCapability { // aiserver.v1.ComposerCapabilityRequest.EditTrailCapability - optional string custom_instructions = 1; - } - message AutoContextCapability { // aiserver.v1.ComposerCapabilityRequest.AutoContextCapability - optional string custom_instructions = 1; - repeated string additional_files = 2; - } - message ContextPlannerCapability { // aiserver.v1.ComposerCapabilityRequest.ContextPlannerCapability - optional string custom_instructions = 1; - repeated CodeChunk attached_code_chunks = 2; - } - message RememberThisCapability { // aiserver.v1.ComposerCapabilityRequest.RememberThisCapability - optional string custom_instructions = 1; - string memory = 2; - } - message DecomposerCapability { // aiserver.v1.ComposerCapabilityRequest.DecomposerCapability - optional string custom_instructions = 1; - } - ComposerCapabilityType type = 1; - LoopOnLintsCapability loop_on_lints = 2; - LoopOnTestsCapability loop_on_tests = 3; - MegaPlannerCapability mega_planner = 4; - LoopOnCommandCapability loop_on_command = 5; - ToolCallCapability tool_call = 6; - DiffReviewCapability diff_review = 7; - ContextPickingCapability context_picking = 8; - EditTrailCapability edit_trail = 9; - AutoContextCapability auto_context = 10; - ContextPlannerCapability context_planner = 11; - RememberThisCapability remember_this = 12; - DecomposerCapability decomposer = 13; -} -message LinterErrors { // aiserver.v1.LinterErrors - string relative_workspace_path = 1; - repeated LinterError errors = 2; - string file_contents = 3; -} -message LinterError { // aiserver.v1.LinterError - message RelatedInformation { // aiserver.v1.Diagnostic.RelatedInformation - string message = 1; - CursorRange range = 2; - } - enum DiagnosticSeverity { // aiserver.v1.Diagnostic.DiagnosticSeverity - DIAGNOSTIC_SEVERITY_UNSPECIFIED = 0; - DIAGNOSTIC_SEVERITY_ERROR = 1; - DIAGNOSTIC_SEVERITY_WARNING = 2; - DIAGNOSTIC_SEVERITY_INFORMATION = 3; - DIAGNOSTIC_SEVERITY_HINT = 4; - } - string message = 1; - CursorRange range = 2; - optional string source = 3; - repeated RelatedInformation related_information = 4; - optional DiagnosticSeverity severity = 5; -} -message CodeChunk { // aiserver.v1.CodeChunk - enum SummarizationStrategy { // aiserver.v1.CodeChunk.SummarizationStrategy - SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0; - SUMMARIZATION_STRATEGY_SUMMARIZED = 1; - SUMMARIZATION_STRATEGY_EMBEDDED = 2; - } - enum Intent { // aiserver.v1.CodeChunk.Intent - INTENT_UNSPECIFIED = 0; - INTENT_COMPOSER_FILE = 1; - INTENT_COMPRESSED_COMPOSER_FILE = 2; - } - string relative_workspace_path = 1; - int32 start_line_number = 2; - repeated string lines = 3; - optional SummarizationStrategy summarization_strategy = 4; - string language_identifier = 5; - optional Intent intent = 6; - optional bool is_final_version = 7; - optional bool is_first_version = 8; -} -message SuggestedCodeBlock { // aiserver.v1.SuggestedCodeBlock - string relative_workspace_path = 1; -} -message RedDiff { // aiserver.v1.RedDiff - string relative_workspace_path = 1; - repeated SimplestRange red_ranges = 2; - repeated SimplestRange red_ranges_reversed = 3; - string start_hash = 4; - string end_hash = 5; -} -message LinterErrorsWithoutFileContents { // aiserver.v1.LinterErrorsWithoutFileContents - string relative_workspace_path = 1; - repeated LinterError errors = 2; -} -message DiffHistoryData { // aiserver.v1.DiffHistoryData - string relative_workspace_path = 1; - repeated ComposerFileDiff diffs = 2; - double timestamp = 3; - string unique_id = 4; - ComposerFileDiff start_to_end_diff = 5; -} -message ComposerFileDiff { // aiserver.v1.ComposerFileDiff - message ChunkDiff { // aiserver.v1.ComposerFileDiff.ChunkDiff + message ChunkDiff { // aiserver.v1.EditFileResult.FileDiff.ChunkDiff string diff_string = 1; int32 old_start = 2; int32 new_start = 3; @@ -952,94 +1358,29 @@ message ComposerFileDiff { // aiserver.v1.ComposerFileDiff int32 lines_removed = 6; int32 lines_added = 7; } - enum Editor { // aiserver.v1.ComposerFileDiff.Editor - EDITOR_UNSPECIFIED = 0; - EDITOR_AI = 1; - EDITOR_HUMAN = 2; - } - repeated ChunkDiff chunks = 1; - Editor editor = 2; - bool hit_timeout = 3; + repeated ChunkDiff chunks = 1; + Editor editor = 2; + bool hit_timeout = 3; + } + FileDiff diff = 1; + bool is_applied = 2; + bool apply_failed = 3; + repeated LinterError linter_errors = 4; } -message ComposerFileDiffHistory { // aiserver.v1.ComposerFileDiffHistory - string file_name = 1; - repeated string diff_history = 2; - repeated double diff_history_timestamps = 3; -} -message ConversationSummary { // aiserver.v1.ConversationSummary - string summary = 1; - string truncation_last_bubble_id_inclusive = 2; - string client_should_start_sending_from_inclusive_bubble_id = 3; -} -message RepositoryInfo { // aiserver.v1.RepositoryInfo - string relative_workspace_path = 1; - repeated string remote_urls = 2; - repeated string remote_names = 3; - string repo_name = 4; - string repo_owner = 5; - bool is_tracked = 6; - bool is_local = 7; - optional int32 num_files = 8; - optional double orthogonal_transform_seed = 9; - optional EmbeddingModel preferred_embedding_model = 10; -} -message ExplicitContext { // aiserver.v1.ExplicitContext - string context = 1; - optional string repo_context = 2; -} -message ModelDetails { // aiserver.v1.ModelDetails - optional string model_name = 1; - optional string api_key = 2; - optional bool enable_ghost_mode = 3; - optional AzureState azure_state = 4; - optional bool enable_slow_pool = 5; - optional string openai_api_base_url = 6; -} -message AzureState { // aiserver.v1.AzureState - string api_key = 1; - string base_url = 2; - string deployment = 3; - bool use_azure = 4; -} -message ChatQuote { // aiserver.v1.ChatQuote - string markdown = 1; - string bubble_id = 2; - int32 section_index = 3; -} -message DebugInfo { // aiserver.v1.DebugInfo - message Breakpoint { // aiserver.v1.DebugInfo.Breakpoint - string relative_workspace_path = 1; - int32 line_number = 2; - repeated string lines_before_breakpoint = 3; - repeated string lines_after_breakpoint = 4; - optional string exception_info = 5; - } - message CallStackFrame { // aiserver.v1.DebugInfo.CallStackFrame - message Scope { // aiserver.v1.DebugInfo.Scope - message Variable { // aiserver.v1.DebugInfo.Variable - string name = 1; - string value = 2; - optional string type = 3; - } - string name = 1; - repeated Variable variables = 2; - } - string relative_workspace_path = 1; - int32 line_number = 2; - string function_name = 3; - repeated Scope scopes = 4; - } - Breakpoint breakpoint = 1; - repeated CallStackFrame call_stack = 2; - repeated CodeBlock history = 3; -} -message ChatExternalLink { // aiserver.v1.ChatExternalLink - string url = 1; - string uuid = 2; -} -message CommitNote { // aiserver.v1.CommitNote - string note = 1; - string commit_hash = 2; +message DiffHistoryResult { // aiserver.v1.DiffHistoryResult + message RenderedDiff { // aiserver.v1.DiffHistoryResult.RenderedDiff + int32 start_line_number = 1; + int32 end_line_number_exclusive = 2; + repeated string before_context_lines = 3; + repeated string removed_lines = 4; + repeated string added_lines = 5; + repeated string after_context_lines = 6; + } + message HumanChange { // aiserver.v1.DiffHistoryResult.HumanChange + string relative_workspace_path = 1; + repeated RenderedDiff rendered_diffs = 2; + } + repeated HumanChange human_changes = 40; } message ContextAST { // aiserver.v1.ContextAST repeated ContainerTree files = 1; @@ -1049,43 +1390,54 @@ message ContainerTree { // aiserver.v1.ContainerTree repeated ContainerTreeNode nodes = 2; } message ContainerTreeNode { // aiserver.v1.ContainerTreeNode - message Container { // aiserver.v1.ContainerTreeNode.Container - message Reference { // aiserver.v1.ContainerTreeNode.Reference - string value = 1; - string relative_workspace_path = 2; - } - string doc_string = 1; - string header = 2; - string trailer = 3; - repeated ContainerTreeNode children = 5; - repeated Reference references = 6; - double score = 7; - } - message Blob { // aiserver.v1.ContainerTreeNode.Blob - optional string value = 1; - } - message Symbol { // aiserver.v1.ContainerTreeNode.Symbol - message Reference { // aiserver.v1.ContainerTreeNode.Reference - string value = 1; - string relative_workspace_path = 2; - } - string doc_string = 1; - string value = 2; - repeated Reference references = 6; - double score = 7; - } - Container container = 1; - Blob blob = 2; - Symbol symbol = 3; + message Symbol { // aiserver.v1.ContainerTreeNode.Symbol + string doc_string = 1; + string value = 2; + repeated Reference references = 6; + double score = 7; + } + message Container { // aiserver.v1.ContainerTreeNode.Container + string doc_string = 1; + string header = 2; + string trailer = 3; + repeated ContainerTreeNode children = 5; + repeated Reference references = 6; + double score = 7; + } + message Blob { // aiserver.v1.ContainerTreeNode.Blob + optional string value = 1; + } + message Reference { // aiserver.v1.ContainerTreeNode.Reference + string value = 1; + string relative_workspace_path = 2; + } + oneof node { + Container container = 1; + Blob blob = 2; + Symbol symbol = 3; + } +} +message CommitNote { // aiserver.v1.CommitNote + string note = 1; + string commit_hash = 2; +} +message ChatQuote { // aiserver.v1.ChatQuote + string markdown = 1; + string bubble_id = 2; + int32 section_index = 3; +} +message ChatExternalLink { // aiserver.v1.ChatExternalLink + string url = 1; + string uuid = 2; } message StreamChatResponse { // aiserver.v1.StreamChatResponse - message ChunkIdentity { // aiserver.v1.StreamChatResponse.ChunkIdentity - string file_name = 1; - int32 start_line = 2; - int32 end_line = 3; - string text = 4; - ChunkType chunk_type = 5; - } + message ChunkIdentity { // aiserver.v1.StreamChatResponse.ChunkIdentity + string file_name = 1; + int32 start_line = 2; + int32 end_line = 3; + string text = 4; + ChunkType chunk_type = 5; + } string text = 1; optional string server_bubble_id = 22; optional string debugging_only_chat_prompt = 2; @@ -1105,6 +1457,38 @@ message StreamChatResponse { // aiserver.v1.StreamChatResponse optional ConversationSummary conversation_summary = 16; optional ServiceStatusUpdate service_status_update = 17; } +message WebCitation { // aiserver.v1.WebCitation + repeated WebReference references = 1; +} +message SymbolLink { // aiserver.v1.SymbolLink + string symbol_name = 1; + string symbol_search_string = 2; + string relative_workspace_path = 3; + int32 rough_line_number = 4; +} +message StatusUpdates { // aiserver.v1.StatusUpdates + repeated StatusUpdate updates = 1; +} +message StatusUpdate { // aiserver.v1.StatusUpdate + string message = 1; + optional string metadata = 2; +} +message ServiceStatusUpdate { // aiserver.v1.ServiceStatusUpdate + string message = 1; + string codicon = 2; + optional bool allow_command_links_potentially_unsafe_please_only_use_for_handwritten_trusted_markdown = 3; + optional string action_to_run_on_status_update = 4; +} +message ServerTimingInfo { // aiserver.v1.ServerTimingInfo + double server_start_time = 1; + double server_first_token_time = 2; + double server_request_sent_time = 3; + double server_end_time = 4; +} +message FileLink { // aiserver.v1.FileLink + string display_name = 1; + string relative_workspace_path = 2; +} message DocumentationCitation { // aiserver.v1.DocumentationCitation repeated DocumentationChunk chunks = 1; } @@ -1115,42 +1499,9 @@ message DocumentationChunk { // aiserver.v1.DocumentationChunk float score = 4; string page_title = 5; } -message DocsReference { // aiserver.v1.DocsReference - string title = 1; - string url = 2; -} -message WebCitation { // aiserver.v1.WebCitation - repeated WebReference references = 1; -} -message WebReference { // aiserver.v1.WebReference - string title = 2; - string url = 1; -} -message StatusUpdates { // aiserver.v1.StatusUpdates - repeated StatusUpdate updates = 1; -} -message StatusUpdate { // aiserver.v1.StatusUpdate - string message = 1; - optional string metadata = 2; -} -message ServerTimingInfo { // aiserver.v1.ServerTimingInfo - double server_start_time = 1; - double server_first_token_time = 2; - double server_request_sent_time = 3; - double server_end_time = 4; -} -message SymbolLink { // aiserver.v1.SymbolLink - string symbol_name = 1; - string symbol_search_string = 2; - string relative_workspace_path = 3; - int32 rough_line_number = 4; -} -message FileLink { // aiserver.v1.FileLink - string display_name = 1; - string relative_workspace_path = 2; -} -message ServiceStatusUpdate { // aiserver.v1.ServiceStatusUpdate - string message = 1; - string codicon = 2; - optional bool allow_command_links_potentially_unsafe_please_only_use_for_handwritten_trusted_markdown = 3; +enum ChunkType { // aiserver.v1.ChunkType + CHUNK_TYPE_UNSPECIFIED = 0; + CHUNK_TYPE_CODEBASE = 1; + CHUNK_TYPE_LONG_FILE = 2; + CHUNK_TYPE_DOCS = 3; } diff --git a/src/chat/aiserver/v1/timestamp.proto b/src/chat/aiserver/v1/timestamp.proto new file mode 100644 index 0000000..fd0bc07 --- /dev/null +++ b/src/chat/aiserver/v1/timestamp.proto @@ -0,0 +1,144 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/timestamppb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// Example 5: Compute Timestamp from Java `Instant.now()`. +// +// Instant now = Instant.now(); +// +// Timestamp timestamp = +// Timestamp.newBuilder().setSeconds(now.getEpochSecond()) +// .setNanos(now.getNano()).build(); +// +// Example 6: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() +// ) to obtain a formatter capable of generating timestamps in this format. +// +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} diff --git a/src/chat/constant.rs b/src/chat/constant.rs index b003cf5..56b9ce2 100644 --- a/src/chat/constant.rs +++ b/src/chat/constant.rs @@ -1,183 +1,205 @@ +use parking_lot::RwLock; +use std::{sync::Arc, time::{Duration, Instant}}; + use super::model::Model; macro_rules! def_pub_const { + // 单个常量定义分支 ($name:ident, $value:expr) => { pub const $name: &'static str = $value; }; -} -def_pub_const!(ERR_UNSUPPORTED_GIF, "不支持动态 GIF"); -def_pub_const!( - ERR_UNSUPPORTED_IMAGE_FORMAT, - "不支持的图片格式,仅支持 PNG、JPEG、WEBP 和非动态 GIF" -); -def_pub_const!(ERR_NODATA, "No data"); -const MODEL_OBJECT: &str = "model"; -const CREATED: &i64 = &1706659200; - -def_pub_const!(ANTHROPIC, "anthropic"); -def_pub_const!(CURSOR, "cursor"); -def_pub_const!(GOOGLE, "google"); -def_pub_const!(OPENAI, "openai"); -def_pub_const!(DEEPSEEK, "deepseek"); - -def_pub_const!(CLAUDE_3_5_SONNET, "claude-3.5-sonnet"); -def_pub_const!(GPT_4, "gpt-4"); -def_pub_const!(GPT_4O, "gpt-4o"); -def_pub_const!(CLAUDE_3_OPUS, "claude-3-opus"); -def_pub_const!(CURSOR_FAST, "cursor-fast"); -def_pub_const!(CURSOR_SMALL, "cursor-small"); -def_pub_const!(GPT_3_5_TURBO, "gpt-3.5-turbo"); -def_pub_const!(GPT_4_TURBO_2024_04_09, "gpt-4-turbo-2024-04-09"); -def_pub_const!(GPT_4O_128K, "gpt-4o-128k"); -def_pub_const!(GEMINI_1_5_FLASH_500K, "gemini-1.5-flash-500k"); -def_pub_const!(CLAUDE_3_HAIKU_200K, "claude-3-haiku-200k"); -def_pub_const!(CLAUDE_3_5_SONNET_200K, "claude-3-5-sonnet-200k"); -def_pub_const!(CLAUDE_3_5_SONNET_20241022, "claude-3-5-sonnet-20241022"); -def_pub_const!(GPT_4O_MINI, "gpt-4o-mini"); -def_pub_const!(O1_MINI, "o1-mini"); -def_pub_const!(O1_PREVIEW, "o1-preview"); -def_pub_const!(O1, "o1"); -def_pub_const!(CLAUDE_3_5_HAIKU, "claude-3.5-haiku"); -def_pub_const!(GEMINI_EXP_1206, "gemini-exp-1206"); -def_pub_const!( - GEMINI_2_0_FLASH_THINKING_EXP, - "gemini-2.0-flash-thinking-exp" -); -def_pub_const!(GEMINI_2_0_FLASH_EXP, "gemini-2.0-flash-exp"); -def_pub_const!(DEEPSEEK_V3, "deepseek-v3"); -def_pub_const!(DEEPSEEK_R1, "deepseek-r1"); - -// #[derive(Clone, PartialEq, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)] -// pub enum ModelType { -// Claude35Sonnet, -// Gpt4, -// Gpt4o, -// Claude3Opus, -// CursorFast, -// CursorSmall, -// Gpt35Turbo, -// Gpt4Turbo202404, -// Gpt4o128k, -// Gemini15Flash500k, -// Claude3Haiku200k, -// Claude35Sonnet200k, -// Claude35Sonnet20241022, -// Gpt4oMini, -// O1Mini, -// O1Preview, -// O1, -// Claude35Haiku, -// GeminiExp1206, -// Gemini20FlashThinkingExp, -// Gemini20FlashExp, -// DeepseekV3, -// DeepseekR1, -// } - -macro_rules! create_model { - ($($id:expr, $owner:expr),* $(,)?) => { - pub const AVAILABLE_MODELS: [Model; count!($( ($id, $owner) )*)] = [ - $( - Model { - id: $id, - created: CREATED, - object: MODEL_OBJECT, - owned_by: $owner, - }, - )* - ]; + // 批量定义分支 + ($($name:ident => $value:expr),+ $(,)?) => { + $( + pub const $name: &'static str = $value; + )+ }; } -macro_rules! count { - () => (0); - (($id:expr, $owner:expr) $( ($id2:expr, $owner2:expr) )*) => (1 + count!($( ($id2, $owner2) )*)); +// 错误信息 +def_pub_const!( + ERR_UNSUPPORTED_GIF => "不支持动态 GIF", + ERR_UNSUPPORTED_IMAGE_FORMAT => "不支持的图片格式,仅支持 PNG、JPEG、WEBP 和非动态 GIF", + ERR_NODATA => "No data", +); + +// 系统常量 +pub const MODEL_OBJECT: &str = "model"; +pub const CREATED: &i64 = &1706659200; + +// AI 服务商 +def_pub_const!( + ANTHROPIC => "anthropic", + CURSOR => "cursor", + GOOGLE => "google", + OPENAI => "openai", + DEEPSEEK => "deepseek", + XAI => "xai", + UNKNOWN => "unknown", +); + +// AI 模型 +def_pub_const!( + // Anthropic 模型 + CLAUDE_3_OPUS => "claude-3-opus", + CLAUDE_3_5_SONNET => "claude-3.5-sonnet", + CLAUDE_3_HAIKU_200K => "claude-3-haiku-200k", + CLAUDE_3_5_SONNET_200K => "claude-3-5-sonnet-200k", + CLAUDE_3_5_SONNET_20241022 => "claude-3-5-sonnet-20241022", + CLAUDE_3_5_HAIKU => "claude-3.5-haiku", + + // OpenAI 模型 + GPT_4 => "gpt-4", + GPT_4O => "gpt-4o", + GPT_3_5_TURBO => "gpt-3.5-turbo", + GPT_4_TURBO_2024_04_09 => "gpt-4-turbo-2024-04-09", + GPT_4O_128K => "gpt-4o-128k", + GPT_4O_MINI => "gpt-4o-mini", + O1_MINI => "o1-mini", + O1_PREVIEW => "o1-preview", + O1 => "o1", + O3_MINI => "o3-mini", + + // Cursor 模型 + CURSOR_FAST => "cursor-fast", + CURSOR_SMALL => "cursor-small", + + // Google 模型 + GEMINI_1_5_FLASH_500K => "gemini-1.5-flash-500k", + GEMINI_EXP_1206 => "gemini-exp-1206", + GEMINI_2_0_PRO_EXP => "gemini-2.0-pro-exp", + GEMINI_2_0_FLASH_THINKING_EXP => "gemini-2.0-flash-thinking-exp", + GEMINI_2_0_FLASH => "gemini-2.0-flash", + + // Deepseek 模型 + DEEPSEEK_V3 => "deepseek-v3", + DEEPSEEK_R1 => "deepseek-r1", + + // XAI 模型 + GROK_2 => "grok-2", +); + +macro_rules! create_models { + ($($model:expr => $owner:expr),* $(,)?) => { + static INSTANCE: std::sync::LazyLock> = std::sync::LazyLock::new(|| { + RwLock::new(Models { + models: Arc::new(vec![ + $( + Model { + id: $model.into(), + created: CREATED, + object: MODEL_OBJECT, + owned_by: $owner, + }, + )* + ]), + last_update: Instant::now() - Duration::from_secs(30 * 60), + }) + }); + }; } -// impl ModelType { -// pub fn as_str_name(&self) -> &'static str { -// match self { -// ModelType::Claude35Sonnet => CLAUDE_3_5_SONNET, -// ModelType::Gpt4 => GPT_4, -// ModelType::Gpt4o => GPT_4O, -// ModelType::Claude3Opus => CLAUDE_3_OPUS, -// ModelType::CursorFast => CURSOR_FAST, -// ModelType::CursorSmall => CURSOR_SMALL, -// ModelType::Gpt35Turbo => GPT_3_5_TURBO, -// ModelType::Gpt4Turbo202404 => GPT_4_TURBO_2024_04_09, -// ModelType::Gpt4o128k => GPT_4O_128K, -// ModelType::Gemini15Flash500k => GEMINI_1_5_FLASH_500K, -// ModelType::Claude3Haiku200k => CLAUDE_3_HAIKU_200K, -// ModelType::Claude35Sonnet200k => CLAUDE_3_5_SONNET_200K, -// ModelType::Claude35Sonnet20241022 => CLAUDE_3_5_SONNET_20241022, -// ModelType::Gpt4oMini => GPT_4O_MINI, -// ModelType::O1Mini => O1_MINI, -// ModelType::O1Preview => O1_PREVIEW, -// ModelType::O1 => O1, -// ModelType::Claude35Haiku => CLAUDE_3_5_HAIKU, -// ModelType::GeminiExp1206 => GEMINI_EXP_1206, -// ModelType::Gemini20FlashThinkingExp => GEMINI_2_0_FLASH_THINKING_EXP, -// ModelType::Gemini20FlashExp => GEMINI_2_0_FLASH_EXP, -// ModelType::DeepseekV3 => DEEPSEEK_V3, -// ModelType::DeepseekR1 => DEEPSEEK_R1, -// } -// } +pub struct Models { + pub models: Arc>, + last_update: Instant, +} -// pub fn from_str_name(id :&str) -> Option { -// match id { -// CLAUDE_3_5_SONNET => Some(ModelType::Claude35Sonnet), -// GPT_4 => Some(ModelType::Gpt4), -// GPT_4O => Some(ModelType::Gpt4o), -// CLAUDE_3_OPUS => Some(ModelType::Claude3Opus), -// CURSOR_FAST => Some(ModelType::CursorFast), -// CURSOR_SMALL => Some(ModelType::CursorSmall), -// GPT_3_5_TURBO => Some(ModelType::Gpt35Turbo), -// GPT_4_TURBO_2024_04_09 => Some(ModelType::Gpt4Turbo202404), -// GPT_4O_128K => Some(ModelType::Gpt4o128k), -// GEMINI_1_5_FLASH_500K => Some(ModelType::Gemini15Flash500k), -// CLAUDE_3_HAIKU_200K => Some(ModelType::Claude3Haiku200k), -// CLAUDE_3_5_SONNET_200K => Some(ModelType::Claude35Sonnet200k), -// CLAUDE_3_5_SONNET_20241022 => Some(ModelType::Claude35Sonnet20241022), -// GPT_4O_MINI => Some(ModelType::Gpt4oMini), -// O1_MINI => Some(ModelType::O1Mini), -// O1_PREVIEW => Some(ModelType::O1Preview), -// O1 => Some(ModelType::O1), -// CLAUDE_3_5_HAIKU => Some(ModelType::Claude35Haiku), -// GEMINI_EXP_1206 => Some(ModelType::GeminiExp1206), -// GEMINI_2_0_FLASH_THINKING_EXP => Some(ModelType::Gemini20FlashThinkingExp), -// GEMINI_2_0_FLASH_EXP => Some(ModelType::Gemini20FlashExp), -// DEEPSEEK_V3 => Some(ModelType::DeepseekV3), -// DEEPSEEK_R1 => Some(ModelType::DeepseekR1), -// _ => None, -// } -// } +impl Models { + // 返回读锁 + pub fn read() -> parking_lot::RwLockReadGuard<'static, Models> { + INSTANCE.read() + } + + // 返回 Arc 的克隆 + pub fn to_arc() -> Arc> { + INSTANCE.read().models.clone() + } + + // 克隆所有模型 + // pub fn cloned() -> Vec { + // INSTANCE.read().models.as_ref().clone() + // } + + // 检查模型是否存在 + pub fn exists(model_id: &str) -> bool { + Self::read().models.iter().any(|m| m.id == model_id) + } + + // 查找模型并返回其 ID + pub fn find_id(model: &str) -> Option { + Self::read() + .models + .iter() + .find(|m| m.id == model) + .map(|m| m.id.clone()) + } + + // 返回所有模型 ID 的列表 + pub fn ids() -> Vec { + Self::read() + .models + .iter() + .map(|m| m.id.clone()) + .collect() + } + + // 写入方法 + pub fn update(new_models: Vec) -> Result<(), &'static str> { + if new_models.is_empty() { + return Err("Models list cannot be empty"); + } + + let mut data = INSTANCE.write(); + + // 检查时间间隔(30分钟) + if data.last_update.elapsed() < Duration::from_secs(30 * 60) { + return Err("Cannot update models more frequently than every 30 minutes"); + } + + // 检查内容是否有变化 + if *data.models == new_models { + return Err("No changes in models"); + } + + // 更新数据和时间戳 + data.models = Arc::new(new_models); + data.last_update = Instant::now(); + + Ok(()) + } +} + +// macro_rules! count { +// () => (0); +// (($id:expr, $owner:expr) $( ($id2:expr, $owner2:expr) )*) => (1 + count!($( ($id2, $owner2) )*)); // } -create_model!( - CLAUDE_3_5_SONNET, ANTHROPIC, - GPT_4, OPENAI, - GPT_4O, OPENAI, - CLAUDE_3_OPUS, ANTHROPIC, - CURSOR_FAST, CURSOR, - CURSOR_SMALL, CURSOR, - GPT_3_5_TURBO, OPENAI, - GPT_4_TURBO_2024_04_09, OPENAI, - GPT_4O_128K, OPENAI, - GEMINI_1_5_FLASH_500K, GOOGLE, - CLAUDE_3_HAIKU_200K, ANTHROPIC, - CLAUDE_3_5_SONNET_200K, ANTHROPIC, - CLAUDE_3_5_SONNET_20241022, ANTHROPIC, - GPT_4O_MINI, OPENAI, - O1_MINI, OPENAI, - O1_PREVIEW, OPENAI, - O1, OPENAI, - CLAUDE_3_5_HAIKU, ANTHROPIC, - GEMINI_EXP_1206, GOOGLE, - GEMINI_2_0_FLASH_THINKING_EXP, GOOGLE, - GEMINI_2_0_FLASH_EXP, GOOGLE, - DEEPSEEK_V3, DEEPSEEK, - DEEPSEEK_R1, DEEPSEEK, +create_models!( + CLAUDE_3_5_SONNET => ANTHROPIC, + GPT_4 => OPENAI, + GPT_4O => OPENAI, + CLAUDE_3_OPUS => ANTHROPIC, + CURSOR_FAST => CURSOR, + CURSOR_SMALL => CURSOR, + GPT_3_5_TURBO => OPENAI, + GPT_4_TURBO_2024_04_09 => OPENAI, + GPT_4O_128K => OPENAI, + GEMINI_1_5_FLASH_500K => GOOGLE, + CLAUDE_3_HAIKU_200K => ANTHROPIC, + CLAUDE_3_5_SONNET_200K => ANTHROPIC, + GPT_4O_MINI => OPENAI, + O1_MINI => OPENAI, + O1_PREVIEW => OPENAI, + O1 => OPENAI, + CLAUDE_3_5_HAIKU => ANTHROPIC, + GEMINI_2_0_PRO_EXP => GOOGLE, + GEMINI_2_0_FLASH_THINKING_EXP => GOOGLE, + GEMINI_2_0_FLASH => GOOGLE, + DEEPSEEK_V3 => DEEPSEEK, + DEEPSEEK_R1 => DEEPSEEK, + O3_MINI => OPENAI, + GROK_2 => XAI, ); pub const USAGE_CHECK_MODELS: [&str; 11] = [ @@ -200,5 +222,3 @@ pub const LONG_CONTEXT_MODELS: [&str; 4] = [ CLAUDE_3_HAIKU_200K, CLAUDE_3_5_SONNET_200K, ]; - -// include!("constant/models.rs"); diff --git a/src/chat/constant/models.rs b/src/chat/constant/models.rs deleted file mode 100644 index bf010b4..0000000 --- a/src/chat/constant/models.rs +++ /dev/null @@ -1,118 +0,0 @@ -pub struct DefaultModel { - pub default_on: bool, - pub is_long_context_only: Option, - pub name: &'static str, -} - -pub const AVAILABLE_MODELS2: [DefaultModel; 22] = [ - DefaultModel { - default_on: true, - is_long_context_only: Some(false), - name: CLAUDE_3_5_SONNET, - }, - DefaultModel { - default_on: false, - is_long_context_only: None, - name: GPT_4, - }, - DefaultModel { - default_on: true, - is_long_context_only: None, - name: GPT_4O, - }, - DefaultModel { - default_on: false, - is_long_context_only: None, - name: CLAUDE_3_OPUS, - }, - DefaultModel { - default_on: false, - is_long_context_only: None, - name: CURSOR_FAST, - }, - DefaultModel { - default_on: false, - is_long_context_only: None, - name: CURSOR_SMALL, - }, - DefaultModel { - default_on: false, - is_long_context_only: None, - name: GPT_3_5_TURBO, - }, - DefaultModel { - default_on: false, - is_long_context_only: None, - name: GPT_4_TURBO_2024_04_09, - }, - DefaultModel { - default_on: true, - is_long_context_only: Some(true), - name: GPT_4O_128K, - }, - DefaultModel { - default_on: true, - is_long_context_only: Some(true), - name: GEMINI_1_5_FLASH_500K, - }, - DefaultModel { - default_on: true, - is_long_context_only: Some(true), - name: CLAUDE_3_HAIKU_200K, - }, - DefaultModel { - default_on: true, - is_long_context_only: Some(true), - name: CLAUDE_3_5_SONNET_200K, - }, - DefaultModel { - default_on: false, - is_long_context_only: Some(false), - name: CLAUDE_3_5_SONNET_20241022, - }, - DefaultModel { - default_on: true, - is_long_context_only: Some(false), - name: GPT_4O_MINI, - }, - DefaultModel { - default_on: true, - is_long_context_only: Some(false), - name: O1_MINI, - }, - DefaultModel { - default_on: true, - is_long_context_only: Some(false), - name: O1_PREVIEW, - }, - DefaultModel { - default_on: true, - is_long_context_only: Some(false), - name: O1, - }, - DefaultModel { - default_on: false, - is_long_context_only: Some(false), - name: CLAUDE_3_5_HAIKU, - }, - DefaultModel { - default_on: false, - is_long_context_only: None, - name: GEMINI_EXP_1206, - }, - DefaultModel { - default_on: false, - is_long_context_only: None, - name: GEMINI_2_0_FLASH_THINKING_EXP, - }, - DefaultModel { - default_on: false, - is_long_context_only: None, - name: GEMINI_2_0_FLASH_EXP, - }, - DefaultModel { - default_on: false, - is_long_context_only: None, - name: DEEPSEEK_V3, - }, -]; \ No newline at end of file diff --git a/src/chat/error.rs b/src/chat/error.rs index c159ba1..d30dfc5 100644 --- a/src/chat/error.rs +++ b/src/chat/error.rs @@ -1,6 +1,6 @@ use super::aiserver::v1::ErrorDetails; use crate::common::model::{ApiStatus, ErrorResponse as CommonErrorResponse}; -use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _}; +use base64::{Engine as _, engine::general_purpose::STANDARD_NO_PAD}; use prost::Message as _; use reqwest::StatusCode; use serde::{Deserialize, Serialize}; @@ -42,7 +42,7 @@ pub struct ErrorDetail { // } impl ChatError { - pub fn to_error_response(self) -> ErrorResponse { + pub fn into_error_response(self) -> ErrorResponse { if self.error.details.is_empty() { return ErrorResponse { status: 500, @@ -108,7 +108,7 @@ impl ErrorResponse { ) } - pub fn to_common(self) -> CommonErrorResponse { + pub fn into_common(self) -> CommonErrorResponse { CommonErrorResponse { status: ApiStatus::Error, code: Some(self.status), diff --git a/src/chat/middleware/auth.rs b/src/chat/middleware/auth.rs index ba1bd7f..46fc0bf 100644 --- a/src/chat/middleware/auth.rs +++ b/src/chat/middleware/auth.rs @@ -1,7 +1,7 @@ use crate::app::{constant::AUTHORIZATION_BEARER_PREFIX, lazy::AUTH_TOKEN}; use axum::{ body::Body, - http::{header::AUTHORIZATION, Request, StatusCode}, + http::{Request, StatusCode, header::AUTHORIZATION}, middleware::Next, response::Response, }; diff --git a/src/chat/model.rs b/src/chat/model.rs index 24576c0..06271b5 100644 --- a/src/chat/model.rs +++ b/src/chat/model.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] @@ -80,22 +82,28 @@ pub struct Usage { // 模型定义 #[derive(Serialize, Clone)] pub struct Model { - pub id: &'static str, + pub id: String, pub created: &'static i64, pub object: &'static str, pub owned_by: &'static str, } -use super::constant::USAGE_CHECK_MODELS; +impl PartialEq for Model { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +use super::constant::{Models, USAGE_CHECK_MODELS}; use crate::app::model::{AppConfig, UsageCheck}; impl Model { - pub fn is_usage_check(&self, usage_check: Option) -> bool { + pub fn is_usage_check(model_id: &String, usage_check: Option) -> bool { match usage_check.unwrap_or(AppConfig::get_usage_check()) { UsageCheck::None => false, - UsageCheck::Default => USAGE_CHECK_MODELS.contains(&self.id), + UsageCheck::Default => USAGE_CHECK_MODELS.contains(&model_id.as_str()), UsageCheck::All => true, - UsageCheck::Custom(models) => models.contains(&self.id), + UsageCheck::Custom(models) => models.contains(model_id), } } } @@ -103,5 +111,18 @@ impl Model { #[derive(Serialize)] pub struct ModelsResponse { pub object: &'static str, - pub data: &'static [Model], + pub data: Arc>, +} + +impl ModelsResponse { + pub(super) fn new(data: Arc>) -> Self { + Self { + object: "list", + data, + } + } + + pub(super) fn with_default_models() -> Self { + Self::new(Models::to_arc()) + } } diff --git a/src/chat/route.rs b/src/chat/route.rs index cf12473..d876c47 100644 --- a/src/chat/route.rs +++ b/src/chat/route.rs @@ -2,12 +2,15 @@ mod logs; pub use logs::{handle_logs, handle_logs_post}; mod health; pub use health::{handle_health, handle_root}; +mod token; +pub use token::{handle_basic_calibration, handle_tokens_page}; mod tokens; pub use tokens::{ - handle_add_tokens, handle_basic_calibration, handle_delete_tokens, handle_get_checksum, - handle_get_hash, handle_get_timestamp_header, handle_get_tokens, handle_reload_tokens, - handle_tokens_page, handle_update_tokens, + handle_add_tokens, handle_delete_tokens, handle_get_tokens, handle_update_token_tags, + handle_update_tokens, }; +mod checksum; +pub use checksum::{handle_get_checksum, handle_get_hash, handle_get_timestamp_header}; mod profile; pub use profile::handle_user_info; mod config; diff --git a/src/chat/route/api.rs b/src/chat/route/api.rs index 109d5f9..7a96160 100644 --- a/src/chat/route/api.rs +++ b/src/chat/route/api.rs @@ -1,26 +1,29 @@ -use axum::response::{IntoResponse, Response}; +use axum::{ + body::Body, + response::{IntoResponse, Response}, +}; use reqwest::header::CONTENT_TYPE; use crate::{ + AppConfig, PageContent, app::constant::{ CONTENT_TYPE_TEXT_HTML_WITH_UTF8, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_API_PATH, }, - AppConfig, PageContent, }; pub async fn handle_api_page() -> impl IntoResponse { match AppConfig::get_page_content(ROUTE_API_PATH).unwrap_or_default() { PageContent::Default => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(include_str!("../../../static/api.min.html").to_string()) + .body(Body::from(include_str!("../../../static/api.min.html"))) .unwrap(), PageContent::Text(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8) - .body(content.clone()) + .body(Body::from(content)) .unwrap(), PageContent::Html(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(content.clone()) + .body(Body::from(content)) .unwrap(), } } diff --git a/src/chat/route/checksum.rs b/src/chat/route/checksum.rs new file mode 100644 index 0000000..7a8b75e --- /dev/null +++ b/src/chat/route/checksum.rs @@ -0,0 +1,59 @@ +use axum::{ + extract::Query, + http::{HeaderMap, header::CONTENT_TYPE}, + response::{IntoResponse as _, Response}, +}; +use serde::Deserialize; + +use crate::{ + app::constant::CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, + common::utils::{ + generate_checksum_with_default, generate_checksum_with_repair, generate_hash, + generate_timestamp_header, + }, +}; + +pub async fn handle_get_hash() -> Response { + let hash = generate_hash(); + + let mut headers = HeaderMap::new(); + headers.insert( + CONTENT_TYPE, + CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8.parse().unwrap(), + ); + + (headers, hash).into_response() +} + +#[derive(Deserialize)] +pub struct ChecksumQuery { + #[serde(default)] + pub checksum: Option, +} + +pub async fn handle_get_checksum(Query(query): Query) -> Response { + let checksum = match query.checksum { + None => generate_checksum_with_default(), + Some(checksum) => generate_checksum_with_repair(&checksum), + }; + + let mut headers = HeaderMap::new(); + headers.insert( + CONTENT_TYPE, + CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8.parse().unwrap(), + ); + + (headers, checksum).into_response() +} + +pub async fn handle_get_timestamp_header() -> Response { + let timestamp_header = generate_timestamp_header(); + + let mut headers = HeaderMap::new(); + headers.insert( + CONTENT_TYPE, + CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8.parse().unwrap(), + ); + + (headers, timestamp_header).into_response() +} diff --git a/src/chat/route/config.rs b/src/chat/route/config.rs index eb94dc2..f6e5677 100644 --- a/src/chat/route/config.rs +++ b/src/chat/route/config.rs @@ -1,30 +1,33 @@ use crate::{ app::{ constant::{ - AUTHORIZATION_BEARER_PREFIX, CONTENT_TYPE_TEXT_CSS_WITH_UTF8, CONTENT_TYPE_TEXT_HTML_WITH_UTF8, CONTENT_TYPE_TEXT_JS_WITH_UTF8, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_ABOUT_PATH, ROUTE_BUILD_KEY_PATH, ROUTE_CONFIG_PATH, ROUTE_README_PATH, ROUTE_SHARED_JS_PATH, ROUTE_SHARED_STYLES_PATH + AUTHORIZATION_BEARER_PREFIX, CONTENT_TYPE_TEXT_CSS_WITH_UTF8, + CONTENT_TYPE_TEXT_HTML_WITH_UTF8, CONTENT_TYPE_TEXT_JS_WITH_UTF8, + CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_ABOUT_PATH, ROUTE_BUILD_KEY_PATH, + ROUTE_CONFIG_PATH, ROUTE_README_PATH, ROUTE_SHARED_JS_PATH, ROUTE_SHARED_STYLES_PATH, }, lazy::{AUTH_TOKEN, KEY_PREFIX}, model::{AppConfig, BuildKeyRequest, BuildKeyResponse, PageContent, UsageCheckModelType}, }, - chat::config::{key_config, KeyConfig}, + chat::config::{KeyConfig, key_config}, common::utils::{to_base64, token_to_tokeninfo}, }; use axum::{ + Json, body::Body, extract::Path, http::{ - header::{AUTHORIZATION, CONTENT_TYPE, LOCATION}, HeaderMap, StatusCode, + header::{AUTHORIZATION, CONTENT_TYPE, LOCATION}, }, response::{IntoResponse, Response}, - Json, }; use prost::Message as _; pub async fn handle_env_example() -> impl IntoResponse { Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8) - .body(include_str!("../../../.env.example").to_string()) + .body(Body::from(include_str!("../../../.env.example"))) .unwrap() } @@ -33,15 +36,15 @@ pub async fn handle_config_page() -> impl IntoResponse { match AppConfig::get_page_content(ROUTE_CONFIG_PATH).unwrap_or_default() { PageContent::Default => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(include_str!("../../../static/config.min.html").to_string()) + .body(Body::from(include_str!("../../../static/config.min.html"))) .unwrap(), PageContent::Text(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8) - .body(content.clone()) + .body(Body::from(content)) .unwrap(), PageContent::Html(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(content.clone()) + .body(Body::from(content)) .unwrap(), } } @@ -52,11 +55,13 @@ pub async fn handle_static(Path(path): Path) -> impl IntoResponse { match AppConfig::get_page_content(ROUTE_SHARED_STYLES_PATH).unwrap_or_default() { PageContent::Default => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_CSS_WITH_UTF8) - .body(include_str!("../../../static/shared-styles.min.css").to_string()) + .body(Body::from(include_str!( + "../../../static/shared-styles.min.css" + ))) .unwrap(), PageContent::Text(content) | PageContent::Html(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_CSS_WITH_UTF8) - .body(content.clone()) + .body(Body::from(content)) .unwrap(), } } @@ -64,17 +69,19 @@ pub async fn handle_static(Path(path): Path) -> impl IntoResponse { match AppConfig::get_page_content(ROUTE_SHARED_JS_PATH).unwrap_or_default() { PageContent::Default => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_JS_WITH_UTF8) - .body(include_str!("../../../static/shared.min.js").to_string()) + .body(Body::from( + include_str!("../../../static/shared.min.js").to_string(), + )) .unwrap(), PageContent::Text(content) | PageContent::Html(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_JS_WITH_UTF8) - .body(content.clone()) + .body(Body::from(content)) .unwrap(), } } _ => Response::builder() .status(StatusCode::NOT_FOUND) - .body("Not found".to_string()) + .body(Body::from("Not found")) .unwrap(), } } @@ -83,15 +90,15 @@ pub async fn handle_readme() -> impl IntoResponse { match AppConfig::get_page_content(ROUTE_README_PATH).unwrap_or_default() { PageContent::Default => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(include_str!("../../../static/readme.min.html").to_string()) + .body(Body::from(include_str!("../../../static/readme.min.html"))) .unwrap(), PageContent::Text(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8) - .body(content.clone()) + .body(Body::from(content)) .unwrap(), PageContent::Html(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(content.clone()) + .body(Body::from(content)) .unwrap(), } } @@ -105,11 +112,11 @@ pub async fn handle_about() -> impl IntoResponse { .unwrap(), PageContent::Text(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8) - .body(Body::from(content.clone())) + .body(Body::from(content)) .unwrap(), PageContent::Html(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(Body::from(content.clone())) + .body(Body::from(content)) .unwrap(), } } @@ -118,15 +125,17 @@ pub async fn handle_build_key_page() -> impl IntoResponse { match AppConfig::get_page_content(ROUTE_BUILD_KEY_PATH).unwrap_or_default() { PageContent::Default => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(include_str!("../../../static/build_key.min.html").to_string()) + .body(Body::from(include_str!( + "../../../static/build_key.min.html" + ))) .unwrap(), PageContent::Text(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8) - .body(content.clone()) + .body(Body::from(content)) .unwrap(), PageContent::Html(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(content.clone()) + .body(Body::from(content)) .unwrap(), } } @@ -142,7 +151,9 @@ pub async fn handle_build_key( .and_then(|h| h.to_str().ok()) .and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX)); - if auth_header.map_or(true, |h| h != AppConfig::get_share_token().as_str() && h != AUTH_TOKEN.as_str()) { + if auth_header + .is_none_or(|h| h != AppConfig::get_share_token().as_str() && h != AUTH_TOKEN.as_str()) + { return ( StatusCode::UNAUTHORIZED, Json(BuildKeyResponse::Error("Unauthorized".to_owned())), @@ -157,7 +168,7 @@ pub async fn handle_build_key( return ( StatusCode::BAD_REQUEST, Json(BuildKeyResponse::Error("Invalid auth token".to_owned())), - ) + ); } }; @@ -173,9 +184,7 @@ pub async fn handle_build_key( if let Some(usage_check_models) = request.usage_check_models { let usage_check = key_config::UsageCheckModel { r#type: match usage_check_models.model_type { - UsageCheckModelType::Default => { - key_config::usage_check_model::Type::Default as i32 - } + UsageCheckModelType::Default => key_config::usage_check_model::Type::Default as i32, UsageCheckModelType::Disabled => { key_config::usage_check_model::Type::Disabled as i32 } diff --git a/src/chat/route/health.rs b/src/chat/route/health.rs index 4623af6..622c4f4 100644 --- a/src/chat/route/health.rs +++ b/src/chat/route/health.rs @@ -6,28 +6,28 @@ use crate::{ ROUTE_BASIC_CALIBRATION_PATH, ROUTE_BUILD_KEY_PATH, ROUTE_CONFIG_PATH, ROUTE_ENV_EXAMPLE_PATH, ROUTE_GET_CHECKSUM, ROUTE_GET_HASH, ROUTE_GET_TIMESTAMP_HEADER, ROUTE_HEALTH_PATH, ROUTE_LOGS_PATH, ROUTE_README_PATH, ROUTE_ROOT_PATH, - ROUTE_STATIC_PATH, ROUTE_TOKENS_ADD_PATH, ROUTE_TOKENS_DELETE_PATH, - ROUTE_TOKENS_GET_PATH, ROUTE_TOKENS_PATH, ROUTE_TOKENS_UPDATE_PATH, - ROUTE_USER_INFO_PATH, + ROUTE_STATIC_PATH, ROUTE_TOKEN_TAGS_UPDATE_PATH, ROUTE_TOKENS_ADD_PATH, + ROUTE_TOKENS_DELETE_PATH, ROUTE_TOKENS_GET_PATH, ROUTE_TOKENS_PATH, + ROUTE_TOKENS_UPDATE_PATH, ROUTE_USER_INFO_PATH, }, - lazy::{get_start_time, AUTH_TOKEN, ROUTE_CHAT_PATH, ROUTE_MODELS_PATH}, + lazy::{AUTH_TOKEN, ROUTE_CHAT_PATH, ROUTE_MODELS_PATH, get_start_time}, model::{AppConfig, AppState, PageContent}, }, - chat::constant::AVAILABLE_MODELS, + chat::constant::Models, common::model::{ - health::{CpuInfo, HealthCheckResponse, MemoryInfo, SystemInfo, SystemStats}, ApiStatus, + health::{CpuInfo, HealthCheckResponse, MemoryInfo, SystemInfo, SystemStats}, }, }; use axum::{ + Json, body::Body, extract::State, http::{ - header::{CONTENT_TYPE, LOCATION}, HeaderMap, StatusCode, + header::{CONTENT_TYPE, LOCATION}, }, response::{IntoResponse, Response}, - Json, }; use chrono::Local; use reqwest::header::AUTHORIZATION; @@ -44,11 +44,11 @@ pub async fn handle_root() -> impl IntoResponse { .unwrap(), PageContent::Text(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8) - .body(Body::from(content.clone())) + .body(Body::from(content)) .unwrap(), PageContent::Html(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(Body::from(content.clone())) + .body(Body::from(content)) .unwrap(), } } @@ -65,7 +65,7 @@ pub async fn handle_health( .get(AUTHORIZATION) .and_then(|h| h.to_str().ok()) .and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX)) - .map_or(false, |token| token == AUTH_TOKEN.as_str()) + .is_some_and(|token| token == AUTH_TOKEN.as_str()) { // 只有在需要系统信息时才创建实例 let mut sys = System::new_with_specifics( @@ -93,8 +93,8 @@ pub async fn handle_health( Some(SystemStats { started: start_time.to_string(), - total_requests: state.total_requests, - active_requests: state.active_requests, + total_requests: state.request_manager.total_requests, + active_requests: state.request_manager.active_requests, system: SystemInfo { memory: MemoryInfo { rss: memory, // 物理内存使用量(字节) @@ -113,7 +113,7 @@ pub async fn handle_health( version: PKG_VERSION, uptime, stats, - models: AVAILABLE_MODELS.iter().map(|m| m.id).collect::>(), + models: Models::ids(), endpoints: vec![ ROUTE_CHAT_PATH.as_str(), ROUTE_MODELS_PATH.as_str(), @@ -122,6 +122,7 @@ pub async fn handle_health( ROUTE_TOKENS_UPDATE_PATH, ROUTE_TOKENS_ADD_PATH, ROUTE_TOKENS_DELETE_PATH, + ROUTE_TOKEN_TAGS_UPDATE_PATH, ROUTE_LOGS_PATH, ROUTE_ENV_EXAMPLE_PATH, ROUTE_CONFIG_PATH, diff --git a/src/chat/route/logs.rs b/src/chat/route/logs.rs index 2e6d18f..17e95c7 100644 --- a/src/chat/route/logs.rs +++ b/src/chat/route/logs.rs @@ -10,14 +10,14 @@ use crate::{ common::{model::ApiStatus, utils::extract_token}, }; use axum::{ + Json, body::Body, extract::State, http::{ - header::{AUTHORIZATION, CONTENT_TYPE}, HeaderMap, StatusCode, + header::{AUTHORIZATION, CONTENT_TYPE}, }, response::{IntoResponse, Response}, - Json, }; use chrono::Local; use std::sync::Arc; @@ -28,17 +28,15 @@ pub async fn handle_logs() -> impl IntoResponse { match AppConfig::get_page_content(ROUTE_LOGS_PATH).unwrap_or_default() { PageContent::Default => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(Body::from( - include_str!("../../../static/logs.min.html").to_string(), - )) + .body(Body::from(include_str!("../../../static/logs.min.html"))) .unwrap(), PageContent::Text(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8) - .body(Body::from(content.clone())) + .body(Body::from(content)) .unwrap(), PageContent::Html(content) => Response::builder() .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(Body::from(content.clone())) + .body(Body::from(content)) .unwrap(), } } @@ -62,10 +60,10 @@ pub async fn handle_logs_post( if auth_header == auth_token { return Ok(Json(LogsResponse { status: ApiStatus::Success, - total: state.total_requests, - active: Some(state.active_requests), - error: Some(state.error_requests), - logs: state.request_logs.clone(), + total: state.request_manager.total_requests, + active: Some(state.request_manager.active_requests), + error: Some(state.request_manager.error_requests), + logs: state.request_manager.request_logs.clone(), timestamp: Local::now().to_string(), })); } @@ -75,6 +73,7 @@ pub async fn handle_logs_post( // 否则筛选出token匹配的日志 let filtered_logs: Vec = state + .request_manager .request_logs .iter() .filter(|log| log.token_info.token == token_part) diff --git a/src/chat/route/profile.rs b/src/chat/route/profile.rs index 6d3af8b..ae2fd81 100644 --- a/src/chat/route/profile.rs +++ b/src/chat/route/profile.rs @@ -1,10 +1,13 @@ use crate::{ chat::constant::ERR_NODATA, - common::{model::userinfo::GetUserInfo, utils::{extract_token, get_token_profile}}, + common::{ + model::userinfo::GetUserInfo, + utils::{extract_token, get_token_profile}, + }, }; use axum::Json; -use super::tokens::TokenRequest; +use super::token::TokenRequest; pub async fn handle_user_info(Json(request): Json) -> Json { let auth_token = match request.token { @@ -12,7 +15,7 @@ pub async fn handle_user_info(Json(request): Json) -> Json { return Json(GetUserInfo::Error { error: ERR_NODATA.to_string(), - }) + }); } }; @@ -21,12 +24,12 @@ pub async fn handle_user_info(Json(request): Json) -> Json { return Json(GetUserInfo::Error { error: ERR_NODATA.to_string(), - }) + }); } }; match get_token_profile(&token).await { - Some(usage) => Json(GetUserInfo::Usage(usage)), + Some(usage) => Json(GetUserInfo::Usage(Box::new(usage))), None => Json(GetUserInfo::Error { error: ERR_NODATA.to_string(), }), diff --git a/src/chat/route/token.rs b/src/chat/route/token.rs new file mode 100644 index 0000000..eabcc21 --- /dev/null +++ b/src/chat/route/token.rs @@ -0,0 +1,99 @@ +use crate::{ + app::{ + constant::{ + CONTENT_TYPE_TEXT_HTML_WITH_UTF8, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_TOKENS_PATH, + }, + model::{AppConfig, PageContent}, + }, + common::{ + model::ApiStatus, + utils::{extract_time, extract_time_ks, extract_user_id, validate_token_and_checksum}, + }, +}; +use axum::{ + Json, + body::Body, + http::header::CONTENT_TYPE, + response::{IntoResponse, Response}, +}; +use serde::{Deserialize, Serialize}; + +pub async fn handle_tokens_page() -> impl IntoResponse { + match AppConfig::get_page_content(ROUTE_TOKENS_PATH).unwrap_or_default() { + PageContent::Default => Response::builder() + .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) + .body(Body::from(include_str!("../../../static/tokens.min.html"))) + .unwrap(), + PageContent::Text(content) => Response::builder() + .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8) + .body(Body::from(content)) + .unwrap(), + PageContent::Html(content) => Response::builder() + .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) + .body(Body::from(content)) + .unwrap(), + } +} + +#[derive(Deserialize)] +pub struct TokenRequest { + pub token: Option, +} + +#[derive(Serialize)] +pub struct BasicCalibrationResponse { + pub status: ApiStatus, + pub message: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub user_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub create_at: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub checksum_time: Option, +} + +pub async fn handle_basic_calibration( + Json(request): Json, +) -> Json { + // 从请求头中获取并验证 auth token + let auth_token = match request.token { + Some(token) => token, + None => { + return Json(BasicCalibrationResponse { + status: ApiStatus::Error, + message: Some("未提供授权令牌".to_string()), + user_id: None, + create_at: None, + checksum_time: None, + }); + } + }; + + // 校验 token 和 checksum + let (token, checksum) = match validate_token_and_checksum(&auth_token) { + Some(parts) => parts, + None => { + return Json(BasicCalibrationResponse { + status: ApiStatus::Error, + message: Some("无效令牌或无效校验和".to_string()), + user_id: None, + create_at: None, + checksum_time: None, + }); + } + }; + + // 提取用户ID和创建时间 + let user_id = extract_user_id(&token); + let create_at = extract_time(&token).map(|dt| dt.to_string()); + let checksum_time = extract_time_ks(&checksum[..8]); + + // 返回校验结果 + Json(BasicCalibrationResponse { + status: ApiStatus::Success, + message: Some("校验成功".to_string()), + user_id, + create_at, + checksum_time, + }) +} diff --git a/src/chat/route/tokens.rs b/src/chat/route/tokens.rs index b5ce309..b754a1e 100644 --- a/src/chat/route/tokens.rs +++ b/src/chat/route/tokens.rs @@ -1,83 +1,29 @@ use crate::{ app::{ - constant::{ - AUTHORIZATION_BEARER_PREFIX, CONTENT_TYPE_TEXT_HTML_WITH_UTF8, - CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_TOKENS_PATH, - }, - lazy::{AUTH_TOKEN, TOKEN_LIST_FILE}, + constant::AUTHORIZATION_BEARER_PREFIX, + lazy::AUTH_TOKEN, model::{ - AppConfig, AppState, PageContent, TokenAddRequestTokenInfo, TokenInfo, - TokenUpdateRequest, TokensDeleteRequest, TokensDeleteResponse, + AppState, TokenAddRequest, TokenInfo, TokenInfoResponse, TokenManager, + TokenTagsResponse, TokenTagsUpdateRequest, TokenUpdateRequest, TokensDeleteRequest, + TokensDeleteResponse, }, }, common::{ - model::{error::ChatError, ApiStatus, ErrorResponse}, + model::{ApiStatus, ErrorResponse, error::ChatError, userinfo::TokenProfile}, utils::{ - extract_time, extract_time_ks, extract_user_id, generate_checksum_with_default, - generate_checksum_with_repair, generate_hash, generate_timestamp_header, load_tokens, - parse_token, validate_token, validate_token_and_checksum, write_tokens, + generate_checksum_with_default, generate_checksum_with_repair, + load_tokens_from_content, parse_token, validate_token, }, }, }; use axum::{ - extract::{Query, State}, - http::{ - header::{AUTHORIZATION, CONTENT_TYPE}, - HeaderMap, - }, - response::{IntoResponse, Response}, Json, + extract::State, + http::{HeaderMap, StatusCode, header::AUTHORIZATION}, }; -use reqwest::StatusCode; -use serde::{Deserialize, Serialize}; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use tokio::sync::Mutex; -pub async fn handle_get_hash() -> Response { - let hash = generate_hash(); - - let mut headers = HeaderMap::new(); - headers.insert( - CONTENT_TYPE, - CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8.parse().unwrap(), - ); - - (headers, hash).into_response() -} - -#[derive(Deserialize)] -pub struct ChecksumQuery { - #[serde(default)] - pub checksum: Option, -} - -pub async fn handle_get_checksum(Query(query): Query) -> Response { - let checksum = match query.checksum { - None => generate_checksum_with_default(), - Some(checksum) => generate_checksum_with_repair(&checksum), - }; - - let mut headers = HeaderMap::new(); - headers.insert( - CONTENT_TYPE, - CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8.parse().unwrap(), - ); - - (headers, checksum).into_response() -} - -pub async fn handle_get_timestamp_header() -> Response { - let timestamp_header = generate_timestamp_header(); - - let mut headers = HeaderMap::new(); - headers.insert( - CONTENT_TYPE, - CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8.parse().unwrap(), - ); - - (headers, timestamp_header).into_response() -} - pub async fn handle_get_tokens( State(state): State>>, headers: HeaderMap, @@ -93,7 +39,8 @@ pub async fn handle_get_tokens( return Err(StatusCode::UNAUTHORIZED); } - let tokens = state.lock().await.token_infos.clone(); + let state = state.lock().await; + let tokens = state.token_manager.tokens.clone(); let tokens_count = tokens.len(); Ok(Json(TokenInfoResponse { @@ -104,49 +51,6 @@ pub async fn handle_get_tokens( })) } -#[derive(Serialize)] -pub struct TokenInfoResponse { - pub status: ApiStatus, - #[serde(skip_serializing_if = "Option::is_none")] - pub tokens: Option>, - pub tokens_count: usize, - #[serde(skip_serializing_if = "Option::is_none")] - pub message: Option, -} - -pub async fn handle_reload_tokens( - State(state): State>>, - headers: HeaderMap, -) -> Result, StatusCode> { - // 验证 AUTH_TOKEN - let auth_header = headers - .get(AUTHORIZATION) - .and_then(|h| h.to_str().ok()) - .and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX)) - .ok_or(StatusCode::UNAUTHORIZED)?; - - if auth_header != AUTH_TOKEN.as_str() { - return Err(StatusCode::UNAUTHORIZED); - } - - // 重新加载 tokens - let tokens = load_tokens(); - let tokens_count = tokens.len(); - - // 更新应用状态 - { - let mut state = state.lock().await; - state.token_infos = tokens; - } - - Ok(Json(TokenInfoResponse { - status: ApiStatus::Success, - tokens: None, - tokens_count, - message: Some("Token list has been reloaded".to_string()), - })) -} - pub async fn handle_update_tokens( State(state): State>>, headers: HeaderMap, @@ -163,19 +67,50 @@ pub async fn handle_update_tokens( return Err(StatusCode::UNAUTHORIZED); } - let token_list_file = TOKEN_LIST_FILE.as_str(); + // 获取当前的 token_manager 以保留现有 token 的 profile 和 tags + let current_token_manager = { + let state = state.lock().await; + state.token_manager.clone() + }; - std::fs::write(&token_list_file, &request.tokens) + // 创建 token -> (profile, tags) 映射 + let token_info_map: HashMap, Option>)> = + current_token_manager + .tokens + .iter() + .map(|token| { + ( + token.token.clone(), + (token.profile.clone(), token.tags.clone()), + ) + }) + .collect(); + + // 从请求内容加载新的 tokens + let mut new_tokens = load_tokens_from_content(&request.tokens); + + // 为相同的 token 保留原有的 profile 和 tags + for token_info in &mut new_tokens { + if let Some((profile, tags)) = token_info_map.get(&token_info.token) { + token_info.profile = profile.clone(); + token_info.tags = tags.clone(); + } + } + + // 创建新的 TokenManager + let token_manager = TokenManager::new(new_tokens); + let tokens_count = token_manager.tokens.len(); + + // 保存到文件 + token_manager + .save_tokens() + .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - // 重新加载 tokens - let token_infos = load_tokens(); - let tokens_count = token_infos.len(); - // 更新应用状态 { let mut state = state.lock().await; - state.token_infos = token_infos; + state.token_manager = token_manager; } Ok(Json(TokenInfoResponse { @@ -189,7 +124,7 @@ pub async fn handle_update_tokens( pub async fn handle_add_tokens( State(state): State>>, headers: HeaderMap, - Json(request): Json>, + Json(request): Json, ) -> Result, (StatusCode, Json)> { // 验证 AUTH_TOKEN let auth_header = headers @@ -208,64 +143,65 @@ pub async fn handle_add_tokens( )); } - let token_list_file = TOKEN_LIST_FILE.as_str(); - - // 获取当前的 tokens 并创建新的 token_infos - let mut token_infos = { + // 获取当前的 token_manager + let mut token_manager = { let state = state.lock().await; - state.token_infos.clone() + state.token_manager.clone() }; // 创建现有token的集合 - let existing_tokens: std::collections::HashSet<_> = - token_infos.iter().map(|info| info.token.as_str()).collect(); - - // 预分配容量 - let mut new_tokens = Vec::with_capacity(request.len()); + let existing_tokens: std::collections::HashSet<_> = token_manager + .tokens + .iter() + .map(|info| info.token.as_str()) + .collect(); // 处理新的tokens - for token_info in request { + let mut new_tokens = Vec::with_capacity(request.tokens.len()); + for token_info in request.tokens { let parsed_token = parse_token(&token_info.token); if !existing_tokens.contains(parsed_token.as_str()) && validate_token(&parsed_token) { new_tokens.push(TokenInfo { token: parsed_token, - // 如果提供了checksum就使用提供的,否则生成新的 checksum: token_info .checksum .as_deref() .map(generate_checksum_with_repair) .unwrap_or_else(generate_checksum_with_default), profile: None, + tags: request.tags.clone(), }); } } // 如果有新tokens才进行后续操作 if !new_tokens.is_empty() { - // 预分配足够的容量 - token_infos.reserve(new_tokens.len()); - token_infos.extend(new_tokens); + // 添加新tokens + token_manager.tokens.extend(new_tokens); + let tokens_count = token_manager.tokens.len(); - // 写入文件 - write_tokens(&token_infos, token_list_file).map_err(|_| { + // 更新全局标签 + if let Some(ref tags) = request.tags { + token_manager.update_global_tags(tags); + } + + // 保存到文件 + token_manager.save_tokens().await.map_err(|_| { ( StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { status: ApiStatus::Error, code: None, - error: Some("Failed to update token list file".to_string()), - message: Some("无法更新token list文件".to_string()), + error: Some("Failed to save token list".to_string()), + message: Some("无法保存token list".to_string()), }), ) })?; - // 获取最终的tokens数量(在更新状态之前) - let tokens_count = token_infos.len(); - // 更新应用状态 { let mut state = state.lock().await; - state.token_infos = token_infos; + state.token_manager = token_manager; } Ok(Json(TokenInfoResponse { @@ -275,12 +211,13 @@ pub async fn handle_add_tokens( message: Some("New tokens have been added and reloaded".to_string()), })) } else { - // 如果没有新tokens,使用原始数量 - let tokens_count = token_infos.len(); + // 如果没有新tokens,返回当前状态 + let tokens = token_manager.tokens.clone(); + let tokens_count = tokens.len(); Ok(Json(TokenInfoResponse { status: ApiStatus::Success, - tokens: None, + tokens: Some(tokens), tokens_count, message: Some("No new tokens were added".to_string()), })) @@ -309,11 +246,11 @@ pub async fn handle_delete_tokens( )); } - let token_infos = state.lock().await.token_infos.clone(); - let original_count = token_infos.len(); // 提前存储原始长度 - - // 获取token_list文件路径 - let token_list_file = TOKEN_LIST_FILE.as_str(); + // 获取当前的 token_manager + let mut token_manager = { + let state = state.lock().await; + state.token_manager.clone() + }; // 创建要删除的tokens的HashSet,提高查找效率 let tokens_to_delete: std::collections::HashSet<_> = request.tokens.iter().collect(); @@ -324,7 +261,12 @@ pub async fn handle_delete_tokens( request .tokens .iter() - .filter(|token| !token_infos.iter().any(|info| &info.token == *token)) + .filter(|token| { + !token_manager + .tokens + .iter() + .any(|token_info| token_info.token == **token) + }) .cloned() .collect::>(), ) @@ -332,28 +274,26 @@ pub async fn handle_delete_tokens( None }; - // 预分配容量并过滤掉要删除的tokens - let estimated_capacity = original_count.saturating_sub(tokens_to_delete.len()); - let mut filtered_token_infos = Vec::with_capacity(estimated_capacity); + let original_count: usize = token_manager.tokens.len(); - // 一次性过滤tokens - for info in token_infos { - if !tokens_to_delete.contains(&info.token) { - filtered_token_infos.push(info); - } - } + // 从每个分组中删除指定的tokens + token_manager + .tokens + .retain(|token_info| !tokens_to_delete.contains(&token_info.token)); + + let new_count: usize = token_manager.tokens.len(); // 如果有tokens被删除才进行更新操作 - if filtered_token_infos.len() < original_count { - // 写入文件 - write_tokens(&filtered_token_infos, token_list_file).map_err(|_| { + if new_count < original_count { + // 保存到文件 + token_manager.save_tokens().await.map_err(|_| { ( StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { status: ApiStatus::Error, code: None, - error: Some("Failed to update token list file".to_string()), - message: Some("无法更新token list文件".to_string()), + error: Some("Failed to save token list".to_string()), + message: Some("无法保存token list".to_string()), }), ) })?; @@ -361,9 +301,10 @@ pub async fn handle_delete_tokens( // 如果需要的话计算 updated_tokens let updated_tokens = if request.expectation.needs_updated_tokens() { Some( - filtered_token_infos + token_manager + .tokens .iter() - .map(|info| info.token.clone()) + .map(|t| t.token.clone()) .collect(), ) } else { @@ -373,7 +314,7 @@ pub async fn handle_delete_tokens( // 更新状态 { let mut state = state.lock().await; - state.token_infos = filtered_token_infos; + state.token_manager = token_manager; } Ok(Json(TokensDeleteResponse { @@ -387,9 +328,10 @@ pub async fn handle_delete_tokens( status: ApiStatus::Success, updated_tokens: if request.expectation.needs_updated_tokens() { Some( - filtered_token_infos + token_manager + .tokens .iter() - .map(|info| info.token.clone()) + .map(|t| t.token.clone()) .collect(), ) } else { @@ -400,82 +342,62 @@ pub async fn handle_delete_tokens( } } -pub async fn handle_tokens_page() -> impl IntoResponse { - match AppConfig::get_page_content(ROUTE_TOKENS_PATH).unwrap_or_default() { - PageContent::Default => Response::builder() - .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(include_str!("../../../static/tokens.min.html").to_string()) - .unwrap(), - PageContent::Text(content) => Response::builder() - .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8) - .body(content.clone()) - .unwrap(), - PageContent::Html(content) => Response::builder() - .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8) - .body(content.clone()) - .unwrap(), +pub async fn handle_update_token_tags( + State(state): State>>, + headers: HeaderMap, + Json(request): Json, +) -> Result, (StatusCode, Json)> { + // 验证 AUTH_TOKEN + let auth_header = headers + .get(AUTHORIZATION) + .and_then(|h| h.to_str().ok()) + .and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX)) + .ok_or(( + StatusCode::UNAUTHORIZED, + Json(ChatError::Unauthorized.to_json()), + ))?; + + if auth_header != AUTH_TOKEN.as_str() { + return Err(( + StatusCode::UNAUTHORIZED, + Json(ChatError::Unauthorized.to_json()), + )); } -} -#[derive(Deserialize)] -pub struct TokenRequest { - pub token: Option, -} - -#[derive(Serialize)] -pub struct BasicCalibrationResponse { - pub status: ApiStatus, - pub message: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub user_id: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub create_at: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub checksum_time: Option, -} - -pub async fn handle_basic_calibration( - Json(request): Json, -) -> Json { - // 从请求头中获取并验证 auth token - let auth_token = match request.token { - Some(token) => token, - None => { - return Json(BasicCalibrationResponse { - status: ApiStatus::Error, - message: Some("未提供授权令牌".to_string()), - user_id: None, - create_at: None, - checksum_time: None, - }) + // 获取并更新 token_manager + { + let mut state = state.lock().await; + if let Err(e) = state + .token_manager + .update_tokens_tags(request.tokens, request.tags) + { + return Err(( + StatusCode::BAD_REQUEST, + Json(ErrorResponse { + status: ApiStatus::Error, + code: None, + error: Some(e.to_string()), + message: Some("更新标签失败".to_string()), + }), + )); } - }; - // 校验 token 和 checksum - let (token, checksum) = match validate_token_and_checksum(&auth_token) { - Some(parts) => parts, - None => { - return Json(BasicCalibrationResponse { - status: ApiStatus::Error, - message: Some("无效令牌或无效校验和".to_string()), - user_id: None, - create_at: None, - checksum_time: None, - }) + // 保存更改 + if (state.token_manager.save_tokens().await).is_err() { + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ErrorResponse { + status: ApiStatus::Error, + code: None, + error: Some("Failed to save token tags".to_string()), + message: Some("无法保存标签信息".to_string()), + }), + )); } - }; + } - // 提取用户ID和创建时间 - let user_id = extract_user_id(&token); - let create_at = extract_time(&token).map(|dt| dt.to_string()); - let checksum_time = extract_time_ks(&checksum[..8]); - - // 返回校验结果 - Json(BasicCalibrationResponse { + Ok(Json(TokenTagsResponse { status: ApiStatus::Success, - message: Some("校验成功".to_string()), - user_id, - create_at, - checksum_time, - }) + message: Some("标签更新成功".to_string()), + })) } diff --git a/src/chat/service.rs b/src/chat/service.rs index dffc972..d04c15a 100644 --- a/src/chat/service.rs +++ b/src/chat/service.rs @@ -4,7 +4,10 @@ use crate::{ AUTHORIZATION_BEARER_PREFIX, FINISH_REASON_STOP, OBJECT_CHAT_COMPLETION, OBJECT_CHAT_COMPLETION_CHUNK, }, - lazy::{AUTH_TOKEN, KEY_PREFIX, KEY_PREFIX_LEN, REQUEST_LOGS_LIMIT, SERVICE_TIMEOUT}, + lazy::{ + AUTH_TOKEN, CURSOR_API2_CHAT_URL, CURSOR_API2_CHAT_WEB_URL, IS_UNLIMITED_REQUEST_LOGS, + KEY_PREFIX, KEY_PREFIX_LEN, REQUEST_LOGS_LIMIT, SERVICE_TIMEOUT, + }, model::{ AppConfig, AppState, ChatRequest, LogStatus, RequestLog, TimingInfo, TokenInfo, UsageCheck, @@ -12,7 +15,7 @@ use crate::{ }, chat::{ config::KeyConfig, - constant::{AVAILABLE_MODELS, USAGE_CHECK_MODELS}, + constant::{Models, USAGE_CHECK_MODELS}, error::StreamError, model::{ ChatResponse, Choice, Delta, Message, MessageContent, ModelsResponse, Role, Usage, @@ -21,22 +24,22 @@ use crate::{ }, common::{ client::build_client, - model::{error::ChatError, userinfo::MembershipType, ApiStatus, ErrorResponse}, + model::{ApiStatus, ErrorResponse, error::ChatError, userinfo::MembershipType}, utils::{ - format_time_ms, from_base64, get_token_profile, tokeninfo_to_token, - validate_token_and_checksum, TrimNewlines as _, + TrimNewlines as _, format_time_ms, from_base64, get_available_models, + get_token_profile, tokeninfo_to_token, validate_token_and_checksum, }, }, }; use axum::{ + Json, body::Body, extract::State, http::{ - header::{AUTHORIZATION, CONTENT_TYPE}, HeaderMap, StatusCode, + header::{AUTHORIZATION, CONTENT_TYPE}, }, response::Response, - Json, }; use bytes::Bytes; use futures::StreamExt; @@ -44,17 +47,129 @@ use prost::Message as _; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{ convert::Infallible, - sync::{atomic::AtomicBool, Arc}, + sync::{Arc, atomic::AtomicBool}, }; use tokio::sync::Mutex; use uuid::Uuid; +use super::{constant::LONG_CONTEXT_MODELS, model::Model}; + +// 辅助函数:提取认证token +fn extract_auth_token(headers: &HeaderMap) -> Result<&str, (StatusCode, Json)> { + headers + .get(AUTHORIZATION) + .and_then(|h| h.to_str().ok()) + .and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX)) + .ok_or(( + StatusCode::UNAUTHORIZED, + Json(ChatError::Unauthorized.to_json()), + )) +} + +// 辅助函数:解析token信息 +async fn resolve_token_info( + auth_header: &str, + state: &Arc>, +) -> Result<(String, String), (StatusCode, Json)> { + match auth_header { + // 管理员Token处理 + token if is_admin_token(token) => resolve_admin_token(state).await, + + // 动态密钥处理 + token if is_dynamic_key(token) => resolve_dynamic_key(token), + + // 普通用户Token处理 + token => validate_token_and_checksum(token).ok_or(( + StatusCode::UNAUTHORIZED, + Json(ChatError::Unauthorized.to_json()), + )), + } +} + +// 辅助函数:检查是否为管理员token +fn is_admin_token(token: &str) -> bool { + token == AUTH_TOKEN.as_str() + || (AppConfig::is_share() && token == AppConfig::get_share_token().as_str()) +} + +// 辅助函数:检查是否为动态密钥 +fn is_dynamic_key(token: &str) -> bool { + AppConfig::get_dynamic_key() && token.starts_with(&*KEY_PREFIX) +} + +// 辅助函数:处理管理员token +async fn resolve_admin_token( + state: &Arc>, +) -> Result<(String, String), (StatusCode, Json)> { + static CURRENT_KEY_INDEX: AtomicUsize = AtomicUsize::new(0); + + let state_guard = state.lock().await; + let token_infos = &state_guard.token_manager.tokens; + + if token_infos.is_empty() { + return Err(( + StatusCode::SERVICE_UNAVAILABLE, + Json(ChatError::NoTokens.to_json()), + )); + } + + let index = CURRENT_KEY_INDEX.fetch_add(1, Ordering::SeqCst) % token_infos.len(); + let token_info = &token_infos[index]; + + Ok((token_info.token.clone(), token_info.checksum.clone())) +} + +// 辅助函数:处理动态密钥 +fn resolve_dynamic_key(token: &str) -> Result<(String, String), (StatusCode, Json)> { + from_base64(&token[*KEY_PREFIX_LEN..]) + .and_then(|decoded_bytes| KeyConfig::decode(&decoded_bytes[..]).ok()) + .and_then(|key_config| key_config.auth_token) + .and_then(|token_info| tokeninfo_to_token(&token_info)) + .ok_or(( + StatusCode::UNAUTHORIZED, + Json(ChatError::Unauthorized.to_json()), + )) +} + // 模型列表处理 -pub async fn handle_models() -> Json { - Json(ModelsResponse { - object: "list", - data: &AVAILABLE_MODELS, - }) +pub async fn handle_models( + State(state): State>>, + headers: HeaderMap, +) -> Result, (StatusCode, Json)> { + // 如果没有认证头,返回默认可用模型 + if headers.get(AUTHORIZATION).is_none() { + return Ok(Json(ModelsResponse::with_default_models())); + } + + // 提取和验证认证token + let auth_token = extract_auth_token(&headers)?; + let (token, checksum) = resolve_token_info(auth_token, &state).await?; + + // 获取可用模型列表 + let models = get_available_models(&token, &checksum).await.ok_or(( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ErrorResponse { + status: ApiStatus::Failure, + code: Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16()), + error: Some("Failed to fetch available models".to_string()), + message: Some("Unable to get available models".to_string()), + }), + ))?; + + // 更新模型列表 + if let Err(e) = Models::update(models) { + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ErrorResponse { + status: ApiStatus::Failure, + code: Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16()), + error: Some("Failed to update models".to_string()), + message: Some(e.to_string()), + }), + )); + } + + Ok(Json(ModelsResponse::new(Models::to_arc()))) } // 聊天处理函数的签名 @@ -73,15 +188,15 @@ pub async fn handle_chat( }; // 验证模型是否支持并获取模型信息 - let model = AVAILABLE_MODELS.iter().find(|m| m.id == model_name); - let model_supported = model.is_some(); - - if !(model_supported || allow_claude && request.model.starts_with("claude")) { - return Err(( - StatusCode::BAD_REQUEST, - Json(ChatError::ModelNotSupported(request.model).to_json()), - )); - } + let model = + if Models::exists(&model_name) || (allow_claude && request.model.starts_with("claude")) { + Some(&model_name) + } else { + return Err(( + StatusCode::BAD_REQUEST, + Json(ChatError::ModelNotSupported(request.model).to_json()), + )); + }; let request_time = chrono::Local::now(); @@ -114,7 +229,7 @@ pub async fn handle_chat( { static CURRENT_KEY_INDEX: AtomicUsize = AtomicUsize::new(0); let state_guard = state.lock().await; - let token_infos = &state_guard.token_infos; + let token_infos = &state_guard.token_manager.tokens; // 检查是否存在可用的token if token_infos.is_empty() { @@ -159,56 +274,85 @@ pub async fn handle_chat( { let state_clone = state.clone(); let mut state = state.lock().await; - state.total_requests += 1; - state.active_requests += 1; + state.request_manager.total_requests += 1; + state.request_manager.active_requests += 1; - // 查找最新的相同token的日志,检查使用情况 - let need_profile_check = state - .request_logs - .iter() - .rev() - .find(|log| log.token_info.token == auth_token && log.token_info.profile.is_some()) - .and_then(|log| log.token_info.profile.as_ref()) - .map(|profile| { - if profile.stripe.membership_type != MembershipType::Free { - return false; + let mut found_count: u32 = 0; + let mut no_prompt_count: u32 = 0; + let mut need_profile_check = false; + + for log in state.request_manager.request_logs.iter().rev() { + if log.token_info.token == auth_token { + if !LONG_CONTEXT_MODELS.contains(&log.model.as_str()) { + found_count += 1; } - let is_premium = USAGE_CHECK_MODELS.contains(&model_name.as_str()); - let standard = &profile.usage.standard; - let premium = &profile.usage.premium; - - if is_premium { - premium - .max_requests - .map_or(false, |max| premium.num_requests >= max) - } else { - standard - .max_requests - .map_or(false, |max| standard.num_requests >= max) + if log.prompt.is_none() { + no_prompt_count += 1; } - }) - .unwrap_or(false); - // 如果达到限制,直接返回未授权错误 + if found_count == 1 && log.token_info.profile.is_some() { + if let Some(profile) = &log.token_info.profile { + if profile.stripe.membership_type == MembershipType::Free { + let is_premium = USAGE_CHECK_MODELS.contains(&model_name.as_str()); + need_profile_check = + if is_premium { + profile.usage.premium.max_requests.is_some_and(|max| { + profile.usage.premium.num_requests >= max + }) + } else { + profile.usage.standard.max_requests.is_some_and(|max| { + profile.usage.standard.num_requests >= max + }) + }; + } + } + } + + if found_count == 2 { + break; + } + } + } + + if found_count == 2 && no_prompt_count == 2 { + state.request_manager.active_requests -= 1; + state.request_manager.error_requests += 1; + return Err(( + StatusCode::TOO_MANY_REQUESTS, + Json(ErrorResponse { + status: ApiStatus::Error, + code: Some(429), + error: Some("rate_limit_exceeded".to_string()), + message: Some("Too many requests without prompt".to_string()), + }), + )); + } + + // 处理检查结果 if need_profile_check { - state.active_requests -= 1; - state.error_requests += 1; + state.request_manager.active_requests -= 1; + state.request_manager.error_requests += 1; return Err(( StatusCode::UNAUTHORIZED, Json(ChatError::Unauthorized.to_json()), )); } - let next_id = state.request_logs.last().map_or(1, |log| log.id + 1); + let next_id = state + .request_manager + .request_logs + .last() + .map_or(1, |log| log.id + 1); current_id = next_id; // 如果需要获取用户使用情况,创建后台任务获取profile if model .map(|m| { - m.is_usage_check(UsageCheck::from_proto( - current_config.usage_check_models.as_ref(), - )) + Model::is_usage_check( + m, + UsageCheck::from_proto(current_config.usage_check_models.as_ref()), + ) }) .unwrap_or(false) { @@ -222,30 +366,35 @@ pub async fn handle_chat( // 先找到所有需要更新的位置的索引 let token_info_idx = state - .token_infos + .token_manager + .tokens .iter() .position(|info| info.token == auth_token_clone); - let log_idx = state.request_logs.iter().rposition(|log| log.id == log_id); + let log_idx = state + .request_manager + .request_logs + .iter() + .rposition(|log| log.id == log_id); // 根据索引更新 match (token_info_idx, log_idx) { (Some(t_idx), Some(l_idx)) => { - state.token_infos[t_idx].profile = profile.clone(); - state.request_logs[l_idx].token_info.profile = profile; + state.token_manager.tokens[t_idx].profile = profile.clone(); + state.request_manager.request_logs[l_idx].token_info.profile = profile; } (Some(t_idx), None) => { - state.token_infos[t_idx].profile = profile; + state.token_manager.tokens[t_idx].profile = profile; } (None, Some(l_idx)) => { - state.request_logs[l_idx].token_info.profile = profile; + state.request_manager.request_logs[l_idx].token_info.profile = profile; } (None, None) => {} } }); } - state.request_logs.push(RequestLog { + state.request_manager.request_logs.push(RequestLog { id: next_id, timestamp: request_time, model: request.model.clone(), @@ -253,6 +402,7 @@ pub async fn handle_chat( token: auth_token.clone(), checksum: checksum.clone(), profile: None, + tags: None, }, prompt: None, timing: TimingInfo { @@ -264,8 +414,10 @@ pub async fn handle_chat( error: None, }); - if state.request_logs.len() > *REQUEST_LOGS_LIMIT { - state.request_logs.remove(0); + if !*IS_UNLIMITED_REQUEST_LOGS + && state.request_manager.request_logs.len() > *REQUEST_LOGS_LIMIT + { + state.request_manager.request_logs.remove(0); } } @@ -283,6 +435,7 @@ pub async fn handle_chat( Err(e) => { let mut state = state.lock().await; if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() @@ -291,8 +444,8 @@ pub async fn handle_chat( log.status = LogStatus::Failed; log.error = Some(e.to_string()); } - state.active_requests -= 1; - state.error_requests += 1; + state.request_manager.active_requests -= 1; + state.request_manager.error_requests += 1; return Err(( StatusCode::INTERNAL_SERVER_ERROR, Json( @@ -303,7 +456,16 @@ pub async fn handle_chat( }; // 构建请求客户端 - let client = build_client(&auth_token, &checksum, is_search); + let client = build_client( + &auth_token, + &checksum, + if is_search { + &CURSOR_API2_CHAT_WEB_URL + } else { + &CURSOR_API2_CHAT_URL + }, + true, + ); // 添加超时设置 let response = tokio::time::timeout( std::time::Duration::from_secs(*SERVICE_TIMEOUT), @@ -319,6 +481,7 @@ pub async fn handle_chat( { let mut state = state.lock().await; if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() @@ -334,6 +497,7 @@ pub async fn handle_chat( { let mut state = state.lock().await; if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() @@ -342,8 +506,8 @@ pub async fn handle_chat( log.status = LogStatus::Failed; log.error = Some(e.to_string()); } - state.active_requests -= 1; - state.error_requests += 1; + state.request_manager.active_requests -= 1; + state.request_manager.error_requests += 1; } return Err(( StatusCode::INTERNAL_SERVER_ERROR, @@ -356,6 +520,7 @@ pub async fn handle_chat( { let mut state = state.lock().await; if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() @@ -364,8 +529,8 @@ pub async fn handle_chat( log.status = LogStatus::Failed; log.error = Some("Request timeout".to_string()); } - state.active_requests -= 1; - state.error_requests += 1; + state.request_manager.active_requests -= 1; + state.request_manager.error_requests += 1; } return Err(( StatusCode::GATEWAY_TIMEOUT, @@ -377,7 +542,7 @@ pub async fn handle_chat( // 释放活动请求计数 { let mut state = state.lock().await; - state.active_requests -= 1; + state.request_manager.active_requests -= 1; } let convert_web_ref = current_config.include_web_references(); @@ -460,6 +625,7 @@ pub async fn handle_chat( { let mut state = ctx.state.lock().await; if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() @@ -494,6 +660,7 @@ pub async fn handle_chat( StreamMessage::Debug(debug_prompt) => { if let Ok(mut state) = ctx.state.try_lock() { if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() @@ -518,11 +685,12 @@ pub async fn handle_chat( if let Err(StreamError::ChatError(error)) = decoder.lock().await.decode(&chunk, convert_web_ref) { - let error_response = error.to_error_response(); + let error_response = error.into_error_response(); // 更新请求日志为失败 { let mut state = state.lock().await; if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() @@ -532,12 +700,12 @@ pub async fn handle_chat( log.error = Some(error_response.native_code()); log.timing.total = format_time_ms(start_time.elapsed().as_secs_f64()); - state.error_requests += 1; + state.request_manager.error_requests += 1; } } return Err(( error_response.status_code(), - Json(error_response.to_common()), + Json(error_response.into_common()), )); } } @@ -553,6 +721,7 @@ pub async fn handle_chat( { let mut state = state.lock().await; if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() @@ -560,7 +729,7 @@ pub async fn handle_chat( { log.status = LogStatus::Failed; log.error = Some("Empty stream response".to_string()); - state.error_requests += 1; + state.request_manager.error_requests += 1; } } return Err(( @@ -667,6 +836,7 @@ pub async fn handle_chat( StreamMessage::Debug(debug_prompt) => { if let Ok(mut state) = state.try_lock() { if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() @@ -681,10 +851,10 @@ pub async fn handle_chat( } } Err(StreamError::ChatError(error)) => { - let error_response = error.to_error_response(); + let error_response = error.into_error_response(); return Err(( error_response.status_code(), - Json(error_response.to_common()), + Json(error_response.into_common()), )); } Err(e) => { @@ -705,6 +875,7 @@ pub async fn handle_chat( { let mut state = state.lock().await; if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() @@ -712,7 +883,7 @@ pub async fn handle_chat( { log.status = LogStatus::Failed; log.error = Some("Empty response received".to_string()); - state.error_requests += 1; + state.request_manager.error_requests += 1; } } return Err(( @@ -747,6 +918,7 @@ pub async fn handle_chat( let total_time = format_time_ms(start_time.elapsed().as_secs_f64()); let mut state = state.lock().await; if let Some(log) = state + .request_manager .request_logs .iter_mut() .rev() diff --git a/src/chat/stream/decoder.rs b/src/chat/stream/decoder.rs index f2926d4..da46e0a 100644 --- a/src/chat/stream/decoder.rs +++ b/src/chat/stream/decoder.rs @@ -1,10 +1,10 @@ use crate::chat::{ - aiserver::v1::StreamChatResponse, + aiserver::v1::{StreamChatResponse, WebReference}, error::{ChatError, StreamError}, }; use flate2::read::GzDecoder; use prost::Message; -use std::{collections::BTreeMap, io::Read}; +use std::io::Read; // 解压gzip数据 fn decompress_gzip(data: &[u8]) -> Option> { @@ -24,17 +24,23 @@ pub trait ToMarkdown { fn to_markdown(&self) -> String; } -impl ToMarkdown for BTreeMap { +impl ToMarkdown for Vec { fn to_markdown(&self) -> String { if self.is_empty() { return String::new(); } let mut result = String::from("WebReferences:\n"); - for (i, (url, title)) in self.iter().enumerate() { - result.push_str(&format!("{}. [{}]({})\n", i + 1, title, url)); + for (i, web_ref) in self.iter().enumerate() { + result.push_str(&format!( + "{}. [{}]({})<{}>\n", + i + 1, + web_ref.title, + web_ref.url, + web_ref.chunk + )); } - result.push_str("\n"); + result.push('\n'); result } } @@ -44,7 +50,7 @@ pub enum StreamMessage { // 调试 Debug(String), // 网络引用 - WebReference(BTreeMap), + WebReference(Vec), // 内容开始标志 ContentStart, // 消息内容 @@ -98,11 +104,15 @@ impl StreamDecoder { self.first_result_ready } - pub fn decode(&mut self, data: &[u8], convert_web_ref: bool) -> Result, StreamError> { + pub fn decode( + &mut self, + data: &[u8], + convert_web_ref: bool, + ) -> Result, StreamError> { self.buffer.extend_from_slice(data); if self.buffer.len() < 5 { - if self.buffer.len() == 0 { + if self.buffer.is_empty() { return Err(StreamError::EmptyStream); } crate::debug_println!("数据长度小于5字节,当前数据: {}", hex::encode(&self.buffer)); @@ -133,15 +143,12 @@ impl StreamDecoder { let msg_data = &self.buffer[offset + 5..offset + 5 + msg_len]; - match self.process_message(msg_type, msg_data)? { - Some(msg) => { - if convert_web_ref { - messages.push(msg.convert_web_ref_to_content()); - } else { - messages.push(msg); - } + if let Some(msg) = self.process_message(msg_type, msg_data)? { + if convert_web_ref { + messages.push(msg.convert_web_ref_to_content()); + } else { + messages.push(msg); } - _ => {} } offset += 5 + msg_len; @@ -157,7 +164,8 @@ impl StreamDecoder { } } if !self.first_result_ready { - self.first_result_ready = self.first_result.is_some() && self.buffer.is_empty() && !self.first_result_taken; + self.first_result_ready = + self.first_result.is_some() && self.buffer.is_empty() && !self.first_result_taken; } Ok(messages) } @@ -182,17 +190,13 @@ impl StreamDecoder { fn handle_text_message(&self, msg_data: &[u8]) -> Result, StreamError> { if let Ok(response) = StreamChatResponse::decode(msg_data) { - // crate::debug_println!("[text] StreamChatResponse [hex: {}]: {:?}", hex::encode(msg_data), response); + // println!("[text] StreamChatResponse [hex: {}]: {:?}", hex::encode(msg_data), response); if !response.text.is_empty() { Ok(Some(StreamMessage::Content(response.text))) } else if let Some(filled_prompt) = response.filled_prompt { Ok(Some(StreamMessage::Debug(filled_prompt))) } else if let Some(web_citation) = response.web_citation { - let mut refs = BTreeMap::new(); - for reference in web_citation.references { - refs.insert(reference.url, reference.title); - } - Ok(Some(StreamMessage::WebReference(refs))) + Ok(Some(StreamMessage::WebReference(web_citation.references))) } else { Ok(None) } @@ -204,17 +208,13 @@ impl StreamDecoder { fn handle_gzip_message(&self, msg_data: &[u8]) -> Result, StreamError> { if let Some(text) = decompress_gzip(msg_data) { if let Ok(response) = StreamChatResponse::decode(&text[..]) { - // crate::debug_println!("[gzip] StreamChatResponse [hex: {}]: {:?}", hex::encode(msg_data), response); + // println!("[gzip] StreamChatResponse [hex: {}]: {:?}", hex::encode(msg_data), response); if !response.text.is_empty() { Ok(Some(StreamMessage::Content(response.text))) } else if let Some(filled_prompt) = response.filled_prompt { Ok(Some(StreamMessage::Debug(filled_prompt))) } else if let Some(web_citation) = response.web_citation { - let mut refs = BTreeMap::new(); - for reference in web_citation.references { - refs.insert(reference.url, reference.title); - } - Ok(Some(StreamMessage::WebReference(refs))) + Ok(Some(StreamMessage::WebReference(web_citation.references))) } else { Ok(None) } @@ -231,7 +231,7 @@ impl StreamDecoder { return Ok(Some(StreamMessage::StreamEnd)); } if let Ok(text) = String::from_utf8(msg_data.to_vec()) { - // println!("JSON消息: {}", text); + // println!("[text] JSON消息 [hex: {}]: {}", hex::encode(msg_data), text); if let Ok(error) = serde_json::from_str::(&text) { return Err(StreamError::ChatError(error)); } @@ -248,7 +248,7 @@ impl StreamDecoder { return Ok(Some(StreamMessage::StreamEnd)); } if let Ok(text) = String::from_utf8(text) { - // println!("JSON消息: {}", text); + // println!("[gzip] JSON消息 [hex: {}]: {}", hex::encode(msg_data), text); if let Ok(error) = serde_json::from_str::(&text) { return Err(StreamError::ChatError(error)); } @@ -293,8 +293,11 @@ mod tests { } StreamMessage::WebReference(refs) => { println!("网页引用:"); - for (i, (url, title)) in refs.iter().enumerate() { - println!("{}. {} - {}", i, url, title); + for (i, web_ref) in refs.iter().enumerate() { + println!( + "{}. {} - {} - {}", + i, web_ref.url, web_ref.title, web_ref.chunk + ); } } StreamMessage::Debug(prompt) => { @@ -375,8 +378,11 @@ mod tests { } StreamMessage::WebReference(refs) => { println!("网页引用 [hex: {}]:", hex_str); - for (i, (url, title)) in refs.iter().enumerate() { - println!("{}. {} - {}", i, url, title); + for (i, web_ref) in refs.iter().enumerate() { + println!( + "{}. {} - {} - {}", + i, web_ref.url, web_ref.title, web_ref.chunk + ); } } StreamMessage::Debug(prompt) => { diff --git a/src/common.rs b/src/common.rs index a1c74da..06c7fb6 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,3 +1,3 @@ +pub mod client; pub mod model; pub mod utils; -pub mod client; diff --git a/src/common/client.rs b/src/common/client.rs index 2028885..b05f36a 100644 --- a/src/common/client.rs +++ b/src/common/client.rs @@ -1,17 +1,21 @@ use super::utils::generate_hash; -use crate::{app::{ - constant::{ - CONTENT_TYPE_CONNECT_PROTO, CURSOR_API2_HOST, CURSOR_HOST, CURSOR_SETTINGS_URL, - HEADER_NAME_GHOST_MODE, TRUE, +use crate::{ + AppConfig, + app::{ + constant::{ + CONTENT_TYPE_CONNECT_PROTO, CONTENT_TYPE_PROTO, CURSOR_API2_HOST, CURSOR_HOST, + CURSOR_SETTINGS_URL, HEADER_NAME_GHOST_MODE, TRUE, + }, + lazy::{ + CURSOR_API2_STRIPE_URL, CURSOR_USAGE_API_URL, CURSOR_USER_API_URL, REVERSE_PROXY_HOST, + USE_REVERSE_PROXY, + }, }, - lazy::{ - CURSOR_API2_CHAT_URL, CURSOR_API2_CHAT_WEB_URL, CURSOR_API2_STRIPE_URL, CURSOR_USAGE_API_URL, CURSOR_USER_API_URL, REVERSE_PROXY_HOST, USE_REVERSE_PROXY - }, -}, AppConfig}; +}; use reqwest::header::{ - ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, CACHE_CONTROL, CONNECTION, CONTENT_TYPE, COOKIE, - DNT, HOST, ORIGIN, PRAGMA, REFERER, TE, TRANSFER_ENCODING, USER_AGENT, - }; + ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, CACHE_CONTROL, CONNECTION, CONTENT_TYPE, COOKIE, DNT, + HOST, ORIGIN, PRAGMA, REFERER, TE, TRANSFER_ENCODING, USER_AGENT, +}; use reqwest::{Client, RequestBuilder}; use std::sync::LazyLock; use uuid::Uuid; @@ -35,7 +39,10 @@ def_const!(VALUE_LANGUAGE, "zh-CN"); def_const!(EMPTY, "empty"); def_const!(CORS, "cors"); def_const!(NO_CACHE, "no-cache"); -def_const!(UA_WIN, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"); +def_const!( + UA_WIN, + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" +); def_const!(SAME_ORIGIN, "same-origin"); def_const!(KEEP_ALIVE, "keep-alive"); def_const!(TRAILERS, "trailers"); @@ -66,13 +73,13 @@ pub fn rebuild_http_client() { /// # 返回 /// /// * `reqwest::RequestBuilder` - 配置好的请求构建器 -pub fn build_client(auth_token: &str, checksum: &str, is_search: bool) -> RequestBuilder { +pub fn build_client( + auth_token: &str, + checksum: &str, + url: &str, + is_stream: bool, +) -> RequestBuilder { let trace_id = Uuid::new_v4().to_string(); - let url = if is_search { - &*CURSOR_API2_CHAT_WEB_URL - } else { - &*CURSOR_API2_CHAT_URL - }; let client = if *USE_REVERSE_PROXY { HTTP_CLIENT @@ -81,14 +88,18 @@ pub fn build_client(auth_token: &str, checksum: &str, is_search: bool) -> Reques .header(HOST, &*REVERSE_PROXY_HOST) .header(PROXY_HOST, CURSOR_API2_HOST) } else { - HTTP_CLIENT - .read() - .post(url) - .header(HOST, CURSOR_API2_HOST) + HTTP_CLIENT.read().post(url).header(HOST, CURSOR_API2_HOST) }; client - .header(CONTENT_TYPE, CONTENT_TYPE_CONNECT_PROTO) + .header( + CONTENT_TYPE, + if is_stream { + CONTENT_TYPE_CONNECT_PROTO + } else { + CONTENT_TYPE_PROTO + }, + ) .bearer_auth(auth_token) .header("connect-accept-encoding", ENCODINGS) .header("connect-protocol-version", ONE) diff --git a/src/common/model.rs b/src/common/model.rs index 2662a28..d4eab61 100644 --- a/src/common/model.rs +++ b/src/common/model.rs @@ -1,6 +1,6 @@ +pub mod config; pub mod error; pub mod health; -pub mod config; pub mod token; pub mod userinfo; @@ -16,8 +16,8 @@ pub enum ApiStatus { Success, #[serde(rename = "error")] Error, - #[serde(rename = "failed")] - Failed, + #[serde(rename = "failure")] + Failure, } // #[derive(Serialize)] diff --git a/src/common/model/config.rs b/src/common/model/config.rs index 2112f35..e1b0f34 100644 --- a/src/common/model/config.rs +++ b/src/common/model/config.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::app::model::{PageContent, UsageCheck, VisionAbility, Proxies}; +use crate::app::model::{PageContent, Proxies, UsageCheck, VisionAbility}; #[derive(Serialize)] pub struct ConfigData { diff --git a/src/common/model/error.rs b/src/common/model/error.rs index 79430db..32c6648 100644 --- a/src/common/model/error.rs +++ b/src/common/model/error.rs @@ -25,10 +25,10 @@ impl ChatError { }; ErrorResponse { - status: super::ApiStatus::Error, - code: None, - error: Some(error.to_string()), - message: Some(message), + status: super::ApiStatus::Error, + code: None, + error: Some(error.to_string()), + message: Some(message), } } } diff --git a/src/common/model/health.rs b/src/common/model/health.rs index f74fe09..b4bbf39 100644 --- a/src/common/model/health.rs +++ b/src/common/model/health.rs @@ -9,7 +9,7 @@ pub struct HealthCheckResponse { pub uptime: i64, #[serde(skip_serializing_if = "Option::is_none")] pub stats: Option, - pub models: Vec<&'static str>, + pub models: Vec, pub endpoints: Vec<&'static str>, } diff --git a/src/common/model/userinfo.rs b/src/common/model/userinfo.rs index 5a1466b..ed6ecdd 100644 --- a/src/common/model/userinfo.rs +++ b/src/common/model/userinfo.rs @@ -1,11 +1,11 @@ use chrono::{DateTime, Local}; -use serde::{Deserialize, Serialize}; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; +use serde::{Deserialize, Serialize}; #[derive(Serialize)] #[serde(untagged)] pub enum GetUserInfo { - Usage(TokenProfile), + Usage(Box), Error { error: String }, } @@ -51,7 +51,7 @@ pub struct ModelUsage { default, skip_serializing_if = "Option::is_none" )] - pub requests_total: Option, + pub total_requests: Option, #[serde(rename(deserialize = "numTokens", serialize = "tokens"))] pub num_tokens: u32, #[serde( @@ -74,6 +74,8 @@ pub struct UsageProfile { pub standard: ModelUsage, #[serde(rename(deserialize = "gpt-4-32k"))] pub unknown: ModelUsage, + #[serde(rename(deserialize = "startOfMonth"))] + pub start_of_month: DateTime, } #[derive(Deserialize, Serialize, Clone, Archive, RkyvDeserialize, RkyvSerialize)] diff --git a/src/common/utils.rs b/src/common/utils.rs index 1adc30d..2e45c02 100644 --- a/src/common/utils.rs +++ b/src/common/utils.rs @@ -1,15 +1,28 @@ mod checksum; -use ::base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use ::base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; pub use checksum::*; mod token; +use prost::Message as _; pub use token::*; mod base64; pub use base64::*; -use super::model::{token::TokenPayload, userinfo::{StripeProfile, TokenProfile, UsageProfile, UserProfile}}; -use crate::app::{ - constant::{COMMA, FALSE, TRUE}, - lazy::{TOKEN_DELIMITER, USE_COMMA_DELIMITER}, +use super::model::{ + token::TokenPayload, + userinfo::{StripeProfile, TokenProfile, UsageProfile, UserProfile}, +}; +use crate::{ + app::{ + constant::{COMMA, FALSE, TRUE}, + lazy::{CURSOR_API2_CHAT_MODELS_URL, TOKEN_DELIMITER, USE_COMMA_DELIMITER}, + }, + chat::{ + aiserver::v1::{AvailableModelsRequest, AvailableModelsResponse}, + constant::{ + ANTHROPIC, CREATED, CURSOR, DEEPSEEK, GOOGLE, MODEL_OBJECT, OPENAI, UNKNOWN, XAI, + }, + model::Model, + }, }; pub fn parse_bool_from_env(key: &str, default: bool) -> bool { @@ -124,6 +137,59 @@ pub async fn get_user_profile(auth_token: &str) -> Option { Some(user_profile) } +pub async fn get_available_models(auth_token: &str, checksum: &str) -> Option> { + let client = + super::client::build_client(auth_token, checksum, &CURSOR_API2_CHAT_MODELS_URL, false); + let request = AvailableModelsRequest { + is_nightly: true, + include_long_context_models: true, + }; + let response = client + .body(encode_message(&request, false).unwrap()) + .send() + .await + .ok()? + .bytes() + .await + .ok()?; + let available_models = AvailableModelsResponse::decode(response.as_ref()).ok()?; + Some( + available_models + .models + .into_iter() + .map(|model| Model { + id: model.name.clone(), + created: CREATED, + object: MODEL_OBJECT, + owned_by: { + let mut chars = model.name.chars(); + match chars.next() { + Some('g') => match chars.next() { + Some('p') => OPENAI, // g + p → "gp" (gpt) + Some('e') => GOOGLE, // g + e → "ge" (gemini) + Some('r') => XAI, // g + r → "ge" (grok) + _ => UNKNOWN, + }, + Some('o') => match chars.next() { + // o 开头需要二次判断 + Some('1') | Some('3') => OPENAI, // o1/o3 系列 + _ => UNKNOWN, + }, + Some('c') => match chars.next() { + Some('l') => ANTHROPIC, // c + l → "cl" (claude) + Some('u') => CURSOR, // c + u → "cu" (cursor) + _ => UNKNOWN, + }, + Some('d') if chars.next() == Some('e') => DEEPSEEK, // d + e → "de" (deepseek) + // 其他情况 + _ => UNKNOWN, + } + }, + }) + .collect(), + ) +} + pub fn validate_token_and_checksum(auth_token: &str) -> Option<(String, String)> { // 尝试使用自定义分隔符查找 let mut delimiter_pos = auth_token.rfind(*TOKEN_DELIMITER); @@ -277,5 +343,33 @@ pub fn tokeninfo_to_token(info: &key_config::TokenInfo) -> Option<(String, Strin }; // 组合 token - Some((format!("{}.{}.{}", HEADER_B64, payload_b64, info.signature), generate_checksum(&device_id, mac_addr.as_deref()))) + Some(( + format!("{}.{}.{}", HEADER_B64, payload_b64, info.signature), + generate_checksum(&device_id, mac_addr.as_deref()), + )) +} + +pub fn encode_message( + message: &impl prost::Message, + with_gzip: bool, +) -> Result, Box> { + let mut encoded = Vec::new(); + message.encode(&mut encoded)?; + + if !with_gzip { + return Ok(encoded); + } + // 构造 5 字节头部 [0x00, len_be_bytes...] + let mut header = Vec::with_capacity(5); + header.push(0x00); // 压缩标记位 + + // 将长度转换为 u32 大端字节(显式长度检查) + let len = u32::try_from(encoded.len()).map_err(|_| "Message length exceeds u32::MAX")?; // 明确错误类型 + header.extend_from_slice(&len.to_be_bytes()); + + // 组合最终数据 + let mut result = header; + result.extend(encoded); + + Ok(result) } diff --git a/src/common/utils/checksum.rs b/src/common/utils/checksum.rs index 1616c20..44e1657 100644 --- a/src/common/utils/checksum.rs +++ b/src/common/utils/checksum.rs @@ -1,9 +1,9 @@ -use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; -use rand::Rng; +use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64}; +use rand::Rng as _; use sha2::{Digest, Sha256}; pub fn generate_hash() -> String { - let random_bytes = rand::thread_rng().gen::<[u8; 32]>(); + let random_bytes = rand::rng().random::<[u8; 32]>(); let mut hasher = Sha256::new(); hasher.update(random_bytes); hex::encode(hasher.finalize()) diff --git a/src/common/utils/token.rs b/src/common/utils/token.rs index 65fe259..c971762 100644 --- a/src/common/utils/token.rs +++ b/src/common/utils/token.rs @@ -1,24 +1,9 @@ use super::generate_checksum_with_repair; -use crate::app::{ - constant::{COMMA, EMPTY_STRING}, - lazy::TOKEN_LIST_FILE, - model::TokenInfo, -}; +use crate::app::{constant::COMMA, model::TokenInfo}; use crate::common::model::token::TokenPayload; -use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; +use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD}; use chrono::{DateTime, Local, TimeZone}; -// 规范化文件内容并写入 -fn normalize_and_write(content: &str, file_path: &str) -> String { - let normalized = content.replace("\r\n", "\n"); - if normalized != content { - if let Err(e) = std::fs::write(file_path, &normalized) { - eprintln!("警告: 无法更新规范化的文件: {}", e); - } - } - normalized -} - // 解析token pub fn parse_token(token_part: &str) -> String { // 查找最后一个:或%3A的位置 @@ -38,82 +23,41 @@ pub fn parse_token(token_part: &str) -> String { } } -// Token 加载函数 -pub fn load_tokens() -> Vec { - let token_list_file = TOKEN_LIST_FILE.as_str(); - - // 确保文件存在 - if !std::path::Path::new(&token_list_file).exists() { - if let Err(e) = std::fs::write(&token_list_file, EMPTY_STRING) { - eprintln!("警告: 无法创建文件 '{}': {}", &token_list_file, e); - } - } - - // 读取和规范化 token-list 文件 - let token_map: std::collections::HashMap = - match std::fs::read_to_string(&token_list_file) { - Ok(content) => { - let normalized = normalize_and_write(&content, &token_list_file); - normalized - .lines() - .filter_map(|line| { - let line = line.trim(); - if line.is_empty() || line.starts_with('#') { - return None; - } - - let parts: Vec<&str> = line.split(COMMA).collect(); - match parts[..] { - [token_part, checksum] => { - let token = parse_token(token_part); - Some((token, generate_checksum_with_repair(checksum))) - } - _ => { - eprintln!("警告: 忽略无效的token-list行: {}", line); - None - } - } - }) - .collect() +// Token 加载函数,支持从字符串内容加载 +pub fn load_tokens_from_content(content: &str) -> Vec { + let token_map: std::collections::HashMap = content + .lines() + .filter_map(|line| { + let line = line.trim(); + if line.is_empty() || line.starts_with('#') { + return None; } - Err(e) => { - eprintln!("警告: 无法读取token-list文件: {}", e); - std::collections::HashMap::new() + + let parts: Vec<&str> = line.split(COMMA).collect(); + match parts[..] { + [token_part, checksum] => { + let token = parse_token(token_part); + Some((token, generate_checksum_with_repair(checksum))) + } + _ => { + eprintln!("警告: 忽略无效的token-list行: {}", line); + None + } } - }; + }) + .collect(); - // 更新 token-list 文件 - let token_list_content = token_map - .iter() - .map(|(token, checksum)| format!("{},{}", token, checksum)) - .collect::>() - .join("\n"); - - if let Err(e) = std::fs::write(&token_list_file, token_list_content) { - eprintln!("警告: 无法更新token-list文件: {}", e); - } - - // 转换为 TokenInfo vector token_map .into_iter() .map(|(token, checksum)| TokenInfo { - token: token.clone(), + token, checksum, profile: None, + tags: None, }) .collect() } -pub fn write_tokens(token_infos: &[TokenInfo], file_path: &str) -> std::io::Result<()> { - let content = token_infos - .iter() - .map(|info| format!("{},{}", info.token, info.checksum)) - .collect::>() - .join("\n"); - - std::fs::write(file_path, content) -} - pub(super) const HEADER_B64: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; pub(super) const ISSUER: &str = "https://authentication.cursor.sh"; pub(super) const SCOPE: &str = "openid profile email offline_access"; diff --git a/src/main.rs b/src/main.rs index 2d0658a..6c655fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,16 +8,16 @@ use app::{ PKG_VERSION, ROUTE_ABOUT_PATH, ROUTE_API_PATH, ROUTE_BASIC_CALIBRATION_PATH, ROUTE_BUILD_KEY_PATH, ROUTE_CONFIG_PATH, ROUTE_ENV_EXAMPLE_PATH, ROUTE_GET_CHECKSUM, ROUTE_GET_HASH, ROUTE_GET_TIMESTAMP_HEADER, ROUTE_HEALTH_PATH, ROUTE_LOGS_PATH, - ROUTE_README_PATH, ROUTE_ROOT_PATH, ROUTE_STATIC_PATH, ROUTE_TOKENS_ADD_PATH, - ROUTE_TOKENS_DELETE_PATH, ROUTE_TOKENS_GET_PATH, ROUTE_TOKENS_PATH, - ROUTE_TOKENS_RELOAD_PATH, ROUTE_TOKENS_UPDATE_PATH, ROUTE_USER_INFO_PATH, + ROUTE_README_PATH, ROUTE_ROOT_PATH, ROUTE_STATIC_PATH, ROUTE_TOKEN_TAGS_UPDATE_PATH, + ROUTE_TOKENS_ADD_PATH, ROUTE_TOKENS_DELETE_PATH, ROUTE_TOKENS_GET_PATH, ROUTE_TOKENS_PATH, + ROUTE_TOKENS_UPDATE_PATH, ROUTE_USER_INFO_PATH, }, lazy::{AUTH_TOKEN, ROUTE_CHAT_PATH, ROUTE_MODELS_PATH}, model::*, }; use axum::{ - routing::{get, post}, Router, + routing::{get, post}, }; use chat::{ route::{ @@ -25,12 +25,12 @@ use chat::{ handle_build_key, handle_build_key_page, handle_config_page, handle_delete_tokens, handle_env_example, handle_get_checksum, handle_get_hash, handle_get_timestamp_header, handle_get_tokens, handle_health, handle_logs, handle_logs_post, handle_readme, - handle_reload_tokens, handle_root, handle_static, handle_tokens_page, handle_update_tokens, - handle_user_info, + handle_root, handle_static, handle_tokens_page, handle_update_token_tags, + handle_update_tokens, handle_user_info, }, service::{handle_chat, handle_models}, }; -use common::utils::{load_tokens, parse_string_from_env, parse_usize_from_env}; +use common::utils::{parse_string_from_env, parse_usize_from_env}; use std::sync::Arc; use tokio::signal; use tokio::sync::Mutex; @@ -58,11 +58,8 @@ async fn main() { // 初始化全局配置 AppConfig::init(); - // 加载 tokens - let token_infos = load_tokens(); - // 初始化应用状态 - let state = Arc::new(Mutex::new(AppState::new(token_infos))); + let state = Arc::new(Mutex::new(AppState::new())); // 尝试加载保存的配置 if let Err(e) = AppConfig::load_saved_config() { @@ -89,7 +86,7 @@ async fn main() { tokio::time::sleep(std::time::Duration::from_secs(wait_duration)).await; let mut app_state = state_for_reload.lock().await; - app_state.update_checksum(); + app_state.token_manager.update_checksum(); // debug_println!("checksum 自动刷新: {}", next_reload); } }); @@ -130,12 +127,12 @@ async fn main() { println!("配置已保存"); } - // 保存日志 + // 保存状态 let state = state_for_shutdown.lock().await; - if let Err(e) = state.save_logs().await { - eprintln!("保存日志失败: {}", e); + if let Err(e) = state.save_state().await { + eprintln!("保存状态失败: {}", e); } else { - println!("日志已保存"); + println!("状态已保存"); } }; @@ -146,7 +143,6 @@ async fn main() { .route(ROUTE_TOKENS_PATH, get(handle_tokens_page)) .route(ROUTE_MODELS_PATH.as_str(), get(handle_models)) .route(ROUTE_TOKENS_GET_PATH, post(handle_get_tokens)) - .route(ROUTE_TOKENS_RELOAD_PATH, post(handle_reload_tokens)) .route(ROUTE_TOKENS_UPDATE_PATH, post(handle_update_tokens)) .route(ROUTE_TOKENS_ADD_PATH, post(handle_add_tokens)) .route(ROUTE_TOKENS_DELETE_PATH, post(handle_delete_tokens)) @@ -167,6 +163,7 @@ async fn main() { .route(ROUTE_USER_INFO_PATH, post(handle_user_info)) .route(ROUTE_BUILD_KEY_PATH, get(handle_build_key_page)) .route(ROUTE_BUILD_KEY_PATH, post(handle_build_key)) + .route(ROUTE_TOKEN_TAGS_UPDATE_PATH, post(handle_update_token_tags)) .layer(RequestBodyLimitLayer::new( 1024 * 1024 * parse_usize_from_env("REQUEST_BODY_LIMIT_MB", 2), )) diff --git a/static/logs.html b/static/logs.html index fe6a5e6..91043fe 100644 --- a/static/logs.html +++ b/static/logs.html @@ -376,6 +376,23 @@ background: var(--error-color); color: white; } + + /* 图表样式 */ + .chart-container { + background: var(--card-background); + padding: 20px; + border-radius: var(--border-radius); + margin-bottom: var(--spacing); + border: 1px solid var(--border-color); + height: 300px; + } + + @media (max-width: 768px) { + .chart-container { + height: 200px; + padding: 16px; + } + } @@ -418,6 +435,11 @@ + +
+ +
+
@@ -529,8 +551,12 @@ + + +