Disallow subagents with no extensions (#5825)
This commit is contained in:
@@ -640,6 +640,28 @@ impl Agent {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn subagents_enabled(&self) -> bool {
|
||||||
|
let config = crate::config::Config::global();
|
||||||
|
let is_autonomous = config.get_goose_mode().unwrap_or(GooseMode::Auto) == GooseMode::Auto;
|
||||||
|
if !is_autonomous {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if self
|
||||||
|
.provider()
|
||||||
|
.await
|
||||||
|
.map(|provider| provider.get_active_model_name().starts_with("gemini"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
!self
|
||||||
|
.extension_manager
|
||||||
|
.list_extensions()
|
||||||
|
.await
|
||||||
|
.map(|ext| ext.is_empty())
|
||||||
|
.unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn list_tools(&self, extension_name: Option<String>) -> Vec<Tool> {
|
pub async fn list_tools(&self, extension_name: Option<String>) -> Vec<Tool> {
|
||||||
let mut prefixed_tools = self
|
let mut prefixed_tools = self
|
||||||
.extension_manager
|
.extension_manager
|
||||||
@@ -647,8 +669,9 @@ impl Agent {
|
|||||||
.await
|
.await
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let subagents_enabled = self.subagents_enabled().await;
|
||||||
if extension_name.is_none() || extension_name.as_deref() == Some("platform") {
|
if extension_name.is_none() || extension_name.as_deref() == Some("platform") {
|
||||||
prefixed_tools.extend([platform_tools::manage_schedule_tool()]);
|
prefixed_tools.push(platform_tools::manage_schedule_tool());
|
||||||
}
|
}
|
||||||
|
|
||||||
if extension_name.is_none() {
|
if extension_name.is_none() {
|
||||||
@@ -656,10 +679,11 @@ impl Agent {
|
|||||||
prefixed_tools.push(final_output_tool.tool());
|
prefixed_tools.push(final_output_tool.tool());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the unified subagent tool
|
if subagents_enabled {
|
||||||
let sub_recipes = self.sub_recipes.lock().await;
|
let sub_recipes = self.sub_recipes.lock().await;
|
||||||
let sub_recipes_vec: Vec<_> = sub_recipes.values().cloned().collect();
|
let sub_recipes_vec: Vec<_> = sub_recipes.values().cloned().collect();
|
||||||
prefixed_tools.push(create_subagent_tool(&sub_recipes_vec));
|
prefixed_tools.push(create_subagent_tool(&sub_recipes_vec));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prefixed_tools
|
prefixed_tools
|
||||||
@@ -1374,7 +1398,7 @@ impl Agent {
|
|||||||
|
|
||||||
let prompt_manager = self.prompt_manager.lock().await;
|
let prompt_manager = self.prompt_manager.lock().await;
|
||||||
let system_prompt = prompt_manager
|
let system_prompt = prompt_manager
|
||||||
.builder(model_name)
|
.builder()
|
||||||
.with_extensions(extensions_info.into_iter())
|
.with_extensions(extensions_info.into_iter())
|
||||||
.with_frontend_instructions(self.frontend_instructions.lock().await.clone())
|
.with_frontend_instructions(self.frontend_instructions.lock().await.clone())
|
||||||
.with_extension_and_tool_counts(extension_count, tool_count)
|
.with_extension_and_tool_counts(extension_count, tool_count)
|
||||||
@@ -1594,7 +1618,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let prompt_manager = agent.prompt_manager.lock().await;
|
let prompt_manager = agent.prompt_manager.lock().await;
|
||||||
let system_prompt = prompt_manager.builder("gpt-4o").build();
|
let system_prompt = prompt_manager.builder().build();
|
||||||
|
|
||||||
let final_output_tool_ref = agent.final_output_tool.lock().await;
|
let final_output_tool_ref = agent.final_output_tool.lock().await;
|
||||||
let final_output_tool_system_prompt =
|
let final_output_tool_system_prompt =
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use crate::agents::extension::ExtensionInfo;
|
use crate::agents::extension::ExtensionInfo;
|
||||||
use crate::agents::router_tools::llm_search_tool_prompt;
|
use crate::agents::router_tools::llm_search_tool_prompt;
|
||||||
use crate::agents::subagent_tool::should_enable_subagents;
|
|
||||||
use crate::hints::load_hints::{load_hint_files, AGENTS_MD_FILENAME, GOOSE_HINTS_FILENAME};
|
use crate::hints::load_hints::{load_hint_files, AGENTS_MD_FILENAME, GOOSE_HINTS_FILENAME};
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, GooseMode},
|
config::{Config, GooseMode},
|
||||||
@@ -47,13 +46,13 @@ struct SystemPromptContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct SystemPromptBuilder<'a, M> {
|
pub struct SystemPromptBuilder<'a, M> {
|
||||||
model_name: String,
|
|
||||||
manager: &'a M,
|
manager: &'a M,
|
||||||
|
|
||||||
extensions_info: Vec<ExtensionInfo>,
|
extensions_info: Vec<ExtensionInfo>,
|
||||||
frontend_instructions: Option<String>,
|
frontend_instructions: Option<String>,
|
||||||
extension_tool_count: Option<(usize, usize)>,
|
extension_tool_count: Option<(usize, usize)>,
|
||||||
router_enabled: bool,
|
router_enabled: bool,
|
||||||
|
subagents_enabled: bool,
|
||||||
hints: Option<String>,
|
hints: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +115,11 @@ impl<'a> SystemPromptBuilder<'a, PromptManager> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_enable_subagents(mut self, subagents_enabled: bool) -> Self {
|
||||||
|
self.subagents_enabled = subagents_enabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> String {
|
pub fn build(self) -> String {
|
||||||
let mut extensions_info = self.extensions_info;
|
let mut extensions_info = self.extensions_info;
|
||||||
|
|
||||||
@@ -152,7 +156,7 @@ impl<'a> SystemPromptBuilder<'a, PromptManager> {
|
|||||||
extension_tool_limits,
|
extension_tool_limits,
|
||||||
goose_mode,
|
goose_mode,
|
||||||
is_autonomous: goose_mode == GooseMode::Auto,
|
is_autonomous: goose_mode == GooseMode::Auto,
|
||||||
enable_subagents: should_enable_subagents(self.model_name.as_str()),
|
enable_subagents: self.subagents_enabled,
|
||||||
max_extensions: MAX_EXTENSIONS,
|
max_extensions: MAX_EXTENSIONS,
|
||||||
max_tools: MAX_TOOLS,
|
max_tools: MAX_TOOLS,
|
||||||
};
|
};
|
||||||
@@ -228,15 +232,15 @@ impl PromptManager {
|
|||||||
self.system_prompt_override = Some(template);
|
self.system_prompt_override = Some(template);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn builder<'a>(&'a self, model_name: &str) -> SystemPromptBuilder<'a, Self> {
|
pub fn builder<'a>(&'a self) -> SystemPromptBuilder<'a, Self> {
|
||||||
SystemPromptBuilder {
|
SystemPromptBuilder {
|
||||||
model_name: model_name.to_string(),
|
|
||||||
manager: self,
|
manager: self,
|
||||||
|
|
||||||
extensions_info: vec![],
|
extensions_info: vec![],
|
||||||
frontend_instructions: None,
|
frontend_instructions: None,
|
||||||
extension_tool_count: None,
|
extension_tool_count: None,
|
||||||
router_enabled: false,
|
router_enabled: false,
|
||||||
|
subagents_enabled: false,
|
||||||
hints: None,
|
hints: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,7 +264,7 @@ mod tests {
|
|||||||
let malicious_override = "System prompt\u{E0041}\u{E0042}\u{E0043}with hidden text";
|
let malicious_override = "System prompt\u{E0041}\u{E0042}\u{E0043}with hidden text";
|
||||||
manager.set_system_prompt_override(malicious_override.to_string());
|
manager.set_system_prompt_override(malicious_override.to_string());
|
||||||
|
|
||||||
let result = manager.builder("gpt-4o").build();
|
let result = manager.builder().build();
|
||||||
|
|
||||||
assert!(!result.contains('\u{E0041}'));
|
assert!(!result.contains('\u{E0041}'));
|
||||||
assert!(!result.contains('\u{E0042}'));
|
assert!(!result.contains('\u{E0042}'));
|
||||||
@@ -275,7 +279,7 @@ mod tests {
|
|||||||
let malicious_extra = "Extra instruction\u{E0041}\u{E0042}\u{E0043}hidden";
|
let malicious_extra = "Extra instruction\u{E0041}\u{E0042}\u{E0043}hidden";
|
||||||
manager.add_system_prompt_extra(malicious_extra.to_string());
|
manager.add_system_prompt_extra(malicious_extra.to_string());
|
||||||
|
|
||||||
let result = manager.builder("gpt-4o").build();
|
let result = manager.builder().build();
|
||||||
|
|
||||||
assert!(!result.contains('\u{E0041}'));
|
assert!(!result.contains('\u{E0041}'));
|
||||||
assert!(!result.contains('\u{E0042}'));
|
assert!(!result.contains('\u{E0042}'));
|
||||||
@@ -291,7 +295,7 @@ mod tests {
|
|||||||
manager.add_system_prompt_extra("Second\u{E0042}instruction".to_string());
|
manager.add_system_prompt_extra("Second\u{E0042}instruction".to_string());
|
||||||
manager.add_system_prompt_extra("Third\u{E0043}instruction".to_string());
|
manager.add_system_prompt_extra("Third\u{E0043}instruction".to_string());
|
||||||
|
|
||||||
let result = manager.builder("gpt-4o").build();
|
let result = manager.builder().build();
|
||||||
|
|
||||||
assert!(!result.contains('\u{E0041}'));
|
assert!(!result.contains('\u{E0041}'));
|
||||||
assert!(!result.contains('\u{E0042}'));
|
assert!(!result.contains('\u{E0042}'));
|
||||||
@@ -307,7 +311,7 @@ mod tests {
|
|||||||
let legitimate_unicode = "Instruction with 世界 and 🌍 emojis";
|
let legitimate_unicode = "Instruction with 世界 and 🌍 emojis";
|
||||||
manager.add_system_prompt_extra(legitimate_unicode.to_string());
|
manager.add_system_prompt_extra(legitimate_unicode.to_string());
|
||||||
|
|
||||||
let result = manager.builder("gpt-4o").build();
|
let result = manager.builder().build();
|
||||||
|
|
||||||
assert!(result.contains("世界"));
|
assert!(result.contains("世界"));
|
||||||
assert!(result.contains("🌍"));
|
assert!(result.contains("🌍"));
|
||||||
@@ -325,7 +329,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let result = manager
|
let result = manager
|
||||||
.builder("gpt-4o")
|
.builder()
|
||||||
.with_extension(malicious_extension_info)
|
.with_extension(malicious_extension_info)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -340,7 +344,7 @@ mod tests {
|
|||||||
fn test_basic() {
|
fn test_basic() {
|
||||||
let manager = PromptManager::with_timestamp(DateTime::<Utc>::from_timestamp(0, 0).unwrap());
|
let manager = PromptManager::with_timestamp(DateTime::<Utc>::from_timestamp(0, 0).unwrap());
|
||||||
|
|
||||||
let system_prompt = manager.builder("gpt-4o").build();
|
let system_prompt = manager.builder().build();
|
||||||
|
|
||||||
assert_snapshot!(system_prompt)
|
assert_snapshot!(system_prompt)
|
||||||
}
|
}
|
||||||
@@ -350,7 +354,7 @@ mod tests {
|
|||||||
let manager = PromptManager::with_timestamp(DateTime::<Utc>::from_timestamp(0, 0).unwrap());
|
let manager = PromptManager::with_timestamp(DateTime::<Utc>::from_timestamp(0, 0).unwrap());
|
||||||
|
|
||||||
let system_prompt = manager
|
let system_prompt = manager
|
||||||
.builder("gpt-4o")
|
.builder()
|
||||||
.with_extension(ExtensionInfo::new(
|
.with_extension(ExtensionInfo::new(
|
||||||
"test",
|
"test",
|
||||||
"how to use this extension",
|
"how to use this extension",
|
||||||
@@ -367,7 +371,7 @@ mod tests {
|
|||||||
let manager = PromptManager::with_timestamp(DateTime::<Utc>::from_timestamp(0, 0).unwrap());
|
let manager = PromptManager::with_timestamp(DateTime::<Utc>::from_timestamp(0, 0).unwrap());
|
||||||
|
|
||||||
let system_prompt = manager
|
let system_prompt = manager
|
||||||
.builder("gpt-4o")
|
.builder()
|
||||||
.with_extension(ExtensionInfo::new(
|
.with_extension(ExtensionInfo::new(
|
||||||
"extension_A",
|
"extension_A",
|
||||||
"<instructions on how to use extension A>",
|
"<instructions on how to use extension A>",
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ use crate::providers::toolshim::{
|
|||||||
modify_system_prompt_for_tool_json, OllamaInterpreter,
|
modify_system_prompt_for_tool_json, OllamaInterpreter,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::agents::subagent_tool::should_enable_subagents;
|
|
||||||
use crate::session::SessionManager;
|
use crate::session::SessionManager;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::session::SessionType;
|
use crate::session::SessionType;
|
||||||
@@ -122,12 +121,6 @@ impl Agent {
|
|||||||
// If router is disabled and no tools were returned, fall back to regular tools
|
// If router is disabled and no tools were returned, fall back to regular tools
|
||||||
if !router_enabled && tools.is_empty() {
|
if !router_enabled && tools.is_empty() {
|
||||||
tools = self.list_tools(None).await;
|
tools = self.list_tools(None).await;
|
||||||
let provider = self.provider().await?;
|
|
||||||
let model_name = provider.get_model_config().model_name;
|
|
||||||
|
|
||||||
if !should_enable_subagents(&model_name) {
|
|
||||||
tools.retain(|tool| tool.name != crate::agents::subagent_tool::SUBAGENT_TOOL_NAME);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add frontend tools
|
// Add frontend tools
|
||||||
@@ -149,16 +142,16 @@ impl Agent {
|
|||||||
// Get model name from provider
|
// Get model name from provider
|
||||||
let provider = self.provider().await?;
|
let provider = self.provider().await?;
|
||||||
let model_config = provider.get_model_config();
|
let model_config = provider.get_model_config();
|
||||||
let model_name = &model_config.model_name;
|
|
||||||
|
|
||||||
let prompt_manager = self.prompt_manager.lock().await;
|
let prompt_manager = self.prompt_manager.lock().await;
|
||||||
let mut system_prompt = prompt_manager
|
let mut system_prompt = prompt_manager
|
||||||
.builder(model_name)
|
.builder()
|
||||||
.with_extensions(extensions_info.into_iter())
|
.with_extensions(extensions_info.into_iter())
|
||||||
.with_frontend_instructions(self.frontend_instructions.lock().await.clone())
|
.with_frontend_instructions(self.frontend_instructions.lock().await.clone())
|
||||||
.with_extension_and_tool_counts(extension_count, tool_count)
|
.with_extension_and_tool_counts(extension_count, tool_count)
|
||||||
.with_router_enabled(router_enabled)
|
.with_router_enabled(router_enabled)
|
||||||
.with_hints(working_dir)
|
.with_hints(working_dir)
|
||||||
|
.with_enable_subagents(self.subagents_enabled().await)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Handle toolshim if enabled
|
// Handle toolshim if enabled
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ use tokio_util::sync::CancellationToken;
|
|||||||
use crate::agents::subagent_handler::run_complete_subagent_task;
|
use crate::agents::subagent_handler::run_complete_subagent_task;
|
||||||
use crate::agents::subagent_task_config::TaskConfig;
|
use crate::agents::subagent_task_config::TaskConfig;
|
||||||
use crate::agents::tool_execution::ToolCallResult;
|
use crate::agents::tool_execution::ToolCallResult;
|
||||||
use crate::config::GooseMode;
|
|
||||||
use crate::providers;
|
use crate::providers;
|
||||||
use crate::recipe::build_recipe::build_recipe_from_template;
|
use crate::recipe::build_recipe::build_recipe_from_template;
|
||||||
use crate::recipe::local_recipes::load_local_recipe_file;
|
use crate::recipe::local_recipes::load_local_recipe_file;
|
||||||
@@ -440,18 +439,6 @@ async fn apply_settings_overrides(
|
|||||||
Ok(task_config)
|
Ok(task_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_enable_subagents(model_name: &str) -> bool {
|
|
||||||
let config = crate::config::Config::global();
|
|
||||||
let is_autonomous = config.get_goose_mode().unwrap_or(GooseMode::Auto) == GooseMode::Auto;
|
|
||||||
if !is_autonomous {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if model_name.starts_with("gemini") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ echo ""
|
|||||||
# TEST 2: Auto Compaction
|
# TEST 2: Auto Compaction
|
||||||
# ==================================================
|
# ==================================================
|
||||||
echo "---------------------------------------------------"
|
echo "---------------------------------------------------"
|
||||||
echo "TEST 2: Auto Compaction via threshold (0.01)"
|
echo "TEST 2: Auto Compaction via threshold (0.005)"
|
||||||
echo "---------------------------------------------------"
|
echo "---------------------------------------------------"
|
||||||
|
|
||||||
TESTDIR=$(mktemp -d)
|
TESTDIR=$(mktemp -d)
|
||||||
@@ -159,8 +159,8 @@ echo "test content" > "$TESTDIR/test.txt"
|
|||||||
echo "Test directory: $TESTDIR"
|
echo "Test directory: $TESTDIR"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Set auto-compact threshold very low (1%) to trigger it quickly
|
# Set auto-compact threshold very low (.5%) to trigger it quickly
|
||||||
export GOOSE_AUTO_COMPACT_THRESHOLD=0.01
|
export GOOSE_AUTO_COMPACT_THRESHOLD=0.005
|
||||||
|
|
||||||
OUTPUT=$(mktemp)
|
OUTPUT=$(mktemp)
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ else
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "✗ FAILED: Auto compaction was not triggered"
|
echo "✗ FAILED: Auto compaction was not triggered"
|
||||||
echo " Expected to see auto-compact messages with threshold of 0.01"
|
echo " Expected to see auto-compact messages with threshold of 0.005"
|
||||||
RESULTS+=("✗ Auto Compaction")
|
RESULTS+=("✗ Auto Compaction")
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user