clippy --fix: 合并嵌套if、简化map_or、移除冗余引用等机械性优化

This commit is contained in:
xiaoxixi 2026-05-08 16:35:21 +08:00
parent ceb8234a30
commit 81e9f1e7db
17 changed files with 79 additions and 115 deletions

View File

@ -162,7 +162,7 @@ impl LoopDetector {
.count(); .count();
// Warn every warn_every times // Warn every warn_every times
if consecutive > 0 && consecutive % self.config.warn_every == 0 { if consecutive > 0 && consecutive.is_multiple_of(self.config.warn_every) {
LoopDetectionResult::Warning(format!( LoopDetectionResult::Warning(format!(
"注意: 工具 '{}' 已连续执行 {} 次,参数相同。如果任务没有进展,请尝试其他方法。", "注意: 工具 '{}' 已连续执行 {} 次,参数相同。如果任务没有进展,请尝试其他方法。",
last.name, consecutive last.name, consecutive
@ -339,7 +339,7 @@ impl AgentLoop {
tracing::debug!(history_len = messages.len(), max_iterations = self.max_iterations, "Starting agent process"); tracing::debug!(history_len = messages.len(), max_iterations = self.max_iterations, "Starting agent process");
// Build and inject system prompt if not present // Build and inject system prompt if not present
let has_system = messages.first().map_or(false, |m| m.role == "system"); let has_system = messages.first().is_some_and(|m| m.role == "system");
if !has_system { if !has_system {
let system_prompt = build_system_prompt(&self.workspace_dir, &self.model_name, &self.tools, None, None); let system_prompt = build_system_prompt(&self.workspace_dir, &self.model_name, &self.tools, None, None);
#[cfg(debug_assertions)] #[cfg(debug_assertions)]

View File

@ -186,14 +186,13 @@ impl PromptSection for UserProfileSection {
let mut output = String::from("## 用户配置\n\n"); let mut output = String::from("## 用户配置\n\n");
// Load USER.md from ~/.picobot/USER.md // Load USER.md from ~/.picobot/USER.md
if let Some(user_config_dir) = get_user_config_dir() { if let Some(user_config_dir) = get_user_config_dir()
if let Some(content) = && let Some(content) =
load_file_from_dir(&user_config_dir, "USER.md", BOOTSTRAP_MAX_CHARS) load_file_from_dir(&user_config_dir, "USER.md", BOOTSTRAP_MAX_CHARS)
{ {
output.push_str(&content); output.push_str(&content);
return output; return output;
} }
}
// No USER.md found, return empty // No USER.md found, return empty
String::new() String::new()

View File

@ -32,6 +32,12 @@ pub struct CliChatChannel {
clients: Mutex<Vec<Arc<Client>>>, clients: Mutex<Vec<Arc<Client>>>,
} }
impl Default for CliChatChannel {
fn default() -> Self {
Self::new()
}
}
impl CliChatChannel { impl CliChatChannel {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {

View File

@ -229,12 +229,11 @@ impl FeishuChannel {
// 1. Check cache // 1. Check cache
{ {
let cached = self.tenant_token.read().await; let cached = self.tenant_token.read().await;
if let Some(ref token) = *cached { if let Some(ref token) = *cached
if Instant::now() < token.refresh_after { && Instant::now() < token.refresh_after {
return Ok(token.value.clone()); return Ok(token.value.clone());
} }
} }
}
// 2. Fetch new token // 2. Fetch new token
let (token, ttl) = self.fetch_new_token().await?; let (token, ttl) = self.fetch_new_token().await?;
@ -901,11 +900,10 @@ impl FeishuChannel {
let (mut content, media) = self.parse_and_download_message(msg_type, &raw_content, &message_id).await?; let (mut content, media) = self.parse_and_download_message(msg_type, &raw_content, &message_id).await?;
// Fetch and prepend quoted message content if this is a reply // Fetch and prepend quoted message content if this is a reply
if let Some(ref pid) = parent_id { if let Some(ref pid) = parent_id
if let Some(reply_ctx) = self.get_message_content(pid).await { && let Some(reply_ctx) = self.get_message_content(pid).await {
content = format!("{}\n{}", reply_ctx, content); content = format!("{}\n{}", reply_ctx, content);
} }
}
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
if let Some(ref m) = media { if let Some(ref m) = media {
@ -1296,8 +1294,8 @@ fn parse_post_content(content: &str) -> String {
// Fall back: try any dict child // Fall back: try any dict child
if let Some(root_obj) = root.as_object() { if let Some(root_obj) = root.as_object() {
for (_key, val) in root_obj { for (_key, val) in root_obj {
if let Some(obj) = val.as_object() { if let Some(obj) = val.as_object()
if obj.get("content").and_then(|c| c.as_array()).is_some() { && obj.get("content").and_then(|c| c.as_array()).is_some() {
parse_block(val, &mut texts); parse_block(val, &mut texts);
let result = texts.join(""); let result = texts.join("");
if !result.trim().is_empty() { if !result.trim().is_empty() {
@ -1307,7 +1305,6 @@ fn parse_post_content(content: &str) -> String {
} }
} }
} }
}
content.to_string() content.to_string()
} }
@ -1329,22 +1326,19 @@ fn extract_interactive_content(content: &str) -> Result<(String, Option<MediaIte
} }
// Extract from card object // Extract from card object
if let Some(card) = parsed.get("card").and_then(|c| c.as_object()) { if let Some(card) = parsed.get("card").and_then(|c| c.as_object())
if let Some(elements) = card.get("elements").and_then(|e| e.as_array()) { && let Some(elements) = card.get("elements").and_then(|e| e.as_array()) {
for el in elements { for el in elements {
extract_element_content(el, &mut texts); extract_element_content(el, &mut texts);
} }
} }
}
// Extract from header // Extract from header
if let Some(header) = parsed.get("header").and_then(|h| h.as_object()) { if let Some(header) = parsed.get("header").and_then(|h| h.as_object())
if let Some(title) = header.get("title").and_then(|t| t.as_object()) { && let Some(title) = header.get("title").and_then(|t| t.as_object())
if let Some(text) = title.get("content").and_then(|c| c.as_str()) { && let Some(text) = title.get("content").and_then(|c| c.as_str()) {
texts.push(format!("title: {}\n", text)); texts.push(format!("title: {}\n", text));
} }
}
}
let result = texts.join("").trim().to_string(); let result = texts.join("").trim().to_string();
if result.is_empty() { if result.is_empty() {
@ -1495,13 +1489,12 @@ fn collect_list_items(items: &[serde_json::Value], lines: &mut Vec<String>, dept
None None
} }
}) })
}) { })
if let Some(children) = children_arr.as_object().and_then(|o| o.get("children")).and_then(|c| c.as_array()) { && let Some(children) = children_arr.as_object().and_then(|o| o.get("children")).and_then(|c| c.as_array()) {
collect_list_items(children, lines, depth + 1); collect_list_items(children, lines, depth + 1);
} }
} }
} }
}
/// Extract text from inline elements (text, link, at-mention) /// Extract text from inline elements (text, link, at-mention)
fn extract_inline_text(el: &serde_json::Value, out: &mut String) { fn extract_inline_text(el: &serde_json::Value, out: &mut String) {
@ -2088,7 +2081,7 @@ impl Channel for FeishuChannel {
content: Self::strip_thinking_tags(&msg.content), content: Self::strip_thinking_tags(&msg.content),
..msg ..msg
}; };
let receive_id = if msg.chat_id.starts_with("oc_") { &msg.chat_id } else { &msg.reply_to.as_ref().unwrap_or(&msg.chat_id) }; let receive_id = if msg.chat_id.starts_with("oc_") { &msg.chat_id } else { msg.reply_to.as_ref().unwrap_or(&msg.chat_id) };
let receive_id_type = if msg.chat_id.starts_with("oc_") { "chat_id" } else { "open_id" }; let receive_id_type = if msg.chat_id.starts_with("oc_") { "chat_id" } else { "open_id" };
// If no media, use smart format detection // If no media, use smart format detection

View File

@ -32,7 +32,7 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) {
let alias = cmd.aliases.first().map(|a| a.as_str()).unwrap_or(&cmd.name); let alias = cmd.aliases.first().map(|a| a.as_str()).unwrap_or(&cmd.name);
ListItem::new(Line::from(vec![ ListItem::new(Line::from(vec![
Span::styled(alias, style.clone()), Span::styled(alias, style),
Span::styled(" - ", Style::default().fg(Color::Gray)), Span::styled(" - ", Style::default().fg(Color::Gray)),
Span::styled(&cmd.description, style), Span::styled(&cmd.description, style),
])) ]))

View File

@ -13,8 +13,7 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) {
.map(|session| { .map(|session| {
let is_current = app let is_current = app
.current_session_id .current_session_id
.as_ref() .as_ref() == Some(&session.session_id);
.map_or(false, |id| id == &session.session_id);
let archived = session.archived_at.is_some(); let archived = session.archived_at.is_some();
let mut content = if is_current { let mut content = if is_current {

View File

@ -36,12 +36,11 @@ async fn handle_socket(ws: WebSocket, state: Arc<GatewayState>) {
// Task: forward from receiver to WebSocket // Task: forward from receiver to WebSocket
tokio::spawn(async move { tokio::spawn(async move {
while let Some(msg) = receiver.recv().await { while let Some(msg) = receiver.recv().await {
if let Ok(text) = serialize_outbound(&msg) { if let Ok(text) = serialize_outbound(&msg)
if ws_sender.send(WsMessage::Text(text.into())).await.is_err() { && ws_sender.send(WsMessage::Text(text.into())).await.is_err() {
break; break;
} }
} }
}
}); });
// Main loop: receive WebSocket messages and forward to CliChatChannel // Main loop: receive WebSocket messages and forward to CliChatChannel

View File

@ -26,11 +26,10 @@ pub fn init_logging() {
let log_dir = get_default_log_dir(); let log_dir = get_default_log_dir();
// Create log directory if it doesn't exist // Create log directory if it doesn't exist
if !log_dir.exists() { if !log_dir.exists()
if let Err(e) = std::fs::create_dir_all(&log_dir) { && let Err(e) = std::fs::create_dir_all(&log_dir) {
eprintln!("Warning: Failed to create log directory {}: {}", log_dir.display(), e); eprintln!("Warning: Failed to create log directory {}: {}", log_dir.display(), e);
} }
}
// Create file appender with daily rotation // Create file appender with daily rotation
let file_appender = RollingFileAppender::new( let file_appender = RollingFileAppender::new(

View File

@ -11,11 +11,10 @@ use std::sync::Arc;
use crate::storage::Storage; use crate::storage::Storage;
fn convert_content_blocks(blocks: &[ContentBlock]) -> Value { fn convert_content_blocks(blocks: &[ContentBlock]) -> Value {
if blocks.len() == 1 { if blocks.len() == 1
if let ContentBlock::Text { text } = &blocks[0] { && let ContentBlock::Text { text } = &blocks[0] {
return Value::String(text.clone()); return Value::String(text.clone());
} }
}
Value::Array(blocks.iter().map(|b| match b { Value::Array(blocks.iter().map(|b| match b {
ContentBlock::Text { text } => json!({ "type": "text", "text": text }), ContentBlock::Text { text } => json!({ "type": "text", "text": text }),
ContentBlock::ImageUrl { image_url } => { ContentBlock::ImageUrl { image_url } => {
@ -77,7 +76,7 @@ impl OpenAIProvider {
"tool_call_id": m.tool_call_id, "tool_call_id": m.tool_call_id,
"name": m.name, "name": m.name,
}) })
} else if m.role == "assistant" && m.tool_calls.as_ref().map_or(false, |c| !c.is_empty()) { } else if m.role == "assistant" && m.tool_calls.as_ref().is_some_and(|c| !c.is_empty()) {
json!({ json!({
"role": m.role, "role": m.role,
"content": convert_content_blocks(&m.content), "content": convert_content_blocks(&m.content),
@ -187,8 +186,8 @@ impl LLMProvider for OpenAIProvider {
for (i, msg) in msgs.iter().enumerate() { for (i, msg) in msgs.iter().enumerate() {
if let Some(content) = msg.get("content").and_then(|c| c.as_array()) { if let Some(content) = msg.get("content").and_then(|c| c.as_array()) {
for (j, item) in content.iter().enumerate() { for (j, item) in content.iter().enumerate() {
if item.get("type").and_then(|t| t.as_str()) == Some("image_url") { if item.get("type").and_then(|t| t.as_str()) == Some("image_url")
if let Some(url_str) = item.get("image_url").and_then(|u| u.get("url")).and_then(|v| v.as_str()) { && let Some(url_str) = item.get("image_url").and_then(|u| u.get("url")).and_then(|v| v.as_str()) {
let prefix: String = url_str.chars().take(20).collect(); let prefix: String = url_str.chars().take(20).collect();
tracing::debug!(msg_idx = i, item_idx = j, image_prefix = %prefix, image_url_len = %url_str.len(), "Image in LLM request (first 20 bytes shown)"); tracing::debug!(msg_idx = i, item_idx = j, image_prefix = %prefix, image_url_len = %url_str.len(), "Image in LLM request (first 20 bytes shown)");
} }
@ -197,7 +196,6 @@ impl LLMProvider for OpenAIProvider {
} }
} }
} }
}
let mut req_builder = self let mut req_builder = self
.client .client

View File

@ -22,7 +22,7 @@ use crate::agent::{AgentLoop, AgentError, ContextCompressor};
use crate::agent::system_prompt::build_system_prompt; use crate::agent::system_prompt::build_system_prompt;
use crate::agent::context_compressor::ContextCompressionConfig; use crate::agent::context_compressor::ContextCompressionConfig;
use crate::providers::{create_provider, LLMProvider}; use crate::providers::{create_provider, LLMProvider};
use crate::session::session_id::{UnifiedSessionId, DEFAULT_DIALOG_ID}; use crate::session::session_id::UnifiedSessionId;
use crate::session::events::DialogInfo; use crate::session::events::DialogInfo;
use crate::skills::SkillsLoader; use crate::skills::SkillsLoader;
use crate::tools::{ToolRegistry, create_default_tools}; use crate::tools::{ToolRegistry, create_default_tools};
@ -193,8 +193,8 @@ impl Session {
self.seq_counter += 1; self.seq_counter += 1;
// Persist to Storage // Persist to Storage
if persist { if persist
if let Some(ref storage) = self.storage { && let Some(ref storage) = self.storage {
let msg_meta = crate::storage::message::MessageMeta { let msg_meta = crate::storage::message::MessageMeta {
id: message.id.clone(), id: message.id.clone(),
session_id: self.id.to_string(), session_id: self.id.to_string(),
@ -214,7 +214,6 @@ impl Session {
}; };
storage.append_message_with_retry(&self.id.to_string(), &msg_meta).await?; storage.append_message_with_retry(&self.id.to_string(), &msg_meta).await?;
} }
}
// Update in-memory state // Update in-memory state
self.messages.push(message); self.messages.push(message);
@ -411,7 +410,7 @@ impl Session {
/// 将当前 session 导出为 markdown 文档并保存到文件 /// 将当前 session 导出为 markdown 文档并保存到文件
pub fn dump_to_file(&self, system_prompt: &str) -> std::io::Result<String> { pub fn dump_to_file(&self, system_prompt: &str) -> std::io::Result<String> {
use chrono::{DateTime, Local}; use chrono::Local;
use std::fs; use std::fs;
use std::io::Write; use std::io::Write;
@ -440,7 +439,7 @@ impl Session {
let now = Local::now().format("%Y-%m-%d %H:%M:%S"); let now = Local::now().format("%Y-%m-%d %H:%M:%S");
let mut md = String::new(); let mut md = String::new();
md.push_str(&format!("# Session Dump\n\n")); md.push_str(&"# Session Dump\n\n".to_string());
md.push_str(&format!("- **Session ID**: `{}`\n", self.id)); md.push_str(&format!("- **Session ID**: `{}`\n", self.id));
md.push_str(&format!("- **Channel**: `{}`\n", self.id.channel)); md.push_str(&format!("- **Channel**: `{}`\n", self.id.channel));
md.push_str(&format!("- **Chat ID**: `{}`\n", self.id.chat_id)); md.push_str(&format!("- **Chat ID**: `{}`\n", self.id.chat_id));
@ -473,7 +472,7 @@ impl Session {
md.push_str("```\n"); md.push_str("```\n");
if let Some(ref tool_calls) = msg.tool_calls { if let Some(ref tool_calls) = msg.tool_calls {
md.push_str(&format!("[Tool Calls]\n")); md.push_str(&"[Tool Calls]\n".to_string());
for tc in tool_calls { for tc in tool_calls {
md.push_str(&format!("- {}: {:?}\n", tc.name, tc.arguments)); md.push_str(&format!("- {}: {:?}\n", tc.name, tc.arguments));
} }
@ -599,11 +598,10 @@ fn repair_tool_call_chains(messages: &mut Vec<ChatMessage>) {
let mut j = i + 1; let mut j = i + 1;
while j < messages.len() && found < expected_count { while j < messages.len() && found < expected_count {
if messages[j].role == "tool" { if messages[j].role == "tool" {
if let Some(ref tc_id) = messages[j].tool_call_id { if let Some(ref tc_id) = messages[j].tool_call_id
if expected_ids.contains(tc_id.as_str()) { && expected_ids.contains(tc_id.as_str()) {
found += 1; found += 1;
} }
}
} else if messages[j].role == "user" || messages[j].role == "assistant" { } else if messages[j].role == "user" || messages[j].role == "assistant" {
// Next user/assistant message — stop scanning, chain is broken // Next user/assistant message — stop scanning, chain is broken
break; break;
@ -1031,7 +1029,7 @@ impl SessionManager {
self.tools.clone(), self.tools.clone(),
Some(self.storage.clone()), Some(self.storage.clone()),
String::new(), String::new(),
format!("新对话"), "新对话".to_string(),
self.memory_manager.clone(), self.memory_manager.clone(),
).await?; ).await?;
@ -1165,15 +1163,12 @@ impl SessionManager {
}; };
if let Some(ref current_id) = current_id { if let Some(ref current_id) = current_id {
match self.storage.get_session(current_id).await { if let Ok(_) = self.storage.get_session(current_id).await {
Ok(_) => {
let parts: Vec<&str> = current_id.split(':').collect(); let parts: Vec<&str> = current_id.split(':').collect();
if parts.len() == 3 { if parts.len() == 3 {
return Ok(UnifiedSessionId::new(channel, chat_id, parts[2])); return Ok(UnifiedSessionId::new(channel, chat_id, parts[2]));
} }
} }
Err(_) => {}
}
} }
let ttl_millis = self.inner.lock().await.session_ttl.as_millis() as i64; let ttl_millis = self.inner.lock().await.session_ttl.as_millis() as i64;
@ -1310,7 +1305,7 @@ impl SessionManager {
let skills_prompt = self.skills_loader.build_skills_prompt(); let skills_prompt = self.skills_loader.build_skills_prompt();
// Fetch memory context // Fetch memory context
let memory_context = match self.memory_manager.recall(&content, 5, Some(crate::memory::MemoryCategory::Knowledge)).await { let memory_context = match self.memory_manager.recall(content, 5, Some(crate::memory::MemoryCategory::Knowledge)).await {
Ok(entries) if !entries.is_empty() => { Ok(entries) if !entries.is_empty() => {
Some(entries.iter() Some(entries.iter()
.map(|e| format!("- {}: {}", e.key, e.content)) .map(|e| format!("- {}: {}", e.key, e.content))
@ -1342,11 +1337,10 @@ impl SessionManager {
} }
// Check if we need to generate a title (after 10 user messages) // Check if we need to generate a title (after 10 user messages)
if session_guard.should_generate_title() { if session_guard.should_generate_title()
if let Err(e) = session_guard.generate_title().await { && let Err(e) = session_guard.generate_title().await {
tracing::warn!("failed to generate title: {}", e); tracing::warn!("failed to generate title: {}", e);
} }
}
result.final_response.content result.final_response.content
}; };
@ -1456,11 +1450,10 @@ impl SessionManager {
.map_err(|e| AgentError::Other(format!("persist error: {}", e)))?; .map_err(|e| AgentError::Other(format!("persist error: {}", e)))?;
} }
if session_guard.should_generate_title() { if session_guard.should_generate_title()
if let Err(e) = session_guard.generate_title().await { && let Err(e) = session_guard.generate_title().await {
tracing::warn!("failed to generate title: {}", e); tracing::warn!("failed to generate title: {}", e);
} }
}
let raw_response = result.final_response.content; let raw_response = result.final_response.content;
let prefix = format!( let prefix = format!(

View File

@ -12,6 +12,7 @@ pub struct Skill {
pub path: Option<PathBuf>, pub path: Option<PathBuf>,
} }
#[derive(Default)]
struct SkillMarkdownMeta { struct SkillMarkdownMeta {
name: Option<String>, name: Option<String>,
description: Option<String>, description: Option<String>,
@ -147,24 +148,21 @@ impl SkillsLoader {
fn get_dir_mtime(dir: &Path) -> Option<SystemTime> { fn get_dir_mtime(dir: &Path) -> Option<SystemTime> {
let mut max_mtime = None; let mut max_mtime = None;
if let Ok(metadata) = std::fs::metadata(dir) { if let Ok(metadata) = std::fs::metadata(dir)
if let Ok(mtime) = metadata.modified() { && let Ok(mtime) = metadata.modified() {
max_mtime = Some(mtime); max_mtime = Some(mtime);
} }
}
if let Ok(entries) = std::fs::read_dir(dir) { if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.flatten() { for entry in entries.flatten() {
let path = entry.path(); let path = entry.path();
if let Ok(metadata) = std::fs::metadata(&path) { if let Ok(metadata) = std::fs::metadata(&path)
if let Ok(mtime) = metadata.modified() { && let Ok(mtime) = metadata.modified()
if max_mtime.map_or(true, |current| mtime > current) { && max_mtime.is_none_or(|current| mtime > current) {
max_mtime = Some(mtime); max_mtime = Some(mtime);
} }
} }
} }
}
}
max_mtime max_mtime
} }
@ -424,15 +422,6 @@ impl Default for SkillsLoader {
} }
} }
impl Default for SkillMarkdownMeta {
fn default() -> Self {
Self {
name: None,
description: None,
always: None,
}
}
}
/// Extract first non-empty, non-heading line as description /// Extract first non-empty, non-heading line as description
fn extract_description(content: &str) -> String { fn extract_description(content: &str) -> String {

View File

@ -96,7 +96,7 @@ impl super::Storage {
.cut(query, true) .cut(query, true)
.into_iter() .into_iter()
.filter(|w| w.len() > 1 || w.bytes().any(|b| b > 127)) .filter(|w| w.len() > 1 || w.bytes().any(|b| b > 127))
.map(|w| w.replace('%', "").replace('_', "")) .map(|w| w.replace(['%', '_'], ""))
.collect(); .collect();
if !terms.is_empty() { if !terms.is_empty() {
@ -159,7 +159,7 @@ impl super::Storage {
.cut(q, true) .cut(q, true)
.into_iter() .into_iter()
.filter(|w| w.len() > 1 || w.bytes().any(|b| b > 127)) .filter(|w| w.len() > 1 || w.bytes().any(|b| b > 127))
.map(|w| w.replace('%', "").replace('_', "")) .map(|w| w.replace(['%', '_'], ""))
.collect(); .collect();
if terms.is_empty() { if terms.is_empty() {

View File

@ -144,7 +144,7 @@ impl Tool for BashTool {
let cwd = self let cwd = self
.working_dir .working_dir
.as_ref() .as_ref()
.map(|d| Path::new(d)) .map(Path::new)
.unwrap_or_else(|| Path::new(".")); .unwrap_or_else(|| Path::new("."));
let result = timeout( let result = timeout(
@ -217,7 +217,7 @@ impl BashTool {
let stderr_str = String::from_utf8_lossy(&stderr); let stderr_str = String::from_utf8_lossy(&stderr);
if !stderr_str.trim().is_empty() { if !stderr_str.trim().is_empty() {
if !output.is_empty() { if !output.is_empty() {
output.push_str("\n"); output.push('\n');
} }
output.push_str("STDERR:\n"); output.push_str("STDERR:\n");
output.push_str(&stderr_str); output.push_str(&stderr_str);

View File

@ -1,10 +1,8 @@
use std::io::Read;
use std::path::Path; use std::path::Path;
use async_trait::async_trait; use async_trait::async_trait;
use serde_json::json; use serde_json::json;
use crate::bus::message::ContentBlock;
use crate::tools::traits::{Tool, ToolResult}; use crate::tools::traits::{Tool, ToolResult};
const MAX_CHARS: usize = 128_000; const MAX_CHARS: usize = 128_000;

View File

@ -113,17 +113,15 @@ impl Tool for FileWriteTool {
}; };
// Create parent directories if needed // Create parent directories if needed
if let Some(parent) = resolved.parent() { if let Some(parent) = resolved.parent()
if !parent.exists() { && !parent.exists()
if let Err(e) = std::fs::create_dir_all(parent) { && let Err(e) = std::fs::create_dir_all(parent) {
return Ok(ToolResult { return Ok(ToolResult {
success: false, success: false,
output: String::new(), output: String::new(),
error: Some(format!("Failed to create parent directory: {}", e)), error: Some(format!("Failed to create parent directory: {}", e)),
}); });
} }
}
}
match std::fs::write(&resolved, content) { match std::fs::write(&resolved, content) {
Ok(_) => Ok(ToolResult { Ok(_) => Ok(ToolResult {

View File

@ -78,17 +78,15 @@ impl HttpRequestTool {
if let Some(obj) = headers.as_object() { if let Some(obj) = headers.as_object() {
for (key, value) in obj { for (key, value) in obj {
if let Some(str_val) = value.as_str() { if let Some(str_val) = value.as_str()
if let Ok(name) = reqwest::header::HeaderName::from_bytes(key.as_bytes()) { && let Ok(name) = reqwest::header::HeaderName::from_bytes(key.as_bytes())
if let Ok(val) = && let Ok(val) =
reqwest::header::HeaderValue::from_str(str_val) reqwest::header::HeaderValue::from_str(str_val)
{ {
header_map.insert(name, val); header_map.insert(name, val);
} }
} }
} }
}
}
header_map header_map
} }

View File

@ -114,11 +114,10 @@ impl SchemaCleanr {
anyhow::bail!("Schema missing required 'type' field"); anyhow::bail!("Schema missing required 'type' field");
} }
if let Some(Value::String(t)) = obj.get("type") { if let Some(Value::String(t)) = obj.get("type")
if t == "object" && !obj.contains_key("properties") { && t == "object" && !obj.contains_key("properties") {
tracing::warn!("Object schema without 'properties' field may cause issues"); tracing::warn!("Object schema without 'properties' field may cause issues");
} }
}
Ok(()) Ok(())
} }
@ -173,11 +172,10 @@ impl SchemaCleanr {
} }
// Handle anyOf/oneOf simplification // Handle anyOf/oneOf simplification
if obj.contains_key("anyOf") || obj.contains_key("oneOf") { if (obj.contains_key("anyOf") || obj.contains_key("oneOf"))
if let Some(simplified) = Self::try_simplify_union(&obj, defs, strategy, ref_stack) { && let Some(simplified) = Self::try_simplify_union(&obj, defs, strategy, ref_stack) {
return simplified; return simplified;
} }
}
// Build cleaned object // Build cleaned object
let mut cleaned = Map::new(); let mut cleaned = Map::new();
@ -244,14 +242,13 @@ impl SchemaCleanr {
return Self::preserve_meta(obj, Value::Object(Map::new())); return Self::preserve_meta(obj, Value::Object(Map::new()));
} }
if let Some(def_name) = Self::parse_local_ref(ref_value) { if let Some(def_name) = Self::parse_local_ref(ref_value)
if let Some(definition) = defs.get(def_name.as_str()) { && let Some(definition) = defs.get(def_name.as_str()) {
ref_stack.insert(ref_value.to_string()); ref_stack.insert(ref_value.to_string());
let cleaned = Self::clean_with_defs(definition.clone(), defs, strategy, ref_stack); let cleaned = Self::clean_with_defs(definition.clone(), defs, strategy, ref_stack);
ref_stack.remove(ref_value); ref_stack.remove(ref_value);
return Self::preserve_meta(obj, cleaned); return Self::preserve_meta(obj, cleaned);
} }
}
tracing::warn!("Cannot resolve $ref: {}", ref_value); tracing::warn!("Cannot resolve $ref: {}", ref_value);
Self::preserve_meta(obj, Value::Object(Map::new())) Self::preserve_meta(obj, Value::Object(Map::new()))
@ -342,17 +339,15 @@ impl SchemaCleanr {
if let Some(Value::Null) = obj.get("const") { if let Some(Value::Null) = obj.get("const") {
return true; return true;
} }
if let Some(Value::Array(arr)) = obj.get("enum") { if let Some(Value::Array(arr)) = obj.get("enum")
if arr.len() == 1 && matches!(arr[0], Value::Null) { && arr.len() == 1 && matches!(arr[0], Value::Null) {
return true; return true;
} }
} if let Some(Value::String(t)) = obj.get("type")
if let Some(Value::String(t)) = obj.get("type") { && t == "null" {
if t == "null" {
return true; return true;
} }
} }
}
false false
} }