chore: 修复 clippy error(loop never loops → if-let)并应用自动修复
This commit is contained in:
@@ -335,15 +335,14 @@ impl WeChatClient {
|
||||
.map_err(|e| format!("解析发送响应失败: {}", e))?;
|
||||
|
||||
// 校验业务返回码(存在 ret 字段且非 0 才算失败)
|
||||
if let Some(ret) = resp.get("ret").and_then(|v| v.as_i64()) {
|
||||
if ret != 0 {
|
||||
if let Some(ret) = resp.get("ret").and_then(|v| v.as_i64())
|
||||
&& ret != 0 {
|
||||
let errmsg = resp.get("errmsg").and_then(|v| v.as_str());
|
||||
return Err(format!(
|
||||
"发送消息业务失败 ret={} errmsg={:?}",
|
||||
ret, errmsg
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(client_id)
|
||||
}
|
||||
|
||||
+2
-37
@@ -118,6 +118,7 @@ pub struct RefMessage {
|
||||
|
||||
/// 消息条目(支持 text/image/voice/file/video)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Default)]
|
||||
pub struct MessageItem {
|
||||
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
||||
pub item_type: Option<i32>,
|
||||
@@ -165,27 +166,11 @@ 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 微信消息 ───
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Default)]
|
||||
pub struct WeixinMessage {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub seq: Option<i64>,
|
||||
@@ -217,26 +202,6 @@ pub struct WeixinMessage {
|
||||
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 {
|
||||
#[allow(dead_code)]
|
||||
|
||||
+10
-15
@@ -61,8 +61,8 @@ impl PipelineContext {
|
||||
info!("收到消息 from={}: {}", from, text);
|
||||
|
||||
// 入库:收到的消息
|
||||
if let Some(db) = &self.database {
|
||||
if let Err(e) = crate::db::models::insert_chat_record(
|
||||
if let Some(db) = &self.database
|
||||
&& let Err(e) = crate::db::models::insert_chat_record(
|
||||
db.pool(),
|
||||
"inbound",
|
||||
from,
|
||||
@@ -76,15 +76,14 @@ impl PipelineContext {
|
||||
{
|
||||
error!("保存聊天记录失败: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Echo 模式
|
||||
if echo {
|
||||
match self.client.send_text(from, text, ctx_token).await {
|
||||
Ok(sent_id) => {
|
||||
info!("回显成功 msg_id={}", sent_id);
|
||||
if let Some(db) = &self.database {
|
||||
if let Err(e) = crate::db::models::insert_chat_record(
|
||||
if let Some(db) = &self.database
|
||||
&& let Err(e) = crate::db::models::insert_chat_record(
|
||||
db.pool(),
|
||||
"outbound",
|
||||
from,
|
||||
@@ -98,7 +97,6 @@ impl PipelineContext {
|
||||
{
|
||||
error!("保存聊天记录失败: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => error!("回显失败: {}", e),
|
||||
}
|
||||
@@ -117,8 +115,8 @@ impl PipelineContext {
|
||||
}
|
||||
|
||||
// 用户切换 + LLM 回复(全局会话锁保证串行)
|
||||
if enable_llm {
|
||||
if let Some(conv) = &conversation {
|
||||
if enable_llm
|
||||
&& let Some(conv) = &conversation {
|
||||
let conv = conv.clone();
|
||||
let client = self.client.clone();
|
||||
let database = self.database.clone();
|
||||
@@ -181,8 +179,8 @@ impl PipelineContext {
|
||||
{
|
||||
Ok(sent_id) => {
|
||||
info!("LLM 回复成功 msg_id={}", sent_id);
|
||||
if let Some(db) = database {
|
||||
if let Err(e) = crate::db::models::insert_chat_record(
|
||||
if let Some(db) = database
|
||||
&& let Err(e) = crate::db::models::insert_chat_record(
|
||||
db.pool(),
|
||||
"outbound",
|
||||
&from_owned,
|
||||
@@ -196,13 +194,11 @@ impl PipelineContext {
|
||||
{
|
||||
error!("保存聊天记录失败: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => error!("发送 LLM 回复失败: {}", e),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -266,8 +262,8 @@ async fn store_usage(
|
||||
provider: &str,
|
||||
u: &Usage,
|
||||
) {
|
||||
if let Some(db) = db {
|
||||
if let Err(e) = crate::db::models::insert_llm_usage(
|
||||
if let Some(db) = db
|
||||
&& let Err(e) = crate::db::models::insert_llm_usage(
|
||||
db.pool(),
|
||||
user_id,
|
||||
model,
|
||||
@@ -281,5 +277,4 @@ async fn store_usage(
|
||||
{
|
||||
tracing::error!("存储 LLM 用量失败: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -106,8 +106,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() => {
|
||||
@@ -117,7 +117,6 @@ async fn generate_summary(messages: &[Message], summarizer: Option<&Summarizer>)
|
||||
_ => tracing::warn!("LLM 摘要失败,回退到简单截断"),
|
||||
}
|
||||
}
|
||||
}
|
||||
summarize_messages(messages)
|
||||
}
|
||||
|
||||
|
||||
+10
-14
@@ -67,7 +67,7 @@ impl MessageQueue {
|
||||
continue;
|
||||
}
|
||||
// 检查是否有待处理消息
|
||||
if self.pending.get(&uid).map_or(true, |q| q.is_empty()) {
|
||||
if self.pending.get(&uid).is_none_or(|q| q.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
self.active.insert(uid.clone(), true);
|
||||
@@ -101,7 +101,7 @@ impl MessageQueue {
|
||||
fn has_pending(&self, user_id: &str) -> bool {
|
||||
self.pending
|
||||
.get(user_id)
|
||||
.map_or(false, |q| !q.is_empty())
|
||||
.is_some_and(|q| !q.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,8 +249,8 @@ pub async fn run(
|
||||
info!("收到消息 from={}: {}", from, text);
|
||||
|
||||
// 入库:收到的消息
|
||||
if let Some(db) = &shared.db {
|
||||
if let Err(e) = crate::db::models::insert_chat_record(
|
||||
if let Some(db) = &shared.db
|
||||
&& let Err(e) = crate::db::models::insert_chat_record(
|
||||
db.pool(),
|
||||
"inbound",
|
||||
from,
|
||||
@@ -264,7 +264,6 @@ pub async fn run(
|
||||
{
|
||||
error!("保存聊天记录失败: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 审批回复检查
|
||||
if let Some((skill_name, decision)) = shared.approval.handle_reply(from, text).await {
|
||||
@@ -500,8 +499,8 @@ async fn handle_worker(
|
||||
cache_miss_tokens,
|
||||
user_id: uid,
|
||||
} => {
|
||||
if let Some(db) = &shared.db {
|
||||
if let Err(e) = crate::db::models::insert_llm_usage(
|
||||
if let Some(db) = &shared.db
|
||||
&& let Err(e) = crate::db::models::insert_llm_usage(
|
||||
db.pool(),
|
||||
&uid,
|
||||
&model,
|
||||
@@ -515,7 +514,6 @@ async fn handle_worker(
|
||||
{
|
||||
error!("存储 LLM 用量失败: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
OutputFrame::Bye => break,
|
||||
},
|
||||
@@ -536,8 +534,8 @@ async fn handle_worker(
|
||||
Ok(sent_id) => {
|
||||
info!("回复成功 user={} msg_id={}", user_id, sent_id);
|
||||
// 入库:发送的回复
|
||||
if let Some(db) = &shared.db {
|
||||
if let Err(e) = crate::db::models::insert_chat_record(
|
||||
if let Some(db) = &shared.db
|
||||
&& let Err(e) = crate::db::models::insert_chat_record(
|
||||
db.pool(),
|
||||
"outbound",
|
||||
&user_id,
|
||||
@@ -551,7 +549,6 @@ async fn handle_worker(
|
||||
{
|
||||
error!("保存聊天记录失败: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => error!("发送回复失败: {}", e),
|
||||
}
|
||||
@@ -564,8 +561,8 @@ async fn handle_worker(
|
||||
{
|
||||
Ok(sent_id) => {
|
||||
info!("回复成功 (chunk) user={} msg_id={}", user_id, sent_id);
|
||||
if let Some(db) = &shared.db {
|
||||
if let Err(e) = crate::db::models::insert_chat_record(
|
||||
if let Some(db) = &shared.db
|
||||
&& let Err(e) = crate::db::models::insert_chat_record(
|
||||
db.pool(),
|
||||
"outbound",
|
||||
&user_id,
|
||||
@@ -579,7 +576,6 @@ async fn handle_worker(
|
||||
{
|
||||
error!("保存聊天记录失败: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => error!("发送回复失败: {}", e),
|
||||
}
|
||||
|
||||
@@ -171,8 +171,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 请求 {} 个工具: {}",
|
||||
@@ -208,7 +208,6 @@ impl Conversation {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 无工具调用:记录回复,检查是否需要溢出摘要
|
||||
if !full_text.is_empty() {
|
||||
@@ -219,7 +218,7 @@ impl Conversation {
|
||||
{
|
||||
let s = self.session.lock().await;
|
||||
let recent = s.recent_messages();
|
||||
let estimated: u32 = recent.iter().map(|m| ctx_builder::estimate_tokens(m)).sum();
|
||||
let estimated: u32 = recent.iter().map(ctx_builder::estimate_tokens).sum();
|
||||
if estimated > s.token_budget {
|
||||
drop(s);
|
||||
summary_builder::trigger_overflow_summary(
|
||||
|
||||
+2
-3
@@ -167,8 +167,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 {
|
||||
@@ -195,7 +195,6 @@ async fn stream_deepseek(
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 构建 tool_calls
|
||||
let tool_calls: Option<Vec<ToolCall>> = if tool_call_builders.is_empty() {
|
||||
|
||||
+12
-16
@@ -110,16 +110,16 @@ pub(crate) fn parse_chat_chunk(line: &str) -> Option<ParsedChunk> {
|
||||
};
|
||||
|
||||
// 提取 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
|
||||
@@ -134,23 +134,19 @@ pub(crate) fn parse_chat_chunk(line: &str) -> Option<ParsedChunk> {
|
||||
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
|
||||
|
||||
+2
-3
@@ -11,8 +11,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();
|
||||
|
||||
@@ -29,7 +29,6 @@ pub fn init_logger(log_dir: Option<&str>, with_file: bool) {
|
||||
.init();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
|
||||
+2
-4
@@ -276,7 +276,6 @@ async fn cmd_listen(
|
||||
.send_text(&uid, &msg, None)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|e| e)
|
||||
})
|
||||
})
|
||||
};
|
||||
@@ -477,8 +476,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,
|
||||
@@ -492,7 +491,6 @@ async fn cmd_send(
|
||||
{
|
||||
error!("保存聊天记录失败: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => error!("发送失败: {}", e),
|
||||
}
|
||||
|
||||
@@ -201,12 +201,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());
|
||||
}
|
||||
|
||||
@@ -136,20 +136,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") {
|
||||
@@ -239,7 +237,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;
|
||||
|
||||
// 段落数(<p> 标签)
|
||||
@@ -280,11 +278,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();
|
||||
@@ -311,12 +308,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::<Vec<_>>().join(" ").trim().to_string();
|
||||
if !t.is_empty() { return t; }
|
||||
}
|
||||
}
|
||||
"无标题".to_string()
|
||||
}
|
||||
|
||||
|
||||
@@ -197,11 +197,10 @@ async fn run_subagent(prompt: &str, timeout_secs: u64) -> Result<String, String>
|
||||
/// 查找 pi 可执行文件路径
|
||||
fn find_pi_exe() -> Result<String, String> {
|
||||
// 1. 检查环境变量 PI_EXE
|
||||
if let Ok(path) = std::env::var("PI_EXE") {
|
||||
if std::path::Path::new(&path).exists() {
|
||||
if let Ok(path) = std::env::var("PI_EXE")
|
||||
&& std::path::Path::new(&path).exists() {
|
||||
return Ok(path);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 在 PATH 中搜索
|
||||
if let Ok(path_var) = std::env::var("PATH") {
|
||||
|
||||
@@ -474,11 +474,9 @@ fn extract_days_from_prompt(prompt: &str) -> u32 {
|
||||
.rev()
|
||||
.collect::<String>()
|
||||
.into()
|
||||
{
|
||||
if let Ok(n) = num_str.parse::<u32>() {
|
||||
&& let Ok(n) = num_str.parse::<u32>() {
|
||||
return n.min(30).max(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3 // 默认
|
||||
@@ -601,7 +599,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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,11 +305,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() {
|
||||
|
||||
@@ -243,8 +243,8 @@ impl ToolExecutorBuilder {
|
||||
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) =
|
||||
if cp.get("action").and_then(|v| v.as_str()) == Some("add")
|
||||
&& let Some(content) =
|
||||
cp.get("content").and_then(|v| v.as_str())
|
||||
{
|
||||
if let Some(ref new_mems) = new_memories {
|
||||
@@ -254,7 +254,6 @@ impl ToolExecutorBuilder {
|
||||
store.write_for(&effective_user, content).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(result.output);
|
||||
}
|
||||
@@ -317,10 +316,8 @@ async fn listen_approve(
|
||||
"⚠️ 操作确认\n\n技能:{}\n\n确认码:{}\n\n回复确认码以继续\n回复 0 或「取消」以取消\n(60秒内有效,最多3次尝试)",
|
||||
tool_name, code
|
||||
);
|
||||
if let Some(ref send) = send_wechat {
|
||||
if let Err(e) = send(user_id, &msg).await {
|
||||
return Err(e);
|
||||
}
|
||||
if let Some(send) = send_wechat {
|
||||
send(user_id, &msg).await?
|
||||
}
|
||||
|
||||
// listen 模式使用较短超时(60s),避免长时间持有全局会话锁
|
||||
|
||||
+3
-4
@@ -17,11 +17,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::<serde_json::Value>(s) {
|
||||
if let Some(obj) = v.as_object() {
|
||||
if let Ok(v) = serde_json::from_str::<serde_json::Value>(s)
|
||||
&& let Some(obj) = v.as_object() {
|
||||
params.extend(obj.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
serde_json::Value::Object(obj) => {
|
||||
params.extend(obj.clone());
|
||||
@@ -323,7 +322,7 @@ fn format_spec(lines: &mut Vec<String>, 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!(
|
||||
" 参数: {}",
|
||||
|
||||
+1
-3
@@ -103,8 +103,7 @@ pub async fn run(sock_path: &str) -> Result<(), String> {
|
||||
// 6b. 若审批已通过且带有参数,直接执行工具并注入结果
|
||||
if let (Some(tool_name), Some(tool_args)) =
|
||||
(&task.approved_tool, &task.approved_tool_args)
|
||||
{
|
||||
if !tool_name.is_empty() && !tool_args.is_empty() {
|
||||
&& !tool_name.is_empty() && !tool_args.is_empty() {
|
||||
info!("审批通过,直接执行工具: {} args={:.80}", tool_name, tool_args);
|
||||
let tool_id = format!("approved_{}", uuid::Uuid::new_v4());
|
||||
// 手动执行工具
|
||||
@@ -131,7 +130,6 @@ pub async fn run(sock_path: &str) -> Result<(), String> {
|
||||
s.add_tool_result(&tool_id, tool_name, &result_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7. 执行 LLM 对话(带审批检测)
|
||||
let (reply, _used_tools, usage) = conv
|
||||
|
||||
Reference in New Issue
Block a user