pub struct FlowRunner { /* private fields */ }Expand description
High-level helper that orchestrates the common load → execute → save pattern.
FlowRunner provides a convenient wrapper around the lower-level graph execution
API. It automatically handles session loading, execution, and persistence.
§When to Use FlowRunner
- Web services: Execute one step per HTTP request
- Interactive applications: Step-by-step workflow progression
- Simple demos: Minimal boilerplate for common use cases
§Performance
FlowRunner is lightweight and efficient:
- Creation cost: ~2 pointer copies (negligible)
- Memory overhead: 16 bytes (2 ×
Arc<T>) - Runtime cost: Identical to manual approach
§Examples
§Basic Usage
use graph_flow::{FlowRunner, Graph, InMemorySessionStorage, Session, SessionStorage};
use std::sync::Arc;
let graph = Arc::new(Graph::new("my_workflow"));
let storage = Arc::new(InMemorySessionStorage::new());
let runner = FlowRunner::new(graph, storage.clone());
// Create a session first
let session = Session::new_from_task("session_id".to_string(), "start_task");
storage.save(session).await?;
// Execute workflow step
let result = runner.run("session_id").await?;
println!("Response: {:?}", result.response);§Shared Runner Pattern (Recommended for Web Services)
use graph_flow::FlowRunner;
use std::sync::Arc;
// Application state
struct AppState {
flow_runner: Arc<FlowRunner>,
}
impl AppState {
fn new(runner: FlowRunner) -> Self {
Self {
flow_runner: Arc::new(runner),
}
}
}
// Request handler
async fn handle_request(
state: Arc<AppState>,
session_id: String,
) -> Result<String, Box<dyn std::error::Error>> {
let result = state.flow_runner.run(&session_id).await?;
Ok(result.response.unwrap_or_default())
}Implementations§
Source§impl FlowRunner
impl FlowRunner
Sourcepub fn new(graph: Arc<Graph>, storage: Arc<dyn SessionStorage>) -> Self
pub fn new(graph: Arc<Graph>, storage: Arc<dyn SessionStorage>) -> Self
Create a new FlowRunner from an Arc<Graph> and any SessionStorage implementation.
§Parameters
graph- The workflow graph to executestorage- Storage backend for session persistence
§Examples
use graph_flow::{FlowRunner, Graph, InMemorySessionStorage};
use std::sync::Arc;
let graph = Arc::new(Graph::new("my_workflow"));
let storage = Arc::new(InMemorySessionStorage::new());
let runner = FlowRunner::new(graph, storage);§With PostgreSQL Storage
use graph_flow::{FlowRunner, Graph, PostgresSessionStorage};
use std::sync::Arc;
let graph = Arc::new(Graph::new("my_workflow"));
let storage = Arc::new(
PostgresSessionStorage::connect("postgresql://localhost/mydb").await?
);
let runner = FlowRunner::new(graph, storage);Sourcepub async fn run(&self, session_id: &str) -> Result<ExecutionResult>
pub async fn run(&self, session_id: &str) -> Result<ExecutionResult>
Execute exactly one task for the given session_id and persist the updated session.
This method:
- Loads the session from storage
- Executes the current task
- Saves the updated session back to storage
- Returns the execution result
§Parameters
session_id- Unique identifier for the session to execute
§Returns
Returns the same ExecutionResult that Graph::execute_session does, so callers can
inspect the assistant’s response and the status (WaitingForInput, Completed, etc.).
§Errors
Returns an error if:
- The session doesn’t exist
- Task execution fails
- Storage operations fail
§Concurrency
This is a load → execute → save cycle with no locking: two concurrent
run calls for the same session id race, and the later save wins
(lost update). Serialize requests per session (e.g. per-session mutex,
sticky routing, or a queue) if your service can receive concurrent
requests for one session.
§Examples
§Basic Execution
let result = runner.run("test_session").await?;
match result.status {
graph_flow::ExecutionStatus::Completed => {
println!("Workflow completed: {:?}", result.response);
}
graph_flow::ExecutionStatus::WaitingForInput => {
println!("Waiting for user input: {:?}", result.response);
}
graph_flow::ExecutionStatus::Paused { next_task_id, reason } => {
println!("Paused, next task: {}, reason: {}", next_task_id, reason);
}
graph_flow::ExecutionStatus::Error(e) => {
eprintln!("Error: {}", e);
}
}§Interactive Loop
loop {
let result = runner.run("session_id").await?;
match result.status {
ExecutionStatus::Completed => break,
ExecutionStatus::WaitingForInput => {
// Get user input and update context
// Then continue loop
break; // For demo
}
ExecutionStatus::Paused { .. } => {
// Continue to next step
continue;
}
ExecutionStatus::Error(e) => {
eprintln!("Error: {}", e);
break;
}
}
}§Error Handling
match runner.run("nonexistent_session").await {
Ok(result) => {
println!("Success: {:?}", result.response);
}
Err(GraphError::SessionNotFound(session_id)) => {
eprintln!("Session not found: {}", session_id);
}
Err(GraphError::TaskExecutionFailed(msg)) => {
eprintln!("Task failed: {}", msg);
}
Err(e) => {
eprintln!("Other error: {}", e);
}
}Trait Implementations§
Source§impl Clone for FlowRunner
impl Clone for FlowRunner
Source§fn clone(&self) -> FlowRunner
fn clone(&self) -> FlowRunner
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for FlowRunner
impl !UnwindSafe for FlowRunner
impl Freeze for FlowRunner
impl Send for FlowRunner
impl Sync for FlowRunner
impl Unpin for FlowRunner
impl UnsafeUnpin for FlowRunner
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more