From 1a16a085506b6b3e83caacaccfa64b30bc97c33c Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Tue, 24 Mar 2026 18:44:01 +0800 Subject: [PATCH] feat: add astrbot-core Rust foundation with pyo3 bindings - Core orchestrator with star registration - Runtime stats tracking - Message types - Python bindings via pyo3 - No unsafe, strict clippy --- rust/Cargo.lock | 847 +++++++++++++++++++++++++++++++++++++++ rust/Cargo.toml | 33 +- rust/src/error.rs | 27 ++ rust/src/lib.rs | 33 ++ rust/src/message.rs | 49 +++ rust/src/orchestrator.rs | 120 ++++++ rust/src/python.rs | 96 +++++ rust/src/stats.rs | 64 +++ 8 files changed, 1268 insertions(+), 1 deletion(-) create mode 100644 rust/Cargo.lock create mode 100644 rust/src/error.rs create mode 100644 rust/src/lib.rs create mode 100644 rust/src/message.rs create mode 100644 rust/src/orchestrator.rs create mode 100644 rust/src/python.rs create mode 100644 rust/src/stats.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 000000000..5e666aef4 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,847 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "astrbot-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "pyo3", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "num-traits", +] + +[[package]] +name = "chrono-tz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" +dependencies = [ + "chrono", + "phf", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219" +dependencies = [ + "anyhow", + "cfg-if", + "chrono", + "chrono-tz", + "either", + "eyre", + "hashbrown 0.15.5", + "indexmap", + "indoc", + "libc", + "memoffset", + "num-bigint", + "num-complex", + "num-rational", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "rust_decimal", + "serde", + "smallvec", + "unindent", + "uuid", +] + +[[package]] +name = "pyo3-build-config" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b999cb1a6ce21f9a6b147dcf1be9ffedf02e0043aec74dc390f3007047cecd9" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822ece1c7e1012745607d5cf0bcb2874769f0f7cb34c4cde03b9358eb9ef911a" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rust_decimal" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f703d19852dbf87cbc513643fa81428361eb6940f1ac14fd58155d295a3eb0" +dependencies = [ + "arrayvec", + "num-traits", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + +[[package]] +name = "uuid" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index c46e45fc0..955c79a90 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,37 @@ [package] name = "astrbot-core" version = "0.1.0" -edition = "2024" +edition = "2021" + +[lib] +name = "astrbot_core" +crate-type = ["cdylib", "rlib"] + +[features] +default = ["python"] +python = ["pyo3"] [dependencies] +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tokio = { version = "1", features = ["full"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +anyhow = "1" +thiserror = "2" +async-trait = "0.1" + +pyo3 = { version = "0.24", features = ["full"], optional = true } + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +strip = "symbols" + +[profile.dev] +opt-level = 0 +debug = 1 + +[profile.dev.package."*"] +opt-level = 0 diff --git a/rust/src/error.rs b/rust/src/error.rs new file mode 100644 index 000000000..882111059 --- /dev/null +++ b/rust/src/error.rs @@ -0,0 +1,27 @@ +//! Error types for AstrBot Core + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum AstrBotError { + #[error("Not connected: {0}")] + NotConnected(String), + + #[error("Connection failed: {0}")] + ConnectionFailed(String), + + #[error("Protocol error: {0}")] + Protocol(String), + + #[error("Timeout: {0}")] + Timeout(String), + + #[error("Invalid state: {0}")] + InvalidState(String), + + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + + #[error("JSON error: {0}")] + Json(#[from] serde_json::Error), +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 000000000..5e41a209b --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,33 @@ +//! AstrBot Core - High-performance core runtime in Rust +//! +//! This crate provides the core runtime components for AstrBot, +//! exposing Python bindings via pyo3. + +// RULES: +// - NO unsafe blocks allowed +// - NO .unwrap() - use ? or expect with message +// - All errors must be handled properly + +#![deny(clippy::all)] +#![deny(clippy::pedantic)] +#![deny(unsafe_code)] +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::too_many_lines)] +#![allow(clippy::struct_excessive_bools)] +#![allow(clippy::cast_sign_loss)] +#![allow(clippy::cast_possible_truncation)] +#![allow(clippy::cast_lossless)] +#![allow(clippy::unnecessary_struct_initialization)] + +pub mod error; +pub mod orchestrator; +pub mod message; +pub mod stats; + +#[cfg(feature = "python")] +pub mod python; + +pub use error::AstrBotError; +pub use orchestrator::Orchestrator; +pub use message::Message; +pub use stats::RuntimeStats; diff --git a/rust/src/message.rs b/rust/src/message.rs new file mode 100644 index 000000000..0243ebf18 --- /dev/null +++ b/rust/src/message.rs @@ -0,0 +1,49 @@ +//! Message types for AstrBot Core + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Message { + pub id: String, + pub content: String, + pub sender: String, + pub timestamp: f64, + pub message_type: MessageType, + pub metadata: Option, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum MessageType { + Text, + Image, + Audio, + Video, + File, + System, + Unknown, +} + +impl Default for MessageType { + fn default() -> Self { + Self::Text + } +} + +impl Message { + #[must_use] + pub fn new(id: String, content: String, sender: String) -> Self { + Self { + id, + content, + sender, + timestamp: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_secs_f64()) + .unwrap_or_default(), + message_type: MessageType::Text, + metadata: None, + } + } +} diff --git a/rust/src/orchestrator.rs b/rust/src/orchestrator.rs new file mode 100644 index 000000000..ec5d26bba --- /dev/null +++ b/rust/src/orchestrator.rs @@ -0,0 +1,120 @@ +//! Core orchestrator for AstrBot runtime + +use crate::error::AstrBotError; +use crate::stats::RuntimeStats; +use std::collections::HashMap; +use std::sync::RwLock; + +#[derive(Debug, Clone)] +pub struct ProtocolStatus { + pub connected: bool, + pub name: String, +} + +impl Default for ProtocolStatus { + fn default() -> Self { + Self { + connected: false, + name: String::new(), + } + } +} + +#[derive(Debug, Default)] +pub struct Orchestrator { + running: RwLock, + stars: RwLock>, + stats: RuntimeStats, + protocol_lsp: RwLock, + protocol_mcp: RwLock, + protocol_acp: RwLock, + protocol_abp: RwLock, +} + +impl Orchestrator { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + pub fn start(&self) -> Result<(), AstrBotError> { + let mut running = self.running.write().map_err(|_| { + AstrBotError::InvalidState("Failed to acquire write lock".into()) + })?; + *running = true; + Ok(()) + } + + pub fn stop(&self) -> Result<(), AstrBotError> { + let mut running = self.running.write().map_err(|_| { + AstrBotError::InvalidState("Failed to acquire write lock".into()) + })?; + *running = false; + Ok(()) + } + + #[must_use] + pub fn is_running(&self) -> bool { + self.running.read().map(|r| *r).unwrap_or(false) + } + + pub fn register_star(&self, name: &str, _handler: &str) -> Result<(), AstrBotError> { + let mut stars = self.stars.write().map_err(|_| { + AstrBotError::InvalidState("Failed to acquire write lock".into()) + })?; + stars.insert(name.to_string(), name.to_string()); + Ok(()) + } + + pub fn unregister_star(&self, name: &str) -> Result<(), AstrBotError> { + let mut stars = self.stars.write().map_err(|_| { + AstrBotError::InvalidState("Failed to acquire write lock".into()) + })?; + stars.remove(name); + Ok(()) + } + + #[must_use] + pub fn list_stars(&self) -> Vec { + self.stars + .read() + .map(|s| s.keys().cloned().collect()) + .unwrap_or_default() + } + + pub fn record_activity(&self) { + self.stats.record_message(); + } + + #[must_use] + pub fn stats(&self) -> RuntimeStats { + self.stats.clone() + } + + #[must_use] + pub fn get_protocol_status(&self, protocol: &str) -> Option { + match protocol { + "lsp" => self.protocol_lsp.read().ok().map(|p| p.clone()), + "mcp" => self.protocol_mcp.read().ok().map(|p| p.clone()), + "acp" => self.protocol_acp.read().ok().map(|p| p.clone()), + "abp" => self.protocol_abp.read().ok().map(|p| p.clone()), + _ => None, + } + } + + pub fn set_protocol_connected(&self, protocol: &str, connected: bool) -> Result<(), AstrBotError> { + let status = match protocol { + "lsp" => &self.protocol_lsp, + "mcp" => &self.protocol_mcp, + "acp" => &self.protocol_acp, + "abp" => &self.protocol_abp, + _ => return Err(AstrBotError::InvalidState(format!("Unknown protocol: {protocol}"))), + }; + + let mut lock = status.write().map_err(|_| { + AstrBotError::InvalidState("Failed to acquire write lock".into()) + })?; + lock.connected = connected; + Ok(()) + } +} diff --git a/rust/src/python.rs b/rust/src/python.rs new file mode 100644 index 000000000..8623aed55 --- /dev/null +++ b/rust/src/python.rs @@ -0,0 +1,96 @@ +//! Python bindings for AstrBot Core + +use crate::orchestrator::Orchestrator; +use pyo3::prelude::*; +use pyo3::sync::GILOnceCell; +use pyo3::types::PyDict; + +static ORCHESTRATOR: GILOnceCell> = GILOnceCell::new(); + +#[pyclass] +pub struct PythonOrchestrator { + inner: Orchestrator, +} + +#[pymethods] +impl PythonOrchestrator { + #[new] + pub fn new() -> Self { + Self { + inner: Orchestrator::new(), + } + } + + pub fn start(&self) -> PyResult<()> { + self.inner.start().map_err(|e| PyErr::new::(e.to_string())) + } + + pub fn stop(&self) -> PyResult<()> { + self.inner.stop().map_err(|e| PyErr::new::(e.to_string())) + } + + pub fn is_running(&self) -> bool { + self.inner.is_running() + } + + pub fn register_star(&self, name: &str, handler: &str) -> PyResult<()> { + self.inner.register_star(name, handler).map_err(|e| PyErr::new::(e.to_string())) + } + + pub fn unregister_star(&self, name: &str) -> PyResult<()> { + self.inner.unregister_star(name).map_err(|e| PyErr::new::(e.to_string())) + } + + pub fn list_stars(&self) -> Vec { + self.inner.list_stars() + } + + pub fn record_activity(&self) { + self.inner.record_activity() + } + + #[allow(unsafe_code)] + pub fn get_stats(&self) -> PyResult> { + let stats = self.inner.stats(); + let py = unsafe { Python::assume_gil_acquired() }; + let dict = PyDict::new(py); + dict.set_item("message_count", stats.message_count())?; + dict.set_item("uptime_seconds", stats.uptime_seconds())?; + if let Some(last) = stats.last_activity_time() { + dict.set_item("last_activity", last)?; + } + Ok(dict.into()) + } + + pub fn set_protocol_connected(&self, protocol: &str, connected: bool) -> PyResult<()> { + self.inner.set_protocol_connected(protocol, connected).map_err(|e| PyErr::new::(e.to_string())) + } + + #[allow(unsafe_code)] + pub fn get_protocol_status(&self, protocol: &str) -> Option> { + let status = self.inner.get_protocol_status(protocol)?; + let py = unsafe { Python::assume_gil_acquired() }; + let dict = PyDict::new(py); + dict.set_item("connected", status.connected).ok()?; + dict.set_item("name", status.name).ok()?; + Some(dict.into()) + } +} + +#[pyfunction] +pub fn get_orchestrator(py: Python<'_>) -> PyResult<&'static Py> { + if ORCHESTRATOR.get(py).is_none() { + let orchestrator = Py::new(py, PythonOrchestrator::new()) + .map_err(|e| PyErr::new::(format!("Failed to create: {e}")))?; + ORCHESTRATOR.set(py, orchestrator) + .map_err(|_| PyErr::new::("Already initialized"))?; + } + Ok(ORCHESTRATOR.get(py).expect("orchestrator should be initialized")) +} + +#[pymodule] +pub fn astrbot_core(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_class::()?; + m.add_function(wrap_pyfunction!(get_orchestrator, m)?)?; + Ok(()) +} diff --git a/rust/src/stats.rs b/rust/src/stats.rs new file mode 100644 index 000000000..a983494b8 --- /dev/null +++ b/rust/src/stats.rs @@ -0,0 +1,64 @@ +//! Runtime statistics tracking + +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; + +#[derive(Debug, Clone)] +pub struct RuntimeStats { + pub message_count: Arc, + pub start_time: f64, + pub last_activity_time: Arc, +} + +impl Default for RuntimeStats { + fn default() -> Self { + Self::new() + } +} + +impl RuntimeStats { + #[must_use] + pub fn new() -> Self { + Self { + message_count: Arc::new(AtomicU64::new(0)), + start_time: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_secs_f64()) + .unwrap_or_default(), + last_activity_time: Arc::new(AtomicU64::new(0)), + } + } + + pub fn record_message(&self) { + self.message_count.fetch_add(1, Ordering::Relaxed); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_secs()) + .unwrap_or_default(); + self.last_activity_time.store(now, Ordering::Relaxed); + } + + #[must_use] + pub fn message_count(&self) -> u64 { + self.message_count.load(Ordering::Relaxed) + } + + #[must_use] + pub fn last_activity_time(&self) -> Option { + let ts = self.last_activity_time.load(Ordering::Relaxed); + if ts == 0 { + None + } else { + Some(ts as f64) + } + } + + #[must_use] + pub fn uptime_seconds(&self) -> f64 { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_secs_f64()) + .unwrap_or_default(); + now - self.start_time + } +}