重构: 后端 df-ai/commands 拆分+df-nodes/workflow 改造+P0 bug 修复
- df-ai: context 历史中毒三档自愈 sanitize_messages(AC3)+anthropic_compat tool_use_id None 跳过(AC1/AC2)+删 router/stream 死码
- df-core: events 加 select_type+decisions 多选审批契约(F-260615-01)
- df-execute: shell run_command 工具复用(F-260615-05)
- df-nodes: human_node 多选校验+2 端到端测(F-01)+取消跳 set_failed(B-03b-R1/R2/R8)
- df-workflow: executor/dag/state cancel 闭环(B-06/07/03a/b)+provider approve options(R-PD-5)
- df-storage: find_path_conflict 抽公共(R-PD-11)+COLS 常量断言
- df-ideas: 删 IdeaPromoter/PromotionPolicy 死码(R-PD-14)
- src-tauri/commands/ai: secret keyring 迁移(FR-S1/R-PD-4)+GeneratingGuard RAII+disarm(B-09/26)+newConversation 软复位(B-10)+stream 心跳/stop select/空回复判错(B-02/04/05/15)+run_command(F-05)+mask audit(AR-3)
- src-tauri/commands/{project,task,workflow,mod,lib,state}: task detail IPC(F-02)+approve decisions+task list 联动(B-29)
- Cargo.lock+Cargo.toml 依赖同步
This commit is contained in:
@@ -5,7 +5,6 @@ use std::path::Path;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::State;
|
||||
|
||||
use df_ai::build_provider;
|
||||
use df_ai::provider::{ChatMessage, CompletionRequest};
|
||||
use df_core::types::new_id;
|
||||
use df_project::scan::{collect_sample, detect_stack, extract_description, normalize_path};
|
||||
@@ -246,17 +245,20 @@ pub async fn purge_project(state: State<'_, AppState>, id: String) -> Result<boo
|
||||
// ============================================================
|
||||
|
||||
/// 查找已绑定该目录的项目(排除 exclude_id 自身)。无冲突返回 None。
|
||||
///
|
||||
/// 委托 `ProjectRepo::find_path_conflict`(DRY R-PD-11:与 tool_registry::bind_dir_to_project
|
||||
/// 共用同一防重复绑定实现)。normalize_path 内含 canonicalize,防 `C:\a\b` vs `C:/a/b/` 绕过。
|
||||
async fn find_binding_conflict(
|
||||
state: &AppState,
|
||||
path: &str,
|
||||
exclude_id: Option<&str>,
|
||||
) -> Result<Option<ProjectRecord>, String> {
|
||||
let norm = normalize_path(path);
|
||||
let projects = state.projects.list_active().await.map_err(|e| e.to_string())?;
|
||||
Ok(projects.into_iter().find(|p| {
|
||||
let excluded = exclude_id.is_some_and(|eid| p.id == eid);
|
||||
!excluded && p.path.as_ref().is_some_and(|pp| normalize_path(pp) == norm)
|
||||
}))
|
||||
state
|
||||
.projects
|
||||
.find_path_conflict(&norm, exclude_id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// 探测目录技术栈(前端选目录后实时预览)
|
||||
@@ -375,7 +377,18 @@ pub async fn scan_project_with_ai(
|
||||
.ok_or_else(|| "未配置 AI 提供商,请先在设置中添加".to_string())?;
|
||||
|
||||
// 3. 构造 provider + LLM 调用(非流式)
|
||||
let provider = build_provider(&pc.provider_type, &pc.base_url, &pc.api_key, &pc.default_model);
|
||||
// build_provider_for 含空 key 早失败:Err → 走纯规则降级(与下方 LLM 失败降级行为一致)
|
||||
let provider = match crate::commands::ai::secret::build_provider_for(&pc) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
return Ok(AiScanResult {
|
||||
description: String::new(),
|
||||
stack: rule_stack,
|
||||
project_type: None,
|
||||
raw: Some(format!("LLM 调用失败: provider 密钥不可用: {e}")),
|
||||
});
|
||||
}
|
||||
};
|
||||
let request = CompletionRequest {
|
||||
model: pc.default_model.clone(),
|
||||
messages: build_scan_prompt(&sample, &rule_stack),
|
||||
|
||||
Reference in New Issue
Block a user