fix(vendor-logging): 修复流式错误日志中文乱码并解耦 antigravity scope 检测;#278
Merged
ThreeFish-AI merged 2 commits intoJul 6, 2026
Conversation
上游供应商返回 4xx/5xx 时,流式错误分支将 response.aread() 得到的 bytes 经 %s 直接格式化,Python 走 repr() 导致非 ASCII 的 UTF-8 字节被转义为 \xe6\x82\xa8 之类的不可读序列(中文乱码)。 - model/vendor.py 新增公开工具函数 decode_error_body(raw, limit=500): 先整体 UTF-8 解码(errors="replace" 容忍非法字节)再按字符截断,避免 在多字节边界切断;作为「错误响应体日志解码」的单一事实源; - base.py / copilot.py / antigravity.py 三处同型流式错误日志统一复用该 helper;其中 antigravity 消除了原先重复的 decode 调用(scope 检查与日志 共用一次解码); - model 包(vendor.__all__ 与 __init__)同步 re-export; - 新增 tests/test_vendor_helpers.py:单元测试覆盖中文解码/字符截断/非法字节/ 空值/回归对照,集成测试经 BaseVendor 流式路径(MockTransport 返回 400+中文) 以 caplog 断言日志含可读中文且无字节转义。 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
流式错误路径此前把 decode_error_body(error_body)(截断至前 500 字符)
既用于日志展示、又传入 _mark_scope_error_if_needed 做子串检测。检测这类
判定性逻辑不应受日志展示上限约束:当上游 scope 错误体在
ACCESS_TOKEN_SCOPE_INSUFFICIENT 标记出现前已超过 500 字符时,标记会被
截断丢弃,token 不再标记 needs_reauth 而继续以 scope 不足的凭证重试。
- 检测改用完整解码文本 error_body.decode("utf-8", errors="replace"),
日志展示仍用 decode_error_body 的 500 字符截断版本,二者关注点解耦;
- 与非流式路径(send_message)传入完整 response.text 的行为恢复一致;
- 补充回归测试:构造标记位于第 500 字符之后的 403 错误体,断言检测仍触发
(旧截断实现下该用例失败)。
🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
ThreeFish-AI
added a commit
that referenced
this pull request
Jul 6, 2026
版本号升级至 0.5.2a8,CHANGELOG 定版。本版收敛 PR #278 两项修复:流式 4xx/5xx 错误日志中文乱码(bytes 经 %s 走 repr() 把 UTF-8 字节转义为不可读序列)经新增 decode_error_body 工具统一收敛;antigravity 流式 scope 检测改用完整解码 body, 与日志展示截断解耦并补回归测试。 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景(Why)
上游 4xx/5xx 的流式错误日志此前以
logger.warning("...body=%s...", error_body[:500])直接格式化bytes。Python 对bytes走repr(),非 ASCII 的 UTF-8 字节被转义为\xe6\x82\xa8之类序列——中文错误信息(如上游限流/鉴权提示)在日志中乱码、不可读,直接拖慢线上排障。变更(What)
model/vendor.py新增decode_error_body(raw, limit=500)——decode("utf-8", errors="replace")容错解码(非法字节降级为�,日志路径绝不抛异常),先整体解码再按字符截断(避免在多字节 UTF-8 边界切断产生二次乱码,故limit语义为字符数),并经model/__init__与vendors/base导出复用。base.py/copilot.py/antigravity.py三处流式错误日志统一改用该 helper。_mark_scope_error_if_needed的子串检测。当ACCESS_TOKEN_SCOPE_INSUFFICIENT标记位于第 500 字符之后时会被截断丢弃,token 不再标记needs_reauth,持续以 scope 不足的凭证重试。改为检测用完整解码文本、日志展示用截断文本,二者关注点解耦,并恢复与非流式路径(send_message传完整response.text)的行为一致性。实现与测试
tests/test_vendor_helpers.py(7 项):中文可读性、字符截断、非法字节降级、空/ASCII、%s-vs-decode 回归对照,以及经BaseVendor.send_message_stream的集成断言。tests/test_antigravity.py::test_stream_scope_detection_scans_full_body_not_log_truncation:构造标记位于第 500 字符之后的 403 错误体,断言检测仍触发(已实测在旧截断实现下该用例失败,证明其守护有效)。rufflint/format 通过,pre-commit hooks 全绿。