Skip to content

Add script/code block action to FlowDefinition#6197

Merged
vinibrsl merged 2 commits into
mainfrom
flow-script-action
Jun 18, 2026
Merged

Add script/code block action to FlowDefinition#6197
vinibrsl merged 2 commits into
mainfrom
flow-script-action

Conversation

@vinibrsl

@vinibrsl vinibrsl commented Jun 17, 2026

Copy link
Copy Markdown
Member

Let a Flow method run trusted inline Python with call: script. The code is compiled once into a generated function and receives the runtime values as arguments.

methods:
  normalize:
    start: true
    do:
      call: script
      code: |
        import math
        state["rounded"] = math.ceil(state["raw_score"])
        return f"rounded:{state['rounded']}"

Even though this shares the same surface of tools (custom code), I decided to make it opt-in for now, using CREWAI_ALLOW_FLOW_SCRIPT_EXECUTION=1.


Note

High Risk
Introduces arbitrary trusted Python execution via flow YAML when an env var is set; misconfiguration or untrusted definitions could run hostile code with full process privileges (not sandboxed).

Overview
Adds a new call: script flow method action so YAML/JSON definitions can run trusted inline Python without a separate importable code ref. The schema introduces FlowScriptActionDefinition (code, optional language: python), wired through ScriptAction in the runtime action builder alongside existing expression/crew/tool actions.

At build time, script source is parsed with ast, wrapped in a generated _flow_script(state, outputs, input, item) function, and exec’d once. Runtime values are passed as arguments (not string-interpolated into the source). Scripts can mutate flow state, read prior step outputs, listener trigger input, and each iteration item.

Security: execution is off by default. Enabling requires CREWAI_ALLOW_FLOW_SCRIPT_EXECUTION=1 (also true/yes). Without it, Flow.from_definition raises FlowScriptExecutionDisabledError explicitly instead of treating the method as an unresolvable action.

Refactor: method output naming/merging for CEL and scripts is centralized in new outputs_by_name (_outputs.py), replacing duplicated logic in _expressions.py.

Reviewed by Cursor Bugbot for commit 5efd07c. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Flows now support executing inline Python scripts via a new call: "script" action type. Scripts can access and modify flow state, read outputs from previous steps, and process inputs/items, with security controls requiring explicit opt-in via environment variable.
  • Improvements

    • Enhanced error handling during action building to properly surface runtime errors.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a call: "script" flow action type (FlowScriptActionDefinition) that executes trusted inline Python code. A new _outputs.py module provides a shared outputs_by_name helper adopted by both the CEL expression context builder (replacing a file-local variant) and the new ScriptAction runtime, which wraps scripts into a _flow_script(state, outputs, input, item) function executed via ast.parse and exec.

Changes

Script Action Feature

Layer / File(s) Summary
FlowScriptActionDefinition contract and union updates
lib/crewai/src/crewai/flow/flow_definition.py
Adds the FlowScriptActionDefinition Pydantic model with call: Literal["script"], code: str, and language: Literal["python"] fields, inserts it into both FlowInnerActionDefinition and FlowActionDefinition unions, and adds it to __all__.
Shared outputs_by_name helper
lib/crewai/src/crewai/flow/runtime/_outputs.py
Introduces _outputs.py with outputs_by_name (aggregates method outputs by name, merges optional local outputs, applies optional serialization via to_serializable) and _output_value helper.
CEL expression context refactor
lib/crewai/src/crewai/flow/runtime/_expressions.py
Refactors to import and use outputs_by_name instead of a file-local helper, and simplifies _expression_context to filter outputs and state from local_context before updating the CEL context.
ScriptAction runtime implementation
lib/crewai/src/crewai/flow/runtime/_actions.py
Adds imports (ast, FlowScriptActionDefinition), environment gating via CREWAI_ALLOW_FLOW_SCRIPT_EXECUTION, the ScriptAction class that wraps scripts into a _flow_script(state, outputs, input, item) function via ast.parse/exec, and registers it in the _ACTION_TYPES dispatch table.
RuntimeError propagation fix
lib/crewai/src/crewai/flow/runtime/__init__.py
Ensures RuntimeError from build_action is explicitly re-raised instead of being caught and treated as an unresolvable action, allowing script execution environment checks to fail fast.
Test coverage for script action
lib/crewai/tests/test_flow_definition.py, lib/crewai/tests/test_flow_from_definition.py
Adds FlowScriptActionDefinition to the expected __all__ export set, and adds four integration tests covering: script execution gating via environment variable; basic script execution with imports and state mutation; script listener reading input and prior outputs; and script usage inside an each action with per-item results.
Checkpoint test refactor
lib/crewai/tests/test_checkpoint.py
Simplifies the legacy outputs readability regression test to assert only that flow.method_outputs remains readable, removing the prior check of _expression_context output handling.

Sequence Diagram(s)

sequenceDiagram
  participant FlowRunner
  participant build_action
  participant ScriptAction
  participant outputs_by_name
  participant _flow_script

  FlowRunner->>build_action: FlowScriptActionDefinition(code=...)
  build_action->>ScriptAction: construct ScriptAction
  ScriptAction->>ScriptAction: ast.parse(code) + exec wrapper
  Note over ScriptAction: Compiled _flow_script function created
  build_action-->>FlowRunner: ScriptAction instance

  Note over FlowRunner: At invocation time
  FlowRunner->>ScriptAction: run(...)
  ScriptAction->>outputs_by_name: flow._method_outputs + local_context["outputs"]
  outputs_by_name-->>ScriptAction: dict[str, Any]
  ScriptAction->>_flow_script: (state, outputs, input, item)
  _flow_script-->>ScriptAction: return value
  ScriptAction-->>FlowRunner: result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Suggested reviewers

  • joaomdmoura
  • akaKuruma
  • greysonlalonde
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add script/code block action to FlowDefinition' accurately and concisely describes the main change: introducing a new 'call: script' action type to Flow method definitions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch flow-script-action

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vinibrsl vinibrsl force-pushed the flow-state-discriminated-union branch from fa2edd7 to 218696f Compare June 17, 2026 04:21
Base automatically changed from flow-state-discriminated-union to main June 17, 2026 04:31
@vinibrsl vinibrsl force-pushed the flow-script-action branch from 69e5eb5 to b69e5cc Compare June 17, 2026 04:32

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/crewai/src/crewai/flow/runtime/_actions.py`:
- Around line 167-173: The exec(compile(...)) call that executes the script
source needs to be gated behind an explicit safety check that is disabled by
default. Add a configuration option or flag (disabled by default) that must be
explicitly enabled to allow script execution. Before the exec(compile(source,
namespace["__name__"], "exec"), namespace) call, check if this unsafe opt-in
flag is enabled, and if not, raise an exception or warning that indicates script
execution is disabled and requires explicit opt-in. This way, the code follows a
fail-closed security pattern where script execution is blocked unless explicitly
allowed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a2d486de-8242-424b-812f-d94adcf05e5f

📥 Commits

Reviewing files that changed from the base of the PR and between 7bb9bc7 and b69e5cc.

📒 Files selected for processing (6)
  • lib/crewai/src/crewai/flow/flow_definition.py
  • lib/crewai/src/crewai/flow/runtime/_actions.py
  • lib/crewai/src/crewai/flow/runtime/_expressions.py
  • lib/crewai/src/crewai/flow/runtime/_outputs.py
  • lib/crewai/tests/test_flow_definition.py
  • lib/crewai/tests/test_flow_from_definition.py

Comment thread lib/crewai/src/crewai/flow/runtime/_actions.py Outdated
@vinibrsl vinibrsl force-pushed the flow-script-action branch from b69e5cc to d96695b Compare June 17, 2026 04:51
@github-actions github-actions Bot added size/L and removed size/M labels Jun 17, 2026
Comment thread lib/crewai/src/crewai/flow/runtime/_actions.py

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d96695b. Configure here.

Comment thread lib/crewai/tests/test_flow_from_definition.py
@vinibrsl vinibrsl changed the title Add script action to FlowDefinition Add script/code block action to FlowDefinition Jun 17, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/crewai/src/crewai/flow/runtime/_actions.py`:
- Around line 174-190: The ast.FunctionDef constructor is being called with the
type_params parameter unconditionally, but this parameter was only introduced in
Python 3.12, causing TypeError on Python 3.10 and 3.11. Modify the
ast.FunctionDef call to conditionally include the type_params parameter only
when running on Python 3.12 or later by checking the Python version using
sys.version_info before constructing the FunctionDef object, and only include
type_params=[] when the version check passes.

In `@lib/crewai/tests/test_flow_from_definition.py`:
- Around line 1148-1164: The test_script_action_requires_explicit_opt_in
function assumes the CREWAI_ALLOW_FLOW_SCRIPT_EXECUTION environment variable is
unset, but if it's pre-set by the test runner, the test will fail
nondeterministically. Modify the test to explicitly control the environment
variable state by using pytest's monkeypatch fixture to ensure the variable is
unset before running the Flow.from_definition call, and allow monkeypatch to
automatically restore the original state after the test completes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fcd735ca-795b-45f9-83a0-02a3254d4a52

📥 Commits

Reviewing files that changed from the base of the PR and between b69e5cc and d96695b.

📒 Files selected for processing (7)
  • lib/crewai/src/crewai/flow/flow_definition.py
  • lib/crewai/src/crewai/flow/runtime/_actions.py
  • lib/crewai/src/crewai/flow/runtime/_expressions.py
  • lib/crewai/src/crewai/flow/runtime/_outputs.py
  • lib/crewai/tests/test_checkpoint.py
  • lib/crewai/tests/test_flow_definition.py
  • lib/crewai/tests/test_flow_from_definition.py
✅ Files skipped from review due to trivial changes (1)
  • lib/crewai/tests/test_flow_definition.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/crewai/src/crewai/flow/runtime/_expressions.py
  • lib/crewai/src/crewai/flow/flow_definition.py

Comment thread lib/crewai/src/crewai/flow/runtime/_actions.py
Comment thread lib/crewai/tests/test_flow_from_definition.py
@vinibrsl vinibrsl force-pushed the flow-script-action branch from d96695b to 386a165 Compare June 17, 2026 05:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/crewai/src/crewai/flow/runtime/__init__.py (1)

1093-1094: ⚡ Quick win

Narrow the fail-fast exception to script opt-in failures.

Re-raising every RuntimeError changes unrelated action-build failures from the existing unresolved-action aggregation into immediate failures. Use a script-specific RuntimeError subclass so only the env-gate failure bypasses unresolved.

Proposed targeted exception
# lib/crewai/src/crewai/flow/runtime/_actions.py
 _ALLOW_SCRIPT_EXECUTION_ENV_VAR = "CREWAI_ALLOW_FLOW_SCRIPT_EXECUTION"
 _TRUSTED_SCRIPT_EXECUTION_VALUES = frozenset({"1", "true", "yes"})
+
+
+class FlowScriptExecutionDisabledError(RuntimeError):
+    """Raised when inline flow script execution is not explicitly enabled."""
# lib/crewai/src/crewai/flow/runtime/_actions.py
-            raise RuntimeError(
+            raise FlowScriptExecutionDisabledError(
                 "Flow script execution is disabled by default. "
                 f"Set {_ALLOW_SCRIPT_EXECUTION_ENV_VAR}=1 to enable it only for "
                 "trusted flow definitions."
             )
# lib/crewai/src/crewai/flow/runtime/__init__.py
-from crewai.flow.runtime._actions import build_action
+from crewai.flow.runtime._actions import (
+    FlowScriptExecutionDisabledError,
+    build_action,
+)
# lib/crewai/src/crewai/flow/runtime/__init__.py
-            except RuntimeError:
+            except FlowScriptExecutionDisabledError:
                 raise
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/flow/runtime/__init__.py` around lines 1093 - 1094, The
except clause at lines 1093-1094 is catching and re-raising all RuntimeError
exceptions, which causes unrelated action-build failures to bypass the
unresolved action aggregation logic instead of being collected. Create a
script-specific RuntimeError subclass (e.g., ScriptRuntimeError or similar) and
modify the except clause to only catch and re-raise that specific subclass. This
ensures that only env-gate failures that raise the script-specific exception
will trigger immediate failure, while other RuntimeError instances will continue
through the existing aggregation logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lib/crewai/src/crewai/flow/runtime/__init__.py`:
- Around line 1093-1094: The except clause at lines 1093-1094 is catching and
re-raising all RuntimeError exceptions, which causes unrelated action-build
failures to bypass the unresolved action aggregation logic instead of being
collected. Create a script-specific RuntimeError subclass (e.g.,
ScriptRuntimeError or similar) and modify the except clause to only catch and
re-raise that specific subclass. This ensures that only env-gate failures that
raise the script-specific exception will trigger immediate failure, while other
RuntimeError instances will continue through the existing aggregation logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1062677d-4525-4ade-afaa-8d712e8f3cbd

📥 Commits

Reviewing files that changed from the base of the PR and between d96695b and 386a165.

📒 Files selected for processing (8)
  • lib/crewai/src/crewai/flow/flow_definition.py
  • lib/crewai/src/crewai/flow/runtime/__init__.py
  • lib/crewai/src/crewai/flow/runtime/_actions.py
  • lib/crewai/src/crewai/flow/runtime/_expressions.py
  • lib/crewai/src/crewai/flow/runtime/_outputs.py
  • lib/crewai/tests/test_checkpoint.py
  • lib/crewai/tests/test_flow_definition.py
  • lib/crewai/tests/test_flow_from_definition.py
✅ Files skipped from review due to trivial changes (1)
  • lib/crewai/tests/test_flow_definition.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • lib/crewai/src/crewai/flow/runtime/_expressions.py
  • lib/crewai/tests/test_flow_from_definition.py
  • lib/crewai/tests/test_checkpoint.py
  • lib/crewai/src/crewai/flow/flow_definition.py
  • lib/crewai/src/crewai/flow/runtime/_outputs.py

@vinibrsl vinibrsl force-pushed the flow-script-action branch from 386a165 to 78fdf4c Compare June 17, 2026 17:17
Comment thread lib/crewai/src/crewai/flow/runtime/__init__.py Outdated
@vinibrsl vinibrsl force-pushed the flow-script-action branch from 78fdf4c to adcebc8 Compare June 18, 2026 01:34
vinibrsl added 2 commits June 17, 2026 18:34
Let a Flow method run trusted inline Python with `call: script`. The code
is compiled once into a generated function and receives the runtime
values as arguments.

```yaml
methods:
  normalize:
    start: true
    do:
      call: script
      code: |
        import math
        state["rounded"] = math.ceil(state["raw_score"])
        return f"rounded:{state['rounded']}"
```

Even though this shares the same surface of tools (custom code), I
decided to make it opt-in for now, using
`CREWAI_ALLOW_FLOW_SCRIPT_EXECUTION=1`.
@vinibrsl vinibrsl force-pushed the flow-script-action branch from adcebc8 to 5efd07c Compare June 18, 2026 01:34
@vinibrsl vinibrsl merged commit 5bd10ee into main Jun 18, 2026
56 checks passed
@vinibrsl vinibrsl deleted the flow-script-action branch June 18, 2026 01:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants