Skip to content

feat(node): add Nebius Agentic Search (llm_nebius + tool_tavily)#1050

Open
EdwardLien0426 wants to merge 15 commits into
rocketride-org:developfrom
EdwardLien0426:feat/RR-1047-nebius-agentic-search
Open

feat(node): add Nebius Agentic Search (llm_nebius + tool_tavily)#1050
EdwardLien0426 wants to merge 15 commits into
rocketride-org:developfrom
EdwardLien0426:feat/RR-1047-nebius-agentic-search

Conversation

@EdwardLien0426
Copy link
Copy Markdown
Collaborator

@EdwardLien0426 EdwardLien0426 commented Jun 1, 2026

Fixes #1047

What

Adds Nebius Agentic Search by composing the engine's existing agent pattern — no new agent loop:

  • llm_nebius — Nebius Token Factory LLM provider node (OpenAI-compatible, fixed base URL https://api.tokenfactory.nebius.com/v1/, NEBIUS_API_KEY env fallback). Cloned from llm_gmi_cloud.
  • tool_tavily — Tavily real-time web search exposed as an agent tool (POST https://api.tavily.com/search, retry + SSRF guard, TAVILY_API_KEY env fallback). Cloned from tool_exa_search.
  • Reuses the existing agent_deepagent to drive the LLM↔tool loop.
  • examples/nebius-agentic-search.pipe — template wiring chat → agent_deepagent ← llm_nebius + tool_tavily.

Why this shape

Every agent node in the repo (agent_deepagent/crewai/langchain/rocketride) drives a wired llm + tool channel; there is no precedent for a self-contained node bundling its own LLM loop, and the engine's LLM seam is text-only. Composing existing infra reuses a battle-tested loop and keeps the change small (the only substantive new code is the Tavily tool).

Verification

  • builder nodes:test670 passed / 0 failed.
  • Unit tests for the Tavily helpers (retry, SSRF, response shaping).
  • End-to-end on the canvas with real keys: Deep Agent discovered the Tavily tool → Nebius reasoned → called tavily → reasoned again → returned a correct, grounded answer.

Notes

  • Reasoning runs on a Nebius-hosted open model (default meta-llama/Llama-3.3-70B-Instruct, configurable); search runs on Tavily (Nebius-acquired). Both require their own API key.
  • classType: llm_nebius → LLM group, tool_tavily → Tools group.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Nebius LLM provider integration for OpenAI‑compatible access to Nebius Token Factory models
    • Tavily web‑search tool for agents with configurable results, depth, and topic
    • Example "Nebius Agentic Search" pipeline demonstrating agentic web research
  • Documentation

    • Provider and tool setup guides and agent tools documentation updated
  • Tests

    • Unit tests added for Tavily tool result shaping and URL validation

EdwardLien0426 and others added 10 commits June 1, 2026 12:07
Self-contained agentic search node mirroring search_exa: a Nebius Token
Factory LLM runs a bounded native-function-calling loop over an internal
Tavily web-search tool and returns a cited answer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(Shape B)

Build Nebius Agentic Search by composing the engine's existing agent
pattern: new tool_tavily_search + new llm_nebius (Token Factory) wired
into the existing agent_deepagent loop, plus a pipeline template. Chosen
over a self-contained bundled-LLM node because every agent in the repo
is built this way and it reuses a battle-tested loop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…SRF guard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Scaffolds llm_nebius as an OpenAI-compatible LLM provider pointing at
https://api.tokenfactory.nebius.com/v1/ with profiles for Llama 3.3 70B
(default), Qwen3 235B, DeepSeek V3, and a custom slot.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oss-test contamination

The unit-test bootstrap stubbed rocketlib/ai.common/requests via
sys.modules.setdefault but never removed them. Under the full
`builder nodes:test-full` run (shared pytest session, xdist workers)
the leaked MagicMock stubs overrode the real modules for sibling node
tests, causing 27 spurious failures in tool_git and tool_filesystem
(real on this branch, absent on develop). Now stubs are injected only
when missing and dropped immediately after importing the module under
test, so nothing leaks into the shared session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The design spec and implementation plan under docs/superpowers/ are
internal workflow artifacts (and carry author/personal info); they are
not part of the shippable node deliverable. Remove them so the PR
contains only the two nodes, the example pipeline, the node-registry
doc entry, and tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added docs Documentation module:nodes Python pipeline nodes labels Jun 1, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

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 two new nodes: llm_nebius (Nebius Token Factory OpenAI-compatible LLM provider) and tool_tavily (Tavily web-search agent tool), including service configs, implementations, lifecycle/validation, tests, docs, and an example pipeline wiring them into an agentic search flow.

Changes

Nebius LLM Provider Node

Layer / File(s) Summary
Service configuration and model profiles
nodes/src/nodes/llm_nebius/services.json
Service protocol, metadata, preconfig and four model profiles, per-profile API key fields, conditional profile selector, required Pipe shape, and smoke test.
Chat client implementation and error handling
nodes/src/nodes/llm_nebius/nebius.py
Chat class wrapping langchain_openai.ChatOpenAI with Nebius base URL, API key resolution, token/temperature settings, retryable error detection, and exception mapping.
Global lifecycle, validation, and instance
nodes/src/nodes/llm_nebius/IGlobal.py, nodes/src/nodes/llm_nebius/IInstance.py
IGlobal lifecycle manages requirements loading, API key enforcement, single-token validateConfig probe (suppresses rate-limit/429), beginGlobal/endGlobal state; IInstance is a minimal LLMBase subclass.
Package exports, requirements, and documentation
nodes/src/nodes/llm_nebius/__init__.py, nodes/src/nodes/llm_nebius/requirements.txt, nodes/src/nodes/llm_nebius/README.md
init re-exports IGlobal/IInstance and provides lazy getChat(); requirements add langchain-openai and openai; README documents node usage, lanes, profiles, and base URL.

Tavily Search Tool Node

Layer / File(s) Summary
Service configuration and field schema
nodes/src/nodes/tool_tavily/services.json
Tool service metadata, preconfig defaults (apikey, maxResults 5, searchDepth advanced, topic general), secure apikey field and validation constraints, key-gated test metadata, and pipeline-builder shape.
Global state and configuration validation
nodes/src/nodes/tool_tavily/IGlobal.py
IGlobal reads config or TAVILY_API_KEY, enforces apikey presence (raises ValueError if missing in non-config mode), clamps/normalizes maxResults/searchDepth/topic, validateConfig emits warnings, endGlobal clears state.
Tool function and request invocation
nodes/src/nodes/tool_tavily/IInstance.py (tavily)
tavily tool validates/normalizes args (non-empty query, bounded max_results, allowed search_depth/topic, optional time_range and domain filters), builds JSON request with Bearer auth, invokes _request_with_retry, and returns structured success/failure output.
Result shaping, URL validation, and retry logic
nodes/src/nodes/tool_tavily/IInstance.py (helpers)
_shape_results maps Tavily results to output schema, skipping unsafe URLs; _validate_public_url enforces http(s), resolves DNS, and blocks private/loopback/link-local/reserved/multicast/unspecified IPs; _request_with_retry posts with exponential backoff on 429, 5xx, and timeouts.
Unit tests, package exports, requirements, and documentation
nodes/test/test_tool_tavily.py, nodes/src/nodes/tool_tavily/__init__.py, nodes/src/nodes/tool_tavily/requirements.txt, nodes/src/nodes/tool_tavily/README.md
Network-independent unit tests for result shaping and URL validation with DNS monkeypatching, init exports, requests dependency, and README documenting agent invoke usage and config fields.

Documentation and Example Pipeline

Layer / File(s) Summary
Node registry updates and example pipeline
docs/README-nodes.md, examples/nebius-agentic-search.pipe
Docs add llm_nebius to LLM Providers and an "Agent Tools" section documenting tool_tavily; example pipeline wires chat trigger → agent_deepagent (using llm_nebius + tool_tavily) → response mapping with UI layout and env var placeholders.

Sequence Diagram: Nebius Agentic Search (high-level)

sequenceDiagram
  participant User
  participant ChatTrigger as chat_1
  participant Agent as agent_deepagent_1
  participant Nebius as llm_nebius_1
  participant Tavily as tool_tavily_1
  participant Response as response_answers_1

  User->>ChatTrigger: submit query
  ChatTrigger->>Agent: questions lane
  Agent->>Nebius: reasoning / generate search queries
  Agent->>Tavily: invoke search tool (query)
  Tavily-->>Agent: search results (filtered)
  Agent->>Nebius: refine + answer
  Nebius-->>Agent: concise answer with citations
  Agent->>Response: answers lane
  Response-->>User: present answer
Loading

Estimated code review effort:
🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers:

  • jmaionchi
  • Rod-Christensen
  • stepmikhaylov
  • asclearuc

🐰 A rabbit peeks at code so spry,
Nebius reasons, Tavily spies,
Together they hop, fetch, and cite,
Safe searches by day and night—
Hooray, the agent’s ready to try!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.33% 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
Linked Issues check ✅ Passed All primary requirements from issue #1047 are met: llm_nebius node created pointing to Nebius Token Factory, tool_tavily node added with Tavily API integration including retry and SSRF protections, example pipeline provided, and existing agent_deepagent reused as specified.
Out of Scope Changes check ✅ Passed All changes directly support the Nebius Agentic Search objectives: LLM and tool nodes, documentation, example pipeline, tests, and node registration configurations are all in-scope.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and concisely summarizes the main change: adding a Nebius Agentic Search feature by composing the llm_nebius LLM provider with the tool_tavily web search tool node.

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

✨ 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 and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

No description provided.

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

Inline comments:
In `@examples/nebius-agentic-search.pipe`:
- Line 94: Remove the redundant config.type property from the
tool_tavily_search_1 component since tool_tavily_search reads only apikey,
maxResults, searchDepth and topic and the server-side
PipelineConfig::encryptComponent will derive type from provider when config.type
is absent; edit the tool_tavily_search_1 config to drop the "type" field so only
the required fields remain, leaving provider (if present) intact for type
derivation.

In `@nodes/src/nodes/tool_tavily_search/requirements.txt`:
- Line 1: The Tavily node's requirements file lists a bare "requests" causing
inconsistent resolution; update the shared baseline
nodes/src/nodes/requirements.txt to specify a consistent minimum (e.g.
requests>=2.34.2) and make the Tavily file
(nodes/src/nodes/tool_tavily_search/requirements.txt) match that same lower
bound (or remove the duplicate if you want the baseline to be authoritative) so
both files reference the same requests>=2.34.2 baseline and avoid
scanner/old-version drift.

In `@nodes/test/test_tool_tavily_search.py`:
- Around line 100-109: The test test_shape_results_maps_tavily_fields is doing a
real DNS lookup because _shape_results calls _validate_public_url which uses
socket.getaddrinfo; stub out DNS in the test by monkeypatching
socket.getaddrinfo (or monkeypatching the module's _validate_public_url to
always allow the example URL) before calling mod._shape_results so the result
isn't dropped—ensure the monkeypatch returns a harmless truthy response
consistent with getaddrinfo's expected return type, then call _shape_results and
run the existing assertions.
🪄 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: 33e85298-f9ac-4faa-83e6-1877c7c33060

📥 Commits

Reviewing files that changed from the base of the PR and between 08558ce and 59fd146.

⛔ Files ignored due to path filters (2)
  • nodes/src/nodes/llm_nebius/nebius.svg is excluded by !**/*.svg
  • nodes/src/nodes/tool_tavily_search/tavily.svg is excluded by !**/*.svg
📒 Files selected for processing (16)
  • docs/README-nodes.md
  • examples/nebius-agentic-search.pipe
  • nodes/src/nodes/llm_nebius/IGlobal.py
  • nodes/src/nodes/llm_nebius/IInstance.py
  • nodes/src/nodes/llm_nebius/README.md
  • nodes/src/nodes/llm_nebius/__init__.py
  • nodes/src/nodes/llm_nebius/nebius.py
  • nodes/src/nodes/llm_nebius/requirements.txt
  • nodes/src/nodes/llm_nebius/services.json
  • nodes/src/nodes/tool_tavily_search/IGlobal.py
  • nodes/src/nodes/tool_tavily_search/IInstance.py
  • nodes/src/nodes/tool_tavily_search/README.md
  • nodes/src/nodes/tool_tavily_search/__init__.py
  • nodes/src/nodes/tool_tavily_search/requirements.txt
  • nodes/src/nodes/tool_tavily_search/services.json
  • nodes/test/test_tool_tavily_search.py

Comment thread examples/nebius-agentic-search.pipe Outdated
@@ -0,0 +1 @@
requests
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.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# How do sibling tool/search nodes declare the requests dependency?
fd 'requirements.txt' nodes/src/nodes --exec sh -c 'echo "== $1 =="; grep -i requests "$1"' _ {}

Repository: rocketride-org/rocketride-server

Length of output: 4300


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Inspect the shared requirements file and look for any version pins for requests
targets=(
  "nodes/src/nodes/requirements.txt"
)
for f in "${targets[@]}"; do
  if [ -f "$f" ]; then
    echo "==== $f ===="
    nl -ba "$f" | sed -n '1,120p'
  else
    echo "Missing $f"
  fi
done

# Look for requests version pins in nodes/src (requirements/constraints/lock files)
rg -n --hidden -S "(requests\s*[<>=!]=\s*|requests==|requests>=)" nodes/src || true
rg -n --hidden -S "(constraints|constraint).*requirements" -g'*.txt' . || true
# Find common pip lock/constraints files at repo root
ls -la | head -n 50
find . -maxdepth 3 -type f \( -iname '*constraints*.txt' -o -iname '*requirements*.txt' -o -iname '*requirements*.in' -o -iname '*poetry*.lock' -o -iname '*pipfile*' \) | head -n 200

Repository: rocketride-org/rocketride-server

Length of output: 181


🏁 Script executed:

#!/bin/bash
set -euo pipefail
targets=("nodes/src/nodes/requirements.txt")
for f in "${targets[@]}"; do
  if [ -f "$f" ]; then
    echo "==== $f ===="
    nl -ba "$f" | sed -n '1,120p'
  else
    echo "Missing $f"
  fi
done

rg -n -S "(requests\s*[<>=!]=\s*|requests==|requests>=)" . || true
find . -maxdepth 4 -type f \( -iname '*constraints*.txt' -o -iname '*requirements*.txt' -o -iname '*requirements*.in' -o -iname '*poetry.lock' -o -iname '*Pipfile*' -o -iname 'poetry*.lock' \) | head -n 200

Repository: rocketride-org/rocketride-server

Length of output: 180


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "==== nodes/src/nodes/requirements.txt (first 120 lines) ===="
if [ -f "nodes/src/nodes/requirements.txt" ]; then
  cat -n "nodes/src/nodes/requirements.txt" | sed -n '1,120p'
else
  echo "Missing nodes/src/nodes/requirements.txt"
fi

echo
echo "==== Any explicit requests pins/constraints anywhere in repo ===="
rg -n -S "(^|\s)requests\s*(==|>=|<=|!=|<|>)" . -g'*.txt' || true
rg -n -S "(constraints|constraint).*requests" . -g'*.txt' || true

echo
echo "==== Candidate constraints/lock files (top 3 levels) ===="
find . -maxdepth 5 -type f \( -iname '*constraints*.txt' -o -iname '*requirements*.txt' -o -iname '*requirements*.in' -o -iname '*pipfile*' -o -iname 'poetry*.lock' \) | head -n 200

echo
echo "==== Contents of any constraints*.txt/requirements*.txt that mention requests (top 50 matches) ===="
rg -n -S "requests" . -g'*constraints*.txt' -g'*requirements*.txt' -m 50 || true

Repository: rocketride-org/rocketride-server

Length of output: 5852


Align requests version pinning to repo convention (prefer shared baseline).

nodes/src/nodes/tool_tavily_search/requirements.txt uses bare requests, and the shared baseline nodes/src/nodes/requirements.txt also uses bare requests (only nodes/src/nodes/tool_github/requirements.txt pins requests>=2.34.2). If you’re trying to prevent OSV/CVE scanner noise and block old requests resolution, apply a consistent lower bound (preferably in nodes/src/nodes/requirements.txt, e.g. requests>=2.34.2) rather than pinning only the Tavily node.

🧰 Tools
🪛 OSV Scanner (2.3.8)

[HIGH] 1-1: requests 2.9.2: undefined

(PYSEC-2018-28)


[HIGH] 1-1: requests 2.9.2: undefined

(PYSEC-2023-74)


[HIGH] 1-1: requests 2.9.2: Requests vulnerable to .netrc credentials leak via malicious URLs

(GHSA-9hjg-9r4m-mvj7)


[HIGH] 1-1: requests 2.9.2: Requests Session object does not verify requests after making first request with verify=False

(GHSA-9wx4-h78v-vm56)


[HIGH] 1-1: requests 2.9.2: Requests has Insecure Temp File Reuse in its extract_zipped_paths() utility function

(GHSA-gc5v-m9x4-r6x2)


[HIGH] 1-1: requests 2.9.2: Unintended leak of Proxy-Authorization header in requests

(GHSA-j8r2-6x86-q33q)


[HIGH] 1-1: requests 2.9.2: Insufficiently Protected Credentials in Requests

(GHSA-x84v-xcm2-53pg)

🤖 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_tavily_search/requirements.txt` at line 1, The Tavily
node's requirements file lists a bare "requests" causing inconsistent
resolution; update the shared baseline nodes/src/nodes/requirements.txt to
specify a consistent minimum (e.g. requests>=2.34.2) and make the Tavily file
(nodes/src/nodes/tool_tavily_search/requirements.txt) match that same lower
bound (or remove the duplicate if you want the baseline to be authoritative) so
both files reference the same requests>=2.34.2 baseline and avoid
scanner/old-version drift.

Comment thread nodes/test/test_tool_tavily_search.py Outdated
…ew nits

CI 'Test' step ran the services.json dynamic tests for both nodes with
no API key (no NEBIUS_API_KEY/TAVILY_API_KEY in CI), and beginGlobal
raises when the key is missing -> RuntimeError. Add 'requires' to both
test blocks so the dynamic test is filtered out at collection when the
key env var is absent (matches llm_openai/llm_ollama/rerank_cohere).

Also addresses CodeRabbit review:
- test_shape_results: stub socket.getaddrinfo so it is network-independent.
- examples/.pipe: drop redundant config.type (server derives type from provider).

Co-Authored-By: Claude Opus 4.8 <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.

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_tavily_search/services.json (1)

118-133: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

requires: ["TAVILY_API_KEY"] contradicts this test case's documented intent.

The comment at Lines 118-122 explains this case deliberately runs with no apikey so validateConfig emits a warning (not an error), exercising config validation without live API calls. Gating it behind "requires": ["TAVILY_API_KEY"] means it is now filtered out in keyless CI — so the keyless validation path it was designed to cover no longer runs there. Either drop the requires gate (and keep this as a true no-key validation case) or update the comment to reflect that the case now expects the key to be present.

🤖 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_tavily_search/services.json` around lines 118 - 133, The
test entry named "Config validation with placeholder key" documents that it
should run without an API key to exercise validateConfig's warning path, but the
test object includes requires: ["TAVILY_API_KEY"] which prevents keyless CI
runs; remove the requires gate from the "test" object (or alternatively update
the comment to state the test now requires a key) so the test either truly runs
keyless to exercise validateConfig or the documentation matches the new
requirement; reference the "test" JSON block and the requires field and
validateConfig behavior when making the change.
🤖 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.

Outside diff comments:
In `@nodes/src/nodes/tool_tavily_search/services.json`:
- Around line 118-133: The test entry named "Config validation with placeholder
key" documents that it should run without an API key to exercise
validateConfig's warning path, but the test object includes requires:
["TAVILY_API_KEY"] which prevents keyless CI runs; remove the requires gate from
the "test" object (or alternatively update the comment to state the test now
requires a key) so the test either truly runs keyless to exercise validateConfig
or the documentation matches the new requirement; reference the "test" JSON
block and the requires field and validateConfig behavior when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 09cb55e9-447d-4824-a75a-50f4f2ad1f38

📥 Commits

Reviewing files that changed from the base of the PR and between 59fd146 and 3781aba.

📒 Files selected for processing (4)
  • examples/nebius-agentic-search.pipe
  • nodes/src/nodes/llm_nebius/services.json
  • nodes/src/nodes/tool_tavily_search/services.json
  • nodes/test/test_tool_tavily_search.py
💤 Files with no reviewable changes (1)
  • examples/nebius-agentic-search.pipe

EdwardLien0426 and others added 4 commits June 1, 2026 15:54
No code change. The previous run's failure was tests/RocketRideClient.test.ts
(client-typescript disconnect teardown leak), unrelated to this PR's Python
nodes; macOS passed. Empty commit to re-run CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gate

The 'test' case comment claimed it runs keyless to exercise validateConfig,
but the dynamic test builds+runs the node (calls beginGlobal, which needs a
key) and the block now carries requires:[TAVILY_API_KEY]. Update the comment
to state the case is skipped without the key, and rename it accordingly.
Addresses CodeRabbit. (Contract tests still cover structure keyless.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the redundant _search suffix from the node id/provider (the agent
tool method is still tavily_search). Updates the directory, services.json
protocol/path/field keys, IGlobal messages, README, the example pipeline
(provider + component id), the node-registry doc, and the unit-test module.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename the agent tool method tavily_search -> tavily and the display
name "Tavily Search" -> "Tavily" (Tavily is a search tool, so the
suffix is redundant). The agent trace now shows tool_tavily_1.tavily.

Also completes the node-id rename that 78fd9a1 captured only as file
moves: this carries the in-file content edits (services.json
protocol/path/field keys, IGlobal messages, README, example pipeline,
node-registry doc, unit-test module) from tool_tavily_search ->
tool_tavily.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@EdwardLien0426 EdwardLien0426 changed the title feat(node): add Nebius Agentic Search (llm_nebius + tool_tavily_search) feat(node): add Nebius Agentic Search (llm_nebius + tool_tavily) Jun 1, 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

🤖 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/nebius-agentic-search.pipe`:
- Line 145: The example pipeline contains a hardcoded "project_id" value that
ties the template to a specific UUID; remove the "project_id":
"c5ba857f-30ff-4f3d-b5bf-9ee90d3ee33b" entry from the
examples/nebius-agentic-search.pipe example (or replace it with a short comment
like // set your project_id here) so the example is generic and copy-paste
ready; locate the "project_id" key in the JSON block and either delete that line
or change it to a placeholder/comment accordingly.

In `@nodes/src/nodes/tool_tavily/IGlobal.py`:
- Around line 55-59: The apikey selection currently does cfg.get('apikey') or
os.environ.get(...) then .strip(), which treats whitespace-only cfg values as
truthy and prevents falling back to TAVILY_API_KEY; change beginGlobal() (and
the similar logic in validateConfig()) to strip each candidate first (e.g.,
cfg_apikey = (cfg.get('apikey') or '').strip(), env_apikey =
os.environ.get('TAVILY_API_KEY','').strip()) and then set apikey = cfg_apikey or
env_apikey so a whitespace-only configured key no longer blocks the environment
variable fallback and the error message is accurate.

In `@nodes/src/nodes/tool_tavily/IInstance.py`:
- Around line 159-162: _request_with_retry currently only retries
requests.exceptions.Timeout and thus treats requests.exceptions.ConnectionError
as a fatal requests.RequestException; update _request_with_retry to include
requests.exceptions.ConnectionError (and/or broaden to retry
requests.RequestException where appropriate) in its retryable exceptions list
and ensure the retry/backoff logic in _request_with_retry (referenced by the
call in the try block where body = _request_with_retry(...)) handles
ConnectionError the same way as Timeout so connection-level transport failures
are retried rather than immediately returning an error.
🪄 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: 8c87c376-3a94-481a-8439-c4eaca637000

📥 Commits

Reviewing files that changed from the base of the PR and between 78fd9a1 and c3d1665.

📒 Files selected for processing (7)
  • docs/README-nodes.md
  • examples/nebius-agentic-search.pipe
  • nodes/src/nodes/tool_tavily/IGlobal.py
  • nodes/src/nodes/tool_tavily/IInstance.py
  • nodes/src/nodes/tool_tavily/README.md
  • nodes/src/nodes/tool_tavily/services.json
  • nodes/test/test_tool_tavily.py

}
],
"source": "chat_1",
"project_id": "c5ba857f-30ff-4f3d-b5bf-9ee90d3ee33b",
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.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider omitting the hardcoded project_id in the example pipeline.

The project_id field is optional and ties the example to a specific project UUID. For an example that users may copy, omitting this field would make the template more immediately usable without requiring users to replace the ID. Alternatively, a comment could indicate that users should set their own project ID.

📝 Optional simplification
   ],
   "source": "chat_1",
-  "project_id": "c5ba857f-30ff-4f3d-b5bf-9ee90d3ee33b",
   "viewport": {
📝 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
"project_id": "c5ba857f-30ff-4f3d-b5bf-9ee90d3ee33b",
],
"source": "chat_1",
"viewport": {
"x": 0,
"y": 0,
"zoom": 1
},
"version": 1,
"docRevision": 1
}
🤖 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 `@examples/nebius-agentic-search.pipe` at line 145, The example pipeline
contains a hardcoded "project_id" value that ties the template to a specific
UUID; remove the "project_id": "c5ba857f-30ff-4f3d-b5bf-9ee90d3ee33b" entry from
the examples/nebius-agentic-search.pipe example (or replace it with a short
comment like // set your project_id here) so the example is generic and
copy-paste ready; locate the "project_id" key in the JSON block and either
delete that line or change it to a placeholder/comment accordingly.

Comment on lines +55 to +59
apikey = str(cfg.get('apikey') or os.environ.get('TAVILY_API_KEY', '')).strip()

if not apikey:
error('tool_tavily: apikey is required — set it in node config or TAVILY_API_KEY env var')
raise ValueError('tool_tavily: apikey is required')
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Strip the configured key before falling back to TAVILY_API_KEY.

A whitespace-only apikey is truthy, so the current cfg.get('apikey') or ... path wins and then .strip() turns it into ''. In that case beginGlobal() still raises even when TAVILY_API_KEY is set, so the new message overstates the fallback behavior. validateConfig() has the same bug.

Suggested fix
-        apikey = str(cfg.get('apikey') or os.environ.get('TAVILY_API_KEY', '')).strip()
+        cfg_apikey = str(cfg.get('apikey') or '').strip()
+        env_apikey = str(os.environ.get('TAVILY_API_KEY', '')).strip()
+        apikey = cfg_apikey or env_apikey
@@
-            apikey = str(cfg.get('apikey') or os.environ.get('TAVILY_API_KEY', '')).strip()
+            cfg_apikey = str(cfg.get('apikey') or '').strip()
+            env_apikey = str(os.environ.get('TAVILY_API_KEY', '')).strip()
+            apikey = cfg_apikey or env_apikey

Also applies to: 73-76

🤖 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_tavily/IGlobal.py` around lines 55 - 59, The apikey
selection currently does cfg.get('apikey') or os.environ.get(...) then .strip(),
which treats whitespace-only cfg values as truthy and prevents falling back to
TAVILY_API_KEY; change beginGlobal() (and the similar logic in validateConfig())
to strip each candidate first (e.g., cfg_apikey = (cfg.get('apikey') or
'').strip(), env_apikey = os.environ.get('TAVILY_API_KEY','').strip()) and then
set apikey = cfg_apikey or env_apikey so a whitespace-only configured key no
longer blocks the environment variable fallback and the error message is
accurate.

Comment on lines +159 to +162
try:
body = _request_with_retry(url=TAVILY_API_URL, headers=headers, payload=payload)
except RuntimeError as exc:
return {'success': False, 'query': query, 'num_results': 0, 'results': [], 'error': str(exc)}
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo 'Inspect the retry branches in the Tavily helper:'
rg -n -A6 -B3 'except requests\.exceptions\.Timeout|except requests\.RequestException|requests\.post|time\.sleep' nodes/src/nodes/tool_tavily/IInstance.py

echo
echo 'Check whether the Tavily tests cover ConnectionError retries yet:'
fd -i 'test_tool_tavily.py' nodes/test --exec rg -n -A2 -B2 'ConnectionError|Timeout|429|500|retry' {}

Repository: rocketride-org/rocketride-server

Length of output: 2345


Retry connection-level transport failures in _request_with_retry

  • _request_with_retry() retries only requests.exceptions.Timeout; requests.exceptions.ConnectionError falls into requests.RequestException and fails immediately (lines ~247-258).
  • Tavily tests mock requests.exceptions.Timeout / requests.exceptions.RequestException but don’t exercise a ConnectionError retry path.
Suggested fix
-        except requests.exceptions.Timeout:
+        except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as exc:
             if attempt < max_retries:
                 delay = base_delay * (2**attempt)
-                debug(f'Tavily request timeout, retrying in {delay}s ({attempt + 1}/{max_retries})')
+                debug(
+                    f'Tavily transport error ({type(exc).__name__}), retrying in {delay}s '
+                    f'({attempt + 1}/{max_retries})'
+                )
                 time.sleep(delay)
                 continue
-            raise RuntimeError('Tavily: request timed out after all retries') from None
+            raise RuntimeError(
+                f'Tavily: request failed after all retries ({type(exc).__name__})'
+            ) from None
🤖 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_tavily/IInstance.py` around lines 159 - 162,
_request_with_retry currently only retries requests.exceptions.Timeout and thus
treats requests.exceptions.ConnectionError as a fatal requests.RequestException;
update _request_with_retry to include requests.exceptions.ConnectionError
(and/or broaden to retry requests.RequestException where appropriate) in its
retryable exceptions list and ensure the retry/backoff logic in
_request_with_retry (referenced by the call in the try block where body =
_request_with_retry(...)) handles ConnectionError the same way as Timeout so
connection-level transport failures are retried rather than immediately
returning an error.

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.

feat(node): add Nebius node

1 participant