From f7d078109074f0c86890c2a00a0308beb53cf032 Mon Sep 17 00:00:00 2001 From: wunianxiao Date: Wed, 10 Jun 2026 10:17:25 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E4=BF=AE=E5=A4=8D=20clippy=20?= =?UTF-8?q?=E9=94=99=E8=AF=AF=EF=BC=88loop=20never=20loops=20=E2=86=92=20i?= =?UTF-8?q?f-let=EF=BC=89=E5=B9=B6=E5=BA=94=E7=94=A8=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/context/builder.rs | 5 ++-- src/daemon.rs | 2 +- src/llm/conversation.rs | 7 +++--- src/llm/deepseek.rs | 5 ++-- src/llm/provider.rs | 28 ++++++++++------------- src/logger.rs | 5 ++-- src/main.rs | 35 +++++++++++----------------- src/queue/message_queue.rs | 7 +++--- src/tools/approval.rs | 5 ++-- src/tools/builtins/fetch_page.rs | 22 ++++++++---------- src/tools/builtins/weather.rs | 6 ++--- src/tools/builtins/web_search.rs | 5 ++-- src/tools/mod.rs | 7 +++--- src/wechat/types.rs | 39 ++------------------------------ src/worker.rs | 5 ++-- 15 files changed, 60 insertions(+), 123 deletions(-) diff --git a/src/context/builder.rs b/src/context/builder.rs index 7e50f3f..120a9cb 100644 --- a/src/context/builder.rs +++ b/src/context/builder.rs @@ -243,8 +243,8 @@ pub async fn trigger_idle_summary( /// 生成摘要:优先使用 LLM,不可用时回退到简单截断 async fn generate_summary(messages: &[Message], summarizer: Option<&Summarizer>) -> String { - if let Some(summarizer) = summarizer { - if messages.len() >= 3 { + if let Some(summarizer) = summarizer + && messages.len() >= 3 { let prompt = build_summary_prompt(messages); match summarizer(prompt).await { Ok(text) if !text.is_empty() => { @@ -254,7 +254,6 @@ async fn generate_summary(messages: &[Message], summarizer: Option<&Summarizer>) _ => tracing::warn!("LLM 摘要失败,回退到简单截断"), } } - } summarize_messages(messages) } diff --git a/src/daemon.rs b/src/daemon.rs index 32eb926..e02d0d7 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -363,7 +363,7 @@ async fn llm_consumer_loop( let session = session.clone(); let n = name.to_string(); let args = args_json.to_string(); - let is_approved = approved_tool.as_ref().map_or(false, |t| t == &n); + let is_approved = approved_tool.as_ref() == Some(&n); Box::pin(async move { let ch = ChannelId::wechat(&uid); diff --git a/src/llm/conversation.rs b/src/llm/conversation.rs index be04649..10bf720 100644 --- a/src/llm/conversation.rs +++ b/src/llm/conversation.rs @@ -291,8 +291,8 @@ impl Conversation { } } - if let Some(calls) = tool_calls { - if !calls.is_empty() { + if let Some(calls) = tool_calls + && !calls.is_empty() { used_tools = true; tracing::info!( "🔧 LLM 请求 {} 个工具: {}", @@ -347,7 +347,6 @@ impl Conversation { } continue; } - } // 无工具调用:记录回复,检查是否需要溢出摘要 if !full_text.is_empty() { @@ -358,7 +357,7 @@ impl Conversation { { let s = self.session.lock().await; let recent = s.recent_messages(); - let estimated: u32 = recent.iter().map(|m| builder::estimate_tokens(m)).sum(); + let estimated: u32 = recent.iter().map(builder::estimate_tokens).sum(); if estimated > s.token_budget { drop(s); builder::trigger_overflow_summary(&self.session, Some(&self.summarizer())) diff --git a/src/llm/deepseek.rs b/src/llm/deepseek.rs index 3ea61ca..c1fcd22 100644 --- a/src/llm/deepseek.rs +++ b/src/llm/deepseek.rs @@ -215,8 +215,8 @@ async fn stream_deepseek( } // 检查最后的 buffer - if !buf.trim().is_empty() { - if let Some(parsed) = parse_chat_chunk(buf.trim()) { + if !buf.trim().is_empty() + && let Some(parsed) = parse_chat_chunk(buf.trim()) { match parsed { ParsedChunk::Text(t) => full_text.push_str(&t), ParsedChunk::ToolCallDelta { @@ -243,7 +243,6 @@ async fn stream_deepseek( _ => {} } } - } // 构建 tool_calls let tool_calls: Option> = if tool_call_builders.is_empty() { diff --git a/src/llm/provider.rs b/src/llm/provider.rs index ddffd1f..6bb3737 100644 --- a/src/llm/provider.rs +++ b/src/llm/provider.rs @@ -159,16 +159,16 @@ pub(crate) fn parse_chat_chunk(line: &str) -> Option { }; // 提取 usage(可能在最后一个 chunk) - if let Some(ref usage) = parsed.usage { - if usage.total_tokens > 0 { + if let Some(ref usage) = parsed.usage + && usage.total_tokens > 0 { return Some(ParsedChunk::Usage(usage.clone())); } - } - for choice in parsed.choices { + // SSE 流式响应中每个 chunk 只包含一个 choice 的 delta + if let Some(choice) = parsed.choices.into_iter().next() { // 工具调用 delta - if let Some(tool_calls) = &choice.delta.tool_calls { - for tc in tool_calls { + if let Some(tool_calls) = &choice.delta.tool_calls + && let Some(tc) = tool_calls.first() { let idx = tc.index.unwrap_or(0); let args = tc .function @@ -183,23 +183,19 @@ pub(crate) fn parse_chat_chunk(line: &str) -> Option { arguments: args, }); } - } - if let Some(reasoning) = &choice.delta.reasoning_content { - if !reasoning.is_empty() { + if let Some(reasoning) = &choice.delta.reasoning_content + && !reasoning.is_empty() { return Some(ParsedChunk::Reasoning(reasoning.clone())); } - } - if let Some(content) = &choice.delta.content { - if !content.is_empty() { + if let Some(content) = &choice.delta.content + && !content.is_empty() { return Some(ParsedChunk::Text(content.clone())); } - } - if let Some(reason) = &choice.finish_reason { - if !reason.is_empty() { + if let Some(reason) = &choice.finish_reason + && !reason.is_empty() { return Some(ParsedChunk::FinishReason(reason.clone())); } - } } None diff --git a/src/logger.rs b/src/logger.rs index e508389..0a6be8d 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -28,8 +28,8 @@ pub fn init_logger(log_dir: Option<&str>, with_file: bool) { .with_target(false) .with_ansi(true); - if with_file { - if let Some(dir) = log_dir { + if with_file + && let Some(dir) = log_dir { let dir_path = PathBuf::from(dir); std::fs::create_dir_all(&dir_path).ok(); @@ -46,7 +46,6 @@ pub fn init_logger(log_dir: Option<&str>, with_file: bool) { .init(); return; } - } tracing_subscriber::registry() .with(env_filter) diff --git a/src/main.rs b/src/main.rs index 9bf5311..56e6db2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -371,7 +371,6 @@ async fn cmd_listen( .send_text(&uid, &msg, None) .await .map(|_| ()) - .map_err(|e| e) }) }) }; @@ -536,8 +535,8 @@ async fn listen_loop( info!("收到消息 from={}: {}", from, text); // === 入库:收到的消息 === - if let Some(db) = database { - if let Err(e) = db::models::insert_chat_record( + if let Some(db) = database + && let Err(e) = db::models::insert_chat_record( db.pool(), "inbound", from, @@ -551,7 +550,6 @@ async fn listen_loop( { error!("保存聊天记录失败: {}", e); } - } // Echo 模式 if echo { @@ -559,8 +557,8 @@ async fn listen_loop( Ok(sent_id) => { info!("回显成功 msg_id={}", sent_id); // === 入库:发送的回显 === - if let Some(db) = database { - if let Err(e) = db::models::insert_chat_record( + if let Some(db) = database + && let Err(e) = db::models::insert_chat_record( db.pool(), "outbound", from, @@ -574,7 +572,6 @@ async fn listen_loop( { error!("保存聊天记录失败: {}", e); } - } } Err(e) => error!("回显失败: {}", e), } @@ -612,8 +609,8 @@ async fn listen_loop( } // LLM 回复(异步执行,避免审批阻塞主循环) - if enable_llm { - if let Some(conv) = &conversation { + if enable_llm + && let Some(conv) = &conversation { let conv = conv.clone(); let client = client.clone(); let database = database.clone(); @@ -639,8 +636,8 @@ async fn listen_loop( { Ok(sent_id) => { info!("LLM 回复成功 msg_id={}", sent_id); - if let Some(db) = database { - if let Err(e) = db::models::insert_chat_record( + if let Some(db) = database + && let Err(e) = db::models::insert_chat_record( db.pool(), "outbound", &from_owned, @@ -654,13 +651,11 @@ async fn listen_loop( { error!("保存聊天记录失败: {}", e); } - } } Err(e) => error!("发送 LLM 回复失败: {}", e), } }); } - } } } Err(e) => { @@ -722,8 +717,8 @@ async fn store_usage( provider: &str, u: &Usage, ) { - if let Some(db) = db { - if let Err(e) = db::models::insert_llm_usage( + if let Some(db) = db + && let Err(e) = db::models::insert_llm_usage( db.pool(), user_id, model, @@ -737,7 +732,6 @@ async fn store_usage( { tracing::error!("存储 LLM 用量失败: {}", e); } - } } async fn cmd_whoami(database: &Option>, file_state: &state::StateManager) { @@ -863,8 +857,8 @@ async fn cmd_send( println!("✅ 消息已发送, msg_id={}", msg_id); // 入库:发送的消息 - if let Some(db) = database { - if let Err(e) = db::models::insert_chat_record( + if let Some(db) = database + && let Err(e) = db::models::insert_chat_record( db.pool(), "outbound", to, @@ -878,7 +872,6 @@ async fn cmd_send( { error!("保存聊天记录失败: {}", e); } - } } Err(e) => error!("发送失败: {}", e), } @@ -898,9 +891,7 @@ async fn builtin_approve(ctx: &ExecutionContext, name: &str) -> Result Option { while let Some(ch) = self.pending.pop_front() { - if let Some(q) = self.queues.get_mut(&ch) { - if let Some(item) = q.pop_front() { + if let Some(q) = self.queues.get_mut(&ch) + && let Some(item) = q.pop_front() { if !q.is_empty() { self.pending.push_back(ch); } else { @@ -274,7 +274,6 @@ impl MessageQueue { } return Some(item); } - } self.queues.remove(&ch); } None @@ -290,7 +289,7 @@ impl MessageQueue { #[allow(dead_code)] pub fn has_pending(&self, ch: &ChannelId) -> bool { - self.queues.get(ch).map_or(false, |q| !q.is_empty()) + self.queues.get(ch).is_some_and(|q| !q.is_empty()) } #[allow(dead_code)] diff --git a/src/tools/approval.rs b/src/tools/approval.rs index 7f31703..87454b0 100644 --- a/src/tools/approval.rs +++ b/src/tools/approval.rs @@ -215,12 +215,11 @@ impl ApprovalManager { let mut map = self.pending.lock().await; for (uid, i, _) in expired.iter().rev() { - if let Some(entries) = map.get_mut(uid) { - if *i < entries.len() { + if let Some(entries) = map.get_mut(uid) + && *i < entries.len() { let entry = entries.remove(*i); let _ = entry.sender.send(ApprovalDecision::Expired); } - } } map.retain(|_, v| !v.is_empty()); } diff --git a/src/tools/builtins/fetch_page.rs b/src/tools/builtins/fetch_page.rs index 683c897..467a490 100644 --- a/src/tools/builtins/fetch_page.rs +++ b/src/tools/builtins/fetch_page.rs @@ -151,20 +151,18 @@ fn extract_content(document: &Html, domain: &str) -> String { let config = load_config(); if let Some(selectors) = config.get(domain) { for sel_str in selectors { - if let Some(text) = try_selector(document, sel_str) { - if text.len() > 50 { + if let Some(text) = try_selector(document, sel_str) + && text.len() > 50 { return text; } - } } } // 2. 先用可读性算法 - if let Some(text) = readability_extract(document) { - if text.len() > 50 { + if let Some(text) = readability_extract(document) + && text.len() > 50 { return text; } - } // 3. 回退到 body if let Some(text) = try_selector(document, "body") { @@ -254,7 +252,7 @@ fn score_element(el: &ElementRef) -> f64 { score += commas * 3.0; // 句号/问号/感叹号 → 完整句子 - let sentences = text.matches(|c| c == '.' || c == '。' || c == '?' || c == '?' || c == '!' || c == '!').count() as f64; + let sentences = text.matches(['.', '。', '?', '?', '!', '!']).count() as f64; score += sentences * 2.0; // 段落数(

标签) @@ -295,11 +293,10 @@ fn extract_clean_text(el: &ElementRef) -> String { for child in el.descendants() { // 跳过噪声元素 - if let Some(child_el) = ElementRef::wrap(child) { - if is_noise(&child_el) { + if let Some(child_el) = ElementRef::wrap(child) + && is_noise(&child_el) { continue; } - } if let Some(t) = child.value().as_text() { let t = t.trim(); @@ -326,12 +323,11 @@ fn extract_clean_text(el: &ElementRef) -> String { // ─── 辅助 ─── fn extract_title(document: &Html) -> String { - if let Ok(sel) = Selector::parse("title") { - if let Some(el) = document.select(&sel).next() { + if let Ok(sel) = Selector::parse("title") + && let Some(el) = document.select(&sel).next() { let t: String = el.text().collect::>().join(" ").trim().to_string(); if !t.is_empty() { return t; } } - } "无标题".to_string() } diff --git a/src/tools/builtins/weather.rs b/src/tools/builtins/weather.rs index f7514b9..c2cfa1a 100644 --- a/src/tools/builtins/weather.rs +++ b/src/tools/builtins/weather.rs @@ -486,11 +486,9 @@ fn extract_days_from_prompt(prompt: &str) -> u32 { .rev() .collect::() .into() - { - if let Ok(n) = num_str.parse::() { + && let Ok(n) = num_str.parse::() { return n.min(30).max(1); } - } } } 3 // 默认 @@ -613,7 +611,7 @@ pub async fn execute(params: serde_json::Value) -> SkillResult { if let Some(arr) = obj.get_mut(*key).and_then(|v| v.as_array_mut()) { for item in arr.iter_mut() { if let Some(m) = item.as_object_mut() { - m.retain(|_, v| !v.is_null() && v.as_str().map_or(true, |s| !s.is_empty())); + m.retain(|_, v| !v.is_null() && v.as_str().is_none_or(|s| !s.is_empty())); } } } diff --git a/src/tools/builtins/web_search.rs b/src/tools/builtins/web_search.rs index 3643057..8b93001 100644 --- a/src/tools/builtins/web_search.rs +++ b/src/tools/builtins/web_search.rs @@ -325,11 +325,10 @@ pub async fn execute(params: serde_json::Value) -> SkillResult { let mut output = String::new(); // AI 摘要 - if let Some(ref answer) = data.answer { - if !answer.is_empty() { + if let Some(ref answer) = data.answer + && !answer.is_empty() { output.push_str(&format!("📝 {}\n\n", answer)); } - } // 搜索结果 if data.results.is_empty() { diff --git a/src/tools/mod.rs b/src/tools/mod.rs index de28d1f..d0a78fe 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -70,11 +70,10 @@ pub fn unpack_call_params(cp: &serde_json::Value) -> serde_json::Value { if let Some(prompt) = cp.get("prompt") { match prompt { serde_json::Value::String(s) => { - if let Ok(v) = serde_json::from_str::(s) { - if let Some(obj) = v.as_object() { + if let Ok(v) = serde_json::from_str::(s) + && let Some(obj) = v.as_object() { params.extend(obj.clone()); } - } } serde_json::Value::Object(obj) => { params.extend(obj.clone()); @@ -192,7 +191,7 @@ fn format_spec(lines: &mut Vec, spec: &SkillSpec) { .parameters .get("properties") .and_then(|v| v.as_object()) - .map_or(true, |o| o.is_empty()) + .is_none_or(|o| o.is_empty()) { lines.push(format!( " 参数: {}", diff --git a/src/wechat/types.rs b/src/wechat/types.rs index b2911ab..ab0341b 100644 --- a/src/wechat/types.rs +++ b/src/wechat/types.rs @@ -135,6 +135,7 @@ pub struct RefMessage { /// /// `text()` 便捷方法用于快速构造文本消息(发送消息时使用)。 #[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Default)] pub struct MessageItem { #[serde(rename = "type", skip_serializing_if = "Option::is_none")] pub item_type: Option, @@ -182,23 +183,6 @@ impl MessageItem { } } -impl Default for MessageItem { - fn default() -> Self { - Self { - item_type: None, - create_time_ms: None, - update_time_ms: None, - is_completed: None, - msg_id: None, - ref_msg: None, - text_item: None, - image_item: None, - voice_item: None, - file_item: None, - video_item: None, - } - } -} /// ## 微信消息结构 /// @@ -214,6 +198,7 @@ impl Default for MessageItem { /// * `group_id` — 群聊 ID(群消息时非空) #[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Default)] pub struct WeixinMessage { #[serde(skip_serializing_if = "Option::is_none")] pub seq: Option, @@ -245,26 +230,6 @@ pub struct WeixinMessage { pub context_token: Option, } -impl Default for WeixinMessage { - fn default() -> Self { - Self { - seq: None, - message_id: None, - from_user_id: None, - to_user_id: None, - client_id: None, - create_time_ms: None, - update_time_ms: None, - delete_time_ms: None, - session_id: None, - group_id: None, - msg_type: None, - message_state: None, - item_list: None, - context_token: None, - } - } -} impl WeixinMessage { #[allow(dead_code)] diff --git a/src/worker.rs b/src/worker.rs index aeb3750..f01433a 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -276,8 +276,8 @@ fn build_worker_executor( if target_name == "manage_memos" { let cp: serde_json::Value = serde_json::from_str(&target_args).unwrap_or_default(); - if cp.get("action").and_then(|v| v.as_str()) == Some("add") { - if let Some(content) = cp.get("content").and_then(|v| v.as_str()) { + if cp.get("action").and_then(|v| v.as_str()) == Some("add") + && let Some(content) = cp.get("content").and_then(|v| v.as_str()) { shared .memories .lock() @@ -287,7 +287,6 @@ fn build_worker_executor( .push(content.to_string()); shared.new_memories.lock().await.push(content.to_string()); } - } } return Ok(result.output); }