Use session IDs as task IDs for subagents instead of UUIDs (#5398)
This commit is contained in:
@@ -454,7 +454,12 @@ impl Agent {
|
|||||||
.map(Value::Object)
|
.map(Value::Object)
|
||||||
.unwrap_or(Value::Object(serde_json::Map::new()));
|
.unwrap_or(Value::Object(serde_json::Map::new()));
|
||||||
sub_recipe_manager
|
sub_recipe_manager
|
||||||
.dispatch_sub_recipe_tool_call(&tool_call.name, arguments, &self.tasks_manager)
|
.dispatch_sub_recipe_tool_call(
|
||||||
|
&tool_call.name,
|
||||||
|
arguments,
|
||||||
|
&self.tasks_manager,
|
||||||
|
&session.working_dir,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
} else if tool_call.name == SUBAGENT_EXECUTE_TASK_TOOL_NAME {
|
} else if tool_call.name == SUBAGENT_EXECUTE_TASK_TOOL_NAME {
|
||||||
let provider = match self.provider().await {
|
let provider = match self.provider().await {
|
||||||
@@ -542,7 +547,13 @@ impl Agent {
|
|||||||
.clone()
|
.clone()
|
||||||
.map(Value::Object)
|
.map(Value::Object)
|
||||||
.unwrap_or(Value::Object(serde_json::Map::new()));
|
.unwrap_or(Value::Object(serde_json::Map::new()));
|
||||||
create_dynamic_task(arguments, &self.tasks_manager, loaded_extensions).await
|
create_dynamic_task(
|
||||||
|
arguments,
|
||||||
|
&self.tasks_manager,
|
||||||
|
loaded_extensions,
|
||||||
|
&session.working_dir,
|
||||||
|
)
|
||||||
|
.await
|
||||||
} else if self.is_frontend_tool(&tool_call.name).await {
|
} else if self.is_frontend_tool(&tool_call.name).await {
|
||||||
// For frontend tools, return an error indicating we need frontend execution
|
// For frontend tools, return an error indicating we need frontend execution
|
||||||
ToolCallResult::from(Err(ErrorData::new(
|
ToolCallResult::from(Err(ErrorData::new(
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use crate::agents::subagent_execution_tool::{
|
|||||||
use crate::agents::tool_execution::ToolCallResult;
|
use crate::agents::tool_execution::ToolCallResult;
|
||||||
use crate::config::GooseMode;
|
use crate::config::GooseMode;
|
||||||
use crate::recipe::{Recipe, RecipeBuilder};
|
use crate::recipe::{Recipe, RecipeBuilder};
|
||||||
|
use crate::session::SessionManager;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use rmcp::model::{Content, ErrorCode, ErrorData, Tool, ToolAnnotations};
|
use rmcp::model::{Content, ErrorCode, ErrorData, Tool, ToolAnnotations};
|
||||||
use rmcp::schemars::{schema_for, JsonSchema};
|
use rmcp::schemars::{schema_for, JsonSchema};
|
||||||
@@ -276,6 +277,7 @@ pub async fn create_dynamic_task(
|
|||||||
params: Value,
|
params: Value,
|
||||||
tasks_manager: &TasksManager,
|
tasks_manager: &TasksManager,
|
||||||
loaded_extensions: Vec<String>,
|
loaded_extensions: Vec<String>,
|
||||||
|
parent_working_dir: &std::path::Path,
|
||||||
) -> ToolCallResult {
|
) -> ToolCallResult {
|
||||||
let task_params_array = extract_task_parameters(¶ms);
|
let task_params_array = extract_task_parameters(¶ms);
|
||||||
|
|
||||||
@@ -299,8 +301,26 @@ pub async fn create_dynamic_task(
|
|||||||
.and_then(|v| v.as_bool())
|
.and_then(|v| v.as_bool())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
// Create a session for this task - use its ID as the task ID
|
||||||
|
let session = match SessionManager::create_session(
|
||||||
|
parent_working_dir.to_path_buf(),
|
||||||
|
"Subagent task".to_string(),
|
||||||
|
crate::session::session_manager::SessionType::SubAgent,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
return ToolCallResult::from(Err(ErrorData {
|
||||||
|
code: ErrorCode::INTERNAL_ERROR,
|
||||||
|
message: Cow::from(format!("Failed to create session: {}", e)),
|
||||||
|
data: None,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let task = Task {
|
let task = Task {
|
||||||
id: uuid::Uuid::new_v4().to_string(),
|
id: session.id,
|
||||||
payload: TaskPayload {
|
payload: TaskPayload {
|
||||||
recipe,
|
recipe,
|
||||||
return_last_only,
|
return_last_only,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use crate::agents::subagent_execution_tool::tasks_manager::TasksManager;
|
|||||||
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;
|
||||||
use crate::recipe::{Recipe, RecipeParameter, RecipeParameterRequirement, SubRecipe};
|
use crate::recipe::{Recipe, RecipeParameter, RecipeParameterRequirement, SubRecipe};
|
||||||
|
use crate::session::SessionManager;
|
||||||
|
|
||||||
use super::param_utils::prepare_command_params;
|
use super::param_utils::prepare_command_params;
|
||||||
|
|
||||||
@@ -53,15 +54,23 @@ fn extract_task_parameters(params: &Value) -> Vec<Value> {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_tasks_from_params(
|
async fn create_tasks_from_params(
|
||||||
sub_recipe: &SubRecipe,
|
sub_recipe: &SubRecipe,
|
||||||
command_params: &[std::collections::HashMap<String, String>],
|
command_params: &[std::collections::HashMap<String, String>],
|
||||||
|
parent_working_dir: &std::path::Path,
|
||||||
) -> Result<Vec<Task>> {
|
) -> Result<Vec<Task>> {
|
||||||
let recipe_file = load_local_recipe_file(&sub_recipe.path)
|
let recipe_file = load_local_recipe_file(&sub_recipe.path)
|
||||||
.map_err(|e| anyhow::anyhow!("Failed to load recipe {}: {}", sub_recipe.path, e))?;
|
.map_err(|e| anyhow::anyhow!("Failed to load recipe {}: {}", sub_recipe.path, e))?;
|
||||||
|
|
||||||
let mut tasks = Vec::new();
|
let mut tasks = Vec::new();
|
||||||
for task_command_param in command_params {
|
for task_command_param in command_params {
|
||||||
|
let session = SessionManager::create_session(
|
||||||
|
parent_working_dir.to_path_buf(),
|
||||||
|
format!("Subagent: {}", sub_recipe.name),
|
||||||
|
crate::session::session_manager::SessionType::SubAgent,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let recipe = build_recipe_from_template(
|
let recipe = build_recipe_from_template(
|
||||||
recipe_file.content.clone(),
|
recipe_file.content.clone(),
|
||||||
&recipe_file.parent_dir,
|
&recipe_file.parent_dir,
|
||||||
@@ -74,7 +83,7 @@ fn create_tasks_from_params(
|
|||||||
.map_err(|e| anyhow::anyhow!("Failed to build recipe: {}", e))?;
|
.map_err(|e| anyhow::anyhow!("Failed to build recipe: {}", e))?;
|
||||||
|
|
||||||
let task = Task {
|
let task = Task {
|
||||||
id: uuid::Uuid::new_v4().to_string(),
|
id: session.id,
|
||||||
payload: TaskPayload {
|
payload: TaskPayload {
|
||||||
recipe,
|
recipe,
|
||||||
return_last_only: false,
|
return_last_only: false,
|
||||||
@@ -106,10 +115,11 @@ pub async fn create_sub_recipe_task(
|
|||||||
sub_recipe: &SubRecipe,
|
sub_recipe: &SubRecipe,
|
||||||
params: Value,
|
params: Value,
|
||||||
tasks_manager: &TasksManager,
|
tasks_manager: &TasksManager,
|
||||||
|
parent_working_dir: &std::path::Path,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
let task_params_array = extract_task_parameters(¶ms);
|
let task_params_array = extract_task_parameters(¶ms);
|
||||||
let command_params = prepare_command_params(sub_recipe, task_params_array.clone())?;
|
let command_params = prepare_command_params(sub_recipe, task_params_array.clone())?;
|
||||||
let tasks = create_tasks_from_params(sub_recipe, &command_params)?;
|
let tasks = create_tasks_from_params(sub_recipe, &command_params, parent_working_dir).await?;
|
||||||
let task_execution_payload = create_task_execution_payload(&tasks, sub_recipe);
|
let task_execution_payload = create_task_execution_payload(&tasks, sub_recipe);
|
||||||
|
|
||||||
let tasks_json = serde_json::to_string(&task_execution_payload)
|
let tasks_json = serde_json::to_string(&task_execution_payload)
|
||||||
|
|||||||
@@ -57,9 +57,10 @@ impl SubRecipeManager {
|
|||||||
tool_name: &str,
|
tool_name: &str,
|
||||||
params: Value,
|
params: Value,
|
||||||
tasks_manager: &TasksManager,
|
tasks_manager: &TasksManager,
|
||||||
|
parent_working_dir: &std::path::Path,
|
||||||
) -> ToolCallResult {
|
) -> ToolCallResult {
|
||||||
let result = self
|
let result = self
|
||||||
.call_sub_recipe_tool(tool_name, params, tasks_manager)
|
.call_sub_recipe_tool(tool_name, params, tasks_manager, parent_working_dir)
|
||||||
.await;
|
.await;
|
||||||
match result {
|
match result {
|
||||||
Ok(call_result) => ToolCallResult::from(Ok(call_result)),
|
Ok(call_result) => ToolCallResult::from(Ok(call_result)),
|
||||||
@@ -76,6 +77,7 @@ impl SubRecipeManager {
|
|||||||
tool_name: &str,
|
tool_name: &str,
|
||||||
params: Value,
|
params: Value,
|
||||||
tasks_manager: &TasksManager,
|
tasks_manager: &TasksManager,
|
||||||
|
parent_working_dir: &std::path::Path,
|
||||||
) -> Result<Vec<Content>, ErrorData> {
|
) -> Result<Vec<Content>, ErrorData> {
|
||||||
let sub_recipe = self.sub_recipes.get(tool_name).ok_or_else(|| {
|
let sub_recipe = self.sub_recipes.get(tool_name).ok_or_else(|| {
|
||||||
let sub_recipe_name = tool_name
|
let sub_recipe_name = tool_name
|
||||||
@@ -97,7 +99,7 @@ impl SubRecipeManager {
|
|||||||
data: None,
|
data: None,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let output = create_sub_recipe_task(sub_recipe, params, tasks_manager)
|
let output = create_sub_recipe_task(sub_recipe, params, tasks_manager, parent_working_dir)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ErrorData {
|
.map_err(|e| ErrorData {
|
||||||
code: ErrorCode::INTERNAL_ERROR,
|
code: ErrorCode::INTERNAL_ERROR,
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ async fn handle_recipe_task(
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
result = run_complete_subagent_task(recipe, task_config, return_last_only) => {
|
result = run_complete_subagent_task(recipe, task_config, return_last_only, task.id.clone()) => {
|
||||||
result.map(|text| serde_json::json!({"result": text}))
|
result.map(|text| serde_json::json!({"result": text}))
|
||||||
.map_err(|e| format!("Recipe execution failed: {}", e))
|
.map_err(|e| format!("Recipe execution failed: {}", e))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use crate::session::session_manager::SessionType;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
agents::{subagent_task_config::TaskConfig, AgentEvent, SessionConfig},
|
agents::{subagent_task_config::TaskConfig, AgentEvent, SessionConfig},
|
||||||
conversation::{message::Message, Conversation},
|
conversation::{message::Message, Conversation},
|
||||||
execution::manager::AgentManager,
|
execution::manager::AgentManager,
|
||||||
recipe::Recipe,
|
recipe::Recipe,
|
||||||
session::SessionManager,
|
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
@@ -21,14 +19,17 @@ pub async fn run_complete_subagent_task(
|
|||||||
recipe: Recipe,
|
recipe: Recipe,
|
||||||
task_config: TaskConfig,
|
task_config: TaskConfig,
|
||||||
return_last_only: bool,
|
return_last_only: bool,
|
||||||
|
session_id: String,
|
||||||
) -> Result<String, anyhow::Error> {
|
) -> Result<String, anyhow::Error> {
|
||||||
let (messages, final_output) = get_agent_messages(recipe, task_config).await.map_err(|e| {
|
let (messages, final_output) = get_agent_messages(recipe, task_config, session_id)
|
||||||
ErrorData::new(
|
.await
|
||||||
ErrorCode::INTERNAL_ERROR,
|
.map_err(|e| {
|
||||||
format!("Failed to execute task: {}", e),
|
ErrorData::new(
|
||||||
None,
|
ErrorCode::INTERNAL_ERROR,
|
||||||
)
|
format!("Failed to execute task: {}", e),
|
||||||
})?;
|
None,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
if let Some(output) = final_output {
|
if let Some(output) = final_output {
|
||||||
return Ok(output);
|
return Ok(output);
|
||||||
@@ -94,7 +95,11 @@ pub async fn run_complete_subagent_task(
|
|||||||
Ok(response_text)
|
Ok(response_text)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_agent_messages(recipe: Recipe, task_config: TaskConfig) -> AgentMessagesFuture {
|
fn get_agent_messages(
|
||||||
|
recipe: Recipe,
|
||||||
|
task_config: TaskConfig,
|
||||||
|
session_id: String,
|
||||||
|
) -> AgentMessagesFuture {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let text_instruction = recipe
|
let text_instruction = recipe
|
||||||
.instructions
|
.instructions
|
||||||
@@ -105,18 +110,9 @@ fn get_agent_messages(recipe: Recipe, task_config: TaskConfig) -> AgentMessagesF
|
|||||||
let agent_manager = AgentManager::instance()
|
let agent_manager = AgentManager::instance()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow!("Failed to create AgentManager: {}", e))?;
|
.map_err(|e| anyhow!("Failed to create AgentManager: {}", e))?;
|
||||||
let parent_session_id = task_config.parent_session_id;
|
|
||||||
let working_dir = task_config.parent_working_dir;
|
|
||||||
let session = SessionManager::create_session(
|
|
||||||
working_dir.clone(),
|
|
||||||
format!("Subagent task for: {}", parent_session_id),
|
|
||||||
SessionType::SubAgent,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| anyhow!("Failed to create a session for sub agent: {}", e))?;
|
|
||||||
|
|
||||||
let agent = agent_manager
|
let agent = agent_manager
|
||||||
.get_or_create_agent(session.id.clone())
|
.get_or_create_agent(session_id.clone())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow!("Failed to get sub agent session file path: {}", e))?;
|
.map_err(|e| anyhow!("Failed to get sub agent session file path: {}", e))?;
|
||||||
|
|
||||||
@@ -149,13 +145,13 @@ fn get_agent_messages(recipe: Recipe, task_config: TaskConfig) -> AgentMessagesF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let session_config = SessionConfig {
|
let session_config = SessionConfig {
|
||||||
id: session.id.clone(),
|
id: session_id.clone(),
|
||||||
schedule_id: None,
|
schedule_id: None,
|
||||||
max_turns: task_config.max_turns.map(|v| v as u32),
|
max_turns: task_config.max_turns.map(|v| v as u32),
|
||||||
retry_config: recipe.retry,
|
retry_config: recipe.retry,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut stream = crate::session_context::with_session_id(Some(session.id.clone()), async {
|
let mut stream = crate::session_context::with_session_id(Some(session_id.clone()), async {
|
||||||
agent.reply(user_message, session_config, None).await
|
agent.reply(user_message, session_config, None).await
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -105,7 +105,14 @@ mod tests {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = create_dynamic_task(params, &tasks_manager, test_loaded_extensions()).await;
|
let working_dir = std::path::Path::new("/tmp");
|
||||||
|
let result = create_dynamic_task(
|
||||||
|
params,
|
||||||
|
&tasks_manager,
|
||||||
|
test_loaded_extensions(),
|
||||||
|
working_dir,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
// Check that the result is successful by awaiting the future
|
// Check that the result is successful by awaiting the future
|
||||||
let tool_result = result.result.await;
|
let tool_result = result.result.await;
|
||||||
@@ -156,7 +163,14 @@ mod tests {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = create_dynamic_task(params, &tasks_manager, test_loaded_extensions()).await;
|
let working_dir = std::path::Path::new("/tmp");
|
||||||
|
let result = create_dynamic_task(
|
||||||
|
params,
|
||||||
|
&tasks_manager,
|
||||||
|
test_loaded_extensions(),
|
||||||
|
working_dir,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
// Check that the result fails since text_instruction is no longer supported
|
// Check that the result fails since text_instruction is no longer supported
|
||||||
let tool_result = result.result.await;
|
let tool_result = result.result.await;
|
||||||
@@ -354,7 +368,14 @@ mod tests {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = create_dynamic_task(params, &tasks_manager, test_loaded_extensions()).await;
|
let working_dir = std::path::Path::new("/tmp");
|
||||||
|
let result = create_dynamic_task(
|
||||||
|
params,
|
||||||
|
&tasks_manager,
|
||||||
|
test_loaded_extensions(),
|
||||||
|
working_dir,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
// Should fail on the invalid task
|
// Should fail on the invalid task
|
||||||
let tool_result = result.result.await;
|
let tool_result = result.result.await;
|
||||||
|
|||||||
Reference in New Issue
Block a user