优化: AI Chat全栈多批审查修复与架构清理(risk_level清理/路由解耦/工具渲染/测试补测/死代码)
This commit is contained in:
@@ -6,10 +6,11 @@ use tauri::{AppHandle, Emitter};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use df_ai::provider::{ChatMessage, CompletionRequest, LlmProvider, MessageRole};
|
||||
// F-01 阶段5: 标题生成路由 — TaskRequirements(Lite + Low 成本,无需工具)。
|
||||
// 标题是短文本摘要任务,选便宜 Lite 模型;池空/无匹配兜底 default_model(行为不变)。
|
||||
// F-01 阶段5: 标题生成路由 — TaskRequirements(无需工具)。
|
||||
// 标题是短文本摘要任务;池空/无匹配兜底 default_model(行为不变)。
|
||||
// 注:路由已解耦(B-260618-03),原 Lite/Low 智力成本约束已删除,纯 weight 选模型。
|
||||
use df_ai::router::{
|
||||
select_model_id, CostTier, IntelligenceTier, Modality, TaskRequirements,
|
||||
select_model_id, Modality, TaskRequirements,
|
||||
};
|
||||
use df_storage::crud::AiConversationRepo;
|
||||
use df_storage::db::Database;
|
||||
@@ -56,6 +57,7 @@ pub(crate) async fn ensure_conversation_title(
|
||||
model: None,
|
||||
status: None,
|
||||
reasoning_content: m.reasoning_content.clone(),
|
||||
timestamp: m.timestamp,
|
||||
})
|
||||
.collect();
|
||||
(summary, session.messages.all_messages_clone())
|
||||
@@ -64,35 +66,55 @@ pub(crate) async fn ensure_conversation_title(
|
||||
return;
|
||||
}
|
||||
|
||||
// B-260617-17 修复:进入即 extract_title 兜底落库 + emit,保证侧栏即时有非"新对话"标题。
|
||||
// 原实现仅在 LLM 返回 None(失败)或 provider 构建失败时才落 extract——但 LLM 卡住
|
||||
// (generate_title_via_llm 的 llm_concurrency 信号量 acquire 阻塞 / 网络挂起 / 后台
|
||||
// spawn 未跑完即刷新)时,既不返回 None 也不落库 → 标题长期停留"新对话"。现先落 extract
|
||||
// 兜底,下方 LLM 生成成功后覆盖;LLM 卡住/失败/超时则保留兜底,无论如何不再"新对话"。
|
||||
let fallback_title = extract_title(&all_msgs).unwrap_or_else(|| "新对话".to_string());
|
||||
if let Err(e) = conv_repo.update_field(conv_id, "title", &fallback_title).await {
|
||||
tracing::error!("对话标题兜底落库失败(conv_id={}, title={}): {}", conv_id, fallback_title, e);
|
||||
}
|
||||
let _ = app_handle.emit("ai-conversation-changed", ());
|
||||
|
||||
// 标题生成是独立一次 LLM 调用,自建 provider(便于后台 spawn,不借主 loop 的 &dyn LlmProvider)
|
||||
// build_provider_for 含空 key 早失败:Err(如 keyring 读不到)→ 跳过 LLM,extract_title 兜底
|
||||
let provider: Box<dyn LlmProvider> = match super::secret::build_provider_for(provider_config) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
tracing::warn!("标题生成跳过(provider 密钥不可用,走 extract_title 兜底): {}", e);
|
||||
let title = extract_title(&all_msgs).unwrap_or_else(|| "新对话".to_string());
|
||||
let _ = conv_repo.update_field(conv_id, "title", &title).await;
|
||||
let _ = app_handle.emit("ai-conversation-changed", ());
|
||||
// 兜底已落库 + emit,LLM 跳过(密钥不可用等)
|
||||
tracing::warn!("标题 LLM 生成跳过(provider 密钥不可用,保留 extract_title 兜底): {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
// F-01 阶段5: 标题生成路由 — 选便宜 Lite 模型(Low 成本上限,无工具)。
|
||||
// select_model_id None(池空/无匹配)→ 兜底 default_model。
|
||||
// F-01 阶段5: 标题生成路由 — select_model_id None(池空/无匹配)→ 兜底 default_model。
|
||||
let title_req = TaskRequirements {
|
||||
modalities: vec![Modality::Text],
|
||||
needs_tool_use: false,
|
||||
min_intelligence: IntelligenceTier::Lite,
|
||||
max_cost: Some(CostTier::Low),
|
||||
estimated_context: 0,
|
||||
};
|
||||
let title_model = select_model_id(&title_req, &provider_config.model_configs)
|
||||
.unwrap_or_else(|| provider_config.default_model.clone());
|
||||
let title = match generate_title_via_llm(&*provider, &title_model, summary_msgs, &llm_concurrency).await {
|
||||
Some(t) => t,
|
||||
None => extract_title(&all_msgs).unwrap_or_else(|| "新对话".to_string()),
|
||||
// B-260617-17: LLM 调用包 20s 超时,防信号量/网络挂起致后台 task 永久堆积;
|
||||
// 超时或返回 None 均保留上方已落的 extract 兜底,不覆盖。
|
||||
let title = match tokio::time::timeout(
|
||||
std::time::Duration::from_secs(20),
|
||||
generate_title_via_llm(&*provider, &title_model, summary_msgs, &llm_concurrency),
|
||||
).await {
|
||||
Ok(Some(t)) => t,
|
||||
Ok(None) => {
|
||||
tracing::warn!("标题 LLM 生成返回空,保留 extract_title 兜底(conv_id={})", conv_id);
|
||||
return;
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::warn!("标题 LLM 生成超时(20s),保留 extract_title 兜底(conv_id={})", conv_id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let _ = conv_repo.update_field(conv_id, "title", &title).await;
|
||||
// LLM 生成成功 → 覆盖 extract 兜底
|
||||
if let Err(e) = conv_repo.update_field(conv_id, "title", &title).await {
|
||||
tracing::error!("对话标题 LLM 落库失败(conv_id={}, title={}): {}", conv_id, title, e);
|
||||
}
|
||||
let _ = app_handle.emit("ai-conversation-changed", ());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user