chore: 修复 clippy 错误(loop never loops → if-let)并应用自动修复

This commit is contained in:
2026-06-10 10:17:25 +08:00
parent 19dc54f842
commit f7d0781090
15 changed files with 60 additions and 123 deletions
+2 -3
View File
@@ -243,8 +243,8 @@ pub async fn trigger_idle_summary(
/// 生成摘要:优先使用 LLM,不可用时回退到简单截断 /// 生成摘要:优先使用 LLM,不可用时回退到简单截断
async fn generate_summary(messages: &[Message], summarizer: Option<&Summarizer>) -> String { async fn generate_summary(messages: &[Message], summarizer: Option<&Summarizer>) -> String {
if let Some(summarizer) = summarizer { if let Some(summarizer) = summarizer
if messages.len() >= 3 { && messages.len() >= 3 {
let prompt = build_summary_prompt(messages); let prompt = build_summary_prompt(messages);
match summarizer(prompt).await { match summarizer(prompt).await {
Ok(text) if !text.is_empty() => { Ok(text) if !text.is_empty() => {
@@ -254,7 +254,6 @@ async fn generate_summary(messages: &[Message], summarizer: Option<&Summarizer>)
_ => tracing::warn!("LLM 摘要失败,回退到简单截断"), _ => tracing::warn!("LLM 摘要失败,回退到简单截断"),
} }
} }
}
summarize_messages(messages) summarize_messages(messages)
} }
+1 -1
View File
@@ -363,7 +363,7 @@ async fn llm_consumer_loop(
let session = session.clone(); let session = session.clone();
let n = name.to_string(); let n = name.to_string();
let args = args_json.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 { Box::pin(async move {
let ch = ChannelId::wechat(&uid); let ch = ChannelId::wechat(&uid);
+3 -4
View File
@@ -291,8 +291,8 @@ impl Conversation {
} }
} }
if let Some(calls) = tool_calls { if let Some(calls) = tool_calls
if !calls.is_empty() { && !calls.is_empty() {
used_tools = true; used_tools = true;
tracing::info!( tracing::info!(
"🔧 LLM 请求 {} 个工具: {}", "🔧 LLM 请求 {} 个工具: {}",
@@ -347,7 +347,6 @@ impl Conversation {
} }
continue; continue;
} }
}
// 无工具调用:记录回复,检查是否需要溢出摘要 // 无工具调用:记录回复,检查是否需要溢出摘要
if !full_text.is_empty() { if !full_text.is_empty() {
@@ -358,7 +357,7 @@ impl Conversation {
{ {
let s = self.session.lock().await; let s = self.session.lock().await;
let recent = s.recent_messages(); 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 { if estimated > s.token_budget {
drop(s); drop(s);
builder::trigger_overflow_summary(&self.session, Some(&self.summarizer())) builder::trigger_overflow_summary(&self.session, Some(&self.summarizer()))
+2 -3
View File
@@ -215,8 +215,8 @@ async fn stream_deepseek(
} }
// 检查最后的 buffer // 检查最后的 buffer
if !buf.trim().is_empty() { if !buf.trim().is_empty()
if let Some(parsed) = parse_chat_chunk(buf.trim()) { && let Some(parsed) = parse_chat_chunk(buf.trim()) {
match parsed { match parsed {
ParsedChunk::Text(t) => full_text.push_str(&t), ParsedChunk::Text(t) => full_text.push_str(&t),
ParsedChunk::ToolCallDelta { ParsedChunk::ToolCallDelta {
@@ -243,7 +243,6 @@ async fn stream_deepseek(
_ => {} _ => {}
} }
} }
}
// 构建 tool_calls // 构建 tool_calls
let tool_calls: Option<Vec<ToolCall>> = if tool_call_builders.is_empty() { let tool_calls: Option<Vec<ToolCall>> = if tool_call_builders.is_empty() {
+12 -16
View File
@@ -159,16 +159,16 @@ pub(crate) fn parse_chat_chunk(line: &str) -> Option<ParsedChunk> {
}; };
// 提取 usage(可能在最后一个 chunk // 提取 usage(可能在最后一个 chunk
if let Some(ref usage) = parsed.usage { if let Some(ref usage) = parsed.usage
if usage.total_tokens > 0 { && usage.total_tokens > 0 {
return Some(ParsedChunk::Usage(usage.clone())); 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 // 工具调用 delta
if let Some(tool_calls) = &choice.delta.tool_calls { if let Some(tool_calls) = &choice.delta.tool_calls
for tc in tool_calls { && let Some(tc) = tool_calls.first() {
let idx = tc.index.unwrap_or(0); let idx = tc.index.unwrap_or(0);
let args = tc let args = tc
.function .function
@@ -183,24 +183,20 @@ pub(crate) fn parse_chat_chunk(line: &str) -> Option<ParsedChunk> {
arguments: args, arguments: args,
}); });
} }
}
if let Some(reasoning) = &choice.delta.reasoning_content { if let Some(reasoning) = &choice.delta.reasoning_content
if !reasoning.is_empty() { && !reasoning.is_empty() {
return Some(ParsedChunk::Reasoning(reasoning.clone())); return Some(ParsedChunk::Reasoning(reasoning.clone()));
} }
} if let Some(content) = &choice.delta.content
if let Some(content) = &choice.delta.content { && !content.is_empty() {
if !content.is_empty() {
return Some(ParsedChunk::Text(content.clone())); return Some(ParsedChunk::Text(content.clone()));
} }
} if let Some(reason) = &choice.finish_reason
if let Some(reason) = &choice.finish_reason { && !reason.is_empty() {
if !reason.is_empty() {
return Some(ParsedChunk::FinishReason(reason.clone())); return Some(ParsedChunk::FinishReason(reason.clone()));
} }
} }
}
None None
} }
+2 -3
View File
@@ -28,8 +28,8 @@ pub fn init_logger(log_dir: Option<&str>, with_file: bool) {
.with_target(false) .with_target(false)
.with_ansi(true); .with_ansi(true);
if with_file { if with_file
if let Some(dir) = log_dir { && let Some(dir) = log_dir {
let dir_path = PathBuf::from(dir); let dir_path = PathBuf::from(dir);
std::fs::create_dir_all(&dir_path).ok(); std::fs::create_dir_all(&dir_path).ok();
@@ -46,7 +46,6 @@ pub fn init_logger(log_dir: Option<&str>, with_file: bool) {
.init(); .init();
return; return;
} }
}
tracing_subscriber::registry() tracing_subscriber::registry()
.with(env_filter) .with(env_filter)
+13 -22
View File
@@ -371,7 +371,6 @@ async fn cmd_listen(
.send_text(&uid, &msg, None) .send_text(&uid, &msg, None)
.await .await
.map(|_| ()) .map(|_| ())
.map_err(|e| e)
}) })
}) })
}; };
@@ -536,8 +535,8 @@ async fn listen_loop(
info!("收到消息 from={}: {}", from, text); info!("收到消息 from={}: {}", from, text);
// === 入库:收到的消息 === // === 入库:收到的消息 ===
if let Some(db) = database { if let Some(db) = database
if let Err(e) = db::models::insert_chat_record( && let Err(e) = db::models::insert_chat_record(
db.pool(), db.pool(),
"inbound", "inbound",
from, from,
@@ -551,7 +550,6 @@ async fn listen_loop(
{ {
error!("保存聊天记录失败: {}", e); error!("保存聊天记录失败: {}", e);
} }
}
// Echo 模式 // Echo 模式
if echo { if echo {
@@ -559,8 +557,8 @@ async fn listen_loop(
Ok(sent_id) => { Ok(sent_id) => {
info!("回显成功 msg_id={}", sent_id); info!("回显成功 msg_id={}", sent_id);
// === 入库:发送的回显 === // === 入库:发送的回显 ===
if let Some(db) = database { if let Some(db) = database
if let Err(e) = db::models::insert_chat_record( && let Err(e) = db::models::insert_chat_record(
db.pool(), db.pool(),
"outbound", "outbound",
from, from,
@@ -575,7 +573,6 @@ async fn listen_loop(
error!("保存聊天记录失败: {}", e); error!("保存聊天记录失败: {}", e);
} }
} }
}
Err(e) => error!("回显失败: {}", e), Err(e) => error!("回显失败: {}", e),
} }
} }
@@ -612,8 +609,8 @@ async fn listen_loop(
} }
// LLM 回复(异步执行,避免审批阻塞主循环) // LLM 回复(异步执行,避免审批阻塞主循环)
if enable_llm { if enable_llm
if let Some(conv) = &conversation { && let Some(conv) = &conversation {
let conv = conv.clone(); let conv = conv.clone();
let client = client.clone(); let client = client.clone();
let database = database.clone(); let database = database.clone();
@@ -639,8 +636,8 @@ async fn listen_loop(
{ {
Ok(sent_id) => { Ok(sent_id) => {
info!("LLM 回复成功 msg_id={}", sent_id); info!("LLM 回复成功 msg_id={}", sent_id);
if let Some(db) = database { if let Some(db) = database
if let Err(e) = db::models::insert_chat_record( && let Err(e) = db::models::insert_chat_record(
db.pool(), db.pool(),
"outbound", "outbound",
&from_owned, &from_owned,
@@ -655,14 +652,12 @@ async fn listen_loop(
error!("保存聊天记录失败: {}", e); error!("保存聊天记录失败: {}", e);
} }
} }
}
Err(e) => error!("发送 LLM 回复失败: {}", e), Err(e) => error!("发送 LLM 回复失败: {}", e),
} }
}); });
} }
} }
} }
}
Err(e) => { Err(e) => {
if !e.contains("超时") && !e.contains("timeout") { if !e.contains("超时") && !e.contains("timeout") {
error!("接收消息失败: {}", e); error!("接收消息失败: {}", e);
@@ -722,8 +717,8 @@ async fn store_usage(
provider: &str, provider: &str,
u: &Usage, u: &Usage,
) { ) {
if let Some(db) = db { if let Some(db) = db
if let Err(e) = db::models::insert_llm_usage( && let Err(e) = db::models::insert_llm_usage(
db.pool(), db.pool(),
user_id, user_id,
model, model,
@@ -738,7 +733,6 @@ async fn store_usage(
tracing::error!("存储 LLM 用量失败: {}", e); tracing::error!("存储 LLM 用量失败: {}", e);
} }
} }
}
async fn cmd_whoami(database: &Option<Arc<Database>>, file_state: &state::StateManager) { async fn cmd_whoami(database: &Option<Arc<Database>>, file_state: &state::StateManager) {
let auth: Option<state::AuthState> = if let Some(db) = database { let auth: Option<state::AuthState> = if let Some(db) = database {
@@ -863,8 +857,8 @@ async fn cmd_send(
println!("✅ 消息已发送, msg_id={}", msg_id); println!("✅ 消息已发送, msg_id={}", msg_id);
// 入库:发送的消息 // 入库:发送的消息
if let Some(db) = database { if let Some(db) = database
if let Err(e) = db::models::insert_chat_record( && let Err(e) = db::models::insert_chat_record(
db.pool(), db.pool(),
"outbound", "outbound",
to, to,
@@ -879,7 +873,6 @@ async fn cmd_send(
error!("保存聊天记录失败: {}", e); error!("保存聊天记录失败: {}", e);
} }
} }
}
Err(e) => error!("发送失败: {}", e), Err(e) => error!("发送失败: {}", e),
} }
} }
@@ -898,9 +891,7 @@ async fn builtin_approve(ctx: &ExecutionContext, name: &str) -> Result<bool, Str
name, code name, code
); );
if let Some(ref send) = ctx.send_wechat { if let Some(ref send) = ctx.send_wechat {
if let Err(e) = send(&ctx.user_id, &msg).await { send(&ctx.user_id, &msg).await?
return Err(e);
}
} }
match tokio::time::timeout(std::time::Duration::from_secs(300), rx).await { match tokio::time::timeout(std::time::Duration::from_secs(300), rx).await {
+3 -4
View File
@@ -265,8 +265,8 @@ impl MessageQueue {
/// - 该渠道还有消息 → 放回 pending 尾部 /// - 该渠道还有消息 → 放回 pending 尾部
pub fn dequeue(&mut self) -> Option<PipelineMessage> { pub fn dequeue(&mut self) -> Option<PipelineMessage> {
while let Some(ch) = self.pending.pop_front() { while let Some(ch) = self.pending.pop_front() {
if let Some(q) = self.queues.get_mut(&ch) { if let Some(q) = self.queues.get_mut(&ch)
if let Some(item) = q.pop_front() { && let Some(item) = q.pop_front() {
if !q.is_empty() { if !q.is_empty() {
self.pending.push_back(ch); self.pending.push_back(ch);
} else { } else {
@@ -274,7 +274,6 @@ impl MessageQueue {
} }
return Some(item); return Some(item);
} }
}
self.queues.remove(&ch); self.queues.remove(&ch);
} }
None None
@@ -290,7 +289,7 @@ impl MessageQueue {
#[allow(dead_code)] #[allow(dead_code)]
pub fn has_pending(&self, ch: &ChannelId) -> bool { 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)] #[allow(dead_code)]
+2 -3
View File
@@ -215,13 +215,12 @@ impl ApprovalManager {
let mut map = self.pending.lock().await; let mut map = self.pending.lock().await;
for (uid, i, _) in expired.iter().rev() { for (uid, i, _) in expired.iter().rev() {
if let Some(entries) = map.get_mut(uid) { if let Some(entries) = map.get_mut(uid)
if *i < entries.len() { && *i < entries.len() {
let entry = entries.remove(*i); let entry = entries.remove(*i);
let _ = entry.sender.send(ApprovalDecision::Expired); let _ = entry.sender.send(ApprovalDecision::Expired);
} }
} }
}
map.retain(|_, v| !v.is_empty()); map.retain(|_, v| !v.is_empty());
} }
} }
+9 -13
View File
@@ -151,20 +151,18 @@ fn extract_content(document: &Html, domain: &str) -> String {
let config = load_config(); let config = load_config();
if let Some(selectors) = config.get(domain) { if let Some(selectors) = config.get(domain) {
for sel_str in selectors { for sel_str in selectors {
if let Some(text) = try_selector(document, sel_str) { if let Some(text) = try_selector(document, sel_str)
if text.len() > 50 { && text.len() > 50 {
return text; return text;
} }
} }
} }
}
// 2. 先用可读性算法 // 2. 先用可读性算法
if let Some(text) = readability_extract(document) { if let Some(text) = readability_extract(document)
if text.len() > 50 { && text.len() > 50 {
return text; return text;
} }
}
// 3. 回退到 body // 3. 回退到 body
if let Some(text) = try_selector(document, "body") { if let Some(text) = try_selector(document, "body") {
@@ -254,7 +252,7 @@ fn score_element(el: &ElementRef) -> f64 {
score += commas * 3.0; 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; score += sentences * 2.0;
// 段落数(<p> 标签) // 段落数(<p> 标签)
@@ -295,11 +293,10 @@ fn extract_clean_text(el: &ElementRef) -> String {
for child in el.descendants() { for child in el.descendants() {
// 跳过噪声元素 // 跳过噪声元素
if let Some(child_el) = ElementRef::wrap(child) { if let Some(child_el) = ElementRef::wrap(child)
if is_noise(&child_el) { && is_noise(&child_el) {
continue; continue;
} }
}
if let Some(t) = child.value().as_text() { if let Some(t) = child.value().as_text() {
let t = t.trim(); let t = t.trim();
@@ -326,12 +323,11 @@ fn extract_clean_text(el: &ElementRef) -> String {
// ─── 辅助 ─── // ─── 辅助 ───
fn extract_title(document: &Html) -> String { fn extract_title(document: &Html) -> String {
if let Ok(sel) = Selector::parse("title") { if let Ok(sel) = Selector::parse("title")
if let Some(el) = document.select(&sel).next() { && let Some(el) = document.select(&sel).next() {
let t: String = el.text().collect::<Vec<_>>().join(" ").trim().to_string(); let t: String = el.text().collect::<Vec<_>>().join(" ").trim().to_string();
if !t.is_empty() { return t; } if !t.is_empty() { return t; }
} }
}
"无标题".to_string() "无标题".to_string()
} }
+2 -4
View File
@@ -486,13 +486,11 @@ fn extract_days_from_prompt(prompt: &str) -> u32 {
.rev() .rev()
.collect::<String>() .collect::<String>()
.into() .into()
{ && let Ok(n) = num_str.parse::<u32>() {
if let Ok(n) = num_str.parse::<u32>() {
return n.min(30).max(1); return n.min(30).max(1);
} }
} }
} }
}
3 // 默认 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()) { if let Some(arr) = obj.get_mut(*key).and_then(|v| v.as_array_mut()) {
for item in arr.iter_mut() { for item in arr.iter_mut() {
if let Some(m) = item.as_object_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()));
} }
} }
} }
+2 -3
View File
@@ -325,11 +325,10 @@ pub async fn execute(params: serde_json::Value) -> SkillResult {
let mut output = String::new(); let mut output = String::new();
// AI 摘要 // AI 摘要
if let Some(ref answer) = data.answer { if let Some(ref answer) = data.answer
if !answer.is_empty() { && !answer.is_empty() {
output.push_str(&format!("📝 {}\n\n", answer)); output.push_str(&format!("📝 {}\n\n", answer));
} }
}
// 搜索结果 // 搜索结果
if data.results.is_empty() { if data.results.is_empty() {
+3 -4
View File
@@ -70,12 +70,11 @@ pub fn unpack_call_params(cp: &serde_json::Value) -> serde_json::Value {
if let Some(prompt) = cp.get("prompt") { if let Some(prompt) = cp.get("prompt") {
match prompt { match prompt {
serde_json::Value::String(s) => { serde_json::Value::String(s) => {
if let Ok(v) = serde_json::from_str::<serde_json::Value>(s) { if let Ok(v) = serde_json::from_str::<serde_json::Value>(s)
if let Some(obj) = v.as_object() { && let Some(obj) = v.as_object() {
params.extend(obj.clone()); params.extend(obj.clone());
} }
} }
}
serde_json::Value::Object(obj) => { serde_json::Value::Object(obj) => {
params.extend(obj.clone()); params.extend(obj.clone());
} }
@@ -192,7 +191,7 @@ fn format_spec(lines: &mut Vec<String>, spec: &SkillSpec) {
.parameters .parameters
.get("properties") .get("properties")
.and_then(|v| v.as_object()) .and_then(|v| v.as_object())
.map_or(true, |o| o.is_empty()) .is_none_or(|o| o.is_empty())
{ {
lines.push(format!( lines.push(format!(
" 参数: {}", " 参数: {}",
+2 -37
View File
@@ -135,6 +135,7 @@ pub struct RefMessage {
/// ///
/// `text()` 便捷方法用于快速构造文本消息(发送消息时使用)。 /// `text()` 便捷方法用于快速构造文本消息(发送消息时使用)。
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Default)]
pub struct MessageItem { pub struct MessageItem {
#[serde(rename = "type", skip_serializing_if = "Option::is_none")] #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub item_type: Option<i32>, pub item_type: Option<i32>,
@@ -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(群消息时非空) /// * `group_id` — 群聊 ID(群消息时非空)
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Default)]
pub struct WeixinMessage { pub struct WeixinMessage {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub seq: Option<i64>, pub seq: Option<i64>,
@@ -245,26 +230,6 @@ pub struct WeixinMessage {
pub context_token: Option<String>, pub context_token: Option<String>,
} }
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 { impl WeixinMessage {
#[allow(dead_code)] #[allow(dead_code)]
+2 -3
View File
@@ -276,8 +276,8 @@ fn build_worker_executor(
if target_name == "manage_memos" { if target_name == "manage_memos" {
let cp: serde_json::Value = let cp: serde_json::Value =
serde_json::from_str(&target_args).unwrap_or_default(); serde_json::from_str(&target_args).unwrap_or_default();
if cp.get("action").and_then(|v| v.as_str()) == Some("add") { 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()) { && let Some(content) = cp.get("content").and_then(|v| v.as_str()) {
shared shared
.memories .memories
.lock() .lock()
@@ -288,7 +288,6 @@ fn build_worker_executor(
shared.new_memories.lock().await.push(content.to_string()); shared.new_memories.lock().await.push(content.to_string());
} }
} }
}
return Ok(result.output); return Ok(result.output);
} }