Skip to content

refactor(workflow): add Jinja2 renderer abstraction for template transform#3

Open
tomerqodo wants to merge 6 commits intocoderabbit_full_base_refactorworkflow_add_jinja2_renderer_abstraction_for_template_transform_pr3from
coderabbit_full_head_refactorworkflow_add_jinja2_renderer_abstraction_for_template_transform_pr3
Open

refactor(workflow): add Jinja2 renderer abstraction for template transform#3
tomerqodo wants to merge 6 commits intocoderabbit_full_base_refactorworkflow_add_jinja2_renderer_abstraction_for_template_transform_pr3from
coderabbit_full_head_refactorworkflow_add_jinja2_renderer_abstraction_for_template_transform_pr3

Conversation

@tomerqodo
Copy link

@tomerqodo tomerqodo commented Jan 30, 2026

Benchmark PR from agentic-review-benchmarks#3

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Jinja2 template rendering support for workflow template transform nodes with improved error handling.
  • Refactor

    • Updated template rendering architecture to use a dedicated renderer interface.
  • Tests

    • Expanded test suite with comprehensive template rendering scenarios, including edge cases, special characters, and various output formats.

laipz8200 and others added 6 commits January 25, 2026 12:04
…de and threaded it through DifyNodeFactory so TemplateTransform nodes receive the dependency by default, keeping behavior unchanged unless an override is provided. Changes are in `api/core/workflow/nodes/template_transform/template_transform_node.py` and `api/core/workflow/nodes/node_factory.py`.

**Commits**
- chore(workflow): identify TemplateTransform dependency on CodeExecutor
- feat(workflow): add CodeExecutor constructor injection to TemplateTransformNode (defaulting to current behavior)
- feat(workflow): inject CodeExecutor from DifyNodeFactory when creating TemplateTransform nodes

**Tests**
- Not run (not requested)

Next step: run `make lint` and `make type-check` if you want to validate the backend checks.
…Transform to use it, keeping CodeExecutor as the default adapter while preserving current behavior. Updates are in `api/core/workflow/nodes/template_transform/template_renderer.py`, `api/core/workflow/nodes/template_transform/template_transform_node.py`, `api/core/workflow/nodes/node_factory.py`, and `api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py`.

Commit-style summary:
- feat(template-transform): add Jinja2 template renderer abstraction with CodeExecutor adapter
- refactor(template-transform): use renderer in node/factory and update unit test patches

Tests not run (not requested).
…ode creation to return TemplateTransformNode directly for template-transform nodes in `api/core/workflow/nodes/node_factory.py`.

Commit-style summary:
- refactor(template-transform): derive TemplateRenderError from ValueError
- refactor(node-factory): instantiate TemplateTransformNode directly with injected renderer

Tests not run (not requested).
…ts/core/workflow/nodes/template_transform/template_transform_node_spec.py`)

chore(type-check): ran `make type-check` (basedpyright clean, 0 errors)

No errors reported.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

The changes introduce a Jinja2-based template rendering abstraction layer for workflow nodes. A new Jinja2TemplateRenderer protocol and CodeExecutorJinja2TemplateRenderer implementation enable template rendering through the code executor. The TemplateTransformNode and DifyNodeFactory are updated to integrate this renderer as a dependency.

Changes

Cohort / File(s) Summary
Template Rendering Abstraction
api/core/workflow/nodes/template_transform/template_renderer.py
Introduces TemplateRenderError exception, Jinja2TemplateRenderer protocol, and CodeExecutorJinja2TemplateRenderer implementation that delegates template rendering to CodeExecutor with proper error handling and output validation.
Node Implementation
api/core/workflow/nodes/template_transform/template_transform_node.py
Replaces direct code execution with template renderer dependency injection. Updates the __init__ signature to accept optional template_renderer, changes error handling from CodeExecutionError to TemplateRenderError, and validates rendered output length before returning results.
Factory Integration
api/core/workflow/nodes/node_factory.py
Adds template_renderer parameter to DifyNodeFactory.__init__, wires a default CodeExecutorJinja2TemplateRenderer instance, and constructs TEMPLATE_TRANSFORM nodes with the renderer dependency.
Test Coverage
api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py
Replaces mocks from CodeExecutor.execute_workflow_code_template to CodeExecutorJinja2TemplateRenderer.render_template. Updates test assertions from dict-based results to plain strings. Adds extensive test scenarios covering booleans, nested structures, lists, special characters, filters, loops, and whitespace handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

Template render, swift and clean, ✨
Jinja2 flows through the machine,
New abstractions, protocols bright,
Variables dance in rendered light,
Code reborn with dependencies tight! 🐰

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is minimal and only references a benchmark PR without providing substantive context, summary, motivation, or the required template structure with sections like Summary, Screenshots, and Checklist completion details. Expand the description to include: a summary of changes and issue reference, motivation and context, dependencies, and completion of the template checklist items (such as confirmation of tests, documentation updates, and linting).
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: introducing a Jinja2 renderer abstraction for template transform nodes, which is the primary focus of the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 88.10% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch coderabbit_full_head_refactorworkflow_add_jinja2_renderer_abstraction_for_template_transform_pr3

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.

@tomerqodo
Copy link
Author

@coderabbitai review

1 similar comment
@tomerqodo
Copy link
Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tomerqodo
Copy link
Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ravidqodo
Copy link

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@api/core/workflow/nodes/template_transform/template_renderer.py`:
- Around line 29-39: render_template currently assigns rendered =
result.get("result") which may be None; ensure the function always returns a
string by checking for None and raising TemplateRenderError when rendered is
None or not a str. Specifically, in render_template (which calls
_code_executor.execute_workflow_code_template with CodeLanguage.JINJA2), after
obtaining result use an explicit check like "if rendered is None or not
isinstance(rendered, str): raise TemplateRenderError(...)" so callers (e.g.,
TemplateTransformNode) never receive None.

Comment on lines +29 to +39
def render_template(self, template: str, variables: Mapping[str, Any]) -> str:
try:
result = self._code_executor.execute_workflow_code_template(
language=CodeLanguage.JINJA2, code=template, inputs=variables
)
except CodeExecutionError as exc:
raise TemplateRenderError(str(exc)) from exc

rendered = result.get("result")
if rendered is not None and not isinstance(rendered, str):
raise TemplateRenderError("Template render result must be a string.")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the CodeExecutor implementation and Jinja2 template handling
rg -n "def execute_workflow_code_template" --type-add "py" -A 10

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 153


🏁 Script executed:

# Find CodeExecutor class definition
rg -n "class.*CodeExecutor" --type-add "py" -B 2 -A 5

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 153


🏁 Script executed:

# Look for Jinja2TemplateTransformer implementation
rg -n "class.*Jinja2" --type-add "py" -A 10

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 153


🏁 Script executed:

# Check TemplateTransformNode and how it uses render_template
fd "template_transform_node.py" -x cat -n {}

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 4335


🏁 Script executed:

# Check the actual template renderer file to see current implementation
cat -n api/core/workflow/nodes/template_transform/template_renderer.py

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 1855


🏁 Script executed:

# Find the CodeExecutor implementation
fd -e py -type f | xargs rg -l "def execute_workflow_code_template" | head -5

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 328


🏁 Script executed:

# Search for CodeExecutor class
fd -e py -type f | xargs rg "class CodeExecutor" -B 2 -A 15

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 5598


🏁 Script executed:

# Get the execute_workflow_code_template method implementation
cat -n api/core/helper/code_executor/code_executor.py | head -200

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 6571


🏁 Script executed:

# Search for execute_workflow_code_template specifically
rg -n "def execute_workflow_code_template" api/core/helper/code_executor/code_executor.py -A 20

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 810


🏁 Script executed:

# Find Jinja2TemplateTransformer implementation
cat -n api/core/helper/code_executor/jinja2/jinja2_transformer.py

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 3410


🏁 Script executed:

# Also check TemplateTransformer base class to understand the contract
rg -n "class TemplateTransformer" api/core/helper/code_executor/template_transformer.py -A 30

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 1302


🏁 Script executed:

# Get the rest of TemplateTransformer to see extract_result_str_from_response fully
rg -n "def extract_result_str_from_response" api/core/helper/code_executor/template_transformer.py -A 10

Repository: agentic-review-benchmarks/dify-combined-coderabbit

Length of output: 622


Ensure render_template always returns a string, not None.

The return type is str, but result.get("result") can return None if the result dict lacks a "result" key or if future template transformers return unexpected values. This violates the contract and causes len(rendered) to crash in TemplateTransformNode line 69. Raise TemplateRenderError if rendered is None instead of returning it.

🔧 Proposed fix
    def render_template(self, template: str, variables: Mapping[str, Any]) -> str:
        try:
            result = self._code_executor.execute_workflow_code_template(
                language=CodeLanguage.JINJA2, code=template, inputs=variables
            )
        except CodeExecutionError as exc:
            raise TemplateRenderError(str(exc)) from exc

        rendered = result.get("result")
-       if rendered is not None and not isinstance(rendered, str):
+       if not isinstance(rendered, str):
            raise TemplateRenderError("Template render result must be a string.")
        return rendered
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def render_template(self, template: str, variables: Mapping[str, Any]) -> str:
try:
result = self._code_executor.execute_workflow_code_template(
language=CodeLanguage.JINJA2, code=template, inputs=variables
)
except CodeExecutionError as exc:
raise TemplateRenderError(str(exc)) from exc
rendered = result.get("result")
if rendered is not None and not isinstance(rendered, str):
raise TemplateRenderError("Template render result must be a string.")
def render_template(self, template: str, variables: Mapping[str, Any]) -> str:
try:
result = self._code_executor.execute_workflow_code_template(
language=CodeLanguage.JINJA2, code=template, inputs=variables
)
except CodeExecutionError as exc:
raise TemplateRenderError(str(exc)) from exc
rendered = result.get("result")
if not isinstance(rendered, str):
raise TemplateRenderError("Template render result must be a string.")
return rendered
🤖 Prompt for AI Agents
In `@api/core/workflow/nodes/template_transform/template_renderer.py` around lines
29 - 39, render_template currently assigns rendered = result.get("result") which
may be None; ensure the function always returns a string by checking for None
and raising TemplateRenderError when rendered is None or not a str.
Specifically, in render_template (which calls
_code_executor.execute_workflow_code_template with CodeLanguage.JINJA2), after
obtaining result use an explicit check like "if rendered is None or not
isinstance(rendered, str): raise TemplateRenderError(...)" so callers (e.g.,
TemplateTransformNode) never receive None.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants