diff --git a/docs/todo.md b/docs/todo.md index 61e02d4..c9e3972 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -462,11 +462,11 @@ ### P1 — 设计完成待实施 -- [ ] F-260614-01 — **[P1🔥]** 模型能力系统 Phase 1 — ModelConfig 数据模型(模态/能力/价格/智力 4 维度) + ModelRouter 智能路由 + 多源探测(预设表+启发式) + 厂商模型列表自动拉取(适配 OpenAI/Anthropic 协议差异) + 7 调用点接入 + Settings 模型池 UI + AiChat 模型下拉。按任务需求自动匹配合适模型,不再所有场景共用 default_model — source:📐 设计定稿 (06-16, [F-01-模型能力系统与智能路由设计-2026-06-16.md](./02-架构设计/F-01-模型能力系统与智能路由设计-2026-06-16.md))。F-07 已完成解除阻塞。 - - [ ] 阶段1:数据模型 — df-ai-core 新增 ModelConfig/Modality/Capability/CostTier/IntelligenceTier + df-storage models 反序列化兼容 - - [ ] 阶段2:预设表+探测器 — df-ai 新建 model_probe.rs + presets/models.json + 启发式推断 + 多源合并 - - [ ] 阶段3:厂商模型列表拉取 — df-ai 新建 model_fetch.rs + openai_compat/anthropic_compat 分派 + URL 拼接 + 噪音过滤 - - [ ] 阶段4:路由器 — df-ai 新建 router.rs + TaskRequirements + select 逻辑 +- [x] ✅ F-260614-01 — **[P1🔥]** 模型能力系统 Phase 1(阶段1-6 全✅,波1-5,ModelConfig 4维度+路由器+多源探测+厂商拉取+7调用点+Settings池UI+AiChat下拉) — ModelConfig 数据模型(模态/能力/价格/智力 4 维度) + ModelRouter 智能路由 + 多源探测(预设表+启发式) + 厂商模型列表自动拉取(适配 OpenAI/Anthropic 协议差异) + 7 调用点接入 + Settings 模型池 UI + AiChat 模型下拉。按任务需求自动匹配合适模型,不再所有场景共用 default_model — source:📐 设计定稿 (06-16, [F-01-模型能力系统与智能路由设计-2026-06-16.md](./02-架构设计/F-01-模型能力系统与智能路由设计-2026-06-16.md))。F-07 已完成解除阻塞。 + - [x] ✅(波1) 阶段1:数据模型 — df-ai-core 新增 ModelConfig/Modality/Capability/CostTier/IntelligenceTier + df-storage models 反序列化兼容 + - [x] ✅(波1) 阶段2:预设表+探测器 — df-ai 新建 model_probe.rs + presets/models.json + 启发式推断 + 多源合并 + - [x] ✅(波1) 阶段3:厂商模型列表拉取 — df-ai 新建 model_fetch.rs + openai_compat/anthropic_compat 分派 + URL 拼接 + 噪音过滤 + - [x] ✅(波2) 阶段4:路由器 — df-ai 新建 router.rs + TaskRequirements + select 逻辑 - [x] ✅(波3 IPC a26b86e + 波4调用点 2ddfea6) 阶段5:6 调用点接入 — agentic/title/knowledge_inject(2)/project(2)/df-ideas/df-nodes + IPC ai_fetch_models/ai_probe_model + router.rs select_model_id helper(池空兜底 default_model 行为不变) - [x] ✅(波5·4c84fbc+主代核查,cargo0err+vue-tsc0+df-ai107pass+compress3pass) 阶段6:前端 — types.ts 类型对齐(✅波3) + Settings 模型池 UI(✅波3) + AiChat 模型下拉+override穿透(✅波5·4c84fbc,override空/不在池落回路由零行为变化) diff --git a/docs/待审查.md b/docs/待审查.md index 65a289b..7487b10 100644 --- a/docs/待审查.md +++ b/docs/待审查.md @@ -290,23 +290,37 @@ - **⚪ CR-48-2(low,build_provider/诊断日志仍用 default_model)**:provider trait 不暴露改 default_model,CompletionRequest.model 走路由 provider impl 适配(OpenAICompatProvider::convert_request:295-296 req.model.is_empty() 回退 default_model),诊断日志仍用 default_model 字段名,非阻断。 - **待修项回流 todo**: **无**(2 low 非阻断:Vision 检测待多模态接入/诊断日志命名,路由核心功能全 PASS)。 -### CR-260616-49 波5 F-01阶段6 AiChat模型下拉+override穿透(4c84fbc) / F-15阶段1剩余compress基础(63bff8b) — 🟡 待审 +### CR-260616-49 波5 F-01阶段6 AiChat模型下拉+override穿透(4c84fbc) / F-15阶段1剩余compress基础(63bff8b) — ✅ 已审(PASS) -- **范围**: 2 agent 并行 workflow wcbzmaqty,文件域隔离(A 独占 commands.rs/agentic.rs/AiChat.vue/useAiSend.ts/api/ai.ts/i18n×2;B 独占 prompt.rs/context.rs/compress.rs)。**mod.rs 双方碰**:A 加 `model_override: Option` 字段(L230)+ new()初值 None(L246);B 加 `pub mod compress;`(L27)+ 模块注释(L14)。位置不冲突,git 干净合并。 -- **commit**: 63bff8b(F-15 compress 基础)+ 4c84fbc(F-01 阶段6)。拆分约束:F-15 commit 必须含 mod.rs 全部(`pub mod compress` 须与 compress.rs 同 commit;model_override 字段 pub 无害不报 dead),F-01 commit 跟后(agentic.rs/commands.rs 用字段)。两 commit 各自 tree 可编译。 -- **主代核查(2026-06-17)**: cargo check --workspace EXIT 0(6 warning 全 pre-existing dead_code,PendingApproval/releases/node_executions)/ npx vue-tsc --noEmit EXIT 0 / cargo test -p df-ai **107 passed**(含 context 9 新方法 + 8 新单测) / cargo test -p devflow --lib compress **3 passed** / grep 核验源码形态(override 兜底 agentic.rs:181-188 + compress select_model_id 兜底 compress.rs:66-67 + context 幂等 context.rs:629-640)。 -- **审查 agent 待复审重点**: - - ①**F-01 override 穿透兜底零行为变化**:agentic.rs:181-188 `match model_override.as_deref()` — Some(id) 且 !id.is_empty() 且 `provider_config.model_configs.iter().any(|m| m.model_id == id)` → 用 id;`_ => resolved_model`(落回路由)。override=None(自动模式默认)完全等价改动前。create/switchConversation 清 `session.model_override`(commands.rs:962/1040)防跨对话污染,前端 watch activeConversationId 同步清(useAiSend.ts modelOverride ref / AiChat.vue:1021)。 - - ②**F-01 空池降级**:AiChat.vue `enabledModels` 空 → 下拉 v-if 隐藏 + toggle :disabled + setSpecifyMode 早退强制自动模式。空 provider 不 crash。 - - ③**F-01 前端 ref 单例**:modelOverride 模块级 ref(useAiSend.ts:53)非 per-conversation,靠 watch 清理对齐后端 switch 清 session.model_override。核验切对话时机无残留。 - - ④**F-01 i18n message 值无字面 `@`/`/`/裸`{}`**(vue-i18n prod runtime 报错字符):aiChat.ts 新 6 key(autoMode/specifyMode/autoModeHint/specifyModeHint/selectModel/noModels)手动 grep 核验。 - - ⑤**F-15 compress 幂等 + 单向不可逆**:compress_old_messages(context.rs:629-640)只处理 `is_active`,saturating_sub 扣 token,已 compressed 跳过;返回 newly_compressed 仅本次新标记。DB 原始消息保留,is_active 白名单(sanitize step0,波3已就绪)隔离。8 单测覆盖(标记+返回/幂等二次空/越界 clamp/跳过 inactive/has_compressible/flag round-trip+clear 复位/insert_at 计 token/build_eviction 三元组原子)。 - - ⑥**F-15 compress_via_llm LLM 失败返 Err**:compress.rs:89 map_err + :92 空摘要 Err。阶段2 IPC 接调用点后降级(不阻塞)。`#[allow(dead_code)]` 占位阶段1(compress.rs:99 prompt.rs)。 - - ⑦**F-15 纯函数优先**:compress_prompt(prompt.rs:148 无 IO)/ build_eviction_units/has_compressible_messages/compress_old_messages 无 IO;compress_via_llm 封装 IO(双层 Semaphore 限流,复用 generate_title_via_llm 模式 + select_model_id Standard/Medium 兜底 default_model)。 - - ⑧**F-15 零行为变化**:push/restore_from_messages/build_for_request/sanitize_messages 未改,仅新增方法 + is_compressing 字段(new/clear 初值 false)。 - - **待修项回流 todo**: **无**(本批为基础暴露 + UI 穿透,核心逻辑 grep+test 核验全 PASS,无阻断。阶段2 IPC 接入后另行登记)。 +- **复审结论(2026-06-17·CR-49 agent 亲跑 cargo+test+vue-tsc+独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪1 + - **验证**: `cargo check --workspace` EXIT 0(6 warning 全 pre-existing dead_code,PendingApproval/releases/node_executions)/ `npx vue-tsc --noEmit` EXIT 0/ `cargo test -p df-ai` **107 passed**(含 context 9 新方法 + 8 新单测)/ `cargo test -p devflow --lib compress` **3 passed**。 + - **①F-01 override 兜底零行为变化 PASS**:agentic.rs:181-188 `match model_override.as_deref()` — Some(id) 且 !id.is_empty() 且 `provider_config.model_configs.iter().any(|m| m.model_id == id)` → 用 id;`_ => resolved_model`(落回路由)。override=None(自动模式默认)完全等价改动前。非空但不在池→落回路由兜底正确,防止 override 导致无模型。 + - **②F-01 清零路径完整 PASS**:create(commands.rs:1147 `session.model_override = None`)+ switch(commands.rs:1225)两入口清零,前端 watch activeConversationId 同步清(AiChat.vue:1071)。防跨对话污染,零残留。 + - **③F-01 续跑透传 PASS**:try_continue_agent_loop(agentic.rs:699-702)读 session.model_override.clone() 透传,审批续跑/达 max 续跑保持同一主对话的 override 一致,语义正确。 + - **④F-01 空池降级 PASS**:AiChat.vue:237 `v-if="enabledModels.length"` 隐藏下拉+ :231 toggle :disabled + :1323 setSpecifyMode 早返。空 provider 不 crash。 + - **⑤F-01 i18n 特殊字符 PASS**:aiChat.ts 新 6 key(autoMode/specifyMode/autoModeHint/specifyModeHint/selectModel/noModels)纯文本,无 @/|/裸{} 特殊字符。 + - **⑥F-15 compress 幂等 PASS**:context.rs:629-640 compress_old_messages 只处理 is_active,saturating_sub 扣 token,已 compressed 跳过;返回 newly_compressed 仅本次新标记。DB 原始消息保留,is_active 白名单隔离。单测 push_token_only_active:954 覆盖直接 push+restore 双路径。 + - **⑦F-15 compress_via_llm 兜底 PASS**:compress.rs:66-67 select_model_id 池空/无匹配→unwrap_or_else 兜底 default_model;:89 map_err + :92 空摘要 Err。阶段2 IPC 接调用点后降级。 + - **⑧F-15 纯函数优先 PASS**:compress_prompt(prompt.rs:148 无 IO)/ build_eviction_units/has_compressible_messages/compress_old_messages 无 IO;compress_via_llm 封装 IO(双层 Semaphore 限流复用 title 模式)。单测 3 passed。 + - **⑨F-15 零行为变化 PASS**:push/restore_from_messages/build_for_request/sanitize_messages 未改,仅新增方法 + is_compressing 字段(new/clear 初值 false)。 + - **⚪ CR-49-1(low,前端 ref 单例设计说明)**:modelOverride 模块级 ref(useAiSend.ts:53)非 per-conversation,靠 watch 清理对齐后端 switch 清 session.model_override。当前单实例 AiChat 可接受,多窗口架构未来若需独立模型选择需改 per-conversation ref。非阻断(设计文档已标注)。 + - **待修项回流 todo**: **无**(9 重点全 PASS,1 low 设计说明非阻断)。 -### CR-260616-50 波6 F-15阶段2 手动上下文管理(2 IPC+3事件+前端按钮+status渲染)(4194842) — 🟡 待审 +### CR-260616-50 波6 F-15阶段2 手动上下文管理(2 IPC+3事件+前端按钮+status渲染)(4194842) — ✅ 已审(🔴 RED 已修 PASS) + +- **复审结论(2026-06-17·波7 ww68uamzu agent CR-50)**: ⚠ **ISSUES → 🔴 RED 已修(主代)** — 后端 IPC/事件/保护区/失败路径全 PASS,前端事件分发 RED(审查抓到真 bug,主代即时修) + - **验证**: `cargo check --workspace` EXIT 0(6 warning 全 pre-existing dead_code)/ `npx vue-tsc --noEmit` EXIT 0(零输出)/ `cargo test -p devflow --lib` **79 passed**(无回归)。 + - **① clear_context 不删 DB PASS**:commands.rs:430-433 经 messages_mut() 标 status="archived_segment"(is_active 自动 false,sanitize 隔离),落库走 save_conversation 全量 upsert(:439),非 clear_messages 删库。 + - **② compress LLM 失败不阻塞 PASS**:commands.rs:543-554 先取 active 克隆(读不改 status)→ compress_via_llm 失败则 set_compressing(false)+ emit AiError(message 不含 api_key)+ 返 Err;消息状态不变(未调 compress_old_messages)。成功路径才 compress_old_messages 标 compressed + insert_at(0, summary system)。 + - **③ 保护区最近 N 条 active 不动 PASS**:PROTECT_COUNT=6(commands.rs:412/465 两处一致),protect_start = len.saturating_sub(6),clear/compress 只对 [0, protect_start) 操作。 + - **④ 空会话/无 active 降级 PASS**:protect_start==0 → emit 对应事件 + Ok(不报错,commands.rs:416-420/485-492);active_msgs 空 → AiCompressed(summary 空,commands.rs:511-515) + Ok。 + - **⑤ 3 事件变体 serde — 🔴 RED(审查抓到真 bug)**:mod.rs AiChatEvent 仅 `#[serde(tag="type")]` 无 `rename_all="snake_case"`,serde 默认序列化 PascalCase(useAiEvents.ts 10 case 铁证)。useAiContext.ts:77/80/84/87 switch case 用 snake_case(`'ai_compressing'` 等)→ 永不命中 → isCompressing 永不复位 → 压缩按钮用过一次永久禁用 + 清空后视图不刷新。**主代修复(2026-06-17)**:case 4 处改 PascalCase(AiCompressing/AiCompressed/AiContextCleared/AiError)+ compressContext 成功兜底复位(后端同步 resolve 非异步)+ doc 注释一致性。vue-tsc EXIT 0 + grep 无残留 snake_case + useAiEvents 对照铁证。未改后端(rename_all 会破坏 useAiEvents 既有 10 case)。 + - **⑥ useAiContext composable 闭环 PASS(RED 修后)**:isCompressing ref 防重入 + 第二 listener 消费 4 事件 PascalCase 分发 + compressContext 成功兜底复位(后端 IPC 同步等 LLM 完成才 resolve 非异步 spawn,成功即复位防事件丢失)。 + - **⑦ status 分组渲染 PASS**:AiChat.vue:2240 messageSegments computed 按 status 分组(archived_segment/compressed 连续合并),:2290 renderItems 扁平化供 v-for;折叠段点击 toggleSegment 展开,正常消息零改动。 + - **⑧ i18n 13 key ×2 无特殊字符 PASS**:aiChat.ts 新 key(clearContext/compressContext/clearContextConfirm/contextArchived/compressed/compressing/clearSuccess/compressSuccess/compressError/clearError/expand/collapse/compressedSummaryLabel)仅合法 {n}/{msg} 插值,无 @/|/裸 {}。 + - **⑨ useAiConversations status 透传 PASS**:useAiConversations.ts:86-90 switchConversation mapper 透传 status 字段(archived_segment/compressed 落 AiMessage 供渲染),types.ts 未含此字段经 as any 读写闭环。 + - **⑩ 已知取舍(history_tokens 口径) PASS**:commands.rs:423-429 注释明确标注口径——clear_context 改 status 后 history_tokens 不立即扣减(私有字段无 setter),后果短暂偏高触发更早裁剪,属保守方向不劣化安全性。 + - **待修项回流 todo**: **无**(🔴 RED 已即时修,主代核验 case PascalCase + vue-tsc EXIT 0 + useAiEvents 10 case 铁证对照)。 - **范围**: 2 agent 并行 workflow w6o61cbqn,文件域隔离(后端独占 commands.rs/mod.rs/lib.rs;前端独占 api/ai.ts/useAiContext.ts(新)/useAiConversations.ts/stores/ai.ts/AiChat.vue/i18n×2)。零重叠。 - **commit**: 4194842(单 commit,功能完整:后端 IPC 无前端无意义,配套提交)。 @@ -322,7 +336,6 @@ - ⑧**i18n 13 key ×2 禁字符**:aiChat.ts 新 key(clearContext/compressContext/clearContextConfirm/contextArchived/compressed/compressing/clearSuccess/compressSuccess/compressError/clearError/expand/collapse/compressedSummaryLabel)message 值无字面 `@`/`/`/裸`{}`(仅合法 `{n}`/`{msg}` 插值)。注意 archived 与侧栏冲突 → 重命名 contextArchived(vue-tsc TS1117 已修)。 - ⑨**useAiConversations status 透传**:switchConversation mapper 透传 status 字段(archived_segment/compressed 落 AiMessage 供渲染折叠),types.ts 未含此字段经 cast 读写闭环。 - **⚠️ 已知设计取舍(backend 自报,记审查项)**:clear_context 经 messages_mut() 改 status 后,ContextManager.history_tokens(私有字段无 setter)不立即扣减 archived 段 token(与 compress_old_messages 路径不同,compress 内部扣 token)。后果:history_tokens 短暂偏高,build_for_request 超预算裁剪可能比实际更早触发,属保守方向(不超发)不劣化安全性。代码注释已标注口径。 - - **待修项回流 todo**: **无**(功能完整,核心逻辑 grep+test+构建全 PASS;history_tokens 口径为已知取舍非 bug,阶段3 自动压缩如需可补 history_tokens setter)。 ---