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

Method execute_workflow_internal

python/src/workflow/executor.rs:1662–1782  ·  view source on GitHub ↗

Internal workflow execution with mode-specific optimizations and tool call handling. When `guardrail_enforcer` is `Some`, the core encodes before LLM and decodes after LLM; we decode before tool usage only (no encode after tool).

(
        llm_config: graphbit_core::llm::LlmConfig,
        workflow: graphbit_core::workflow::Workflow,
        config: ExecutionConfig,
        guardrail_enforcer: Option<Arc<Enforcer>>,
    )

Source from the content-addressed store, hash-verified

1660 /// When `guardrail_enforcer` is `Some`, the core encodes before LLM and decodes after LLM;
1661 /// we decode before tool usage only (no encode after tool).
1662 async fn execute_workflow_internal(
1663 llm_config: graphbit_core::llm::LlmConfig,
1664 workflow: graphbit_core::workflow::Workflow,
1665 config: ExecutionConfig,
1666 guardrail_enforcer: Option<Arc<Enforcer>>,
1667 ) -> Result<graphbit_core::types::WorkflowContext, graphbit_core::errors::GraphBitError> {
1668 let conditional_handlers =
1669 crate::workflow::node::build_core_conditional_handlers(&workflow)?;
1670 let executor = match config.mode {
1671 ExecutionMode::Balanced => CoreWorkflowExecutor::new()
1672 .with_default_llm_config(llm_config.clone())
1673 .with_conditional_handlers(conditional_handlers),
1674 };
1675
1676 // Execute the workflow (core applies encode before LLM, decode after LLM when enforcer is Some)
1677 let mut context = executor
1678 .execute(workflow.clone(), guardrail_enforcer.clone())
1679 .await?;
1680
1681 // Store LLM config in context metadata for tool call handling
1682 if let Ok(llm_config_json) = serde_json::to_value(&llm_config) {
1683 context
1684 .metadata
1685 .insert("llm_config".to_string(), llm_config_json);
1686 }
1687
1688 // Store workflow name in context metadata for result schema
1689 context.metadata.insert(
1690 "workflow_name".to_string(),
1691 serde_json::Value::String(workflow.name.clone()),
1692 );
1693
1694 // Check if any node outputs contain tool_calls_required responses and handle them
1695 let mut context = context;
1696 let mut rerun_attempts = 0;
1697 loop {
1698 let (ctx, nodes_with_tool_calls) = Self::handle_tool_calls_in_context(
1699 context,
1700 &workflow,
1701 guardrail_enforcer.as_ref().map(|arc| arc.as_ref()),
1702 )
1703 .await?;
1704 context = ctx;
1705
1706 if nodes_with_tool_calls.is_empty() {
1707 break;
1708 }
1709
1710 // Identify downstream nodes that depend on tool-resolved outputs and need rerun.
1711 // This includes nodes that themselves may have tool calls, as they can still depend on
1712 // upstream nodes whose outputs were just resolved.
1713 let mut downstream_nodes: HashSet<String> = HashSet::new();
1714 if let Some(deps_obj) = context.metadata.get("node_dependencies").and_then(|v| v.as_object()) {
1715 let mut queue: Vec<String> = nodes_with_tool_calls.clone();
1716 while let Some(parent_id) = queue.pop() {
1717 for (node_id, parents) in deps_obj.iter() {
1718 if downstream_nodes.contains(node_id) {
1719 continue;

Callers

nothing calls this directly

Calls 11

cloneMethod · 0.80
insertMethod · 0.80
to_stringMethod · 0.80
removeMethod · 0.80
execute_with_contextMethod · 0.80
executeMethod · 0.45
is_emptyMethod · 0.45
getMethod · 0.45

Tested by

no test coverage detected