Skip to content

feat(nodes): add Guild.ai tool node - #1689

Merged
EdwardLien0426 merged 9 commits into
rocketride-org:developfrom
EdwardLien0426:feat/RR-1688-tool-guild
Jul 29, 2026
Merged

feat(nodes): add Guild.ai tool node#1689
EdwardLien0426 merged 9 commits into
rocketride-org:developfrom
EdwardLien0426:feat/RR-1688-tool-guild

Conversation

@EdwardLien0426

@EdwardLien0426 EdwardLien0426 commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add tool_guild — a dual ["data","tool"] node that runs Guild.ai agents from a RocketRide pipeline or an agent (mirrors tool_n8n for the dual-node structure and tool_v0 for IGlobal/test conventions and raise-not-return error handling).
  • Pipeline face: lane input runs the configured Guild agent; the answer is emitted on answers/text/documents/table.
  • Tool face: run_agent / get_session / get_session_events let an agent delegate to and follow up on Guild sessions.
  • HTTP Basic auth with a Guild trigger API key; per-run session budget; POSTs are never retried (each session is billed); agent-supplied identifiers are sanitised to plain path segments.

Why this fits the codebase

Guild.ai is a control plane for AI agents with no data-processing model of its own. This node is the complement to a RocketRide pipeline: RocketRide does the data work (ingest, parse, chunk, embed, retrieve) and hands a well-formed payload to a governed Guild agent to perform the action. The dual data+tool shape follows the tool_n8n precedent for delegating to a remote runtime.

Type

feature (new pipeline/agent-tool node) + docs + tests

Testing

  • Tests added — 82 unit + contract tests (nodes/test/test_tool_guild.py), including regression fixtures captured from a live Guild transcript
  • Tested locally — ruff check/format clean; gitleaks clean; pytest nodes/test/test_tool_guild.py 82 pass (full suite 380; test_contracts.py 298 pass); verified end-to-end against a live Guild workspace (api_trigger session over HTTP Basic auth; session id / status / event response shapes confirmed and pinned as fixtures)
  • Docs added — node README with all sections, upstream docs links, and the ROCKETRIDE:GENERATED:PARAMS marker block
  • ./builder nodes:test-contracts — not completed locally; the builder fails at server:setup-pip (a build-env bootstrap step, unrelated to this node) before tests run. Ran the contract pytest directly instead (298 pass). CI runs the builder path in a clean environment.
  • ./builder nodes:test / nodes:test-full — not run (same local build-env blocker)
  • ./builder nodes:docs-generate — not run (feature branch; an empty marker pair is expected until develop)

Checklist

  • Commit messages follow conventional commits
  • No secrets or credentials included (examples use ${ROCKETRIDE_GUILD_*} placeholders)
  • Breaking changes — none (purely additive; node-local diff: node dir + nodes/test/ + examples/)

Linked Issue

Closes #1688

Summary by CodeRabbit

  • New Features
    • Added a Guild.ai integration that supports both pipeline-step execution and delegated tool calls, including session start, polling for status/events, and final output extraction.
    • Added configurable runtime controls: wait vs start mode, request timeout, TLS verification, and per-run session budgeting to prevent overruns.
    • Added new example pipeline templates demonstrating direct Guild agent execution and delegated governed actions.
  • Documentation
    • Documented setup, credentials/config options, polling/limit behavior, and tool response semantics.
  • Tests
    • Added a network-free test suite validating configuration invariants, safety parsing, retry/error behavior, and pipeline/tool wiring.

Add `tool_guild`, a dual data+tool node that runs Guild.ai agents from a
RocketRide pipeline or an agent. Mirrors `tool_n8n` (dual-node structure,
lane + tool faces, result mode) and `tool_v0` (IGlobal/test conventions,
raise-not-return error handling).

- Pipeline face: lane input runs the configured Guild agent; the answer is
  emitted on answers/text/documents/table.
- Tool face: run_agent / get_session / get_session_events let an agent
  delegate to and follow up on Guild sessions.
- HTTP Basic auth with a Guild trigger API key; per-run session budget;
  POSTs are never retried (each session is billed); agent-supplied
  identifiers are sanitised to plain path segments.

Tests: 82 unit + contract tests; verified end-to-end against a live Guild
workspace (api_trigger session over Basic auth; session/status/event
response shapes confirmed and pinned as regression fixtures).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added docs Documentation module:nodes Python pipeline nodes labels Jul 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor
🤖 Internal: Discord sync marker

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

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Guild.ai integration with shared configuration, authenticated session APIs, tool and pipeline execution modes, service metadata, documentation, example pipelines, dependency setup, and network-free tests.

Changes

Guild.ai node integration

Layer / File(s) Summary
Service contract and configuration
nodes/src/nodes/tool_guild/services.json, nodes/src/nodes/tool_guild/IGlobal.py, nodes/src/nodes/tool_guild/__init__.py, nodes/src/nodes/tool_guild/requirements.txt, nodes/src/nodes/tool_guild/README.md, nodes/test/framework/pipeline.py
Defines the Guild.ai node schema, dependency initialization, shared configuration, session limits, validation behavior, credentials, operating constraints, and environment-variable mappings.
Guild session client
nodes/src/nodes/tool_guild/guild_client.py, nodes/test/test_tool_guild.py
Adds authenticated HTTP calls, session lifecycle operations, polling, retry handling, response normalization, safe identifiers, output extraction, pagination, and network-free client tests.
Dual-face node execution
nodes/src/nodes/tool_guild/IInstance.py, nodes/test/test_tool_guild.py, nodes/src/nodes/tool_guild/README.md
Adds agent tools, pipeline input accumulation, synchronous execution, output routing, metadata emission, session budget enforcement, and execution documentation.
Example pipeline wiring
examples/guild-agent.pipe, examples/guild-delegate-agent.pipe, examples/README.md
Adds direct webhook-to-Guild and delegated-agent pipeline examples with configured components, lanes, credentials, project metadata, and template documentation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Pipeline
  participant IInstance
  participant guild_client
  participant GuildAPI
  Pipeline->>IInstance: Submit pipeline input
  IInstance->>guild_client: Start Guild session
  guild_client->>GuildAPI: POST session request
  GuildAPI-->>guild_client: Return session identifier
  IInstance->>guild_client: Poll and fetch session events
  guild_client->>GuildAPI: GET status and transcript
  GuildAPI-->>IInstance: Return status and events
  IInstance-->>Pipeline: Emit text, answers, documents, and table outputs
Loading

Suggested reviewers: jmaionchi, rod-christensen, stepmikhaylov

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.29% 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 is concise and accurately names the main change: adding a Guild.ai tool node.
Linked Issues check ✅ Passed The changes implement the requested dual data/tool Guild.ai node, including pipeline execution, tool methods, HTTP Basic auth, and read/run-only behavior.
Out of Scope Changes check ✅ Passed No clear unrelated code changes stand out; the added examples, docs, tests, and support code all align with the Guild.ai node feature.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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

🤖 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 `@examples/guild-delegate-agent.pipe`:
- Around line 27-32: Update the instruction string in the guild delegation
example to prohibit retrying any uncertain tool_guild_1.run_agent call,
including timeouts, transport failures, or surfaced structured errors, because
the billed non-idempotent session may already have started. Clarify that
failures are raised for the engine to surface rather than returned as
success:false, and direct follow-up to tool_guild_1.get_session or
tool_guild_1.get_session_events when session status is uncertain.

In `@nodes/src/nodes/tool_guild/guild_client.py`:
- Around line 163-167: Update the docstrings for the authenticated API-call
function at nodes/src/nodes/tool_guild/guild_client.py:163-167 and the
session/deadline handling function at
nodes/src/nodes/tool_guild/guild_client.py:305-310. Document ValueError only for
missing credentials in the first, RuntimeError for transport or HTTP failures
there, and change the second function’s session/deadline exception description
to RuntimeError; no implementation changes are needed.
- Around line 343-355: Replace the denylist validation in the
identifier-validation helper with an allowlist permitting only plain identifier
characters, while preserving required-value handling and the existing
invalid-field error behavior. Ensure valid hyphenated, underscored, dotted,
alphanumeric, and UUID-style values remain accepted, while encoded separators,
semicolon parameters, whitespace, control characters, and other characters are
rejected for agent, session_id, owner, and workspace inputs.

In `@nodes/src/nodes/tool_guild/IInstance.py`:
- Around line 187-193: Remove the self._require_workspace() call from the
get_session and corresponding events tool methods, since session-id endpoints
only require the existing session identifier. Keep _require_workspace() in the
session-start path unchanged.
- Line 252: Rename the open method parameter from object to obj in the IInstance
class, preserving its Entry type annotation and updating any references within
the method body to use obj.
- Around line 276-282: Update _build_input in IInstance so _text_parts and
_documents are joined with newline separators instead of an empty string,
preserving each content part byte-exactly while retaining boundaries between
adjacent chunks and documents. Update the expected input in
test_pipeline_closing_emits_answer_on_connected_lanes_only to reflect the
inserted newlines.

In `@nodes/src/nodes/tool_guild/README.md`:
- Around line 113-115: Update the README’s “Every field” statement to clarify
that environment fallbacks apply only to base URL, key ID/secret, owner,
workspace, and agent; do not imply env support for resultMode, timeout,
maxSessions, or verifyTls.

In `@nodes/test/test_tool_guild.py`:
- Around line 643-661: Update the _make_instance helper to accept the pytest
monkeypatch fixture and replace the direct _imod.guild_client assignment with
monkeypatch.setattr(_imod, 'guild_client', gclient), ensuring the module-level
client binding is restored after each test.
🪄 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 Plus

Run ID: 4deed526-ccc6-4e04-bff0-2376eba3ba81

📥 Commits

Reviewing files that changed from the base of the PR and between f773adf and 4400014.

⛔ Files ignored due to path filters (1)
  • nodes/src/nodes/tool_guild/guild.svg is excluded by !**/*.svg
📒 Files selected for processing (10)
  • examples/guild-agent.pipe
  • examples/guild-delegate-agent.pipe
  • nodes/src/nodes/tool_guild/IGlobal.py
  • nodes/src/nodes/tool_guild/IInstance.py
  • nodes/src/nodes/tool_guild/README.md
  • nodes/src/nodes/tool_guild/__init__.py
  • nodes/src/nodes/tool_guild/guild_client.py
  • nodes/src/nodes/tool_guild/requirements.txt
  • nodes/src/nodes/tool_guild/services.json
  • nodes/test/test_tool_guild.py

Comment thread examples/guild-delegate-agent.pipe Outdated
Comment thread nodes/src/nodes/tool_guild/guild_client.py
Comment thread nodes/src/nodes/tool_guild/guild_client.py
Comment thread nodes/src/nodes/tool_guild/IInstance.py
Comment thread nodes/src/nodes/tool_guild/IInstance.py Outdated
Comment thread nodes/src/nodes/tool_guild/IInstance.py Outdated
Comment thread nodes/src/nodes/tool_guild/README.md Outdated
Comment thread nodes/test/test_tool_guild.py Outdated
EdwardLien0426 and others added 2 commits July 27, 2026 09:11
- get_session / get_session_events no longer require workspace config; those
  endpoints are addressed by session id alone, so an agent can follow up with
  only the id from an earlier run_agent.
- safe_segment now validates path-segment identifiers with an allowlist
  ([A-Za-z0-9._~-]) instead of a denylist, rejecting percent-encoding and other
  smuggling the denylist missed.
- _build_input joins lane parts with a newline so adjacent chunks/documents
  keep a boundary instead of running together.
- Correct guild_client docstrings (transport/HTTP failures raise RuntimeError,
  not ValueError) and the README env-fallback scope (connection fields only).
- Strengthen the delegate example's no-retry guidance (a timed-out or errored
  run may already be a billed session) and rename the open() shadow of builtin.
- Tests use monkeypatch to swap the stubbed guild_client so it can't leak.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The services.json invariant tests read the file with `read_text()` and no
explicit encoding, which uses the locale default. In CI the bundled Python
defaults to ASCII, so the em-dashes in the field descriptions raised
UnicodeDecodeError and failed the four services tests (they passed locally only
because the dev locale is UTF-8). Match the tool_n8n convention and decode as
UTF-8 explicitly.

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

Copy link
Copy Markdown
Collaborator Author

Addressed the CodeRabbit review in be413584, plus a CI fix in a55e8709:

Review items (all 8):

  • get_session / get_session_events no longer require workspace config — those endpoints are addressed by session id alone.
  • safe_segment now uses an allowlist ([A-Za-z0-9._~-]) instead of a denylist (rejects percent-encoding and other smuggling the denylist missed).
  • _build_input joins lane parts with a newline so adjacent chunks/documents keep a boundary.
  • Corrected guild_client docstrings (transport/HTTP failures raise RuntimeError, not ValueError) and the README env-fallback scope (connection fields only).
  • Strengthened the delegate example's no-retry guidance (a timed-out/errored run may already be a billed session) and renamed the open() builtin shadow.
  • Tests swap the stubbed guild_client via monkeypatch so it can't leak.

CI fix: the four services.json invariant tests read the file without an explicit encoding, which failed under CI's ASCII locale (em-dashes in field descriptions → UnicodeDecodeError). Now decoded as UTF-8, matching the tool_n8n convention. Verified locally under a forced ASCII locale.

@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: 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 `@examples/guild-delegate-agent.pipe`:
- Around line 30-32: Update the retry guidance in the tool_guild_1 instructions
so wrong agent names or session IDs are retried only when the error conclusively
proves no session was created. Otherwise treat the outcome as uncertain: use
tool_guild_1.get_session and tool_guild_1.get_session_events to inspect the
existing session, or report the failure without starting another billed session.
🪄 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 Plus

Run ID: cb7f07e9-0cc2-43f7-b34e-8dbf8e2801ba

📥 Commits

Reviewing files that changed from the base of the PR and between 4400014 and be41358.

📒 Files selected for processing (5)
  • examples/guild-delegate-agent.pipe
  • nodes/src/nodes/tool_guild/IInstance.py
  • nodes/src/nodes/tool_guild/README.md
  • nodes/src/nodes/tool_guild/guild_client.py
  • nodes/test/test_tool_guild.py

Comment thread examples/guild-delegate-agent.pipe
Only retry a failed run_agent when the error conclusively proves no session
was started; a wrong agent name does not by itself establish that Guild
rejected the request before creating a billed session. Otherwise inspect the
existing session or report the uncertain outcome.

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

@asclearuc asclearuc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks @EdwardLien0426 — this is a careful piece of work. Isolating the whole wire contract in guild_client.py, claiming the session budget before the billable POST, and pinning regression fixtures from a real Guild session are all exactly right. The CodeRabbit points were properly fixed rather than waved away, and CI is fully green.

I am requesting changes for two correctness issues and one coverage gap. Details are in the inline comments.

Blocking

  1. extract_output can emit internal noise as the agent's answer (guild_client.py:479). The final fallback takes the last event with any text, without checking its type. Running it against your own fixture shapes returns the user's own input, or the literal string 'text', or an intermediate progress step — as the answer, with no error.
  2. get_session_events reads only the first page (guild_client.py:288). Your captured fixture shows pagination: {has_more, limit, offset, total_count} and events arrive oldest-first, so the answer is always last. A transcript over 100 events loses the answer and then hits issue 1.
  3. Missing tests. beginGlobal, validateConfig, and the three public tool functions have no tests.

Smaller points, non-blocking

  • guild_client.py:49 still says "UNVERIFIED AGAINST A LIVE WORKSPACE". That now contradicts the per-function VERIFIED: notes and the PR description.
  • IGlobal.py:95 and :99 re-implement config_int from ai.common.utils.
  • services.json test.requires: the two credential env vars do not map to the node's config field names.
  • Prefer from requests.status_codes import codes as status_codes over the raw numbers in guild_client.py.
  • examples/README.md has a ### <name>.pipe section per example. The two new .pipe files are not listed there, so a user browsing that page will not find them. (I could not comment on this inline — that file is not part of the diff.)

The structure and the safety design are solid. Fix the two output-correctness paths and add the missing tests and this is ready.

Comment thread nodes/src/nodes/tool_guild/guild_client.py Outdated
Comment thread nodes/src/nodes/tool_guild/guild_client.py Outdated
Comment thread nodes/src/nodes/tool_guild/guild_client.py Outdated
Comment thread nodes/src/nodes/tool_guild/guild_client.py Outdated
Comment thread nodes/src/nodes/tool_guild/IGlobal.py Outdated
Comment thread nodes/src/nodes/tool_guild/services.json
Comment thread nodes/test/test_tool_guild.py
Comment thread nodes/test/test_tool_guild.py
Comment thread nodes/test/test_tool_guild.py Outdated
Two output-correctness bugs plus the coverage gaps from @asclearuc's review.

Blocking:
- extract_output no longer emits internal noise as the answer. The blind
  "last event with any text" fallback could return the user's own echo, a
  span marker's literal 'text', or an intermediate progress step. It now skips
  a known-noise deny-list and returns '' when no real answer is present — an
  empty answer is a better failure than a wrong one.
- get_session_events now follows pagination. Events arrive oldest-first so the
  answer is on the last page; the single-page read dropped it on any transcript
  past one page. Pages are followed via offset until has_more is false, bounded
  by MAX_EVENT_LIMIT.
- Added tests for beginGlobal (env fallbacks, clamps, CONFIG-mode early return,
  result_mode), validateConfig (all warning branches), the three public tool
  functions, pagination, and the noise-vs-answer paths. The weak
  `assert out in (...)` that accepted the wrong answer now asserts one value.

Non-blocking:
- Rewrote the guild_client header (VERIFIED, not UNVERIFIED) and the stale
  two-segment endpoint path.
- IGlobal uses config_int for timeout/maxSessions (a 0 now means "default",
  not "5 seconds").
- services.json test credentials now map to config fields: added KEY_ID /
  KEY_SECRET to the test framework's _ENV_ATTR_MAP (shared file — see below).
- Use requests.status_codes names instead of magic HTTP numbers.
- List the two guild example pipes in examples/README.md.

Shared-code change: nodes/test/framework/pipeline.py gains two _ENV_ATTR_MAP
entries (KEY_ID -> apiKeyId, KEY_SECRET -> apiKeySecret). No other node uses
those env-var tokens, so the addition is purely additive.

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

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough review — the two output-correctness paths were the important ones. Addressed in f4ba0062:

Blocking

  1. extract_output noise — the blind fallback now skips a known-noise deny-list (user_message, trigger_message, system_message, the runtime_* / llm_* families, and agent_notification_progress) and returns '' when no real answer is present. Same reasoning as session_status: an empty answer beats a wrong one. Pinned with tests for the user-echo, noise-only, and progress-only shapes (each asserts one exact value), plus one that a genuinely-new but non-noise answer type is still surfaced.
  2. get_session_events pagination — now follows offset until has_more is false, bounded by MAX_EVENT_LIMIT. _has_more reads the pagination block (has_more, then total_count). Tests cover the two-page case (answer on the last page), the single-page fast path, and the cap.
  3. Coverage — added tests for beginGlobal (config read, the six env fallbacks, the timeout/maxSessions clamps and non-numeric path, result_mode normalising, the CONFIG-mode early return), validateConfig (all five warning branches via the collector), and the three public tool functions (run_agent resolving wait from result_mode and falling back to default_agent, get_session, get_session_events with bounded limit). The weak assert out in (...) now asserts one value.

Non-blocking (all done)

  • Header rewritten to VERIFIED with the date and the confirmed facts; fixed the stale two-segment endpoint path and the test_tool_guild.py docstring.
  • IGlobal uses config_int for timeout/maxSessions — a slider at 0 now means "default", not "5 seconds".
  • requests.status_codes names instead of magic numbers.
  • Both example pipes are now listed in examples/README.md.

Shared-code change (flagging explicitly): nodes/test/framework/pipeline.py gains two _ENV_ATTR_MAP entries — KEY_ID -> apiKeyId, KEY_SECRET -> apiKeySecret — so the smoke test's credential env vars map to the node's actual config fields. Grepped the tree: no other node uses those env-var tokens, so it is purely additive.

100 unit + contract tests pass (up from 82), verified under a forced ASCII locale.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nodes/src/nodes/tool_guild/IGlobal.py (1)

71-106: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing PEP 257 docstrings on beginGlobal and validateConfig.

Both are substantial, branchy methods (config loading with env fallbacks/clamping; five-branch config validation) yet neither has a docstring, unlike claim_session_slot in the same file. As per path instructions, nodes/**/*.py requires "PEP 257 docstrings."

📝 Proposed docstrings
     def beginGlobal(self) -> None:
+        """Load Guild connection settings, credentials, and session-budget state from config."""
         # Opened only to render the config UI — never touch the network or read
     def validateConfig(self) -> None:
+        """Warn about canvas-visible misconfigurations without touching the network."""
         # Config-only checks (no network) so this is safe to run during canvas

Also applies to: 121-156

🤖 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 `@nodes/src/nodes/tool_guild/IGlobal.py` around lines 71 - 106, Add PEP
257-compliant docstrings to the beginGlobal and validateConfig methods in
IGlobal, briefly documenting their configuration initialization and validation
responsibilities. Keep the existing branching, fallback, clamping, and
validation behavior unchanged, matching the style of the existing
claim_session_slot docstring.

Source: Path instructions

🤖 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 `@examples/README.md`:
- Around line 149-151: Update the documentation section for guild-agent.pipe to
state that its Guild session has a finite timeout and that timed-out or errored
runs may still create a billed, non-idempotent session. Add the same “do not
retry blindly” guidance used in the guild-delegate-agent.pipe section.

---

Outside diff comments:
In `@nodes/src/nodes/tool_guild/IGlobal.py`:
- Around line 71-106: Add PEP 257-compliant docstrings to the beginGlobal and
validateConfig methods in IGlobal, briefly documenting their configuration
initialization and validation responsibilities. Keep the existing branching,
fallback, clamping, and validation behavior unchanged, matching the style of the
existing claim_session_slot docstring.
🪄 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 Plus

Run ID: 1fad069a-7f21-4bbb-9d3a-09cc737ca15b

📥 Commits

Reviewing files that changed from the base of the PR and between fb728ea and f4ba006.

📒 Files selected for processing (5)
  • examples/README.md
  • nodes/src/nodes/tool_guild/IGlobal.py
  • nodes/src/nodes/tool_guild/guild_client.py
  • nodes/test/framework/pipeline.py
  • nodes/test/test_tool_guild.py

Comment thread examples/README.md
… step

The pipeline face is deterministic (no agent retry loop), so the delegate
example's "do not retry blindly" guidance does not map to it. Add the accurate
equivalent: a run starts a billed Guild session, and a timeout does not cancel
it on Guild's side, so re-running the pipeline starts a new one.

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

@asclearuc asclearuc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks @EdwardLien0426 — good, fast turnaround, and you fixed the right things rather than the easy ones. I re-verified each fix by running the code, not by reading the description.

Verdict: requesting changes

Why: the pagination fix changed the meaning of the limit parameter, and neither place that documents it was updated. limit used to cap how many events came back. It is now the page size, and the call returns the whole transcript regardless. The agent-facing tool description (IInstance.py:209) and the node README (README.md:85) both still say "Max events to return".

That is blocking for two reasons. First, an LLM picks its argument from that description, so the wrong text steers the agent to the worst value. Second, CLAUDE.md requires the co-located doc to change in the same commit as the contract it documents, and this commit changed the contract without touching either file.

Everything else below is non-blocking. This is a small, contained fix — not a rework.

The blocking issue in detail

Measured against a 1000-event transcript:

Agent asks HTTP requests Events returned
limit: 5 200 1000
limit: 100 10 1000
limit: 1000 1 1000

The parameter is inverted — a smaller limit means more work and the same full result. An agent that sets limit: 5 to keep its context small, which is exactly what the description tells it to expect, gets 1000 events and 200 round trips instead.

Splitting the two ideas fixes it cleanly: keep page size as an internal constant, and let limit cap the events actually returned. That also resolves the truncation-end point (guild_client.py:355) in the same edit.

Confirmed fixed

All six points from my last review, each verified by running the code:

  • extract_output noise — all three cases I reported now return '', and a genuinely-new answer type still surfaces.
  • get_session_events pagination — the answer is found across 4 / 250 / 1000-event transcripts, with all three stop conditions present (empty page, has_more false, hard cap).
  • Coverage — I ran the suite: 100 passed. validateConfig now covers all five warning branches, beginGlobal covers the env fallbacks and clamps, and all three tool functions are tested. Contract suite still 298 passed.
  • The module header, config_int, _ENV_ATTR_MAP, the status-code constants, and examples/README.md.

You also caught a wrong endpoint path in the module header ({owner}/{workspace}{owner}~{workspace}) that I had missed. Good.

Non-blocking

  • guild_client.py:355 — the cap keeps the oldest 1000 events, but the transcript is oldest-first so the answer is at the other end. A 1400-event transcript returns ''. It fails empty rather than wrong, which is right, but silently.
  • guild_client.py:358 — the _has_more docstring promises a fallback the code does not have when there is no pagination block.
  • guild_client.py:143 — the noise deny-list matches exact strings while its comment says the "runtime_* / llm_* families". Recording it as a decision, not asking for a change.
  • nodes/test/framework/pipeline.py:36 — the one change here that can reach other nodes, and nothing tests it. I checked the collision risk by hand and it is clear.

One thing that is not on you: CI does not run this test suite. There is no pytest job for nodes/ in ci.yml, pr-checks.yml, or _build.yaml, so those 100 tests only ran because I ran them locally. Flagging it so neither of us reads a green check as "the node tests passed".

The core is in good shape now. Fix the limit contract and its two docs, and I am happy.

Comment thread nodes/src/nodes/tool_guild/guild_client.py Outdated
Comment thread nodes/src/nodes/tool_guild/guild_client.py Outdated
Comment thread nodes/src/nodes/tool_guild/guild_client.py Outdated
Comment thread nodes/src/nodes/tool_guild/guild_client.py
Comment thread nodes/src/nodes/tool_guild/IInstance.py
Comment thread nodes/src/nodes/tool_guild/README.md Outdated
Comment thread nodes/test/framework/pipeline.py
Addresses the second-round review (asclearuc): the MAX_EVENT_LIMIT cap kept the
oldest 1000 events, but the transcript is oldest-first so the answer is at the
other end — a >1000-event transcript returned '' silently.

- get_session_events now returns the MOST RECENT `limit` events (bounded by
  MAX_EVENT_LIMIT). When the API reports total_count > limit it seeks straight to
  the tail page; otherwise it pages forward, bounded, and trims to the tail. The
  answer (always last) is never dropped. Tests cover the seek path, the
  page-forward path, and the hard cap.
- `limit` is now documented as "max events to return (most recent)", consistent
  across the tool schema and README, instead of an undocumented page size.
- Fixed the `_has_more` docstring to match the code (no pagination block -> stop);
  split out `_total_count`.
- Noise deny-list comment now says the types are matched exactly, not by family.
- Added a test that the shared `_ENV_ATTR_MAP` change (KEY_ID/KEY_SECRET) resolves
  ROCKETRIDE_GUILD_* to the node's config fields.

Tests: 102 unit + contract pass (verified under a forced ASCII locale).

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

Copy link
Copy Markdown
Collaborator Author

Thanks — the limit-contract catch was the important one. Fixed in 2b72342d:

  • get_session_events now returns the most recent limit events, not the oldest. When the API reports total_count > limit it seeks straight to the tail page (offset = total_count - limit); otherwise it pages forward, bounded by MAX_EVENT_LIMIT, and trims to the tail. The answer (always last) is never dropped — including the >1000-event case that previously returned ''. Tests cover the seek path, the page-forward path, and the hard cap.
  • limit is documented consistently as "max events to return (most recent)" across the tool schema and the README, instead of an undocumented page size.
  • _has_more docstring now matches the code (no pagination block → stop); split _total_count out.
  • Noise deny-list comment now says the types are matched exactly, not by "family".
  • Added a test that the shared _ENV_ATTR_MAP change resolves ROCKETRIDE_GUILD_KEY_ID/_SECRET to apiKeyId/apiKeySecret (the one change that can reach other nodes).

On CI not running the node suite — you're right, and thanks for flagging it rather than letting the green check imply coverage. There is no pytest job for nodes/ in ci.yml; these 102 tests only run locally (and I ran them under a forced ASCII locale, which is how the earlier services.json encoding bug surfaced). Adding a nodes/ pytest job touches shared CI config, so I'd rather not fold it into this node PR — I can open a separate chore(ci): PR/issue for it if you'd like. For now the honest state is: node tests pass locally, not in CI.

102 unit + contract pass.

@EdwardLien0426

Copy link
Copy Markdown
Collaborator Author

Heads-up on the red checks at 2b72342d — none are from this PR's diff:

  • Build (Ubuntu/Windows) + Compile universal node constraints + CI OK fail on
    onnxruntime-gpu==1.20.1 ... unsatisfiable. That pin is in anonymize/requirements.txt and
    audio_transcribe/requirements.txt (not this node), and the universal-lockfile step aggregates
    every node's requirements, so one upstream-unavailable pin fails the whole job. The same job was
    green on my previous commit 320aea86 and this commit touches no dependency — onnxruntime-gpu==1.20.1
    became unresolvable upstream between the two runs. This is repo-wide (develop's next run will hit it
    too) and needs a separate dependency-bump PR by the owners of those two nodes, not a change here.
  • check-externals (PR) failed at "Set up vcpkg" with ECONNRESET from registry.npmjs.org — a
    transient network flake, unrelated to the diff. I don't have rerun rights on this repo; a maintainer
    rerun should clear it.

This PR's own checks (Ruff, gitleaks) are green, and tool_guild/requirements.txt is only
requests + idna. The node code and tests are unaffected. Flagging so the red isn't read as this PR's.

@asclearuc asclearuc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks @EdwardLien0426 — this is ready. You fixed the limit contract with a better design than the one I suggested, and the docs moved in the same commit.

Verdict: approved

Everything I raised across both rounds is fixed. I verified each one by running the code, not by reading the description.

limit now caps the result, and it costs less than before:

Agent asks Before Now
limit: 5 200 requests, 1000 events 2 requests, 5 events
limit: 100 10 requests, 1000 events 2 requests, 100 events

The tail is kept, not the head. A 5000-event transcript returns the last 100 and finds the answer. The 1400-event case that used to return '' now returns the answer in 2 requests. Seeking straight to the tail with total_count is better than the forward-paging fix I proposed — it makes the cost independent of transcript length.

Docs moved with the contract. Both the agent-facing description (IInstance.py:209) and README.md:85 now describe what the code does. The deny-list comment saying "matched exactly (not by prefix)" is the honest wording — thanks for picking the accurate option rather than widening the match.

Tests. I ran them, because CI has no pytest job for nodes/: 102 tool_guild + 314 contract = 416 passed. The new tests pin the tail-seek offset, the request-count bound, and the new _has_more signature. test_credential_env_vars_map_to_config_fields also closes my earlier point that the shared _ENV_ATTR_MAP change had nothing exercising it.

CI. Ruff, gitleaks, Shell API contract, and the macOS build pass. The Ubuntu and Windows builds were still running when I approved — they are the upstream onnxruntime pin and npm flake you already called out, not this diff. Please glance at them before merging.

Two notes, neither blocking

Both are inline. Neither is reachable against real Guild, so treat them as cleanup whenever you next touch this file — not as merge conditions.

I also resolved my own outdated review threads on this PR, so what stays open is only what still applies. I did not touch anyone else's threads.

Nice work on this one. The safety design — budget claimed before the billable POST, POSTs never retried, an empty answer preferred over a wrong one — is what makes the node trustworthy, and it held up under every case I threw at it.

Comment thread nodes/src/nodes/tool_guild/guild_client.py
Comment thread nodes/src/nodes/tool_guild/guild_client.py
@EdwardLien0426

Copy link
Copy Markdown
Collaborator Author

The Ubuntu/Windows builds are green now — I merged upstream/develop into the branch (98b392b9), which carried the onnxruntime pin fix (#1725). Compile universal node constraints, all three builds, Shell API contract, and check-externals pass; CI OK is green. The 102 tool_guild + 314 contract tests still pass after the merge.

On the two inline nits — agreed, and I'm deferring both rather than push new commits onto an approved, green PR:

  • guild_client.py:365 — recorded for next-touch. The fallback branch only runs on a has_more-without-total_count response, which real Guild never sends (the fixture always carries total_count, so the tail-seek fast path wins). When I next touch this file I'll page with a fixed DEFAULT_EVENT_LIMIT there and keep the [-cap:] trim, to give that branch the same page-size/result-cap separation as the main path.
  • guild_client.py:354 — agreed it's a known trade-off, not worth the complexity now: a running session has no final answer, so a stale total misses nothing that exists. Re-reading total_count from the second response would close it if it ever matters.

Both are unreachable against real Guild today, so they don't change behaviour on any live path.

@EdwardLien0426

Copy link
Copy Markdown
Collaborator Author

Resolved the eight open review threads to clear the merge block — all six from rounds 1–2 were fixed in commits (you confirmed each on approval), and the two guild_client.py nits are the acknowledged next-touch cleanups above. Please reopen any you disagree with. mergeStateStatus is now CLEAN.

@EdwardLien0426
EdwardLien0426 merged commit c48b0b6 into rocketride-org:develop Jul 29, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation module:nodes Python pipeline nodes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Guild.ai node (tool_guild)

2 participants