use super::*;
use crate::api::models::{ChatRequest, Message};
use crate::api::provider::OpenAiCompatibleProvider;
impl SwarmCoordinator {
pub(super) async fn analyze_and_split(
&self,
user_msg: &str,
system_prompt: &str,
) -> crate::common::Result<Vec<SwarmTask>> {
let coord_client = self.hive_config.coordinator_client(&self.client);
self.analyze_and_split_with_client(user_msg, system_prompt, &coord_client)
.await
}
pub(super) async fn analyze_and_split_fallback(
&self,
user_msg: &str,
system_prompt: &str,
) -> crate::common::Result<Vec<SwarmTask>> {
let fallback_client = if self.hive_config.worker_model.is_some() {
let c = self.hive_config.worker_client(&self.client);
tracing::info!(
"Retrying analyze_and_split with fallback model: {}",
c.model
);
c
} else {
tracing::info!("Retrying analyze_and_split with default model");
self.client.clone()
};
self.analyze_and_split_with_client(user_msg, system_prompt, &fallback_client)
.await
}
pub(super) async fn analyze_and_split_with_client(
&self,
user_msg: &str,
_system_prompt: &str,
coord_client: &OpenAiCompatibleProvider,
) -> crate::common::Result<Vec<SwarmTask>> {
let split_prompt = format!(
"You are a task coordinator. Analyze the following task and decide if it \
should be split into parallel subtasks. Default to a single agent unless \
splitting provides clear, measurable benefit.\n\n\
Rules:\n\
- DEFAULT to 1 subtask. Splitting is the exception, not the rule.\n\
- Return EXACTLY 1 subtask if ANY of the following apply:\n\
* The work touches ≤3 files\n\
* The work is naturally sequential (A must finish before B starts)\n\
* The subtasks would share or conflict on the same files\n\
* The task is adding/modifying a single feature or module\n\
* The task is a bug fix, rename, or simple refactor\n\
- Only split when ALL of the following are true:\n\
* There are 2+ clearly independent workstreams with NO file overlap\n\
* Each subtask is large enough to justify its own agent (>10 tool calls expected)\n\
* Parallel execution would meaningfully reduce total time\n\
- Maximum {} subtasks\n\
- Each subtask must be independently executable with zero file overlap\n\
- Include dependency information if subtasks depend on each other\n\
- The task language does not affect the split decision — evaluate structure, not keywords\n\
Respond with a JSON array (1 element if no split needed):\n\
```json\n\
[{{\n\
\"id\": \"t1\",\n\
\"prompt\": \"detailed instruction for this subtask\",\n\
\"role\": \"worker\",\n\
\"dependencies\": [],\n\
\"target_files\": [\"src/foo.rs\"]\n\
}}]\n\
```\n\n\
Task: {user_msg}",
self.hive_config.max_agents
);
let messages = vec![Message {
role: "user".to_string(),
content: Some(crate::api::Content::text(split_prompt)),
reasoning_content: None,
tool_calls: None,
tool_call_id: None,
}];
let request = ChatRequest {
model: coord_client.model.clone(),
messages,
max_tokens: 4096,
stream: false,
tools: None,
tool_choice: None,
temperature: None,
thinking_budget_tokens: None,
reasoning_effort: None,
};
let response = coord_client
.chat(&request)
.await
.map_err(|e| crate::common::AgentError::Transport(e.to_string()))?;
let text = response
.choices
.first()
.and_then(|c| c.message.content.as_ref().map(|c| c.text_content()))
.unwrap_or_default();
let mut tasks = parse_task_json(&text)?;
for task in &mut tasks {
task.agent_name = None;
}
enforce_file_disjoint(&mut tasks);
Ok(tasks)
}
}