# 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
```

结果：通过。
