e2e testing

This commit is contained in:
2026-08-10 07:22:56 -05:00
parent fa43f723a5
commit 88ad290c7e
29 changed files with 1695 additions and 99 deletions
+26 -3
View File
@@ -19,11 +19,16 @@ pub async fn generate_multi_agent_output(
cancellation_rx: futures::channel::oneshot::Receiver<()>,
) -> Result<ResponseStream, ConvertToAPITypeError> {
let supported_tools_override = params.supported_tools_override.take();
let supported_tools = supported_tools_override
let mut supported_tools = supported_tools_override
.clone()
.unwrap_or_else(|| get_supported_tools(&params));
let supported_cli_agent_tools =
remove_orchestration_tools_if_disabled(&mut supported_tools, params.orchestration_enabled);
let mut supported_cli_agent_tools =
supported_tools_override.unwrap_or_else(|| get_supported_cli_agent_tools(&params));
remove_orchestration_tools_if_disabled(
&mut supported_cli_agent_tools,
params.orchestration_enabled,
);
if params.should_redact_secrets {
redaction::redact_inputs(&mut params.input);
}
@@ -254,6 +259,21 @@ pub async fn generate_multi_agent_output(
}
}
fn remove_orchestration_tools_if_disabled(
supported_tools: &mut Vec<api::ToolType>,
orchestration_enabled: bool,
) {
if orchestration_enabled {
return;
}
supported_tools.retain(|tool| {
!matches!(
tool,
api::ToolType::Subagent | api::ToolType::RunAgents | api::ToolType::StartAgentV2
)
});
}
fn get_supported_tools(params: &RequestParams) -> Vec<api::ToolType> {
let mut supported_tools = vec![
api::ToolType::Grep,
@@ -262,7 +282,6 @@ fn get_supported_tools(params: &RequestParams) -> Vec<api::ToolType> {
api::ToolType::ReadMcpResource,
api::ToolType::CallMcpTool,
api::ToolType::RunShellCommand,
api::ToolType::Subagent,
api::ToolType::WriteToLongRunningShellCommand,
api::ToolType::ReadShellCommandOutput,
api::ToolType::ReadDocuments,
@@ -270,6 +289,10 @@ fn get_supported_tools(params: &RequestParams) -> Vec<api::ToolType> {
api::ToolType::EditDocuments,
];
if params.orchestration_enabled {
supported_tools.push(api::ToolType::Subagent);
}
if FeatureFlag::ConversationsAsContext.is_enabled() {
supported_tools.push(api::ToolType::FetchConversation);
}
+37 -1
View File
@@ -2,7 +2,9 @@ use galaxy_core::features::FeatureFlag;
use galaxy_core::HostId;
use warp_multi_agent_api as api;
use super::{get_supported_cli_agent_tools, get_supported_tools};
use super::{
get_supported_cli_agent_tools, get_supported_tools, remove_orchestration_tools_if_disabled,
};
use crate::ai::agent::api::RequestParams;
use crate::ai::blocklist::SessionContext;
use crate::ai::llms::LLMId;
@@ -77,6 +79,40 @@ fn supported_tools_expose_local_subagents_without_hosted_orchestration_tools() {
assert!(!supported_tools.contains(&api::ToolType::StartAgentV2));
}
#[test]
fn supported_tools_omit_subagents_when_orchestration_is_disabled() {
let params = request_params_with_ask_user_question_enabled(false);
let supported_tools = get_supported_tools(&params);
assert!(!supported_tools.contains(&api::ToolType::Subagent));
}
#[test]
fn supported_tool_override_cannot_restore_leaf_orchestration_tools() {
let mut supported_tools = vec![
api::ToolType::Grep,
api::ToolType::Subagent,
api::ToolType::RunAgents,
api::ToolType::StartAgentV2,
];
remove_orchestration_tools_if_disabled(&mut supported_tools, false);
assert_eq!(supported_tools, vec![api::ToolType::Grep]);
}
#[test]
fn enabled_orchestration_preserves_supported_tool_override() {
let mut supported_tools = vec![api::ToolType::Grep, api::ToolType::Subagent];
remove_orchestration_tools_if_disabled(&mut supported_tools, true);
assert_eq!(
supported_tools,
vec![api::ToolType::Grep, api::ToolType::Subagent]
);
}
#[test]
fn supported_tools_omit_hosted_only_capabilities() {
let params = request_params_with_ask_user_question_enabled(false);
+54 -1
View File
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use warp_multi_agent_api as api;
@@ -360,6 +360,59 @@ impl TaskStore {
append_refs_for_task(tasks, &mut refs, root_task);
}
let indexed_task_ids = refs
.iter()
.map(|exchange_ref| exchange_ref.task_id.clone())
.collect::<HashSet<_>>();
let mut direct_cli_tasks = tasks
.values()
.filter(|task| {
!indexed_task_ids.contains(task.id())
&& task.parent_id().as_ref() == Some(root_task_id)
&& task.is_cli_subagent()
&& task
.subagent_params()
.is_some_and(|params| params.tool_call_id.is_empty())
&& task.exchanges().next().is_some()
})
.collect::<Vec<_>>();
direct_cli_tasks.sort_by(|left, right| {
left.exchanges()
.next()
.map(|exchange| exchange.start_time)
.cmp(&right.exchanges().next().map(|exchange| exchange.start_time))
.then_with(|| left.id().to_string().cmp(&right.id().to_string()))
});
// Direct providers synthesize CLI monitor tasks without a parent Subagent message.
// Place each task as one chronological block so its exchanges remain reachable without
// changing the DFS order of server-linked subtasks.
for task in direct_cli_tasks {
let first_start_time = task
.exchanges()
.next()
.expect("direct CLI task was filtered to contain an exchange")
.start_time;
let insertion_index = refs
.iter()
.position(|exchange_ref| {
tasks
.get(&exchange_ref.task_id)
.and_then(|task| task.exchanges().nth(exchange_ref.exchange_index))
.is_some_and(|exchange| exchange.start_time > first_start_time)
})
.unwrap_or(refs.len());
let task_id = task.id().clone();
let task_refs = task
.exchanges()
.enumerate()
.map(|(exchange_index, _)| ExchangeRef {
task_id: task_id.clone(),
exchange_index,
})
.collect::<Vec<_>>();
refs.splice(insertion_index..insertion_index, task_refs);
}
refs
}
}
+40
View File
@@ -146,6 +146,46 @@ fn test_insert_subtask() {
assert!(store.contains(&subtask_id));
}
#[test]
fn test_unlinked_direct_cli_task_is_linearized_chronologically() {
let base_time = Local::now();
let mut root_task = Task::new_optimistic_root();
let root_task_id = root_task.id().clone();
let mut before_cli = create_test_exchange();
before_cli.start_time = base_time;
let before_cli_id = before_cli.id;
root_task.append_exchange(before_cli);
let mut after_cli = create_test_exchange();
after_cli.start_time = base_time + chrono::Duration::seconds(2);
let after_cli_id = after_cli.id;
root_task.append_exchange(after_cli);
let mut cli_task =
Task::new_optimistic_cli_agent_subtask(BlockId::new(), Some(root_task_id.to_string()));
let mut cli_exchange = create_test_exchange();
cli_exchange.start_time = base_time + chrono::Duration::seconds(1);
let cli_exchange_id = cli_exchange.id;
cli_task.append_exchange(cli_exchange);
let mut store = TaskStore::with_root_task(root_task);
store.insert(cli_task);
let exchange_ids = store
.all_exchanges()
.map(|exchange| exchange.id)
.collect::<Vec<_>>();
assert_eq!(
exchange_ids,
vec![before_cli_id, cli_exchange_id, after_cli_id]
);
assert_eq!(
store.latest_exchange().map(|exchange| exchange.id),
Some(after_cli_id)
);
}
#[test]
fn test_remove_task() {
let task = create_test_task_with_exchanges(3);