Skip to content

fix(backend): 收敛 LLM finish_reason 契约、修复 redacted_thinking 串写与 Windows 平台缺陷 - #158

Merged
li-xiu-qi merged 2 commits into
devfrom
fix/message-ir-and-windows-compat
Aug 2, 2026
Merged

fix(backend): 收敛 LLM finish_reason 契约、修复 redacted_thinking 串写与 Windows 平台缺陷#158
li-xiu-qi merged 2 commits into
devfrom
fix/message-ir-and-windows-compat

Conversation

@li-xiu-qi

Copy link
Copy Markdown
Collaborator

Closes #156
Closes #157

改动概述

两个独立主题,各一个 commit,互不依赖。

1. 收敛 LLM finish_reason 契约并修复 redacted_thinking 数据串写(#156

finish_reason 统一口径

  • 六值枚举 + raw 逃生舱,OpenAI / Anthropic 各一张映射表
  • 适配器漏归一化时 __post_init__ 降级为 other 并打 warning,避免非法值静默穿透
  • anthropic_clientstop_reason 改为 _finish_reason 状态机,修复 message_stop 硬编码 stop 覆盖 message_delta 已给出的 tool_use,导致工具执行后 ReAct 循环直接终止
  • codex/responses 修正 _saw_tool_call(Responses 在工具场景只报 response.completed),补 response.incompletetruncated 分支
  • 只读批次加 return_exceptions=True,避免单个只读工具抛异常中断整批、留下未闭合的 tool_call 序列

redacted_thinking 修复

redacted_thinkingdata(加密推理内容)原本被写进 reasoning_signature(可读 thinking 的签名),两条失败路径详见 #156。修复方式为新增独立字段 reasoning_redacted_data,回灌函数改为返回块列表让 redacted 独立成块。

2. 修复 Windows 上的路径失真与 SQLite 连接泄漏(#157

  • FileSnapshot.path 等改用 as_posix():这些值既进 API 响应又持久化,存原生分隔符会导致 Windows 建的工作区在其他平台上 key 全不匹配
  • 5 处 with sqlite3.connect(...) 改为显式关闭:该写法只管事务不管连接,Windows 上锁住 .db 文件导致删除报 WinError 32
  • 测试改平台无关写法:newline="\n"sys.executable

验证

在 Windows(win32 / Python 3.12.11)本地跑通:

结果
全量测试(dev 基线 + 本 PR) 1557 passed, 0 failed
ruff format --check app/ 431 files already formatted
ruff check(改动文件) All checks passed

redacted_thinking 与 sqlite 泄漏两处修复均走真实链路验证,并用「临时回滚修复」做反证确认断言具备鉴别力,而非空转通过。

关于测试环境的说明

本仓 vendor/sqlite-vec/**/*.dll 属 gitignored,干净 checkout 缺少该二进制时,test_sqlite_kb_service.pytest_global_workspace_file_routes.py 共 12 例会失败。该现象与本 PR 无关(在不含本 PR 的 upstream/dev 上逐条重现),补齐二进制后全绿。这两个文件目前都在 CI 的 --ignore 列表内。

顺带发现 ensure_vec_extension 会把「扩展文件缺失」误报为「平台不支持」,已记录在 #157 末尾,未在本 PR 修改,避免扩大范围。

审阅提示

统一 finish_reason 口径:
- 六值枚举 + raw 逃生舱,OpenAI / Anthropic 各一张映射表;适配器漏归一化时
  __post_init__ 降级为 other 并打 warning
- anthropic_client 的 stop_reason 改为 _finish_reason 状态机,修复 message_stop
  硬编码 stop 覆盖 message_delta 已给出的 tool_use,导致工具执行后 ReAct 循环
  直接终止
- codex/responses 修正 _saw_tool_call(Responses 在工具场景只报
  response.completed),补 response.incomplete -> truncated 分支
- 只读批次加 return_exceptions=True 与 _finish_tool_execution_safe,避免单个
  只读工具抛异常中断整批、留下未闭合的 tool_call 序列

修复 redacted_thinking 数据串写:
- redacted 块的 data 原本被写进 reasoning_signature,造成两条真实失败路径:
  仅含 redacted 的轮次里加密块被整体丢弃;同时含 thinking 与 redacted 的轮次里
  data 覆盖真正的 signature_delta,发给 Claude 的是加密数据而非签名
- 新增 LlmDelta.reasoning_redacted_data 承载加密块,
  _build_anthropic_thinking_blocks 改为返回块列表让 redacted 独立成块,
  session_stream 四处回灌点与 reasoning 分支同级而非嵌套

测试:
- 17 处夹具改用 FinishReason 合法值(护栏抓出假 client 喂的是 provider 原始值
  "stop"/"length",被降级成 other 后令续写分支静默失效)
- 新增 message IR 回归测试,覆盖 thinking 签名三态与只读批次异常序列闭合
路径持久化失真:
- FileSnapshot.path 与竞赛 skill 的 created_files / created_dirs / project_dir
  改用 as_posix()。这些值既进 API 响应又持久化到 file_snapshots.json,存平台
  原生分隔符会导致 Windows 建的工作区在其他平台上 key 全不匹配、文件被误判为
  已变更

SQLite 连接泄漏(Windows 上锁住 .db 文件,删除报 WinError 32):
- with sqlite3.connect(...) 只管事务不管连接,退出 with 后连接仍打开;Linux
  允许 unlink 已打开文件所以长期无感,Windows 会直接失败
- sqlite_graph_store 的 list 路径改显式 try/finally 关闭并补 commit
- workspaces_resources_files 的 _create_graph_db_at_path /
  _write_knowledge_db_metadata、file_initializer 的
  _write_knowledge_db_metadata / _create_graph_db_tables 四处改用
  contextlib.closing(这四处原本就有显式 commit,异常路径未提交等价于回滚)
- 删除知识图谱改为先释放缓存 service 再 unlink,并按规范走 as_system_path()

测试改为平台无关写法:
- write_text 显式 newline="\n",避免 Windows 上被转成 CRLF
- acp_command 硬编码 python3 改 sys.executable
@li-xiu-qi
li-xiu-qi merged commit d85c4f7 into dev Aug 2, 2026
1 check passed
@li-xiu-qi
li-xiu-qi deleted the fix/message-ir-and-windows-compat branch August 2, 2026 18:51
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