Repository navigation
feat(acp-bridge): session/new 可选 model 锁定会话模型 + setModel 对象形态修复 (0.16 实测) - #52
Conversation
… 实测)
问题 (0.16.1-0.16.9 实测):
1. setModel 透传形态与 0.16 后端 schema 不匹配: 桥发 {sessionId, modelId}
字符串, 后端要 model 对象 → 必报 -32602 "expected object, received
undefined; Unrecognized key: modelId"。即 0.16 时代 setModel 从未真正
可用, ACP client 无法切换会话模型。
2. headless 场景模型解析跟随 personal provider registry / App「上次使用」,
client 无法按会话指定模型: App 里选了什么, spawn 出的引擎就默认用什么
(provider 目录不含 config.json 静态 provider, env 三件套在 personal
registry 存在时被无视)。
修复/新增:
- setModel: 改为 {sessionId, model} 原样透传 ModelSelection 对象
{providerId, modelId, options?: {reasoningLevel}}。旧 modelId 形态移除
(在 0.16 后端从未通过 schema, 保留只会让调用方误以为已生效)。
- session/new 新增可选 model:
* 字符串形态 ("GLM-5.3"): 按 create 快照 settings.model.available 的
catalog 解析, 补 reasoningLevel=reasoning.defaultLevel;
* 对象形态: 原样透传;
* create 成功后经 session/setModel 应用, 失败则 session/new 整体报错。
刻意不透传给 create 本身: 0.16.9 实测 create 的初始 model 形参接受对象
但丢 options, turn 阶段报 ModelProtocolError "Reasoning level is
required for <provider>/<model>"; setModel 路径无此问题。
实测 (ZCode App 3.14.0 / CLI 0.16.9, personal provider bigmodel-api):
- session/new {model: "GLM-5.3"} → session/prompt → stopReason=end_turn;
- setModel {model: {providerId, modelId, options:{reasoningLevel:max}}} →
ok → prompt → end_turn;
- setModel 缺 options (无 reasoning 的模型除外) → turn 失败, 与 catalog
defaultLevel 补齐后通过。
测试: M5 改写 (对象透传/缺参 -32602/旧形态拒绝) + C4 系列 7 例 (字符串
catalog 解析/无 reasoning 条目/对象透传/未知 modelId/setModel 失败连带
new 失败/不带 model 向后兼容/非法形态)。547 passed + 34 subtests。
|
感谢贡献——这是本仓库第一个外部 PR,先说结论:质量很高,修后可合并。以下是维护者侧的独立审查(含活体验证);另有一条来自独立代码审查 agent 的意见见下一评论。 核心声明逐项活体验证(本机 App 3.14.4 / CLI 0.16.9,官方 app-server)
另外确认:PR 与现行 main(cbc35ab)无冲突(MERGEABLE)。 意见应修(合并前):
建议:
与仓库现状的呼应你的问题陈述(「凭证 env 三件套在 personal registry 存在时被无视、模型跟随 App 上次使用」)与我们在 #55 的独立调研结论完全一致——这是同一问题的 app-server 侧视角。你给出的会话级解法(session/new 锁定模型)与 #55 的配置级引导互补,正好补上 ACP 调用方的刚需。 合并顺序:建议本 PR 先行(#53 叠在其上,需要 rebase)。 |
结论: P1(应修)
P2(建议)
P3(提示)
亮点
未发现问题的维度:安全性(ModelSelection 不含凭证,无新增泄漏面);性能(带 model 的 new 多一次 15s 上限 RPC,无退化);并发(桥主循环串行,无新竞态)。 给贡献者的一句话:这是一份质量高于平均线的贡献——问题定位有实测原文、修复方向克制、文档/测试同步意识强。剩余问题集中在错误路径的会话生命周期处置和三处文档面,均为小改;改完即可合并,欢迎继续贡献。 |
在 jasonQin6 PR tizerluo#52 基础上按 R1 审查清单修复, 保持原方案 (setModel 对象 形态 + session/new 可选 model) 不变, 只修审查指出的问题。 A (P1 孤儿会话): - model 形状校验前移到 session/create 之前 — 非法形态本地 -32602 且不 触碰后端, 不再先真实建会话再报错; - create 后失败 (catalog 未命中/不可用、setModel 失败、setModel 超时) 的错误文案带已创建的 sessionId (「已创建会话 <sid> 但 model 未生效: <原因>」) 供客户端收编; 后端无会话删除方法, 不尝试回收; - session_map 注册后移到 model 应用成功之后, 失败会话不登记、不能被 session/prompt 使用。消费方确认: session/prompt/session/cancel 用 .get(acp_sid) 严格取, 扩展 handler 用 .get(acp_sid) or acp_sid 兜底; acp_sid 恒等于 zcode_sid 且登记先于响应返回, 移位对成功路径无行为差异; - _on_session_new docstring 同步失败语义。 B (P3): - 对象形态 providerId/modelId 须为非空字符串 (本地 -32602, 消除透传后端 -32602 被 _passthrough_error 改写成 -32603 的不一致); - catalog 条目/ref/reasoning 加 isinstance 守卫 (畸形条目不抛 AttributeError); - 「catalog 不可用」与「未命中」文案区分; - _passthrough_error 加 prefix 参数 + docstring 注记 session/new 嵌套 setModel 是核心 handler 走本 helper 的例外场景; - _on_session_set_model docstring 改写 (需要按 catalog 解析的调用方应在 session/new 指定; 本方法要求调用方提供完整对象)。 C (文档): - README 版本兼容性加注: setModel 对象形态仅 0.16.x 验证 (0.16.9 实测), ≤0.15 未验证; - README/SKILL 的 session/new 说明: 「补默认 reasoningLevel」条件化为 「catalog 条目带 reasoning.defaultLevel 时补」, 补 model:null 语义; - agent-help key_methods: setModel 参数改对象形态 + 0.16 注记; - docs/recheck-3.14.0.md 追加补遗 (旧形态 -32602 原文/新对象形态实测/ create 快照 catalog 形态/空参探存活的方法论教训)。 D (测试 547→551): - C4/C4a/C4b 补 sessionId 断言; C4d 补 setModel 文案 + 超时分支 + 失败 不登记; C4e 锁定 create 参数逐字段不变; - 新增 C4g (嵌套 setModel -32601 → 「已移除 (session/setModel)」文案)、 C4h (对象缺字段 → 本地 -32602 且不发起后端调用)、C4i (catalog 不可用 文案)、C4j (畸形 catalog 条目不抛); - C4c/C4f 补「不发起 create」「不登记 session_map」断言。 验证: python3 -m unittest discover -s tests → 551 passed; ruff check 干净。
|
感谢这份高质量贡献,也先道个歉:这两个 PR 在仓库里搁了近两周才被我们看到——是我们的问题,不是你的问题;为了不再耽误,我们在你的分支上直接代为修复后合并了(你勾选了「允许维护者修改」,固此路可行)。 处理经过
贡献记录你的提交 你发现的问题(「凭证 env 在 personal registry 存在时被无视、模型跟随 App 上次使用」)与我们同期在 #55 的独立调研结论完全一致——你是从 app-server 侧独立撞出来的,这个视角非常宝贵。欢迎继续贡献;后续 PR 我们不会再让它们晾着了。 |
问题(0.16.1–0.16.9 实测, App 3.14.0/CLI 0.16.9)
1. setModel 透传形态与 0.16 后端 schema 不匹配
桥的
_on_session_set_model发{sessionId, modelId: "<string>"},而 0.16 后端 zod schema 要 model 对象,实测必报:即 0.16 时代 ACP 面的
session/setModel从未真正可用(即使参数名对上,注册表里没有该 provider 也会报Provider Registry 中不存在 Model)。2. client 无法按会话指定模型(headless 刚需)
headless spawn 的 app-server,模型解析跟随 personal provider registry / App「上次使用」默认;config.json 静态 provider 不进注册表,凭证 env 三件套在 personal registry 存在时被无视。结果:App 里选了什么模型,脚本/编辑器拉起的会话就默认用什么,client 无从覆盖。
修复/新增
{sessionId, model}原样透传 ModelSelection 对象{providerId, modelId, options?: {reasoningLevel}}。旧modelId字符串形态移除(在 0.16 后端从未通过 schema,保留只会让调用方误以为已生效)。model:"GLM-5.3"):按 create 快照settings.model.availablecatalog 解析,补reasoningLevel = reasoning.defaultLevel;ModelProtocolError: Reasoning level is required for <provider>/<model>;setModel 路径无此问题。实测(ZCode App 3.14.0 / CLI 0.16.9, personal provider bigmodel-api)
session/new {model: "GLM-5.3"}→session/promptstopReason=end_turn,流式文本正常session/new素建 +session/setModel {model: {providerId, modelId, options:{reasoningLevel:max}}}→ prompt{modelId}形态测试
modelId形态显式拒绝)python3 -m pytest tests/ -q→ 547 passed, 34 subtests passed兼容性说明
{modelId}→{model}是破坏性 ACP 参数变更,但旧形态对 0.16 后端本来就 100% 失败,无实际可用性损失;≤0.15 后端未验证(0.16 用户不受影响,老版本用户如需可反馈再兼容)。model的 session/new 行为逐字节不变(C4e 锁定)。