MCPcopy Index your code
hub / github.com/InfinitiBit/graphbit / handle_tool_calls_in_context

Method handle_tool_calls_in_context

python/src/workflow/executor.rs:1797–2627  ·  view source on GitHub ↗

Handle tool calls in workflow context using an iterative ReAct loop. When an agent node returns `tool_calls_required`, this function: 1. Executes the requested tools via Python 2. Sends tool results back to the LLM WITH tool definitions 3. If the LLM requests more tools, repeats from step 1 4. Exits when the LLM returns a final answer (no tool calls) or max_iterations is reached This enables mul

(
        mut context: graphbit_core::types::WorkflowContext,
        workflow: &graphbit_core::workflow::Workflow,
        guardrail_enforcer: Option<&Enforcer>,
    )

Source from the content-addressed store, hash-verified

1795 /// When `guardrail_enforcer` is `Some`, decodes tool-call parameters before execution only;
1796 /// after tool execution we do nothing (no encode of tool results).
1797 async fn handle_tool_calls_in_context(
1798 mut context: graphbit_core::types::WorkflowContext,
1799 workflow: &graphbit_core::workflow::Workflow,
1800 guardrail_enforcer: Option<&Enforcer>,
1801 ) -> Result<(graphbit_core::types::WorkflowContext, Vec<String>), graphbit_core::errors::GraphBitError> {
1802 use crate::workflow::node::execute_production_tool_calls;
1803 use graphbit_core::llm::{LlmMessage, LlmProvider, LlmRequest, LlmTool, LlmToolCall};
1804
1805 // Check each node output for tool_calls_required responses
1806 let node_outputs = context.node_outputs.clone();
1807 let mut nodes_with_tool_calls: Vec<String> = Vec::new();
1808
1809 for (node_id, output) in node_outputs {
1810 if let Ok(response_obj) = serde_json::from_value::<serde_json::Value>(output.clone()) {
1811 if let Some(response_type) = response_obj.get("type").and_then(|v| v.as_str()) {
1812 if response_type == "tool_calls_required" {
1813 // Extract initial tool calls and original prompt
1814 if let (Some(initial_tool_calls), Some(original_prompt)) = (
1815 response_obj.get("tool_calls"),
1816 response_obj.get("original_prompt").and_then(|v| v.as_str()),
1817 ) {
1818 // Get the node from the workflow
1819 let node = match workflow
1820 .graph
1821 .get_nodes()
1822 .iter()
1823 .find(|(id, _)| id.to_string() == node_id)
1824 .map(|(_, node)| node.clone())
1825 {
1826 Some(n) => n,
1827 None => continue,
1828 };
1829
1830 // Only handle agent nodes
1831 if !matches!(
1832 node.node_type,
1833 graphbit_core::graph::NodeType::Agent { .. }
1834 ) {
1835 continue;
1836 }
1837
1838 nodes_with_tool_calls.push(node.id.to_string());
1839
1840 // Get node name for metadata storage
1841 let node_name = workflow
1842 .graph
1843 .get_nodes()
1844 .iter()
1845 .find(|(id, _)| **id == node.id)
1846 .map(|(_, n)| n.name.clone())
1847 .unwrap_or_else(|| "unknown".to_string());
1848
1849 // Extract available tool names for this node
1850 let node_tools = node
1851 .config
1852 .get("tools")
1853 .and_then(|v| v.as_array())
1854 .map(|arr| {

Callers

nothing calls this directly

Calls 15

toolFunction · 0.85
cloneMethod · 0.80
get_nodesMethod · 0.80
to_stringMethod · 0.80
decodeMethod · 0.80
insertMethod · 0.80
encodeMethod · 0.80
with_toolMethod · 0.80
with_top_pMethod · 0.80
with_prompt_cachingMethod · 0.80
countMethod · 0.80

Tested by

no test coverage detected