floxide 1.1.0

A directed graph workflow system in Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# πŸš€ Floxide: The Power of Workflows in Rust

[![CI](https://github.com/aitoroses/floxide/actions/workflows/ci.yml/badge.svg)](https://github.com/aitoroses/floxide/actions/workflows/ci.yml)
[![Crates.io](https://img.shields.io/crates/v/floxide-core.svg)](https://crates.io/crates/floxide-core)
[![Documentation](https://docs.rs/floxide-core/badge.svg)](https://docs.rs/floxide-core)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

> A type-safe, composable directed graph workflow system written in Rust.

## πŸ’« Overview

Floxide transforms complex workflow orchestration into a delightful experience. Built with Rust's powerful type system at its core, Floxide provides a flexible, performant, and type-safe way to create sophisticated workflow graphs with crystal-clear transitions between steps.

## ✨ Key Features

- **πŸ”’ Type-Safe By Design**: Leverage Rust's type system for compile-time workflow correctness
- **🧩 Composable Architecture**: Build complex workflows from simple, reusable components
- **⚑ Async First**: Native support for asynchronous execution with Tokio
- **πŸ”„ Advanced Patterns**: Support for batch processing, event-driven workflows, and more
- **πŸ’Ύ State Management**: Built-in serialization for workflow persistence
- **πŸ” Observability**: Comprehensive tracing and monitoring capabilities
- **πŸ§ͺ Testable**: Design your workflows for easy testing and verification

## πŸ“ Architectural Decisions

This project follows documented architectural decisions recorded in ADRs (Architectural Decision Records). Each ADR captures the context, decision, and consequences of significant architectural choices. The development is guided by an LLM with rules defined in the `.cursorrules` file.

Key architectural decisions include:

- [**Core Framework Abstractions**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0003-core-framework-abstractions.md - Defining the fundamental abstractions like Node, Action, and Workflow with a trait-based approach for type safety and flexibility.

- [**Project Structure and Crate Organization**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0002-project-structure-and-crate-organization.md - Organizing the framework as a Cargo workspace with multiple specialized crates for modularity and separation of concerns.

- [**Async Runtime Selection**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0004-async-runtime-selection.md - Choosing Tokio as the primary async runtime for its comprehensive feature set and wide adoption.

- [**Node Lifecycle Methods**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0008-node-lifecycle-methods.md - Implementing a three-phase lifecycle (prep/exec/post) for workflow nodes to provide clear separation of concerns.

- [**Batch Processing Implementation**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0007-batch-processing-implementation.md - Designing a batch processing system that efficiently handles parallel execution with configurable concurrency limits.

- [**Event-Driven Workflow Pattern**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0009-event-driven-workflow-pattern.md - Extending the framework with event-driven capabilities for handling asynchronous events.

- [**Reactive Node Implementation**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0017-reactive-node-implementation.md - Creating nodes that can respond to changes in external data sources using a stream-based approach.

- [**Timer Node Implementation**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0021-timer-node-implementation.md - Supporting time-based scheduling for workflow execution with various scheduling patterns.

- [**Long-Running Node Implementation**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0022-longrunning-node-implementation.md - Enabling workflows to process work incrementally with state persistence between executions.

- [**Simplified Publishing with Maintained Subcrate Structure**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0033-implementing-single-package-with-features.md - Using cargo-workspaces for version management and publishing.

- [**Script Consolidation for Release Process**]https://github.com/aitoroses/floxide/tree/main/docs/adrs/0034-script-consolidation-for-release-process.md - Streamlining the release process with consolidated scripts.

## πŸ“¦ Release Process

Floxide uses [cargo-workspaces](https://github.com/pksunkara/cargo-workspaces) for version management and publishing. The release process is automated through a GitHub Actions workflow:

**Combined Release Workflow**: The `combined-release.yml` workflow handles the entire release process in one go. It can be triggered manually from the GitHub Actions tab with options for:
- Bump type (patch, minor, major)
- Dry run (preview without making changes)
- Whether to publish to crates.io
- Tagging options

This workflow combines version bumping, tagging, and publishing into a single, streamlined process.

For local development and testing, you can use the following scripts:
- `./scripts/release_with_workspaces.sh <version> [--dry-run] [--skip-publish]` - Bump version and optionally publish
- `./scripts/update_versions.sh` - Update subcrate versions to use workspace inheritance
- `./scripts/run_ci_locally.sh` - Run CI checks locally
- `./scripts/serve-docs.sh` - Serve documentation locally

For more details on the release process, see [ADR-0035: Combined Version Bump and Release Workflow](https://github.com/aitoroses/floxide/tree/main/docs/adrs/0035-combined-version-bump-and-release-workflow.md).

## πŸš€ Quick Start

Add Floxide to your project:

```toml
[dependencies]
floxide = { version = "1.0.0", features = ["transform", "event"] }
```

Create your first workflow:

```rust
use floxide::{lifecycle_node, LifecycleNode, Workflow, DefaultAction, FloxideError};
use async_trait::async_trait;
use std::sync::Arc;

// Define your context type
#[derive(Debug, Clone)]
struct MessageContext {
    input: String,
    result: Option<String>,
}

// Create a node using the convenience function
fn create_processor_node() -> impl LifecycleNode<MessageContext, DefaultAction> {
    lifecycle_node(
        Some("processor"), // Node ID
        |ctx: &mut MessageContext| async move {
            // Preparation phase
            println!("Preparing to process: {}", ctx.input);
            Ok(ctx.input.clone())
        },
        |input: String| async move {
            // Execution phase
            println!("Processing message...");
            Ok(format!("βœ… Processed: {}", input))
        },
        |_prep, exec_result, ctx: &mut MessageContext| async move {
            // Post-processing phase
            ctx.result = Some(exec_result);
            Ok(DefaultAction::Next)
        },
    )
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a context
    let mut context = MessageContext {
        input: "Hello, Floxide!".to_string(),
        result: None,
    };

    // Create a node and workflow
    let node = Arc::new(create_processor_node());
    let mut workflow = Workflow::new(node);

    // Execute the workflow
    workflow.execute(&mut context).await?;

    // Print the result
    println!("Result: {:?}", context.result);

    Ok(())
}
```

## πŸ“¦ Feature Flags

Floxide uses feature flags to allow you to include only the functionality you need:

| Feature | Description | Dependencies |
|---------|-------------|-------------|
| `core` | Core abstractions and functionality (default) | None |
| `transform` | Transform node implementations | `core` |
| `event` | Event-driven workflow functionality | `core` |
| `timer` | Time-based workflow functionality | `core` |
| `longrunning` | Long-running process functionality | `core` |
| `reactive` | Reactive workflow functionality | `core` |
| `full` | All features | All of the above |

Example of using specific features:

```toml
# Only include core and transform functionality
floxide = { version = "1.0.0", features = ["transform"] }

# Include event-driven and timer functionality
floxide = { version = "1.0.0", features = ["event", "timer"] }

# Include all functionality
floxide = { version = "1.0.0", features = ["full"] }
```

## 🧩 Workflow Pattern Examples

Floxide supports a wide variety of workflow patterns through its modular crate system. Each pattern is designed to solve specific workflow challenges:

### πŸ”„ Simple Chain (Linear Workflow)

A basic sequence of nodes executed one after another. This is the foundation of all workflows.

```mermaid
graph LR
    A["Process Data"] --> B["Format Output"] --> C["Store Result"]
    style A fill:#c4e6ff,stroke:#1a73e8,stroke-width:2px,color:black
    style B fill:#c4e6ff,stroke:#1a73e8,stroke-width:2px,color:black
    style C fill:#c4e6ff,stroke:#1a73e8,stroke-width:2px,color:black
```

**Example:** [lifecycle_node.rs](https://github.com/aitoroses/floxide/tree/main/examples/lifecycle_node.rs)

### 🌲 Conditional Branching

Workflows that make decisions based on context data or node results, directing flow through different paths.

```mermaid
graph TD
    A["Validate Input"] -->|Valid| B["Process Data"]
    A -->|Invalid| C["Error Handler"]
    B -->|Success| D["Format Output"]
    B -->|Error| C
    style A fill:#c4e6ff,stroke:#1a73e8,stroke-width:2px,color:black
    style B fill:#c4e6ff,stroke:#1a73e8,stroke-width:2px,color:black
    style C fill:#ffcccc,stroke:#e53935,stroke-width:2px,color:black
    style D fill:#c4e6ff,stroke:#1a73e8,stroke-width:2px,color:black
```

**Example:** [order_processing.rs](https://github.com/aitoroses/floxide/tree/main/examples/order_processing.rs)

### πŸ”„ Transform Pipeline

A specialized workflow for data transformation, where each node transforms input to output in a functional style.

```mermaid
graph LR
    A["Raw Data"] --> B["Validate"] --> C["Transform"] --> D["Format"] --> E["Output"]
    style A fill:#e8f5e9,stroke:#43a047,stroke-width:2px,color:black
    style B fill:#e8f5e9,stroke:#43a047,stroke-width:2px,color:black
    style C fill:#e8f5e9,stroke:#43a047,stroke-width:2px,color:black
    style D fill:#e8f5e9,stroke:#43a047,stroke-width:2px,color:black
    style E fill:#e8f5e9,stroke:#43a047,stroke-width:2px,color:black
```

**Example:** [transform_node.rs](https://github.com/aitoroses/floxide/tree/main/examples/transform_node.rs)

### πŸ”€ Parallel Batch Processing

Process multiple items concurrently with controlled parallelism, ideal for high-throughput data processing.

```mermaid
graph TD
    A["Batch Input"] --> B["Split Batch"]
    B --> C1["Process Item 1"]
    B --> C2["Process Item 2"]
    B --> C3["Process Item 3"]
    C1 --> D["Aggregate Results"]
    C2 --> D
    C3 --> D
    style A fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:black
    style B fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:black
    style C1 fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:black
    style C2 fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:black
    style C3 fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:black
    style D fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:black
```

**Example:** [batch_processing.rs](https://github.com/aitoroses/floxide/tree/main/examples/batch_processing.rs)

### πŸ“‘ Event-Driven Flow

Workflows that respond to external events, ideal for building reactive systems that process events as they arrive.

```mermaid
graph TD
    A["Event Source"] -->|Events| B["Event Classifier"]
    B -->|Type A| C["Handler A"]
    B -->|Type B| D["Handler B"]
    B -->|Type C| E["Handler C"]
    C --> F["Event Source"]
    D --> F
    E --> F
    style A fill:#e8eaf6,stroke:#3949ab,stroke-width:2px,color:black
    style B fill:#e8eaf6,stroke:#3949ab,stroke-width:2px,color:black
    style C fill:#e8eaf6,stroke:#3949ab,stroke-width:2px,color:black
    style D fill:#e8eaf6,stroke:#3949ab,stroke-width:2px,color:black
    style E fill:#e8eaf6,stroke:#3949ab,stroke-width:2px,color:black
    style F fill:#e8eaf6,stroke:#3949ab,stroke-width:2px,color:black
```

**Example:** [event_driven_workflow.rs](https://github.com/aitoroses/floxide/tree/main/examples/event_driven_workflow.rs)

### ⏱️ Time-Based Workflows

Workflows that execute based on time schedules, supporting one-time, interval, and calendar-based scheduling.

```mermaid
graph TD
    A["Timer Source"] -->|Trigger| B["Scheduled Task"]
    B --> C["Process Result"]
    C -->|Reschedule| A
    style A fill:#fff8e1,stroke:#ff8f00,stroke-width:2px,color:black
    style B fill:#fff8e1,stroke:#ff8f00,stroke-width:2px,color:black
    style C fill:#fff8e1,stroke:#ff8f00,stroke-width:2px,color:black
```

**Example:** [timer_node.rs](https://github.com/aitoroses/floxide/tree/main/examples/timer_node.rs)

### πŸ”„ Reactive Workflows

Workflows that react to changes in external data sources, such as files, databases, or streams.

```mermaid
graph TD
    A["Data Source"] -->|Change| B["Change Detector"]
    B --> C["Process Change"]
    C --> D["Update State"]
    D --> A
    style A fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:black
    style B fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:black
    style C fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:black
    style D fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:black
```

**Example:** [reactive_node.rs](https://github.com/aitoroses/floxide/tree/main/examples/reactive_node.rs)

### ⏸️ Long-Running Processes

Workflows for processes that can be suspended and resumed, with state persistence between executions.

```mermaid
graph TD
    A["Start Process"] --> B["Execute Step"]
    B -->|Complete| C["Final Result"]
    B -->|Suspend| D["Save State"]
    D -->|Resume| B
    style A fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:black
    style B fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:black
    style C fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:black
    style D fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:black
```

**Example:** [longrunning_node.rs](https://github.com/aitoroses/floxide/tree/main/examples/longrunning_node.rs)

### πŸ€– Multi-Agent LLM System

A workflow pattern for orchestrating multiple AI agents that collaborate to solve complex tasks.

```mermaid
graph TD
    A["User Input"] --> B["Router Agent"]
    B -->|Research Task| C["Research Agent"]
    B -->|Code Task| D["Coding Agent"]
    B -->|Analysis Task| E["Analysis Agent"]
    C --> F["Aggregator Agent"]
    D --> F
    E --> F
    F --> G["Response Generator"]
    G --> H["User Output"]
    style A fill:#e0f7fa,stroke:#00838f,stroke-width:2px,color:black
    style B fill:#e0f7fa,stroke:#00838f,stroke-width:2px,color:black
    style C fill:#e0f7fa,stroke:#00838f,stroke-width:2px,color:black
    style D fill:#e0f7fa,stroke:#00838f,stroke-width:2px,color:black
    style E fill:#e0f7fa,stroke:#00838f,stroke-width:2px,color:black
    style F fill:#e0f7fa,stroke:#00838f,stroke-width:2px,color:black
    style G fill:#e0f7fa,stroke:#00838f,stroke-width:2px,color:black
    style H fill:#e0f7fa,stroke:#00838f,stroke-width:2px,color:black
```

This pattern demonstrates how to build a multi-agent LLM system where specialized agents handle different aspects of a task. Each agent is implemented as a node in the workflow, with the router determining which agents to invoke based on the task requirements. The aggregator combines results from multiple agents before generating the final response.

**Implementation Example:**

```rust
// Define agent context
#[derive(Debug, Clone)]
struct AgentContext {
    user_query: String,
    agent_responses: HashMap<String, String>,
    final_response: Option<String>,
}

// Create router agent node
fn create_router_agent() -> impl LifecycleNode<AgentContext, AgentAction> {
    lifecycle_node(
        Some("router"),
        |ctx: &mut AgentContext| async move {
            // Preparation: analyze the query
            println!("Router analyzing query: {}", ctx.user_query);
            Ok(ctx.user_query.clone())
        },
        |query: String| async move {
            // Execution: determine which agents to invoke
            let requires_research = query.contains("research") || query.contains("information");
            let requires_coding = query.contains("code") || query.contains("program");
            let requires_analysis = query.contains("analyze") || query.contains("evaluate");

            Ok((requires_research, requires_coding, requires_analysis))
        },
        |_prep, (research, coding, analysis), ctx: &mut AgentContext| async move {
            // Post-processing: route to appropriate agents
            if research {
                return Ok(AgentAction::Research);
            } else if coding {
                return Ok(AgentAction::Code);
            } else if analysis {
                return Ok(AgentAction::Analyze);
            }
            Ok(AgentAction::Aggregate) // Default if no specific routing
        },
    )
}

// Similar implementations for research_agent, coding_agent, analysis_agent, and aggregator_agent
```

## πŸ“š Examples & Documentation

Explore our extensive examples and documentation:

- [Complete API Documentation]https://docs.rs/floxide-core
- [Example Workflows]https://github.com/aitoroses/floxide/tree/main/examples
- [Architectural Decision Records]https://github.com/aitoroses/floxide/tree/main/docs/adrs

Try our examples directly:

```bash
git clone https://github.com/aitoroses/floxide.git
cd floxide
cargo run --example lifecycle_node
```

## 🀝 Contributing

We welcome contributions of all kinds! Whether you're fixing a bug, adding a feature, or improving documentation, your help is appreciated.

See our [Contributing Guidelines](CONTRIBUTING.md) for more details on how to get started.

## πŸ“„ License

Floxide is available under the MIT License - see the [LICENSE](LICENSE) file for details.

## πŸ™ Acknowledgments

- The Rust community for their excellent crates and support
- Our amazing contributors who help make Floxide better every day
<!-- Trigger rebuild: martes, 25 de febrero de 2025, 18:49:33 CET -->