x
This commit is contained in:
@@ -61,6 +61,8 @@
|
||||
- `api_key`:接口访问密钥(同时用于 8000 与 8001)
|
||||
- `tensor_parallel_size`:张量并行数,双卡建议 `2`
|
||||
- `dtype`:推理精度,默认 `bfloat16`
|
||||
- `model_root`:本地模型根目录,建议 `/opt/model`
|
||||
- `offline_mode`:保留字段,当前实现固定只走离线本地模型
|
||||
- `models.selected`:当前生效模型,留空时回退到 `models.default`
|
||||
|
||||
## config.json 说明
|
||||
@@ -70,7 +72,7 @@
|
||||
- `models.default`:默认模型名
|
||||
- `models.selected`:当前生效模型名
|
||||
- `models.profiles`:模型配置集合
|
||||
- 每个模型至少建议包含:`hf_model_id`、`ctx`、`max_num_seqs`、`max_tokens`、`gpu_util`、`valid_tp`
|
||||
- 每个模型必须包含:`local_path`、`ctx`、`max_num_seqs`、`max_tokens`、`gpu_util`、`valid_tp`
|
||||
|
||||
启动时会按以下优先级选模型:
|
||||
|
||||
@@ -79,7 +81,7 @@
|
||||
|
||||
模型被选中后,会自动覆盖运行参数,包括:
|
||||
|
||||
- `model_name` ← `hf_model_id`
|
||||
- `model_name` ← `local_path`(相对路径会自动拼接 `model_root`)
|
||||
- `max_model_len` ← `ctx`
|
||||
- `max_num_seqs` ← `max_num_seqs`
|
||||
- `max_tokens` ← `max_tokens`
|
||||
@@ -100,19 +102,19 @@ docker compose up -d --build
|
||||
3. 验证自定义推理服务:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/health
|
||||
curl http://localhost:<services.api.port>/health
|
||||
```
|
||||
|
||||
4. 验证 OpenAI 协议服务:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8001/v1/models
|
||||
curl http://localhost:<services.openai.port>/v1/models
|
||||
```
|
||||
|
||||
## 推理请求示例
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/v1/generate" \
|
||||
curl -X POST "http://localhost:<services.api.port>/v1/generate" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"prompt\":\"请用三句话介绍大模型推理优化\",\"max_tokens\":128,\"temperature\":0.7}"
|
||||
```
|
||||
@@ -132,6 +134,7 @@ curl -X POST "http://localhost:8001/v1/chat/completions" \
|
||||
- API Key 使用 `config.json` 中 `api_key`
|
||||
- 模型名使用 `config.json` 中 `models.profiles.<模型名>.served_model_name`
|
||||
- 若使用工具调用,`config.json` 中应配置 `tool_call_parser` 与 `enable_auto_tool_choice`
|
||||
- 服务强制离线模式,不会回退到 Hugging Face 远程下载
|
||||
- 所有路径按 Ubuntu 规范填写,本地模型建议使用 `/opt/model/<模型目录>`
|
||||
|
||||
## 双 AMD R9700 调优建议
|
||||
|
||||
@@ -17,6 +17,8 @@ class Settings(BaseModel):
|
||||
port: int = 8000
|
||||
openai_host: str = "0.0.0.0"
|
||||
openai_port: int = 8001
|
||||
model_root: str = "/opt/model"
|
||||
offline_mode: bool = True
|
||||
max_model_len: int = 8192
|
||||
gpu_memory_utilization: float = 0.92
|
||||
tensor_parallel_size: int = 2
|
||||
@@ -42,6 +44,8 @@ def get_settings() -> Settings:
|
||||
port=runtime["port"],
|
||||
openai_host=runtime["openai_host"],
|
||||
openai_port=runtime["openai_port"],
|
||||
model_root=runtime["model_root"],
|
||||
offline_mode=runtime["offline_mode"],
|
||||
api_key=runtime["api_key"],
|
||||
tensor_parallel_size=runtime["tensor_parallel_size"],
|
||||
dtype=runtime["dtype"],
|
||||
|
||||
+27
-1
@@ -29,6 +29,27 @@ def _to_float(value: Any, default: float) -> float:
|
||||
return default
|
||||
|
||||
|
||||
def _to_str(value: Any, default: str = "") -> str:
|
||||
return str(value).strip() if value is not None else default
|
||||
|
||||
|
||||
def _join_posix(base_path: str, suffix_path: str) -> str:
|
||||
return f"{base_path.rstrip('/')}/{suffix_path.lstrip('/')}"
|
||||
|
||||
|
||||
def _resolve_profile_model_path(profile: dict[str, Any], model_root: str, model_key: str) -> str:
|
||||
local_path = _to_str(profile.get("local_path"))
|
||||
if not local_path:
|
||||
raise ValueError(f"model profile '{model_key}' must provide local_path")
|
||||
if "://" in local_path:
|
||||
raise ValueError(f"model profile '{model_key}' local_path must be local filesystem path")
|
||||
if local_path.startswith("/"):
|
||||
return local_path
|
||||
if not model_root:
|
||||
raise ValueError("config.json model_root cannot be empty when local_path is relative")
|
||||
return _join_posix(model_root, local_path)
|
||||
|
||||
|
||||
def load_catalog(catalog_path: str = "config.json") -> dict[str, Any]:
|
||||
content = json.loads(Path(catalog_path).read_text(encoding="utf-8"))
|
||||
if not isinstance(content, dict):
|
||||
@@ -50,6 +71,8 @@ def resolve_runtime_settings(content: dict[str, Any]) -> dict[str, Any]:
|
||||
"tensor_parallel_size": _to_int(content.get("tensor_parallel_size"), 2),
|
||||
"dtype": str(content.get("dtype", "bfloat16")),
|
||||
"revision": str(content.get("revision", "")).strip() or None,
|
||||
"model_root": _to_str(content.get("model_root"), "/opt/model"),
|
||||
"offline_mode": True,
|
||||
"model_key": str(models.get("selected", "")).strip() or None,
|
||||
}
|
||||
|
||||
@@ -66,6 +89,7 @@ def resolve_model_profile(
|
||||
profile = profiles[model_key]
|
||||
if not isinstance(profile, dict):
|
||||
raise ValueError(f"model profile '{model_key}' must be a JSON object")
|
||||
model_root = _to_str(content.get("model_root"), "/opt/model")
|
||||
valid_tp_raw = profile.get("valid_tp", [])
|
||||
valid_tp = [_to_int(item, 0) for item in valid_tp_raw if _to_int(item, 0) > 0]
|
||||
resolved_tp = requested_tp
|
||||
@@ -73,7 +97,7 @@ def resolve_model_profile(
|
||||
resolved_tp = valid_tp[0]
|
||||
updates = {
|
||||
"selected_model": model_key,
|
||||
"model_name": profile.get("hf_model_id", model_key),
|
||||
"model_name": _resolve_profile_model_path(profile, model_root, model_key),
|
||||
"served_model_name": profile.get("served_model_name", model_key),
|
||||
"max_model_len": _to_int(profile.get("ctx"), 8192),
|
||||
"max_num_seqs": _to_int(profile.get("max_num_seqs"), 64),
|
||||
@@ -86,4 +110,6 @@ def resolve_model_profile(
|
||||
"enable_auto_tool_choice": _to_bool(profile.get("enable_auto_tool_choice"), False),
|
||||
}
|
||||
env_vars = {str(k): str(v) for k, v in dict(profile.get("env", {})).items()}
|
||||
env_vars["HF_HUB_OFFLINE"] = "1"
|
||||
env_vars["TRANSFORMERS_OFFLINE"] = "1"
|
||||
return model_key, updates, env_vars
|
||||
|
||||
@@ -12,12 +12,15 @@
|
||||
"api_key": "sk-szcjw",
|
||||
"tensor_parallel_size": 2,
|
||||
"dtype": "bfloat16",
|
||||
"model_root": "/opt/model",
|
||||
"offline_mode": true,
|
||||
"revision": "",
|
||||
"models": {
|
||||
"default": "Qwen3.5-35B-A3B-GPTQ-Int4",
|
||||
"selected": "Qwen3.5-35B-A3B-GPTQ-Int4",
|
||||
"profiles": {
|
||||
"Qwen3-Next-80B-A3B-Instruct-AWQ-4bit": {
|
||||
"local_path": "Qwen3-Next-80B-A3B-Instruct-AWQ-4bit",
|
||||
"ctx": "24576",
|
||||
"trust_remote": true,
|
||||
"valid_tp": [2],
|
||||
@@ -34,6 +37,7 @@
|
||||
"hf_model_id": "cpatonn/Qwen3-Next-80B-A3B-Instruct-AWQ-4bit"
|
||||
},
|
||||
"GLM-4.7-Flash-AWQ": {
|
||||
"local_path": "GLM-4.7-Flash-AWQ",
|
||||
"ctx": "32768",
|
||||
"trust_remote": true,
|
||||
"valid_tp": [1, 2],
|
||||
@@ -46,6 +50,7 @@
|
||||
"hf_model_id": "THUDM/GLM-4.7-Flash-AWQ"
|
||||
},
|
||||
"Qwen3.5-27B-FP8": {
|
||||
"local_path": "Qwen3.5-27B-FP8",
|
||||
"ctx": "32768",
|
||||
"trust_remote": true,
|
||||
"valid_tp": [1, 2],
|
||||
@@ -58,6 +63,7 @@
|
||||
"hf_model_id": "RedHatAI/Qwen3.5-27B-FP8-dynamic"
|
||||
},
|
||||
"Qwen3.5-35B-A3B-GPTQ-Int4": {
|
||||
"local_path": "Qwen3.5-35B-A3B-GPTQ-Int4",
|
||||
"ctx": "32768",
|
||||
"trust_remote": true,
|
||||
"valid_tp": [1, 2],
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ services:
|
||||
container_name: rocm-vllm-inference
|
||||
command: ["python", "-m", "app.start_api"]
|
||||
ports:
|
||||
- "8000:8000"
|
||||
- "8002:8002"
|
||||
volumes:
|
||||
- /opt/model:/opt/model:ro
|
||||
- ./config.json:/workspace/config.json:ro
|
||||
|
||||
Reference in New Issue
Block a user