reflex-workflow 0.0.1a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reflex_workflow-0.0.1a1/.gitignore +45 -0
- reflex_workflow-0.0.1a1/CHANGELOG.md +48 -0
- reflex_workflow-0.0.1a1/PKG-INFO +631 -0
- reflex_workflow-0.0.1a1/README.md +613 -0
- reflex_workflow-0.0.1a1/benchmarks/README.md +104 -0
- reflex_workflow-0.0.1a1/benchmarks/bench.py +709 -0
- reflex_workflow-0.0.1a1/examples/README.md +35 -0
- reflex_workflow-0.0.1a1/pyproject.toml +37 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/__init__.py +59 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/cron.py +209 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/dataclass.py +134 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/engine/__init__.py +13 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/engine/claim.py +646 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/engine/execute.py +907 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/engine/handles.py +433 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/engine/notify.py +180 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/engine/rows.py +216 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/engine/runner.py +599 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/engine/runtime.py +122 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/model.py +934 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/otel.py +193 -0
- reflex_workflow-0.0.1a1/src/reflex_workflow/py.typed +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
**/.DS_Store
|
|
2
|
+
**/*.pyc
|
|
3
|
+
**/__pycache__/
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
**/.ruff_cache/
|
|
7
|
+
.mypy_cache/
|
|
8
|
+
assets/external/*
|
|
9
|
+
dist/*
|
|
10
|
+
# Only the tracked examples; private apps under examples/ stay ignored. The
|
|
11
|
+
# pattern has a separator in the middle, so it is anchored to this directory
|
|
12
|
+
# and leaves the examples that live inside packages and tests alone.
|
|
13
|
+
examples/*
|
|
14
|
+
!examples/README.md
|
|
15
|
+
!examples/ruff.toml
|
|
16
|
+
!examples/playground/
|
|
17
|
+
.web
|
|
18
|
+
.states
|
|
19
|
+
.idea
|
|
20
|
+
.vscode
|
|
21
|
+
.coverage
|
|
22
|
+
.coverage.*
|
|
23
|
+
.venv
|
|
24
|
+
venv
|
|
25
|
+
requirements.txt
|
|
26
|
+
.pyi_generator_last_run
|
|
27
|
+
.pyi_generator_diff
|
|
28
|
+
reflex.db
|
|
29
|
+
.codspeed
|
|
30
|
+
.reflex-bench/
|
|
31
|
+
.env
|
|
32
|
+
.env.*
|
|
33
|
+
node_modules
|
|
34
|
+
package-lock.json
|
|
35
|
+
# ...except the javascript test harness, which CI installs with `npm ci`.
|
|
36
|
+
!tests/js/package-lock.json
|
|
37
|
+
*.pyi
|
|
38
|
+
.pre-commit-config.yaml
|
|
39
|
+
.claude/.worktrees
|
|
40
|
+
.claude/settings.local.json
|
|
41
|
+
CLAUDE.local.md
|
|
42
|
+
|
|
43
|
+
# Backups written by scripts/delete_automated_releases.sh
|
|
44
|
+
automated-releases-backup-*.json
|
|
45
|
+
tests/js/node_modules
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
## v0.0.1a1 (2026-11-05)
|
|
2
|
+
|
|
3
|
+
### Breaking Changes
|
|
4
|
+
|
|
5
|
+
- A wait's deadline now decides when the wait ends: once it has passed, `deliver` refuses the event and returns 0, even where no worker has reached the timeout step yet. Previously an event landing in that window won and the timeout's work was discarded, which let a caller delivering faster than the workers pass keep a deadline from ever being kept. An answer arriving just after the deadline is now refused rather than taken late. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
6
|
+
- Require Python 3.11 or newer; Python 3.10 is no longer supported. ([#7449](https://github.com/reflex-dev/reflex/issues/7449))
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- `run_workflows(suspends_when_idle=True)` is for a database that stops itself when nothing is querying it and charges for being up, such as Neon: the worker listens for nothing, because a held `LISTEN` is a connection such a database counts as work, and waits an hour between looks rather than thirty seconds. Measured on Neon, an idle worker with a listener never let compute suspend; without one it suspended five minutes after the last query. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
11
|
+
- A workflow can declare `__workflow_limit__ = Limit(by="customer", at_most=3)` to cap how much of one group runs at once, counted across every worker. Workers also take their tables in turn and share each pass between them, so a table with a backlog no longer holds up the rest. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
12
|
+
- Map `AttemptLog` onto your base and the engine records what each step attempt did — the step, the attempt number, the outcome, the error and how long it took — readable with `await run.history()`. Each record is written in the same transaction as the step it describes. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
13
|
+
- `connect_workflows(Session)` sets up a process to start runs, deliver events and read their state without running any steps, so a web process no longer has to start a worker it does not want. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
14
|
+
- Add `reflex-workflow`, a prototype of durable workflows where each workflow is a SQLAlchemy table and workers in the app run its steps on Postgres. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
15
|
+
- A step can return `fan_out(children, then=step)` to run many child runs at once and carry on when the last one finishes. Each child is an ordinary run with its own retries and errors, and `self.children(Child)` reads them back. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
16
|
+
- `Limit` now also says how often a group may start: `Limit(by="provider", at_most=5, rate=60, per=timedelta(minutes=1))`. Rates count in a token bucket that refills continuously, in a table the application maps with `RateBucket`. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
17
|
+
- A workflow whose own attribute takes one of the engine's column names — a `parent` relationship, or a column of that name holding something else — is refused where it is declared, instead of failing on the first `start()` or the first claim. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
18
|
+
- Workers hear about new work through Postgres `LISTEN`/`NOTIFY` rather than waiting for their next poll, so a run started or an event delivered by another process is picked up straight away. Polling remains the fallback. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
19
|
+
- Runs can wait and repeat: a step returns `wait_for(step, timeout=..., on_timeout=...)` to
|
|
20
|
+
park until `deliver(...)` hands it an event, or `every(step, schedule)` to run again on an
|
|
21
|
+
interval or a `Cron("0 9 * * MON-FRI", "America/Los_Angeles")` expression. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
22
|
+
- `@step(lane="media")` keeps a step on the workers started to serve that lane, with `run_workflows(..., lanes=["media"])`. Heavy work can have its own machines while ordinary steps run anywhere, and a worker skips tables it can run nothing of. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
23
|
+
- `run_workflows(on_idle=...)` reports the instant a worker is next waiting for, on the database's clock and only when it changes, and `reflex_workflow.wake(timeout)` makes the worker in this process look now and holds until it has nothing left to take. Together they let a platform wake a deployment that suspends when idle, so its timers fire without anyone visiting the app. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
24
|
+
- Workflows can be mixed into models on a `MappedAsDataclass` base by importing `Workflow`, `AttemptLog` and `RateBucket` from `reflex_workflow.dataclass`, which SQLAlchemy 2.1 requires and 2.0 warns about. The engine's columns stay out of the constructor, so a model reads as it did before the mixin was added. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
25
|
+
- Steps are traced with OpenTelemetry when `reflex-otel` is instrumented: every attempt gets a `<table>.<step>` span linked to the handler, request, or step that scheduled it, with the attempt number, outcome, and error on it. Step arguments and row columns are never recorded, beyond what a failed attempt's exception message quotes. ([#7635](https://github.com/reflex-dev/reflex/issues/7635))
|
|
26
|
+
|
|
27
|
+
### Bug Fixes
|
|
28
|
+
|
|
29
|
+
- A rate-limit group too long to name in the bucket key is named by a digest of itself, rather than failing the insert; a group the engine cannot claim no longer stops the rest of its table from being claimed. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
30
|
+
- `cancel()` no longer stops a run that began matching its predicate after it had read the rows to stop. Such a run was cancelled without its parent being told, leaving a fan-out waiting for a child that would never report. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
31
|
+
- An event `key` is remembered when the run runs the event, not when it is buffered, so a resend of an event the run discarded unrun is accepted instead of being refused forever. A run going round an `every(...)` schedule no longer holds a buffered event it will never reach, which used to refuse every later delivery. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
32
|
+
- `cancel()` over a large backlog no longer fails once the rows it matches would exceed a statement's parameter limit. It picks and stops them in one statement now, which is also one round trip rather than two. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
33
|
+
- `start()` stores a column the caller set to `None` as `None`, rather than falling back to the column's default. `deliver(..., restart=True)` leaves a cancelled step its lease, as `run()` does, so the restarted step waits for it instead of running beside it. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
34
|
+
- `reflex_workflow.wake` no longer returns while a step the worker took is still running, or before the step it continued into has run, so a host that suspends when it returns does not stop that work midway. ([#7382](https://github.com/reflex-dev/reflex/issues/7382))
|
|
35
|
+
- An event delivered while a run is running the event it held for a wait is now held for the run's next wait, instead of being refused and lost. A run that waits on the same step again, such as a conversation, no longer misses a message that arrives during that step. A second answer to a wait whose held event has not been taken yet is still refused. ([#7396](https://github.com/reflex-dev/reflex/issues/7396))
|
|
36
|
+
|
|
37
|
+
### Performance
|
|
38
|
+
|
|
39
|
+
- An idle worker now waits the same whether or not it can listen for notifications, and a listener that cannot reconnect backs off up to `max_idle_interval` instead of retrying every second. A database that suspends itself when nothing is querying — Neon, and other managed Postgres — is no longer held awake by a worker with nothing to do. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
40
|
+
- Claiming from a limited table and asking when work is next due now read indexes rather than scanning the table. On a 700k-row table with 500 groups a claim took 11.7s and is now 5ms, and the idle question 18ms and is now 1ms. Workflow tables gain an `ix_<table>_lease` index, so autogenerate a migration after upgrading. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
41
|
+
|
|
42
|
+
### Documentation
|
|
43
|
+
|
|
44
|
+
- The README covers using workflows from a Reflex app: why they go on their own `DeclarativeBase` rather than `rx.Model`, registering that base so `reflex db makemigrations` picks the tables up, and the session factory to give `run_workflows`. ([#7288](https://github.com/reflex-dev/reflex/issues/7288))
|
|
45
|
+
|
|
46
|
+
### Miscellaneous
|
|
47
|
+
|
|
48
|
+
- Type-check cleanly against SQLAlchemy 2.1. ([#7645](https://github.com/reflex-dev/reflex/issues/7645))
|