Execute an agent node (static version). When `guardrail_enforcer` is `Some`, encodes prompt before LLM and decodes response after. When `stream_mode.emits_tokens()` and the provider supports streaming, emits `Token` events per chunk via `event_tx` and accumulates into a full response. Falls back to `complete()` if the provider does not support streaming.
(
current_node_id: &NodeId,
agent_node_config: &AgentNodeConfig,
node_config: &std::collections::HashMap<String, serde_json::Value>,
context: Arc<Mutex<WorkflowContext>
| 1433 | /// events per chunk via `event_tx` and accumulates into a full response. Falls back to |
| 1434 | /// `complete()` if the provider does not support streaming. |
| 1435 | async fn execute_agent_node_static( |
| 1436 | current_node_id: &NodeId, |
| 1437 | agent_node_config: &AgentNodeConfig, |
| 1438 | node_config: &std::collections::HashMap<String, serde_json::Value>, |
| 1439 | context: Arc<Mutex<WorkflowContext>>, |
| 1440 | agents: Arc<RwLock<HashMap<crate::types::AgentId, Arc<dyn AgentTrait>>>>, |
| 1441 | guardrail_enforcer: Option<Arc<Enforcer>>, |
| 1442 | event_tx: Option<tokio::sync::mpsc::Sender<crate::stream::StreamEvent>>, |
| 1443 | stream_mode: crate::stream::StreamMode, |
| 1444 | ) -> GraphBitResult<serde_json::Value> { |
| 1445 | let agent_id = &agent_node_config.agent_id; |
| 1446 | let prompt_template = &agent_node_config.prompt_template; |
| 1447 | let conversational_context = agent_node_config.conversational_context.as_deref(); |
| 1448 | // Use read lock for better performance |
| 1449 | let agents_guard = agents.read().await; |
| 1450 | let agent = agents_guard |
| 1451 | .get(agent_id) |
| 1452 | .ok_or_else(|| GraphBitError::agent_not_found(agent_id.to_string()))? |
| 1453 | .clone(); |
| 1454 | drop(agents_guard); // Release the lock early |
| 1455 | |
| 1456 | // Build implicit preamble from upstream (parent) node outputs, then resolve templates |
| 1457 | let (resolved_prompt, resolved_context, metadata_input_raw) = { |
| 1458 | let ctx = context.lock().await; |
| 1459 | |
| 1460 | // Extract dependency map and name map from metadata |
| 1461 | let deps_map = ctx |
| 1462 | .metadata |
| 1463 | .get("node_dependencies") |
| 1464 | .cloned() |
| 1465 | .unwrap_or(serde_json::json!({})); |
| 1466 | let id_name_map = ctx |
| 1467 | .metadata |
| 1468 | .get("node_id_to_name") |
| 1469 | .cloned() |
| 1470 | .unwrap_or(serde_json::json!({})); |
| 1471 | |
| 1472 | // Collect preamble sections from DIRECT parents of this node |
| 1473 | let mut sections: Vec<String> = Vec::new(); |
| 1474 | // Also collect a JSON map of parent outputs for CrewAI-style context passing |
| 1475 | let mut parents_json: serde_json::Map<String, serde_json::Value> = |
| 1476 | serde_json::Map::new(); |
| 1477 | |
| 1478 | // Use id->name map for titles |
| 1479 | let id_name_obj = id_name_map.as_object(); |
| 1480 | |
| 1481 | // Resolve current node id string and direct parents from deps map |
| 1482 | let cur_id_str = current_node_id.to_string(); |
| 1483 | let parent_ids: Vec<String> = deps_map |
| 1484 | .as_object() |
| 1485 | .and_then(|m| m.get(&cur_id_str)) |
| 1486 | .and_then(|v| v.as_array()) |
| 1487 | .map(|arr| { |
| 1488 | arr.iter() |
| 1489 | .filter_map(|v| v.as_str().map(str::to_string)) |
| 1490 | .collect::<Vec<String>>() |
| 1491 | }) |
| 1492 | .unwrap_or_default(); |
nothing calls this directly
no test coverage detected