-
Notifications
You must be signed in to change notification settings - Fork 0
feat(flare-workflow): embedded durable workflow engine for agent orchestration #472
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
6fd550b
feat(flare-workflow): scaffold crate with typed DAG types + journal +…
getappz 5898637
feat(flare-workflow): journaled DAG engine core with retries + event bus
getappz 02c9da3
feat(flare-workflow): OpenFang step semantics — conditional/loop/fano…
getappz e4a92f5
feat(flare-workflow): durable waits — Sleep timers + WaitEvent promis…
getappz 09c17e0
feat(flare-workflow): recovery pass — crash-resume with exactly-once …
getappz d02b648
feat(flare-workflow): OpenFang JSON schema + compile + example workflows
getappz 0812c41
docs(flare-workflow): crate README — usage, durability, JSON workflows
getappz e996e40
chore: update Cargo.lock for flare-workflow crate
getappz b1c3562
feat(workflow): mcp__flare__workflow + agentflare workflow CLI + real…
getappz c856e20
feat(workflow): MCP handler tests + async cores for daemon-safe invoc…
getappz 08b8dc8
fix(flare-workflow): resolve code-review findings on task/447
getappz f07942f
fix(flare-workflow): drop unmaintained backoff crate, regen hakari wo…
getappz 8bbd85d
Merge remote-tracking branch 'origin/master' into task/447
getappz 96d3e4e
fix(flare-workflow): resolve CodeRabbit findings — durable sleep, upd…
getappz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| [package] | ||
| name = "flare-workflow" | ||
| version = "0.1.0" | ||
| edition = "2024" | ||
| rust-version = "1.91" | ||
| license = "Apache-2.0" | ||
| description = "Embedded durable workflow engine for agent orchestration — typed DAG steps with journaled execution, durable waits, and human-in-the-loop events." | ||
| publish = false | ||
|
|
||
| [dependencies] | ||
| async-trait = "0.1" | ||
| chrono = { version = "0.4", features = ["serde"] } | ||
| parking_lot = "0.12" | ||
| rand = "0.8" | ||
| rusqlite = { version = "0.40", features = ["bundled"] } | ||
| rusqlite_migration = "2" | ||
| serde = { version = "1", features = ["derive"] } | ||
| serde_json = "1" | ||
| thiserror = "2" | ||
| tokio = { version = "1", features = ["sync", "rt-multi-thread", "macros", "time"] } | ||
| tracing = "0.1" | ||
| uuid = { version = "1", features = ["serde", "v4", "v7"] } | ||
| db_kit = { package = "agentflare-db-kit", path = "../agentflare-db-kit" } | ||
| agentflare-workspace-hack = { version = "0.1", path = "../../agentflare-workspace-hack" } | ||
|
|
||
| [dev-dependencies] | ||
| tempfile = "3" | ||
|
|
||
| [lints.rust] | ||
| unsafe_code = "warn" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| # flare-workflow | ||
|
|
||
| Embedded **durable workflow engine** for agent orchestration — typed DAG | ||
| steps with journaled execution, durable waits, and human-in-the-loop events. | ||
| No external binary, no sidecar runtime: durability lives in agentflare's own | ||
| SQLite (via `agentflare-db-kit`). | ||
|
|
||
| This is the Cloudflare/Vercel-Workflows-style spine beneath the coordination | ||
| layer (items / claims / handoffs). | ||
|
|
||
| ## Design lineage | ||
|
|
||
| | Source | Adopted | License | | ||
| |---|---|---| | ||
| | SMG `wfaas` | typed DAG engine, retry/backoff, `StateStore` trait, event bus | Apache-2.0 | | ||
| | OpenFang | StepMode semantics, `{{input}}`/`{{var}}` templating, ErrorMode, JSON schema | MIT / Apache-2.0 | | ||
| | Restate | journal durability (CompletableEntry), durable timers/promises | BSL — design only, no code | | ||
| | DBOS | step outputs checkpointed with user state ⇒ exactly-once | MIT | | ||
|
|
||
| ## Usage | ||
|
|
||
| ```rust | ||
| use flare_workflow::{WorkflowDefinition, StepDefinition, WorkflowEngine, InMemoryStore}; | ||
| use flare_workflow::executor::FunctionStep; | ||
|
|
||
| let wf = WorkflowDefinition::new("wf", "wf") | ||
| .add_step(StepDefinition::new("a", "a", Arc::new(FunctionStep::new(|ctx| { | ||
| ctx.output = "hello".into(); | ||
| Box::pin(async { Ok(StepResult::Success) }) | ||
| })))) | ||
| .add_step(StepDefinition::new("b", "b", /* ... */).depends_on(&["a"])); | ||
|
|
||
| let engine = WorkflowEngine::<Ctx, InMemoryStore<Ctx>>::new(); | ||
| engine.register_workflow(wf)?; | ||
| let run = engine.start_workflow(WorkflowId::new("wf"), ctx, "input".into()).await?; | ||
| engine.wait_for_completion(run, "wf", Duration::from_secs(300)).await?; | ||
| ``` | ||
|
|
||
| ### Durable execution | ||
|
|
||
| - **Journal**: every terminal step result is appended (`StepRun` / `Sleep` / | ||
| `WaitEvent`); a step with a completed entry is **never re-executed**. | ||
| - **Recovery**: `engine.recover()` resumes `Running` runs from the SQLite | ||
| journal after a crash, skipping completed steps (exactly-once). | ||
| - **Durable waits**: `StepMode::Sleep { duration_secs }` (timer) and | ||
| `StepMode::WaitEvent { name, timeout_secs }` (promise) survive restart; | ||
| `engine.complete_event(run_id, name, result)` resolves them from anywhere | ||
| (journaled pre-delivery closes the notify-before-wait race). | ||
| - **Retries**: per-step `RetryPolicy` (fixed/exponential/linear backoff + | ||
| jitter), `is_retryable`, `RetryIndefinitely`, per-attempt timeout. | ||
|
|
||
| ### JSON workflows (agent pipelines) | ||
|
|
||
| OpenFang-style JSON definitions route prompts to agents via a `SendMessage` | ||
| hook: | ||
|
|
||
| ```rust | ||
| let json: JsonWorkflow = serde_json::from_str(WORKFLOW_JSON)?; | ||
| let wf = compile_workflow(&json, send_message)?; | ||
| engine.register_workflow(wf)?; | ||
| ``` | ||
|
|
||
| Step modes: `sequential` / `fan_out` / `collect` / `conditional` / `loop` / | ||
| `sleep` / `wait_event`; error modes `fail` / `skip` / `retry`; `{{input}}` and | ||
| `{{var}}` templating; per-step token accounting. | ||
|
|
||
| ## Store backends | ||
|
|
||
| - `InMemoryStore<D>` — default, tests/dev. | ||
| - `SqliteStore<D>` — durable, on `agentflare-db-kit` (WAL, migrations); | ||
| tables `workflow_runs` (authoritative JSON state), `journal` (append-only), | ||
| `step_state`, `run_vars` (queryable projections). | ||
|
|
||
| ## License | ||
|
|
||
| Apache-2.0. Restate ideas are adopted as design only (BSL 1.1); no Restate | ||
| code is copied. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: getappz/agentflare
Length of output: 7346
🏁 Script executed:
Repository: getappz/agentflare
Length of output: 50374
🏁 Script executed:
Repository: getappz/agentflare
Length of output: 32063
🏁 Script executed:
Repository: getappz/agentflare
Length of output: 477
Make the usage example compile as a doctest.
The block uses undeclared
Arc,Duration,StepResult,WorkflowId, andCtx. It also contains?, top-level.await, and/* ... */where an executor expression is required. Add valid hidden setup and an asyncResult-returning example function, or mark the block as non-Rust text.🤖 Prompt for AI Agents