docs: update and optimize README and project documentation system
- Update README.md with improved formatting, logo, badges, and comprehensive documentation - Add README.en.md with English translation of the documentation - Include detailed quick start guide, CLI reference, and supported models table - Add changelog information highlighting recent features - Add script directory and adjust script file locations
This commit is contained in:
@@ -0,0 +1,601 @@
|
||||
# Development Guide
|
||||
|
||||
This guide covers contributing to AHA, including development setup, adding new models, and submitting contributions.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Development Setup](#development-setup)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Adding a New Model](#adding-a-new-model)
|
||||
- [Testing](#testing)
|
||||
- [Code Style](#code-style)
|
||||
- [Submitting Contributions](#submitting-contributions)
|
||||
- [Release Process](#release-process)
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Rust 1.85+ (edition 2024)
|
||||
- Git
|
||||
- Optional: CUDA Toolkit, Metal, FFmpeg (for feature development)
|
||||
|
||||
### Clone and Build
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/jhqxxx/aha.git
|
||||
cd aha
|
||||
|
||||
# Build in debug mode (faster compilation)
|
||||
cargo build
|
||||
|
||||
# Build in release mode (better performance)
|
||||
cargo build --release
|
||||
|
||||
# Build with features
|
||||
cargo build --features "cuda,flash-attn"
|
||||
cargo build --features "metal"
|
||||
cargo build --features "ffmpeg"
|
||||
```
|
||||
|
||||
### Development Workflow
|
||||
|
||||
```bash
|
||||
# Run the CLI
|
||||
cargo run -- -m qwen3-0.6b
|
||||
|
||||
# Run tests
|
||||
cargo test
|
||||
|
||||
# Run specific test
|
||||
cargo test test_qwen3vl_generate
|
||||
|
||||
# Run with logging
|
||||
RUST_LOG=debug cargo run -- -m qwen3-0.6b
|
||||
|
||||
# Check code without building
|
||||
cargo check
|
||||
|
||||
# Format code
|
||||
cargo fmt
|
||||
|
||||
# Run linter
|
||||
cargo clippy
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
aha/
|
||||
├── Cargo.toml # Build configuration
|
||||
├── src/
|
||||
│ ├── main.rs # CLI entry point
|
||||
│ ├── lib.rs # Library exports
|
||||
│ ├── api.rs # HTTP API handlers
|
||||
│ ├── exec/ # CLI command implementations
|
||||
│ │ ├── mod.rs
|
||||
│ │ ├── cli.rs
|
||||
│ │ ├── serv.rs
|
||||
│ │ ├── download.rs
|
||||
│ │ └── run.rs
|
||||
│ ├── models/ # Model implementations
|
||||
│ │ ├── mod.rs # Model factory and exports
|
||||
│ │ ├── common/ # Shared model utilities
|
||||
│ │ │ ├── config.rs
|
||||
│ │ │ └── mod.rs
|
||||
│ │ ├── qwen3vl/ # Qwen3-VL model
|
||||
│ │ │ ├── config.rs
|
||||
│ │ │ ├── model.rs
|
||||
│ │ │ ├── generate.rs
|
||||
│ │ │ ├── processor.rs
|
||||
│ │ │ └── mod.rs
|
||||
│ │ ├── voxcpm/ # VoxCPM model
|
||||
│ │ └── ... # Other models
|
||||
│ ├── tokenizer/ # Tokenization utilities
|
||||
│ ├── chat_template/ # Chat template handling
|
||||
│ ├── position_embed/ # Positional embeddings
|
||||
│ └── utils/ # Utility functions
|
||||
│ ├── audio_utils.rs
|
||||
│ ├── image_utils.rs
|
||||
│ ├── download.rs
|
||||
│ └── common.rs
|
||||
├── tests/ # Integration tests
|
||||
│ ├── test_qwen2_5vl.rs
|
||||
│ ├── test_qwen3vl.rs
|
||||
│ └── ...
|
||||
├── examples/ # Example code
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
## Adding a New Model
|
||||
|
||||
This section provides a step-by-step guide for adding a new model to AHA.
|
||||
|
||||
### Step 1: Create Model Directory
|
||||
|
||||
Create a new directory under `src/models/`:
|
||||
|
||||
```bash
|
||||
mkdir -p src/models/newmodel
|
||||
```
|
||||
|
||||
### Step 2: Implement Model Files
|
||||
|
||||
Create the following files in `src/models/newmodel/`:
|
||||
|
||||
#### config.rs
|
||||
|
||||
Define model configuration:
|
||||
|
||||
```rust
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct NewModelConfig {
|
||||
pub vocab_size: usize,
|
||||
pub hidden_size: usize,
|
||||
pub num_hidden_layers: usize,
|
||||
pub num_attention_heads: usize,
|
||||
// ... other config fields
|
||||
}
|
||||
|
||||
impl Default for NewModelConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
vocab_size: 32000,
|
||||
hidden_size: 2048,
|
||||
num_hidden_layers: 24,
|
||||
num_attention_heads: 32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GenerationConfig {
|
||||
pub temperature: f64,
|
||||
pub top_p: f64,
|
||||
pub max_tokens: usize,
|
||||
}
|
||||
|
||||
impl Default for GenerationConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
temperature: 1.0,
|
||||
top_p: 1.0,
|
||||
max_tokens: 100,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### model.rs
|
||||
|
||||
Implement the model architecture:
|
||||
|
||||
```rust
|
||||
use candle::{Result, Tensor};
|
||||
use candle_nn::{Module, VarBuilder};
|
||||
|
||||
use super::config::NewModelConfig;
|
||||
|
||||
pub struct NewModel {
|
||||
// Define model components
|
||||
config: NewModelConfig,
|
||||
}
|
||||
|
||||
impl NewModel {
|
||||
pub fn load(vb: VarBuilder, config: &NewModelConfig) -> Result<Self> {
|
||||
// Load model weights
|
||||
Ok(Self {
|
||||
config: config.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn forward(&self, input_ids: &Tensor) -> Result<Tensor> {
|
||||
// Implement forward pass
|
||||
Ok(input_ids.clone())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### generate.rs
|
||||
|
||||
Implement the GenerateModel trait:
|
||||
|
||||
```rust
|
||||
use std::path::Path;
|
||||
use anyhow::Result;
|
||||
use candle::Device;
|
||||
|
||||
use super::config::{GenerationConfig, NewModelConfig};
|
||||
use aha::models::common::GenerateModel;
|
||||
|
||||
pub struct NewModelGenerate {
|
||||
// Define generate state
|
||||
model: super::model::NewModel,
|
||||
config: GenerationConfig,
|
||||
device: Device,
|
||||
}
|
||||
|
||||
impl GenerateModel for NewModelGenerate {
|
||||
type Config = NewModelConfig;
|
||||
type GenerateConfig = GenerationConfig;
|
||||
|
||||
fn init(
|
||||
model_path: &str,
|
||||
config: Option<Self::Config>,
|
||||
generate_config: Option<Self::GenerateConfig>,
|
||||
) -> Result<Self> {
|
||||
// Load model from disk
|
||||
let device = Device::Cpu;
|
||||
let config = config.unwrap_or_default();
|
||||
let generate_config = generate_config.unwrap_or_default();
|
||||
|
||||
// Load weights
|
||||
let model_path = Path::new(model_path);
|
||||
// ... load model implementation
|
||||
|
||||
Ok(Self {
|
||||
model: todo!(),
|
||||
config: generate_config,
|
||||
device,
|
||||
})
|
||||
}
|
||||
|
||||
fn generate(&mut self, prompt: &str) -> Result<String> {
|
||||
// Tokenize prompt
|
||||
// Run inference
|
||||
// Decode output
|
||||
Ok(prompt.to_string())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### processor.rs (optional)
|
||||
|
||||
For complex models with multimodal input:
|
||||
|
||||
```rust
|
||||
use anyhow::Result;
|
||||
|
||||
pub struct NewModelProcessor {
|
||||
// Processing state
|
||||
}
|
||||
|
||||
impl NewModelProcessor {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn process_text(&self, text: &str) -> Result<Vec<String>> {
|
||||
// Process text input
|
||||
Ok(vec![text.to_string()])
|
||||
}
|
||||
|
||||
pub fn process_image(&self, image_path: &str) -> Result<Tensor> {
|
||||
// Process image input
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### mod.rs
|
||||
|
||||
Export the model:
|
||||
|
||||
```rust
|
||||
mod config;
|
||||
mod model;
|
||||
mod generate;
|
||||
pub mod processor;
|
||||
|
||||
pub use generate::NewModelGenerate;
|
||||
pub use config::{GenerationConfig, NewModelConfig};
|
||||
```
|
||||
|
||||
### Step 3: Register Model
|
||||
|
||||
Update `src/models/mod.rs`:
|
||||
|
||||
```rust
|
||||
// Add to imports
|
||||
pub mod newmodel;
|
||||
|
||||
// Add to WhichModel enum
|
||||
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum WhichModel {
|
||||
// ... existing models
|
||||
NewModel,
|
||||
}
|
||||
|
||||
// Add to model loading
|
||||
pub fn load_model(
|
||||
model_type: &WhichModel,
|
||||
model_path: &str,
|
||||
device: &Device,
|
||||
) -> Result<Box<dyn common::GenerateModel>> {
|
||||
match model_type {
|
||||
// ... existing models
|
||||
WhichModel::NewModel => {
|
||||
Ok(Box::new(newmodel::NewModelGenerate::init(model_path, None, None)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Update CLI
|
||||
|
||||
Update `src/main.rs` to include the new model in help text and CLI options if needed.
|
||||
|
||||
### Step 5: Add Tests
|
||||
|
||||
Create `tests/test_newmodel.rs`:
|
||||
|
||||
```rust
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_newmodel_generate() -> Result<()> {
|
||||
let model_path = "path/to/test/model";
|
||||
let mut model = aha::models::newmodel::NewModelGenerate::init(model_path, None, None)?;
|
||||
|
||||
let result = model.generate("Test prompt")?;
|
||||
assert!(!result.is_empty());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Update Documentation
|
||||
|
||||
Update the following files:
|
||||
- `README.md` - Add model to supported models list
|
||||
- `docs/cli.md` - Add model ID to model list
|
||||
- `docs/api.md` - Add model to supported models section
|
||||
- `CHANGELOG.md` - Add entry for new model
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
cargo test
|
||||
|
||||
# Run specific test
|
||||
cargo test test_newmodel_generate
|
||||
|
||||
# Run with output
|
||||
cargo test -- --nocapture
|
||||
|
||||
# Run tests in parallel
|
||||
cargo test -- --test-threads=4
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
Integration tests are located in `tests/`:
|
||||
|
||||
```bash
|
||||
# Run specific integration test
|
||||
cargo test --test test_qwen3vl
|
||||
```
|
||||
|
||||
### Manual Testing
|
||||
|
||||
Test the CLI and API manually:
|
||||
|
||||
```bash
|
||||
# Test CLI
|
||||
cargo run -- -m newmodel
|
||||
|
||||
# Test API
|
||||
cargo run -- -m newmodel -p 8080
|
||||
curl http://127.0.0.1:8080/chat/completions -d '{...}'
|
||||
```
|
||||
|
||||
### Model-Specific Tests
|
||||
|
||||
Each model should have tests covering:
|
||||
- Model loading
|
||||
- Basic inference
|
||||
- Edge cases
|
||||
- Error handling
|
||||
|
||||
## Code Style
|
||||
|
||||
### Formatting
|
||||
|
||||
```bash
|
||||
# Format all code
|
||||
cargo fmt
|
||||
|
||||
# Check formatting without making changes
|
||||
cargo fmt --check
|
||||
```
|
||||
|
||||
### Linting
|
||||
|
||||
```bash
|
||||
# Run clippy
|
||||
cargo clippy
|
||||
|
||||
# Fix clippy warnings
|
||||
cargo clippy --fix
|
||||
```
|
||||
|
||||
### Conventions
|
||||
|
||||
- Use 4 spaces for indentation
|
||||
- Prefer `Result<T>` over `Option<T>` for errors
|
||||
- Use `anyhow::Result` for application errors
|
||||
- Document public APIs with rustdoc comments
|
||||
- Keep functions focused and small
|
||||
- Use meaningful variable names
|
||||
|
||||
### Documentation
|
||||
|
||||
```rust
|
||||
/// Generates text using the NewModel.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `prompt` - The input prompt text
|
||||
/// * `max_tokens` - Maximum tokens to generate
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Generated text as a String
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// let result = model.generate("Hello")?;
|
||||
/// ```
|
||||
pub fn generate(&mut self, prompt: &str) -> Result<String> {
|
||||
// Implementation
|
||||
}
|
||||
```
|
||||
|
||||
## Submitting Contributions
|
||||
|
||||
### Pull Request Process
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch:
|
||||
```bash
|
||||
git checkout -b feature/new-model
|
||||
```
|
||||
3. Make your changes
|
||||
4. Add tests
|
||||
5. Update documentation
|
||||
6. Ensure all tests pass:
|
||||
```bash
|
||||
cargo test
|
||||
cargo clippy
|
||||
cargo fmt --check
|
||||
```
|
||||
7. Commit and push:
|
||||
```bash
|
||||
git commit -m "Add NewModel support"
|
||||
git push origin feature/new-model
|
||||
```
|
||||
8. Create a pull request on GitHub
|
||||
|
||||
### Pull Request Checklist
|
||||
|
||||
- [ ] Tests added/updated
|
||||
- [ ] Documentation updated
|
||||
- [ ] CHANGELOG.md updated
|
||||
- [ ] Code formatted (`cargo fmt`)
|
||||
- [ ] Clippy warnings fixed (`cargo clippy`)
|
||||
- [ ] All tests pass (`cargo test`)
|
||||
- [ ] Commit messages follow conventions
|
||||
|
||||
### Commit Message Conventions
|
||||
|
||||
Follow conventional commits:
|
||||
|
||||
```
|
||||
feat: add NewModel support
|
||||
fix: correct tensor dimensions in Qwen3VL
|
||||
docs: update installation guide
|
||||
test: add integration test for VoxCPM
|
||||
refactor: simplify model loading logic
|
||||
perf: improve inference speed by 20%
|
||||
```
|
||||
|
||||
## Release Process
|
||||
|
||||
### Version Bump
|
||||
|
||||
Update `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
version = "0.1.9" # Bump version
|
||||
```
|
||||
|
||||
### Update Changelog
|
||||
|
||||
Add entry to `CHANGELOG.md`:
|
||||
|
||||
```markdown
|
||||
## [0.1.9] - 2025-02-06
|
||||
|
||||
### Added
|
||||
- NewModel support
|
||||
|
||||
### Fixed
|
||||
- Memory leak in image processing
|
||||
|
||||
### Changed
|
||||
- Improved error messages
|
||||
```
|
||||
|
||||
### Create Release
|
||||
|
||||
```bash
|
||||
# Tag the release
|
||||
git tag -a v0.1.9 -m "Release v0.1.9"
|
||||
git push origin v0.1.9
|
||||
|
||||
# Build release artifacts
|
||||
cargo build --release
|
||||
|
||||
# Publish to crates.io (if applicable)
|
||||
cargo publish
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Logging
|
||||
|
||||
Enable debug logging:
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug cargo run -- -m qwen3-0.6b
|
||||
```
|
||||
|
||||
Set specific module logging:
|
||||
|
||||
```bash
|
||||
RUST_LOG=aha::models::qwen3vl=debug cargo run -- -m qwen3-0.6b
|
||||
```
|
||||
|
||||
### Debugging Tests
|
||||
|
||||
```bash
|
||||
# Print test output
|
||||
cargo test -- --nocapture
|
||||
|
||||
# Show backtrace
|
||||
RUST_BACKTRACE=1 cargo test
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Build Errors
|
||||
|
||||
- **Linking errors**: Install required system dependencies
|
||||
- **CUDA errors**: Ensure CUDA toolkit is installed
|
||||
- **Metal errors**: Check you're on Apple Silicon
|
||||
|
||||
#### Runtime Errors
|
||||
|
||||
- **Model not found**: Check model path and download
|
||||
- **Out of memory**: Use smaller model or enable GPU
|
||||
- **Slow inference**: Enable GPU acceleration
|
||||
|
||||
## Resources
|
||||
|
||||
- [Candle Documentation](https://github.com/huggingface/candle)
|
||||
- [Rust Guidelines](https://rust-lang.github.io/api-guidelines/)
|
||||
- [Conventional Commits](https://www.conventionalcommits.org/)
|
||||
|
||||
## See Also
|
||||
|
||||
- [Architecture & Design](./concepts.md) - How AHA works
|
||||
- [Installation](./installation.md) - Setup guide
|
||||
- [API Reference](./api.md) - API documentation
|
||||
Reference in New Issue
Block a user