Skip to content

接入 Grok Console 真流式 Responses 事件#16

Merged
zqbxdev merged 9 commits into
zqbxdev:devfrom
Zker67:fix-grok-console-streaming
May 25, 2026
Merged

接入 Grok Console 真流式 Responses 事件#16
zqbxdev merged 9 commits into
zqbxdev:devfrom
Zker67:fix-grok-console-streaming

Conversation

@Zker67
Copy link
Copy Markdown
Contributor

@Zker67 Zker67 commented May 24, 2026

变更说明

这个 PR 处理 #12:Grok Console 模型在 /v1/chat/completions 中传入 stream=true 时,不再先等待阻塞式完整响应,而是接入 Console Responses 的 SSE 事件流,并逐步转换成 OpenAI chat completion chunk。

主要改动

  • GrokConsoleClient 增加 stream_response()

    • 请求 console.x.ai/v1/responses 时传入 stream=true
    • 使用 HTTP streaming 读取上游 SSE 行。
    • 解析 data: {...} 事件,并在 [DONE] 后停止。
    • response.failed / response.incomplete / error 事件抛出明确错误。
  • 新增 Console 流式 delta 提取:

    • response.output_text.delta 转成 delta.content
    • response.reasoning_summary_text.delta 等 reasoning/thinking delta 转成 delta.reasoning_content
    • 忽略 response.completed 这类完成快照,避免把完整文本重复输出。
  • 修改 Grok Console 的 /v1/chat/completions 流式路径:

    • stream=true 时调用新的 console_chat_completion_events()
    • 不再调用阻塞式 console_chat_completion()
    • app-chat 模型路径保持原样。
    • stream=false 的现有非流式行为保持原样。

验证

已通过定向回归测试:

uv run python -m unittest \
  test.test_grok_provider.GrokProviderTests.test_extract_console_stream_delta_from_output_text_delta \
  test.test_grok_provider.GrokProviderTests.test_extract_console_stream_delta_from_reasoning_delta \
  test.test_grok_provider.GrokProviderTests.test_extract_console_stream_delta_ignores_completed_snapshot \
  test.test_grok_provider.GrokProviderTests.test_grok_console_stream_response_parses_sse_lines \
  test.test_grok_provider.GrokProviderTests.test_grok_console_stream_response_raises_stream_errors \
  test.test_grok_provider.GrokProviderTests.test_streaming_grok_chat_completion_returns_openai_chunks \
  test.test_grok_provider.GrokProviderTests.test_streaming_grok_console_completion_emits_reasoning_content

结果:Ran 7 tests ... OK

另外执行了:

git diff --check

结果:通过。

关联:#12

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI PR Review

Decision: request_changes
Confidence: 0.90

Summary

感谢这次改动,整体方向正确(将 Grok Console 的 stream=true 路径改为真实 SSE 转发,并补充了针对性测试)。但当前 SSE 解析实现存在协议层面的缺陷,可能导致流被提前终止或事件解析错误,属于阻塞问题。

Findings

  1. [major] services/providers/grok.py - _iter_console_stream_events()if not payload or payload == "[DONE]": break 处把空 data: 负载也当成流结束处理。按 SSE 语义,空 data 帧不应终止整个流(通常应忽略或作为空事件处理)。这会导致上游偶发空 data 行时提前中断输出。
  2. [major] services/providers/grok.py - _iter_console_stream_events() 按“单行即单事件 JSON”解析,未支持 SSE 的多行 data: 聚合(同一事件可能由多条 data: 行组成,空行分隔事件)。当前实现在遇到分片/多行 data 时会解析失败并丢事件,影响流式稳定性与完整性。

Recent Comment Summary

建议先修复 SSE 事件解析:不要将空 payload 视为结束;按 SSE 规范累积同一事件的多行 data,遇事件分隔后再统一 JSON 解析;仅在显式 [DONE] 时结束。修复后这组改动就更稳了。

@Zker67
Copy link
Copy Markdown
Contributor Author

Zker67 commented May 24, 2026

补充说明:当前 enforce 检查失败不是代码或目标分支问题。

这个 PR 的 base 是 dev。失败日志显示 workflow 在执行 removeWrongTargetBranchLabel() 时调用 DELETE /issues/16/labels/wrong-target-branch,但 fork PR 场景下 GITHUB_TOKEN 对该 label 操作返回 403 Resource not accessible by integration

review 检查已通过;本地定向回归测试也已通过。

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI PR Review

Decision: request_changes
Confidence: 0.91

Summary

整体改动方向正确,已将 Grok Console 的 chat/completions 流式路径切到真实 SSE 事件并补充了针对性测试;但当前 SSE 解析实现不符合标准事件聚合语义,存在在合法 SSE 分帧下丢事件的风险,需要先修复后再合入。

Findings

  1. [major] services/providers/grok.py:246 - _iter_console_stream_events 逐行把 data: 直接当成完整 JSON 解析,没有按 SSE 规范聚合同一事件的多条 data: 行(以空行作为事件结束)。这会导致上游若发送合法的多行 data 事件时被 json.loads 失败并丢弃事件,流式输出可能缺块或中断。建议按事件边界缓存并拼接 data 后再解析。

Recent Comment Summary

请先修复 SSE 事件聚合解析(支持多 data: 行 + 空行提交事件)再继续;其余流式路径切换与测试补充整体良好。

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI PR Review

Decision: comment
Confidence: 0.84

Summary

整体改动方向正确,已将 Grok Console 的流式路径从阻塞式完整返回切换为基于 Responses SSE 事件逐步产出 OpenAI chunk,测试覆盖也比较完整。我没有看到必须阻塞合并的确定性缺陷。

Findings

  1. [minor] services/providers/grok.py:970 - console_chat_completion_events()account_service.mark_text_used(access_token) 仅在上游流被完整消费后才会执行。对于客户端中途断开或调用方提前停止迭代的场景,这个计数可能不会落账,建议评估是否需要在更稳妥的位置(如 finally)处理使用标记。
  2. [minor] services/providers/grok.py:252 - _iter_console_stream_events() 对每行执行了 strip(),会移除 data: 载荷的前后空白。对当前 JSON 事件影响较小,但从 SSE 规范角度这会改变原始 data 内容(尤其是文本型/非 JSON 事件),建议只处理换行并尽量保留有效载荷原样。

Recent Comment Summary

功能实现与测试总体良好,建议补强流式提前终止时的账号使用标记时机,并优化 SSE 行处理以避免不必要的 payload 空白裁剪。

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI PR Review

Decision: comment
Confidence: 0.85

Summary

整体实现方向正确:已将 Grok Console 的 stream=true 从阻塞式响应切到基于 SSE 的增量事件转换,并补充了较完整的单元测试覆盖(delta 提取、SSE 解析、多行 data、错误事件、流式路径切换)。未发现明确阻塞性问题。

Findings

  1. [minor] services/providers/grok.py:456 - stream_response() 中直接迭代 response.iter_lines(),但没有显式关闭/释放 response 对象。若底层客户端在长连接或异常中断时不自动回收,可能带来连接资源占用风险。建议在可行时使用上下文管理或在 finally 中显式 close。

Recent Comment Summary

实现与测试整体质量较好,流式行为改造符合 PR 目标;建议补充 response 生命周期管理以降低潜在连接泄漏风险。

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI PR Review

Decision: request_changes
Confidence: 0.93

Summary

整体实现方向正确,已将 Grok Console 的 chat/completions 流式路径切换到 Responses SSE,并补充了较完整的单测。但 SSE 解析器存在一个会导致事件类型串扰的协议问题,可能错误标注后续 chunk 类型(如把普通 output 误判为 reasoning),属于需要修复的阻塞项。

Findings

  1. [major] services/providers/grok.py:269 - _iter_console_stream_eventscurrent_event 在一次事件 flush 后未重置,导致后续仅含 data:(无 event:)的事件会沿用上一次事件类型。按 SSE 语义,每个事件块若未声明 event 应回退为默认类型而不是继承前一块,当前实现会产生事件类型串扰并可能把后续文本错误映射为 reasoning_content 或其他类型。建议在每次 dispatch(空行 flush 或 [DONE] flush)后重置 current_event

Recent Comment Summary

请先修复 SSE 事件类型在事件边界后的重置问题;该问题会导致流式 delta 类型误判,影响输出正确性。其余改动和测试覆盖总体良好。

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI PR Review

Decision: comment
Confidence: 0.84

Summary

整体实现方向正确:已将 Grok Console 的 stream=true 路径切换为基于 Responses SSE 的增量转发,并补充了较完整的单测覆盖(delta 提取、SSE 解析、错误事件、路由选择)。未发现明确阻塞合入的缺陷。

Findings

  1. [minor] services/providers/grok.py:994 - console_chat_completion_events() 仅在 stream_started=True(即至少 yield 过一个事件)时才调用 account_service.mark_text_used(access_token)。若上游成功建立流但仅返回控制事件/空流(例如直接 [DONE]),该请求不会计入使用,可能导致用量统计偏差。建议按“请求已成功发起并获得非错误响应”判定是否计费/标记使用,而非是否产出业务 delta。

Recent Comment Summary

变更质量较好,流式链路和错误处理总体到位;建议修正账号使用标记时机,避免空流成功请求漏记。

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI PR Review

Decision: comment
Confidence: 0.88

Summary

整体实现方向正确:PR 已将 Grok Console 的流式路径从阻塞式完成响应切换为 SSE 事件流,并在 OpenAI chunk 输出侧完成了增量转换;测试也覆盖了关键场景(delta 提取、SSE 解析、错误抛出、流式分支选择)。未发现必须阻塞合入的严重缺陷。

Findings

  1. [minor] services/providers/grok.py:497 - stream_response()response.status_code >= 400 时会直接抛错,但未尝试读取并附带上游响应体中的错误详情(仅返回 HTTP <status>)。这会降低排障效率,建议在可用时提取上游错误 message/code 并拼接到异常信息中。

Recent Comment Summary

变更整体质量较好、目标分支为 dev 也符合流程要求。建议补充上游 4xx/5xx 响应体错误透传,以便定位线上流式失败原因。

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI PR Review

Decision: comment
Confidence: 0.89

Summary

整体实现方向正确:PR 已将 Grok Console 的 stream=true 路径从阻塞式 completion 切换为基于 Responses SSE 的增量事件转换,并补充了较完整的单测覆盖(delta 提取、SSE 解析、错误事件、路由分支)。目标分支为 dev,符合仓库流程要求。未发现明确的阻塞性问题。

Findings

  1. [minor] services/providers/grok.py:1031 - console_chat_completion_events() 在已产生部分流事件后若发生 GrokConsoleError,会在 except 中将 mark_used 强制置回 False,导致最终不执行 mark_text_used。这可能使“已实际消耗账号额度但流中途失败”的场景下账号使用统计偏差。建议仅在“完全未开始产出事件”时不标记已使用,或通过独立标志区分“是否已消耗”与“是否成功完成”。

Recent Comment Summary

实现与测试质量总体不错,建议修正中途失败时的账号使用标记语义,以避免统计不一致。

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI PR Review

Decision: comment
Confidence: 0.86

Summary

整体改动方向正确:已将 Grok Console 的 stream=true 路径切换为真实 SSE 事件流,并补充了较完整的单测覆盖(delta 提取、SSE 解析、错误事件处理、路由行为)。从补丁看未发现明确阻塞性缺陷。

Findings

  1. [minor] services/providers/grok.py:234 - extract_console_stream_delta() 目前通过 "reasoning" in event_type or "thinking" in event_type 判定推理字段,属于宽匹配。若上游未来新增包含这些子串但语义非推理的 delta 事件,可能被错误映射到 reasoning_content。建议后续考虑维护明确的事件白名单(如 response.reasoning_summary_text.delta 等)以降低误判风险。
  2. [info] services/protocol/openai_v1_chat_complete.py:118 - 新的流式分片发送逻辑会在首个有效 delta 才发送 role=assistant;若上游只发送完成事件且无任何 delta,会回退发送空内容 chunk(这是合理兜底)。建议在接口文档中补一句该行为,便于客户端理解空首包场景。

Recent Comment Summary

实现思路与测试覆盖都比较扎实,建议后续仅在 delta 事件类型映射的鲁棒性上做收敛(白名单化)并补充文档说明。

@Zker67
Copy link
Copy Markdown
Contributor Author

Zker67 commented May 25, 2026

补充更新:已按 review 修正 Grok Console SSE 流式解析。

  • 解析 event: 名称,并在 data: JSON 缺少 type 时用于当前事件块。
  • 支持多行 data: 聚合,空 data: 不再结束流,仅 [DONE] 结束。
  • 事件块 dispatch 后重置 SSE event,避免类型串扰。
  • 流式 response 在结束或中断时显式 close。
  • 账号使用标记覆盖正常完成、客户端提前关闭、空流成功完成、部分事件后失败。
  • 4xx/5xx 流式错误会透传上游响应体中的 message/code,便于排障。

本地验证:python -m py_compile services/providers/grok.py test/test_grok_provider.py;11 个 Grok Console 流式相关单测通过;git diff --check 通过。

@zqbxdev zqbxdev merged commit 2f0990d into zqbxdev:dev May 25, 2026
1 of 2 checks passed
@zqbxdev
Copy link
Copy Markdown
Owner

zqbxdev commented May 25, 2026

Thanks @Zker67, merged this as 2f0990dfbeb20a15b48412e4a9f8714bc1d33f42.

Review/verification summary:

  • Confirmed the change is scoped to Grok Console /v1/chat/completions streaming and keeps app-chat plus non-streaming Console behavior separate.
  • Reviewed the SSE parsing fixes for event names, multiline data: aggregation, [DONE] handling, response closing, stream error propagation, and account usage marking on completed/partial streams.
  • Security check found no dependency/script/CI/Docker changes, no suspicious execution/download paths, and no sensitive credential logging introduced.
  • Ran focused Grok Console streaming tests: Ran 7 tests ... OK.
  • Ran adjacent Grok regressions including Console payload/search and Browser Bridge coverage: Ran 17 tests ... OK.
  • Ran python3 -m compileall -q services/protocol/openai_v1_chat_complete.py services/providers/grok.py test/test_grok_provider.py successfully.
  • Built and smoke-tested Docker image webchat2api:pr16-merged (sha256:7a88e17e5e7388682ef95966d335f71a53ecc130d32c828183d4181f5e968ca6): /health returned {"status":"ok"} and /version returned {"version":"0.0.7"}.

Notes:

  • The PR Target Branch / enforce failure is the known fork-label cleanup false-red (403 Resource not accessible by integration) from the older workflow revision; the same issue has already been fixed on dev.
  • Full local unittest discovery remains noisy in this environment due existing import/stub issues, so I treated the focused Grok tests, adjacent regressions, compile, Docker build, and smoke test as the merge gate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants