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:
159
docs/references/para_openai_serving_alignment.md
Normal file
159
docs/references/para_openai_serving_alignment.md
Normal 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
|
||||
```
|
||||
|
||||
结果:通过。
|
||||
Reference in New Issue
Block a user