已合并
vllm_to_motor全量配置转化工具 #471
vllm_to_motor全量配置转化工具 #471
已合并
高鹏创建于 7月10日
24 个文件变更+2050-955
@@ -14,6 +14,8 @@
14 14 
15### 参数说明15### 参数说明
16 16 
17+Motor**服务部署**参数说明
18+ 
17| 参数 | 简写 | 说明 |19| 参数 | 简写 | 说明 |
18|------|------|------|20|------|------|------|
19| `--config_dir` | `--dir` | 配置文件所在目录,目录下需包含 `user_config.json``env.json` |21| `--config_dir` | `--dir` | 配置文件所在目录,目录下需包含 `user_config.json``env.json` |
@@ -25,6 +27,16 @@
25| `--auto_log_collect` | - | 部署完成后自动启动日志采集 |27| `--auto_log_collect` | - | 部署完成后自动启动日志采集 |
26| `--nostep` | - | 部署完成后不显示服务启动进度条 |28| `--nostep` | - | 部署完成后不显示服务启动进度条 |
27 29 
30+Motor**配置文件自动生成**参数说明
31+ 
32+| 参数 | 简写 | 说明 |
33+|------|------|------|
34+| `--mode` | - | `deploy`(默认)或 `general_config`(从 vLLM 脚本生成配置) |
35+| `--deploy-scenario` | - | `general_config` 必填:`hybrid` / `separate` |
36+| `--hardware-type` | - | `general_config` 必填:`A2` / `A3` |
37+| `--weight-path` | - | `general_config` 可选:权重挂载路径 |
38+| `--image-name` | - | `general_config` 可选:镜像名称 |
39+ 
28### 使用方式40### 使用方式
29 41 
30#### 方式零:交互式 TUI 模式42#### 方式零:交互式 TUI 模式
@@ -74,6 +86,10 @@ python deploy.py --config_dir ../infer_engines/vllm --config /path/to/custom_use
74 86 
75当同时指定 `--config_dir``--config`/`--env` 时,以 `--config``--env` 为准。87当同时指定 `--config_dir``--config`/`--env` 时,以 `--config``--env` 为准。
76 88 
89+#### 方式四:基于vllm部署脚本生成Motor全量配置文件
90+ 
91+使用方式请参阅[Motor配置自动生成指导](../infer_engines/vllm/models/README.md)。
92+ 
77### 其他操作93### 其他操作
78 94 
79#### 更新配置95#### 更新配置
Rexamples/infer_engines/vllm/models/deepseek/v4/pro/env.jsonexamples/deployer/config_tool/output_config/env.json+8-18
@@ -3,24 +3,20 @@
3 "motor_common_env": {3 "motor_common_env": {
4 "CANN_INSTALL_PATH": "/usr/local/Ascend"4 "CANN_INSTALL_PATH": "/usr/local/Ascend"
5 },5 },
6- "motor_controller_env": {6+ "motor_controller_env": {},
7- },7+ "motor_coordinator_env": {},
8- "motor_coordinator_env": {
9- },
10 "motor_engine_prefill_env": {8 "motor_engine_prefill_env": {
11 "VLLM_RPC_TIMEOUT": 3600000,9 "VLLM_RPC_TIMEOUT": 3600000,
12 "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS": 30000,10 "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS": 30000,
13 "HCCL_EXEC_TIMEOUT": 204,11 "HCCL_EXEC_TIMEOUT": 204,
14- "HCCL_CONNECT_TIMEOUT": 1200,12+ "HCCL_CONNECT_TIMEOUT": 120,
15 "OMP_PROC_BIND": "false",13 "OMP_PROC_BIND": "false",
16 "OMP_NUM_THREADS": 10,14 "OMP_NUM_THREADS": 10,
17 "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",15 "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
18- "HCCL_BUFFSIZE": 1024,16+ "HCCL_BUFFSIZE": 2560,
19 "TASK_QUEUE_ENABLE": 1,17 "TASK_QUEUE_ENABLE": 1,
20 "HCCL_OP_EXPANSION_MODE": "AIV",18 "HCCL_OP_EXPANSION_MODE": "AIV",
21- "ASCEND_BUFFER_POOL": "4:8",19+ "VLLM_ASCEND_ENABLE_FLASHCOMM1": 1
22- "USE_MULTI_GROUPS_KV_CACHE": 1,
23- "USE_MULTI_BLOCK_POOL": 1
24 },20 },
25 "motor_engine_decode_env": {21 "motor_engine_decode_env": {
26 "HCCL_OP_EXPANSION_MODE": "AIV",22 "HCCL_OP_EXPANSION_MODE": "AIV",
@@ -29,16 +25,10 @@
29 "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS": 30000,25 "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS": 30000,
30 "HCCL_EXEC_TIMEOUT": 204,26 "HCCL_EXEC_TIMEOUT": 204,
31 "HCCL_CONNECT_TIMEOUT": 1200,27 "HCCL_CONNECT_TIMEOUT": 1200,
32- "VLLM_ASCEND_ENABLE_FUSED_MC2": 1,
33 "OMP_PROC_BIND": "false",28 "OMP_PROC_BIND": "false",
34 "OMP_NUM_THREADS": 10,29 "OMP_NUM_THREADS": 10,
35 "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",30 "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
36- "HCCL_BUFFSIZE": 1024,31+ "HCCL_BUFFSIZE": 1024
37- "ASCEND_BUFFER_POOL": "4:8",
38- "VLLM_TORCH_PROFILER_DIR": "./vllm_profile",
39- "USE_MULTI_GROUPS_KV_CACHE": 1,
40- "USE_MULTI_BLOCK_POOL": 1
41 },32 },
42- "motor_kv_cache_pool_env": {33+ "motor_kv_cache_pool_env": {}
43- }34+}
44-}
Rexamples/infer_engines/vllm/models/deepseek/v4/flash/user_config.jsonexamples/deployer/config_tool/output_config/user_config.json+130-119
@@ -1,119 +1,130 @@
1-{1+{
2- "version": "v2.0",2+ "version": "v2.0",
3- "motor_deploy_config": {3+ "motor_deploy_config": {
4- "p_instances_num": 1,4+ "p_instances_num": 1,
5- "d_instances_num": 1,5+ "d_instances_num": 1,
6- "single_p_instance_pod_num": 1,6+ "single_p_instance_pod_num": 1,
7- "single_d_instance_pod_num": 1,7+ "single_d_instance_pod_num": 1,
8- "p_pod_npu_num": 16,8+ "p_pod_npu_num": 16,
9- "d_pod_npu_num": 16,9+ "d_pod_npu_num": 16,
10- "image_name": "vllm_motor:motor",10+ "image_name": "<请手动填写镜像名称,例如:mindie-motor-vllm:dev-26.1.0.B081-800I-A3-py311-Ubuntu24.04-lts-aarch64>",
11- "job_id": "mindie-motor",11+ "job_id": "mindie-motor",
12- "hardware_type": "800I_A3",12+ "hardware_type": "800I_A3",
13- "env_path": "./conf/env.json",13+ "weight_mount_path": "<请按实际情况填写模型权重文件的访问路径>"
14- "weight_mount_path": "/mnt/share/v4_w8a8",14+ },
15- "deploy_mode": "multi_deployment"15+ "motor_controller_config": {},
16- },16+ "motor_coordinator_config": {},
17- "motor_controller_config": {},17+ "motor_engine_prefill_config": {
18- "motor_coordinator_config": {},18+ "engine_type": "vllm",
19- "motor_engine_prefill_config": {19+ "engine_config": {
20- "engine_type": "vllm",20+ "data_parallel_size": 4,
21- "motor_nodemanger_config": {},21+ "tensor_parallel_size": 4,
22- "engine_config": {22+ "pipeline_parallel_size": 1,
23- "served_model_name": "dsv4",23+ "data_parallel_rpc_port": 9000,
24- "model": "/mnt/share/v4_w8a8",24+ "served_model_name": "dsv4",
25- "gpu_memory_utilization": 0.85,25+ "model": "<请按实际情况填写模型权重文件的访问路径>",
26- "data_parallel_size": 16,26+ "seed": 1024,
27- "tensor_parallel_size": 1,27+ "enable_expert_parallel": true,
28- "pipeline_parallel_size": 1,28+ "max-model-len": 1048576,
29- "enable_expert_parallel": true,29+ "max-num-batched-tokens": 8192,
30- "data_parallel_rpc_port": 9000,30+ "max-num-seqs": 16,
31- "seed": 1024,31+ "block-size": 128,
32- "max-model-len": 13000,32+ "enforce-eager": true,
33- "max-num-batched-tokens": 8192,33+ "no_disable_hybrid_kv_cache_manager": true,
34- "max-num-seqs": 4,34+ "trust-remote-code": true,
35- "no_disable_hybrid_kv_cache_manager": true,35+ "gpu_memory_utilization": 0.9,
36- "safetensors-load-strategy": "prefetch",36+ "quantization": "ascend",
37- "trust-remote-code": true,37+ "safetensors-load-strategy": "prefetch",
38- "tokenizer-mode": "deepseek_v4",38+ "model-loader-extra-config": {
39- "tool-call-parser": "deepseek_v4",39+ "enable_multithread_load": "true",
40- "enable-auto-tool-choice": true,40+ "num_threads": 128
41- "reasoning-parser": "deepseek_v4",41+ },
42- "quantization": "ascend",42+ "tokenizer-mode": "deepseek_v4",
43- "profiler-config": {43+ "tool-call-parser": "deepseek_v4",
44- "profiler": "torch",44+ "enable-auto-tool-choice": true,
45- "torch_profiler_dir": "./vllm_profile",45+ "reasoning-parser": "deepseek_v4",
46- "torch_profiler_with_stack": false46+ "speculative-config": {
47- },47+ "num_speculative_tokens": 1,
48- "enforce-eager": true,48+ "method": "mtp",
49- "additional-config": {49+ "enforce_eager": true
50- "enable_cpu_binding": true50+ },
51- },51+ "profiler-config": {
52- "kv_transfer_config": {52+ "profiler": "torch",
53- "kv_connector": "MooncakeHybridConnector",53+ "torch_profiler_dir": "./vllm_profile",
54- "kv_role": "kv_producer",54+ "torch_profiler_with_stack": false
55- "kv_port": "30001",55+ },
56- "engine_id": "0"56+ "additional-config": {
57- }57+ "enable_cpu_binding": true,
58- }58+ "enable_shared_expert_dp": true,
59- },59+ "enable_dsa_cp": true
60- "motor_engine_decode_config": {60+ },
61- "engine_type": "vllm",61+ "kv_transfer_config": {
62- "motor_nodemanger_config": {},62+ "kv_connector": "MooncakeHybridConnector",
63- "engine_config": {63+ "kv_role": "kv_producer",
64- "served_model_name": "auto",64+ "kv_port": "30000",
65- "model": "/mnt/share/v4_w8a8",65+ "engine_id": "0"
66- "gpu_memory_utilization": 0.88,66+ },
67- "data_parallel_size": 16,67+ "no-enable-prefix-caching": true
68- "tensor_parallel_size": 1,68+ }
69- "pipeline_parallel_size": 1,69+ },
70- "enable_expert_parallel": true,70+ "motor_engine_decode_config": {
71- "data_parallel_rpc_port": 9000,71+ "engine_type": "vllm",
72- "seed": 1024,72+ "engine_config": {
73- "max-model-len": 13500,73+ "data_parallel_size": 16,
74- "max-num-batched-tokens": 120,74+ "tensor_parallel_size": 1,
75- "max-num-seqs": 60,75+ "pipeline_parallel_size": 1,
76- "async-scheduling": true,76+ "data_parallel_rpc_port": 9000,
77- "no_disable_hybrid_kv_cache_manager": true,77+ "served_model_name": "dsv4",
78- "safetensors-load-strategy": "prefetch",78+ "model": "<请按实际情况填写模型权重文件的访问路径>",
79- "trust-remote-code": true,79+ "seed": 1024,
80- "tokenizer-mode": "deepseek_v4",80+ "enable_expert_parallel": true,
81- "tool-call-parser": "deepseek_v4",81+ "max-model-len": 1048576,
82- "enable-auto-tool-choice": true,82+ "max-num-batched-tokens": 120,
83- "reasoning-parser": "deepseek_v4",83+ "max-num-seqs": 60,
84- "quantization": "ascend",84+ "async-scheduling": true,
85- "profiler-config": {85+ "block-size": 128,
86- "profiler": "torch",86+ "no-enable-prefix-caching": true,
87- "torch_profiler_dir": "./vllm_profile",87+ "no_disable_hybrid_kv_cache_manager": true,
88- "torch_profiler_with_stack": false88+ "safetensors-load-strategy": "prefetch",
89- },89+ "model-loader-extra-config": {
90- "speculative-config": {"num_speculative_tokens": 1, "method":"deepseek_mtp"},90+ "enable_multithread_load": "true",
91- "compilation_config": {91+ "num_threads": 128
92- "cudagraph_mode": "FULL_DECODE_ONLY"92+ },
93- },93+ "trust-remote-code": true,
94- "kv_transfer_config": {94+ "tokenizer-mode": "deepseek_v4",
95- "kv_connector": "MooncakeHybridConnector",95+ "tool-call-parser": "deepseek_v4",
96- "kv_role": "kv_consumer",96+ "enable-auto-tool-choice": true,
97- "kv_port": "30001",97+ "reasoning-parser": "deepseek_v4",
98- "engine_id": "0"98+ "gpu_memory_utilization": 0.9,
99- },99+ "quantization": "ascend",
100- "additional-config": {100+ "compilation-config": {
101- "ascend_compilation_config": {101+ "cudagraph_mode": "FULL_DECODE_ONLY"
102- "enable_npugraph_ex": true,102+ },
103- "enable_static_kernel": false103+ "speculative-config": {
104- },104+ "num_speculative_tokens": 1,
105- "eplb_config": {105+ "method": "mtp",
106- "dynamic_eplb":true,106+ "enforce_eager": true
107- "expert_heat_collection_interval":600,107+ },
108- "algorithm_execution_interval":50,108+ "profiler-config": {
109- "eplb_policy_type":2,109+ "profiler": "torch",
110- "num_redundant_experts":16110+ "torch_profiler_dir": "./vllm_profile",
111- },111+ "torch_profiler_with_stack": false
112- "enable_cpu_binding": true,112+ },
113- "multistream_dsa_preprocess": false,113+ "additional-config": {
114- "multistream_overlap_shared_expert":false,114+ "enable_cpu_binding": true,
115- "recompute_scheduler_enable":true115+ "multistream_overlap_shared_expert": true,
116- }116+ "recompute_scheduler_enable": true,
117- }117+ "ascend_compilation_config": {
118- }118+ "enable_npugraph_ex": true,
119-}119+ "enable_static_kernel": false
120+ }
121+ },
122+ "kv_transfer_config": {
123+ "kv_connector": "MooncakeHybridConnector",
124+ "kv_role": "kv_consumer",
125+ "kv_port": "30100",
126+ "engine_id": "1"
127+ }
128+ }
129+ }
130+}
@@ -0,0 +1,74 @@
1+nic_name="xxxx" # change to your own nic name
atomgit-botatomgit-bot
atomgit-botatomgit-bot7月10日

🟡 Medium Priority

建议:在脚本第1行之前添加 #!/bin/bash,第2行添加 set -euo pipefail

likedislike
不准确?
atomgit-botatomgit-bot7月10日

🟡 Medium Priority

脚本 run_dp_template_decode.sh 没有设置 set -euo pipefail。如果任一 export 语句或 vllm serve 命令失败(例如 $LD_PRELOAD 中指定的 .so 文件不存在、vLLM 进程崩溃),脚本仍会继续执行,导致:1) 以不完整的环境变量启动后续逻辑;2) 用户难以排查失败原因。该脚本作为模板被用户直接执行,且被 vllm_to_motor.py 解析用于生成配置,错误静默传播会浪费排查时间。

建议:在脚本开头(第 1 行 nic_name 赋值之前)添加 set -euo pipefail(或至少 set -eu),确保任何命令失败时脚本立即退出。

likedislike
不准确?
2+local_ip=xx.xx.xx.2 # change to your own ip
3+ 
4+export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
5+export HCCL_OP_EXPANSION_MODE="AIV"
6+export TASK_QUEUE_ENABLE=1
7+export VLLM_RPC_TIMEOUT=3600000
8+export VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=30000
9+export HCCL_EXEC_TIMEOUT=204
10+export HCCL_CONNECT_TIMEOUT=1200
11+export HCCL_IF_IP=$local_ip
12+export GLOO_SOCKET_IFNAME=$nic_name
13+export TP_SOCKET_IFNAME=$nic_name
14+export HCCL_SOCKET_IFNAME=$nic_name
15+export OMP_PROC_BIND=false
16+export OMP_NUM_THREADS=10
17+export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
18+export HCCL_BUFFSIZE=1024
19+export ASCEND_RT_VISIBLE_DEVICES=$1
20+ 
21+vllm serve /root/.cache/modelscope/hub/models/vllm-ascend/DeepSeek-V4-Flash-w8a8-mtp \
22+ --host 0.0.0.0 \
23+ --port $2 \
24+ --data-parallel-size $3 \
25+ --data-parallel-rank $4 \
26+ --data-parallel-address $5 \
27+ --data-parallel-rpc-port $6 \
28+ --tensor-parallel-size $7 \
atomgit-botatomgit-bot
atomgit-botatomgit-bot7月10日

🟡 Medium Priority

脚本中存在多处未加引号的变量展开,可能因单词拆分(word splitting)或 glob 展开导致非预期行为:

  • 第 23-28 行:--port $2--data-parallel-size $3 等 → 应加引号

用户需要将模板中的占位符(xxxxxx.xx.xx.2)替换为实际值;若值中包含空格(例如网卡名含空格虽不常见但在某些环境可能发生),变量将错误拆分。虽概率不高,但作为模板应示范最佳实践。

建议:为所有变量展开加双引号:export HCCL_IF_IP="$local_ip"export ASCEND_RT_VISIBLE_DEVICES="$1"--port "$2" 等。

likedislike
不准确?
atomgit-botatomgit-bot7月10日

🟡 Medium Priority

建议:对所有 $1~$7 位置参数添加双引号保护。

改动建议
28
+ export ASCEND_RT_VISIBLE_DEVICES="$1"
29
+
30
+ vllm serve /root/.cache/modelscope/hub/models/vllm-ascend/DeepSeek-V4-Flash-w8a8-mtp \
31
+ --host 0.0.0.0 \
32
+ --port "$2" \
33
+ --data-parallel-size "$3" \
34
+ --data-parallel-rank "$4" \
35
+ --data-parallel-address "$5" \
36
+ --data-parallel-rpc-port "$6" \
28
- --tensor-parallel-size $7 \
37
+ --tensor-parallel-size "$7" \
应用建议
likedislike
不准确?
29+ --enable-expert-parallel \
30+ --seed 1024 \
31+ --served-model-name dsv4 \
32+ --max-model-len 1048576 \
33+ --max-num-batched-tokens 120 \
34+ --max-num-seqs 60 \
35+ --async-scheduling \
36+ --block-size 128 \
37+ --no-disable-hybrid-kv-cache-manager \
38+ --no-enable-prefix-caching \
39+ --safetensors-load-strategy 'prefetch' \
40+ --trust-remote-code \
41+ --tokenizer-mode deepseek_v4 \
42+ --model-loader-extra-config='{"enable_multithread_load": "true", "num_threads": 128}' \
43+ --tool-call-parser deepseek_v4 \
44+ --enable-auto-tool-choice \
45+ --reasoning-parser deepseek_v4 \
46+ --gpu-memory-utilization 0.9 \
47+ --quantization ascend \
48+ --speculative-config '{"num_speculative_tokens": 1,"method": "mtp","enforce_eager": true}' \
49+ --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \
50+ --kv-transfer-config \
51+ '{"kv_connector": "MooncakeHybridConnector",
52+ "kv_role": "kv_consumer",
53+ "kv_port": "30100",
54+ "engine_id": "1",
55+ "kv_connector_extra_config": {
56+ "prefill": {
57+ "dp_size": 4,
58+ "tp_size": 4
59+ },
60+ "decode": {
61+ "dp_size": 16,
62+ "tp_size": 1
63+ }
64+ }
65+ }' \
66+ --additional-config '{
67+ "ascend_compilation_config":{
68+ "enable_npugraph_ex":true,
69+ "enable_static_kernel":false
70+ },
71+ "enable_cpu_binding":true,
72+ "multistream_overlap_shared_expert":true,
73+ "recompute_scheduler_enable":true
74+ }'
@@ -0,0 +1,38 @@
1+export OMP_PROC_BIND=false
atomgit-botatomgit-bot
atomgit-botatomgit-bot7月10日

🟡 Medium Priority

run_dp_template_decode.sh 相同,run_dp_template_hybrid.sh 没有设置 set -euo pipefail。若导出或 vLLM 命令失败,脚本静默继续运行,错误难以发现。

建议:在第 1 行后添加 set -euo pipefail

likedislike
不准确?
atomgit-botatomgit-bot7月10日

🟡 Medium Priority

建议:在脚本第1行之前添加 #!/bin/bash,第2行添加 set -euo pipefail

likedislike
不准确?
2+export OMP_NUM_THREADS=10
3+export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
4+export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
5+export HCCL_BUFFSIZE=1024
6+export VLLM_ASCEND_ENABLE_FLASHCOMM1=1
7+export TASK_QUEUE_ENABLE=1
8+export HCCL_OP_EXPANSION_MODE="AIV"
9+ 
10+vllm serve /root/.cache/modelscope/hub/models/vllm-ascend/DeepSeek-V4-Flash-w8a8-mtp \
11+ --max-model-len 1048576 \
12+ --max-num-batched-tokens 10240 \
13+ --served-model-name dsv4 \
14+ --gpu-memory-utilization 0.9 \
15+ --api-server-count 1 \
16+ --max-num-seqs 64 \
17+ --data-parallel-size 4 \
18+ --tensor-parallel-size 4 \
19+ --enable-expert-parallel \
20+ --tokenizer-mode deepseek_v4 \
21+ --tool-call-parser deepseek_v4 \
22+ --enable-auto-tool-choice \
23+ --reasoning-parser deepseek_v4 \
24+ --safetensors-load-strategy 'prefetch' \
25+ --model-loader-extra-config='{"enable_multithread_load": "true", "num_threads": 128}' \
26+ --quantization ascend \
27+ --port 8900 \
28+ --block-size 128 \
29+ --speculative-config '{"num_speculative_tokens": 1,"method": "mtp","enforce_eager": true}' \
30+ --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \
31+ --async-scheduling \
32+ --additional-config '
33+ {"ascend_compilation_config":{
34+ "enable_npugraph_ex":true,
35+ "enable_static_kernel":false
36+ },
37+ "enable_cpu_binding": true,
38+ "multistream_overlap_shared_expert":true}'
@@ -0,0 +1,66 @@
1+nic_name="xxxx" # change to your own nic name
atomgit-botatomgit-bot
atomgit-botatomgit-bot7月10日

🟡 Medium Priority

建议:在脚本第1行之前添加 #!/bin/bash,第2行添加 set -euo pipefail

likedislike
不准确?
atomgit-botatomgit-bot7月10日

🟡 Medium Priority

脚本 run_dp_template_prefill.sh 没有设置 set -euo pipefail。与 decode 脚本同样的可靠性问题:命令失败会被静默忽略。

建议:在第 1 行后添加 set -euo pipefail

likedislike
不准确?
2+local_ip=xx.xx.xx.1 # change to your own ip
3+ 
4+export HCCL_IF_IP=$local_ip
5+export GLOO_SOCKET_IFNAME=$nic_name
6+export TP_SOCKET_IFNAME=$nic_name
7+export HCCL_SOCKET_IFNAME=$nic_name
8+export VLLM_RPC_TIMEOUT=3600000
9+export VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=30000
10+export HCCL_EXEC_TIMEOUT=204
11+export HCCL_CONNECT_TIMEOUT=120
12+export OMP_PROC_BIND=false
13+export OMP_NUM_THREADS=10
14+export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
15+export HCCL_BUFFSIZE=2560
16+export TASK_QUEUE_ENABLE=1
17+export VLLM_ASCEND_ENABLE_FLASHCOMM1=1
18+export HCCL_OP_EXPANSION_MODE="AIV"
19+export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
20+export ASCEND_RT_VISIBLE_DEVICES=$1
21+ 
22+vllm serve /root/.cache/modelscope/hub/models/vllm-ascend/DeepSeek-V4-Flash-w8a8-mtp \
23+ --host 0.0.0.0 \
24+ --port $2 \
25+ --data-parallel-size $3 \
26+ --data-parallel-rank $4 \
27+ --data-parallel-address $5 \
28+ --data-parallel-rpc-port $6 \
29+ --tensor-parallel-size $7 \
atomgit-botatomgit-bot
atomgit-botatomgit-bot7月10日

🟡 Medium Priority

建议:对所有 $1~$7 位置参数添加双引号保护。

改动建议
29
+ export ASCEND_RT_VISIBLE_DEVICES="$1"
30
+
31
+ vllm serve /root/.cache/modelscope/hub/models/vllm-ascend/DeepSeek-V4-Flash-w8a8-mtp \
32
+ --host 0.0.0.0 \
33
+ --port "$2" \
34
+ --data-parallel-size "$3" \
35
+ --data-parallel-rank "$4" \
36
+ --data-parallel-address "$5" \
37
+ --data-parallel-rpc-port "$6" \
29
- --tensor-parallel-size $7 \
38
+ --tensor-parallel-size "$7" \
应用建议
likedislike
不准确?
atomgit-botatomgit-bot7月10日

🟡 Medium Priority

脚本中存在多处未加引号的变量展开:

  • 第 24-29 行:--port $2--data-parallel-size $3

若用户替换占位符后的值含有空格,变量将被错误拆分。

建议:为所有变量展开加双引号:export HCCL_IF_IP="$local_ip"export ASCEND_RT_VISIBLE_DEVICES="$1"--port "$2" 等。

likedislike
不准确?
30+ --enable-expert-parallel \
31+ --seed 1024 \
32+ --served-model-name dsv4 \
33+ --max-model-len 1048576 \
34+ --max-num-batched-tokens 8192 \
35+ --max-num-seqs 16 \
36+ --no-disable-hybrid-kv-cache-manager \
37+ --model-loader-extra-config='{"enable_multithread_load": "true", "num_threads": 128}' \
38+ --no-enable-prefix-caching \
39+ --safetensors-load-strategy 'prefetch' \
40+ --speculative-config '{"num_speculative_tokens": 1,"method": "mtp","enforce_eager": true}' \
41+ --trust-remote-code \
42+ --block-size 128 \
43+ --tokenizer-mode deepseek_v4 \
44+ --tool-call-parser deepseek_v4 \
45+ --enable-auto-tool-choice \
46+ --reasoning-parser deepseek_v4 \
47+ --gpu-memory-utilization 0.9 \
48+ --quantization ascend \
49+ --enforce-eager \
50+ --additional-config '{"enable_cpu_binding": true, "enable_shared_expert_dp": true, "enable_dsa_cp": true}' \
51+ --kv-transfer-config \
52+ '{"kv_connector": "MooncakeHybridConnector",
53+ "kv_role": "kv_producer",
54+ "kv_port": "30000",
55+ "engine_id": "0",
56+ "kv_connector_extra_config": {
57+ "prefill": {
58+ "dp_size": 4,
59+ "tp_size": 4
60+ },
61+ "decode": {
62+ "dp_size": 16,
63+ "tp_size": 1
64+ }
65+ }
66+ }'
@@ -0,0 +1,1517 @@
1+# Copyright (c) Huawei Technologies Co., Ltd. 2025-2026. All rights reserved.
2+#
3+# 独立脚本:不依赖 motor 包,可直接拷贝到任意目录运行。
4+# 部署示例:只需拷贝本文件到 /mnt/share/g00955508/b081_0707/ 即可使用。
5+#
6+# 用法:
7+# PD 混部: 同目录放置 run_dp_template.sh 后执行
8+# python vllm_to_motor.py --deploy-scenario hybrid --hardware-type A3
9+# PD 分离: 同目录放置 run_dp_template_prefill.sh / run_dp_template_decode.sh 后执行
10+# python vllm_to_motor.py --deploy-scenario separate --hardware-type A3
11+# 可选: --weight-path <路径> --image-name <镜像>
12+# 输出: output_config/user_config.json、output_config/env.json
13+#
14+# 并行度:脚本 kv extra 提供 world_size;--hardware-type 仅在 tp 超过单节点上限时重算 engine dp/tp。
15+# deploy Pod 切分:按 world_size 打包到节点(pod 数 = world/cards,每 pod 卡数 = cards 或 world)。
16+# env.json:仅转换脚本中字面量 export。部署/运行时在 shell 中展开的环境变量不写入 env.json:
17+# - 显式跳过:HCCL_IF_IP、网卡名、LD_PRELOAD、ASCEND_RT_VISIBLE_DEVICES 等(见 SKIP_ENV_KEYS)
18+# - 值含 $ 引用:如 LD_PRELOAD=...:$LD_PRELOAD、LD_LIBRARY_PATH=...:$LD_LIBRARY_PATH、$1/$nic_name 等
19+ 
20+from __future__ import annotations
21+ 
22+import argparse
23+import json
24+import re
25+import shlex
26+import sys
27+from pathlib import Path
28+from typing import Any
29+ 
30+# ---------------------------------------------------------------------------
31+# converter core (standalone copy, no motor import)
32+# ---------------------------------------------------------------------------
33+ 
34+SKIP_RUNTIME_KEYS = frozenset(
35+ {
36+ "host",
37+ "port",
38+ "data_parallel_rank",
39+ "data_parallel_address",
40+ }
41+)
42+ 
43+# 并行度以 kv_connector_extra_config 为准;CLI 中的以下字段忽略
44+SKIP_PARALLEL_CLI_KEYS = frozenset(
45+ {
46+ "data_parallel_size",
47+ "tensor_parallel_size",
48+ "data_parallel_rpc_port",
49+ }
50+)
51+ 
52+DEFAULT_DP_RPC_PORT = 9000
53+ 
54+# Motor 部署/运行时自行注入,不转换到 env.json
55+SKIP_ENV_KEYS = frozenset(
56+ {
57+ "HCCL_IF_IP",
58+ "GLOO_SOCKET_IFNAME",
59+ "TP_SOCKET_IFNAME",
60+ "HCCL_SOCKET_IFNAME",
61+ "LD_PRELOAD",
62+ "ASCEND_RT_VISIBLE_DEVICES",
63+ }
64+)
65+ 
66+# export 值中的 shell 变量引用($VAR / ${VAR} / $1),表示运行时拼接,不转换
67+_SHELL_VAR_REF = re.compile(r"\$(?:\{[^}]+\}|[A-Za-z_][A-Za-z0-9_]*|\d+)")
68+ 
69+DEFAULT_ENV_COMMON = {
70+ "CANN_INSTALL_PATH": "/usr/local/Ascend",
71+}
72+ 
73+DEFAULT_OUTPUT_DIR = "output_config"
74+DEFAULT_USER_CONFIG_NAME = "user_config.json"
75+DEFAULT_ENV_NAME = "env.json"
76+DEPLOY_SCENARIO_HYBRID = "hybrid"
77+DEPLOY_SCENARIO_SEPARATE = "separate"
78+ 
79+AUTO_HYBRID_SCRIPT = "run_dp_template_hybrid.sh"
80+AUTO_PREFILL_SCRIPT = "run_dp_template_prefill.sh"
81+AUTO_DECODE_SCRIPT = "run_dp_template_decode.sh"
82+MANUAL_FILL_WEIGHT_MOUNT_PATH = "<请按实际情况填写模型权重文件的访问路径>"
83+MANUAL_FILL_IMAGE_NAME = "<请按实际情况填写镜像名称>"
84+ 
85+_ANSI_BLUE = "\033[34m"
86+_ANSI_RESET = "\033[0m"
87+ 
88+ENV_CONFIG_KEY_ORDER = (
89+ "version",
90+ "motor_common_env",
91+ "motor_controller_env",
92+ "motor_coordinator_env",
93+ "motor_engine_prefill_env",
94+ "motor_engine_decode_env",
95+ "motor_kv_cache_pool_env",
96+)
97+ 
98+HYBRID_ENV_CONFIG_KEY_ORDER = (
99+ "version",
100+ "motor_common_env",
101+ "motor_controller_env",
102+ "motor_coordinator_env",
103+ "motor_engine_union_env",
104+ "motor_kv_cache_pool_env",
105+)
106+ 
107+UNION_ENV_KEY_ORDER = (
108+ "VLLM_RPC_TIMEOUT",
109+ "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS",
110+ "HCCL_EXEC_TIMEOUT",
111+ "HCCL_CONNECT_TIMEOUT",
112+ "OMP_PROC_BIND",
113+ "OMP_NUM_THREADS",
114+ "PYTORCH_NPU_ALLOC_CONF",
115+ "HCCL_BUFFSIZE",
116+ "TASK_QUEUE_ENABLE",
117+ "HCCL_OP_EXPANSION_MODE",
118+ "VLLM_ASCEND_ENABLE_FLASHCOMM1",
119+ "VLLM_ASCEND_ENABLE_FUSED_MC2",
120+ "DYNAMIC_EPLB",
121+ "VLLM_TORCH_PROFILER_DIR",
122+ "VLLM_TORCH_PROFILER_WITH_STACK",
123+)
124+ 
125+PREFILL_ENV_KEY_ORDER = (
126+ "VLLM_RPC_TIMEOUT",
127+ "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS",
128+ "HCCL_EXEC_TIMEOUT",
129+ "HCCL_CONNECT_TIMEOUT",
130+ "OMP_PROC_BIND",
131+ "OMP_NUM_THREADS",
132+ "PYTORCH_NPU_ALLOC_CONF",
133+ "HCCL_BUFFSIZE",
134+ "TASK_QUEUE_ENABLE",
135+ "HCCL_OP_EXPANSION_MODE",
136+ "VLLM_ASCEND_ENABLE_FLASHCOMM1",
137+)
138+ 
139+DECODE_ENV_KEY_ORDER = (
140+ "HCCL_OP_EXPANSION_MODE",
141+ "TASK_QUEUE_ENABLE",
142+ "VLLM_RPC_TIMEOUT",
143+ "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS",
144+ "HCCL_EXEC_TIMEOUT",
145+ "HCCL_CONNECT_TIMEOUT",
146+ "VLLM_ASCEND_ENABLE_FUSED_MC2",
147+ "OMP_PROC_BIND",
148+ "OMP_NUM_THREADS",
149+ "PYTORCH_NPU_ALLOC_CONF",
150+ "HCCL_BUFFSIZE",
151+ "VLLM_ASCEND_ENABLE_FLASHCOMM1",
152+ "DYNAMIC_EPLB",
153+ "VLLM_TORCH_PROFILER_DIR",
154+ "VLLM_TORCH_PROFILER_WITH_STACK",
155+)
156+ 
157+UNDERSCORE_KEYS = frozenset(
158+ {
159+ "data_parallel_size",
160+ "tensor_parallel_size",
161+ "pipeline_parallel_size",
162+ "data_parallel_rpc_port",
163+ "enable_expert_parallel",
164+ "gpu_memory_utilization",
165+ "no_disable_hybrid_kv_cache_manager",
166+ "served_model_name",
167+ "model",
168+ "kv_transfer_config",
169+ }
170+)
171+ 
172+DEFAULT_PROFILER_CONFIG = {
173+ "profiler": "torch",
174+ "torch_profiler_dir": "./vllm_profile",
175+ "torch_profiler_with_stack": False,
176+}
177+ 
178+DEFAULT_DEPLOY_CONFIG = {
179+ "p_instances_num": 1,
180+ "d_instances_num": 1,
181+ "single_p_instance_pod_num": 1,
182+ "single_d_instance_pod_num": 1,
183+ "p_pod_npu_num": 16,
184+ "d_pod_npu_num": 16,
185+ "image_name": "",
186+ "job_id": "mindie-motor",
187+ "hardware_type": "800I_A3",
188+ "weight_mount_path": "/mnt/weight/",
189+}
190+ 
191+# 各硬件平台默认部署参数(其余字段可生成后手工修改)
192+HARDWARE_PRESETS: dict[str, dict[str, Any]] = {
193+ "800I_A2": {
194+ "hardware_type": "800I_A2",
195+ "cards_per_node": 8,
196+ "image_name": "<请手动填写镜像名称,例如:mindie-motor-vllm:dev-26.1.0.B081-800I-A3-py311-Ubuntu24.04-lts-aarch64>",
197+ "weight_mount_path": "/data01/models/",
198+ "job_id": "mindie-motor",
199+ },
200+ "800I_A3": {
201+ "hardware_type": "800I_A3",
202+ "cards_per_node": 16,
203+ "image_name": "<请手动填写镜像名称,例如:mindie-motor-vllm:dev-26.1.0.B081-800I-A3-py311-Ubuntu24.04-lts-aarch64>",
204+ "weight_mount_path": "/mnt/weight/",
205+ "job_id": "mindie-motor",
206+ },
207+}
208+ 
209+USER_CONFIG_KEY_ORDER = (
210+ "version",
211+ "motor_deploy_config",
212+ "motor_controller_config",
213+ "motor_coordinator_config",
214+ "motor_engine_prefill_config",
215+ "motor_engine_decode_config",
216+)
217+ 
218+HYBRID_USER_CONFIG_KEY_ORDER = (
219+ "version",
220+ "motor_deploy_config",
221+ "motor_controller_config",
222+ "motor_coordinator_config",
223+ "motor_engine_union_config",
224+)
225+ 
226+DEPLOY_CONFIG_KEY_ORDER = (
227+ "p_instances_num",
228+ "d_instances_num",
229+ "single_p_instance_pod_num",
230+ "single_d_instance_pod_num",
231+ "p_pod_npu_num",
232+ "d_pod_npu_num",
233+ "image_name",
234+ "job_id",
235+ "hardware_type",
236+ "weight_mount_path",
237+)
238+ 
239+HYBRID_DEPLOY_CONFIG_KEY_ORDER = (
240+ "deploy_mode",
241+ "hybrid_instances_num",
242+ "single_hybrid_instance_pod_num",
243+ "hybrid_pod_npu_num",
244+ "image_name",
245+ "job_id",
246+ "hardware_type",
247+ "weight_mount_path",
248+)
249+ 
250+ENGINE_ROLE_KEY_ORDER = ("engine_type", "engine_config")
251+ 
252+HYBRID_ENGINE_CONFIG_KEY_ORDER = (
253+ "served_model_name",
254+ "model",
255+ "gpu_memory_utilization",
256+ "data_parallel_size",
257+ "tensor_parallel_size",
258+ "pipeline_parallel_size",
259+ "enable_expert_parallel",
260+ "data_parallel_rpc_port",
261+ "seed",
262+ "max-model-len",
263+ "max-num-batched-tokens",
264+ "max-num-seqs",
265+ "block-size",
266+ "enforce-eager",
267+ "async-scheduling",
268+ "enable-prefix-caching",
269+ "no-enable-prefix-caching",
270+ "trust-remote-code",
271+ "quantization",
272+ "safetensors-load-strategy",
273+ "model-loader-extra-config",
274+ "tokenizer-mode",
275+ "tool-call-parser",
276+ "enable-auto-tool-choice",
277+ "reasoning-parser",
278+ "speculative-config",
279+ "compilation-config",
280+ "profiler-config",
281+ "additional-config",
282+)
283+ 
284+PREFILL_ENGINE_CONFIG_KEY_ORDER = (
285+ "data_parallel_size",
286+ "tensor_parallel_size",
287+ "pipeline_parallel_size",
288+ "data_parallel_rpc_port",
289+ "served_model_name",
290+ "model",
291+ "seed",
292+ "enable_expert_parallel",
293+ "max-model-len",
294+ "max-num-batched-tokens",
295+ "max-num-seqs",
296+ "block-size",
297+ "enforce-eager",
298+ "async-scheduling",
299+ "no_disable_hybrid_kv_cache_manager",
300+ "enable-prefix-caching",
301+ "trust-remote-code",
302+ "gpu_memory_utilization",
303+ "quantization",
304+ "safetensors-load-strategy",
305+ "model-loader-extra-config",
306+ "tokenizer-mode",
307+ "tool-call-parser",
308+ "enable-auto-tool-choice",
309+ "reasoning-parser",
310+ "speculative-config",
311+ "profiler-config",
312+ "additional-config",
313+ "kv_transfer_config",
314+)
315+ 
316+DECODE_ENGINE_CONFIG_KEY_ORDER = (
317+ "data_parallel_size",
318+ "tensor_parallel_size",
319+ "pipeline_parallel_size",
320+ "data_parallel_rpc_port",
321+ "served_model_name",
322+ "model",
323+ "seed",
324+ "enable_expert_parallel",
325+ "max-model-len",
326+ "max-num-batched-tokens",
327+ "max-num-seqs",
328+ "async-scheduling",
329+ "block-size",
330+ "no-enable-prefix-caching",
331+ "no_disable_hybrid_kv_cache_manager",
332+ "safetensors-load-strategy",
333+ "model-loader-extra-config",
334+ "trust-remote-code",
335+ "tokenizer-mode",
336+ "tool-call-parser",
337+ "enable-auto-tool-choice",
338+ "reasoning-parser",
339+ "gpu_memory_utilization",
340+ "quantization",
341+ "compilation-config",
342+ "speculative-config",
343+ "profiler-config",
344+ "additional-config",
345+ "kv_transfer_config",
346+)
347+ 
348+NESTED_KEY_ORDERS: dict[str, tuple[str, ...]] = {
349+ "profiler-config": (
350+ "profiler",
351+ "torch_profiler_dir",
352+ "torch_profiler_with_stack",
353+ ),
354+ "kv_transfer_config": (
355+ "kv_connector",
356+ "kv_role",
357+ "kv_port",
358+ "engine_id",
359+ ),
360+ "compilation-config": ("cudagraph_mode",),
361+ "ascend_compilation_config": (
362+ "enable_npugraph_ex",
363+ "enable_static_kernel",
364+ ),
365+}
366+ 
367+ 
368+def reorder_dict(
369+ data: dict[str, Any],
370+ key_order: tuple[str, ...] | list[str],
371+ *,
372+ nested_orders: dict[str, tuple[str, ...]] | None = None,
373+) -> dict[str, Any]:
374+ """Reorder dict keys; unknown keys are appended at the end."""
375+ nested_orders = nested_orders or NESTED_KEY_ORDERS
376+ ordered: dict[str, Any] = {}
377+ for key in key_order:
378+ if key not in data:
379+ continue
380+ value = data[key]
381+ if isinstance(value, dict) and key in nested_orders:
382+ value = reorder_dict(value, nested_orders[key], nested_orders=nested_orders)
383+ elif isinstance(value, dict) and key == "additional-config":
384+ value = _reorder_additional_config(value)
385+ ordered[key] = value
386+ for key, value in data.items():
387+ if key in ordered:
388+ continue
389+ if isinstance(value, dict) and key in nested_orders:
390+ value = reorder_dict(value, nested_orders[key], nested_orders=nested_orders)
391+ elif isinstance(value, dict) and key == "additional-config":
392+ value = _reorder_additional_config(value)
393+ ordered[key] = value
394+ return ordered
395+ 
396+ 
397+def _reorder_additional_config(data: dict[str, Any]) -> dict[str, Any]:
398+ preferred = (
399+ "enable_cpu_binding",
400+ "enable_shared_expert_dp",
401+ "enable_dsa_cp",
402+ "multistream_overlap_shared_expert",
403+ "recompute_scheduler_enable",
404+ "ascend_compilation_config",
405+ )
406+ ordered = reorder_dict(data, preferred, nested_orders=NESTED_KEY_ORDERS)
407+ return ordered
408+ 
409+ 
410+def format_engine_config(engine_config: dict[str, Any], *, role: str) -> dict[str, Any]:
411+ if role == "hybrid":
412+ key_order = HYBRID_ENGINE_CONFIG_KEY_ORDER
413+ elif role == "prefill":
414+ key_order = PREFILL_ENGINE_CONFIG_KEY_ORDER
415+ else:
416+ key_order = DECODE_ENGINE_CONFIG_KEY_ORDER
417+ return reorder_dict(engine_config, key_order)
418+ 
419+ 
420+def format_engine_role_config(role_config: dict[str, Any], *, role: str) -> dict[str, Any]:
421+ formatted = reorder_dict(role_config, ENGINE_ROLE_KEY_ORDER)
422+ if "engine_config" in formatted:
423+ formatted["engine_config"] = format_engine_config(formatted["engine_config"], role=role)
424+ return formatted
425+ 
426+ 
427+def format_user_config(config: dict[str, Any]) -> dict[str, Any]:
428+ """Apply canonical key order for user_config.json output."""
429+ if "motor_engine_union_config" in config:
430+ ordered = reorder_dict(config, HYBRID_USER_CONFIG_KEY_ORDER)
431+ if "motor_deploy_config" in ordered:
432+ ordered["motor_deploy_config"] = reorder_dict(
433+ ordered["motor_deploy_config"],
434+ HYBRID_DEPLOY_CONFIG_KEY_ORDER,
435+ )
436+ if "motor_engine_union_config" in ordered:
437+ ordered["motor_engine_union_config"] = format_engine_role_config(
438+ ordered["motor_engine_union_config"],
439+ role="hybrid",
440+ )
441+ return ordered
442+ 
443+ if "motor_engine_prefill_config" not in config and "version" not in config:
444+ role = "prefill"
445+ kv = config.get("kv_transfer_config") or {}
446+ if isinstance(kv, dict) and str(kv.get("kv_role", "")).lower() == "kv_consumer":
447+ role = "decode"
448+ return format_engine_config(config, role=role)
449+ 
450+ ordered = reorder_dict(config, USER_CONFIG_KEY_ORDER)
451+ if "motor_deploy_config" in ordered:
452+ ordered["motor_deploy_config"] = reorder_dict(
453+ ordered["motor_deploy_config"],
454+ DEPLOY_CONFIG_KEY_ORDER,
455+ )
456+ if "motor_engine_prefill_config" in ordered:
457+ ordered["motor_engine_prefill_config"] = format_engine_role_config(
458+ ordered["motor_engine_prefill_config"],
459+ role="prefill",
460+ )
461+ if "motor_engine_decode_config" in ordered:
462+ ordered["motor_engine_decode_config"] = format_engine_role_config(
463+ ordered["motor_engine_decode_config"],
464+ role="decode",
465+ )
466+ return ordered
467+ 
468+ 
469+def normalize_hardware_type(value: str) -> str:
470+ """Normalize ``A2`` / ``800I-A2`` -> ``800I_A2``."""
471+ text = value.strip().upper().replace("-", "_")
472+ if text in {"A2", "800I_A2", "910B"}:
473+ return "800I_A2"
474+ if text in {"A3", "800I_A3"}:
475+ return "800I_A3"
476+ if text in HARDWARE_PRESETS:
477+ return text
478+ raise ValueError(f"unsupported hardware type: {value!r}, use A2 or A3")
479+ 
480+ 
481+def _get_kv_config(cli_args: dict[str, Any]) -> dict[str, Any] | None:
482+ for key in ("kv-transfer-config", "kv_transfer_config"):
483+ raw = cli_args.get(key)
484+ if raw is None:
485+ continue
486+ if isinstance(raw, str):
487+ return json.loads(raw)
488+ if isinstance(raw, dict):
489+ return raw
490+ return None
491+ 
492+ 
493+def extract_parallel_from_cli_args(cli_args: dict[str, Any]) -> dict[str, int]:
494+ """Read P/D dp/tp from kv_connector_extra_config (preferred)."""
495+ kv_config = _get_kv_config(cli_args)
496+ if not kv_config:
497+ raise ValueError("脚本中缺少 kv-transfer-config / kv_connector_extra_config,无法推断 P/D 并行度。")
498+ 
499+ extra = kv_config.get("kv_connector_extra_config") or {}
500+ prefill = extra.get("prefill") or {}
501+ decode = extra.get("decode") or {}
502+ 
503+ def _as_int(value: Any) -> int | None:
504+ if value is None or value is True or value is False:
505+ return None
506+ try:
507+ return int(value)
508+ except (TypeError, ValueError):
509+ return None
510+ 
511+ prefill_dp = _as_int(prefill.get("dp_size"))
512+ prefill_tp = _as_int(prefill.get("tp_size"))
513+ decode_dp = _as_int(decode.get("dp_size"))
514+ decode_tp = _as_int(decode.get("tp_size"))
515+ 
516+ if not all([prefill_dp, prefill_tp, decode_dp, decode_tp]):
517+ raise ValueError("kv_connector_extra_config 中需包含完整的 prefill/decode dp_size 与 tp_size。")
518+ return {
519+ "prefill_dp": prefill_dp,
520+ "prefill_tp": prefill_tp,
521+ "decode_dp": decode_dp,
522+ "decode_tp": decode_tp,
523+ }
524+ 
525+ 
526+def _as_positive_int(value: Any, *, field: str) -> int:
527+ if value is None or value is True or value is False:
528+ raise ValueError(f"脚本中缺少有效的 {field}。")
529+ try:
530+ parsed = int(value)
531+ except (TypeError, ValueError) as exc:
532+ raise ValueError(f"脚本中 {field} 无法解析为整数: {value!r}") from exc
533+ if parsed <= 0:
534+ raise ValueError(f"脚本中 {field} 必须为正整数,当前为 {parsed}。")
535+ return parsed
536+ 
537+ 
538+def extract_hybrid_parallel_from_cli_args(cli_args: dict[str, Any]) -> tuple[int, int]:
539+ """Read dp/tp from hybrid script CLI flags; omitted values default to 1."""
540+ dp_raw = cli_args.get("data-parallel-size", cli_args.get("data_parallel_size"))
541+ tp_raw = cli_args.get("tensor-parallel-size", cli_args.get("tensor_parallel_size"))
542+ dp = 1 if dp_raw is None else _as_positive_int(dp_raw, field="--data-parallel-size")
543+ tp = 1 if tp_raw is None else _as_positive_int(tp_raw, field="--tensor-parallel-size")
544+ return dp, tp
545+ 
546+ 
547+def remap_hybrid_parallel_for_hardware(dp: int, tp: int, hardware_type: str) -> tuple[int, int]:
548+ """Remap hybrid dp/tp while preserving world_size (same rules as PD remap)."""
549+ cards = cards_per_node(hardware_type)
550+ world = dp * tp
551+ new_tp = min(tp, cards)
552+ if new_tp == tp:
553+ return dp, tp
554+ if world % new_tp != 0:
555+ raise ValueError(
556+ f"hybrid: world_size={world} 在 tp 上限 {cards} 下无法整除为 tp={new_tp},"
557+ "请检查脚本中的 --data-parallel-size / --tensor-parallel-size。"
558+ )
559+ return world // new_tp, new_tp
560+ 
561+ 
562+def cards_per_node(hardware_type: str) -> int:
563+ return int(HARDWARE_PRESETS[normalize_hardware_type(hardware_type)]["cards_per_node"])
564+ 
565+ 
566+def remap_parallel_for_hardware(
567+ parallel: dict[str, int],
568+ hardware_type: str,
569+) -> dict[str, int]:
570+ """Remap dp/tp while preserving world_size.
571+ 
572+ Script tp is kept when already <= cards-per-node; only values above the
573+ hardware cap (8 for A2, 16 for A3) are reduced and dp is increased accordingly.
574+ """
575+ cards = cards_per_node(hardware_type)
576+ 
577+ def _remap_role(dp: int, tp: int, role: str) -> tuple[int, int]:
578+ world = dp * tp
579+ new_tp = min(tp, cards)
580+ if new_tp == tp:
581+ return dp, tp
582+ if world % new_tp != 0:
583+ raise ValueError(
584+ f"{role}: world_size={world} 在 tp 上限 {cards} 下无法整除为 tp={new_tp},"
585+ "请检查 kv_connector_extra_config 中的 dp_size/tp_size。"
586+ )
587+ return world // new_tp, new_tp
588+ 
589+ p_dp, p_tp = _remap_role(parallel["prefill_dp"], parallel["prefill_tp"], "prefill")
590+ d_dp, d_tp = _remap_role(parallel["decode_dp"], parallel["decode_tp"], "decode")
591+ return {
592+ "prefill_dp": p_dp,
593+ "prefill_tp": p_tp,
594+ "decode_dp": d_dp,
595+ "decode_tp": d_tp,
596+ }
597+ 
598+ 
599+def apply_engine_parallel(
600+ engine_config: dict[str, Any],
601+ *,
602+ role: str,
603+ parallel: dict[str, int],
604+) -> None:
605+ """Write data_parallel_size / tensor_parallel_size from kv extra; rpc port fixed."""
606+ if role == "prefill":
607+ engine_config["data_parallel_size"] = parallel["prefill_dp"]
608+ engine_config["tensor_parallel_size"] = parallel["prefill_tp"]
609+ elif role == "decode":
610+ engine_config["data_parallel_size"] = parallel["decode_dp"]
611+ engine_config["tensor_parallel_size"] = parallel["decode_tp"]
612+ else:
613+ raise ValueError(f"unsupported engine role: {role}")
614+ engine_config["data_parallel_rpc_port"] = DEFAULT_DP_RPC_PORT
615+ engine_config["pipeline_parallel_size"] = 1
616+ 
617+ 
618+def _infer_role_from_cli_args(cli_args: dict[str, Any]) -> str:
619+ kv_config = _get_kv_config(cli_args) or {}
620+ role = str(kv_config.get("kv_role", "")).lower()
621+ if role == "kv_producer":
622+ return "prefill"
623+ if role == "kv_consumer":
624+ return "decode"
625+ return "prefill"
626+ 
627+ 
628+def _infer_pod_layout(dp: int, tp: int, cards: int, *, role: str) -> tuple[int, int]:
629+ """Pack dp×tp world_size onto nodes with ``cards`` NPUs each."""
630+ world = dp * tp
631+ if tp > cards:
632+ raise ValueError(f"{role}: tp={tp} 超过单节点上限 {cards} 卡。")
633+ if world <= cards:
634+ return 1, world
635+ if world % cards != 0:
636+ raise ValueError(f"{role}: world_size={world} 无法按每节点 {cards} 卡整除切分 Pod。")
637+ return world // cards, cards
638+ 
639+ 
640+def infer_motor_deploy_config(
641+ parallel: dict[str, int],
642+ hardware_type: str,
643+ *,
644+ overrides: dict[str, Any] | None = None,
645+) -> dict[str, Any]:
646+ """Infer motor_deploy_config by packing remapped dp×tp onto nodes."""
647+ hw = normalize_hardware_type(hardware_type)
648+ preset = dict(HARDWARE_PRESETS[hw])
649+ cards = int(preset.pop("cards_per_node"))
650+ 
651+ p_pods, p_npu = _infer_pod_layout(
652+ parallel["prefill_dp"],
653+ parallel["prefill_tp"],
654+ cards,
655+ role="prefill",
656+ )
657+ d_pods, d_npu = _infer_pod_layout(
658+ parallel["decode_dp"],
659+ parallel["decode_tp"],
660+ cards,
661+ role="decode",
662+ )
663+ 
664+ deploy = {
665+ "p_instances_num": 1,
666+ "d_instances_num": 1,
667+ "single_p_instance_pod_num": p_pods,
668+ "single_d_instance_pod_num": d_pods,
669+ "p_pod_npu_num": p_npu,
670+ "d_pod_npu_num": d_npu,
671+ }
672+ 
673+ deploy.update(preset)
674+ if overrides:
675+ deploy.update(overrides)
676+ return deploy
677+ 
678+ 
679+def infer_hybrid_motor_deploy_config(
680+ dp: int,
681+ tp: int,
682+ hardware_type: str,
683+ *,
684+ overrides: dict[str, Any] | None = None,
685+) -> dict[str, Any]:
686+ """Infer hybrid motor_deploy_config by packing dp×tp onto nodes."""
687+ hw = normalize_hardware_type(hardware_type)
688+ preset = dict(HARDWARE_PRESETS[hw])
689+ cards = int(preset.pop("cards_per_node"))
690+ pods, npu = _infer_pod_layout(dp, tp, cards, role="hybrid")
691+ deploy = {
692+ "deploy_mode": "infer_service_set",
693+ "hybrid_instances_num": 1,
694+ "single_hybrid_instance_pod_num": pods,
695+ "hybrid_pod_npu_num": npu,
696+ }
697+ deploy.update(preset)
698+ if overrides:
699+ deploy.update(overrides)
700+ return deploy
701+ 
702+ 
703+def resolve_hybrid_script(directory: Path) -> Path:
704+ """Read hybrid template script from *directory*."""
705+ script_path = directory / AUTO_HYBRID_SCRIPT
706+ if not script_path.is_file():
707+ raise FileNotFoundError(
708+ f"在目录 {directory} 未找到: {AUTO_HYBRID_SCRIPT}。\n"
709+ f"PD 混部场景请使用 --deploy-scenario hybrid;"
710+ f"PD 分离场景请使用 --deploy-scenario separate。"
711+ )
712+ return script_path
713+ 
714+ 
715+def resolve_pd_scripts(directory: Path) -> tuple[Path, Path]:
716+ """Read P/D template scripts from *directory*."""
717+ prefill_path = directory / AUTO_PREFILL_SCRIPT
718+ decode_path = directory / AUTO_DECODE_SCRIPT
719+ missing = [p.name for p in (prefill_path, decode_path) if not p.is_file()]
720+ if missing:
721+ raise FileNotFoundError(
722+ f"在目录 {directory} 未找到: {', '.join(missing)}。\n请放置 {AUTO_PREFILL_SCRIPT}{AUTO_DECODE_SCRIPT}。"
723+ )
724+ return prefill_path, decode_path
725+ 
726+ 
727+def apply_manual_fill_placeholders(
728+ user_config: dict[str, Any],
729+ *,
730+ hybrid: bool = False,
731+) -> None:
732+ """Mark deploy/model paths that must be edited manually after generation."""
733+ user_config["motor_deploy_config"]["weight_mount_path"] = MANUAL_FILL_WEIGHT_MOUNT_PATH
734+ if hybrid:
735+ engine_config = user_config["motor_engine_union_config"]["engine_config"]
736+ if "model" in engine_config:
737+ engine_config["model"] = MANUAL_FILL_WEIGHT_MOUNT_PATH
738+ return
739+ for role_key in ("motor_engine_prefill_config", "motor_engine_decode_config"):
740+ engine_config = user_config[role_key]["engine_config"]
741+ if "model" in engine_config:
742+ engine_config["model"] = MANUAL_FILL_WEIGHT_MOUNT_PATH
743+ 
744+ 
745+def _try_parse_json_text(raw: str) -> Any | None:
746+ """Parse CLI JSON blob; tolerate leading spaces and shlex escape artifacts."""
747+ if not isinstance(raw, str):
748+ return None
749+ text = raw.strip()
750+ if not text or text[0] not in "{[":
751+ return None
752+ if '\\"' in text:
753+ text = text.replace('\\"', '"')
754+ try:
755+ return json.loads(text)
756+ except json.JSONDecodeError:
757+ return None
758+ 
759+ 
760+def _coerce(raw: str) -> Any:
761+ lowered = raw.lower()
762+ if lowered == "true":
763+ return True
764+ if lowered == "false":
765+ return False
766+ parsed_json = _try_parse_json_text(raw)
767+ if parsed_json is not None:
768+ return parsed_json
769+ for caster in (int, float):
770+ try:
771+ return caster(raw)
772+ except ValueError:
773+ pass
774+ return raw
775+ 
776+ 
777+def _set_config(config: dict[str, Any], key: str, value: Any) -> None:
778+ if "." not in key:
779+ config[key] = value
780+ return
781+ node = config
782+ for part in key.split(".")[:-1]:
783+ name = part.replace("-", "_")
784+ if not isinstance(node.get(name), dict):
785+ node[name] = {}
786+ node = node[name]
787+ node[key.rsplit(".", 1)[-1].replace("-", "_")] = value
788+ 
789+ 
790+def cli_to_config(tokens: list[str]) -> dict[str, Any]:
791+ config: dict[str, Any] = {}
792+ rest = list(tokens)
793+ while rest:
794+ head, *rest = rest
795+ if not head.startswith("--"):
796+ config.setdefault("model", head)
797+ continue
798+ key = head[2:]
799+ if rest and not rest[0].startswith("--"):
800+ value = rest[0]
801+ _set_config(config, key, _coerce(value) if isinstance(value, str) else value)
802+ rest = rest[1:]
803+ else:
804+ _set_config(config, key, True)
805+ return config
806+ 
807+ 
808+def _unquote_shell_value(raw: str) -> str:
809+ if len(raw) >= 2 and raw[0] == raw[-1] and raw[0] in "\"'":
810+ return raw[1:-1]
811+ return raw
812+ 
813+ 
814+def _coerce_env_value(raw: str) -> Any:
815+ """Coerce export values for env.json (bools stay lowercase strings)."""
816+ lowered = raw.lower()
817+ if lowered in ("true", "false"):
818+ return lowered
819+ if raw.isdigit() or (raw.startswith("-") and raw[1:].isdigit()):
820+ return int(raw)
821+ return raw
822+ 
823+ 
824+def _value_has_shell_var_ref(raw: str) -> bool:
825+ """True when export value depends on shell expansion (lib path append, $1, etc.)."""
826+ return _SHELL_VAR_REF.search(raw) is not None
827+ 
828+ 
829+def parse_script_exports(text: str) -> dict[str, Any]:
830+ """Parse ``export KEY=VALUE`` lines before ``vllm serve``.
831+ 
832+ Skips ``SKIP_ENV_KEYS`` and any value containing ``$`` shell references.
833+ Export 段不做 shell 变量展开。
834+ """
835+ exports: dict[str, Any] = {}
836+ for line in text.splitlines():
837+ stripped = line.split("#", 1)[0].strip()
838+ if not stripped:
839+ continue
840+ if re.search(r"\bvllm\s+serve\b", stripped):
841+ break
842+ if re.match(r"^(nic_name|local_ip)=", stripped, re.IGNORECASE):
843+ continue
844+ match = re.match(r"^export\s+([A-Za-z_][A-Za-z0-9_]*)=(.*)$", stripped)
845+ if not match:
846+ continue
847+ key, raw_value = match.group(1), match.group(2).strip()
848+ if key in SKIP_ENV_KEYS or _value_has_shell_var_ref(raw_value):
849+ continue
850+ exports[key] = _coerce_env_value(_unquote_shell_value(raw_value))
851+ return exports
852+ 
853+ 
854+def extract_vllm_command_text(text: str) -> str:
855+ """Keep only the ``vllm serve ...`` portion of a shell script."""
856+ chunks: list[str] = []
857+ started = False
858+ for line in text.splitlines():
859+ stripped = line.split("#", 1)[0].strip()
860+ if not stripped:
861+ continue
862+ if re.search(r"\bvllm\s+serve\b", stripped):
863+ started = True
864+ if started:
865+ chunks.append(stripped)
866+ if not chunks:
867+ return text
868+ return "\n".join(chunks)
869+ 
870+ 
871+def format_role_env(env: dict[str, Any], *, role: str) -> dict[str, Any]:
872+ if role == "hybrid":
873+ key_order = UNION_ENV_KEY_ORDER
874+ elif role == "prefill":
875+ key_order = PREFILL_ENV_KEY_ORDER
876+ else:
877+ key_order = DECODE_ENV_KEY_ORDER
878+ return reorder_dict(env, key_order)
879+ 
880+ 
881+def build_env_config(
882+ prefill_env: dict[str, Any],
883+ decode_env: dict[str, Any],
884+) -> dict[str, Any]:
885+ return reorder_dict(
886+ {
887+ "version": "2.0.0",
888+ "motor_common_env": dict(DEFAULT_ENV_COMMON),
889+ "motor_controller_env": {},
890+ "motor_coordinator_env": {},
891+ "motor_engine_prefill_env": format_role_env(prefill_env, role="prefill"),
892+ "motor_engine_decode_env": format_role_env(decode_env, role="decode"),
893+ "motor_kv_cache_pool_env": {},
894+ },
895+ ENV_CONFIG_KEY_ORDER,
896+ )
897+ 
898+ 
899+def build_hybrid_env_config(script_env: dict[str, Any]) -> dict[str, Any]:
900+ return reorder_dict(
901+ {
902+ "version": "2.0.0",
903+ "motor_common_env": dict(DEFAULT_ENV_COMMON),
904+ "motor_controller_env": {},
905+ "motor_coordinator_env": {},
906+ "motor_engine_union_env": format_role_env(script_env, role="hybrid"),
907+ "motor_kv_cache_pool_env": {},
908+ },
909+ HYBRID_ENV_CONFIG_KEY_ORDER,
910+ )
911+ 
912+ 
913+def _normalize_script_text(text: str) -> str:
914+ text = re.sub(r"\\\s*\r?\n", " ", text)
915+ text = re.sub(r"\s*\\\s*$", "", text, flags=re.MULTILINE)
916+ lines = []
917+ for line in text.splitlines():
918+ stripped = line.split("#", 1)[0].strip()
919+ if stripped:
920+ lines.append(stripped)
921+ return " ".join(lines)
922+ 
923+ 
924+def _fold_multiline_quoted_json_flags(text: str) -> str:
925+ """Collapse multiline single-quoted JSON values (e.g. --kv-transfer-config) to one line."""
926+ 
927+ def _collapse(match: re.Match[str]) -> str:
928+ flag = match.group(1)
929+ body = match.group(2)
930+ compact = " ".join(body.split())
931+ return f"{flag} '{compact}'"
932+ 
933+ return re.sub(
934+ r"(--[\w-]+)\s*'\s*(\{.*?\})\s*'",
935+ _collapse,
936+ text,
937+ flags=re.DOTALL,
938+ )
939+ 
940+ 
941+def _neutralize_shell_vars_for_parse(text: str) -> str:
942+ """Replace non-numeric shell vars so shlex can tokenize tutorial scripts."""
943+ 
944+ def _replace_braced(match: re.Match[str]) -> str:
945+ name = match.group(1)
946+ if name.isdigit():
947+ return match.group(0)
948+ if name == "MODEL_PATH":
949+ return "/placeholder/model"
950+ return "placeholder"
951+ 
952+ def _replace_bare(match: re.Match[str]) -> str:
953+ name = match.group(1)
954+ if name.isdigit():
955+ return match.group(0)
956+ return "placeholder"
957+ 
958+ text = re.sub(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}", _replace_braced, text)
959+ text = re.sub(r"\$([A-Za-z_][A-Za-z0-9_]*)", _replace_bare, text)
960+ return text
961+ 
962+ 
963+def _prepare_command_text_for_parse(text: str) -> str:
964+ normalized = _normalize_script_text(text)
965+ normalized = _fold_multiline_quoted_json_flags(normalized)
966+ return _neutralize_shell_vars_for_parse(normalized)
967+ 
968+ 
969+def _substitute_shell_vars(text: str, variables: dict[str, str] | None = None) -> str:
970+ variables = variables or {}
971+ 
972+ def replacer(match: re.Match[str]) -> str:
973+ name = match.group(1) or match.group(2)
974+ return str(variables[name]) if name in variables else match.group(0)
975+ 
976+ return re.sub(r"\$(\d+)|\$\{(\d+)\}", replacer, text)
977+ 
978+ 
979+def _expand_inline_assignments(tokens: list[str]) -> list[str]:
980+ expanded: list[str] = []
981+ for token in tokens:
982+ if token.startswith("--") and "=" in token[2:]:
983+ key, value = token[2:].split("=", 1)
984+ expanded.extend([f"--{key}", value])
985+ else:
986+ expanded.append(token)
987+ return expanded
988+ 
989+ 
990+def _substitute_tokens(tokens: list[str], variables: dict[str, str] | None) -> list[str]:
991+ if not variables:
992+ return tokens
993+ joined = _substitute_shell_vars(" ".join(shlex.quote(t) for t in tokens), variables)
994+ return shlex.split(joined, posix=True)
995+ 
996+ 
997+def script_to_cli_tokens(text: str, *, variables: dict[str, str] | None = None) -> list[str]:
998+ command_text = extract_vllm_command_text(text)
999+ normalized = _prepare_command_text_for_parse(command_text)
1000+ normalized = _substitute_shell_vars(normalized, variables)
1001+ tokens = shlex.split(normalized, posix=True)
1002+ if tokens and tokens[0] == "vllm":
1003+ tokens = tokens[1:]
1004+ if tokens and tokens[0] == "serve":
1005+ tokens = tokens[1:]
1006+ return _expand_inline_assignments(tokens)
1007+ 
1008+ 
1009+def parse_vllm_serve_command(text: str, *, variables: dict[str, str] | None = None) -> dict[str, Any]:
1010+ tokens = script_to_cli_tokens(text, variables=variables)
1011+ if not tokens:
1012+ raise ValueError("empty vLLM command")
1013+ return cli_to_config(tokens)
1014+ 
1015+ 
1016+def _motor_config_key(cli_key: str) -> str:
1017+ underscored = cli_key.replace("-", "_")
1018+ return underscored if underscored in UNDERSCORE_KEYS else cli_key
1019+ 
1020+ 
1021+def _should_skip_key(cli_key: str, *, include_parallel: bool = False) -> bool:
1022+ normalized = cli_key.replace("-", "_")
1023+ if normalized in SKIP_RUNTIME_KEYS:
1024+ return True
1025+ if not include_parallel and normalized in SKIP_PARALLEL_CLI_KEYS:
1026+ return True
1027+ return False
1028+ 
1029+ 
1030+def _convert_kv_transfer_config(raw_value: Any) -> dict[str, Any]:
1031+ if isinstance(raw_value, str):
1032+ kv_config = json.loads(raw_value)
1033+ elif isinstance(raw_value, dict):
1034+ kv_config = dict(raw_value)
1035+ else:
1036+ raise ValueError("kv_transfer_config must be a JSON object")
1037+ motor_kv = {
1038+ "kv_connector": kv_config.get("kv_connector"),
1039+ "kv_role": kv_config.get("kv_role"),
1040+ "kv_port": str(kv_config.get("kv_port", "")),
1041+ "engine_id": str(kv_config.get("engine_id", "0")),
1042+ }
1043+ return {k: v for k, v in motor_kv.items() if v is not None}
1044+ 
1045+ 
1046+_JSON_CONFIG_SUFFIXES = ("-config", "_config")
1047+ 
1048+ 
1049+def _normalize_json_config_fields(engine_config: dict[str, Any]) -> None:
1050+ """Coerce stringified JSON blobs (e.g. multiline --additional-config) to objects."""
1051+ for key, value in list(engine_config.items()):
1052+ if not isinstance(value, str):
1053+ continue
1054+ if not any(key.endswith(suffix) for suffix in _JSON_CONFIG_SUFFIXES):
1055+ continue
1056+ parsed = _try_parse_json_text(value)
1057+ if parsed is not None:
1058+ engine_config[key] = parsed
1059+ 
1060+ 
1061+def cli_args_to_engine_config(
1062+ cli_args: dict[str, Any],
1063+ *,
1064+ weight_mount_path: str | None = None,
1065+ overrides: dict[str, Any] | None = None,
1066+ add_profiler_config: bool = True,
1067+ strip_kv_extra_config: bool = True,
1068+ role: str | None = None,
1069+ parallel: dict[str, int] | None = None,
1070+ include_parallel_cli: bool = False,
1071+ skip_kv_transfer: bool = False,
1072+) -> dict[str, Any]:
1073+ engine_config: dict[str, Any] = {}
1074+ for cli_key, value in cli_args.items():
1075+ if _should_skip_key(cli_key, include_parallel=include_parallel_cli):
1076+ continue
1077+ config_key = _motor_config_key(cli_key)
1078+ if config_key == "kv_transfer_config":
1079+ if skip_kv_transfer:
1080+ continue
1081+ if strip_kv_extra_config:
1082+ engine_config[config_key] = _convert_kv_transfer_config(value)
1083+ if role == "prefill":
1084+ engine_config[config_key]["kv_role"] = "kv_producer"
1085+ elif role == "decode":
1086+ engine_config[config_key]["kv_role"] = "kv_consumer"
1087+ continue
1088+ engine_config[config_key] = value
1089+ 
1090+ _normalize_json_config_fields(engine_config)
1091+ 
1092+ if weight_mount_path:
1093+ engine_config["model"] = weight_mount_path
1094+ elif "model" in engine_config:
1095+ engine_config["model"] = str(engine_config["model"])
1096+ 
1097+ if parallel is None:
1098+ try:
1099+ parallel = extract_parallel_from_cli_args(cli_args)
1100+ except ValueError:
1101+ parallel = None
1102+ 
1103+ if parallel is not None:
1104+ engine_role = role or _infer_role_from_cli_args(cli_args)
1105+ apply_engine_parallel(engine_config, role=engine_role, parallel=parallel)
1106+ else:
1107+ engine_config.setdefault("pipeline_parallel_size", 1)
1108+ engine_config.setdefault("data_parallel_rpc_port", DEFAULT_DP_RPC_PORT)
1109+ 
1110+ if add_profiler_config and "profiler-config" not in engine_config:
1111+ engine_config["profiler-config"] = dict(DEFAULT_PROFILER_CONFIG)
1112+ if overrides:
1113+ engine_config.update(overrides)
1114+ return engine_config
1115+ 
1116+ 
1117+def cli_tokens_to_engine_config(
1118+ tokens: list[str],
1119+ *,
1120+ variables: dict[str, str] | None = None,
1121+ weight_mount_path: str | None = None,
1122+ overrides: dict[str, Any] | None = None,
1123+) -> dict[str, Any]:
1124+ if tokens[:1] == ["--"]:
1125+ tokens = tokens[1:]
1126+ tokens = _expand_inline_assignments(_substitute_tokens(tokens, variables))
1127+ return cli_args_to_engine_config(
1128+ cli_to_config(tokens),
1129+ weight_mount_path=weight_mount_path,
1130+ overrides=overrides,
1131+ )
1132+ 
1133+ 
1134+def build_engine_role_config(
1135+ engine_config: dict[str, Any],
1136+ *,
1137+ engine_type: str = "vllm",
1138+ minimal: bool = False,
1139+) -> dict[str, Any]:
1140+ role_cfg: dict[str, Any] = {
1141+ "engine_type": engine_type,
1142+ "engine_config": engine_config,
1143+ }
1144+ if not minimal:
1145+ role_cfg["motor_nodemanger_config"] = {}
1146+ return role_cfg
1147+ 
1148+ 
1149+def _coerce_deploy_value(value: str) -> Any:
1150+ if value.isdigit():
1151+ return int(value)
1152+ try:
1153+ if "." in value:
1154+ return float(value)
1155+ except ValueError:
1156+ pass
1157+ if value.lower() in {"true", "false"}:
1158+ return value.lower() == "true"
1159+ return value
1160+ 
1161+ 
1162+def build_user_config(
1163+ prefill_engine_config: dict[str, Any],
1164+ decode_engine_config: dict[str, Any],
1165+ *,
1166+ deploy_config: dict[str, Any] | None = None,
1167+ minimal_template: bool = False,
1168+) -> dict[str, Any]:
1169+ deploy = dict(DEFAULT_DEPLOY_CONFIG)
1170+ if deploy_config:
1171+ for key, value in deploy_config.items():
1172+ deploy[key] = _coerce_deploy_value(value) if isinstance(value, str) else value
1173+ model_path = (
1174+ deploy.get("weight_mount_path")
1175+ if deploy.get("weight_mount_path") not in (None, "", "/mnt/weight/", "/data01/models/")
1176+ else prefill_engine_config.get("model")
1177+ or decode_engine_config.get("model")
1178+ or deploy.get("weight_mount_path")
1179+ or "/mnt/weight/"
1180+ )
1181+ deploy["weight_mount_path"] = model_path
1182+ return format_user_config(
1183+ {
1184+ "version": "v2.0",
1185+ "motor_deploy_config": deploy,
1186+ "motor_controller_config": {},
1187+ "motor_coordinator_config": {},
1188+ "motor_engine_prefill_config": build_engine_role_config(
1189+ prefill_engine_config,
1190+ minimal=minimal_template,
1191+ ),
1192+ "motor_engine_decode_config": build_engine_role_config(
1193+ decode_engine_config,
1194+ minimal=minimal_template,
1195+ ),
1196+ }
1197+ )
1198+ 
1199+ 
1200+def build_hybrid_user_config(
1201+ engine_config: dict[str, Any],
1202+ *,
1203+ deploy_config: dict[str, Any] | None = None,
1204+ minimal_template: bool = False,
1205+) -> dict[str, Any]:
1206+ deploy = {
1207+ "deploy_mode": "infer_service_set",
1208+ "hybrid_instances_num": 1,
1209+ "single_hybrid_instance_pod_num": 1,
1210+ "hybrid_pod_npu_num": 1,
1211+ "image_name": "",
1212+ "job_id": "mindie-motor",
1213+ "hardware_type": "800I_A3",
1214+ "weight_mount_path": "/mnt/weight/",
1215+ }
1216+ if deploy_config:
1217+ for key, value in deploy_config.items():
1218+ deploy[key] = _coerce_deploy_value(value) if isinstance(value, str) else value
1219+ model_path = (
1220+ deploy.get("weight_mount_path")
1221+ if deploy.get("weight_mount_path") not in (None, "", "/mnt/weight/", "/data01/models/")
1222+ else engine_config.get("model") or deploy.get("weight_mount_path") or "/mnt/weight/"
1223+ )
1224+ deploy["weight_mount_path"] = model_path
1225+ return format_user_config(
1226+ {
1227+ "version": "v2.0",
1228+ "motor_deploy_config": deploy,
1229+ "motor_controller_config": {},
1230+ "motor_coordinator_config": {
1231+ "scheduler_config": {
1232+ "deploy_mode": "single_node",
1233+ },
1234+ },
1235+ "motor_engine_union_config": build_engine_role_config(
1236+ engine_config,
1237+ minimal=minimal_template,
1238+ ),
1239+ }
1240+ )
1241+ 
1242+ 
1243+def convert_vllm_hybrid_script_to_user_config(
1244+ script: str,
1245+ *,
1246+ variables: dict[str, str] | None = None,
1247+ weight_mount_path: str | None = None,
1248+ hardware_type: str | None = None,
1249+ deploy_config: dict[str, Any] | None = None,
1250+ engine_overrides: dict[str, Any] | None = None,
1251+ minimal_template: bool = True,
1252+) -> tuple[dict[str, Any], dict[str, Any]]:
1253+ cli = parse_vllm_serve_command(script, variables=variables)
1254+ dp, tp = extract_hybrid_parallel_from_cli_args(cli)
1255+ if hardware_type:
1256+ dp, tp = remap_hybrid_parallel_for_hardware(dp, tp, hardware_type)
1257+ 
1258+ env_config = build_hybrid_env_config(parse_script_exports(script))
1259+ engine_config = cli_args_to_engine_config(
1260+ cli,
1261+ weight_mount_path=weight_mount_path,
1262+ overrides=engine_overrides,
1263+ include_parallel_cli=True,
1264+ skip_kv_transfer=True,
1265+ add_profiler_config=False,
1266+ )
1267+ engine_config["data_parallel_size"] = dp
1268+ engine_config["tensor_parallel_size"] = tp
1269+ engine_config.setdefault("pipeline_parallel_size", 1)
1270+ engine_config.setdefault("data_parallel_rpc_port", DEFAULT_DP_RPC_PORT)
1271+ 
1272+ merged_deploy = dict(deploy_config or {})
1273+ if weight_mount_path:
1274+ merged_deploy["weight_mount_path"] = weight_mount_path
1275+ if hardware_type:
1276+ merged_deploy = infer_hybrid_motor_deploy_config(
1277+ dp,
1278+ tp,
1279+ hardware_type,
1280+ overrides=merged_deploy or None,
1281+ )
1282+ elif merged_deploy:
1283+ merged_deploy = {
1284+ "deploy_mode": "infer_service_set",
1285+ "hybrid_instances_num": 1,
1286+ "single_hybrid_instance_pod_num": 1,
1287+ "hybrid_pod_npu_num": dp * tp,
1288+ **merged_deploy,
1289+ }
1290+ 
1291+ user_config = build_hybrid_user_config(
1292+ engine_config,
1293+ deploy_config=merged_deploy or None,
1294+ minimal_template=minimal_template,
1295+ )
1296+ if not weight_mount_path:
1297+ apply_manual_fill_placeholders(user_config, hybrid=True)
1298+ return user_config, env_config
1299+ 
1300+ 
1301+def convert_vllm_scripts_to_user_config(
1302+ prefill_script: str,
1303+ decode_script: str,
1304+ *,
1305+ variables: dict[str, str] | None = None,
1306+ weight_mount_path: str | None = None,
1307+ hardware_type: str | None = None,
1308+ deploy_config: dict[str, Any] | None = None,
1309+ prefill_overrides: dict[str, Any] | None = None,
1310+ decode_overrides: dict[str, Any] | None = None,
1311+ minimal_template: bool = True,
1312+) -> tuple[dict[str, Any], dict[str, Any]]:
1313+ prefill_cli = parse_vllm_serve_command(prefill_script, variables=variables)
1314+ decode_cli = parse_vllm_serve_command(decode_script, variables=variables)
1315+ script_parallel = extract_parallel_from_cli_args(prefill_cli)
1316+ parallel = remap_parallel_for_hardware(script_parallel, hardware_type) if hardware_type else script_parallel
1317+ env_config = build_env_config(
1318+ parse_script_exports(prefill_script),
1319+ parse_script_exports(decode_script),
1320+ )
1321+ 
1322+ prefill_engine = cli_args_to_engine_config(
1323+ prefill_cli,
1324+ weight_mount_path=weight_mount_path,
1325+ overrides=prefill_overrides,
1326+ role="prefill",
1327+ parallel=parallel,
1328+ )
1329+ decode_engine = cli_args_to_engine_config(
1330+ decode_cli,
1331+ weight_mount_path=weight_mount_path,
1332+ overrides=decode_overrides,
1333+ role="decode",
1334+ parallel=parallel,
1335+ )
1336+ 
1337+ merged_deploy = dict(deploy_config or {})
1338+ if weight_mount_path:
1339+ merged_deploy["weight_mount_path"] = weight_mount_path
1340+ 
1341+ if hardware_type:
1342+ inferred = infer_motor_deploy_config(parallel, hardware_type, overrides=merged_deploy or None)
1343+ merged_deploy = inferred
1344+ elif merged_deploy:
1345+ merged_deploy = {**DEFAULT_DEPLOY_CONFIG, **merged_deploy}
1346+ 
1347+ user_config = build_user_config(
1348+ prefill_engine,
1349+ decode_engine,
1350+ deploy_config=merged_deploy or None,
1351+ minimal_template=minimal_template,
1352+ )
1353+ if not weight_mount_path:
1354+ apply_manual_fill_placeholders(user_config)
1355+ return user_config, env_config
1356+ 
1357+ 
1358+# ---------------------------------------------------------------------------
1359+# CLI
1360+# ---------------------------------------------------------------------------
1361+ 
1362+ 
1363+def _default_output_paths() -> tuple[Path, Path]:
1364+ user_path = Path(DEFAULT_OUTPUT_DIR) / DEFAULT_USER_CONFIG_NAME
1365+ env_path = Path(DEFAULT_OUTPUT_DIR) / DEFAULT_ENV_NAME
1366+ return user_path, env_path
1367+ 
1368+ 
1369+def _blue_text(text: str, *, stream: Any = sys.stderr) -> str:
1370+ """Wrap *text* in blue ANSI codes when writing to a TTY."""
1371+ if hasattr(stream, "isatty") and stream.isatty():
1372+ return f"{_ANSI_BLUE}{text}{_ANSI_RESET}"
1373+ return text
1374+ 
1375+ 
1376+def _print_optional_arg_reminders(
1377+ *,
1378+ weight_path: str | None,
1379+ image_name: str | None,
1380+ deploy_scenario: str,
1381+ hardware_type: str,
1382+) -> None:
1383+ if weight_path and image_name:
1384+ return
1385+ 
1386+ print(
1387+ f"\n{_blue_text('[提示]')} 配置文件生成成功,请基于 user_config.json 内的提示补充两项参数(env.json文件无需修改),完成后可以正常使用。",
1388+ file=sys.stderr,
1389+ )
1390+ print(file=sys.stderr)
1391+ print(
1392+ f"{_blue_text('[推荐]')} 如果您不希望二次修改 user_config.json,可以执行以下全量生成命令:",
1393+ file=sys.stderr,
1394+ )
1395+ print(file=sys.stderr)
1396+ cmd = (
1397+ f"python3 deploy.py --mode general_config --deploy-scenario {deploy_scenario} "
1398+ f"--hardware-type {hardware_type} "
1399+ f"--weight-path <权重路径> --image-name <镜像名称>"
1400+ )
1401+ print(cmd, file=sys.stderr)
1402+ 
1403+ example_weight = "/home/weights/DeepSeek-V4-Flash-w8a8-mtp"
1404+ hw = normalize_hardware_type(hardware_type)
1405+ example_images = {
1406+ "800I_A2": "mindie-motor-vllm:r0.17.0rc1-800I-A2-py311-lts-aarch64",
1407+ "800I_A3": "mindie-motor-vllm:dev-26.1.0.B081-800I-A3-py311-Ubuntu24.04-lts-aarch64",
1408+ }
1409+ example_image = example_images.get(hw, example_images["800I_A3"])
1410+ example_cmd = (
1411+ f"python3 deploy.py --mode general_config --deploy-scenario {deploy_scenario} "
1412+ f"--hardware-type {hardware_type} "
1413+ f"--weight-path {example_weight} "
1414+ f"--image-name {example_image}"
1415+ )
1416+ print(file=sys.stderr)
1417+ print(f"例如:\n{example_cmd}", file=sys.stderr)
1418+ 
1419+ 
1420+def main(argv: list[str] | None = None) -> int:
1421+ parser = argparse.ArgumentParser(
1422+ description="从 vLLM 启动脚本生成 Motor 配置。",
1423+ formatter_class=argparse.RawDescriptionHelpFormatter,
1424+ epilog=(
1425+ f"PD 混部输入: {AUTO_HYBRID_SCRIPT} (--deploy-scenario hybrid)\n"
1426+ f"PD 分离输入: {AUTO_PREFILL_SCRIPT}, {AUTO_DECODE_SCRIPT} "
1427+ f"(--deploy-scenario separate)\n"
1428+ f"输出: {DEFAULT_OUTPUT_DIR}/{DEFAULT_USER_CONFIG_NAME}, "
1429+ f"{DEFAULT_OUTPUT_DIR}/{DEFAULT_ENV_NAME}"
1430+ ),
1431+ )
1432+ parser.add_argument(
1433+ "--deploy-scenario",
1434+ required=True,
1435+ choices=[DEPLOY_SCENARIO_HYBRID, DEPLOY_SCENARIO_SEPARATE],
1436+ help="部署场景: hybrid=PD混部(单脚本); separate=PD分离(prefill+decode双脚本)",
1437+ )
1438+ parser.add_argument(
1439+ "--hardware-type",
1440+ required=True,
1441+ help="硬件类型: A2 或 A3",
1442+ )
1443+ parser.add_argument(
1444+ "--weight-path",
1445+ default=None,
1446+ help="模型权重挂载路径(可选,未指定时写入占位说明)",
1447+ )
1448+ parser.add_argument(
1449+ "--image-name",
1450+ default=None,
1451+ help="容器镜像名称(可选,未指定时写入占位说明)",
1452+ )
1453+ args = parser.parse_args(argv)
1454+ 
1455+ deploy_overrides: dict[str, str] = {}
1456+ if args.image_name:
1457+ deploy_overrides["image_name"] = args.image_name
1458+ 
1459+ workdir = Path.cwd()
1460+ user_path, env_path = _default_output_paths()
1461+ try:
1462+ if args.deploy_scenario == DEPLOY_SCENARIO_HYBRID:
1463+ script_path = resolve_hybrid_script(workdir)
1464+ user_config, env_config = convert_vllm_hybrid_script_to_user_config(
1465+ script_path.read_text(encoding="utf-8"),
1466+ hardware_type=args.hardware_type,
1467+ weight_mount_path=args.weight_path,
1468+ deploy_config=deploy_overrides or None,
1469+ )
1470+ print(
1471+ f"已读取: {script_path.name} (scenario=hybrid, hardware={normalize_hardware_type(args.hardware_type)})",
1472+ file=sys.stderr,
1473+ )
1474+ else:
1475+ prefill_path, decode_path = resolve_pd_scripts(workdir)
1476+ user_config, env_config = convert_vllm_scripts_to_user_config(
1477+ prefill_path.read_text(encoding="utf-8"),
1478+ decode_path.read_text(encoding="utf-8"),
1479+ hardware_type=args.hardware_type,
1480+ weight_mount_path=args.weight_path,
1481+ deploy_config=deploy_overrides or None,
1482+ )
1483+ print(
1484+ f"已读取: {prefill_path.name}, {decode_path.name} "
1485+ f"(scenario=separate, hardware={normalize_hardware_type(args.hardware_type)})",
1486+ file=sys.stderr,
1487+ )
1488+ except FileNotFoundError as exc:
1489+ raise SystemExit(str(exc)) from exc
1490+ 
1491+ if not args.image_name:
1492+ user_config["motor_deploy_config"]["image_name"] = MANUAL_FILL_IMAGE_NAME
1493+ 
1494+ env_path.parent.mkdir(parents=True, exist_ok=True)
1495+ env_path.write_text(
1496+ json.dumps(env_config, indent=2, ensure_ascii=False) + "\n",
1497+ encoding="utf-8",
1498+ )
1499+ print(f"已生成: {env_path}", file=sys.stderr)
1500+ 
1501+ user_path.parent.mkdir(parents=True, exist_ok=True)
1502+ user_path.write_text(
1503+ json.dumps(format_user_config(user_config), indent=2, ensure_ascii=False) + "\n",
1504+ encoding="utf-8",
1505+ )
1506+ print(f"已生成: {user_path}")
1507+ _print_optional_arg_reminders(
1508+ weight_path=args.weight_path,
1509+ image_name=args.image_name,
1510+ deploy_scenario=args.deploy_scenario,
1511+ hardware_type=args.hardware_type,
1512+ )
1513+ return 0
1514+ 
1515+ 
1516+if __name__ == "__main__":
1517+ raise SystemExit(main())
@@ -256,8 +256,40 @@ def _start_log_collection(deploy_config):
256 logger.info("Log collection started via show_log.sh")256 logger.info("Log collection started via show_log.sh")
257 257 
258 258 
259+def handle_general_config(args):
260+ config_tool_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config_tool")
261+ script = os.path.join(config_tool_dir, "vllm_to_motor.py")
262+ cmd = [
263+ sys.executable,
264+ script,
265+ "--deploy-scenario",
266+ args.deploy_scenario,
267+ "--hardware-type",
268+ args.hardware_type,
269+ ]
270+ if args.weight_path:
271+ cmd.extend(["--weight-path", args.weight_path])
272+ if args.image_name:
273+ cmd.extend(["--image-name", args.image_name])
274+ subprocess.run(cmd, cwd=config_tool_dir, check=True)
275+ 
276+ 
259def parse_arguments():277def parse_arguments():
260 parser = argparse.ArgumentParser()278 parser = argparse.ArgumentParser()
279+ parser.add_argument(
280+ "--mode",
281+ choices=["deploy", "general_config"],
282+ default="deploy",
283+ help="deploy: deploy service; general_config: generate user_config.json/env.json from config_tool",
284+ )
285+ parser.add_argument(
286+ "--deploy-scenario",
287+ choices=["hybrid", "separate"],
288+ help="Required for general_config mode",
289+ )
290+ parser.add_argument("--hardware-type", type=str, help="Required for general_config mode: A2 or A3")
291+ parser.add_argument("--weight-path", type=str, help="Optional for general_config mode: weight mount path")
292+ parser.add_argument("--image-name", type=str, help="Optional for general_config mode: container image name")
261 parser.add_argument(293 parser.add_argument(
262 "--config_dir",294 "--config_dir",
263 "--dir",295 "--dir",
@@ -355,6 +387,13 @@ def start_monitoring(user_config):
355def main():387def main():
356 args = parse_arguments()388 args = parse_arguments()
357 389 
390+ if args.mode == "general_config":
391+ if not args.deploy_scenario or not args.hardware_type:
392+ logger.error("In general_config mode, the --deploy-scenario and --hardware-type parameters are required.")
393+ sys.exit(2)
394+ handle_general_config(args)
395+ return
396+ 
358 # No configuration at all → launch TUI directly (undeployed mode)397 # No configuration at all → launch TUI directly (undeployed mode)
359 no_config = not (args.config_dir or args.user_config_path or args.env_config_path)398 no_config = not (args.config_dir or args.user_config_path or args.env_config_path)
360 if no_config:399 if no_config:
@@ -1,5 +0,0 @@
1-# 说明
2- 
3-| 文件 | vllm版本 | PyMotor版本 | 权重 | 执行时间 | 说明 |
4-| --- | --- | --- | --- | --- | --- |
5-| user_config.json | 0.17.0 | 主线版本 | GLM-5-w8a8 | 2026.3.27 | |
@@ -1,46 +0,0 @@
1-{
2- "version": "2.0.0",
3- "motor_common_env": {
4- "CANN_INSTALL_PATH": "/usr/local/Ascend",
5- "MOTOR_LOG_ROOT_PATH": "/root/ascend/log"
6- },
7- "motor_controller_env": {},
8- "motor_coordinator_env": {
9- "OPENAI_STANDARD": "GLM"
10- },
11- "motor_engine_prefill_env": {
12- "VLLM_ASCEND_ENABLE_MLAPO": 1,
13- "VLLM_ASCEND_ENABLE_NZ": 1,
14- "HCCL_OP_EXPANSION_MODE": "AIV",
15- "OMP_PROC_BIND": "false",
16- "OMP_NUM_THREADS": 10,
17- "VLLM_USE_V1": 1,
18- "HCCL_BUFFSIZE": 200,
19- "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
20- "TASK_QUEUE_ENABLE": 1,
21- "CPU_AFFINITY_CONF": 1,
22- "VLLM_ASCEND_ENABLE_FLASHCOMM1": 1,
23- "ASCEND_AGGREGATE_ENABLE": 1,
24- "ASCEND_TRANSPORT_PRINT": 1,
25- "ACL_OP_INIT_MODE": 1,
26- "VLLM_NIXL_ABORT_REQUEST_TIMEOUT": 300000
27- },
28- "motor_engine_decode_env": {
29- "VLLM_ASCEND_ENABLE_MLAPO": 1,
30- "VLLM_ASCEND_ENABLE_NZ": 1,
31- "HCCL_OP_EXPANSION_MODE": "AIV",
32- "OMP_PROC_BIND": "false",
33- "OMP_NUM_THREADS": 10,
34- "VLLM_USE_V1": 1,
35- "HCCL_BUFFSIZE": 200,
36- "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
37- "TASK_QUEUE_ENABLE": 1,
38- "CPU_AFFINITY_CONF": 1,
39- "ASCEND_AGGREGATE_ENABLE": 1,
40- "ASCEND_TRANSPORT_PRINT": 1,
41- "ACL_OP_INIT_MODE": 1,
42- "VLLM_NIXL_ABORT_REQUEST_TIMEOUT": 300000
43- },
44- "motor_kv_cache_pool_env": {},
45- "motor_kv_conductor_env": {}
46-}
@@ -1,138 +0,0 @@
1-{
2- "version": "v2.0",
3- "motor_deploy_config": {
4- "p_instances_num": 1,
5- "d_instances_num": 1,
6- "single_p_instance_pod_num": 4,
7- "single_d_instance_pod_num": 4,
8- "p_pod_npu_num": 8,
9- "d_pod_npu_num": 8,
10- "image_name": "mindie-motor-vllm:r0.17.0rc1-800I-A2-py311-lts-aarch64",
11- "job_id": "mindie-motor",
12- "hardware_type": "800I_A2",
13- "env_path": "./conf/env.json",
14- "weight_mount_path": "/data01/models/",
15- "deploy_mode": "multi_deployment"
16- },
17- "motor_controller_config": {},
18- "motor_coordinator_config": {},
19- "motor_nodemanger_config": {},
20- "motor_engine_prefill_config": {
21- "engine_type": "vllm",
22- "engine_config": {
23- "served_model_name": "GLM-5",
24- "model": "/data01/models/GLM-5-w4a8-mtp-QuaRot",
25- "gpu_memory_utilization": 0.95,
26- "data_parallel_size": 4,
27- "tensor_parallel_size": 8,
28- "pipeline_parallel_size": 1,
29- "data_parallel_rpc_port": 9000,
30- "enable_expert_parallel": true,
31- "enable-chunked-prefill": true,
32- "seed": 1024,
33- "max-model-len": 65536,
34- "max-num-batched-tokens": 4096,
35- "trust-remote-code": true,
36- "max-num-seqs": 48,
37- "quantization": "ascend",
38- "async-scheduling": true,
39- "enforce-eager": true,
40- "enable-auto-tool-choice": true,
41- "tool-call-parser": "glm47",
42- "reasoning-parser": "glm45",
43- "speculative-config": {
44- "num_speculative_tokens": 3,
45- "method": "deepseek_mtp"
46- },
47- "additional-config": {
48- "recompute_scheduler_enable": true,
49- "multistream_overlap_shared_expert": true,
50- "fuse_qknorm_rope": false,
51- "fuse_muls_add": true,
52- "enable_npugraph_ex": true
53- },
54- "kv_transfer_config": {
55- "kv_connector": "MooncakeConnectorV1",
56- "kv_buffer_device": "npu",
57- "kv_role": "kv_producer",
58- "kv_parallel_size": 1,
59- "kv_port": "21001",
60- "engine_id": "0",
61- "kv_rank": 0,
62- "kv_connector_extra_config": {
63- "use_ascend_direct": true
64- }
65- }
66- },
67- "health_check_config": {
68- "enable_virtual_inference": false,
69- "npu_usage_threshold": 3,
70- "max_failure_count": 6
71- }
72- },
73- "motor_engine_decode_config": {
74- "engine_type": "vllm",
75- "engine_config": {
76- "served_model_name": "GLM-5",
77- "model": "/data01/models/GLM-5-w4a8-mtp-QuaRot",
78- "gpu_memory_utilization": 0.95,
79- "data_parallel_size": 8,
80- "tensor_parallel_size": 4,
81- "pipeline_parallel_size": 1,
82- "data_parallel_rpc_port": 9000,
83- "enable_expert_parallel": true,
84- "enable-chunked-prefill": true,
85- "seed": 1024,
86- "max-model-len": 65536,
87- "max-num-batched-tokens": 64,
88- "trust-remote-code": true,
89- "max-num-seqs": 48,
90- "async-scheduling": true,
91- "quantization": "ascend",
92- "enable-auto-tool-choice": true,
93- "tool-call-parser": "glm47",
94- "reasoning-parser": "glm45",
95- "compilation_config": {
96- "cudagraph_capture_sizes": [
97- 1, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48, 56, 64, 80, 96
98- ],
99- "cudagraph_mode": "FULL_DECODE_ONLY"
100- },
101- "speculative-config": {
102- "num_speculative_tokens": 3,
103- "method": "deepseek_mtp"
104- },
105- "additional-config": {
106- "recompute_scheduler_enable": true,
107- "multistream_overlap_shared_expert": true,
108- "fuse_qknorm_rope": false,
109- "fuse_muls_add": true,
110- "enable_npugraph_ex": true
111- },
112- "kv_transfer_config": {
113- "kv_connector": "MooncakeConnectorV1",
114- "kv_buffer_device": "npu",
115- "kv_role": "kv_consumer",
116- "kv_parallel_size": 1,
117- "kv_port": "21001",
118- "engine_id": "0",
119- "kv_rank": 0,
120- "kv_connector_extra_config": {
121- "use_ascend_direct": true
122- }
123- }
124- },
125- "health_check_config": {
126- "enable_virtual_inference": false,
127- "npu_usage_threshold": 3,
128- "max_failure_count": 6
129- }
130- },
131- "kv_cache_pool_config": {
132- "metadata_server": "P2PHANDSHAKE",
133- "protocol": "ascend",
134- "device_name": "",
135- "alloc_in_same_node": true,
136- "global_segment_size": "1GB"
137- }
138-}
@@ -0,0 +1,162 @@
1+# MindIE Motor配置自动生成指导
2+ 
3+MindIE Motor的一键部署工具可以实现“将vllm-ascend社区的部署脚本转换为Motor部署配置”,以降低维护成本并保证与下游推理引擎配置一致。
4+ 
5+---
6+ 
7+## 目录简介
8+ 
9+配置生成脚本存放于[examples/deployer/config_tool/](../../../deployer/config_tool/)目录下,各文件功能如下。
10+ 
11+```bash
12+examples/deployer/config_tool/
13+├── vllm_to_motor.py # 配置转换脚本
14+├── run_dp_template_hybrid.sh # 用户粘贴:vLLM-ascend 混部启动脚本
15+├── run_dp_template_prefill.sh # 用户粘贴:vLLM-ascend P 实例启动脚本
16+├── run_dp_template_decode.sh # 用户粘贴:vLLM-ascend D 实例启动脚本
17+└── output_config/ # 生成的Motor配置内容
18+ ├── user_config.json
19+ └── env.json
20+```
21+ 
22+---
23+ 
24+## 注意事项
25+ 
26+1. 在执行使用方法的第2步[在vllm-ascend社区查找模型部署脚本]时,请确保部署镜像中的vllm-ascend版本和社区版本一致,**避免新配置应用于旧代码的情况**
27+2. 生成的Motor配置仅给出一种可行的模型切分示例,**用户可根据集群服务器数量,调整服务占用的服务器数量和模型划分策略,调整模型切分策略时关注以下参数即可**
28+ 
29+ | 配置项 | 取值类型 | 取值范围 | 配置说明 |
30+ | --- | --- | --- | --- |
31+ | p_instances_num | int | ≥1 | Prefill 实例数量。 |
32+ | d_instances_num | int | ≥1| Decode 实例数量。 |
33+ | single_p_instance_pod_num | int | ≥1 | 1 个 P 实例拆成几个 Pod。|
34+ | single_d_instance_pod_num | int | ≥1 | 1 个 D 实例拆成几个 Pod。 |
35+ | p_pod_npu_num | int | ≥1,单 Pod 最大 16 卡 | 每个 P Pod 使用的 NPU 卡数。 |
36+ | d_pod_npu_num | int | ≥1,单 Pod 最大 16 卡 | 每个 D Pod 使用的 NPU 卡数。 |
37+ | data_parallel_size | int | ≥1 | 数据并行(DP)数 |
38+ | tensor_parallel_size | int | ≥1 | 张量并行(TP)数 |
39+ 
40+ 1个P实例占用的NPU卡数 = single_p_instance_pod_num(占用几个pod,跨几机) × p_pod_npu_num (每个pod占用的NPU数)= data_parallel_size × tensor_parallel_size
41+ 
42+3. 生成的Motor配置仅支撑基础推理服务成功部署,**Motor特性调整(例如:主备倒换、KV 亲和性调度、服务限流)需要用户手动修改配置**
43+ 
44+---
45+ 
46+## 使用方法
47+ 
48+1. 进入配置脚本主目录。
49+ 
50+ ```bash
51+ cd examples/deployer/
52+ ```
53+ 
54+2. 在vllm-ascend社区查找部署脚本。
55+ 
56+ 进入[vllm-ascend模型部署指导网址](https://github.com/vllm-project/vllm-ascend/tree/main/docs/source/tutorials/models),基于模型选择对应文档,在文档的`Online Service Deployment`小节(通常为第5小节)找到模型部署脚本(通常命名为`run_dp_template.sh`),重点关注:**pd混部部署脚本**(小标题名称为`Single-Node Online Deployment`)和**PD分离部署脚本**(小标题名称为`Multi-Node PD Separation Deployment`)。
57+ 
58+ **举例**:
59+ 
60+ [dsv4 flash部署指导](https://github.com/vllm-project/vllm-ascend/blob/main/docs/source/tutorials/models/DeepSeek-V4-Flash.md#51-single-node-online-deployment)下。
61+ 
62+ - 5.1小节的"A3 series"小标题下,即为**PD混部部署脚本**,内容如下:
E
Eella077月10日

在5.1小节下,没找到A3 series这标题。

likedislike
高鹏
高鹏
7月10日 评论:
63+ 
64+ ```bash
65+ export OMP_PROC_BIND=false
66+ export OMP_NUM_THREADS=10
67+ ...
68+ 
69+ vllm serve /root/.cache/modelscope/hub/models/vllm-ascend/DeepSeek-V4-Flash-w8a8-mtp \
70+ --max-model-len 1048576 \
71+ --max-num-batched-tokens 10240 \
72+ ...
73+ ```
74+ 
75+ - 5.2.1小节的"run_dp_template.sh"的脚本内容,即为**PD分离部署脚本**,以P实例脚本为例:
76+ 
77+ ```bash
78+ nic_name="xxxx" # change to your own nic name
79+ local_ip=xx.xx.xx.1 # change to your own ip
80+ 
81+ export HCCL_IF_IP=$local_ip
82+ export GLOO_SOCKET_IFNAME=$nic_name
83+ ...
84+ 
85+ vllm serve /root/.cache/modelscope/hub/models/vllm-ascend/DeepSeek-V4-Flash-w8a8-mtp \
86+ --host 0.0.0.0 \
87+ --port $2 \
88+ ...
89+ ```
90+ 
91+ >[!NOTE]说明
92+ >一个实例占用多个服务器的场景下,vllm-ascend社区可能为一个实例的部署提供多份脚本(分别对应多台服务器),这些部署脚本的配置没有明显差异,仅需要关注其中一份脚本。
93+ >
94+ >例如:[qwen3-235B模型部署指导](https://github.com/vllm-project/vllm-ascend/blob/main/docs/source/tutorials/models/Qwen3-235B-A22B.md#52-multi-node-pd-separation-deployment)的5.2小节中,同时存在Decode node 0和Decode node 1,在使用时选取任意一份作为D实例部署脚本即可。
95+ 
96+3. 拷贝vllm-ascend模型部署脚本至examples/deployer/config_tool/目录。
97+ 
98+ run_dp_template_prefill.sh、run_dp_template_decode.sh和run_dp_template_hybrid.sh文件用于保存vllm-ascnd部署脚本,这些文件均保存于examples/deployer/config_tool/目录下。
99+ 
100+ - **场景一**:通过Motor部署PD分离服务。
101+ 
102+ 无需额外修改,将上述网址中PD分离部署脚本直接拷贝至run_dp_template_prefill.sh(P节点)和run_dp_template_decode.sh(D节点)文件内。
103+ 
104+ - **场景二**:通过Motor部署PD混部服务。
105+ 
106+ 无需额外修改,将上述网址中的PD混部部署脚本直接拷贝至 run_dp_template_hybrid.sh文件内。
107+ 
108+4. 生成Motor配置。
109+ 
110+ 根据场景,执行以下命令直接生成Motor配置:
111+ 
112+ ```bash
113+ # PD分离、Atlas 800I A3 超节点服务器
114+ python3 deploy.py --mode general_config --deploy-scenario separate --hardware-type A3
115+ # PD混部、Atlas 800I A3 超节点服务器
116+ python3 deploy.py --mode general_config --deploy-scenario hybrid --hardware-type A3
117+ # PD分离、Atlas 800I A2 推理服务器
118+ python3 deploy.py --mode general_config --deploy-scenario separate --hardware-type A2
119+ # PD混部、Atlas 800I A2 推理服务器
120+ python3 deploy.py --mode general_config --deploy-scenario hybrid --hardware-type A2
121+ ```
122+ 
123+5. 查看结果并微调。
124+ 
125+ 进入output_config目录,可以查看生成的user_config.json和env.json文件。
126+ 
127+ ```bash
128+ cd examples/deployer/config_tool/output_config && ls
129+ ```
130+ 
131+ user_config.json文件的以下内容需要用户根据实际情况手动填写:
132+ 
133+ ```bash
134+ {
135+ "version": "v2.0",
136+ "motor_deploy_config": {
137+ ...
138+ "image_name": "<请手动填写镜像名称>",
139+ ...
140+ "weight_mount_path": "<请按实际情况填写模型权重文件的访问路径>"
141+ },
142+ ...
143+ "motor_engine_prefill_config": {
144+ ...
145+ "engine_config": {
146+ ...
147+ "model": "<请按实际情况填写模型权重文件的访问路径>",
148+ ...
149+ }
150+ },
151+ "motor_engine_decode_config": {
152+ ...
153+ "engine_config": {
154+ ...
155+ "model": "<请按实际情况填写模型权重文件的访问路径>",
156+ ...
157+ }
158+ }
159+ }
160+ ```
161+ 
162+ env.json文件无需修改,至此,配置文件生成完成。
@@ -1,3 +0,0 @@
1-# 说明
2- 
3-该目录配置主要针对`800I_A3`
@@ -1,37 +0,0 @@
1-{
2- "version": "2.0.0",
3- "motor_common_env": {
4- "CANN_INSTALL_PATH": "/usr/local/Ascend",
5- "MOTOR_LOG_ROOT_PATH": "/root/ascend/log"
6- },
7- "motor_engine_prefill_env": {
8- "VLLM_RPC_TIMEOUT": 3600000,
9- "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS": 30000,
10- "HCCL_EXEC_TIMEOUT": 360,
11- "HCCL_CONNECT_TIMEOUT": 600,
12- "OMP_PROC_BIND": false,
13- "OMP_NUM_THREADS": 10,
14- "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
15- "VLLM_ASCEND_ENABLE_MLAPO": 1,
16- "HCCL_BUFFSIZE": 256,
17- "TASK_QUEUE_ENABLE": 1,
18- "HCCL_OP_EXPANSION_MODE": "AIV",
19- "VLLM_USE_V1": 1,
20- "ASCEND_BUFFER_POOL": "0:0"
21- },
22- "motor_engine_decode_env": {
23- "VLLM_RPC_TIMEOUT": 3600000,
24- "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS": 30000,
25- "HCCL_EXEC_TIMEOUT": 3600,
26- "HCCL_CONNECT_TIMEOUT": 3600,
27- "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
28- "VLLM_ASCEND_ENABLE_MLAPO": 1,
29- "HCCL_BUFFSIZE": 1200,
30- "TASK_QUEUE_ENABLE": 1,
31- "HCCL_OP_EXPANSION_MODE": "AIV",
32- "OMP_PROC_BIND": "false",
33- "OMP_NUM_THREADS": 10,
34- "ASCEND_BUFFER_POOL": "0:0",
35- "VLLM_USE_V1": 1
36- }
37-}
@@ -1,103 +0,0 @@
1-{
2- "version": "v2.0",
3- "motor_deploy_config": {
4- "p_instances_num": 2,
5- "d_instances_num": 1,
6- "single_p_instance_pod_num": 1,
7- "single_d_instance_pod_num": 2,
8- "p_pod_npu_num": 16,
9- "d_pod_npu_num": 16,
10- "image_name": "mindie-motor-vllm:dev-2.3.RC1.B132-800I-A3-py311-Ubuntu24.04-lts-aarch64",
11- "job_id": "mindie-motor",
12- "hardware_type": "800I_A3",
13- "weight_mount_path": "/mnt/weight/"
14- },
15- "motor_controller_config": {},
16- "motor_coordinator_config": {},
17- "motor_engine_prefill_config": {
18- "engine_type": "vllm",
19- "motor_nodemanger_config": {},
20- "engine_config": {
21- "served_model_name": "dsv3_1",
22- "model": "/mnt/weights/DeepSeek-V3.1_w8a8mix_mtp/",
23- "gpu_memory_utilization": 0.9,
24- "data_parallel_size": 2,
25- "tensor_parallel_size": 8,
26- "pipeline_parallel_size": 1,
27- "data_parallel_rpc_port": 9000,
28- "enable_expert_parallel": true,
29- "enforce-eager": true,
30- "trust-remote-code": true,
31- "max_model_len": 73000,
32- "max-num-batched-tokens": 16384,
33- "max-num-seqs": 16,
34- "quantization": "ascend",
35- "seed": 1024,
36- "distributed-executor-backend": "mp",
37- "speculative-config": {
38- "num_speculative_tokens": 1,
39- "method": "deepseek_mtp"
40- },
41- "additional-config": {
42- "recompute_scheduler_enable": true
43- },
44- "kv_transfer_config": {
45- "kv_connector": "MooncakeLayerwiseConnector",
46- "kv_buffer_device": "npu",
47- "kv_role": "kv_producer",
48- "kv_parallel_size": 1,
49- "kv_port": "30001",
50- "engine_id": "0",
51- "kv_rank": 0,
52- "kv_connector_extra_config": {
53- "use_ascend_direct": true
54- }
55- }
56- }
57- },
58- "motor_engine_decode_config": {
59- "engine_type": "vllm",
60- "motor_nodemanger_config": {},
61- "engine_config": {
62- "served_model_name": "dsv3_1",
63- "model": "/mnt/weights/DeepSeek-V3.1_w8a8mix_mtp/",
64- "gpu_memory_utilization": 0.9,
65- "data_parallel_size": 32,
66- "tensor_parallel_size": 1,
67- "pipeline_parallel_size": 1,
68- "data_parallel_rpc_port": 9000,
69- "enable_expert_parallel": true,
70- "trust-remote-code": true,
71- "max_model_len": 73000,
72- "max-num-batched-tokens": 256,
73- "max-num-seqs": 24,
74- "quantization": "ascend",
75- "seed": 1024,
76- "compilation_config": {
77- "cudagraph_capture_sizes": [4, 8, 16, 32, 48, 64, 80, 96],
78- "cudagraph_mode": "FULL_DECODE_ONLY"
79- },
80- "distributed-executor-backend": "mp",
81- "speculative-config": {
82- "num_speculative_tokens": 3,
83- "method": "deepseek_mtp"
84- },
85- "additional-config": {
86- "recompute_scheduler_enable": true,
87- "lm_head_tensor_parallel_size": 16
88- },
89- "kv_transfer_config": {
90- "kv_connector": "MooncakeLayerwiseConnector",
91- "kv_buffer_device": "npu",
92- "kv_role": "kv_consumer",
93- "kv_parallel_size": 1,
94- "kv_port": "30001",
95- "engine_id": "0",
96- "kv_rank": 0,
97- "kv_connector_extra_config": {
98- "use_ascend_direct": true
99- }
100- }
101- }
102- }
103-}
@@ -1,5 +0,0 @@
1-# 说明
2- 
3-| 文件 | vllm版本 | PyMotor版本 | 权重 | 执行时间 | 说明 |
4-| --- | --- | --- | --- | --- | --- |
5-| user_config.json | 0.17.0 | 主线版本 | DeepSeek-V3.2-w8a8-mtp-QuaRot | 2026.3.27 | |
@@ -1,44 +0,0 @@
1-{
2- "version": "2.0.0",
3- "motor_common_env": {
4- "CANN_INSTALL_PATH": "/usr/local/Ascend",
5- "MOTOR_LOG_ROOT_PATH": "/root/ascend/log"
6- },
7- "motor_controller_env": {},
8- "motor_coordinator_env": {},
9- "motor_engine_prefill_env": {
10- "VLLM_ASCEND_ENABLE_MLAPO": 1,
11- "VLLM_ASCEND_ENABLE_NZ": 1,
12- "HCCL_OP_EXPANSION_MODE": "AIV",
13- "OMP_PROC_BIND": "false",
14- "OMP_NUM_THREADS": 10,
15- "VLLM_USE_V1": 1,
16- "HCCL_BUFFSIZE": 200,
17- "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
18- "TASK_QUEUE_ENABLE": 1,
19- "CPU_AFFINITY_CONF": 1,
20- "VLLM_ASCEND_ENABLE_FLASHCOMM1": 1,
21- "ASCEND_AGGREGATE_ENABLE": 1,
22- "ASCEND_TRANSPORT_PRINT": 1,
23- "ACL_OP_INIT_MODE": 1,
24- "VLLM_NIXL_ABORT_REQUEST_TIMEOUT": 300000
25- },
26- "motor_engine_decode_env": {
27- "VLLM_ASCEND_ENABLE_MLAPO": 1,
28- "VLLM_ASCEND_ENABLE_NZ": 1,
29- "HCCL_OP_EXPANSION_MODE": "AIV",
30- "OMP_PROC_BIND": "false",
31- "OMP_NUM_THREADS": 10,
32- "VLLM_USE_V1": 1,
33- "HCCL_BUFFSIZE": 200,
34- "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
35- "TASK_QUEUE_ENABLE": 1,
36- "CPU_AFFINITY_CONF": 1,
37- "ASCEND_AGGREGATE_ENABLE": 1,
38- "ASCEND_TRANSPORT_PRINT": 1,
39- "ACL_OP_INIT_MODE": 1,
40- "VLLM_NIXL_ABORT_REQUEST_TIMEOUT": 300000
41- },
42- "motor_kv_cache_pool_env": {},
43- "motor_kv_conductor_env": {}
44-}
@@ -1,131 +0,0 @@
1-{
2- "version": "v2.0",
3- "motor_deploy_config": {
4- "p_instances_num": 1,
5- "d_instances_num": 1,
6- "single_p_instance_pod_num": 4,
7- "single_d_instance_pod_num": 4,
8- "p_pod_npu_num": 8,
9- "d_pod_npu_num": 8,
10- "image_name": "mindie-motor-vllm:kv_and_tracing-r0.17.0rc1-800I-A2-py311-lts-aarch64-traton",
11- "job_id": "mindie-motor",
12- "hardware_type": "800I_A2",
13- "env_path": "./conf/env.json",
14- "weight_mount_path": "/data01/models/",
15- "deploy_mode": "multi_deployment"
16- },
17- "motor_controller_config": {},
18- "motor_coordinator_config": {},
19- "motor_engine_prefill_config": {
20- "engine_type": "vllm",
21- "motor_nodemanger_config": {},
22- "engine_config": {
23- "served_model_name": "DeepSeek-V3.2",
24- "model": "/data01/models/DeepSeek-V3.2-w8a8-mtp-QuaRot",
25- "gpu_memory_utilization": 0.95,
26- "data_parallel_size": 4,
27- "tensor_parallel_size": 8,
28- "pipeline_parallel_size": 1,
29- "data_parallel_rpc_port": 9000,
30- "enable_expert_parallel": true,
31- "enable-chunked-prefill": true,
32- "seed": 1024,
33- "max-model-len": 65536,
34- "max-num-batched-tokens": 4096,
35- "trust-remote-code": true,
36- "max-num-seqs": 48,
37- "quantization": "ascend",
38- "async-scheduling": true,
39- "enforce-eager": true,
40- "tokenizer-mode": "deepseek_v32",
41- "enable-auto-tool-choice": true,
42- "tool-call-parser": "deepseek_v32",
43- "reasoning-parser": "deepseek_v3",
44- "speculative-config": {
45- "num_speculative_tokens": 3,
46- "method": "deepseek_mtp"
47- },
48- "additional-config": {
49- "recompute_scheduler_enable": true,
50- "multistream_overlap_shared_expert": true,
51- "fuse_qknorm_rope": false,
52- "fuse_muls_add": true,
53- "enable_npugraph_ex": true
54- },
55- "kv_transfer_config": {
56- "kv_connector": "MooncakeConnectorV1",
57- "kv_buffer_device": "npu",
58- "kv_role": "kv_producer",
59- "kv_parallel_size": 1,
60- "kv_port": "21001",
61- "engine_id": "0",
62- "kv_rank": 0,
63- "kv_connector_extra_config": {
64- "use_ascend_direct": true
65- }
66- }
67- }
68- },
69- "motor_engine_decode_config": {
70- "engine_type": "vllm",
71- "motor_nodemanger_config": {},
72- "engine_config": {
73- "served_model_name": "DeepSeek-V3.2",
74- "model": "/data01/models/DeepSeek-V3.2-w8a8-mtp-QuaRot",
75- "gpu_memory_utilization": 0.95,
76- "data_parallel_size": 8,
77- "tensor_parallel_size": 4,
78- "pipeline_parallel_size": 1,
79- "data_parallel_rpc_port": 9000,
80- "enable_expert_parallel": true,
81- "enable-chunked-prefill": true,
82- "seed": 1024,
83- "max-model-len": 65536,
84- "max-num-batched-tokens": 64,
85- "trust-remote-code": true,
86- "max-num-seqs": 48,
87- "async-scheduling": true,
88- "quantization": "ascend",
89- "tokenizer-mode": "deepseek_v32",
90- "enable-auto-tool-choice": true,
91- "tool-call-parser": "deepseek_v32",
92- "reasoning-parser": "deepseek_v3",
93- "compilation_config": {
94- "cudagraph_capture_sizes": [
95- 1, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48, 56, 64, 80, 96
96- ],
97- "cudagraph_mode": "FULL_DECODE_ONLY"
98- },
99- "speculative-config": {
100- "num_speculative_tokens": 3,
101- "method": "deepseek_mtp"
102- },
103- "additional-config": {
104- "recompute_scheduler_enable": true,
105- "multistream_overlap_shared_expert": true,
106- "fuse_qknorm_rope": false,
107- "fuse_muls_add": true,
108- "enable_npugraph_ex": true
109- },
110- "kv_transfer_config": {
111- "kv_connector": "MooncakeConnectorV1",
112- "kv_buffer_device": "npu",
113- "kv_role": "kv_consumer",
114- "kv_parallel_size": 1,
115- "kv_port": "21001",
116- "engine_id": "0",
117- "kv_rank": 0,
118- "kv_connector_extra_config": {
119- "use_ascend_direct": true
120- }
121- }
122- }
123- },
124- "kv_cache_pool_config": {
125- "metadata_server": "P2PHANDSHAKE",
126- "protocol": "ascend",
127- "device_name": "",
128- "alloc_in_same_node": true,
129- "global_segment_size": "1GB"
130- }
131-}
@@ -1,5 +0,0 @@
1-# 说明
2- 
3-| 文件 | vllm版本 | PyMotor版本 | 权重 | 执行时间 | 说明 |
4-| --- | --- | --- | --- | --- | --- |
5-| user_config.json | 0.18.0 | 主线版本 | DeepSeek-V4-Flash-w8a8-mtp | 2026.5.5 | |
@@ -1,46 +0,0 @@
1-{
2- "version": "2.0.0",
3- "motor_common_env": {
4- "CANN_INSTALL_PATH": "/usr/local/Ascend"
5- },
6- "motor_controller_env": {
7- },
8- "motor_coordinator_env": {
9- },
10- "motor_engine_prefill_env": {
11- "VLLM_RPC_TIMEOUT": 3600000,
12- "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS": 30000,
13- "HCCL_EXEC_TIMEOUT": 204,
14- "HCCL_CONNECT_TIMEOUT": 1200,
15- "OMP_PROC_BIND": "false",
16- "OMP_NUM_THREADS": 10,
17- "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
18- "HCCL_BUFFSIZE": 2560,
19- "TASK_QUEUE_ENABLE": 1,
20- "HCCL_OP_EXPANSION_MODE": "AIV",
21- "ASCEND_BUFFER_POOL": "4:8",
22- "USE_MULTI_GROUPS_KV_CACHE": 1,
23- "USE_MULTI_BLOCK_POOL": 1
24- },
25- "motor_engine_decode_env": {
26- "HCCL_OP_EXPANSION_MODE": "AIV",
27- "TASK_QUEUE_ENABLE": 1,
28- "VLLM_RPC_TIMEOUT": 3600000,
29- "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS": 30000,
30- "HCCL_EXEC_TIMEOUT": 204,
31- "HCCL_CONNECT_TIMEOUT": 1200,
32- "VLLM_ASCEND_ENABLE_FUSED_MC2": 1,
33- "OMP_PROC_BIND": "false",
34- "OMP_NUM_THREADS": 10,
35- "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
36- "HCCL_BUFFSIZE": 1024,
37- "ASCEND_BUFFER_POOL": "4:8",
38- "DYNAMIC_EPLB": "true",
39- "VLLM_TORCH_PROFILER_DIR": "./vllm_profile",
40- "VLLM_TORCH_PROFILER_WITH_STACK": 0,
41- "USE_MULTI_GROUPS_KV_CACHE": 1,
42- "USE_MULTI_BLOCK_POOL": 1
43- },
44- "motor_kv_cache_pool_env": {
45- }
46-}
@@ -1,5 +0,0 @@
1-# 说明
2- 
3-| 文件 | vllm版本 | PyMotor版本 | 权重 | 执行时间 | 说明 |
4-| --- | --- | --- | --- | --- | --- |
5-| user_config.json | 0.18.0 | 主线版本 | DeepSeek-V4-Pro-w4a8-mtp | 2026.5.12 | |
@@ -1,115 +0,0 @@
1-{
2- "version": "v2.0",
3- "motor_deploy_config": {
4- "p_instances_num": 1,
5- "d_instances_num": 1,
6- "single_p_instance_pod_num": 2,
7- "single_d_instance_pod_num": 2,
8- "p_pod_npu_num": 16,
9- "d_pod_npu_num": 16,
10- "image_name": "mindie-motor-vllm:3.0.0b1-800I-A3-py3.11-Ubuntu24.04-lts-aarch64",
11- "job_id": "mindie-motor",
12- "hardware_type": "800I_A3",
13- "env_path": "./conf/env.json",
14- "weight_mount_path": "/data1/DeepSeek-V4-Pro-w4a8-mtp",
15- "deploy_mode": "multi_deployment"
16- },
17- "motor_controller_config": {},
18- "motor_coordinator_config": {},
19- "motor_engine_prefill_config": {
20- "engine_type": "vllm",
21- "motor_nodemanger_config": {},
22- "engine_config": {
23- "served_model_name": "auto",
24- "model": "/data1/DeepSeek-V4-Pro-w4a8-mtp",
25- "gpu_memory_utilization": 0.9,
26- "data_parallel_size": 2,
27- "tensor_parallel_size": 16,
28- "pipeline_parallel_size": 1,
29- "enable_expert_parallel": true,
30- "data_parallel_rpc_port": 9000,
31- "seed": 1024,
32- "max-model-len": 135000,
33- "max-num-batched-tokens": 8192,
34- "max-num-seqs": 4,
35- "no_disable_hybrid_kv_cache_manager": true,
36- "safetensors-load-strategy": "prefetch",
37- "trust-remote-code": true,
38- "tokenizer-mode": "deepseek_v4",
39- "tool-call-parser": "deepseek_v4",
40- "enable-auto-tool-choice": true,
41- "reasoning-parser": "deepseek_v4",
42- "quantization": "ascend",
43- "block-size": "128",
44- "profiler-config": {
45- "profiler": "torch",
46- "torch_profiler_dir": "./vllm_profile",
47- "torch_profiler_with_stack": false
48- },
49- "enforce-eager": true,
50- "speculative-config": {"num_speculative_tokens": 1, "method":"deepseek_mtp"},
51- "additional-config": {
52- "enable_cpu_binding": true
53- },
54- "kv_transfer_config": {
55- "kv_connector": "MooncakeHybridConnector",
56- "kv_role": "kv_producer",
57- "kv_port": "30001",
58- "engine_id": "0"
59- }
60- }
61- },
62- "motor_engine_decode_config": {
63- "engine_type": "vllm",
64- "motor_nodemanger_config": {},
65- "engine_config": {
66- "served_model_name": "auto",
67- "model": "/data1/DeepSeek-V4-Pro-w4a8-mtp/",
68- "gpu_memory_utilization": 0.9,
69- "data_parallel_size": 16,
70- "tensor_parallel_size": 2,
71- "pipeline_parallel_size": 1,
72- "enable_expert_parallel": true,
73- "data_parallel_rpc_port": 9000,
74- "seed": 1024,
75- "max-model-len": 135000,
76- "max-num-batched-tokens": 120,
77- "max-num-seqs": 60,
78- "async-scheduling": true,
79- "block-size": "128",
80- "no_disable_hybrid_kv_cache_manager": true,
81- "safetensors-load-strategy": "prefetch",
82- "trust-remote-code": true,
83- "tokenizer-mode": "deepseek_v4",
84- "tool-call-parser": "deepseek_v4",
85- "enable-auto-tool-choice": true,
86- "reasoning-parser": "deepseek_v4",
87- "quantization": "ascend",
88- "profiler-config": {
89- "profiler": "torch",
90- "torch_profiler_dir": "./vllm_profile",
91- "torch_profiler_with_stack": false
92- },
93- "speculative-config": {"num_speculative_tokens": 1, "method":"deepseek_mtp"},
94- "compilation_config": {
95- "cudagraph_mode": "FULL_DECODE_ONLY"
96- },
97- "kv_transfer_config": {
98- "kv_connector": "MooncakeHybridConnector",
99- "kv_role": "kv_consumer",
100- "kv_port": "30001",
101- "engine_id": "0"
102- },
103- "additional-config": {
104- "ascend_compilation_config": {
105- "enable_npugraph_ex": true,
106- "enable_static_kernel": false
107- },
108- "enable_cpu_binding": true,
109- "multistream_dsa_preprocess": false,
110- "multistream_overlap_shared_expert":false,
111- "recompute_scheduler_enable":true
112- }
113- }
114- }
115-}
@@ -1,41 +0,0 @@
1-{
2- "version": "2.0.0",
3- "motor_common_env": {
4- "CANN_INSTALL_PATH": "/usr/local/Ascend",
5- "MOTOR_LOG_ROOT_PATH": "/root/ascend/log"
6- },
7- "motor_controller_env": {
8- },
9- "motor_coordinator_env": {
10- },
11- "motor_engine_prefill_env": {
12- "VLLM_RPC_TIMEOUT": 3600000,
13- "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS": 30000,
14- "HCCL_EXEC_TIMEOUT": 360,
15- "HCCL_CONNECT_TIMEOUT": 600,
16- "OMP_PROC_BIND": false,
17- "OMP_NUM_THREADS": 10,
18- "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
19- "VLLM_ASCEND_ENABLE_MLAPO": 1,
20- "HCCL_BUFFSIZE": 256,
21- "TASK_QUEUE_ENABLE": 1,
22- "HCCL_OP_EXPANSION_MODE": "AIV",
23- "VLLM_USE_V1": 1,
24- "ASCEND_BUFFER_POOL": "0:0"
25- },
26- "motor_engine_decode_env": {
27- "VLLM_RPC_TIMEOUT": 3600000,
28- "VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS": 30000,
29- "HCCL_EXEC_TIMEOUT": 3600,
30- "HCCL_CONNECT_TIMEOUT": 3600,
31- "PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
32- "VLLM_ASCEND_ENABLE_MLAPO": 1,
33- "HCCL_BUFFSIZE": 1024,
34- "TASK_QUEUE_ENABLE": 1,
35- "HCCL_OP_EXPANSION_MODE": "AIV",
36- "OMP_PROC_BIND": "false",
37- "OMP_NUM_THREADS": 10,
38- "ASCEND_BUFFER_POOL": "0:0",
39- "VLLM_USE_V1": 1
40- }
41-}