优化: AI Chat全栈多批审查修复与架构清理(risk_level清理/路由解耦/工具渲染/测试补测/死代码)

This commit is contained in:
2026-06-18 22:57:19 +08:00
parent 0ca5d9805f
commit a2871a66e0
87 changed files with 5720 additions and 3012 deletions

View File

@@ -10,7 +10,7 @@ use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use df_ai::df_ai_core::model::{IntelligenceTier, Modality, ModelConfig};
use df_ai::df_ai_core::model::{Modality, ModelConfig};
use df_ai::provider::{ChatMessage, CompletionRequest, LlmProvider};
// F-01 阶段5: AiNode 路由 — 节点 config.model_id 优先;否则按 TaskRequirements 路由
// (默认 Standard + needs_tool_use=true)。池空/无匹配兜底 record.default_model。
@@ -66,6 +66,27 @@ struct ResolvedProvider {
/// 无任何 provider → 友好错误「未配置 AI Provider」。
///
/// modelconfig["model"] 非空用之,否则 record.default_model再否则 "gpt-4o-mini" 占位。
/// SW-260618-09: provider 三件套 DRY —— resolve_provider + parse_params 合并
/// (AiNode/AiSelfReviewNode execute 逐字重复)。
async fn resolve_and_parse(
db: &Arc<Database>,
config: &serde_json::Value,
inputs: &HashMap<String, NodeOutput>,
) -> anyhow::Result<AiNodeParams> {
let provider_cfg = resolve_provider(db, config).await?;
parse_params(config, inputs, provider_cfg)
}
/// SW-260618-09: build_provider 5 行封装 DRY(AiNode/AiSelfReviewNode execute 逐字重复)。
fn provider_from_params(p: &AiNodeParams) -> Box<dyn LlmProvider> {
df_ai::build_provider(
&p.provider.protocol,
&p.provider.base_url,
&p.provider.api_key,
&p.provider.default_model,
)
}
async fn resolve_provider(
db: &Arc<Database>,
config: &serde_json::Value,
@@ -90,23 +111,16 @@ async fn resolve_provider(
}
// ── 路径 2老明文路径兼容过渡warn ──
let has_plain_base = config.get("base_url").and_then(|v| v.as_str()).is_some();
let has_plain_key = config.get("api_key").and_then(|v| v.as_str()).is_some();
if has_plain_base && has_plain_key {
// base_url / api_key 各解析一次(复用于 has 判定与取值,避免 DRY 重复解析同字段)。
let plain_base = config.get("base_url").and_then(|v| v.as_str());
let plain_key = config.get("api_key").and_then(|v| v.as_str());
if let (Some(base_url_str), Some(api_key_str)) = (plain_base, plain_key) {
tracing::warn!(
"AiNode 明文 api_key/base_url 经 config 注入已废弃, 改用 provider_id (FR-S1). \
老路径将在后续版本移除"
);
let base_url = config
.get("base_url")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let api_key = config
.get("api_key")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let base_url = base_url_str.to_string();
let api_key = api_key_str.to_string();
ensure_resolved_key("(明文注入)", &api_key)
.map_err(anyhow::Error::msg)?;
let protocol = config
@@ -201,14 +215,12 @@ fn parse_params(
let model = if !config_model.is_empty() {
config_model
} else {
// F-01 阶段5: AiNode 默认路由 — Standard + needs_tool_use=true(工作流无人值守 AI 步骤
// F-01 阶段5: AiNode 默认路由 — needs_tool_use=true(工作流无人值守 AI 步骤
// 常含工具调用,如检索/生成;无需工具的节点应在 config 显式指定 model)。
// select_model_id None(池空/无匹配)→ 空串(由 provider impl 回填 default_model)。
let node_req = TaskRequirements {
modalities: vec![Modality::Text],
needs_tool_use: true,
min_intelligence: IntelligenceTier::Standard,
max_cost: None,
estimated_context: 0,
};
select_model_id(&node_req, &provider.model_pool).unwrap_or_default()
@@ -258,15 +270,8 @@ impl Node for AiNode {
tracing::info!("AiNode 执行: node_id={}", ctx.node_id);
// FR-S1 注入链provider 经 df_storage::secret 在 AiNode 内存解析api_key 不进 config。
let provider_cfg = resolve_provider(&self.db, &ctx.config).await?;
let p = parse_params(&ctx.config, &ctx.inputs, provider_cfg)?;
let provider: Box<dyn LlmProvider> = df_ai::build_provider(
&p.provider.protocol,
&p.provider.base_url,
&p.provider.api_key,
&p.provider.default_model,
);
let p = resolve_and_parse(&self.db, &ctx.config, &ctx.inputs).await?;
let provider: Box<dyn LlmProvider> = provider_from_params(&p);
// ── 构建消息 ──
let mut messages = Vec::with_capacity(2);
@@ -343,7 +348,8 @@ impl Node for AiNode {
"base_url": { "type": "string", "description": "(已废弃过渡)明文 API 地址,改用 provider_id" },
"api_key": { "type": "string", "description": "(已废弃过渡)明文 API 密钥,改用 provider_idFR-S1 下经 secret 解析" }
},
"required": ["provider_id"]
// SW-260618-15: prompt/provider_id 均"留空走兜底"(prompt 取上游、provider_id 走默认 provider),与 required 矛盾。改 required=[] 对齐 execute 运行时,防前端按 schema 误拒合法配置。
"required": []
}),
output: serde_json::json!({
"type": "object",
@@ -468,8 +474,7 @@ impl Node for AiSelfReviewNode {
tracing::info!("AiSelfReviewNode 执行: node_id={}", ctx.node_id);
// FR-S1 注入链provider 经 df_storage::secret 在 AiNode 内存解析api_key 不进 config。
let provider_cfg = resolve_provider(&self.db, &ctx.config).await?;
let p = parse_params(&ctx.config, &ctx.inputs, provider_cfg)?;
let p = resolve_and_parse(&self.db, &ctx.config, &ctx.inputs).await?;
// ── 读任务(需求 + 产出) ──
// task_id 必填(自审对象是具体任务的产出);缺失报错而非静默跳过。
@@ -496,12 +501,7 @@ impl Node for AiSelfReviewNode {
let prompt = Self::build_review_prompt(&task.description, &output_text);
let provider: Box<dyn LlmProvider> = df_ai::build_provider(
&p.provider.protocol,
&p.provider.base_url,
&p.provider.api_key,
&p.provider.default_model,
);
let provider: Box<dyn LlmProvider> = provider_from_params(&p);
let request = CompletionRequest {
model: p.model.clone(),