Skip to content

fix(routes): 修复 count_tokens 路由对 target_vendor.name 的错误属性访问;#235

Merged
ThreeFish-AI merged 1 commit into
feature/1.x.xfrom
ThreeFish-AI/diagnose-backend-log-error
May 11, 2026
Merged

fix(routes): 修复 count_tokens 路由对 target_vendor.name 的错误属性访问;#235
ThreeFish-AI merged 1 commit into
feature/1.x.xfrom
ThreeFish-AI/diagnose-backend-log-error

Conversation

@ThreeFish-AI
Copy link
Copy Markdown
Owner

问题概述

后台日志反复出现 POST /v1/messages/count_tokens?beta=true 500 Internal Server Error,伴随 AttributeError: 'ZhipuVendor' object has no attribute 'name'。同一时间窗口内大量请求 200 OK、少量 500,呈"间歇性"故障特征。

根因分析

  • 表因src/coding/proxy/server/routes.py:153,160count_tokens 路由中访问 target_vendor.name,触发 AttributeError 返回 500。
  • 根因BaseVendor 仅暴露抽象方法 get_name() -> strsrc/coding/proxy/vendors/base.py:75-77),所有派生类(ZhipuVendorAnthropicVendorCopilotVendorMinimaxVendorDoubaoVendorKimiVendor 等)均通过 _vendor_name 类属性配合 get_name() 返回名称 —— 并无 name 实例属性
  • 间歇性原因:第 152 行 if source: 是守卫;sourceinfer_source_vendor_from_body() 启发式推断,仅当请求体出现 zhipu 私有产物(srvtoolu_* id 或 server_tool_use 块)时返回 "zhipu",否则 None。纯净的首轮 count_tokens 请求自然绕过 153 行,因此 200/500 共存。

变更内容

  • src/coding/proxy/server/routes.py:将 target_vendor.name 改为 target_vendor.get_name(),并提取局部变量 target_name 避免重复方法调用、保障日志/调用点一致。
  • tests/test_app_routes.py:新增回归测试 test_count_tokens_triggers_zhipu_to_target_channel,通过注入 server_tool_use + srvtoolu_*infer_source_vendor_from_body 返回 "zhipu",断言返回 200 且 debug 日志含 count_tokens channel zhipu → anthropic反向验证通道被实际触发(避免"未触达分支即通过"的盲点)。
  • docs/issue.md:补充完整 Issue 档案(表因 / 根因 / 处理方式 / 后续防范 / 同类问题影响),沉淀同类问题的处理范式。

防范与启示

  • 跨模块引用 Vendor 实例字段时,统一通过 BaseVendor 暴露的方法get_name()map_model() 等),避免直接访问派生类未定义的"假属性"。
  • 测试覆盖原则:路由层涉及"内容感知"分支时,至少补一个让分支命中的最小用例,避免守卫掩盖代码缺陷。
  • grep -rn "vendor\.name\b" src/ 全仓扫描,确认误用仅 routes.py 的这两处,已一并消除。/v1/messages 主链路调用的 tier.nameTier 对象的合法 dataclass 属性,不受影响。
  • 长期演进可考虑在 BaseVendor 增加 @property name 指向 get_name(),将契约前移到类型系统由 mypy / pyright 拦截 —— 属"演进式设计"范畴,不在本次最小干预范围内。

验证

  • tests/test_app_routes.py 全部 34 用例通过(含新增 1 例)。
  • grep -rn "vendor\.name\b" src/ 零匹配。

根因: BaseVendor 仅暴露抽象方法 get_name(), 所有 Vendor 子类均无 name 实例属性. routes.py:153/160 错误访问 target_vendor.name 导致 AttributeError 返回 500.

间歇性原因: 仅当 infer_source_vendor_from_body() 从请求体推断出非空 source (含 zhipu 私有产物 srvtoolu_* / server_tool_use) 时才走错误分支, 因此日志中 200/500 共存.

变更要点:
- routes.py 两处 .name 改为 .get_name() 并提取局部变量 target_name 复用, 避免重复方法调用与日志/调用点不一致风险
- 新增回归测试 test_count_tokens_triggers_zhipu_to_target_channel: 注入 server_tool_use + srvtoolu_* 触发 zhipu→anthropic 通道, 断言 200 且 debug 日志含 channel adaptation, 弥补此前 6 个 count_tokens 测试均未触达该分支的盲区
- docs/issue.md 追加档案, 总结根因/防范/同类问题影响

🤖 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 ThreeFish-AI merged commit 71e913f into feature/1.x.x May 11, 2026
6 checks passed
@ThreeFish-AI ThreeFish-AI deleted the ThreeFish-AI/diagnose-backend-log-error branch May 12, 2026 13:24
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.

1 participant