refactor(protocols): update protocol client implementations

This commit is contained in:
LIghtJUNction
2026-03-25 00:10:29 +08:00
parent 613910f592
commit be65022de1
17 changed files with 2148 additions and 246 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-24

View File

@@ -0,0 +1,3 @@
# rust-core-runtime-migration
将核心运行时从 Python 迁移到 Rust

View File

@@ -0,0 +1,188 @@
## Context
AstrBot's core runtime is currently implemented in Python. While Python provides flexibility and rapid development, performance-critical components (orchestration, protocol management, message processing) would benefit from Rust's:
- Memory safety without garbage collection
- Zero-cost abstractions
- Native performance for concurrent operations
- Strong type safety at compile time
The Rust implementation provides a high-performance foundation that can be exposed to Python via pyo3 bindings.
## Goals / Non-Goals
**Goals:**
- Create a `astrbot-core` Rust crate with core runtime components
- Implement thread-safe Orchestrator using RwLock
- Define ProtocolClient trait for LSP, MCP, ACP, ABP clients
- Provide TOML-based configuration management
- Expose Python bindings via pyo3
- CLI binary using clap
**Non-Goals:**
- Not replacing the Python implementation immediately (coexistence)
- Not implementing anyio (uses native Rust async/tokio)
- Not creating a full ABP protocol implementation in Rust
- Not implementing platform adapters or message pipeline
## Decisions
### 1. Architecture: Stub with Python Integration
The initial Rust implementation is a **stub** that provides:
- Structural definitions matching the expected interfaces
- Thread-safe state management (RwLock)
- Python bindings verification via pyo3
This allows:
- Validating the pyo3 integration works
- Ensuring clippy pedantic compliance
- Establishing the project structure
### 2. Concurrency Model: RwLock for Thread Safety
```rust
pub struct Orchestrator {
running: RwLock<bool>,
stars: RwLock<HashMap<String, String>>,
protocol_lsp: RwLock<ProtocolStatus>,
// ...
}
```
Using `RwLock` allows:
- Multiple readers concurrently (most operations are reads)
- Exclusive writer (state changes)
- No deadlocks (standard read-write lock pattern)
### 3. Error Handling: thiserror for Ergonomic Errors
```rust
#[derive(Error, Debug)]
pub enum AstrBotError {
#[error("Not connected: {0}")]
NotConnected(String),
// ...
}
```
Using `thiserror` provides:
- Compile-time error message generation
- `?` operator compatibility
- Debug output for development
### 4. Python Bindings: GILOnceCell Singleton
```rust
static ORCHESTRATOR: GILOnceCell<Py<PythonOrchestrator>> = GILOnceCell::new();
#[pyfunction]
pub fn get_orchestrator(py: Python<'_>) -> PyResult<&'static Py<PythonOrchestrator>> {
if ORCHESTRATOR.get(py).is_none() {
ORCHESTRATOR.set(py, Py::new(py, PythonOrchestrator::new())?)?;
}
Ok(ORCHESTRATOR.get(py).expect("initialized"))
}
```
Using `GILOnceCell` provides:
- Thread-safe global singleton
- GIL-aware initialization
- Lazy initialization on first Python access
### 5. Rust Rules Enforcement
```rust
#![deny(unsafe_code)]
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
```
- **No unsafe**: All memory access is safe by construction
- **No unwrap()**: Errors propagated via `?` or expect with messages
- **Clippy pedantic**: Catches style issues and potential bugs
### 6. ProtocolClient Trait: Static Lifetime for Names
```rust
#[async_trait]
pub trait ProtocolClient: Send + Sync {
fn name(&self) -> &'static str;
// ...
}
```
Using `&'static str` ensures:
- No lifetime issues from borrowed data
- Compile-time guaranteed string validity
- Simple implementation for hardcoded client names
## Risks / Trade-offs
| Risk | Mitigation |
|------|------------|
| pyo3 compatibility with Python 3.14 | Use `PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1` |
| Two implementations to maintain | Rust is opt-in via feature flag |
| Performance overhead of bindings | Rust called only for core operations |
| Clippy pedantic false positives | Use `#[allow(...)]` for intentional patterns |
## File Structure
```
rust/
├── Cargo.toml
├── src/
│ ├── lib.rs # Crate root with module declarations
│ ├── main.rs # CLI binary
│ ├── error.rs # AstrBotError enum
│ ├── orchestrator.rs # Core orchestrator
│ ├── message.rs # Message types
│ ├── stats.rs # RuntimeStats
│ ├── protocol.rs # ProtocolClient trait + implementations
│ ├── config.rs # Configuration structs
│ └── python.rs # pyo3 bindings
└── target/ # Build output (gitignored)
```
## Cargo Features
```toml
[features]
default = ["python"]
python = ["pyo3"]
```
- Default enables Python bindings
- Can build pure Rust library without Python
## Verification
| Check | Command |
|-------|---------|
| Clippy | `PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 cargo clippy` |
| Build | `PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 cargo build` |
| Python import | `python -c "from astrbot_core import PythonOrchestrator"` |
| CLI help | `cargo run -- --help` |
## Current Implementation Status
| Component | Status | Notes |
|-----------|--------|-------|
| error.rs | ✅ Complete | thiserror-based errors |
| orchestrator.rs | ✅ Complete | Thread-safe with RwLock |
| message.rs | ✅ Complete | serde serialization |
| stats.rs | ✅ Complete | AtomicU64 message count |
| protocol.rs | ✅ Complete | Trait + 4 client stubs |
| config.rs | ✅ Complete | TOML load/save |
| python.rs | ✅ Complete | pyo3 bindings |
| main.rs | ✅ Complete | clap CLI |
| lib.rs | ✅ Complete | Module declarations |
| Clippy | ✅ Passing | No warnings |
| Build | ✅ Passing | Compiles successfully |
## Next Steps (Future Work)
1. **Real Protocol Implementations**: Replace stub clients with actual LSP/MCP/ACP/ABP implementations
2. **Python Integration**: Connect Rust orchestrator to Python platform adapters
3. **Performance Benchmarking**: Compare Python vs Rust performance
4. **Feature Parity**: Match all Python orchestrator functionality
5. **Production Readiness**: Add more tests, error handling, edge cases

View File

@@ -0,0 +1,86 @@
## Why
AstrBot's core runtime is currently implemented in Python. Performance-critical components (orchestration, protocol management, message processing) would benefit from Rust's memory safety, zero-cost abstractions, and native performance. Additionally, exposing core functionality via pyo3 allows seamless Python integration while leveraging Rust's strengths.
## What Changes
- Create a new Rust crate `astrbot-core` in `rust/` directory
- Implement core runtime components in Rust:
- `Orchestrator`: Thread-safe runtime coordinator with RwLock
- `ProtocolClient` trait: Unified interface for LSP, MCP, ACP, ABP clients
- `Message` and `MessageType`: Message serialization with serde
- `RuntimeStats`: Atomic message counting and uptime tracking
- `Config`: TOML-based configuration management
- Provide Python bindings via pyo3 for seamless integration
- Follow strict Rust best practices:
- No `unsafe` code
- No `.unwrap()` - proper error handling
- Clippy pedantic compliance
- Full test coverage
## Architecture
```
Python Layer (astrbot/core/)
▼ (pyo3 bindings)
┌─────────────────────────────────────────────────┐
│ Rust Core (astrbot-core) │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ Orchestrator│ │ Config │ │ Stats │ │
│ └─────────────┘ └─────────────┘ └──────────┘ │
│ ┌─────────────────────────────────────────────┐│
│ │ Protocol Clients ││
│ │ LSP │ MCP │ ACP │ ABP ││
│ └─────────────────────────────────────────────┘│
└─────────────────────────────────────────────────┘
```
## Capabilities
### New Capabilities
- `astrbot-core`: Rust-based high-performance core runtime with pyo3 bindings
### Modified Capabilities
- (none - new implementation)
## Impact
- New directory: `rust/` containing Cargo.toml and src/
- New files:
- `rust/Cargo.toml`
- `rust/src/lib.rs`
- `rust/src/main.rs` (CLI binary)
- `rust/src/error.rs`
- `rust/src/orchestrator.rs`
- `rust/src/message.rs`
- `rust/src/stats.rs`
- `rust/src/protocol.rs`
- `rust/src/config.rs`
- `rust/src/python.rs`
- Python integration via `astrbot_core` Python module
- CLI: `astrbot-core` binary with start/stats/health commands
## Verification
- `cargo clippy` passes with no warnings
- `cargo build` compiles successfully
- Python bindings importable: `from astrbot_core import PythonOrchestrator`
- CLI functional: `astrbot-core --help`
## Relationship to OpenSpec Architecture
This change introduces a new implementation pathway that complements (not replaces) the existing Python architecture defined in `openspec/SPEC.md`. The Rust implementation:
1. Provides a reference implementation of the same interfaces (Orchestrator, ProtocolClient, etc.)
2. Uses Rust idioms (no anyio - uses native Rust async/tokio)
3. Is opt-in via pyo3 feature flag
4. Coexists with Python implementation until Rust is production-ready
## Status
- [x] Proposal created
- [ ] Spec created
- [ ] Design created
- [ ] Tasks created
- [ ] Implementation started

View File

@@ -0,0 +1,251 @@
# AstrBot Core Runtime (Rust) Specification
## Overview
AstrBot Core Runtime is a high-performance Rust implementation of the core orchestrator, protocol clients, and configuration management. It provides Python bindings via pyo3 for seamless integration with the existing AstrBot Python codebase.
## Module Structure
### Core Modules
#### 1. orchestrator.rs - Runtime Orchestrator
Central coordinator managing protocol clients and star registry.
```rust
pub struct Orchestrator {
running: RwLock<bool>,
stars: RwLock<HashMap<String, String>>,
stats: RuntimeStats,
protocol_lsp: RwLock<ProtocolStatus>,
protocol_mcp: RwLock<ProtocolStatus>,
protocol_acp: RwLock<ProtocolStatus>,
protocol_abp: RwLock<ProtocolStatus>,
}
impl Orchestrator {
pub fn new() -> Self;
pub fn start(&self) -> Result<(), AstrBotError>;
pub fn stop(&self) -> Result<(), AstrBotError>;
pub fn is_running(&self) -> bool;
pub fn register_star(&self, name: &str, handler: &str) -> Result<(), AstrBotError>;
pub fn unregister_star(&self, name: &str) -> Result<(), AstrBotError>;
pub fn list_stars(&self) -> Vec<String>;
pub fn record_activity(&self);
pub fn stats(&self) -> RuntimeStats;
pub fn get_protocol_status(&self, protocol: &str) -> Option<ProtocolStatus>;
pub fn set_protocol_connected(&self, protocol: &str, connected: bool) -> Result<(), AstrBotError>;
}
```
#### 2. protocol.rs - Protocol Client Trait
Unified interface for all protocol clients.
```rust
#[async_trait]
pub trait ProtocolClient: Send + Sync {
async fn connect(&mut self) -> Result<(), AstrBotError>;
async fn disconnect(&mut self) -> Result<(), AstrBotError>;
fn is_connected(&self) -> bool;
fn name(&self) -> &'static str;
}
```
Implementations:
- `LspClient` - Language Server Protocol client
- `McpClient` - Model Context Protocol client
- `AcpClient` - AstrBot Communication Protocol client
- `AbpClient` - AstrBot Protocol client
#### 3. message.rs - Message Types
Message structures with serde serialization.
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub id: String,
pub content: String,
pub sender: String,
pub timestamp: f64,
pub message_type: MessageType,
pub metadata: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Default)]
pub enum MessageType {
#[default]
Text,
Image,
Audio,
Video,
File,
System,
Unknown,
}
```
#### 4. stats.rs - Runtime Statistics
Thread-safe message counting and uptime tracking.
```rust
#[derive(Debug, Clone)]
pub struct RuntimeStats {
message_count: AtomicU64,
start_time: Instant,
last_activity: Mutex<Option<Instant>>,
}
impl RuntimeStats {
pub fn new() -> Self;
pub fn record_message(&self);
pub fn message_count(&self) -> u64;
pub fn uptime_seconds(&self) -> f64;
pub fn last_activity_time(&self) -> Option<f64>;
}
```
#### 5. config.rs - Configuration Management
TOML-based configuration with serde.
```rust
pub struct Config {
pub runtime: RuntimeConfig,
pub protocols: ProtocolsConfig,
pub logging: LoggingConfig,
}
impl Config {
pub fn load(path: &PathBuf) -> anyhow::Result<Self>;
pub fn save(&self, path: &PathBuf) -> anyhow::Result<()>;
}
```
#### 6. error.rs - Error Types
Using thiserror for ergonomic error handling.
```rust
#[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),
}
```
#### 7. python.rs - Python Bindings
pyo3 bindings for Python integration.
```rust
#[pyclass]
pub struct PythonOrchestrator {
inner: Orchestrator,
}
#[pymethods]
impl PythonOrchestrator {
#[new]
pub fn new() -> Self;
pub fn start(&self) -> PyResult<()>;
pub fn stop(&self) -> PyResult<()>;
pub fn is_running(&self) -> bool;
pub fn register_star(&self, name: &str, handler: &str) -> PyResult<()>;
pub fn unregister_star(&self, name: &str) -> PyResult<()>;
pub fn list_stars(&self) -> Vec<String>;
pub fn record_activity(&self);
pub fn get_stats(&self) -> PyResult<Py<PyAny>>;
pub fn set_protocol_connected(&self, protocol: &str, connected: bool) -> PyResult<()>;
pub fn get_protocol_status(&self, protocol: &str) -> Option<Py<PyAny>>;
}
#[pyfunction]
pub fn get_orchestrator(py: Python<'_>) -> PyResult<&'static Py<PythonOrchestrator>>;
```
#### 8. main.rs - CLI Binary
Command-line interface using clap.
```rust
#[derive(Parser, Debug)]
enum Command {
Start,
Stats,
Health,
}
```
Commands:
- `start` - Start the astrbot-core runtime
- `stats` - Display runtime statistics
- `health` - Check runtime health status
## Rust Rules
1. **No unsafe code** - All memory access is safe
2. **No .unwrap()** - Use `?` operator or `expect()` with descriptive messages
3. **Clippy pedantic compliance** - Pass `cargo clippy` with no warnings
4. **Full error handling** - All errors properly propagated
## Python Integration
The module is importable as `astrbot_core`:
```python
from astrbot_core import PythonOrchestrator, get_orchestrator
# Get singleton
orch = get_orchestrator()
# Use methods
orch.start()
orch.register_star("my-star", "handler-id")
stars = orch.list_stars()
```
## Cargo Features
```toml
[features]
default = ["python"]
python = ["pyo3"]
```
- `python`: Enable pyo3 bindings (default)
- Without `python`: Pure Rust library without Python dependencies
## Dependencies
- `serde` + `serde_json` - Serialization
- `toml` - Configuration file parsing
- `tokio` - Async runtime
- `tracing` + `tracing-subscriber` - Logging
- `anyhow` - Error handling
- `thiserror` - Error derive
- `async-trait` - Async trait methods
- `clap` - CLI argument parsing
- `pyo3` - Python bindings (optional)
## Verification Criteria
- [x] `cargo clippy` passes with no warnings
- [x] `cargo build` compiles successfully
- [x] `cargo test` passes (if tests exist)
- [x] Python module imports successfully
- [x] CLI `--help` works correctly

View File

@@ -0,0 +1,53 @@
# Implementation Tasks
## 1. Project Setup
- [x] 1.1 Create rust/ directory
- [x] 1.2 Initialize with cargo init --name astrbot-core
- [x] 1.3 Add Cargo.toml with dependencies (serde, tokio, pyo3, clap, etc.)
- [x] 1.4 Create .cargo/config.toml for pyo3 forward compatibility
## 2. Core Modules
- [x] 2.1 Create lib.rs with module declarations and clippy settings
- [x] 2.2 Create error.rs with AstrBotError enum using thiserror
- [x] 2.3 Create orchestrator.rs with Orchestrator struct and methods
- [x] 2.4 Create message.rs with Message and MessageType
- [x] 2.5 Create stats.rs with RuntimeStats using AtomicU64
- [x] 2.6 Create protocol.rs with ProtocolClient trait and client implementations
- [x] 2.7 Create config.rs with Config and related structs
- [x] 2.8 Create python.rs with pyo3 bindings
## 3. CLI Binary
- [x] 3.1 Create main.rs with clap CLI (start, stats, health commands)
## 4. Rust Rules Compliance
- [x] 4.1 Ensure no unsafe code (#![deny(unsafe_code)])
- [x] 4.2 Ensure no .unwrap() without message (#[allow] where needed)
- [x] 4.3 Add clippy pedantic settings (#![deny(clippy::pedantic)])
- [x] 4.4 Fix all clippy warnings
## 5. Verification
- [x] 5.1 Run `cargo clippy` - no warnings
- [x] 5.2 Run `cargo build` - compiles successfully
- [x] 5.3 Verify CLI works: `cargo run -- --help`
## 6. Documentation
- [x] 6.1 Create proposal.md
- [x] 6.2 Create spec.md
- [x] 6.3 Create design.md
- [ ] 6.4 Create tasks.md (this file)
## 7. Future Work (Not in Scope)
- [ ] 7.1 Implement real LSP client functionality
- [ ] 7.2 Implement real MCP client functionality
- [ ] 7.3 Implement real ACP client functionality
- [ ] 7.4 Implement real ABP client functionality
- [ ] 7.5 Connect Rust orchestrator to Python platform adapters
- [ ] 7.6 Add comprehensive test suite
- [ ] 7.7 Performance benchmarking