Skip to content

Feat: Pipeline tool#647

Open
dylan-savage wants to merge 5 commits intodevelopfrom
feat/pipe-tool
Open

Feat: Pipeline tool#647
dylan-savage wants to merge 5 commits intodevelopfrom
feat/pipe-tool

Conversation

@dylan-savage
Copy link
Copy Markdown
Collaborator

@dylan-savage dylan-savage commented Apr 9, 2026

Summary

  • Adds tool_pipe, a new canvas node that exposes an inline sub-pipeline as an agent tool — connect its
    output lanes to any downstream nodes on the same canvas, and the agent can invoke the full pipeline as a
    named tool
  • Fixes stack.cpp so control-invoked nodes have their downstream data-flow connections included in
    pipeStack, enabling sub-pipeline nodes to bind correctly and participate in the filter chain

Type

Testing

  • Tests added or updated
  • Tested locally
  • ./builder test passes

Checklist

  • Commit messages follow conventional commits
  • No secrets or credentials included
  • Wiki updated (if applicable)
  • Breaking changes documented (if applicable)

Linked Issue

Fixes #646

Summary by CodeRabbit

  • New Features

    • Added "Pipeline Tool" node to run pipelines as reusable tools with configurable description and selectable return types (text, answers, documents, table).
    • Outputs normalized to consistent string/JSON forms based on selected return type.
    • Added Pipetool icon for the UI.
  • Bug Fixes

    • Pipeline execution now includes invoked sub-pipelines and their downstream nodes in the execution stack.

dylan-savage and others added 2 commits April 6, 2026 12:28
Rewrites tool_pipe as an inline canvas node that routes agent tool
calls directly through connected output lanes (text/questions/documents/
table) using synchronous filter-chain execution, eliminating the need
for an external RocketRide client, URI config, or separate .pipe file.

Also fixes stack.cpp so that control-invoked nodes (e.g. tool_pipe)
have their downstream data-flow connections included in pipeStack,
enabling sub-pipeline nodes to receive binder bindings and participate
in the filter chain.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

No description provided.

@github-actions github-actions bot added module:server C++ engine and server components module:nodes Python pipeline nodes labels Apr 9, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

Adds a new tool_pipe node exposing a sub-pipeline as an invokable tool: global config for description and return type, instance logic to open and route input through connected lanes and format results, a service descriptor, icon registration, and an engine stack traversal change to include downstream sub-pipeline nodes.

Changes

Cohort / File(s) Summary
Tool Pipe Global
nodes/src/nodes/tool_pipe/IGlobal.py
New IGlobal implementing global state: tool_description, return_type; loads config in beginGlobal, enforces allowed return types, warns on validation, no-op endGlobal.
Tool Pipe Instance
nodes/src/nodes/tool_pipe/IInstance.py
New IInstance with run_pipe tool function: normalizes input, opens sub-instance, routes input synchronously to connected lanes (questions, documents, table, text, answers), ensures closure on error, converts response to dict, extracts/serializes result per configured return_type, returns {'result': <string>}.
Package Export & Dependencies
nodes/src/nodes/tool_pipe/__init__.py
Registers package requirements, imports and re-exports IGlobal and IInstance, defines __all__.
Service Descriptor
nodes/src/nodes/tool_pipe/services.json
Adds "Pipeline Tool" service for tool_pipe:// with _source lane wired to text, questions, documents, table, answers; preconfig defaults and public configurable fields tool_description and enum return_type.
Engine Stack Traversal
packages/server/engine-lib/engLib/store/stack.cpp
When generating pipeline stack for invoked control components, now also walks downstream components reachable via input lanes to include sub-pipeline nodes.
UI Icon Map
packages/shared-ui/src/modules/flow/util/get-icon-path.ts
Registers bundled icon pipetool.svg under the pipetool key in the icon map.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Agent
    participant ToolPipe as "tool_pipe Node"
    participant SubPipeline as "Sub-Pipeline"
    participant Resultizer as "Result Extractor"

    Agent->>ToolPipe: invoke run_pipe(input)
    ToolPipe->>ToolPipe: normalize input & choose lane
    ToolPipe->>SubPipeline: open sub-instance and write to selected lane
    SubPipeline->>SubPipeline: execute connected nodes
    SubPipeline-->>ToolPipe: return entry.response
    ToolPipe->>ToolPipe: convert response to dict and extract by return_type
    ToolPipe->>Resultizer: serialize/extract final string
    Resultizer-->>ToolPipe: formatted result
    ToolPipe-->>Agent: return {'result': "<string>"}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • jmaionchi
  • Rod-Christensen
  • ryan-t-christensen
  • stepmikhaylov

Poem

🐰 A little pipe hops, threads the lanes,

I bundle inputs, answers, and gains.
Sub-parts chatter, routes align,
I gather output, make it shine.
Hooray — a tool built from the vine!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat: Pipeline tool' is concise and accurately summarizes the main change: introduction of a new pipeline tool node feature.
Linked Issues check ✅ Passed The PR implements all core objectives from issue #646: a tool_pipe node exposing sub-pipelines as agent-invokable tools with configurable return types and downstream lane connections.
Out of Scope Changes check ✅ Passed The stack.cpp change is directly necessary to enable the tool_pipe functionality by fixing control-invoked node downstream connections. The icon mapping is a supporting UI asset for the new node.

✏️ 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 feat/pipe-tool

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.

@dylan-savage dylan-savage changed the title Feat/pipe tool Feat: Pipeline tool Apr 9, 2026
Copy link
Copy Markdown
Contributor

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nodes/src/nodes/tool_pipe/IInstance.py`:
- Around line 114-131: _send_to_connected_lane currently writes the same data to
every listener instead of honoring the “first connected output lane” contract
and also ignores the declared answers lane (_source). Change
_send_to_connected_lane to iterate over the output lanes in the node’s
advertised order and send the payload only to the first listener found (use
instance.hasListener checks in that order), mapping lane names to the correct
write calls (e.g., call writeQuestions for 'questions', writeDocuments for
'documents', writeTable for 'table', writeText for 'text') and add handling for
the 'answers' lane per services.json (_source) so the first matching lane
(including 'answers') receives the data and no other lanes are invoked.
- Around line 92-98: The current block can leave the pipeline instance open if
self.instance.open(entry) or _send_to_connected_lane(data) raises, so change the
flow to ensure self.instance.close() always runs when open succeeded: call
self.instance.open(entry), set a local flag (e.g., opened = True) immediately
after a successful open, perform _send_to_connected_lane(data) inside the try,
and in a finally block check the flag and call self.instance.close(); preserve
the existing exception/return behavior after closing. Reference: getObject(),
self.instance.open(...), _send_to_connected_lane(data), self.instance.close().
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 457d7f9c-8844-4b56-92fa-5590a833b3fb

📥 Commits

Reviewing files that changed from the base of the PR and between a8f8eb5 and fe1b342.

📒 Files selected for processing (6)
  • nodes/src/nodes/tool_pipe/IGlobal.py
  • nodes/src/nodes/tool_pipe/IInstance.py
  • nodes/src/nodes/tool_pipe/__init__.py
  • nodes/src/nodes/tool_pipe/requirements.txt
  • nodes/src/nodes/tool_pipe/services.json
  • packages/server/engine-lib/engLib/store/stack.cpp

@ryan-t-christensen
Copy link
Copy Markdown
Collaborator

aside from CR comments - maybe ask Aaron for an SVG asset you can map for this node?

- Ensure self.instance.close() always runs when open() succeeded by
  using an opened flag with a finally block
- Add missing answers lane handler in _send_to_connected_lane to match
  services.json _source declaration
- Add pipetool.svg icon and register it in get-icon-path.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added the module:ui Chat UI and Dropper UI label Apr 9, 2026
Copy link
Copy Markdown
Contributor

@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: 3

♻️ Duplicate comments (1)
nodes/src/nodes/tool_pipe/IInstance.py (1)

98-99: ⚠️ Potential issue | 🟡 Minor

Silent exception swallowing hides pipeline failures.

The bare except Exception catches and discards all errors without logging. This makes debugging sub-pipeline failures difficult. Consider logging the exception before returning the empty result.

🛠️ Proposed fix
-        except Exception:
+        except Exception as exc:
+            debug(f'tool_pipe: sub-pipeline failed: {exc}')
             return {'result': ''}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nodes/src/nodes/tool_pipe/IInstance.py` around lines 98 - 99, The except
block in IInstance that currently swallows all exceptions (the "except
Exception: return {'result': ''}" block) should capture the exception (e.g.
"except Exception as e") and log it before returning so failures in
sub-pipelines are visible; modify the handler inside the method in IInstance.py
to log the exception with traceback (using the module logger or self.logger via
logger.exception or logging.exception) and then return the same empty result,
ensuring you reference the existing return {'result': ''} location.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nodes/src/nodes/tool_pipe/IInstance.py`:
- Around line 45-52: The code documents an 'image' return type in
_RETURN_TYPE_DESCRIPTIONS and services.json but there is no implementation in
routing or source lanes; either remove 'image' from the enum/dictionary or
implement full lane support. To fix, choose one: (A) remove 'image' from
_RETURN_TYPE_DESCRIPTIONS and from services.json return_type enum and any
references to image lanes; or (B) implement image support by adding an 'image'
branch to _send_to_connected_lane to route to the image lane, add 'image' to the
_source lanes in services.json, and ensure routing calls writeImage where other
lanes call writeText/writeJson (update any producers/consumers to handle base64
image payloads). Reference symbols: _RETURN_TYPE_DESCRIPTIONS,
_send_to_connected_lane, services.json _source lanes, and writeImage.
- Around line 138-139: The code currently calls
self.instance.writeAnswers([data]) passing a list of raw strings; instead create
an Answer object, set its text, and pass that Answer instance to writeAnswers.
Specifically, import/instantiate Answer, call answer.setAnswer(data) (or the
appropriate setter on the Answer class), and replace the list call with
self.instance.writeAnswers(answer) in the IInstance logic where
instance.hasListener('answers') is checked so writeAnswers receives a single
Answer object.

In `@nodes/src/nodes/tool_pipe/services.json`:
- Around line 13-14: The return_type enum includes "image" but the pipeline
lacks an image lane and handlers; either remove "image" from the return_type
enum to prevent unsupported configuration, or fully add image support by (a)
adding "image" to the "_source" lanes array in services.json, (b) implementing
image routing in IInstance._send_to_connected_lane to handle the "image" lane,
and (c) adding a writeImage method to the filter protocol (and any downstream
handlers) so the pipeline can accept and produce images; pick one of these two
options and apply it consistently across return_type, _source lanes,
IInstance._send_to_connected_lane, and the filter protocol.

---

Duplicate comments:
In `@nodes/src/nodes/tool_pipe/IInstance.py`:
- Around line 98-99: The except block in IInstance that currently swallows all
exceptions (the "except Exception: return {'result': ''}" block) should capture
the exception (e.g. "except Exception as e") and log it before returning so
failures in sub-pipelines are visible; modify the handler inside the method in
IInstance.py to log the exception with traceback (using the module logger or
self.logger via logger.exception or logging.exception) and then return the same
empty result, ensuring you reference the existing return {'result': ''}
location.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7ad7bc56-0353-471d-9e6a-5873c003789a

📥 Commits

Reviewing files that changed from the base of the PR and between fe1b342 and c4bd3da.

⛔ Files ignored due to path filters (1)
  • packages/shared-ui/src/assets/nodes/pipetool.svg is excluded by !**/*.svg
📒 Files selected for processing (3)
  • nodes/src/nodes/tool_pipe/IInstance.py
  • nodes/src/nodes/tool_pipe/services.json
  • packages/shared-ui/src/modules/flow/util/get-icon-path.ts

…wers

- Remove 'image' from return_type enum and VALID_RETURN_TYPES — no
  corresponding lane, write handler, or _source declaration exists
- Fix writeAnswers call to pass an Answer object instead of a raw
  string list, matching the contract used by all other nodes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nodes/src/nodes/tool_pipe/IGlobal.py`:
- Around line 54-65: validateConfig currently treats whitespace-only return_type
as valid while beginGlobal/runtime rejects it; change the normalization in
validateConfig to match runtime by doing return_type =
(str(cfg.get('return_type') or '').strip() or 'text') (or equivalent) before the
membership check so whitespace-only values default to 'text' and validation
behavior matches the constructor/runtime path (see validateConfig and the place
that sets self.return_type).

In `@nodes/src/nodes/tool_pipe/IInstance.py`:
- Around line 94-99: The current try/except in IInstance swallows all exceptions
from self.instance.open(entry) and _send_to_connected_lane(data) and returns
{'result': ''}, hiding real failures; change the except to catch Exception as e,
perform necessary cleanup (undo any partial state e.g., if you set opened = True
then call the instance cleanup method or close on self.instance, or reset any
flags), then either re-raise the original exception (raise) or return an
explicit failure payload such as {'error': str(e), 'result': None}; update the
except block around self.instance.open and _send_to_connected_lane to use the
exception variable e and perform cleanup before re-raising or returning the
explicit error payload.

In `@nodes/src/nodes/tool_pipe/services.json`:
- Around line 10-20: Update the tile description in
nodes/src/nodes/tool_pipe/services.json to reflect the actual fan-out behavior
(input is copied to every connected listener) instead of implying single-lane
selection; reference the runtime behavior implemented in
nodes/src/nodes/tool_pipe/IInstance.py (the code around the send-to-listeners
loop at lines ~118-143) and change phrases like “routed to whichever lane has a
connected node” to something like “sent to all connected lanes/listeners” and
clarify that multiple downstream branches will all receive the input.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4553ba49-415d-4e74-b1da-8a5fa8273b5c

📥 Commits

Reviewing files that changed from the base of the PR and between c4bd3da and ff08145.

📒 Files selected for processing (3)
  • nodes/src/nodes/tool_pipe/IGlobal.py
  • nodes/src/nodes/tool_pipe/IInstance.py
  • nodes/src/nodes/tool_pipe/services.json

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

Labels

module:nodes Python pipeline nodes module:server C++ engine and server components module:ui Chat UI and Dropper UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feat(nodes): Pipeline Tool

2 participants