fluidgrids-workflows 2026.1030.1__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.
- fluidgrids_workflows-2026.1030.1/.gitignore +30 -0
- fluidgrids_workflows-2026.1030.1/CHANGELOG.md +144 -0
- fluidgrids_workflows-2026.1030.1/LICENSE +9 -0
- fluidgrids_workflows-2026.1030.1/PKG-INFO +356 -0
- fluidgrids_workflows-2026.1030.1/README.md +318 -0
- fluidgrids_workflows-2026.1030.1/pyproject.toml +124 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/__init__.py +175 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/__main__.py +7 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/_json.py +269 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/_project.py +115 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/_version.py +24 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/builder/__init__.py +1 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/builder/assemble.py +257 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/builder/descriptors.py +182 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/builder/refs.py +447 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/builder/workflow.py +1191 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/cli.py +258 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/__init__.py +264 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/allowlist.py +119 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/defaults.py +31 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/file.py +73 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/gate.py +208 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/schema/history-file.schema.json +2825 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/schema/run-event.schema.json +2636 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/schema/workflow-command.schema.json +1272 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/schema/workflow-task.schema.json +3948 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/schema.py +73 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/types.py +805 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/history/validate.py +276 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/__init__.py +132 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/_cron.py +975 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/canonical.py +206 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/diff.py +122 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/duration.py +73 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/executor_view.py +132 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/expressions.py +243 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/hashing.py +71 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/ir-v2.schema.json +716 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/layout.py +73 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/node_types.py +100 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/normalize.py +310 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/schedule.py +247 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/schema.py +45 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/ir/validate.py +895 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/lower.py +454 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/project/__init__.py +42 -0
- fluidgrids_workflows-2026.1030.1/src/fluidgrids_workflows/py.typed +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.venv/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# Tool caches and reports
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
.coverage.*
|
|
15
|
+
coverage.xml
|
|
16
|
+
htmlcov/
|
|
17
|
+
|
|
18
|
+
# Node (the TypeScript reference package installed for scripts/sync_fixtures.py)
|
|
19
|
+
node_modules/
|
|
20
|
+
|
|
21
|
+
# IDE and OS
|
|
22
|
+
.vscode/
|
|
23
|
+
.idea/
|
|
24
|
+
*.swp
|
|
25
|
+
.DS_Store
|
|
26
|
+
|
|
27
|
+
# Environment and local agent state
|
|
28
|
+
.env
|
|
29
|
+
.env.*
|
|
30
|
+
.claude/
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `fluidgrids-workflows` are recorded here. Versions are
|
|
4
|
+
CalVer (`YYYY.MDD.PATCH`); the release workflow stamps the version, so
|
|
5
|
+
`pyproject.toml` is not bumped by hand.
|
|
6
|
+
|
|
7
|
+
## Unreleased (R2 vendoring)
|
|
8
|
+
|
|
9
|
+
The Python side of SDK release R2 (BOFF-9905, work package B19-P2). Python
|
|
10
|
+
tier 2 is still Phase C, so no tier-2 options are added; the Python SDK reads
|
|
11
|
+
and checks R2 histories.
|
|
12
|
+
|
|
13
|
+
- The fixtures and the five JSON Schemas are vendored from
|
|
14
|
+
`fluidgrids-workflows-sdk-node` `8274143` (SDK R2: the C-GATE and C-SCHEMA
|
|
15
|
+
freeze plus the R2 runtime). `fixtures/histories/` now holds 78 histories:
|
|
16
|
+
the 53 R1 histories, unchanged, and 25 new ones (the 21 R2 conformance
|
|
17
|
+
histories plus `local-activity-cancelled-in-scope`,
|
|
18
|
+
`local-activity-abandoned-at-completion`,
|
|
19
|
+
`continue-as-new-r1-retry-options` and
|
|
20
|
+
`continue-as-new-r1-long-task-queue`).
|
|
21
|
+
- Histories that Phase B (R1) runs stored keep loading: the vendored schemas
|
|
22
|
+
leave `WORKFLOW_CONTINUED_AS_NEW.options` a plain object (an R1 run could
|
|
23
|
+
store a fractional `retryDelay`, a `backoffMultiplier` below 1 or a task
|
|
24
|
+
queue over 256 characters there), and the activity-shaped `CONTINUE_AS_NEW`
|
|
25
|
+
retry has the R1 number semantics.
|
|
26
|
+
- The command feature gate (C-GATE) is ported: `CommandFeature`,
|
|
27
|
+
`COMMAND_FEATURES`, `PHASE_B_CORE_FEATURES`, `PHASE_B_PARITY_FEATURES`,
|
|
28
|
+
`CORE_CHILD_OPTIONS`, `CORE_CAN_OPTIONS`, `command_features`,
|
|
29
|
+
`continue_as_new_retry_shape`, `CommandRelief`, `is_relief_command` and
|
|
30
|
+
`unsupported_feature_message`. `validate_commands` and
|
|
31
|
+
`is_workflow_command_enabled` take a feature set (`CommandGate`), and
|
|
32
|
+
`validate_commands` takes the lease's `relief`. The release levels `"core"`
|
|
33
|
+
and `"parity"` and `is_command_enabled` are deprecated: they keep their R1
|
|
34
|
+
meaning and are not the service's Phase B gate. `scripts/ts_reference.mjs`
|
|
35
|
+
records the gate's verdicts for every command batch, 23 new R2 batches
|
|
36
|
+
among them.
|
|
37
|
+
- `durable_phase_issues` (and `validate(..., refuse_phase_c=True)`) refuses
|
|
38
|
+
every `code.features` entry that is not a Phase B runtime feature: the Phase
|
|
39
|
+
C ones and unknown values (`PHASE_B_CODE_FEATURES`,
|
|
40
|
+
`PHASE_C_CODE_FEATURES`). The IR v2 schema gains
|
|
41
|
+
`config.retry`, `code.features` and `code.exports.workflows.<fn>.config`.
|
|
42
|
+
- `fluidgrids_workflows.history` gains the C-SCHEMA fields:
|
|
43
|
+
`WorkflowInfoSnapshot` adds `workflowId`, `runAttempt`, `memo`,
|
|
44
|
+
`versioningBehavior` and `retry` (and `timeouts` is typed);
|
|
45
|
+
`WorkflowIdTarget` adds `firstRunId`; `RecordLocalActivityCommand` adds
|
|
46
|
+
`nodeType`, `operation` and `summary`; `WorkflowTaskResult` adds
|
|
47
|
+
`unfinishedHandlers` and types `localActivityRequests`.
|
|
48
|
+
- New TypedDicts: `RunRetryPolicy`, `WorkflowTimeouts`, `WorkflowTaskRequest`,
|
|
49
|
+
`BuiltInQuery`, `LocalActivityRequest` (with `attempt`, `backoffMs` and
|
|
50
|
+
`summary`), `LocalActivityResult` and `UnfinishedHandler`. New constants:
|
|
51
|
+
`RUNTIME_RELEASE`, `WORKFLOW_ID_REUSE_POLICIES`,
|
|
52
|
+
`WORKFLOW_ID_CONFLICT_POLICIES`, `VERSIONING_BEHAVIORS`,
|
|
53
|
+
`WORKFLOW_TIMEOUT_TYPES` and `LOCAL_ACTIVITY_MAX_TIMEOUT_MS`, checked
|
|
54
|
+
against the TypeScript values in `fixtures/ts-vectors.json`.
|
|
55
|
+
- New tests compare every history TypedDict, command TypedDicts included,
|
|
56
|
+
with its definition in the vendored schemas: the keys, the required keys,
|
|
57
|
+
and the `Literal` values of every `enum` or `const` property (or the
|
|
58
|
+
constant tuple a `str`-typed enum is built from), so a schema change that
|
|
59
|
+
the Python types miss fails the suite. `WorkflowTaskRequest.protocol` and
|
|
60
|
+
`WorkflowTaskResult.protocol` are typed `Literal[1]`. Integer properties stay
|
|
61
|
+
`int | float`, as in R1: JSON Schema counts `1.0` as an integer.
|
|
62
|
+
- The TypeScript reference outputs come from the published R2,
|
|
63
|
+
`@fluidgrids/workflows@2026.1029.1` (npm `gitHead` `8274143`), pinned in
|
|
64
|
+
`scripts/ts-reference`. They differ from the 2026.1028.2 outputs only in
|
|
65
|
+
`authoring.generatorVersion` and in the history vectors for the new files
|
|
66
|
+
and constants.
|
|
67
|
+
The release workflow now runs the same `references` check and publishes
|
|
68
|
+
only when it passes, so a release cannot ship fixtures no published
|
|
69
|
+
TypeScript SDK backs.
|
|
70
|
+
|
|
71
|
+
## Unreleased (2026.1028.x)
|
|
72
|
+
|
|
73
|
+
Phase B of the code-first spec set (BOFF-9905, work package B2): the Python
|
|
74
|
+
SDK's part of managed durable functions. It pairs with
|
|
75
|
+
`@fluidgrids/workflows@2026.1028.2`.
|
|
76
|
+
|
|
77
|
+
- `wf.code_workflow(export_name, ...)` adds an `internal:code-workflow` node
|
|
78
|
+
with `input`, `task_queue`, `wait_for_completion`, `timeout_seconds`,
|
|
79
|
+
`fail_on_error` and `parent_close_policy`, lowering byte for byte like
|
|
80
|
+
`wf.codeWorkflow`. Options left out are not written; the service applies the
|
|
81
|
+
DB13 parent-close default.
|
|
82
|
+
- `wf.wait_for_signal` takes `consume` (`first` or `all`) and `until`
|
|
83
|
+
(`limitWaitUntil`, an ISO 8601 string or a timezone-aware `datetime`).
|
|
84
|
+
- `validate` ports the B1 rules: the warnings `codeWorkflow.closePolicyEndsChild`
|
|
85
|
+
and `codeWorkflow.timeoutIgnored`, the error `waitForSignal.invalidDeadline`
|
|
86
|
+
(with a port of `Date.parse` for ISO strings), and the schema's new
|
|
87
|
+
wait-for-signal fields.
|
|
88
|
+
- `durable_phase_issues` and `validate(..., refuse_phase_c=True)` report the
|
|
89
|
+
features that arrive in Phase C (`durable.phaseC`): Python bundles,
|
|
90
|
+
self-hosted workers, user activities and task queues other than `managed`.
|
|
91
|
+
`lower_project` returns them as `LowerResult.issues`, and the `lower` command
|
|
92
|
+
prints them under `issues`; `validate --refuse-phase-c` is the command-line
|
|
93
|
+
form.
|
|
94
|
+
- `fluidgrids_workflows.history`: the durable-function contract (enums,
|
|
95
|
+
TypedDicts, the four JSON Schemas vendored from the TypeScript SDK, command
|
|
96
|
+
and history validation, `load_history`, the activity allow-list and the
|
|
97
|
+
DB13 default).
|
|
98
|
+
- The fixtures are vendored from `fluidgrids-workflows-sdk-node` `9a02b2c` (tag 2026.1028.2),
|
|
99
|
+
including the 53 durable-function histories in `fixtures/histories/`; the
|
|
100
|
+
TypeScript reference outputs come from `@fluidgrids/workflows@2026.1028.2`.
|
|
101
|
+
- `conformance/hybrid-code-workflow/` is a Python-owned conformance fixture
|
|
102
|
+
with its TypeScript twin; `scripts/ts_reference.mjs` lowers the twin.
|
|
103
|
+
|
|
104
|
+
## 2026.1027.x
|
|
105
|
+
|
|
106
|
+
The first release: the Python half of Phase A of the code-first workflows spec
|
|
107
|
+
set (BOFF-9904, epic BOFF-9902).
|
|
108
|
+
|
|
109
|
+
- The tier-1 graph builder (`define_workflow`, `wf.trigger`, `wf.step` and a
|
|
110
|
+
helper for every internal node type, typed references and predicates,
|
|
111
|
+
`expr`, `edge`, `wf.connect`, `wf.edge_order_is`, sticky notes, `extra`),
|
|
112
|
+
lowering byte for byte to the same IR v2 as `@fluidgrids/workflows` on every
|
|
113
|
+
shared fixture.
|
|
114
|
+
- `to_ir` / `assemble_recording` with connection resolution, the layout
|
|
115
|
+
sidecar, the automatic layout and pinned data; `lower_project` for a project
|
|
116
|
+
directory, with a determinism check that imports the project twice.
|
|
117
|
+
- Ports of the IR tools: `normalize_definition`, `validate` (with `strict` and
|
|
118
|
+
`is_task_queue_known`), `ir_hash`, RFC 8785 canonical JSON, `stable_stringify`,
|
|
119
|
+
`diff_definitions`, `executor_view`, `auto_layout`, durations, schedule checks
|
|
120
|
+
and the `{{ … }}` reference parser; the IR v2 JSON Schema.
|
|
121
|
+
- `fluidgrids_workflows.project` loads the project's generated node clients
|
|
122
|
+
(`.fluidgrids/nodes.py`), and `op` / `trigger` / `Json` / `FileRef` are the
|
|
123
|
+
names those clients import.
|
|
124
|
+
- The `fluidgrids-workflows` command line: `lower`, `validate`, `hash`,
|
|
125
|
+
`normalize`.
|
|
126
|
+
- The shared fixtures and TypeScript reference outputs of
|
|
127
|
+
`@fluidgrids/workflows@2026.1027.1`, vendored with checksums.
|
|
128
|
+
- Cron schedules are checked by a port of `cron-parser` (`ir/_cron.py`)
|
|
129
|
+
instead of `croniter`, so `validate` gives the same verdict and fire times as
|
|
130
|
+
the TypeScript SDK, including the empty expression, `?`, `L`, `#`, `W`,
|
|
131
|
+
reversed ranges and lower-case or offset time zones. `croniter` is no longer
|
|
132
|
+
a dependency.
|
|
133
|
+
- `lower` keeps standard output for its JSON result: what the project prints
|
|
134
|
+
while it is imported (`print`, logging, C code writing to descriptor 1) goes
|
|
135
|
+
to standard error.
|
|
136
|
+
- The determinism check also re-imports project modules imported by absolute
|
|
137
|
+
name (`from workflows.helpers import X`), and lowering unloads them.
|
|
138
|
+
- A workflow module that calls `sys.exit` fails lowering with reason `import`.
|
|
139
|
+
- The command line reports every error as one `fluidgrids-workflows:` line,
|
|
140
|
+
reads files that are not valid UTF-8 with replacement characters as Node
|
|
141
|
+
does, and reads integers beyond the double range as `Infinity`, as
|
|
142
|
+
`JSON.parse` does.
|
|
143
|
+
- `validate` leaves `nodeId` out of a per-node issue when the node id is the
|
|
144
|
+
empty string, as the TypeScript SDK does.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Copyright (c) 2025 Algoshred Technologies Private Limited.
|
|
2
|
+
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software is proprietary and confidential. Unauthorized copying, modification, distribution, or use of this software, via any medium, is strictly prohibited without the express written permission of the copyright holders.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
8
|
+
|
|
9
|
+
For licensing inquiries, please contact: legal@algoshred.com
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: fluidgrids-workflows
|
|
3
|
+
Version: 2026.1030.1
|
|
4
|
+
Summary: Author FluidGrids workflows as code in Python: the tier-1 graph builder, byte-identical lowering to the workflow IR v2, and the IR tools (normalise, validate, hash).
|
|
5
|
+
Project-URL: Homepage, https://fluidgrids.ai
|
|
6
|
+
Project-URL: Repository, https://github.com/Algoshred/fluidgrids-workflows-sdk-python
|
|
7
|
+
Project-URL: Issues, https://github.com/Algoshred/fluidgrids-workflows-sdk-python/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/Algoshred/fluidgrids-workflows-sdk-python/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: FluidGrids <info@fluidgrids.ai>
|
|
10
|
+
Maintainer-email: FluidGrids <info@fluidgrids.ai>
|
|
11
|
+
License: Copyright (c) 2025 Algoshred Technologies Private Limited.
|
|
12
|
+
|
|
13
|
+
All rights reserved.
|
|
14
|
+
|
|
15
|
+
This software is proprietary and confidential. Unauthorized copying, modification, distribution, or use of this software, via any medium, is strictly prohibited without the express written permission of the copyright holders.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
18
|
+
|
|
19
|
+
For licensing inquiries, please contact: legal@algoshred.com
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Keywords: automation,fluidgrids,sdk,workflow,workflow-as-code
|
|
22
|
+
Classifier: Development Status :: 3 - Alpha
|
|
23
|
+
Classifier: Intended Audience :: Developers
|
|
24
|
+
Classifier: License :: Other/Proprietary License
|
|
25
|
+
Classifier: Operating System :: OS Independent
|
|
26
|
+
Classifier: Programming Language :: Python :: 3
|
|
27
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
32
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
33
|
+
Classifier: Typing :: Typed
|
|
34
|
+
Requires-Python: >=3.11
|
|
35
|
+
Requires-Dist: jsonschema<5,>=4.23
|
|
36
|
+
Requires-Dist: tzdata>=2025.2; sys_platform == 'win32'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# fluidgrids-workflows
|
|
40
|
+
|
|
41
|
+
Author [FluidGrids](https://fluidgrids.ai) workflows as code in Python. This
|
|
42
|
+
package is the Python half of Phase A of the code-first workflows spec set
|
|
43
|
+
(BOFF-9902 / BOFF-9904, `fluidgrids-specs/product/specs/code-first/`, spec 03):
|
|
44
|
+
|
|
45
|
+
- the tier-1 graph builder: Python that constructs a canvas workflow;
|
|
46
|
+
- lowering a Python project to the workflow IR, schema version 2, byte for
|
|
47
|
+
byte the same as `@fluidgrids/workflows` lowers the TypeScript twin of the
|
|
48
|
+
same graph;
|
|
49
|
+
- ports of the IR tools: `normalize_definition`, `validate`, `ir_hash`,
|
|
50
|
+
canonical JSON (RFC 8785), the structural diff, the executor view, the
|
|
51
|
+
automatic layout, durations and schedule checks;
|
|
52
|
+
- loading the typed node clients that `fluidgrids codegen --lang python`
|
|
53
|
+
generates (`.fluidgrids/nodes.py`);
|
|
54
|
+
- from Phase B (BOFF-9905): `wf.code_workflow`, which embeds a TypeScript
|
|
55
|
+
durable function in a graph, and `fluidgrids_workflows.history`, the
|
|
56
|
+
durable-function contract (event and command enums, the command and history
|
|
57
|
+
JSON Schemas, command and history validation, `load_history`).
|
|
58
|
+
|
|
59
|
+
Python durable (tier-2) workflow functions and the Python worker are Phase C
|
|
60
|
+
(BOFF-9906) and are not in this package yet; `lower` and `validate
|
|
61
|
+
--refuse-phase-c` report what needs them as `durable.phaseC`. Lifting (IR to
|
|
62
|
+
Python source) lives in the TypeScript package, which the service and the CLI
|
|
63
|
+
use for `fluidgrids pull --format py`.
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uv add fluidgrids-workflows # or: pip install fluidgrids-workflows
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Python 3.11 or later. The floor matches `fluidgrids-node-sdk` (spec 03 §1) and
|
|
72
|
+
the generated clients, which use `typing.Required`.
|
|
73
|
+
|
|
74
|
+
## Tier 1: the graph builder
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from fluidgrids_workflows import define_workflow, expr, triggers
|
|
78
|
+
from fluidgrids_workflows.project import nodes as n
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@define_workflow(key="lead-enrichment", name="Lead enrichment")
|
|
82
|
+
def lead_enrichment(wf):
|
|
83
|
+
inbound = wf.trigger(triggers.webhook, id="inbound", args={"path": "leads"})
|
|
84
|
+
enrich = wf.http(
|
|
85
|
+
id="enrich",
|
|
86
|
+
after=inbound,
|
|
87
|
+
connection="enrichment-api",
|
|
88
|
+
args={
|
|
89
|
+
"method": "GET",
|
|
90
|
+
"url": "https://person.clearbit.com/v2/combined/find",
|
|
91
|
+
"queryParams": {"email": wf.json.body.email},
|
|
92
|
+
},
|
|
93
|
+
retry={"maxRetries": 3, "retryDelay": 2000},
|
|
94
|
+
)
|
|
95
|
+
is_enterprise = wf.if_(
|
|
96
|
+
id="is-enterprise",
|
|
97
|
+
after=enrich,
|
|
98
|
+
condition=enrich.output.body.company.metrics.employees >= 500,
|
|
99
|
+
)
|
|
100
|
+
wf.step(
|
|
101
|
+
n.slack.send_message,
|
|
102
|
+
id="notify-sales",
|
|
103
|
+
after=is_enterprise.true,
|
|
104
|
+
connection="slack-ops",
|
|
105
|
+
args={
|
|
106
|
+
"channelId": "C0SALES",
|
|
107
|
+
"text": expr("New enterprise lead: ", enrich.output.body.person.name.fullName),
|
|
108
|
+
},
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The decorated function runs once, immediately, and records a graph;
|
|
113
|
+
`to_ir(workflow, connections=..., layout=...)` turns the recording into IR v2.
|
|
114
|
+
Lowering is a pure function of the builder calls, so the same source always
|
|
115
|
+
gives the same IR.
|
|
116
|
+
|
|
117
|
+
- Builder methods and keyword options are snake_case (`wf.if_`,
|
|
118
|
+
`wf.wait_for_signal`, `on_error`, `execution_settings`). Dictionary values
|
|
119
|
+
(`args`, `retry`, `config`, `extra`, …) keep the IR's own keys
|
|
120
|
+
(`maxRetries`, `channelId`) and lower byte for byte.
|
|
121
|
+
- Every node needs an `id` (`define_workflow(..., auto_ids=True)` derives them
|
|
122
|
+
from labels for quick scripts).
|
|
123
|
+
- References lower to the expression language the workers evaluate:
|
|
124
|
+
`enrich.output.a.b` becomes `{{ $node["enrich"].item.a.b }}` and `wf.json.x`
|
|
125
|
+
becomes `{{ $json.x }}`. Use `ref["key"]` for keys that are not identifiers,
|
|
126
|
+
are Python keywords, start with `_` or are reference method names, and
|
|
127
|
+
`ref[0]` for indices.
|
|
128
|
+
- Comparisons (`>=`, `==`, …, `contains`, `is_empty`, `matches`, the generic
|
|
129
|
+
`is_(operator, value)`) and `and_()` / `or_()` become the condition tree
|
|
130
|
+
`internal:if` evaluates. `and`, `or`, `not` and `in` raise, because they would
|
|
131
|
+
be evaluated by Python at definition time.
|
|
132
|
+
- `expr("text ", ref, " more")` concatenates text and references.
|
|
133
|
+
- Leaving an option out writes the default; `False` for `operation`, `version`
|
|
134
|
+
and edge `id` / `type` writes no such field; `None` is JSON `null`.
|
|
135
|
+
- `edge(source, id=..., type=..., target_handle=..., label=..., data=...,
|
|
136
|
+
mapping=..., extra=...)`, `wf.connect`, `wf.edge_order_is` and the `extra`
|
|
137
|
+
option on every node keep any stored definition expressible exactly.
|
|
138
|
+
- An edge without an explicit `id` gets
|
|
139
|
+
`e-<source>-<target>[-<sourceHandle>][-<targetHandle>]`; when that id is taken
|
|
140
|
+
the later edges get `-2`, `-3`, … in declaration order.
|
|
141
|
+
- `connection="name"` resolves to a connection id when the workflow is pushed;
|
|
142
|
+
an unknown name raises `UnresolvedConnectionsError`.
|
|
143
|
+
- Positions come from the layout sidecar `workflows/<key>.layout.json`, or the
|
|
144
|
+
automatic layout the AI draft generator uses.
|
|
145
|
+
|
|
146
|
+
The complete surface is pinned by the shared fixtures; `fixtures/README.md`
|
|
147
|
+
lists it.
|
|
148
|
+
|
|
149
|
+
### Durable functions in a graph
|
|
150
|
+
|
|
151
|
+
`wf.code_workflow("<exportName>", ...)` adds an `internal:code-workflow` node
|
|
152
|
+
that runs a `workflow()` function of the definition's code bundle as a durable
|
|
153
|
+
child run. In Phase B the bundle is a TypeScript one, given with
|
|
154
|
+
`define_workflow(code=...)`:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from datetime import UTC, datetime
|
|
158
|
+
|
|
159
|
+
process = wf.code_workflow(
|
|
160
|
+
"processOrder",
|
|
161
|
+
id="process-order",
|
|
162
|
+
after=received,
|
|
163
|
+
input={"order": wf.json.body}, # inputMapping
|
|
164
|
+
timeout_seconds=900, # 0 or absent: no node-level timeout
|
|
165
|
+
fail_on_error=False,
|
|
166
|
+
)
|
|
167
|
+
shipped = wf.wait_for_signal(
|
|
168
|
+
id="await-shipment",
|
|
169
|
+
after=process,
|
|
170
|
+
name="shipped",
|
|
171
|
+
consume="all", # or "first" (the default)
|
|
172
|
+
until=datetime(2026, 12, 1, tzinfo=UTC), # limitWaitUntil; or limit="2d"
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
As with every builder option, an option left out writes nothing and the
|
|
177
|
+
service applies the default: `task_queue` `managed`, `wait_for_completion`
|
|
178
|
+
`True`, and the parent-close policy of decision DB13, `TERMINATE` when the node
|
|
179
|
+
waits for the child and `ABANDON` when it does not
|
|
180
|
+
(`fluidgrids_workflows.history.code_workflow_parent_close_policy`).
|
|
181
|
+
`validate` warns about a fire-and-forget node with `TERMINATE` or
|
|
182
|
+
`REQUEST_CANCEL` (`codeWorkflow.closePolicyEndsChild`) or a timeout
|
|
183
|
+
(`codeWorkflow.timeoutIgnored`). `until` takes an ISO 8601 string or a
|
|
184
|
+
timezone-aware `datetime`.
|
|
185
|
+
|
|
186
|
+
### What arrives in Phase C
|
|
187
|
+
|
|
188
|
+
`durable_phase_issues(definition)` (in `fluidgrids_workflows.ir`) lists, with
|
|
189
|
+
code `durable.phaseC`, what a definition needs that Phase B does not run: a
|
|
190
|
+
Python code bundle, `determinism: "worker"`, `code.exports.activities` (user
|
|
191
|
+
activities), task queues other than `managed`, and every `code.features` entry
|
|
192
|
+
that is not a Phase B runtime feature: the Phase C ones (`userActivity`,
|
|
193
|
+
`updates`, `queries`, `searchAttributes`, `dynamic`, `interceptors`) and any
|
|
194
|
+
value the SDK does not know, so the check fails closed
|
|
195
|
+
(`PHASE_B_CODE_FEATURES`, `PHASE_C_CODE_FEATURES`). `lower_project` returns them
|
|
196
|
+
as `LowerResult.issues`, `fluidgrids-workflows lower` prints them under
|
|
197
|
+
`issues`, and `validate(..., refuse_phase_c=True)` (`validate
|
|
198
|
+
--refuse-phase-c`) makes them errors. Without that option `validate` gives the
|
|
199
|
+
TypeScript SDK's verdict.
|
|
200
|
+
|
|
201
|
+
## Projects and generated node clients
|
|
202
|
+
|
|
203
|
+
A project is a directory with workflow modules in `workflows/*.py`, optional
|
|
204
|
+
layout sidecars (`workflows/<key>.layout.json`), TEST-only pinned data
|
|
205
|
+
(`workflows/<key>.pinned.json`) and the generated clients in
|
|
206
|
+
`.fluidgrids/nodes.py`. Workflow modules import the clients with
|
|
207
|
+
`from fluidgrids_workflows.project import nodes as n`, which resolves to the
|
|
208
|
+
project being lowered, then `FLUIDGRIDS_PROJECT_DIR`, then the nearest
|
|
209
|
+
`.fluidgrids/nodes.py` at or above the working directory.
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from fluidgrids_workflows import lower_project
|
|
213
|
+
|
|
214
|
+
result = lower_project(".", connections={"slack-ops": {"connectionId": "c1", "adapter": "slack"}})
|
|
215
|
+
for workflow in result.workflows:
|
|
216
|
+
print(workflow.key, workflow.definition)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`lower_project` imports the project's modules, so it is for code the caller
|
|
220
|
+
trusts (the CLI lowering a developer's own project; services never run Python
|
|
221
|
+
workflow code, spec decision D1). It imports the project twice and refuses a
|
|
222
|
+
workflow whose graph changes between the two (`LowerError(reason="determinism")`),
|
|
223
|
+
which catches definitions that read the clock or a random source.
|
|
224
|
+
|
|
225
|
+
## Command line
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
fluidgrids-workflows lower [PROJECT] [--connections FILE] [--entry workflows/x.py]
|
|
229
|
+
fluidgrids-workflows validate FILE [--strict] [--allow-empty] [--refuse-phase-c]
|
|
230
|
+
fluidgrids-workflows hash FILE
|
|
231
|
+
fluidgrids-workflows normalize FILE
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Every command prints JSON; `FILE` may be `-` for standard input. The FluidGrids
|
|
235
|
+
CLI calls `lower` for Python projects; its result carries `issues` only when a
|
|
236
|
+
workflow needs a Phase C feature.
|
|
237
|
+
|
|
238
|
+
## The IR
|
|
239
|
+
|
|
240
|
+
`fluidgrids_workflows.ir` ports `@fluidgrids/workflows/ir`:
|
|
241
|
+
|
|
242
|
+
- `normalize_definition` rewrites the builder's legacy mirrors into the
|
|
243
|
+
canonical node fields following the executor's reading order; it is pure and
|
|
244
|
+
idempotent.
|
|
245
|
+
- `ir_hash` is `sha256:` + the SHA-256 of the RFC 8785 canonical JSON of the
|
|
246
|
+
normalised definition without `authoring`, so both SDKs and the canvas hash a
|
|
247
|
+
graph equally.
|
|
248
|
+
- `validate(definition, strict=..., is_task_queue_known=...)` applies the
|
|
249
|
+
service's structural rules and the version-2 rules of spec 01 §9. Issue codes
|
|
250
|
+
match the TypeScript validator; messages are English and may differ in
|
|
251
|
+
wording.
|
|
252
|
+
- `canonical_json`, `stable_stringify`, `diff_definitions`, `executor_view`,
|
|
253
|
+
`auto_layout`, `duration_ms`, `check_schedule_spec` and the `{{ … }}`
|
|
254
|
+
reference parser are exact ports.
|
|
255
|
+
- `ir_v2_schema()` is the IR v2 JSON Schema, vendored byte for byte from the
|
|
256
|
+
TypeScript package.
|
|
257
|
+
|
|
258
|
+
## The durable-function contract
|
|
259
|
+
|
|
260
|
+
`fluidgrids_workflows.history` is the Python side of
|
|
261
|
+
`@fluidgrids/workflows/history`, the contract the service, the workers and both
|
|
262
|
+
SDKs share:
|
|
263
|
+
|
|
264
|
+
- `EVENT_TYPES`, `COMMAND_TYPES`, `TASK_FAILURE_CAUSES`, the Phase B core,
|
|
265
|
+
parity and Phase C command sets, and the event classes, in the TypeScript
|
|
266
|
+
order;
|
|
267
|
+
- TypedDicts for events (`RunEventRecord`), every command, failures, retry
|
|
268
|
+
policies, task requests and results, local-activity requests and results,
|
|
269
|
+
run targets and history files; `tests/test_history.py` checks each one
|
|
270
|
+
against the vendored schemas;
|
|
271
|
+
- the runtime release the contract describes (`RUNTIME_RELEASE`, 2 from SDK
|
|
272
|
+
R2) and the R2 enums: workflow-id reuse and conflict policies, versioning
|
|
273
|
+
behaviours, workflow timeout types and `LOCAL_ACTIVITY_MAX_TIMEOUT_MS`;
|
|
274
|
+
- `history_schema(name)` for the four vendored JSON Schemas (`run-event`,
|
|
275
|
+
`workflow-command`, `workflow-task`, `history-file`);
|
|
276
|
+
- `validate_command`, `validate_commands` (shape, the gate, unique ids,
|
|
277
|
+
terminal command last, the activity allow-list), `validate_history` and
|
|
278
|
+
`validate_history_file`, with the TypeScript verdicts and failure causes;
|
|
279
|
+
- the command feature gate (C-GATE) the service applies: `command_features`,
|
|
280
|
+
`PHASE_B_CORE_FEATURES`, `PHASE_B_PARITY_FEATURES` (what
|
|
281
|
+
`fluidgrids.durable-parity` adds), `is_relief_command` and `CommandRelief`.
|
|
282
|
+
Pass a feature set as `validate_commands(..., enabled=PHASE_B_CORE_FEATURES)`
|
|
283
|
+
for the service's Phase B verdict. The default levels `"core"` and
|
|
284
|
+
`"parity"` (and `is_command_enabled`) are deprecated: they keep their R1
|
|
285
|
+
meaning, decide by command type only and are not the service's gate (`core`
|
|
286
|
+
accepts the R2 child options and workflow-id targets the service refuses
|
|
287
|
+
without the parity flag, and refuses `CONTINUE_AS_NEW`, which it accepts);
|
|
288
|
+
- `load_history(path)` / `parse_history_file(text)`, which raise
|
|
289
|
+
`HistoryFileError` for a file that does not match the schema;
|
|
290
|
+
- `is_activity_node_type_allowed` (the activity allow-list) and
|
|
291
|
+
`code_workflow_parent_close_policy` (DB13).
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
from fluidgrids_workflows.history import load_history
|
|
295
|
+
|
|
296
|
+
history = load_history("fixtures/histories/signal-wait.history.json")
|
|
297
|
+
print([event["type"] for event in history["events"]])
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Python does not replay histories in Phase B; the replay runner is the
|
|
301
|
+
TypeScript SDK's.
|
|
302
|
+
|
|
303
|
+
## Conformance with the TypeScript SDK
|
|
304
|
+
|
|
305
|
+
`fixtures/` is vendored from `fluidgrids-workflows-sdk-node` by
|
|
306
|
+
`scripts/sync_fixtures.py`, together with reference outputs the published
|
|
307
|
+
`@fluidgrids/workflows` computes (`<fixture>/lowered.ts.json` and
|
|
308
|
+
`fixtures/ts-vectors.json`). The test suite:
|
|
309
|
+
|
|
310
|
+
- lowers every fixture's `workflows/<key_snake>.py` and requires the canonical
|
|
311
|
+
JSON to equal the TypeScript lowering of `workflows/<key>.ts` (apart from the
|
|
312
|
+
provenance block) and the `irHash` to equal `fixture.json`;
|
|
313
|
+
- checks normalisation, hashing, the executor view, validation codes, canonical
|
|
314
|
+
JSON, SHA-256, durations, schedules, reference parsing, diffs and layouts
|
|
315
|
+
against the TypeScript outputs;
|
|
316
|
+
- lowers the Python-owned fixtures in `conformance/` (features that are only
|
|
317
|
+
sugar in a builder, such as `wf.code_workflow`, whose TypeScript twins the
|
|
318
|
+
lifted fixtures cannot express) and compares them with the TypeScript
|
|
319
|
+
lowering of their twin in the same directory;
|
|
320
|
+
- loads and validates every history in `fixtures/histories/` and compares the
|
|
321
|
+
history contract with `@fluidgrids/workflows/history`;
|
|
322
|
+
- checks `fixtures/CHECKSUMS.sha256`, so a hand edit to a vendored file
|
|
323
|
+
(fixtures and the five schemas) fails.
|
|
324
|
+
|
|
325
|
+
`scripts/ts-reference/` pins the `@fluidgrids/workflows` release the
|
|
326
|
+
references come from. To pick up new fixtures, bump that pin (`package.json`
|
|
327
|
+
and `npm install --package-lock-only`), then:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
make ts-reference
|
|
331
|
+
make sync-fixtures NODE_SDK=../fluidgrids-workflows-sdk-node
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
CI recomputes the TypeScript reference outputs from the pinned package
|
|
335
|
+
(`make verify-references`) and fails when they differ from the committed ones.
|
|
336
|
+
|
|
337
|
+
## Development
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
uv sync --locked
|
|
341
|
+
make lint # ruff check + ruff format --check
|
|
342
|
+
make typecheck # mypy --strict
|
|
343
|
+
make test # pytest with the 85% coverage gate
|
|
344
|
+
make sanity # all of the above, plus a build of the sdist and wheel
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Releases are CalVer (`YYYY.MDD.PATCH`) through `.github/workflows/release.yml`
|
|
348
|
+
(the shared G60 workflow): push a CalVer tag or run the workflow from the
|
|
349
|
+
Actions tab. The upload uses PyPI trusted publishing when the repository
|
|
350
|
+
variable `PYPI_TRUSTED_PUBLISHING` is `true`, otherwise the
|
|
351
|
+
`PYPI_BURDENOFF_TOKEN` secret; with neither, the publish job fails and names
|
|
352
|
+
the missing secret.
|
|
353
|
+
|
|
354
|
+
## License
|
|
355
|
+
|
|
356
|
+
Proprietary. Copyright (c) Algoshred Technologies Private Limited. See `LICENSE`.
|