Skip to content

feat(tool): add DAP tool subcommand for direct @tool_function invocation#1270

Merged
Rod-Christensen merged 2 commits into
developfrom
feat/tools
Jun 14, 2026
Merged

feat(tool): add DAP tool subcommand for direct @tool_function invocation#1270
Rod-Christensen merged 2 commits into
developfrom
feat/tools

Conversation

@Rod-Christensen

@Rod-Christensen Rod-Christensen commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add a tool DAP subcommand to rrext_process that lets SDK callers invoke any @tool_function on any node in a running pipeline directly -- no Question/Answer/SSE overhead
  • Remove QuestionType.EXECUTE and DIALECT, replace with @tool_function methods (execute, dialect) on database nodes
  • Add list and stats tools to vector store nodes
  • Add client.tool() and DataPipe.tool() to both Python and TypeScript SDKs
  • Fix pre-existing bug where the mid-tier DAP proxy silently swallowed subprocess errors
  • Skip nodes:docs-generate on non-develop branches to prevent 100+ README file changes in diffs

Usage example

# Start a pipeline with a vector store node
result = await client.use(pipeline=my_rag_pipeline, token='my-token')
token = result['token']

# Get document count and collection stats
stats = await client.tool(token=token, tool='stats', node_id='qdrant_1')
print(f"Documents: {stats['count']}, Model: {stats['model']}, Dimensions: {stats['vector_size']}")

# List stored document paths
paths = await client.tool(token=token, tool='list', node_id='qdrant_1')
print(f"Paths: {paths['paths']}")

# Search directly
results = await client.tool(
    token=token,
    tool='search',
    node_id='qdrant_1',
    input={'query': 'revenue growth', 'top_k': 5},
)

# Execute SQL on a database node
rows = await client.tool(
    token=token,
    tool='execute',
    node_id='postgres_1',
    input={'sql': 'SELECT count(*) FROM users'},
)

# Same thing via the DatabaseApi convenience wrapper
rows = await client.database.query(token=token, sql='SELECT count(*) FROM users')
// TypeScript equivalent
const stats = await client.tool<{ count: number; model: string; vector_size: number }>({
    token,
    tool: 'stats',
    nodeId: 'qdrant_1',
});
console.log(`Documents: ${stats.count}, Model: ${stats.model}`);

// Pipe-bound: reuse an open pipe for multiple tool calls
const pipe = await client.pipe(token, {}, 'text/plain');
await pipe.open();

const paths = await pipe.tool('list', 'qdrant_1');
const searchResults = await pipe.tool('search', 'qdrant_1', { query: 'revenue', top_k: 5 });

await pipe.close();

Architecture

The tool subcommand walks the pipe's filter chain via IServiceFilterInstance.next to locate the target node by component ID, then calls node.pyInstance.invoke(param) directly. This bypasses the C++ cb_control dispatcher (which requires controller map entries that are scoped to control bindings, not available from the data connection layer) and instead invokes IInstanceBase.invoke() -> _dispatch_tool() -- the same code path agents use internally.

Two SDK entry points:

  • Standalone client.tool(token, tool, node_id, input) -- server borrows a pipe from the pool
  • Pipe-bound pipe.tool(tool, node_id, input) -- reuses the caller's open pipe via pipe_id

Files changed

Area Files What
Server data_conn.py tool subcommand + _tool() handler
Server task_engine.py _send_data() now raises on subprocess success: False
Server cmd_data.py Comment update (error propagation now handled by _send_data)
Server cmd_cprofile.py Same
Server store.py list + stats @tool_function on VectorStoreToolMixin
Server db_instance_base.py execute + dialect @tool_function, removed EXECUTE/DIALECT from writeQuestions
Server db_global_base.py Comment update
Python SDK client.py Standalone tool() method
Python SDK data.py DataPipe.tool() pipe-bound method
Python SDK database.py Rewritten to use client.tool() internally
Python SDK question.py Removed EXECUTE/DIALECT from QuestionType enum
TS SDK client.ts Standalone tool() + DataPipe.tool()
TS SDK database.ts Rewritten to use client.tool() internally
TS SDK Question.ts Removed EXECUTE/DIALECT from QuestionType enum
Tests test_tool.py, tool.test.ts Integration tests (Python + TS)
Tests tool_pipeline.py, tool.pipeline.ts Pipeline fixtures
Tests conftest.py Shared test config + fixtures
Tests test_vectordb_tool_mixin.py Updated assertions for new list/stats tools
Build gen-node-tables.mjs Skip on non-develop branches

Test plan

  • Python: builder client-python:test -- 83 passed
  • TypeScript: builder client-typescript:test -- all passed
  • Nodes: builder nodes:test --pytest="-k test_collect_tool_methods" -- 3 passed
  • Full CI

Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added direct tool invocation APIs (tool()), including pipeline-bound tool execution.
    • Enabled tool routing via the data connection using a tool subcommand.
    • Added vectordb controls: list (with pagination) and stats (collection stats).
    • Updated database operations to run raw SQL/Cypher and fetch dialect via tool calls.
  • Bug Fixes

    • Improved error propagation for failed data-channel requests.
  • Tests

    • Added integration coverage for tool() and tool-bound pipe behavior.
  • Breaking Changes

    • Removed EXECUTE and DIALECT question types; use tool-based database APIs instead.

WHY
---
Until now, external callers had no way to invoke a node's @tool_function
methods directly.  The only path was through the Question/Answer pipeline
(e.g. QuestionType.EXECUTE for raw SQL, QuestionType.DIALECT for engine
discovery), which forced tool invocations through the full data-lane
lifecycle -- serializing a Question, routing through writeQuestions,
emitting an Answer, and parsing the result from SSE.  This was
architecturally wrong: tool invocations are stateless RPC calls, not
conversational turns.

LLM agents already had direct tool access internally via
IInvokeTool.Invoke dispatched through the control plane, but this
mechanism was not exposed to SDK callers.  The admin-ui Web AI page's
upcoming document management features (list/stats on vector stores)
need this capability, as does any client that wants to call execute,
dialect, search, upsert, delete, or any other @tool_function without
the overhead of the chat pipeline.

WHAT CHANGED
------------

1. New tool DAP subcommand (data_conn.py)

Added a tool subcommand to on_rrext_process alongside the existing
open/write/close lifecycle.  The handler (_tool()) receives a tool
name, node ID, optional input dict, and optional pipe_id.

The dispatch mechanism walks the filter chain via the next pointer
(exposed by the C++ bindings as IServiceFilterInstance.next) to locate
the target node by its component ID (pipeType.id).  Once found, it
accesses the node's Python IInstance via node.pyInstance and calls
invoke(param) directly -- bypassing the C++ cb_control dispatcher
entirely.

This bypass is necessary because cb_control checks the pipe's
controller map for registered control listeners of a given classType.
The pipe root (IServiceFilterPipe from getPipe()) does not have tool
entries in its controller map -- those entries are scoped to the node
that owns the control binding (e.g. an agent node that wires
control: [{classType: tool, from: agent_1}]).  Since we are
calling from the data connection layer (outside any node), we cannot
go through the normal control dispatch path.  Calling
pyInstance.invoke(param) directly invokes IInstanceBase.invoke(),
which dispatches to _dispatch_tool() -- the same code path that
agents use internally.

When the node does not own the requested tool, _dispatch_tool raises
PreventDefault.  The _tool() handler catches this and converts it to
a ValueError so the DAP layer surfaces the error to the client.

When pipe_id is provided, the handler reuses the caller's already-open
pipe (looked up from _pipe_map).  When omitted, it borrows a pipe from
the endpoint pool via getPipe()/putPipe().

2. Vector store list/stats tools (store.py)

Added two new @tool_function methods to VectorStoreToolMixin:

- list: calls store.getPaths() with optional parent/offset/limit
  filtering.  Returns the map of unique parent paths.
- stats: calls store.count_documents() and reads store.modelName
  and store.vectorSize.  Returns collection statistics.

The existing search, upsert, and delete tools become externally
callable for free -- they were already registered via @tool_function but
previously only reachable from within an agent's tool discovery.

Updated test_vectordb_tool_mixin.py to expect the two new tools in
the namespace assertion sets.

3. Python SDK -- two tool() methods

RocketRideClient.tool() (client.py): Standalone method that sends
the tool DAP subcommand via client.call('rrext_process', ...).  The
server borrows a pipe for the call.  Returns the tool's output directly.

DataPipe.tool() (data.py): Pipe-bound method on the DataPipe inner
class.  Sends the same subcommand but includes pipe_id so the server
reuses the caller's already-open pipe.

4. TypeScript SDK -- two tool() methods

RocketRideClient.tool() (client.ts): Standalone method using
client.call().  Accepts a typed options object with token, tool,
nodeId, input, and timeout.  Generic return type <T>.

DataPipe.tool() (client.ts): Pipe-bound method that passes
pipe_id automatically from the open pipe.

5. EXECUTE/DIALECT removal from QuestionType

Removed EXECUTE and DIALECT from the QuestionType enum in both
the Python SDK (question.py) and TypeScript SDK (Question.ts).
These were special-purpose question types that bypassed the LLM to
run raw SQL or discover database dialects -- functionality now properly
served by the execute and dialect @tool_function methods.

6. DB nodes -- execute/dialect as @tool_function (db_instance_base.py)

Removed the EXECUTE and DIALECT handling from writeQuestions() in
DatabaseInstanceBase.  Added two @tool_function methods in their place:

- execute: validates allow_execute config gate, calls
  _executeRawQuery(), sanitizes rows, returns {rows, affected_rows}.
- dialect: returns {dialect: self._db_dialect()}.

The Neo4j node (IInstance.py) receives the same treatment -- its
execute tool calls IGlobal._run_query_raw() and its dialect tool
returns {dialect: neo4j}.  Both the old EXECUTE/DIALECT branches
are removed from writeQuestions().

7. DatabaseApi rewrite (database.py, database.ts)

Both Python and TypeScript DatabaseApi classes are rewritten to call
client.tool() internally instead of constructing Question objects:

- query() calls client.tool(tool='execute', input={sql: sql})
- dialect() calls client.tool(tool='dialect')

The public API signatures are preserved (with the addition of an
optional node_id/nodeId parameter for targeting specific nodes).

8. Fix: subprocess error propagation (task_engine.py, cmd_data.py)

Task._send_data() was returning the subprocess DAP response without
checking its success field.  When the subprocess returned
success: False (e.g. for an invalid tool name), the mid-tier proxy in
cmd_data.py silently rewrapped it as success: True and the client
never saw the error.

Fixed by adding a did_fail() check in _send_data() that raises
RuntimeError with the subprocess error message.  This propagates
through the existing except Exception handler in cmd_data.py (and
cmd_cprofile.py) which re-raises, and the DAP layer converts it to a
proper success: False response to the client.

This was a pre-existing bug affecting all subprocess data operations,
not just the new tool subcommand.

9. Test infrastructure

conftest.py (client-python): Extracted shared TEST_CONFIG,
ensure_clean_pipeline(), and a client fixture from the main test
file into conftest.py for reuse across test modules.

tool_pipeline.py / tool.pipeline.ts: Pipeline fixture with a
webhook source and a tool_python node connected via
control: [{classType: tool, from: webhook_1}].  The tool_python
node runs Python code in a sandbox with no external dependencies.

test_tool.py / tool.test.ts: Integration tests covering:
- Standalone client.tool() executing Python and validating results
- Stdout capture from sandboxed scripts
- Error handling for invalid tool names (expects RuntimeError/throw)
- Pipe-bound DataPipe.tool() through an open pipe
- Pre-open error (pipe not open)

10. gen-node-tables.mjs -- skip on non-develop branches

The nodes:docs-generate task was regenerating every node's README.md
on every build, even on feature branches.  The generated Source URL
contains the branch name (resolved from origin/HEAD), so switching
branches caused 100+ file changes in the diff.

Added an early-exit guard: the generator now checks the current git
branch and skips entirely when not on develop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new rrext_process tool subcommand that enables direct invocation of @tool_function endpoints on pipeline nodes, bypassing the LLM chat layer. Server-side dispatch (DataConn._tool) is wired through the pipeline filter chain. Database execute/dialect tool functions replace QuestionType.EXECUTE/DIALECT. VectorStoreToolMixin gains list and stats tools. Python and TypeScript SDKs expose client.tool() and DataPipe.tool(). Integration tests are added for both runtimes. The doc-gen script gains a develop-only branch guard.

Changes

Tool invocation plumbing

Layer / File(s) Summary
Server-side tool dispatch handler
packages/ai/src/ai/modules/data/data_conn.py, packages/ai/src/ai/modules/task/task_engine.py, packages/ai/src/ai/modules/task/commands/cmd_data.py
DataConn routes subcommand=tool to a new _tool handler that borrows/reuses a pipe, walks the filter chain to find the target node, calls invoke, and maps APERR(PreventDefault) to ValueError. task_engine._send_data() now raises RuntimeError on failed data responses.
Database execute and dialect tool functions
packages/ai/src/ai/common/database/db_instance_base.py, packages/ai/src/ai/common/database/db_global_base.py, packages/client-python/src/rocketride/schema/question.py, packages/client-typescript/src/client/schema/Question.ts
New execute and dialect @tool_function methods added to DatabaseInstanceBase. writeQuestions is simplified by removing QuestionType.EXECUTE/DIALECT branches. Both Python and TypeScript QuestionType enums drop the EXECUTE and DIALECT members.
VectorStoreToolMixin list and stats tools
packages/ai/src/ai/common/store.py, nodes/test/test_vectordb_tool_mixin.py
list (paginated getPaths) and stats (count_documents, modelName, vectorSize) tools added to VectorStoreToolMixin. Tests updated to assert the new namespaced methods.
Python client tool() API and database migration
packages/client-python/src/rocketride/client.py, packages/client-python/src/rocketride/mixins/data.py, packages/client-python/src/rocketride/database.py
RocketRideClient.tool() and DataPipe.tool() added. DatabaseApi.query and DatabaseApi.dialect reworked to call execute/dialect tools directly, removing the chat/Question/on_sse path.
TypeScript client tool() API and database migration
packages/client-typescript/src/client/client.ts, packages/client-typescript/src/client/database.ts
DataPipe.tool() and RocketRideClient.tool() added. DatabaseApi.query and DatabaseApi.dialect reworked to call client.tool('execute') and client.tool('dialect'), removing Question/client.chat and onSSE.
Integration tests
packages/client-python/tests/conftest.py, packages/client-python/tests/test_tool.py, packages/client-python/tests/tool_pipeline.py, packages/client-typescript/tests/tool.test.ts, packages/client-typescript/tests/tool.pipeline.ts
Python and TypeScript integration suites covering standalone client.tool() and pipe-bound DataPipe.tool(). Shared fixtures provision webhook→tool_python→response pipelines. Tests cover result structure, stdout capture, and error cases.

Doc generation branch guard

Layer / File(s) Summary
Branch guard in gen-node-tables.mjs
nodes/scripts/gen-node-tables.mjs
main() now checks the current git branch and exits early with a skip message when not on develop.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(173, 216, 230, 0.5)
    note over CallerSDK,DataConn: Tool invocation (new path)
  end
  participant CallerSDK as RocketRideClient / DataPipe
  participant DataConn
  participant Pipeline
  participant ToolNode as `@tool_function` Node

  CallerSDK->>DataConn: rrext_process(subcommand=tool, token/pipe_id, tool, nodeId, input)
  DataConn->>DataConn: validate tool name non-empty
  DataConn->>Pipeline: borrow or reuse pipe by pipe_id / endpoint pool
  DataConn->>Pipeline: walk pipe.next to match nodeId
  DataConn->>ToolNode: invoke(IInvokeTool.Invoke{tool, input})
  alt tool not found on node
    ToolNode-->>DataConn: APERR(Ec.PreventDefault)
    DataConn-->>CallerSDK: ValueError / RuntimeError
  else success
    ToolNode-->>DataConn: tool result
    DataConn->>Pipeline: return borrowed pipe
    DataConn-->>CallerSDK: { result: tool_result }
  end
Loading
sequenceDiagram
  rect rgba(255, 200, 150, 0.5)
    note over DatabaseApi,DatabaseInstanceBase: Database operations (old vs new)
  end
  participant DatabaseApi
  participant RocketRideClient
  participant DataConn
  participant DatabaseInstanceBase

  note over DatabaseApi,DatabaseInstanceBase: Old path (removed)
  DatabaseApi->>RocketRideClient: chat(Question{type: EXECUTE/DIALECT})
  RocketRideClient->>DataConn: writeQuestions with QuestionType branch

  note over DatabaseApi,DatabaseInstanceBase: New path
  DatabaseApi->>RocketRideClient: tool(execute | dialect, nodeId, {sql})
  RocketRideClient->>DataConn: rrext_process(subcommand=tool)
  DataConn->>DatabaseInstanceBase: invoke execute() / dialect()
  DatabaseInstanceBase->>DatabaseInstanceBase: _executeRawQuery(sql) / _db_dialect()
  DatabaseInstanceBase-->>DataConn: { rows, affected_rows } / { dialect }
  DataConn-->>RocketRideClient: { result }
  RocketRideClient-->>DatabaseApi: Dict[str, Any] / DatabaseDialect
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

module:ai, module:server

Suggested reviewers

  • jmaionchi
  • stepmikhaylov
  • asclearuc

Poem

🐇 Hop hop, no more chat detour!
The tool subcommand opens every door.
execute, dialect, list, stats too —
Direct to the node, the pipeline flew.
No LLM middleman, just raw SQL bliss,
A rabbit rejoices a PR like this! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately captures the main feature: adding a DAP tool subcommand for direct @tool_function invocation. It's concise, specific, and directly reflects the primary change across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 82.50% which is sufficient. The required threshold is 80.00%.
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.

✏️ 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/tools

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.

@github-actions

Copy link
Copy Markdown
🤖 Internal: Discord sync marker

Auto-managed by the Discord notification workflow. Stores the linked Discord message ID. Do not edit or delete.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ai/src/ai/common/store.py`:
- Around line 1114-1115: The int() conversions for offset and limit parameters
lack error handling and will raise ValueError for non-integer input values,
crashing the tool invocation. Wrap the int() conversion calls for both the
offset and limit parameters in a try-except block to catch ValueError
exceptions, and when caught, return a structured error response that provides
clear feedback to the caller about the invalid input instead of allowing the
exception to propagate up the call stack.

In `@packages/ai/src/ai/modules/data/data_conn.py`:
- Around line 790-833: The tool_sync function is missing the in_use flag and
activity timestamp guards that protect pipes in the _write method, allowing
_monitor_pipes to reclaim a borrowed pipe mid-invocation. Before executing the
tool invocation through py_instance.invoke(), set conn_pipe.in_use to True and
refresh its activity timestamp (matching the pattern used in _write), then reset
conn_pipe.in_use to False in the finally block to release the lock. This
prevents the monitor process from reclaiming the pipe while the tool is
executing.
- Around line 786-813: The node lookup logic in the tool_sync() function does
not implement the documented broadcast behavior for an empty nodeId. When nodeId
is an empty string, the code should apply a "first handler wins" broadcast
pattern to tool-lane nodes instead of attempting exact ID matching which fails.
Modify the node lookup section where the filter chain is walked: add a
conditional check before the while loop to handle the empty nodeId case
separately, implementing the broadcast behavior for tool-lane nodes, while
keeping the existing exact ID matching logic for non-empty nodeIds. This will
allow optional callers that don't specify a nodeId to properly invoke the first
available handler rather than raising a ValueError.
🪄 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: d61fc14a-acde-4e61-b211-31fc0e0e8e30

📥 Commits

Reviewing files that changed from the base of the PR and between 923f4e1 and 96cd9bd.

📒 Files selected for processing (20)
  • nodes/scripts/gen-node-tables.mjs
  • nodes/test/test_vectordb_tool_mixin.py
  • packages/ai/src/ai/common/database/db_global_base.py
  • packages/ai/src/ai/common/database/db_instance_base.py
  • packages/ai/src/ai/common/store.py
  • packages/ai/src/ai/modules/data/data_conn.py
  • packages/ai/src/ai/modules/task/commands/cmd_data.py
  • packages/ai/src/ai/modules/task/task_engine.py
  • packages/client-python/src/rocketride/client.py
  • packages/client-python/src/rocketride/database.py
  • packages/client-python/src/rocketride/mixins/data.py
  • packages/client-python/src/rocketride/schema/question.py
  • packages/client-python/tests/conftest.py
  • packages/client-python/tests/test_tool.py
  • packages/client-python/tests/tool_pipeline.py
  • packages/client-typescript/src/client/client.ts
  • packages/client-typescript/src/client/database.ts
  • packages/client-typescript/src/client/schema/Question.ts
  • packages/client-typescript/tests/tool.pipeline.ts
  • packages/client-typescript/tests/tool.test.ts
💤 Files with no reviewable changes (2)
  • packages/client-python/src/rocketride/schema/question.py
  • packages/client-typescript/src/client/schema/Question.ts

Comment thread packages/ai/src/ai/common/store.py Outdated
Comment thread packages/ai/src/ai/modules/data/data_conn.py Outdated
Comment thread packages/ai/src/ai/modules/data/data_conn.py Outdated
…pipe guards

- store.py: wrap offset/limit int() conversions in try/except to return
  a structured error instead of crashing on invalid input
- data_conn.py: implement empty-nodeId broadcast behavior (walk all
  filter-chain nodes, first handler wins) matching the documented contract
- data_conn.py: add in_use flag and activity-timer guards to pipe-bound
  tool calls so _monitor_pipes cannot reclaim the pipe mid-invocation
  (mirrors the existing _write pattern)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 `@packages/ai/src/ai/common/store.py`:
- Around line 1114-1120: The current validation only checks if offset and limit
can be converted to integers, but allows negative values to pass through to
store.getPaths(). Add a validation check after the int() conversions to ensure
both offset and limit are non-negative (>= 0), and return an error response with
an appropriate message if either value is negative. This validation should occur
before the call to store.getPaths() to enforce a consistent contract across all
store implementations.

In `@packages/ai/src/ai/modules/data/data_conn.py`:
- Around line 803-808: The `_tool()` method bypasses the concurrency limit when
`pipe_id` is omitted because it obtains the pipe via `self._target.getPipe()`
without acquiring the `self._pipe_sem` semaphore. When the pipe is borrowed
(when `conn_pipe is None` in the else block and `borrowed` is set to `True`),
the code must still acquire and release `self._pipe_sem` to enforce the
connection's thread count limit. Apply this fix at all locations where pipes are
borrowed directly from the target, ensuring the semaphore is acquired before
using the borrowed pipe and released when done.
🪄 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: b69641e0-754e-4c0f-88a7-5cb40e3a457a

📥 Commits

Reviewing files that changed from the base of the PR and between 96cd9bd and ec4bf38.

📒 Files selected for processing (2)
  • packages/ai/src/ai/common/store.py
  • packages/ai/src/ai/modules/data/data_conn.py

Comment thread packages/ai/src/ai/common/store.py
Comment thread packages/ai/src/ai/modules/data/data_conn.py
@Rod-Christensen Rod-Christensen enabled auto-merge (squash) June 14, 2026 06:27
@Rod-Christensen Rod-Christensen merged commit ac74740 into develop Jun 14, 2026
26 checks passed
@Rod-Christensen Rod-Christensen deleted the feat/tools branch June 14, 2026 06:28
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.

2 participants