Prhub

#33446 [Fix] Reformat /vertex_generate successful predictions

原始 PR 作者 dstnluong 合并时间 2026-08-07 08:00 文件变更 1 提交数 3 评论 2 代码增减 +4 / -2

执行摘要

修复 /vertex_generate 成功响应未包装 predictions 的问题

PR body 说明:"Successful predictions were being short-circuited the last line never got executed." 即原有逻辑中只要 ret 是 Response 就直接返回,导致最后一行包装 predictions 的逻辑永远不执行。该问题源于之前为正确报告错误所做的改动("This issue came from previous efforts to correctly reports errors"),使得 Vertex AI 路由的成功响应格式不符合预期。

该 PR 值得快速阅读,因为它揭示了一个典型的"短路返回导致后续逻辑不可达"的 bug 模式,以及修复时容易遗留死代码的教训。建议阅读 http_server.pyvertex_generate 的实现,并关注其与 generate_request 返回类型的互动。合并后应跟进清理死代码、补充单元测试,并确认非 Response 返回值是否需要保留包装逻辑。

讨论亮点

该 PR 几乎没有实质技术讨论。唯一 review 来自 JustinTong0323,直接给出 APPROVED 和 "LGTM"。Issue 评论中有 gemini-code-assist 的自动退出提示,以及 Jiminator 触发的 /tag-and-rerun-ci。没有针对死代码或行为变更的质疑。

实现拆解

实现拆解如下:

  1. 定位问题:在 python/sglang/srt/entrypoints/http_server.pyvertex_generate 函数中,原代码 if isinstance(ret, Response): return ret 会在成功响应(也是 Response 对象)时直接返回,导致后续 return ORJSONResponse({"predictions": ret}) 成为死代码。

  2. 修改响应分支:将原判断改为 if isinstance(ret, Response) and ret.status_code == 200 and hasattr(ret, "body"),命中时用 orjson.loads(ret.body) 解析响应体,再包装进 {"predictions": ...} 字典返回;其他情况(错误响应、非 Response 数据)直接 return ret 透传。

  3. 遗留问题:由于修改时未删除原先兜底的 return ORJSONResponse({"predictions": ret}) 行,该行成为不可达死代码;同时非 Response 的成功数据(如果存在)不再被包装,可能改变响应契约。

  4. 测试配套:该 PR 未新增或修改任何测试文件,也没有补充 Vertex AI 路由的单元测试,仅依赖现有 CI。

文件 模块 状态 重要度
python/sglang/srt/entrypoints/http_server.py HTTP 服务 modified 5.19

关键符号

vertex_generate

关键源码片段

python/sglang/srt/entrypoints/http_server.py core-logic

这是唯一被修改的文件,也是问题所在。`vertex_generate` 函数的响应处理逻辑被重写,修复了成功响应未包装的问题,但引入了死代码。

# Vertex AI 兼容的生成端点(默认路由 /vertex_generate)
@app.post(os.environ.get("AIP_PREDICT_ROUTE", "/vertex_generate"))
async def vertex_generate(
    vertex_req: Annotated[VertexGenerateReqInput, Body()], raw_request: Request
):
    # 没有实例时直接返回空列表
    if not vertex_req.instances:
        return []
​
    # 从 instances 中提取第一类有效输入:text / input_ids / input_embeds
    inputs = {}
    for input_key in ("text", "input_ids", "input_embeds"):
        if vertex_req.instances[0].get(input_key):
            inputs[input_key] = [
                instance.get(input_key) for instance in vertex_req.instances
            ]
            break
​
    # 收集所有非空 image_data,若全部为空则为 None
    image_data = [
        instance.get("image_data")
        for instance in vertex_req.instances
        if instance.get("image_data") is not None
    ] or None
​
    # 组装请求并调用底层生成逻辑
    req = GenerateReqInput(
        **inputs,
        image_data=image_data,
        **(vertex_req.parameters or {}),
    )
    ret = await generate_request(req, raw_request)
​
    # 成功响应(HTTP 200 且带 body)需解析 body 后包装成 {"predictions": ...}
    if isinstance(ret, Response) and ret.status_code == 200 and hasattr(ret, "body"):
        return ORJSONResponse({"predictions": orjson.loads(ret.body)})
​
    # 非成功 Response(如错误)或非 Response 数据直接透传
    return ret
​
    # FIXME: 下面这行是不可达死代码,应删除;
    # 原逻辑中非 Response 数据会在此处包装为 predictions,
    # 但上方 return ret 已短路,永远不会执行。
    return ORJSONResponse({"predictions": ret})

评论区精华

CI 重跑 other

Jiminator 在 issue 评论中触发 `/tag-and-rerun-ci`,用于重新运行 CI 流水线。

结论:CI 重新运行后通过(Latest PR Test (Extra) 为绿色)。 · 已解决

风险与影响

主要风险如下:

  • 死代码残留http_server.pyreturn ORJSONResponse({"predictions": ret}) 位于 return ret 之后,永远不可达,说明代码清理不彻底,容易误导后续维护者。
  • 响应格式行为变化:修改前非 Response 的成功数据会被包装为 {"predictions": ...},修改后被直接透传(return ret)。虽然当前 generate_request 大概率总是返回 Response,但无法确定所有路径,存在改变 API 契约的潜在风险。
  • 缺少测试覆盖:没有针对 vertex_generate 的成功、错误、异常路径的单元测试,后续回归难以察觉。
  • 低风险性:改动只影响单个端点,且错误响应透传逻辑与原意图一致,整体风险可控。

影响范围限定在 Vertex AI 兼容入口 /vertex_generate(或 AIP_PREDICT_ROUTE 环境变量指定的路由)。对于调用该端点的用户,成功响应的 JSON 结构从直接返回生成结果变为 {"predictions": <生成结果>},与 Vertex AI 预言接口的格式对齐;错误响应格式不变。对系统其他模块无影响,团队后续需要清理死代码并补测试。

死代码残留 缺少测试覆盖 API 响应格式变更

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论