aha 0.2.5

aha model inference library, now supports Qwen(2.5VL/3/3VL/3.5/ASR/3Embedding/3Reranker), MiniCPM4, VoxCPM/1.5, DeepSeek-OCR/2, Hunyuan-OCR, PaddleOCR-VL/1.5, RMBG2.0, GLM(ASR-Nano-2512/OCR), Fun-ASR-Nano-2512, LFM(2/2.5/2VL/2.5VL)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
# 开发指南

本指南涵盖为 AHA 做贡献,包括开发设置、添加新模型和提交贡献。

## 目录

- [开发设置]#开发设置
- [项目结构]#项目结构
- [添加新模型]#添加新模型
- [测试]#测试
- [代码风格]#代码风格
- [提交贡献]#提交贡献
- [发布流程]#发布流程

## 开发设置

### 前置要求

- Rust 1.85+ (edition 2024)
- Git
- 可选:CUDA Toolkit、Metal、FFmpeg(用于功能开发)

### 克隆和构建

```bash
# 克隆仓库
git clone https://github.com/jhqxxx/aha.git
cd aha

# 调试模式构建(编译更快)
cargo build

# 发布模式构建(性能更好)
cargo build --release

# 使用功能构建
cargo build --features cuda,flash-attn
cargo build --features metal
cargo build --features ffmpeg
```

### 开发工作流程

```bash
# 运行 CLI
cargo run -- -m Qwen/Qwen3-0.6B

# 运行测试
cargo test

# 运行特定测试
cargo test test_qwen3vl_generate

# 启用日志运行
RUST_LOG=debug cargo run -- -m Qwen/Qwen3-0.6B

# 检查代码而不构建
cargo check

# 格式化代码
cargo fmt

# 运行 linter
cargo clippy
```

## 项目结构

```
aha/
├── Cargo.toml              # 构建配置
├── src/
│   ├── main.rs             # CLI 入口点
│   ├── lib.rs              # 库导出
│   ├── api.rs              # HTTP API 处理程序
│   ├── exec/               # CLI 命令实现
│   │   ├── mod.rs
│   │   ├── cli.rs
│   │   ├── serv.rs
│   │   ├── download.rs
│   │   └── run.rs
│   ├── models/             # 模型实现
│   │   ├── mod.rs          # 模型工厂和导出
│   │   ├── common/         # 共享模型工具
│   │   │   ├── config.rs
│   │   │   └── mod.rs
│   │   ├── qwen3vl/        # Qwen3-VL 模型
│   │   │   ├── config.rs
│   │   │   ├── model.rs
│   │   │   ├── generate.rs
│   │   │   ├── processor.rs
│   │   │   └── mod.rs
│   │   ├── voxcpm/         # VoxCPM 模型
│   │   └── ...             # 其他模型
│   ├── tokenizer/          # 分词工具
│   ├── chat_template/      # 聊天模板处理
│   ├── position_embed/     # 位置编码
│   └── utils/              # 工具函数
│       ├── audio_utils.rs
│       ├── image_utils.rs
│       ├── tensor_utils.rs
│       └── mod.rs
├── tests/                  # 集成测试
│   ├── test_qwen2_5vl.rs
│   ├── test_qwen3vl.rs
│   └── ...
└── docs/                   # 文档
```

## 添加新模型

本节提供了向 AHA 添加新模型的分步指南。

### 步骤 1:创建模型目录

在 `src/models/` 下创建新目录:

```bash
mkdir -p src/models/newmodel
```

### 步骤 2:实现模型文件

在 `src/models/newmodel/` 中创建以下文件:

#### config.rs

定义模型配置:

```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,
    // ... 其他配置字段
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerationConfig {
    pub temperature: f64,
    pub top_p: f64,
    pub max_tokens: usize,
}

```

#### model.rs

实现模型架构:

```rust
use candle::{Result, Tensor};
use candle_nn::{Module, VarBuilder};

use super::config::NewModelConfig;

pub struct NewModel {
    // 定义模型组件
    config: NewModelConfig,
}

impl NewModel {
    pub fn load(vb: VarBuilder, config: &NewModelConfig) -> Result<Self> {
        // 加载模型权重
        Ok(Self {
            config: config.clone(),
        })
    }

    pub fn forward(&self, input_ids: &Tensor) -> Result<Tensor> {
        // 实现前向传播
        Ok(input_ids.clone())
    }
}
```

#### generate.rs

实现 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 {
    // 定义生成状态
    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> {
        // 从磁盘加载模型
        let device = Device::Cpu;
        let config = config.unwrap_or_default();
        let generate_config = generate_config.unwrap_or_default();

        // 加载权重
        let model_path = Path::new(model_path);
        // ... 加载模型实现

        Ok(Self {
            model: todo!(),
            config: generate_config,
            device,
        })
    }

    fn generate(&mut self, prompt: &str) -> Result<String> {
        // 分词提示
        // 运行推理
        // 解码输出
        Ok(prompt.to_string())
    }
}
```

#### processor.rs(可选)

对于具有多模态输入的复杂模型:

```rust
use anyhow::Result;

pub struct NewModelProcessor {
    // 处理状态
}

impl NewModelProcessor {
    pub fn new() -> Self {
        Self {}
    }

    pub fn process_text(&self, text: &str) -> Result<Vec<String>> {
        // 处理文本输入
        Ok(vec![text.to_string()])
    }

    pub fn process_image(&self, image_path: &str) -> Result<Tensor> {
        // 处理图像输入
        todo!()
    }
}
```

#### mod.rs

导出模型:

```rust
mod config;
mod model;
mod generate;
pub mod processor;

pub use generate::NewModelGenerate;
pub use config::{GenerationConfig, NewModelConfig};
```

### 步骤 3:注册模型

更新 `src/models/mod.rs`:

```rust
// 添加到导入
pub mod newmodel;

// 添加到 WhichModel 枚举
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
pub enum WhichModel {
    // ... 现有模型
    NewModel,
}

// 添加到模型加载
pub fn load_model(
    model_type: &WhichModel,
    model_path: &str,
    device: &Device,
) -> Result<Box<dyn common::GenerateModel>> {
    match model_type {
        // ... 现有模型
        WhichModel::NewModel => {
            Ok(Box::new(newmodel::NewModelGenerate::init(model_path, None, None)?))
        }
    }
}
```

### 步骤 4:更新 CLI

更新 `src/main.rs` 以在帮助文本和 CLI 选项中包含新模型(如果需要)。

### 步骤 5:添加测试

创建 `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("测试提示")?;
    assert!(!result.is_empty());
    
    Ok(())
}
```

### 步骤 6:更新文档

更新以下文件:
- `README.md` - 将模型添加到支持的模型列表
- `docs/cli.md` - 将模型 ID 添加到模型列表
- `docs/api.md` - 将模型添加到支持的模型部分
- `CHANGELOG.md` - 为新模型添加条目

## 测试

### 单元测试

```bash
# 运行所有测试
cargo test

# 运行特定测试
cargo test test_newmodel_generate

# 启用输出运行
cargo test -- --nocapture

# 并行运行测试
cargo test -- --test-threads=4
```

### 集成测试

集成测试位于 `tests/` 中:

```bash
# 运行特定集成测试
cargo test --test test_qwen3vl
```

### 手动测试

手动测试 CLI 和 API:

```bash
# 测试 CLI
cargo run -- -m newmodel

# 测试 API
cargo run -- -m newmodel -p 8080
curl http://127.0.0.1:8080/chat/completions -d '{...}'
```

### 模型特定测试

每个模型都应该有涵盖以下内容的测试:
- 模型加载
- 基本推理
- 边缘情况
- 错误处理

## 代码风格

### 格式化

```bash
# 格式化所有代码
cargo fmt

# 检查格式化而不进行更改
cargo fmt --check
```

### Linting

```bash
# 运行 clippy
cargo clippy

# 修复 clippy 警告
cargo clippy --fix
```

### 约定

- 使用 4 个空格缩进
- 对于错误,优先使用 `Result<T>` 而不是 `Option<T>`
- 对于应用程序错误使用 `anyhow::Result`
- 使用 rustdoc 注释记录公共 API
- 保持函数专注和小型
- 使用有意义的变量名

### 文档

```rust
/// 使用 NewModel 生成文本。
///
/// # 参数
///
/// * `prompt` - 输入提示文本
/// * `max_tokens` - 要生成的最大令牌数
///
/// # 返回
///
/// 生成的文本作为字符串
///
/// # 示例
///
/// ```no_run
/// let result = model.generate("你好")?;
/// ```
pub fn generate(&mut self, prompt: &str) -> Result<String> {
    // 实现
}
```

## 提交贡献

### Pull Request 流程

1. Fork 仓库
2. 创建功能分支:
   ```bash
   git checkout -b feature/new-model
   ```
3. 进行更改
4. 添加测试
5. 更新文档
6. 确保所有测试通过:
   ```bash
   cargo test
   cargo clippy
   cargo fmt --check
   ```
7. 提交并推送:
   ```bash
   git commit -m "添加 NewModel 支持"
   git push origin feature/new-model
   ```
8. 在 GitHub 上创建 pull request

### Pull Request 清单

- [ ] 已添加/更新测试
- [ ] 已更新文档
- [ ] 已更新 CHANGELOG.md
- [ ] 代码已格式化(`cargo fmt`- [ ] 已修复 Clippy 警告(`cargo clippy`- [ ] 所有测试通过(`cargo test`- [ ] 提交消息遵循约定

### 提交消息约定

遵循约定式提交:

```
feat: 添加 NewModel 支持
fix: 修正 Qwen3VL 中的张量维度
docs: 更新安装指南
test: 添加 NewModel 集成测试
refactor: 简化模型加载逻辑
perf: 将推理速度提高 20%
```

## 发布流程

### 版本升级

更新 `Cargo.toml`:

```toml
[package]
version = "0.1.9"  # 升级版本
```

### 更新变更日志

将条目添加到 `CHANGELOG.md`:

```markdown
## [0.1.9] - 2025-02-06

### 新增
- NewModel 支持

### 修复
- 图像处理中的内存泄漏

### 更改
- 改进错误消息
```

### 创建发布

```bash
# 标记发布
git tag -a v0.1.9 -m "Release v0.1.9"
git push origin v0.1.9

# 构建发布产物
cargo build --release

# 发布到 crates.io(如果适用)
cargo publish
```

## 调试

### 日志

启用调试日志:

```bash
RUST_LOG=debug cargo run -- -m Qwen/Qwen3-0.6B
```

设置特定模块日志:

```bash
RUST_LOG=aha::models::qwen3vl=debug cargo run -- -m Qwen/Qwen3-0.6B
```

### 调试测试

```bash
# 打印测试输出
cargo test -- --nocapture

# 显示回溯
RUST_BACKTRACE=1 cargo test
```

### 常见问题

#### 构建错误

- **链接错误**:安装所需的系统依赖
- **CUDA 错误**:确保已安装 CUDA toolkit
- **Metal 错误**:检查您是否在 Apple Silicon 上

#### 运行时错误

- **未找到模型**:检查模型路径和下载
- **内存不足**:使用更小的模型或启用 GPU
- **推理速度慢**:启用 GPU 加速

## 资源

- [Candle 文档]https://github.com/huggingface/candle
- [Rust 指南]https://rust-lang.github.io/api-guidelines/
- [约定式提交]https://www.conventionalcommits.org/

## 另见

- [架构与设计]./concepts.zh-CN.md - AHA 的工作原理
- [安装]./installation.zh-CN.md - 设置指南
- [API 参考]./api.zh-CN.md - API 文档