Overview
Edges define how nodes connect in a graph. They determine:
- Source and target nodes
- Conditions for traversal
- Data mapping between nodes
Unlike traditional graphs, Hive edges can be created dynamically by a Builder agent based on the goal.
Class: EdgeSpec
from framework.graph import EdgeSpec, EdgeCondition
edge = EdgeSpec(
id="calc-to-format",
source="calculator",
target="formatter",
condition=EdgeCondition.ON_SUCCESS,
input_mapping={"value_to_format": "result"}
)
Required Fields
Unique identifier for the edge
When to Traverse
condition
EdgeCondition
default:"EdgeCondition.ALWAYS"
When this edge should be traversed:
ALWAYS: Always after source completes
ON_SUCCESS: Only if source succeeds
ON_FAILURE: Only if source fails
CONDITIONAL: Based on expression evaluation
LLM_DECIDE: Let LLM decide based on goal and context
Expression for CONDITIONAL edges, e.g., "output.confidence > 0.8"
Data Flow
input_mapping
dict[str, str]
default:"{}"
Map source outputs to target inputs: {target_key: source_key}
Priority
Higher priority edges are evaluated first when multiple edges exist
Human-readable description of this edge
Edge Conditions
Enum: EdgeCondition
from framework.graph import EdgeCondition
Always traverse after source completes
Only traverse if source succeeds
Only traverse if source fails
Traverse based on expression evaluation (safe subset only)
Let LLM decide based on goal and context (goal-aware routing)
Methods
should_traverse()
Determine if this edge should be traversed.
await edge.should_traverse(
source_success=True,
source_output={"result": 42, "confidence": 0.9},
memory={"total": 100},
llm=llm_provider,
goal=goal_obj
)
Whether the source node succeeded
Output from the source node
Current shared memory state
llm
LLMProvider | None
default:"None"
LLM provider for LLM_DECIDE edges
goal
Goal | None
default:"None"
Goal object for LLM_DECIDE edges
True if the edge should be traversed
Map source outputs to target inputs.
target_inputs = edge.map_inputs(
source_output={"result": 42},
memory={"total": 100}
)
# Returns: {"value_to_format": 42}
Input dict for target node
Examples
Simple Success-Based Routing
EdgeSpec(
id="calc-to-format",
source="calculator",
target="formatter",
condition=EdgeCondition.ON_SUCCESS,
input_mapping={"value_to_format": "result"}
)
Conditional Routing
# Only proceed if confidence is high
EdgeSpec(
id="validate-to-publish",
source="validator",
target="publisher",
condition=EdgeCondition.CONDITIONAL,
condition_expr="output.confidence > 0.8",
description="Only publish if validation confidence > 80%"
)
# Route based on memory value
EdgeSpec(
id="check-to-retry",
source="api-call",
target="retry-handler",
condition=EdgeCondition.CONDITIONAL,
condition_expr="retries < 3 and error_type == 'rate_limit'",
)
LLM-Powered Routing (Goal-Aware)
EdgeSpec(
id="search-to-filter",
source="search_results",
target="filter_results",
condition=EdgeCondition.LLM_DECIDE,
description="Only filter if results need refinement to meet goal",
)
The LLM evaluates:
- Current goal and success criteria
- Source node output
- Shared memory state
- Edge description
And decides whether proceeding is the best next step.
Priority-Based Routing
# High priority: error handling
EdgeSpec(
id="node-to-error",
source="processor",
target="error_handler",
condition=EdgeCondition.ON_FAILURE,
priority=100, # Evaluated first
)
# Lower priority: normal flow
EdgeSpec(
id="node-to-next",
source="processor",
target="next_step",
condition=EdgeCondition.ON_SUCCESS,
priority=0,
)
Fallback Routing
# Primary path
EdgeSpec(
id="primary-path",
source="analyzer",
target="fast-processor",
condition=EdgeCondition.CONDITIONAL,
condition_expr="output.complexity < 5",
priority=10,
)
# Fallback path
EdgeSpec(
id="fallback-path",
source="analyzer",
target="thorough-processor",
condition=EdgeCondition.ALWAYS,
priority=0, # Lower priority
)
Conditional Expression Syntax
For CONDITIONAL edges, the condition_expr is evaluated using a safe subset of Python:
Available Context
output: Source node output dict
memory: Shared memory dict
result: Shorthand for output.get("result")
true / false: Boolean literals
- All memory keys are also available directly
Supported Operators
- Comparison:
==, !=, <, >, <=, >=
- Logical:
and, or, not
- Membership:
in, not in
- Arithmetic:
+, -, *, /, %
- Attribute access:
output.key
- Indexing:
output["key"]
Examples
# Simple comparison
"output.confidence > 0.8"
# Multiple conditions
"output.status == 'ready' and retries < 3"
# Memory access
"total_processed < max_items"
# Attribute access
"output.metadata.priority == 'high'"
# List membership
"output.category in ['A', 'B', 'C']"
# Complex logic
"(output.score > 90 or priority == 'urgent') and not already_processed"
Security
- Only safe operations are allowed (no
exec, eval, imports, etc.)
- AST-based whitelist validation
- Expressions are sandboxed
The input_mapping field maps source node outputs to target node inputs:
input_mapping = {
"target_key": "source_key",
"amount": "calculated_total",
"recipient": "validated_email",
}
Default Behavior
If input_mapping is empty, all source outputs are passed through to the target.
Resolution Order
- Try source output first
- Fall back to memory if not in output
Example
# Source output
source_output = {"result": 42, "status": "ok"}
# Shared memory
memory = {"user_id": 123, "total": 100}
# Edge with mapping
edge = EdgeSpec(
source="calculator",
target="formatter",
input_mapping={
"value": "result", # From source output
"user": "user_id", # From memory (not in output)
"format": "display_mode", # Will be None (not in either)
}
)
target_inputs = edge.map_inputs(source_output, memory)
# Result: {"value": 42, "user": 123}
Graph Patterns
Sequential Flow
edges = [
EdgeSpec(id="1", source="A", target="B", condition=EdgeCondition.ALWAYS),
EdgeSpec(id="2", source="B", target="C", condition=EdgeCondition.ALWAYS),
EdgeSpec(id="3", source="C", target="D", condition=EdgeCondition.ALWAYS),
]
Parallel Fan-Out
# Process executes A, B, C in parallel
edges = [
EdgeSpec(id="1", source="process", target="A", condition=EdgeCondition.ON_SUCCESS),
EdgeSpec(id="2", source="process", target="B", condition=EdgeCondition.ON_SUCCESS),
EdgeSpec(id="3", source="process", target="C", condition=EdgeCondition.ON_SUCCESS),
]
Conditional Branch
edges = [
EdgeSpec(
id="1",
source="validator",
target="processor_A",
condition=EdgeCondition.CONDITIONAL,
condition_expr="output.category == 'A'",
priority=10,
),
EdgeSpec(
id="2",
source="validator",
target="processor_B",
condition=EdgeCondition.CONDITIONAL,
condition_expr="output.category == 'B'",
priority=10,
),
EdgeSpec(
id="3",
source="validator",
target="default_processor",
condition=EdgeCondition.ALWAYS,
priority=0, # Fallback
),
]
Error Handling
edges = [
EdgeSpec(
id="success",
source="risky-operation",
target="next-step",
condition=EdgeCondition.ON_SUCCESS,
),
EdgeSpec(
id="failure",
source="risky-operation",
target="error-handler",
condition=EdgeCondition.ON_FAILURE,
),
]
Feedback Loop
# Retry up to 3 times
edges = [
EdgeSpec(
id="retry",
source="processor",
target="processor", # Self-loop
condition=EdgeCondition.CONDITIONAL,
condition_expr="not output.success and retries < 3",
),
EdgeSpec(
id="success",
source="processor",
target="next-step",
condition=EdgeCondition.ON_SUCCESS,
),
]