Execution Limits
View SourceAgentForge provides execution limits to ensure that your workflows behave predictably and efficiently. This guide covers the use of timeouts and execution statistics to monitor and control your flows.
Table of Contents
Overview
When running complex workflows, especially those interacting with external systems or performing intensive computations, it's important to have safeguards against:
- Infinite loops
- Long-running operations
- Resource exhaustion
- Unresponsive services
AgentForge's execution limits provide these safeguards through timeouts and detailed statistics tracking.
Timeout Limits
Setting Timeouts
You can set a timeout in milliseconds for any flow processing:
# Create a handler
handler = fn signal, state ->
# Long-running operation...
{{:emit, Signal.new(:done, result)}, state}
end
# Apply a 5-second timeout
{:ok, result, state} = AgentForge.process_with_limits(
[handler],
signal,
%{},
timeout_ms: 5000 # 5 second timeout
)
If the processing exceeds the timeout, it will be terminated and return an error:
{:error, "Flow execution timed out after 5000ms", state}
Default Timeout
If not specified, the default timeout is 30 seconds (30,000 ms). You can adjust this based on your application's needs.
Execution Statistics
AgentForge can collect detailed statistics about flow execution, including:
- Number of steps executed
- Total execution time
- Completion status
- Result of execution
Collecting Statistics
Statistics are collected by default but not returned unless requested:
# Get statistics in the result
{:ok, result, state, stats} = AgentForge.process_with_limits(
handlers,
signal,
%{},
return_stats: true
)
# Examine the statistics
IO.inspect(stats.steps) # Number of steps executed
IO.inspect(stats.elapsed_ms) # Execution time in milliseconds
IO.inspect(stats.complete) # Whether execution completed normally
Retrieving Last Execution Statistics
Even if you don't request statistics in the return value, you can retrieve them afterward:
# Process without requesting statistics in the return
{:ok, result, state} = AgentForge.process_with_limits(handlers, signal, %{})
# Retrieve statistics later
stats = AgentForge.get_last_execution_stats()
This is particularly useful for logging and monitoring.
Error Handling
Execution limits can produce several error scenarios:
Timeout Errors
When a flow exceeds its time limit:
{:error, "Flow execution timed out after 5000ms", state}
With statistics:
{:error, "Flow execution timed out after 5000ms", state, stats}
Handler Errors
When a handler raises an exception:
{:error, "Flow processing error: ...", state}
Handling Errors Gracefully
Always wrap flow execution in appropriate error handling:
case AgentForge.process_with_limits(handlers, signal, state, timeout_ms: 5000) do
{:ok, result, new_state} ->
# Process completed successfully
handle_success(result, new_state)
{:error, "Flow execution timed out" <> _, state} ->
# Handle timeout specifically
handle_timeout(state)
{:error, error_message, state} ->
# Handle other errors
handle_error(error_message, state)
end
API Reference
AgentForge.process_with_limits/4
@spec process_with_limits(
[handler_function],
Signal.t(),
state_map,
options
) ::
{:ok, Signal.t(), state_map} |
{:ok, Signal.t(), state_map, ExecutionStats.t()} |
{:error, String.t(), state_map} |
{:error, String.t(), state_map, ExecutionStats.t()}
Options:
timeout_ms
: Maximum execution time in milliseconds (default: 30000)collect_stats
: Whether to collect execution statistics (default: true)return_stats
: Whether to include statistics in the return value (default: false)store_name
: Name of the store to use for state persistencestore_key
: Key within the store to access state
AgentForge.get_last_execution_stats/0
@spec get_last_execution_stats() :: ExecutionStats.t() | nil
Returns the statistics from the last flow execution or nil if none are available.
Examples
Basic Timeout Example
# Define a handler that may take too long
potentially_slow_handler = fn signal, state ->
result = perform_intensive_operation(signal.data)
{{:emit, Signal.new(:processed, result)}, state}
end
# Process with a timeout
case AgentForge.process_with_limits([potentially_slow_handler], signal, %{}, timeout_ms: 10000) do
{:ok, result, state} ->
IO.puts("Completed successfully: #{inspect(result.data)}")
{:error, error_message, _state} ->
IO.puts("Error: #{error_message}")
end
Collecting Performance Metrics
# Process and collect statistics
{:ok, result, _state, stats} = AgentForge.process_with_limits(
workflow,
signal,
%{},
return_stats: true
)
# Log performance metrics
Logger.info("Workflow completed in #{stats.elapsed_ms}ms with #{stats.steps} steps")
For a complete working example, see limited_workflow.exs in the examples directory.