Align OpenAI serving behavior with Para deployments

Absorb PR 11's final Para compatibility surface as an opt-in OpenAI serving layer rather than hard-coding business defaults into protocol models. The change adds server args for Para chat defaults, Kimi/GLM compatibility, tool-choice normalization, tool-role text flattening, and streaming first-chunk error preflight while preserving default upstream behavior unless explicitly enabled.

Reasoning token usage is also propagated through chat/completion usage paths, with GLM compatibility emitting completion_tokens_details.reasoning_tokens. Low-risk protocol fixes accept string image_url content parts and preserve GLM function-call argument value whitespace.

Constraint: Online Para-compatible deployments require request/response semantics that differ from default OpenAI serving behavior.

Constraint: Current CP/HiCache/bs>1 work must not be coupled to OpenAI serving compatibility changes.

Rejected: Merge PR 11 history directly | intermediate commits briefly hard-code chat max_tokens=32768 before later gating it by server args.

Rejected: Enable Para compatibility by default | would change non-Para OpenAI-compatible deployments.

Confidence: high

Scope-risk: moderate

Directive: Keep Para-specific serving policies behind explicit server args unless the business contract changes globally.

Tested: PYTHONPATH=python:. python -m unittest discover -s test/registered/unit/entrypoints/openai -p 'test_para_serving_protocol.py' -v (19 tests OK)

Tested: python -m py_compile modified OpenAI serving, tokenizer manager, server_args, function-call detector, and test files

Not-tested: Live router/prefill/decode OpenAI serving E2E after enabling Para flags.

Co-authored-by: OmX <omx@oh-my-codex.dev>
This commit is contained in:
laoyao0822
2026-06-11 05:59:08 +08:00
parent 8cfdb0466e
commit 9c8e3e99cb
11 changed files with 1262 additions and 7 deletions

View File

@@ -0,0 +1,159 @@
# Para OpenAI Serving 对齐说明
本文总结当前分支对 `sglang-para` OpenAI API / serving 相关行为的对齐范围、默认策略和启用方式。
## 总体策略
本分支已经实现 Para 侧高风险 serving 行为,但为了降低耦合,**默认不启用 Para 兼容策略**。需要对齐 Para 业务行为时,通过 server args 显式打开。
默认保持上游/本分支原行为:
- chat 请求不自动填 `max_tokens=32768`
- 不强制把 `tool_choice` 改为 `auto`
- 不按模型路径自动启用 Kimi / GLM 特化逻辑。
- 不默认 flatten tool role 的 content list。
- streaming 不默认预拉首 chunk 做 HTTP error 转换。
## 启用 Para 兼容的参数
完整启用 Para OpenAI serving 对齐时,可在启动参数中加入:
```bash
--openai-chat-default-max-tokens 32768 \
--openai-force-tool-choice-auto \
--openai-kimi-compat \
--openai-glm-compat \
--openai-flatten-tool-role-text-content \
--openai-streaming-error-preflight
```
参数含义:
| 参数 | 默认值 | 启用后行为 |
| ----------------------------------------- | ------- | ----------------------------------------------------------------------------------------- |
| `--openai-chat-default-max-tokens 32768` | `0` | chat 请求未传 `max_tokens/max_completion_tokens` 时,在 serving 层补默认输出上限。 |
| `--openai-force-tool-choice-auto` | `False` | 将显式非 `auto` 的 chat `tool_choice``auto` 服务。 |
| `--openai-kimi-compat` | `False` | 根据 `model_path` 识别 Kimi启用 Kimi `thinking` 映射和固定采样参数。 |
| `--openai-glm-compat` | `False` | 根据 `model_path` 识别 GLM启用 GLM tool choice 降级、413 超长错误和 GLM usage details。 |
| `--openai-flatten-tool-role-text-content` | `False` | 将 tool role 的纯 text content parts list flatten 成字符串后交给 chat template。 |
| `--openai-streaming-error-preflight` | `False` | streaming 先预拉首 chunk若首 chunk 是错误,转成普通 HTTP error而不是 SSE 内报错。 |
## 已对齐的功能点
### 1. Chat 默认输出长度
Para 行为:未传 `max_tokens/max_completion_tokens` 时默认 `32768`
当前实现:
- `ChatCompletionRequest.max_tokens` 仍保持 `None`,避免协议模型硬编码业务默认值。
- serving 层读取 `openai_chat_default_max_tokens`
-`--openai-chat-default-max-tokens 32768` 后,效果与 Para 一致。
### 2. Kimi 兼容
启用 `--openai-kimi-compat` 后:
- 支持 chat 请求中的 `thinking` 字段。
- Kimi 模型下将 `thinking.type != "disabled"` 映射到 `chat_template_kwargs["thinking"]`
- 对 Kimi 应用 Para 固定采样参数:
- `top_p=0.95`
- `presence_penalty=0.0`
- `frequency_penalty=0.0`
- `n=1`
### 3. GLM 兼容
启用 `--openai-glm-compat` 后:
- 根据 `model_path` 包含 `glm` 识别 GLM。
- GLM 下 `tool_choice="required"` 降级为 `"auto"`
- GLM input token 已超过 context length 时,抛 `PayloadTooLargeError`OpenAI serving 返回 HTTP `413`
- GLM chat usage 使用 `completion_tokens_details.reasoning_tokens`
未启用时GLM 超长输入仍走普通 `ValueError -> HTTP 400` 路径。
### 4. Tool choice 全局兼容
启用 `--openai-force-tool-choice-auto` 后:
- chat 请求中显式非 `auto``tool_choice` 会在 serving 层转为 `auto`
- 协议层不再改写 `tool_choice`,便于关闭该行为并降低耦合。
### 5. Tool role content flatten
启用 `--openai-flatten-tool-role-text-content` 后:
-`role="tool"` 且 content 是纯 text parts list 的消息,将内容拼成字符串。
- 仅 flatten 纯 text parts包含其他结构字段的 list 保持原样,避免破坏依赖结构化 tool content 的模板。
### 6. GLM function call 参数保留空格
无条件对齐 Para
- `glm4_moe_detector.py`
- `glm47_moe_detector.py`
两处 detector 不再对参数值执行 `strip()`,只 strip 参数 key。这样可以保留模型输出或客户端参数值里的前后空格。
### 7. `image_url` 字符串兼容
无条件对齐 Para
- 支持 OpenAI multimodal content part 中 `image_url` 直接传字符串。
- Pydantic validator 自动转成 `{ "url": ... }`
### 8. Streaming 首包错误转换
启用 `--openai-streaming-error-preflight` 后:
- chat streaming 会先拉取首个 chunk。
- 如果首 chunk 是 SSE error payload会转成普通 HTTP error response。
- 如果首 chunk 正常,会 prepend 回 stream不影响正常 streaming。
### 9. Usage / reasoning tokens
已对齐 Para usage 聚合能力:
- `UsageInfo` 增加 `completion_tokens_details`
- `UsageProcessor` 聚合 `reasoning_tokens`
- Chat/Completion serving 都向 usage processor 传递 reasoning token 计数。
- 启用 GLM compat 后GLM chat 使用 `completion_tokens_details.reasoning_tokens`
## 主要改动文件
- `python/sglang/srt/server_args.py`
- `python/sglang/srt/entrypoints/openai/protocol.py`
- `python/sglang/srt/entrypoints/openai/serving_chat.py`
- `python/sglang/srt/entrypoints/openai/serving_base.py`
- `python/sglang/srt/entrypoints/openai/usage_processor.py`
- `python/sglang/srt/entrypoints/openai/serving_completions.py`
- `python/sglang/srt/managers/tokenizer_manager.py`
- `python/sglang/srt/function_call/glm4_moe_detector.py`
- `python/sglang/srt/function_call/glm47_moe_detector.py`
- `test/registered/unit/entrypoints/openai/test_para_serving_protocol.py`
## 验证
本地 macOS 使用 uv 虚拟环境完成轻量单测验证:
```bash
VIRTUAL_ENV=/private/tmp/sglang-para-test-venv \
PATH=/private/tmp/sglang-para-test-venv/bin:$PATH \
UV_CACHE_DIR=/private/tmp/uv-cache \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONPATH=python:. \
uv run --active --no-sync python -m unittest discover \
-s test/registered/unit/entrypoints/openai \
-p 'test_para_serving_protocol.py' -v
```
结果:`Ran 19 tests ... OK`
同时执行:
```bash
govctl check
```
结果:通过。