Execute an agent with tool calling orchestration. When `guardrail_enforcer` is `Some`, encodes prompt before LLM and decodes response after. When `stream_mode.emits_tool_events()` and `event_tx` is `Some`, emits `ToolCallStarted` for each tool call requested by the LLM before returning the `tool_calls_required` response to the Python layer (which handles actual execution and must emit `ToolCallCom
(
_agent_id: &crate::types::AgentId,
agent_node_config: &AgentNodeConfig,
prompt: &str,
conversational_context: Option<&str>,
metadata_input: &str,
node
| 2066 | /// [`crate::stream::StreamEvent::Token`] per chunk; otherwise that call uses |
| 2067 | /// [`LlmProvider::complete`] (same as non-streaming `execute()`). |
| 2068 | async fn execute_agent_with_tools( |
| 2069 | _agent_id: &crate::types::AgentId, |
| 2070 | agent_node_config: &AgentNodeConfig, |
| 2071 | prompt: &str, |
| 2072 | conversational_context: Option<&str>, |
| 2073 | metadata_input: &str, |
| 2074 | node_config: &std::collections::HashMap<String, serde_json::Value>, |
| 2075 | agent: Arc<dyn AgentTrait>, |
| 2076 | node_id: &NodeId, |
| 2077 | node_name: &str, |
| 2078 | context: Arc<Mutex<WorkflowContext>>, |
| 2079 | guardrail_enforcer: Option<Arc<Enforcer>>, |
| 2080 | event_tx: Option<tokio::sync::mpsc::Sender<crate::stream::StreamEvent>>, |
| 2081 | stream_mode: crate::stream::StreamMode, |
| 2082 | ) -> GraphBitResult<serde_json::Value> { |
| 2083 | tracing::info!("Starting execute_agent_with_tools for agent: {_agent_id}"); |
| 2084 | use crate::llm::{LlmMessage, LlmRequest, LlmTool}; |
| 2085 | |
| 2086 | // ... (rest of tool calling logic, but with LlmRequest::with_messages handled correctly below) |
| 2087 | |
| 2088 | // Build the executions array for metadata |
| 2089 | let mut executions: Vec<serde_json::Value> = Vec::new(); |
| 2090 | |
| 2091 | // Guardrail: encode prompt and context individually before sending to LLM |
| 2092 | let mut masked_input_for_meta = metadata_input.to_string(); |
| 2093 | let prompt_for_llm = if let Some(ref enforcer) = guardrail_enforcer { |
| 2094 | tracing::debug!("Guardrail: encoding prompt and context before LLM call (tool path)"); |
| 2095 | |
| 2096 | // 1. Encode context if present |
| 2097 | let (masked_context, signature_ctx, ctx_rules, ctx_count) = |
| 2098 | if let Some(ctx) = conversational_context { |
| 2099 | let enc = enforcer.encode( |
| 2100 | serde_json::Value::String(ctx.to_string()), |
| 2101 | EncodeContext::Llm, |
| 2102 | ); |
| 2103 | ( |
| 2104 | enc.payload.as_str().unwrap_or_default().to_string(), |
| 2105 | enc.signature_injection_text, |
| 2106 | enc.rule_names, |
| 2107 | enc.rules_applied_count, |
| 2108 | ) |
| 2109 | } else { |
| 2110 | (String::new(), String::new(), Vec::new(), 0) |
| 2111 | }; |
| 2112 | |
| 2113 | // 2. Encode prompt |
| 2114 | let enc_prompt = enforcer.encode( |
| 2115 | serde_json::Value::String(prompt.to_string()), |
| 2116 | EncodeContext::Llm, |
| 2117 | ); |
| 2118 | |
| 2119 | // 3. Encode metadata input specifically |
| 2120 | let enc_meta = enforcer.encode( |
| 2121 | serde_json::Value::String(metadata_input.to_string()), |
| 2122 | EncodeContext::Llm, |
| 2123 | ); |
| 2124 | masked_input_for_meta = enc_meta.payload.as_str().unwrap_or_default().to_string(); |
| 2125 |
nothing calls this directly
no test coverage detected