From 06cf92a21dd475b28b77c54cbcddfaeba49d829c Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 2 Jun 2026 22:54:50 -0400 Subject: [PATCH] refactor: move system prompt filter to goose-providers Signed-off-by: Jack Amadeo --- crates/goose-providers/src/text.rs | 50 +++++++++++++++++++++++++++++ crates/goose/src/providers/utils.rs | 26 +-------------- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/crates/goose-providers/src/text.rs b/crates/goose-providers/src/text.rs index 9a2e0fb48ea2..d1ece326b0fe 100644 --- a/crates/goose-providers/src/text.rs +++ b/crates/goose-providers/src/text.rs @@ -1,6 +1,31 @@ use once_cell::sync::Lazy; use regex::Regex; +pub fn filter_extensions_from_system_prompt(system: &str) -> String { + let Some(extensions_start) = system.find("# Extensions") else { + return system.to_string(); + }; + + let Some(after_extensions) = system.get(extensions_start + 1..) else { + return system.to_string(); + }; + + if let Some(next_section_pos) = after_extensions.find("\n# ") { + let Some(before) = system.get(..extensions_start) else { + return system.to_string(); + }; + let Some(after) = system.get(extensions_start + next_section_pos + 1..) else { + return system.to_string(); + }; + format!("{}{}", before.trim_end(), after) + } else { + system + .get(..extensions_start) + .map(|text| text.trim_end().to_string()) + .unwrap_or_else(|| system.to_string()) + } +} + #[derive(Debug, Default, PartialEq, Eq)] pub struct FilterOut { pub content: String, @@ -270,6 +295,31 @@ fn contains_unquoted_gt(text: &str) -> bool { mod tests { use super::*; + #[test] + fn filters_extensions_section_from_system_prompt() { + let system = + "# Instructions\nBe helpful.\n\n# Extensions\nTool details.\n\n# Other\nKeep this."; + assert_eq!( + filter_extensions_from_system_prompt(system), + "# Instructions\nBe helpful.\n# Other\nKeep this." + ); + } + + #[test] + fn filters_trailing_extensions_section_from_system_prompt() { + let system = "# Instructions\nBe helpful.\n\n# Extensions\nTool details."; + assert_eq!( + filter_extensions_from_system_prompt(system), + "# Instructions\nBe helpful." + ); + } + + #[test] + fn preserves_system_prompt_without_extensions_section() { + let system = "# Instructions\nBe helpful."; + assert_eq!(filter_extensions_from_system_prompt(system), system); + } + #[test] fn split_think_blocks_extracts_inline_reasoning() { assert_eq!( diff --git a/crates/goose/src/providers/utils.rs b/crates/goose/src/providers/utils.rs index 530ceda2d8bc..e52993febb16 100644 --- a/crates/goose/src/providers/utils.rs +++ b/crates/goose/src/providers/utils.rs @@ -9,6 +9,7 @@ pub use goose_providers::image::ImageFormat; pub use goose_providers::json::{ get_model, json_escape_control_chars_in_string, safely_parse_json, unescape_json_values, }; +pub use goose_providers::text::filter_extensions_from_system_prompt; use goose_types::ModelConfig; pub use goose_types::{ extract_reasoning_effort, is_openai_responses_model, openai_reasoning_effort_for_thinking, @@ -39,31 +40,6 @@ pub fn convert_image(image: &ImageContent, image_format: &ImageFormat) -> Value } } -pub fn filter_extensions_from_system_prompt(system: &str) -> String { - let Some(extensions_start) = system.find("# Extensions") else { - return system.to_string(); - }; - - let Some(after_extensions) = system.get(extensions_start + 1..) else { - return system.to_string(); - }; - - if let Some(next_section_pos) = after_extensions.find("\n# ") { - let Some(before) = system.get(..extensions_start) else { - return system.to_string(); - }; - let Some(after) = system.get(extensions_start + next_section_pos + 1..) else { - return system.to_string(); - }; - format!("{}{}", before.trim_end(), after) - } else { - system - .get(..extensions_start) - .map(|s| s.trim_end().to_string()) - .unwrap_or_else(|| system.to_string()) - } -} - /// Check if a file is actually an image by examining its magic bytes fn is_image_file(path: &Path) -> bool { if let Ok(mut file) = std::fs::File::open(path) {