Unify subrecipe and subagent execution through shared recipe pipeline (#5082)
This commit is contained in:
@@ -3,6 +3,7 @@ use crate::{
|
||||
agents::{subagent_task_config::TaskConfig, AgentEvent, SessionConfig},
|
||||
conversation::{message::Message, Conversation},
|
||||
execution::manager::AgentManager,
|
||||
recipe::Recipe,
|
||||
session::SessionManager,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
@@ -10,27 +11,30 @@ use futures::StreamExt;
|
||||
use rmcp::model::{ErrorCode, ErrorData};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use tracing::debug;
|
||||
use tracing::{debug, info};
|
||||
|
||||
type AgentMessagesFuture =
|
||||
Pin<Box<dyn Future<Output = Result<(Conversation, Option<String>)>> + Send>>;
|
||||
|
||||
/// Standalone function to run a complete subagent task with output options
|
||||
pub async fn run_complete_subagent_task(
|
||||
text_instruction: String,
|
||||
recipe: Recipe,
|
||||
task_config: TaskConfig,
|
||||
return_last_only: bool,
|
||||
) -> Result<String, anyhow::Error> {
|
||||
let messages = get_agent_messages(text_instruction, task_config)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
ErrorData::new(
|
||||
ErrorCode::INTERNAL_ERROR,
|
||||
format!("Failed to execute task: {}", e),
|
||||
None,
|
||||
)
|
||||
})?;
|
||||
let (messages, final_output) = get_agent_messages(recipe, task_config).await.map_err(|e| {
|
||||
ErrorData::new(
|
||||
ErrorCode::INTERNAL_ERROR,
|
||||
format!("Failed to execute task: {}", e),
|
||||
None,
|
||||
)
|
||||
})?;
|
||||
|
||||
if let Some(output) = final_output {
|
||||
return Ok(output);
|
||||
}
|
||||
|
||||
// Extract text content based on return_last_only flag
|
||||
let response_text = if return_last_only {
|
||||
// Get only the last message's text content
|
||||
messages
|
||||
.messages()
|
||||
.last()
|
||||
@@ -44,7 +48,6 @@ pub async fn run_complete_subagent_task(
|
||||
})
|
||||
.unwrap_or_else(|| String::from("No text content in last message"))
|
||||
} else {
|
||||
// Extract all text content from all messages (original behavior)
|
||||
let all_text_content: Vec<String> = messages
|
||||
.iter()
|
||||
.flat_map(|message| {
|
||||
@@ -88,15 +91,17 @@ pub async fn run_complete_subagent_task(
|
||||
all_text_content.join("\n")
|
||||
};
|
||||
|
||||
// Return the result
|
||||
Ok(response_text)
|
||||
}
|
||||
|
||||
fn get_agent_messages(
|
||||
text_instruction: String,
|
||||
task_config: TaskConfig,
|
||||
) -> Pin<Box<dyn Future<Output = Result<Conversation>> + Send>> {
|
||||
fn get_agent_messages(recipe: Recipe, task_config: TaskConfig) -> AgentMessagesFuture {
|
||||
Box::pin(async move {
|
||||
let text_instruction = recipe
|
||||
.instructions
|
||||
.clone()
|
||||
.or(recipe.prompt.clone())
|
||||
.ok_or_else(|| anyhow!("Recipe has no instructions or prompt"))?;
|
||||
|
||||
let agent_manager = AgentManager::instance()
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to create AgentManager: {}", e))?;
|
||||
@@ -130,14 +135,24 @@ fn get_agent_messages(
|
||||
}
|
||||
}
|
||||
|
||||
let has_response_schema = recipe.response.is_some();
|
||||
agent
|
||||
.apply_recipe_components(recipe.sub_recipes.clone(), recipe.response.clone(), true)
|
||||
.await;
|
||||
|
||||
let user_message = Message::user().with_text(text_instruction);
|
||||
let mut conversation = Conversation::new_unvalidated(vec![user_message.clone()]);
|
||||
|
||||
if let Some(activities) = recipe.activities {
|
||||
for activity in activities {
|
||||
info!("Recipe activity: {}", activity);
|
||||
}
|
||||
}
|
||||
let session_config = SessionConfig {
|
||||
id: session.id.clone(),
|
||||
schedule_id: None,
|
||||
max_turns: task_config.max_turns.map(|v| v as u32),
|
||||
retry_config: None,
|
||||
retry_config: recipe.retry,
|
||||
};
|
||||
|
||||
let mut stream = crate::session_context::with_session_id(Some(session.id.clone()), async {
|
||||
@@ -159,6 +174,17 @@ fn get_agent_messages(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(conversation)
|
||||
let final_output = if has_response_schema {
|
||||
agent
|
||||
.final_output_tool
|
||||
.lock()
|
||||
.await
|
||||
.as_ref()
|
||||
.and_then(|tool| tool.final_output.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok((conversation, final_output))
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user