diff --git a/README.md b/README.md index 2f482dc..5808d73 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ - `services.openai.host` / `services.openai.port`:OpenAI 协议服务监听地址与端口(默认 `0.0.0.0:8001`) - `public_model_name`:对外固定模型名,切换底层模型时可保持调用方参数不变 +- `reasoning_enabled`:思考推理开关,默认 `false`(关闭) - `api_key`:OpenAI 接口访问密钥 - `tensor_parallel_size`:张量并行数,双卡建议 `2` - `dtype`:推理精度,默认 `bfloat16` @@ -44,7 +45,7 @@ - `models.default`:默认模型名 - `models.selected`:当前生效模型名 - `models.profiles`:模型配置集合 -- 每个模型必须包含:`local_path`,并建议补充 `ctx`、`max_num_seqs`、`max_tokens`、`dtype`、`quantization` +- 每个模型必须包含:`local_path`,并建议补充 `ctx`、`max_num_seqs`、`max_tokens`、`dtype`、`quantization`、`reasoning_parser` 启动时会按以下优先级选模型: @@ -91,6 +92,7 @@ curl -X POST "http://localhost:8001/v1/chat/completions" \ - Base URL 使用 `http://<服务器IP>:8001/v1` - API Key 使用 `config.json` 中 `api_key` - 模型名固定使用 `config.json` 中 `public_model_name`(默认 `Qwen_local_model`) +- 若要启用思考推理,将 `config.json` 中 `reasoning_enabled` 设为 `true` - 若使用工具调用,`config.json` 中应配置 `tool_call_parser` 与 `enable_auto_tool_choice` - 服务强制离线模式,不会回退到 Hugging Face 远程下载 - 所有路径按 Ubuntu 规范填写,本地模型建议使用 `/opt/model/<模型目录>` diff --git a/app/config.py b/app/config.py index 0d98a35..392c42f 100644 --- a/app/config.py +++ b/app/config.py @@ -18,6 +18,7 @@ class Settings(BaseModel): openai_host: str = "0.0.0.0" openai_port: int = 8001 public_model_name: str = "Qwen_local_model" + reasoning_enabled: bool = False model_root: str = "/opt/model" offline_mode: bool = True max_model_len: int = 8192 @@ -46,6 +47,7 @@ def get_settings() -> Settings: openai_host=runtime["openai_host"], openai_port=runtime["openai_port"], public_model_name=runtime["public_model_name"], + reasoning_enabled=runtime["reasoning_enabled"], model_root=runtime["model_root"], offline_mode=runtime["offline_mode"], api_key=runtime["api_key"], diff --git a/app/model_catalog.py b/app/model_catalog.py index 3a3c3ba..6c01468 100644 --- a/app/model_catalog.py +++ b/app/model_catalog.py @@ -70,6 +70,7 @@ def resolve_runtime_settings(content: dict[str, Any]) -> dict[str, Any]: "openai_port": _to_int(openai_service.get("port"), 8001), "public_model_name": _to_str(content.get("public_model_name"), "Qwen_local_model"), "api_key": str(content.get("api_key", "")).strip() or None, + "reasoning_enabled": _to_bool(content.get("reasoning_enabled"), False), "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, @@ -103,6 +104,7 @@ def resolve_model_profile( "served_model_name": profile.get("served_model_name", model_key), "dtype": _to_str(profile.get("dtype")), "quantization": _to_str(profile.get("quantization")), + "reasoning_parser": _to_str(profile.get("reasoning_parser")), "max_model_len": _to_int(profile.get("ctx"), 8192), "max_num_seqs": _to_int(profile.get("max_num_seqs"), 64), "max_tokens": _to_int(profile.get("max_tokens"), 4096), diff --git a/app/start_openai.py b/app/start_openai.py index b32479f..f0d7b38 100644 --- a/app/start_openai.py +++ b/app/start_openai.py @@ -19,9 +19,11 @@ def build_command() -> list[str]: host = str(runtime["openai_host"]) port = str(runtime["openai_port"]) public_model_name = str(runtime["public_model_name"]).strip() + reasoning_enabled = bool(runtime["reasoning_enabled"]) api_key = runtime["api_key"] or "" dtype = str(updates["dtype"] or runtime["dtype"]) quantization = str(updates["quantization"] or "").strip() + reasoning_parser = str(updates["reasoning_parser"] or "").strip() revision = runtime["revision"] or "" cmd = [ sys.executable, @@ -54,6 +56,8 @@ def build_command() -> list[str]: cmd.append("--enable-auto-tool-choice") if updates["tool_call_parser"]: cmd.extend(["--tool-call-parser", str(updates["tool_call_parser"])]) + if reasoning_enabled and reasoning_parser: + cmd.extend(["--reasoning-parser", reasoning_parser]) if quantization: cmd.extend(["--quantization", quantization]) if revision: diff --git a/config.json b/config.json index 9d2513a..955e120 100644 --- a/config.json +++ b/config.json @@ -10,6 +10,7 @@ } }, "public_model_name": "Qwen_local_model", + "reasoning_enabled": false, "api_key": "sk-szcjw", "tensor_parallel_size": 2, "dtype": "bfloat16", @@ -33,6 +34,7 @@ "VLLM_USE_TRITON_AWQ": "1" }, "tool_call_parser": "qwen3_xml", + "reasoning_parser": "qwen3", "enable_auto_tool_choice": true, "served_model_name": "Qwen3-Next-80B-A3B-Instruct-AWQ-4bit", "hf_model_id": "cpatonn/Qwen3-Next-80B-A3B-Instruct-AWQ-4bit" @@ -46,6 +48,7 @@ "max_tokens": "32768", "gpu_util": "0.98", "tool_call_parser": "qwen3_xml", + "reasoning_parser": "qwen3", "enable_auto_tool_choice": true, "served_model_name": "GLM-4.7-Flash-AWQ", "hf_model_id": "THUDM/GLM-4.7-Flash-AWQ" @@ -60,6 +63,7 @@ "max_tokens": "8192", "gpu_util": "0.94", "tool_call_parser": "qwen3_xml", + "reasoning_parser": "qwen3", "enable_auto_tool_choice": true, "served_model_name": "Qwen3.5-27B-FP8", "hf_model_id": "RedHatAI/Qwen3.5-27B-FP8-dynamic" @@ -70,12 +74,13 @@ "quantization": "gptq", "ctx": "65536", "trust_remote": true, - "valid_tp": [1, 2], + "valid_tp": [2], "max_num_seqs": "2", - "max_tokens": "8192", + "max_tokens": "2048", "gpu_util": "0.90", "enforce_eager": false, "tool_call_parser": "qwen3_xml", + "reasoning_parser": "qwen3", "enable_auto_tool_choice": true, "served_model_name": "Qwen3.5-35B-A3B-GPTQ-Int4", "hf_model_id": "Qwen/Qwen3.5-35B-A3B-GPTQ-Int4"