FlowExecutionContext
Execution context passed to nodes during graph execution. Provides access to input/output data and shared state.
Overview
FlowExecutionContext is passed to every node’s ExecuteAsync method during graph execution. Use it to read inputs, write outputs, and manage shared state.
Properties
| Property | Type | Description |
|---|---|---|
| CancellationToken | CancellationToken | Cancellation token for long operations |
| Execution | FlowGraphExecution | Execution handler instance |
| Node | FlowNodeBase | Current node being executed |
| CustomData | Dictionary<string, object?> | Shared data across all nodes |
Socket Data Methods
GetInputSocketData
Gets data from an input socket.
Signatures:
object? GetInputSocketData(string socketName)T? GetInputSocketData<T>(string socketName)
public override async ValueTask ExecuteAsync(FlowExecutionContext context)
{
// Get typed input
var floatValue = context.GetInputSocketData<float>("InputA");
// Get untyped input
var value = context.GetInputSocketData("InputB");
// Handle optional input
var optional = context.GetInputSocketData<float?>("Optional") ?? 0f;
await ValueTask.CompletedTask;
}
SetOutputSocketData
Sets data for an output socket.
Signature: void SetOutputSocketData(string socketName, object? value)
public override async ValueTask ExecuteAsync(FlowExecutionContext context)
{
var result = ComputeResult();
// Set single output
context.SetOutputSocketData("Result", result);
// Set multiple outputs
context.SetOutputSocketData("Sum", a + b);
context.SetOutputSocketData("Product", a * b);
context.SetOutputSocketData("Average", (a + b) / 2);
await ValueTask.CompletedTask;
}
GetOutputSocketData
Gets data from an output socket.
Signatures:
object? GetOutputSocketData(string socketName)T? GetOutputSocketData<T>(string socketName)
// Usually called internally, but available if needed
var previousOutput = context.GetOutputSocketData<float>("Result");
State Management Methods
GetState
Gets a value from shared execution state.
Signature: T GetState<T>(string key)
public override async ValueTask ExecuteAsync(FlowExecutionContext context)
{
// Get shared counter
var counter = context.GetState<int>("globalCounter");
await ValueTask.CompletedTask;
}
SetState
Sets a value in shared execution state.
Signature: void SetState<T>(string key, T value)
public override async ValueTask ExecuteAsync(FlowExecutionContext context)
{
// Set shared counter
context.SetState("globalCounter", 42);
// Set complex object
context.SetState("userData", new UserData { Name = "John" });
await ValueTask.CompletedTask;
}
HasState
Checks if a key exists in execution state.
Signature: bool HasState(string key)
if (context.HasState("cachedResult"))
{
var result = context.GetState<float>("cachedResult");
}
RemoveState
Removes a key from execution state.
Signature: bool RemoveState(string key)
context.RemoveState("tempData");
Custom Data Dictionary
The CustomData dictionary is shared across all nodes in a single execution:
public override async ValueTask ExecuteAsync(FlowExecutionContext context)
{
// Store data
context.CustomData["userId"] = 123;
context.CustomData["timestamp"] = DateTime.Now;
// Retrieve data
if (context.CustomData.TryGetValue("userId", out var userId))
{
Console.WriteLine($"User ID: {userId}");
}
await ValueTask.CompletedTask;
}
Cancellation Support
Check cancellation token for long-running operations:
public override async ValueTask ExecuteAsync(FlowExecutionContext context)
{
for (int i = 0; i < 1000; i++)
{
// Check if cancelled
if (context.CancellationToken.IsCancellationRequested)
{
break;
}
// Do work
await ProcessItemAsync(i);
}
}
Complete Examples
Simple Math Node
[FlowNodeMetadata(Title = "Multiply", Category = "Math")]
public partial class MultiplyNode : FlowNodeBase
{
public override async ValueTask ExecuteAsync(FlowExecutionContext context)
{
// Get inputs
var a = context.GetInputSocketData<float>("A");
var b = context.GetInputSocketData<float>("B");
// Calculate
var result = a * b;
// Set output
context.SetOutputSocketData("Result", result);
await ValueTask.CompletedTask;
}
}
Node with State
[FlowNodeMetadata(Title = "Accumulator", Category = "Utility")]
public partial class AccumulatorNode : FlowNodeBase
{
public override async ValueTask ExecuteAsync(FlowExecutionContext context)
{
// Get or initialize accumulator
var total = context.GetState<float>("accumulatorTotal");
// Get input and add to total
var value = context.GetInputSocketData<float>("Value");
total += value;
// Save new total
context.SetState("accumulatorTotal", total);
// Output current total
context.SetOutputSocketData("Total", total);
await ValueTask.CompletedTask;
}
}
Node with Custom Data
[FlowNodeMetadata(Title = "Logger", Category = "Debug")]
public partial class LoggerNode : FlowNodeBase
{
public override async ValueTask ExecuteAsync(FlowExecutionContext context)
{
var message = context.GetInputSocketData<string>("Message");
// Get or create log list in CustomData
if (!context.CustomData.ContainsKey("logs"))
{
context.CustomData["logs"] = new List<string>();
}
var logs = (List<string>)context.CustomData["logs"]!;
logs.Add($"[{DateTime.Now:HH:mm:ss}] {message}");
// Output log count
context.SetOutputSocketData("Count", logs.Count);
await ValueTask.CompletedTask;
}
}
Async Operation with Cancellation
[FlowNodeMetadata(Title = "HTTP Fetch", Category = "Network")]
public partial class HttpFetchNode : FlowNodeBase
{
[Inject]
private HttpClient Http { get; set; } = null!;
public override async ValueTask ExecuteAsync(FlowExecutionContext context)
{
var url = context.GetInputSocketData<string>("URL");
try
{
// Use cancellation token
var response = await Http.GetStringAsync(url, context.CancellationToken);
context.SetOutputSocketData("Data", response);
context.SetOutputSocketData("Success", true);
}
catch (OperationCanceledException)
{
context.SetOutputSocketData("Success", false);
context.SetOutputSocketData("Error", "Operation cancelled");
}
catch (Exception ex)
{
context.SetOutputSocketData("Success", false);
context.SetOutputSocketData("Error", ex.Message);
}
}
}
State vs CustomData
Use State methods when:
- You want type-safe access
- You’re storing simple values
- You prefer convenience methods
Use CustomData when:
- You need dynamic key access
- You’re storing complex objects
- You want direct dictionary access
// State methods (type-safe, convenient)
context.SetState("count", 42);
var count = context.GetState<int>("count");
// CustomData (flexible, direct)
context.CustomData["count"] = 42;
var count = (int)context.CustomData["count"]!;
See Also
- FlowNodeBase - Base node class with ExecuteAsync
- FlowGraph - Graph execution
- Custom Nodes - Creating nodes with execution logic