CLI update: subcommand must be specified
This commit is contained in:
@@ -47,8 +47,9 @@ aha is a high-performance, cross-platform AI inference engine built with Rust an
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
### 2026-04-03
|
### 2026-04-03
|
||||||
|
- CLI update: subcommand must be specified
|
||||||
- ChatCompletionParameters add repeat_penalty and repeat_last_n
|
- ChatCompletionParameters add repeat_penalty and repeat_last_n
|
||||||
- generate add penalty repeat code
|
- generate add penalty repeat
|
||||||
|
|
||||||
### 2026-04-02
|
### 2026-04-02
|
||||||
- refactor generate code
|
- refactor generate code
|
||||||
@@ -113,7 +114,7 @@ aha list
|
|||||||
aha download -m Qwen/Qwen3-ASR-0.6B
|
aha download -m Qwen/Qwen3-ASR-0.6B
|
||||||
|
|
||||||
# Download model and start service
|
# Download model and start service
|
||||||
aha -m Qwen/Qwen3-ASR-0.6B
|
aha cli -m Qwen/Qwen3-ASR-0.6B
|
||||||
|
|
||||||
# Run inference directly (without starting service)
|
# Run inference directly (without starting service)
|
||||||
aha run -m Qwen/Qwen3-ASR-0.6B -i "audio.wav"
|
aha run -m Qwen/Qwen3-ASR-0.6B -i "audio.wav"
|
||||||
|
|||||||
+2
-4
@@ -47,6 +47,7 @@ aha 是一款基于 Rust 和 Candle 框架构建的高性能跨平台 AI 推理
|
|||||||
## 更新日志
|
## 更新日志
|
||||||
## Changelog
|
## Changelog
|
||||||
### 2026-04-03
|
### 2026-04-03
|
||||||
|
- CLI 更新: 必须指定子命令
|
||||||
- ChatCompletionParameters 新增 repeat_penalty 和 repeat_last_n 参数
|
- ChatCompletionParameters 新增 repeat_penalty 和 repeat_last_n 参数
|
||||||
- 生成添加重复惩罚代码
|
- 生成添加重复惩罚代码
|
||||||
|
|
||||||
@@ -73,9 +74,6 @@ aha 是一款基于 Rust 和 Candle 框架构建的高性能跨平台 AI 推理
|
|||||||
- 新增 LFM2.5-1.2B-Instruct
|
- 新增 LFM2.5-1.2B-Instruct
|
||||||
- 新增 LFM2-1.2B
|
- 新增 LFM2-1.2B
|
||||||
|
|
||||||
### v0.2.3 (2026-03-18)
|
|
||||||
- 新增 DeepSeek-OCR-2
|
|
||||||
|
|
||||||
**[查看完整更新日志](docs/changelog.zh-CN.md)** →
|
**[查看完整更新日志](docs/changelog.zh-CN.md)** →
|
||||||
|
|
||||||
|
|
||||||
@@ -116,7 +114,7 @@ aha list
|
|||||||
aha download -m Qwen/Qwen3-ASR-0.6B
|
aha download -m Qwen/Qwen3-ASR-0.6B
|
||||||
|
|
||||||
# 下载模型并启动服务
|
# 下载模型并启动服务
|
||||||
aha -m Qwen/Qwen3-ASR-0.6B
|
aha cli -m Qwen/Qwen3-ASR-0.6B
|
||||||
|
|
||||||
# 直接运行推理(无需启动服务)
|
# 直接运行推理(无需启动服务)
|
||||||
aha run -m Qwen/Qwen3-ASR-0.6B -i "audio.wav"
|
aha run -m Qwen/Qwen3-ASR-0.6B -i "audio.wav"
|
||||||
|
|||||||
+2
-1
@@ -6,8 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
### 2026-04-03
|
### 2026-04-03
|
||||||
|
- CLI update: subcommand must be specified
|
||||||
- ChatCompletionParameters add repeat_penalty and repeat_last_n
|
- ChatCompletionParameters add repeat_penalty and repeat_last_n
|
||||||
- generate add penalty repeat code
|
- generate add penalty repeat
|
||||||
|
|
||||||
### 2026-04-02
|
### 2026-04-02
|
||||||
- refactor generate code
|
- refactor generate code
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/spec/v2.0.0.html)。
|
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/spec/v2.0.0.html)。
|
||||||
|
|
||||||
### 2026-04-03
|
### 2026-04-03
|
||||||
|
- CLI 更新: 必须指定子命令
|
||||||
- ChatCompletionParameters 新增 repeat_penalty 和 repeat_last_n 参数
|
- ChatCompletionParameters 新增 repeat_penalty 和 repeat_last_n 参数
|
||||||
- 生成添加重复惩罚代码
|
- 生成添加重复惩罚代码
|
||||||
|
|
||||||
|
|||||||
+20
-25
@@ -18,16 +18,19 @@ aha [COMMAND] [OPTIONS]
|
|||||||
| `--weight-path <WEIGHT_PATH>` | Local model weight path | - |
|
| `--weight-path <WEIGHT_PATH>` | Local model weight path | - |
|
||||||
| `--save-dir <SAVE_DIR>` | Model download save directory | ~/.aha/ |
|
| `--save-dir <SAVE_DIR>` | Model download save directory | ~/.aha/ |
|
||||||
| `--download-retries <DOWNLOAD_RETRIES>` | Download retry count | 3 |
|
| `--download-retries <DOWNLOAD_RETRIES>` | Download retry count | 3 |
|
||||||
| `--gguf-path <GGUF_PATH>` | Local GGUF weight | - |
|
| `--gguf-path <GGUF_PATH>` | Local GGUF weight(required when using GGUF models) | - |
|
||||||
| `--mmproj-path <MMPROJ_PATH>` | Local mmproj GGUF weight | - |
|
| `--mmproj-path <MMPROJ_PATH>` | Local mmproj GGUF weight | - |
|
||||||
|
| `--onnx-path <ONNX_PATH>` | Local ONNX weight(required when using ONNX models) | - |
|
||||||
|
| `--config-path <ONNX_PATH>` | extra config path for gguf/onnx | - |
|
||||||
| `-h, --help` | Display help information | - |
|
| `-h, --help` | Display help information | - |
|
||||||
| `-V, --version` | Display version number | - |
|
| `-V, --version` | Display version number | - |
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
### cli - Download model and start service (default)
|
### cli - Download model and start service
|
||||||
|
|
||||||
Download the specified model and start an HTTP service. This command is used by default when no subcommand is specified.
|
Download the specified model and start an HTTP service.
|
||||||
|
Download only supports models in safetensors format; for GGUF/ONNX models, you must specify a local file path.
|
||||||
|
|
||||||
**Syntax:**
|
**Syntax:**
|
||||||
```bash
|
```bash
|
||||||
@@ -44,8 +47,10 @@ aha cli [OPTIONS] --model <MODEL>
|
|||||||
| `--weight-path <WEIGHT_PATH>` | Local model weight path (skip download if specified) | - |
|
| `--weight-path <WEIGHT_PATH>` | Local model weight path (skip download if specified) | - |
|
||||||
| `--save-dir <SAVE_DIR>` | Model download save directory | ~/.aha/ |
|
| `--save-dir <SAVE_DIR>` | Model download save directory | ~/.aha/ |
|
||||||
| `--download-retries <DOWNLOAD_RETRIES>` | Download retry count | 3 |
|
| `--download-retries <DOWNLOAD_RETRIES>` | Download retry count | 3 |
|
||||||
| `--gguf-path <GGUF_PATH>` | Local GGUF weight | - |
|
| `--gguf-path <GGUF_PATH>` | Local GGUF weight(required when using GGUF models) | - |
|
||||||
| `--mmproj-path <MMPROJ_PATH>` | Local mmproj GGUF weight | - |
|
| `--mmproj-path <MMPROJ_PATH>` | Local mmproj GGUF weight | - |
|
||||||
|
| `--onnx-path <ONNX_PATH>` | Local ONNX weight(required when using ONNX models) | - |
|
||||||
|
| `--config-path <ONNX_PATH>` | extra config path for gguf/onnx | - |
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
|
|
||||||
@@ -59,9 +64,6 @@ aha cli -m Qwen/Qwen3-VL-2B-Instruct -p 8080 --save-dir /data/models
|
|||||||
# Use local model (skip download)
|
# Use local model (skip download)
|
||||||
aha cli -m Qwen/Qwen3-VL-2B-Instruct --weight-path /path/to/model
|
aha cli -m Qwen/Qwen3-VL-2B-Instruct --weight-path /path/to/model
|
||||||
|
|
||||||
# Backward compatible way (equivalent to cli subcommand)
|
|
||||||
aha -m Qwen/Qwen3-VL-2B-Instruct
|
|
||||||
|
|
||||||
# use gguf-path and mmproj-path
|
# use gguf-path and mmproj-path
|
||||||
aha cli -m qwen3.5-gguf --gguf-path /path/to/xxx.gguf --mmproj-path /path/to/mmproj-xxx.gguf
|
aha cli -m qwen3.5-gguf --gguf-path /path/to/xxx.gguf --mmproj-path /path/to/mmproj-xxx.gguf
|
||||||
```
|
```
|
||||||
@@ -72,7 +74,7 @@ Run model inference directly without starting an HTTP service. Suitable for one-
|
|||||||
|
|
||||||
**Syntax:**
|
**Syntax:**
|
||||||
```bash
|
```bash
|
||||||
aha run [OPTIONS] --model <MODEL> --input <INPUT> [--input <INPUT2>] [--weight-path <WEIGHT_PATH>] [--gguf-path <GGUF_PATH>] [--mmproj-path <MMPROJ_PATH>]
|
aha run [OPTIONS] --model <MODEL> --input <INPUT> [--input <INPUT2>] [--weight-path <WEIGHT_PATH>] [--gguf-path <GGUF_PATH>] [--mmproj-path <MMPROJ_PATH>] [--onnx-path <ONNX_PATH>] [--config-path <CONFIG_PATH>]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Options:**
|
**Options:**
|
||||||
@@ -82,9 +84,12 @@ aha run [OPTIONS] --model <MODEL> --input <INPUT> [--input <INPUT2>] [--weight-p
|
|||||||
| `-m, --model <MODEL>` | Model type (required) | - |
|
| `-m, --model <MODEL>` | Model type (required) | - |
|
||||||
| `-i, --input <INPUT>` | Input text or file path (model-specific interpretation, supports 1-2 parameters: input1: prompt text, input2: file path) | - |
|
| `-i, --input <INPUT>` | Input text or file path (model-specific interpretation, supports 1-2 parameters: input1: prompt text, input2: file path) | - |
|
||||||
| `-o, --output <OUTPUT>` | Output file path (optional, auto-generated if not specified) | - |
|
| `-o, --output <OUTPUT>` | Output file path (optional, auto-generated if not specified) | - |
|
||||||
| `--weight-path <WEIGHT_PATH>` | Local model weight path (required when using non-GGUF models) | - |
|
| `--weight-path <WEIGHT_PATH>` | Local model weight path (required when using safetensors models) | - |
|
||||||
| `--gguf-path <GGUF_PATH>` | Local GGUF model weight path(required when using GGUF models) | - |
|
| `--gguf-path <GGUF_PATH>` | Local GGUF model weight path(required when using GGUF models) | - |
|
||||||
| `--mmproj-path <MMPROJ_PATH>` | Local mmproj GGUF weight path(optional,If not specified, the module will not be loaded) | - |
|
| `--mmproj-path <MMPROJ_PATH>` | Local mmproj GGUF weight path(optional,If not specified, the module will not be loaded) | - |
|
||||||
|
| `--onnx-path <ONNX_PATH>` | Local ONNX weight(required when using ONNX models) | - |
|
||||||
|
| `--config-path <ONNX_PATH>` | extra config path for gguf/onnx | - |
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -129,11 +134,13 @@ aha run -m qwen3.5-gguf -i 提取图片中的文本 -i https://ai.bdstatic.com/f
|
|||||||
|
|
||||||
### serv - Start service
|
### serv - Start service
|
||||||
|
|
||||||
Start HTTP service with a model. The `--weight-path` is optional - if not specified, it defaults to `~/.aha/{model_id}`.
|
Start HTTP service with a model.
|
||||||
|
Safetensors model: The `--weight-path` is optional - if not specified, it defaults to `~/.aha/{model_id}`.
|
||||||
|
GGUF/ONNX model: The `--gguf-path`/ `--onnx-path` must be specified
|
||||||
|
|
||||||
**Syntax:**
|
**Syntax:**
|
||||||
```bash
|
```bash
|
||||||
aha serv [OPTIONS] --model <MODEL> [--weight-path <WEIGHT_PATH>] [--gguf-path <GGUF_PATH>] [--mmproj-path <MMPROJ_PATH>]
|
aha serv [OPTIONS] --model <MODEL> [--weight-path <WEIGHT_PATH>] [--gguf-path <GGUF_PATH>] [--mmproj-path <MMPROJ_PATH>] [--onnx-path <ONNX_PATH>] [--config-path <CONFIG_PATH>]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Options:**
|
**Options:**
|
||||||
@@ -336,7 +343,7 @@ Example:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# One command to download and start service
|
# One command to download and start service
|
||||||
aha -m Qwen/Qwen3-VL-2B-Instruct
|
aha cli -m Qwen/Qwen3-VL-2B-Instruct
|
||||||
```
|
```
|
||||||
|
|
||||||
### Scenario 2: Start service with existing model
|
### Scenario 2: Start service with existing model
|
||||||
@@ -360,7 +367,7 @@ aha serv -m Qwen/Qwen3-VL-2B-Instruct --weight-path /data/models/Qwen/Qwen3-VL-2
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Start service on 0.0.0.0:8080, allow external access
|
# Start service on 0.0.0.0:8080, allow external access
|
||||||
aha -m Qwen/Qwen3-VL-2B-Instruct -a 0.0.0.0 -p 8080
|
aha cli -m Qwen/Qwen3-VL-2B-Instruct -a 0.0.0.0 -p 8080
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
@@ -395,18 +402,6 @@ After the service starts, the following API endpoints are available:
|
|||||||
- **Format**: JSON response
|
- **Format**: JSON response
|
||||||
|
|
||||||
|
|
||||||
## Backward Compatibility
|
|
||||||
|
|
||||||
To maintain compatibility with older versions, the following two usage methods are equivalent:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# New way (recommended)
|
|
||||||
aha cli -m Qwen/Qwen3-VL-2B-Instruct
|
|
||||||
|
|
||||||
# Old way (backward compatible)
|
|
||||||
aha -m Qwen/Qwen3-VL-2B-Instruct
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
1. **serv subcommand requires `--weight-path`**: Since the `serv` subcommand does not download models, you must specify the path to an already downloaded model via `--weight-path`.
|
1. **serv subcommand requires `--weight-path`**: Since the `serv` subcommand does not download models, you must specify the path to an already downloaded model via `--weight-path`.
|
||||||
|
|||||||
+24
-27
@@ -15,19 +15,22 @@ aha [COMMAND] [OPTIONS]
|
|||||||
| `-a, --address <ADDRESS>` | 服务监听地址 | 127.0.0.1 |
|
| `-a, --address <ADDRESS>` | 服务监听地址 | 127.0.0.1 |
|
||||||
| `-p, --port <PORT>` | 服务监听端口 | 10100 |
|
| `-p, --port <PORT>` | 服务监听端口 | 10100 |
|
||||||
| `-m, --model <MODEL>` | 模型类型(必选) | - |
|
| `-m, --model <MODEL>` | 模型类型(必选) | - |
|
||||||
| `--weight-path <WEIGHT_PATH>` | 本地模型权重路径 | - |
|
| `--weight-path <WEIGHT_PATH>` | 本地safetensors模型权重路径 | - |
|
||||||
| `--save-dir <SAVE_DIR>` | 模型下载保存目录 | ~/.aha/ |
|
| `--save-dir <SAVE_DIR>` | 模型下载保存目录 | ~/.aha/ |
|
||||||
| `--download-retries <DOWNLOAD_RETRIES>` | 下载重试次数 | 3 |
|
| `--download-retries <DOWNLOAD_RETRIES>` | 下载重试次数 | 3 |
|
||||||
| `--gguf-path <GGUF_PATH>` | 本地 GGUF 模型权重 | - |
|
| `--gguf-path <GGUF_PATH>` | 本地 GGUF 模型权重(使用GGUF模型时必选) | - |
|
||||||
| `--mmproj-path <MMPROJ_PATH>` | 本地 mmproj GGUF 模型权重 | - |
|
| `--mmproj-path <MMPROJ_PATH>` | 本地 mmproj GGUF 模型权重(可选,未指定则不加载该模块) | - |
|
||||||
|
| `--onnx-path <ONNX_PATH>` | 本地 ONNX 模型权重 (使用ONNX模型时必选) | - |
|
||||||
|
| `--config-path <ONNX_PATH>` | GGUF/ONNX 需要的额外配置路径(可选) | - |
|
||||||
| `-h, --help` | 显示帮助信息 | - |
|
| `-h, --help` | 显示帮助信息 | - |
|
||||||
| `-V, --version` | 显示版本号 | - |
|
| `-V, --version` | 显示版本号 | - |
|
||||||
|
|
||||||
## 子命令
|
## 子命令
|
||||||
|
|
||||||
### cli - 下载模型并启动服务(默认)
|
### cli - 下载模型并启动服务
|
||||||
|
|
||||||
下载指定的模型并启动 HTTP 服务。当不指定子命令时,默认使用此命令。
|
下载指定的模型并启动 HTTP 服务。
|
||||||
|
下载仅支持safetensors格式模型, GGUF/ONNX模型必须指定本地文件路径
|
||||||
|
|
||||||
**语法:**
|
**语法:**
|
||||||
```bash
|
```bash
|
||||||
@@ -41,11 +44,13 @@ aha cli [OPTIONS] --model <MODEL>
|
|||||||
| `-a, --address <ADDRESS>` | 服务监听地址 | 127.0.0.1 |
|
| `-a, --address <ADDRESS>` | 服务监听地址 | 127.0.0.1 |
|
||||||
| `-p, --port <PORT>` | 服务监听端口 | 10100 |
|
| `-p, --port <PORT>` | 服务监听端口 | 10100 |
|
||||||
| `-m, --model <MODEL>` | 模型类型(必选) | - |
|
| `-m, --model <MODEL>` | 模型类型(必选) | - |
|
||||||
| `--weight-path <WEIGHT_PATH>` | 本地模型权重路径(如指定则跳过下载) | - |
|
| `--weight-path <WEIGHT_PATH>` | 本地safetensors模型权重路径(如指定则跳过下载) | - |
|
||||||
| `--save-dir <SAVE_DIR>` | 模型下载保存目录 | ~/.aha/ |
|
| `--save-dir <SAVE_DIR>` | 模型下载保存目录 | ~/.aha/ |
|
||||||
| `--download-retries <DOWNLOAD_RETRIES>` | 下载重试次数 | 3 |
|
| `--download-retries <DOWNLOAD_RETRIES>` | 下载重试次数 | 3 |
|
||||||
| `--gguf-path <GGUF_PATH>` | 本地 GGUF 模型权重 | - |
|
| `--gguf-path <GGUF_PATH>` | 本地 GGUF 模型权重(使用GGUF模型时必选) | - |
|
||||||
| `--mmproj-path <MMPROJ_PATH>` | 本地 mmproj GGUF 模型权重 | - |
|
| `--mmproj-path <MMPROJ_PATH>` | 本地 mmproj GGUF 模型权重(可选,未指定则不加载该模块) | - |
|
||||||
|
| `--onnx-path <ONNX_PATH>` | 本地 ONNX 模型权重 (使用ONNX模型时必选) | - |
|
||||||
|
| `--config-path <ONNX_PATH>` | GGUF/ONNX 需要的额外配置路径(可选) | - |
|
||||||
|
|
||||||
**示例:**
|
**示例:**
|
||||||
|
|
||||||
@@ -59,9 +64,6 @@ aha cli -m Qwen/Qwen3-VL-2B-Instruct -p 8080 --save-dir /data/models
|
|||||||
# 使用本地模型(不下载)
|
# 使用本地模型(不下载)
|
||||||
aha cli -m Qwen/Qwen3-VL-2B-Instruct --weight-path /path/to/model
|
aha cli -m Qwen/Qwen3-VL-2B-Instruct --weight-path /path/to/model
|
||||||
|
|
||||||
# 向后兼容方式(等同于 cli 子命令)
|
|
||||||
aha -m Qwen/Qwen3-VL-2B-Instruct
|
|
||||||
|
|
||||||
# 指定gguf-path和mmproj-path
|
# 指定gguf-path和mmproj-path
|
||||||
aha cli -m qwen3.5-gguf --gguf-path /path/to/xxx.gguf --mmproj-path /path/to/mmproj-xxx.gguf
|
aha cli -m qwen3.5-gguf --gguf-path /path/to/xxx.gguf --mmproj-path /path/to/mmproj-xxx.gguf
|
||||||
```
|
```
|
||||||
@@ -72,7 +74,7 @@ aha cli -m qwen3.5-gguf --gguf-path /path/to/xxx.gguf --mmproj-path /path/to/mmp
|
|||||||
|
|
||||||
**语法:**
|
**语法:**
|
||||||
```bash
|
```bash
|
||||||
aha run [OPTIONS] --model <MODEL> --input <INPUT> [--input <INPUT2>] [--weight-path <WEIGHT_PATH>] [--gguf-path <GGUF_PATH>] [--mmproj-path <MMPROJ_PATH>]
|
aha run [OPTIONS] --model <MODEL> --input <INPUT> [--input <INPUT2>] [--weight-path <WEIGHT_PATH>] [--gguf-path <GGUF_PATH>] [--mmproj-path <MMPROJ_PATH>] [--onnx-path <ONNX_PATH>] [--config-path <CONFIG_PATH>]
|
||||||
```
|
```
|
||||||
|
|
||||||
**选项:**
|
**选项:**
|
||||||
@@ -82,9 +84,11 @@ aha run [OPTIONS] --model <MODEL> --input <INPUT> [--input <INPUT2>] [--weight-p
|
|||||||
| `-m, --model <MODEL>` | 模型类型(必选) | - |
|
| `-m, --model <MODEL>` | 模型类型(必选) | - |
|
||||||
| `-i, --input <INPUT>` | 输入文本或文件路径(模型特定解释,支持1-2个参数, input1: 提示文本, input2: 文件地址) | - |
|
| `-i, --input <INPUT>` | 输入文本或文件路径(模型特定解释,支持1-2个参数, input1: 提示文本, input2: 文件地址) | - |
|
||||||
| `-o, --output <OUTPUT>` | 输出文件路径(可选,未指定则自动生成) | - |
|
| `-o, --output <OUTPUT>` | 输出文件路径(可选,未指定则自动生成) | - |
|
||||||
| `--weight-path <WEIGHT_PATH>` | 本地模型权重路径(使用非GGUF模型时必选) | - |
|
| `--weight-path <WEIGHT_PATH>` | 本地模型权重路径(使用Safetensors模型时必选) | - |
|
||||||
| `--gguf-path <GGUF_PATH>` | 本地GGUF模型权重路径(使用GGUF模型时必选) | - |
|
| `--gguf-path <GGUF_PATH>` | 本地GGUF模型权重路径(使用GGUF模型时必选) | - |
|
||||||
| `--mmproj-path <MMPROJ_PATH>` | 本地mmproj GGUF模型权重路径(可选,未指定则不加载该模块) | - |
|
| `--mmproj-path <MMPROJ_PATH>` | 本地mmproj GGUF模型权重路径(可选,未指定则不加载该模块) | - |
|
||||||
|
| `--onnx-path <ONNX_PATH>` | 本地 ONNX 模型权重 (使用ONNX模型时必选) | - |
|
||||||
|
| `--config-path <ONNX_PATH>` | GGUF/ONNX 需要的额外配置路径(可选) | - |
|
||||||
|
|
||||||
**示例:**
|
**示例:**
|
||||||
|
|
||||||
@@ -129,7 +133,9 @@ aha run -m qwen3.5-gguf -i 提取图片中的文本 -i https://ai.bdstatic.com/f
|
|||||||
|
|
||||||
### serv - 启动服务
|
### serv - 启动服务
|
||||||
|
|
||||||
使用指定模型启动 HTTP 服务。`--weight-path` 是可选的 - 如果不指定,默认使用 `~/.aha/{model_id}`。
|
使用指定模型启动 HTTP 服务。
|
||||||
|
safetensors模型`--weight-path` 是可选的 - 如果不指定,默认使用 `~/.aha/{model_id}`。
|
||||||
|
GGUF/ONNX模型必须指定本地文件路径
|
||||||
|
|
||||||
**语法:**
|
**语法:**
|
||||||
```bash
|
```bash
|
||||||
@@ -147,6 +153,8 @@ aha serv [OPTIONS] --model <MODEL> [--weight-path <WEIGHT_PATH>] [--gguf-path <G
|
|||||||
| `--allow-remote-shutdown` | 允许远程关机请求(不推荐) | false |
|
| `--allow-remote-shutdown` | 允许远程关机请求(不推荐) | false |
|
||||||
| `--gguf-path <GGUF_PATH>` | 本地GGUF模型权重路径(使用GGUF模型时必选) | - |
|
| `--gguf-path <GGUF_PATH>` | 本地GGUF模型权重路径(使用GGUF模型时必选) | - |
|
||||||
| `--mmproj-path <MMPROJ_PATH>` | 本地mmproj GGUF模型权重路径(可选,未指定则不加载该模块) | - |
|
| `--mmproj-path <MMPROJ_PATH>` | 本地mmproj GGUF模型权重路径(可选,未指定则不加载该模块) | - |
|
||||||
|
| `--onnx-path <ONNX_PATH>` | 本地 ONNX 模型权重 (使用ONNX模型时必选) | - |
|
||||||
|
| `--config-path <ONNX_PATH>` | GGUF/ONNX 需要的额外配置路径(可选) | - |
|
||||||
|
|
||||||
**示例:**
|
**示例:**
|
||||||
|
|
||||||
@@ -336,7 +344,7 @@ aha list -j
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 一条命令下载并启动服务
|
# 一条命令下载并启动服务
|
||||||
aha -m Qwen/Qwen3-VL-2B-Instruct
|
aha cli -m Qwen/Qwen3-VL-2B-Instruct
|
||||||
```
|
```
|
||||||
|
|
||||||
### 场景 2:使用已有模型启动服务
|
### 场景 2:使用已有模型启动服务
|
||||||
@@ -360,7 +368,7 @@ aha serv -m Qwen/Qwen3-VL-2B-Instruct --weight-path /data/models/Qwen/Qwen3-VL-2
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 在 0.0.0.0:8080 启动服务,允许外部访问
|
# 在 0.0.0.0:8080 启动服务,允许外部访问
|
||||||
aha -m Qwen/Qwen3-VL-2B-Instruct -a 0.0.0.0 -p 8080
|
aha cli -m Qwen/Qwen3-VL-2B-Instruct -a 0.0.0.0 -p 8080
|
||||||
```
|
```
|
||||||
|
|
||||||
## API 接口
|
## API 接口
|
||||||
@@ -394,17 +402,6 @@ aha -m Qwen/Qwen3-VL-2B-Instruct -a 0.0.0.0 -p 8080
|
|||||||
- **安全性**: 默认仅允许本地访问,使用 `--allow-remote-shutdown` 标志启用远程访问(不推荐)
|
- **安全性**: 默认仅允许本地访问,使用 `--allow-remote-shutdown` 标志启用远程访问(不推荐)
|
||||||
- **格式**: JSON 响应
|
- **格式**: JSON 响应
|
||||||
|
|
||||||
## 向后兼容性
|
|
||||||
|
|
||||||
为了保持与旧版本的兼容性,以下两种使用方式是等效的:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 新方式(推荐)
|
|
||||||
aha cli -m Qwen/Qwen3-VL-2B-Instruct
|
|
||||||
|
|
||||||
# 旧方式(向后兼容)
|
|
||||||
aha -m Qwen/Qwen3-VL-2B-Instruct
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
|
|||||||
@@ -291,7 +291,8 @@ OpenBMB/MiniCPM4-0.5B OpenBMB llm ✔
|
|||||||
Qwen/Qwen2.5-VL-3B-Instruct Qwen vlm ✔
|
Qwen/Qwen2.5-VL-3B-Instruct Qwen vlm ✔
|
||||||
Qwen/Qwen2.5-VL-7B-Instruct Qwen vlm
|
Qwen/Qwen2.5-VL-7B-Instruct Qwen vlm
|
||||||
Qwen/Qwen3-0.6B Qwen llm ✔
|
Qwen/Qwen3-0.6B Qwen llm ✔
|
||||||
Qwen/Qwen3-1.7B Qwen llm ✔
|
Qwen/Qwen3-1.7B Qwen llm
|
||||||
|
Qwen/Qwen3-4B Qwen llm
|
||||||
Qwen/Qwen3.5-0.8B Qwen vlm ✔
|
Qwen/Qwen3.5-0.8B Qwen vlm ✔
|
||||||
Qwen/Qwen3.5-2B Qwen vlm
|
Qwen/Qwen3.5-2B Qwen vlm
|
||||||
Qwen/Qwen3.5-4B Qwen vlm
|
Qwen/Qwen3.5-4B Qwen vlm
|
||||||
|
|||||||
@@ -291,7 +291,8 @@ OpenBMB/MiniCPM4-0.5B OpenBMB llm ✔
|
|||||||
Qwen/Qwen2.5-VL-3B-Instruct Qwen vlm ✔
|
Qwen/Qwen2.5-VL-3B-Instruct Qwen vlm ✔
|
||||||
Qwen/Qwen2.5-VL-7B-Instruct Qwen vlm
|
Qwen/Qwen2.5-VL-7B-Instruct Qwen vlm
|
||||||
Qwen/Qwen3-0.6B Qwen llm ✔
|
Qwen/Qwen3-0.6B Qwen llm ✔
|
||||||
Qwen/Qwen3-1.7B Qwen llm ✔
|
Qwen/Qwen3-1.7B Qwen llm
|
||||||
|
Qwen/Qwen3-4B Qwen llm
|
||||||
Qwen/Qwen3.5-0.8B Qwen vlm ✔
|
Qwen/Qwen3.5-0.8B Qwen vlm ✔
|
||||||
Qwen/Qwen3.5-2B Qwen vlm
|
Qwen/Qwen3.5-2B Qwen vlm
|
||||||
Qwen/Qwen3.5-4B Qwen vlm
|
Qwen/Qwen3.5-4B Qwen vlm
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ OpenBMB/MiniCPM4-0.5B OpenBMB llm ✔
|
|||||||
Qwen/Qwen2.5-VL-3B-Instruct Qwen vlm ✔
|
Qwen/Qwen2.5-VL-3B-Instruct Qwen vlm ✔
|
||||||
Qwen/Qwen2.5-VL-7B-Instruct Qwen vlm
|
Qwen/Qwen2.5-VL-7B-Instruct Qwen vlm
|
||||||
Qwen/Qwen3-0.6B Qwen llm ✔
|
Qwen/Qwen3-0.6B Qwen llm ✔
|
||||||
Qwen/Qwen3-1.7B Qwen llm ✔
|
Qwen/Qwen3-1.7B Qwen llm
|
||||||
|
Qwen/Qwen3-4B Qwen llm
|
||||||
Qwen/Qwen3.5-0.8B Qwen vlm ✔
|
Qwen/Qwen3.5-0.8B Qwen vlm ✔
|
||||||
Qwen/Qwen3.5-2B Qwen vlm
|
Qwen/Qwen3.5-2B Qwen vlm
|
||||||
Qwen/Qwen3.5-4B Qwen vlm
|
Qwen/Qwen3.5-4B Qwen vlm
|
||||||
@@ -44,7 +45,7 @@ ZhipuAI/GLM-OCR ZhipuAI ocr ✔
|
|||||||
|
|
||||||
| Model | Parameters | Model Id | License |
|
| Model | Parameters | Model Id | License |
|
||||||
|-------|-----------|--------|---------|
|
|-------|-----------|--------|---------|
|
||||||
| **Qwen3-0.6B** | 0.6B | Qwen/Qwen3-0.6B <br> Qwen/Qwen3-1.7B | [Apache 2.0](https://huggingface.co/datasets/choosealicense/licenses/blob/main/markdown/apache-2.0.md) |
|
| **Qwen3-0.6B** | 0.6B | Qwen/Qwen3-0.6B <br> Qwen/Qwen3-1.7B <br> Qwen/Qwen3-4B | [Apache 2.0](https://huggingface.co/datasets/choosealicense/licenses/blob/main/markdown/apache-2.0.md) |
|
||||||
| **MiniCPM4-0.5B** | 0.5B | OpenBMB/MiniCPM4-0.5B | [Apache 2.0](https://huggingface.co/datasets/choosealicense/licenses/blob/main/markdown/apache-2.0.md) |
|
| **MiniCPM4-0.5B** | 0.5B | OpenBMB/MiniCPM4-0.5B | [Apache 2.0](https://huggingface.co/datasets/choosealicense/licenses/blob/main/markdown/apache-2.0.md) |
|
||||||
| **LFM2-1.2B** | 1.2B | LiquidAI/LFM2-1.2B | [lfm1.0](https://huggingface.co/LiquidAI/LFM2-1.2B/blob/main/LICENSE) |
|
| **LFM2-1.2B** | 1.2B | LiquidAI/LFM2-1.2B | [lfm1.0](https://huggingface.co/LiquidAI/LFM2-1.2B/blob/main/LICENSE) |
|
||||||
| **LFM2.5-1.2B-Instruct** | 1.2B | LiquidAI/LFM2.5-1.2B-Instruct | [lfm1.0](https://huggingface.co/LiquidAI/LFM2.5-1.2B-Instruct/blob/main/LICENSE) |
|
| **LFM2.5-1.2B-Instruct** | 1.2B | LiquidAI/LFM2.5-1.2B-Instruct | [lfm1.0](https://huggingface.co/LiquidAI/LFM2.5-1.2B-Instruct/blob/main/LICENSE) |
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ OpenBMB/MiniCPM4-0.5B OpenBMB llm ✔
|
|||||||
Qwen/Qwen2.5-VL-3B-Instruct Qwen vlm ✔
|
Qwen/Qwen2.5-VL-3B-Instruct Qwen vlm ✔
|
||||||
Qwen/Qwen2.5-VL-7B-Instruct Qwen vlm
|
Qwen/Qwen2.5-VL-7B-Instruct Qwen vlm
|
||||||
Qwen/Qwen3-0.6B Qwen llm ✔
|
Qwen/Qwen3-0.6B Qwen llm ✔
|
||||||
Qwen/Qwen3-1.7B Qwen llm ✔
|
Qwen/Qwen3-1.7B Qwen llm
|
||||||
|
Qwen/Qwen3-4B Qwen llm
|
||||||
Qwen/Qwen3.5-0.8B Qwen vlm ✔
|
Qwen/Qwen3.5-0.8B Qwen vlm ✔
|
||||||
Qwen/Qwen3.5-2B Qwen vlm
|
Qwen/Qwen3.5-2B Qwen vlm
|
||||||
Qwen/Qwen3.5-4B Qwen vlm
|
Qwen/Qwen3.5-4B Qwen vlm
|
||||||
@@ -44,7 +45,7 @@ ZhipuAI/GLM-OCR ZhipuAI ocr ✔
|
|||||||
|
|
||||||
| 模型 | 参数量 | 模型id | 开源协议 |
|
| 模型 | 参数量 | 模型id | 开源协议 |
|
||||||
|------|--------|------|---------|
|
|------|--------|------|---------|
|
||||||
| **Qwen3-0.6B** | 0.6B | Qwen/Qwen3-0.6B <br> Qwen/Qwen3-1.7B | [Apache 2.0](https://huggingface.co/datasets/choosealicense/licenses/blob/main/markdown/apache-2.0.md) |
|
| **Qwen3-0.6B** | 0.6B | Qwen/Qwen3-0.6B <br> Qwen/Qwen3-1.7B <br> Qwen/Qwen3-4B | [Apache 2.0](https://huggingface.co/datasets/choosealicense/licenses/blob/main/markdown/apache-2.0.md) |
|
||||||
| **MiniCPM4-0.5B** | 0.5B | OpenBMB/MiniCPM4-0.5B | [Apache 2.0](https://huggingface.co/datasets/choosealicense/licenses/blob/main/markdown/apache-2.0.md) |
|
| **MiniCPM4-0.5B** | 0.5B | OpenBMB/MiniCPM4-0.5B | [Apache 2.0](https://huggingface.co/datasets/choosealicense/licenses/blob/main/markdown/apache-2.0.md) |
|
||||||
| **LFM2-1.2B** | 1.2B | LiquidAI/LFM2-1.2B | [lfm1.0](https://huggingface.co/LiquidAI/LFM2-1.2B/blob/main/LICENSE) |
|
| **LFM2-1.2B** | 1.2B | LiquidAI/LFM2-1.2B | [lfm1.0](https://huggingface.co/LiquidAI/LFM2-1.2B/blob/main/LICENSE) |
|
||||||
| **LFM2.5-1.2B-Instruct** | 1.2B | LiquidAI/LFM2.5-1.2B-Instruct | [lfm1.0](https://huggingface.co/LiquidAI/LFM2.5-1.2B-Instruct/blob/main/LICENSE) |
|
| **LFM2.5-1.2B-Instruct** | 1.2B | LiquidAI/LFM2.5-1.2B-Instruct | [lfm1.0](https://huggingface.co/LiquidAI/LFM2.5-1.2B-Instruct/blob/main/LICENSE) |
|
||||||
|
|||||||
+40
-73
@@ -5,40 +5,8 @@ use clap::{Args, Parser, Subcommand};
|
|||||||
#[command(name = "aha")]
|
#[command(name = "aha")]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
pub(crate) struct Cli {
|
pub(crate) struct Cli {
|
||||||
/// Service listen address
|
|
||||||
#[arg(short, long, default_value = "127.0.0.1")]
|
|
||||||
pub address: Option<String>,
|
|
||||||
|
|
||||||
/// Service listen port
|
|
||||||
#[arg(short, long)]
|
|
||||||
pub port: Option<u16>,
|
|
||||||
|
|
||||||
/// Model type (required for backward compatibility)
|
|
||||||
#[arg(short, long)]
|
|
||||||
pub model: Option<WhichModel>,
|
|
||||||
|
|
||||||
/// Local model weight path
|
|
||||||
#[arg(long)]
|
|
||||||
pub weight_path: Option<String>,
|
|
||||||
|
|
||||||
/// Model download save directory
|
|
||||||
#[arg(long)]
|
|
||||||
pub save_dir: Option<String>,
|
|
||||||
|
|
||||||
/// Download retry count
|
|
||||||
#[arg(long)]
|
|
||||||
pub download_retries: Option<u32>,
|
|
||||||
|
|
||||||
/// Local GGUF model weight path (required for loading models with GGUF).
|
|
||||||
#[arg(long)]
|
|
||||||
pub gguf_path: Option<String>,
|
|
||||||
|
|
||||||
/// Local path for mmproj GGUF model weights (required for loading with multimodel GGUF)
|
|
||||||
#[arg(long)]
|
|
||||||
pub mmproj_path: Option<String>,
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Option<Commands>,
|
pub command: Commands,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
@@ -61,7 +29,7 @@ pub(crate) enum Commands {
|
|||||||
|
|
||||||
/// Common/shared arguments for server operations
|
/// Common/shared arguments for server operations
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
pub(crate) struct CommonArgs {
|
pub(crate) struct ServerCommonArgs {
|
||||||
/// Service listen address
|
/// Service listen address
|
||||||
#[arg(short, long, default_value = "127.0.0.1")]
|
#[arg(short, long, default_value = "127.0.0.1")]
|
||||||
pub address: String,
|
pub address: String,
|
||||||
@@ -70,24 +38,42 @@ pub(crate) struct CommonArgs {
|
|||||||
#[arg(short, long, default_value_t = 10100)]
|
#[arg(short, long, default_value_t = 10100)]
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
|
||||||
/// Model type (required)
|
|
||||||
#[arg(short, long)]
|
|
||||||
pub model: WhichModel,
|
|
||||||
|
|
||||||
/// Allow remote shutdown requests (default: local only, use with caution)
|
/// Allow remote shutdown requests (default: local only, use with caution)
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub allow_remote_shutdown: bool,
|
pub allow_remote_shutdown: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Arguments for the 'cli' subcommand (download + serve)
|
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
pub(crate) struct CliArgs {
|
pub(crate) struct PathCommonArgs {
|
||||||
#[command(flatten)]
|
|
||||||
pub common: CommonArgs,
|
|
||||||
|
|
||||||
/// Local model weight path (skip download if provided)
|
/// Local model weight path (skip download if provided)
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub weight_path: Option<String>,
|
pub weight_path: Option<String>,
|
||||||
|
/// Local GGUF model weight path (required for loading models with GGUF).
|
||||||
|
#[arg(long)]
|
||||||
|
pub gguf_path: Option<String>,
|
||||||
|
|
||||||
|
/// Local path for mmproj GGUF model weights (required for loading with multimodel GGUF)
|
||||||
|
#[arg(long)]
|
||||||
|
pub mmproj_path: Option<String>,
|
||||||
|
|
||||||
|
/// Local path for onnx model weights (required for loading with onnx)
|
||||||
|
#[arg(long)]
|
||||||
|
pub onnx_path: Option<String>,
|
||||||
|
|
||||||
|
/// config path for onnx/gguf model need extra config file
|
||||||
|
#[arg(long)]
|
||||||
|
pub config_path: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arguments for the 'cli' subcommand (download + serve)
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
pub(crate) struct CliArgs {
|
||||||
|
/// Model type (required)
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub model: WhichModel,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub server_common: ServerCommonArgs,
|
||||||
|
|
||||||
/// Model download save directory
|
/// Model download save directory
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
@@ -97,32 +83,22 @@ pub(crate) struct CliArgs {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub download_retries: Option<u32>,
|
pub download_retries: Option<u32>,
|
||||||
|
|
||||||
/// Local GGUF model weight path (required for loading models with GGUF).
|
#[command(flatten)]
|
||||||
#[arg(long)]
|
pub path_common: PathCommonArgs,
|
||||||
pub gguf_path: Option<String>,
|
|
||||||
|
|
||||||
/// Local path for mmproj GGUF model weights (required for loading with multimodel GGUF)
|
|
||||||
#[arg(long)]
|
|
||||||
pub mmproj_path: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Arguments for the 'serv start' subcommand
|
/// Arguments for the 'serv start' subcommand
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
pub(crate) struct ServArgs {
|
pub(crate) struct ServArgs {
|
||||||
|
/// Model type (required)
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub model: WhichModel,
|
||||||
|
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
pub common: CommonArgs,
|
pub server_common: ServerCommonArgs,
|
||||||
|
|
||||||
/// Local model weight path (defaults to ~/.aha/{model_id} if not specified)
|
#[command(flatten)]
|
||||||
#[arg(long)]
|
pub path_common: PathCommonArgs,
|
||||||
pub weight_path: Option<String>,
|
|
||||||
|
|
||||||
/// Local GGUF model weight path (required for loading models with GGUF).
|
|
||||||
#[arg(long)]
|
|
||||||
pub gguf_path: Option<String>,
|
|
||||||
|
|
||||||
/// Local path for mmproj GGUF model weights (required for loading with multimodel GGUF)
|
|
||||||
#[arg(long)]
|
|
||||||
pub mmproj_path: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Arguments for the 'serv list' subcommand
|
/// Arguments for the 'serv list' subcommand
|
||||||
@@ -164,17 +140,8 @@ pub(crate) struct RunArgs {
|
|||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub output: Option<String>,
|
pub output: Option<String>,
|
||||||
|
|
||||||
/// Local model weight path (defaults to ~/.aha/{model_id} if not specified)
|
#[command(flatten)]
|
||||||
#[arg(long)]
|
pub path_common: PathCommonArgs,
|
||||||
pub weight_path: Option<String>,
|
|
||||||
|
|
||||||
/// Local GGUF model weight path (required for loading models with GGUF).
|
|
||||||
#[arg(long)]
|
|
||||||
pub gguf_path: Option<String>,
|
|
||||||
|
|
||||||
/// Local path for mmproj GGUF model weights (required for loading with multimodel GGUF)
|
|
||||||
#[arg(long)]
|
|
||||||
pub mmproj_path: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Arguments for the 'delete' subcommand (delete model from default location)
|
/// Arguments for the 'delete' subcommand (delete model from default location)
|
||||||
|
|||||||
+58
-28
@@ -76,22 +76,27 @@ pub(crate) fn run_list(args: ListArgs) -> anyhow::Result<()> {
|
|||||||
/// Run the 'cli' subcommand: download model (if needed) and start service
|
/// Run the 'cli' subcommand: download model (if needed) and start service
|
||||||
pub(crate) async fn run_cli(args: CliArgs) -> anyhow::Result<()> {
|
pub(crate) async fn run_cli(args: CliArgs) -> anyhow::Result<()> {
|
||||||
let CliArgs {
|
let CliArgs {
|
||||||
common,
|
model,
|
||||||
weight_path,
|
server_common,
|
||||||
save_dir,
|
save_dir,
|
||||||
download_retries,
|
download_retries,
|
||||||
gguf_path,
|
path_common,
|
||||||
mmproj_path,
|
|
||||||
} = args;
|
} = args;
|
||||||
let model_id = common.model.as_string();
|
let model_id = model.as_string();
|
||||||
|
|
||||||
let (model_path, gguf, mmproj) = if model_id.contains("gguf") {
|
let (model_path, gguf, mmproj) = if model.is_gguf() {
|
||||||
if gguf_path.is_none() {
|
if path_common.gguf_path.is_none() {
|
||||||
return Err(anyhow!("gguf model path is required"));
|
return Err(anyhow!("gguf model path is required"));
|
||||||
}
|
}
|
||||||
("GGUF".to_string(), gguf_path, mmproj_path)
|
(
|
||||||
|
"GGUF".to_string(),
|
||||||
|
path_common.gguf_path,
|
||||||
|
path_common.mmproj_path,
|
||||||
|
)
|
||||||
|
} else if model.is_onnx() {
|
||||||
|
return Err(anyhow!("onnx model not support now"));
|
||||||
} else {
|
} else {
|
||||||
let model_path = match weight_path {
|
let model_path = match path_common.weight_path {
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
None => {
|
None => {
|
||||||
let save_dir = match save_dir {
|
let save_dir = match save_dir {
|
||||||
@@ -106,8 +111,13 @@ pub(crate) async fn run_cli(args: CliArgs) -> anyhow::Result<()> {
|
|||||||
(model_path, None, None)
|
(model_path, None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
init(common.model, model_path, gguf, mmproj)?;
|
init(model, model_path, gguf, mmproj)?;
|
||||||
start_http_server(common.address, common.port, common.allow_remote_shutdown).await?;
|
start_http_server(
|
||||||
|
server_common.address,
|
||||||
|
server_common.port,
|
||||||
|
server_common.allow_remote_shutdown,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -115,27 +125,41 @@ pub(crate) async fn run_cli(args: CliArgs) -> anyhow::Result<()> {
|
|||||||
/// Run the 'serv' subcommand: start service only (no download)
|
/// Run the 'serv' subcommand: start service only (no download)
|
||||||
pub(crate) async fn run_serv(args: ServArgs) -> anyhow::Result<()> {
|
pub(crate) async fn run_serv(args: ServArgs) -> anyhow::Result<()> {
|
||||||
let ServArgs {
|
let ServArgs {
|
||||||
common,
|
model,
|
||||||
weight_path,
|
server_common,
|
||||||
gguf_path,
|
path_common,
|
||||||
mmproj_path,
|
|
||||||
} = args;
|
} = args;
|
||||||
let model_id = common.model.as_string();
|
let (model_path, gguf, mmproj) = if model.is_gguf() {
|
||||||
let (model_path, gguf, mmproj) = if model_id.contains("gguf") {
|
if path_common.gguf_path.is_none() {
|
||||||
if gguf_path.is_none() {
|
|
||||||
return Err(anyhow!("gguf model path is required"));
|
return Err(anyhow!("gguf model path is required"));
|
||||||
}
|
}
|
||||||
("GGUF".to_string(), gguf_path, mmproj_path)
|
(
|
||||||
|
"GGUF".to_string(),
|
||||||
|
path_common.gguf_path,
|
||||||
|
path_common.mmproj_path,
|
||||||
|
)
|
||||||
|
} else if model.is_onnx() {
|
||||||
|
return Err(anyhow!("onnx model not support now"));
|
||||||
} else {
|
} else {
|
||||||
let model_path = match weight_path {
|
let model_path = match path_common.weight_path {
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
None => get_default_weight_path(common.model),
|
None => get_default_weight_path(model),
|
||||||
};
|
};
|
||||||
|
if !std::path::Path::new(&model_path).exists() {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"serv subcommand will not download model, use `weight-path` to pass the model path"
|
||||||
|
));
|
||||||
|
}
|
||||||
(model_path, None, None)
|
(model_path, None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
init(common.model, model_path, gguf, mmproj)?;
|
init(model, model_path, gguf, mmproj)?;
|
||||||
start_http_server(common.address, common.port, common.allow_remote_shutdown).await?;
|
start_http_server(
|
||||||
|
server_common.address,
|
||||||
|
server_common.port,
|
||||||
|
server_common.allow_remote_shutdown,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -205,13 +229,11 @@ pub(crate) fn run_run(args: RunArgs) -> anyhow::Result<()> {
|
|||||||
model,
|
model,
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
weight_path,
|
path_common,
|
||||||
gguf_path,
|
|
||||||
mmproj_path,
|
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
// Use default weight path if not specified
|
// Use default weight path if not specified
|
||||||
let weight_path = match weight_path {
|
let weight_path = match path_common.weight_path {
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
None => get_default_weight_path(model),
|
None => get_default_weight_path(model),
|
||||||
};
|
};
|
||||||
@@ -243,6 +265,9 @@ pub(crate) fn run_run(args: RunArgs) -> anyhow::Result<()> {
|
|||||||
WhichModel::Qwen3_1_7B => {
|
WhichModel::Qwen3_1_7B => {
|
||||||
qwen3::Qwen3Exec::run(&input, output.as_deref(), &weight_path)?;
|
qwen3::Qwen3Exec::run(&input, output.as_deref(), &weight_path)?;
|
||||||
}
|
}
|
||||||
|
WhichModel::Qwen3_4B => {
|
||||||
|
qwen3::Qwen3Exec::run(&input, output.as_deref(), &weight_path)?;
|
||||||
|
}
|
||||||
WhichModel::Qwen3_5_0_8B => {
|
WhichModel::Qwen3_5_0_8B => {
|
||||||
qwen3_5::Qwen3_5Exec::run(&input, output.as_deref(), &weight_path)?;
|
qwen3_5::Qwen3_5Exec::run(&input, output.as_deref(), &weight_path)?;
|
||||||
}
|
}
|
||||||
@@ -256,7 +281,12 @@ pub(crate) fn run_run(args: RunArgs) -> anyhow::Result<()> {
|
|||||||
qwen3_5::Qwen3_5Exec::run(&input, output.as_deref(), &weight_path)?;
|
qwen3_5::Qwen3_5Exec::run(&input, output.as_deref(), &weight_path)?;
|
||||||
}
|
}
|
||||||
WhichModel::Qwen3_5Gguf => {
|
WhichModel::Qwen3_5Gguf => {
|
||||||
qwen3_5::Qwen3_5Exec::run_gguf(&input, output.as_deref(), gguf_path, mmproj_path)?;
|
qwen3_5::Qwen3_5Exec::run_gguf(
|
||||||
|
&input,
|
||||||
|
output.as_deref(),
|
||||||
|
path_common.gguf_path,
|
||||||
|
path_common.mmproj_path,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
WhichModel::Qwen3ASR0_6B => {
|
WhichModel::Qwen3ASR0_6B => {
|
||||||
qwen3_asr::Qwen3ASRExec::run(&input, output.as_deref(), &weight_path)?;
|
qwen3_asr::Qwen3ASRExec::run(&input, output.as_deref(), &weight_path)?;
|
||||||
|
|||||||
+8
-27
@@ -1,7 +1,7 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
args::{Cli, CliArgs, Commands, CommonArgs},
|
args::{Cli, Commands},
|
||||||
run_cli, run_delete, run_download, run_list, run_ps, run_run, run_serv,
|
run_cli, run_delete, run_download, run_list, run_ps, run_run, run_serv,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -11,32 +11,13 @@ mod server;
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Some(Commands::Cli(args)) => run_cli(args).await,
|
Commands::Cli(args) => run_cli(args).await,
|
||||||
Some(Commands::Serv(args)) => run_serv(args).await,
|
Commands::Serv(args) => run_serv(args).await,
|
||||||
Some(Commands::Ps(args)) => run_ps(args),
|
Commands::Ps(args) => run_ps(args),
|
||||||
Some(Commands::Delete(args)) => run_delete(args),
|
Commands::Delete(args) => run_delete(args),
|
||||||
Some(Commands::Download(args)) => run_download(args).await,
|
Commands::Download(args) => run_download(args).await,
|
||||||
Some(Commands::Run(args)) => run_run(args),
|
Commands::Run(args) => run_run(args),
|
||||||
Some(Commands::List(args)) => run_list(args),
|
Commands::List(args) => run_list(args),
|
||||||
None => {
|
|
||||||
// Backward compatibility: when no subcommand is provided, use 'cli' behavior
|
|
||||||
let model = cli.model.expect("Model is required (use -m or --model)");
|
|
||||||
let args = CliArgs {
|
|
||||||
common: CommonArgs {
|
|
||||||
address: cli.address.unwrap_or_else(|| "127.0.0.1".to_string()),
|
|
||||||
port: cli.port.unwrap_or(10100),
|
|
||||||
model,
|
|
||||||
allow_remote_shutdown: false,
|
|
||||||
},
|
|
||||||
weight_path: cli.weight_path,
|
|
||||||
save_dir: cli.save_dir,
|
|
||||||
download_retries: cli.download_retries,
|
|
||||||
gguf_path: cli.gguf_path,
|
|
||||||
mmproj_path: cli.mmproj_path,
|
|
||||||
};
|
|
||||||
run_cli(args).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ pub enum WhichModel {
|
|||||||
Qwen3_0_6B,
|
Qwen3_0_6B,
|
||||||
#[value(name = "Qwen/Qwen3-1.7B")]
|
#[value(name = "Qwen/Qwen3-1.7B")]
|
||||||
Qwen3_1_7B,
|
Qwen3_1_7B,
|
||||||
|
#[value(name = "Qwen/Qwen3-4B")]
|
||||||
|
Qwen3_4B,
|
||||||
#[value(name = "Qwen/Qwen3.5-0.8B")]
|
#[value(name = "Qwen/Qwen3.5-0.8B")]
|
||||||
Qwen3_5_0_8B,
|
Qwen3_5_0_8B,
|
||||||
#[value(name = "Qwen/Qwen3.5-2B")]
|
#[value(name = "Qwen/Qwen3.5-2B")]
|
||||||
@@ -67,18 +69,40 @@ pub enum WhichModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WhichModel {
|
impl WhichModel {
|
||||||
/// Get the ModelScope model ID for this model variant
|
/// Get the model ID for this model variant
|
||||||
pub fn as_string(&self) -> String {
|
pub fn as_string(&self) -> String {
|
||||||
self.to_possible_value()
|
self.to_possible_value()
|
||||||
.expect("not exists")
|
.expect("not exists")
|
||||||
.get_name()
|
.get_name()
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the model is in GGUF format
|
||||||
|
///
|
||||||
|
/// Returns true if the model ID contains "gguf", false otherwise
|
||||||
|
pub fn is_gguf(&self) -> bool {
|
||||||
|
let model_id = self.as_string();
|
||||||
|
model_id.to_lowercase().contains("gguf")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the model is in ONNX format
|
||||||
|
///
|
||||||
|
/// Returns true if the model ID contains "onnx", false otherwise
|
||||||
|
pub fn is_onnx(&self) -> bool {
|
||||||
|
let model_id = self.as_string();
|
||||||
|
model_id.to_lowercase().contains("onnx")
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the WhichModel enum list
|
/// Get the WhichModel enum list
|
||||||
pub fn model_list() -> Vec<Self> {
|
pub fn model_list() -> Vec<Self> {
|
||||||
WhichModel::value_variants().to_vec()
|
WhichModel::value_variants().to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts the model owner/organization from the model ID
|
||||||
|
///
|
||||||
|
/// Splits the model ID string on '/' and returns the first part which typically represents
|
||||||
|
/// the organization or user who owns the model in Hugging Face format (e.g., "Qwen" from "Qwen/Qwen3-0.6B")
|
||||||
|
/// Returns "none" if the model ID doesn't contain a '/' separator
|
||||||
pub fn model_owner(&self) -> String {
|
pub fn model_owner(&self) -> String {
|
||||||
let name = self.as_string();
|
let name = self.as_string();
|
||||||
let names: Vec<&str> = name.split("/").collect();
|
let names: Vec<&str> = name.split("/").collect();
|
||||||
@@ -96,6 +120,7 @@ impl WhichModel {
|
|||||||
WhichModel::MiniCPM4_0_5B
|
WhichModel::MiniCPM4_0_5B
|
||||||
| WhichModel::Qwen3_0_6B
|
| WhichModel::Qwen3_0_6B
|
||||||
| WhichModel::Qwen3_1_7B
|
| WhichModel::Qwen3_1_7B
|
||||||
|
| WhichModel::Qwen3_4B
|
||||||
| WhichModel::LFM2_1_2B
|
| WhichModel::LFM2_1_2B
|
||||||
| WhichModel::LFM2_5_1_2BInstruct => "llm",
|
| WhichModel::LFM2_5_1_2BInstruct => "llm",
|
||||||
// VLM models
|
// VLM models
|
||||||
|
|||||||
+27
-12
@@ -129,12 +129,27 @@ impl<'a> GenerateModel for ModelInstance<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_model<'a>(
|
#[allow(unused)]
|
||||||
|
pub fn load_gguf_model<'a>(
|
||||||
model_type: WhichModel,
|
model_type: WhichModel,
|
||||||
path: &str,
|
config_path: Option<&str>, // 有些gguf未包含模型其他配置,需额外指定
|
||||||
gguf: Option<&str>,
|
gguf_path: &str,
|
||||||
mmproj: Option<&str>,
|
mmproj_path: Option<&str>,
|
||||||
) -> Result<ModelInstance<'a>> {
|
) -> Result<ModelInstance<'a>> {
|
||||||
|
let model = match model_type {
|
||||||
|
WhichModel::Qwen3_5Gguf => {
|
||||||
|
let model = Qwen3_5GenerateModel::init_from_gguf(gguf_path, mmproj_path, None)?;
|
||||||
|
ModelInstance::Qwen3_5(model)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let model_id = model_type.as_string();
|
||||||
|
return Err(anyhow!("model id {model_id} is not gguf model"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_model<'a>(model_type: WhichModel, path: &str) -> Result<ModelInstance<'a>> {
|
||||||
let model = match model_type {
|
let model = match model_type {
|
||||||
WhichModel::MiniCPM4_0_5B => {
|
WhichModel::MiniCPM4_0_5B => {
|
||||||
let model = MiniCPMGenerateModel::init(path, None, None)?;
|
let model = MiniCPMGenerateModel::init(path, None, None)?;
|
||||||
@@ -172,6 +187,10 @@ pub fn load_model<'a>(
|
|||||||
let model = Qwen3GenerateModel::init(path, None, None)?;
|
let model = Qwen3GenerateModel::init(path, None, None)?;
|
||||||
ModelInstance::Qwen3(model)
|
ModelInstance::Qwen3(model)
|
||||||
}
|
}
|
||||||
|
WhichModel::Qwen3_4B => {
|
||||||
|
let model = Qwen3GenerateModel::init(path, None, None)?;
|
||||||
|
ModelInstance::Qwen3(model)
|
||||||
|
}
|
||||||
WhichModel::Qwen3_5_0_8B => {
|
WhichModel::Qwen3_5_0_8B => {
|
||||||
let model = Qwen3_5GenerateModel::init(path, None, None)?;
|
let model = Qwen3_5GenerateModel::init(path, None, None)?;
|
||||||
ModelInstance::Qwen3_5(model)
|
ModelInstance::Qwen3_5(model)
|
||||||
@@ -188,14 +207,6 @@ pub fn load_model<'a>(
|
|||||||
let model = Qwen3_5GenerateModel::init(path, None, None)?;
|
let model = Qwen3_5GenerateModel::init(path, None, None)?;
|
||||||
ModelInstance::Qwen3_5(model)
|
ModelInstance::Qwen3_5(model)
|
||||||
}
|
}
|
||||||
WhichModel::Qwen3_5Gguf => {
|
|
||||||
if gguf.is_none() {
|
|
||||||
return Err(anyhow!("Qwen3_5Gguf gguf model path is required"));
|
|
||||||
}
|
|
||||||
let gguf = gguf.unwrap();
|
|
||||||
let model = Qwen3_5GenerateModel::init_from_gguf(gguf, mmproj, None)?;
|
|
||||||
ModelInstance::Qwen3_5(model)
|
|
||||||
}
|
|
||||||
WhichModel::Qwen3ASR0_6B => {
|
WhichModel::Qwen3ASR0_6B => {
|
||||||
let model = Qwen3AsrGenerateModel::init(path, None, None)?;
|
let model = Qwen3AsrGenerateModel::init(path, None, None)?;
|
||||||
ModelInstance::Qwen3ASR(model)
|
ModelInstance::Qwen3ASR(model)
|
||||||
@@ -264,6 +275,10 @@ pub fn load_model<'a>(
|
|||||||
let model = GlmOcrGenerateModel::init(path, None, None)?;
|
let model = GlmOcrGenerateModel::init(path, None, None)?;
|
||||||
ModelInstance::GlmOCR(model)
|
ModelInstance::GlmOCR(model)
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
let model_id = model_type.as_string();
|
||||||
|
return Err(anyhow!("model id {model_id} is not safetensor model"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(model)
|
Ok(model)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ impl<'a> Qwen3_5GenerateModel<'a> {
|
|||||||
qwen3_5,
|
qwen3_5,
|
||||||
device,
|
device,
|
||||||
model_name: model_name.to_string(),
|
model_name: model_name.to_string(),
|
||||||
repeat_penalty: 1.01,
|
repeat_penalty: 1.0,
|
||||||
repeat_last_n: 64,
|
repeat_last_n: 64,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -116,7 +116,7 @@ impl<'a> Qwen3_5GenerateModel<'a> {
|
|||||||
qwen3_5,
|
qwen3_5,
|
||||||
device,
|
device,
|
||||||
model_name: stem.to_string(),
|
model_name: stem.to_string(),
|
||||||
repeat_penalty: 1.1,
|
repeat_penalty: 1.2,
|
||||||
repeat_last_n: 64,
|
repeat_last_n: 64,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-4
@@ -2,9 +2,11 @@ use std::pin::pin;
|
|||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, OnceLock};
|
use std::sync::{Arc, OnceLock};
|
||||||
|
|
||||||
|
use aha::models::load_gguf_model;
|
||||||
use aha::models::{GenerateModel, ModelInstance, common::model_mapping::WhichModel, load_model};
|
use aha::models::{GenerateModel, ModelInstance, common::model_mapping::WhichModel, load_model};
|
||||||
use aha::params::chat::ChatCompletionParameters;
|
use aha::params::chat::ChatCompletionParameters;
|
||||||
use aha::utils::string_to_static_str;
|
use aha::utils::string_to_static_str;
|
||||||
|
use anyhow::anyhow;
|
||||||
use rocket::futures::StreamExt;
|
use rocket::futures::StreamExt;
|
||||||
use rocket::serde::{Serialize, json::Json};
|
use rocket::serde::{Serialize, json::Json};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
@@ -37,10 +39,21 @@ pub fn init(
|
|||||||
gguf: Option<String>,
|
gguf: Option<String>,
|
||||||
mmproj: Option<String>,
|
mmproj: Option<String>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let model_path = string_to_static_str(path);
|
let model = if model_type.is_gguf() {
|
||||||
let gguf = gguf.map(string_to_static_str);
|
if let Some(gguf_path) = gguf {
|
||||||
let mmproj = mmproj.map(string_to_static_str);
|
let gguf_path = string_to_static_str(gguf_path);
|
||||||
let model = load_model(model_type, model_path, gguf, mmproj)?;
|
let mmproj_path = mmproj.map(string_to_static_str);
|
||||||
|
load_gguf_model(model_type, None, gguf_path, mmproj_path)?
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!("gguf model need gguf model path"));
|
||||||
|
}
|
||||||
|
} else if model_type.is_onnx() {
|
||||||
|
return Err(anyhow!("onnx comming soon but now not support"));
|
||||||
|
} else {
|
||||||
|
let model_path = string_to_static_str(path);
|
||||||
|
load_model(model_type, model_path)?
|
||||||
|
};
|
||||||
|
|
||||||
MODEL.get_or_init(|| {
|
MODEL.get_or_init(|| {
|
||||||
Arc::new(RwLock::new(StoredModel {
|
Arc::new(RwLock::new(StoredModel {
|
||||||
which_model: model_type,
|
which_model: model_type,
|
||||||
|
|||||||
Reference in New Issue
Block a user