Skip to content

feat(nodes): add Cohere Rerank pipeline node - #898

Merged
stepmikhaylov merged 7 commits into
rocketride-org:developfrom
nihalnihalani:feat/cohere-rerank-clean
May 20, 2026
Merged

stepmikhaylov merged 7 commits into
rocketride-org:developfrom
nihalnihalani:feat/cohere-rerank-clean

Conversation

@nihalnihalani

@nihalnihalani nihalnihalani commented May 15, 2026 •

Copy link
Copy Markdown
Collaborator

#Hack-with-bay-2

Contribution Type

Feature — Add Cohere Rerank pipeline node for RAG quality improvement

Resubmission of closed PR #499 (rerank portion only)

PR #499 was closed by @Rod-Christensen on 2026-05-15 for scope reasons (it had grown to include 3 nodes plus unrelated file churn). This PR is the rerank_cohere portion only, on a clean branch from current develop, with no unrelated changes. Companion PRs are being opened for the chunker and search_hybrid nodes separately.

Summary

Adds a rerank_cohere pipeline node for the Embedding → Vector Store → Rerank → LLM RAG flow.

  • RerankClient wraps Cohere ClientV2.rerank; validates top_n >= 1 and 0.0 <= min_score <= 1.0 at construction; maps Cohere SDK exceptions onto a stable hierarchy (RerankAuthenticationError, RerankRateLimitError, RerankBadRequestError, RerankServerError) with names chosen for circuit-breaker substring heuristics.
  • IGlobal.validateConfig only checks config presence/format — no live API call, so a Cohere outage doesn't break the config UI.
  • IInstance.writeQuestions deep-copies the question (fan-out safe), builds a parallel original_indices list so rerank results index back to the correct original Doc even when blank documents are filtered out, and always calls writeAnswers(answer) so downstream nodes get an output even when min_score filters everything.
  • services.json exposes top_n and min_score for all preset profiles (not just custom), uses rerank-english-v3.0 (real model id), and constrains top_n.minimum: 1 and min_score to [0.0, 1.0].

Review items addressed (from @asclearuc 2026-03-31 review on #499)

  • Metadata-misalignment bug — parallel original_indices keeps rerank results pinned to their source docs.
  • Output-lane fix — writeAnswers(Answer) instead of writeAnswers(question).
  • services.json — custom profile has title; top_n.minimum: 1; min_score range [0.0, 1.0]; rerank-v3.0 → rerank-english-v3.0; presets expose top_n/min_score.
  • IGlobal.validateConfig no longer calls client.rerank(...).
  • RerankClient validates top_n and min_score in constructor; unknown Cohere errors wrap as RerankServerError.
  • test_rerank_cohere.py resets mocks between tests.

Validation

  • 44 unit tests covering rerank ordering, top_n filtering, min_score thresholds, error mapping, IGlobal lifecycle, IInstance contract, metadata preservation, mutation prevention, and circuit-breaker exception compatibility. All pass locally.
  • Clean diff: only nodes/src/nodes/rerank_cohere/, the matching test, the Cohere mock package for dynamic tests, the cohere icon, and the one-line icon-map registration.

Companion PRs

  • chunker — separate PR
  • search_hybrid — separate PR

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added Cohere rerank node to reorder retrieved documents by relevance using the Cohere reranking API. Supports configurable model selection, relevance scoring thresholds, and result limiting. Includes comprehensive error handling for authentication, rate limiting, and API issues.
  • Tests

    • Added comprehensive test suite covering reranking behavior, configuration validation, error handling, and integration scenarios.

Review Change Stack

@github-actions github-actions Bot added module:nodes Python pipeline nodes module:ui Chat UI and Dropper UI labels May 15, 2026
@coderabbitai

coderabbitai Bot commented May 15, 2026 •

Copy link
Copy Markdown
Contributor

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

This pull request introduces a complete Cohere document reranking node for RocketRide. The implementation includes a rerank client wrapper, global lifecycle management, instance-level question processing, service definition with profiles, comprehensive mocking for tests, a full test suite, and UI icon registration.

Changes

Cohere Rerank Node

Layer / File(s) Summary
Rerank client wrapper and exception hierarchy
nodes/src/nodes/rerank_cohere/rerank_client.py
RerankClient validates configuration (model, top_n, min_score, apikey), constructs Cohere ClientV2, and provides rerank() and rerank_with_threshold() methods that map Cohere SDK exceptions to custom RerankError subclasses and return ranked results as dicts with index/score/document.
Global lifecycle and configuration
nodes/src/nodes/rerank_cohere/IGlobal.py
IGlobal manages a shared _reranker instance, conditionally installs SDK dependencies (skipped under ROCKETRIDE_MOCK), validates config without live API calls, initializes RerankClient in source mode, and clears state on teardown.
Document reranking and answer generation
nodes/src/nodes/rerank_cohere/IInstance.py
IInstance.writeQuestions() deep-copies questions, extracts query text, filters document content, calls rerank_with_threshold(), remaps Cohere result indices to original document positions while preserving metadata, and writes reranked documents and concatenated answers.
Service definition, packaging, and requirements
nodes/src/nodes/rerank_cohere/__init__.py, nodes/src/nodes/rerank_cohere/requirements.txt, nodes/src/nodes/rerank_cohere/services.json
Package exports IGlobal/IInstance; requirements.txt pins cohere>=5.0.0,<6.0.0; services.json defines node metadata, profiles, conditional schema mappings for model/tuning parameters, and an embedded integration test.
Mock Cohere SDK for testing
nodes/test/mocks/cohere/__init__.py, nodes/test/mocks/cohere/errors.py
ClientV2.rerank() implements deterministic overlap-based scoring; errors.py provides pass-through Cohere-compatible exception classes for test scenarios.
Comprehensive unit tests
nodes/test/rerank_cohere/test_all.py
TestRerankClient validates initialization, rerank() behavior, exception mapping, top_n coercion, and min_score validation; TestIGlobal checks lifecycle and dependency handling; TestIInstance validates document reranking, metadata preservation, and answer generation; TestRerankExceptions confirms exception inheritance.
UI icon and test framework integration
packages/shared-ui/src/components/canvas/util/get-icon-path.ts, nodes/test/framework/pipeline.py
Icon map registers the bundled Cohere SVG; mock credentials entry supplies apikey placeholder when ROCKETRIDE_MOCK is enabled.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • rocketride-org/rocketride-server#687: Adds avoidMocks handling and adjusts ROCKETRIDE_MOCK usage in test/pipeline framework, which indirectly affects the rerank node's mock-gating behavior.

Suggested reviewers

  • jmaionchi
  • stepmikhaylov
  • Rod-Christensen

Poem

🐰 Cohere's ranking hops in with grace,
Documents reordered, each finds its place,
Mocks keep the tests from the real API,
Questions and answers now dance, oh my!
A node is born in the RocketRide sky. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.00% 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 clearly and concisely describes the main change: adding a new Cohere Rerank pipeline node to the nodes package.
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 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
Contributor

No description provided.

@nihalnihalani
nihalnihalani force-pushed the feat/cohere-rerank-clean branch from 7081de9 to 475c9b5 Compare May 15, 2026 20:39

@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 `@nodes/src/nodes/rerank_cohere/IGlobal.py`:
- Around line 58-66: The validateConfig logic (in IGlobal.py's validateConfig
handling of apikey and model) currently allows non-string or whitespace-only
values; update it to first ensure config.get('apikey') and config.get('model')
are instances of str and that apikey.strip() and model.strip() are non-empty; if
apikey is missing, not a str, or only whitespace, call warning('Cohere API key
is required') and return; if model is missing, not a str, or only whitespace,
call warning('Cohere model name must not be empty') and return; use the same
symbol names (apikey, model) so the checks replace the current loose truthiness
checks.

In `@nodes/src/nodes/rerank_cohere/rerank_client.py`:
- Around line 96-99: Boolean values currently pass the numeric isinstance checks
for _top_n and _min_score, so update those validations to explicitly exclude
bools: for _top_n in the constructor/validator change the condition to reject if
not (isinstance(self._top_n, int) and not isinstance(self._top_n, bool)) or
self._top_n < 1 (or use type(self._top_n) is int), and for _min_score change to
reject if not (isinstance(self._min_score, (int, float)) and not
isinstance(self._min_score, bool)) or not (0.0 <= self._min_score <= 1.0); apply
the same pattern to the other identical checks referenced (around the blocks at
the later occurrences) so True/False are no longer accepted as numeric values.

In `@test/nodes/test_rerank_cohere.py`:
- Around line 194-201: The test currently sets global imports by assigning
sys.modules['rocketlib']=_mock_rocketlib, sys.modules['cohere']=_mock_cohere,
sys.modules['cohere.errors']=_mock_cohere_errors, etc., which leaks mocks across
the pytest process; change these to scoped patches using
unittest.mock.patch.dict or pytest's monkeypatch (e.g., use
patch.dict(sys.modules, {...}) as a context manager or in a fixture) so that the
mappings for keys like 'rocketlib', 'ai', 'ai.common', 'ai.common.schema',
'ai.common.config', 'depends', 'cohere', and 'cohere.errors' are installed only
for the duration of the test(s) and automatically restored afterward; update
existing top-level assignments to be applied inside the test functions or a
module-scoped fixture that yields and then restores.
🪄 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: f52779e9-4d50-48f6-8a38-3d82e078c569

📥 Commits

Reviewing files that changed from the base of the PR and between 5362aad and 7081de9.

⛔ Files ignored due to path filters (1)
  • packages/shared-ui/src/assets/nodes/cohere.svg is excluded by !**/*.svg
📒 Files selected for processing (11)
  • nodes/src/nodes/rerank_cohere/IGlobal.py
  • nodes/src/nodes/rerank_cohere/IInstance.py
  • nodes/src/nodes/rerank_cohere/__init__.py
  • nodes/src/nodes/rerank_cohere/requirements.txt
  • nodes/src/nodes/rerank_cohere/rerank_client.py
  • nodes/src/nodes/rerank_cohere/services.json
  • nodes/test/framework/pipeline.py
  • nodes/test/mocks/cohere/__init__.py
  • nodes/test/mocks/cohere/errors.py
  • packages/shared-ui/src/components/canvas/util/get-icon-path.ts
  • test/nodes/test_rerank_cohere.py

Comment thread nodes/src/nodes/rerank_cohere/IGlobal.py
Comment thread nodes/src/nodes/rerank_cohere/rerank_client.py Outdated
Comment thread test/nodes/test_rerank_cohere.py Outdated

@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 `@nodes/src/nodes/rerank_cohere/IInstance.py`:
- Around line 54-63: The code accepts whitespace-only strings as valid
query/docs; update the extraction and validation to trim whitespace and treat
empty-after-trim as missing. Specifically, when building query_text from
question.questions (variables: query_text, first_question, first_question.text),
use .strip() on the extracted string and set query_text = '' if stripped is
empty, then raise ValueError if not query_text; apply the same change to the
analogous documents extraction block (the doc_text/document.text handling around
the other block mentioned).

In `@nodes/src/nodes/rerank_cohere/services.json`:
- Around line 2-235: The file contains JavaScript-style line comments (//) which
break strict JSON parsing; remove all leading and inline // comment lines and
convert any explanatory comment blocks into valid JSON values or external docs
so the document becomes strict JSON (e.g., keep keys like "title": "Cohere
Rerank", "protocol": "rerank_cohere://", "preconfig", "fields", "shape", "test"
intact and only remove/comment text outside JSON syntax). Ensure no trailing
commas remain and the final payload parses as JSON by validating the top-level
object that includes "title", "protocol", "classType", "preconfig.profiles",
"fields" and "shape".
🪄 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: fffff165-0c24-43ec-a3cb-44efee4bb863

📥 Commits

Reviewing files that changed from the base of the PR and between 7081de9 and 475c9b5.

⛔ Files ignored due to path filters (1)
  • packages/shared-ui/src/assets/nodes/cohere.svg is excluded by !**/*.svg
📒 Files selected for processing (11)
  • nodes/src/nodes/rerank_cohere/IGlobal.py
  • nodes/src/nodes/rerank_cohere/IInstance.py
  • nodes/src/nodes/rerank_cohere/__init__.py
  • nodes/src/nodes/rerank_cohere/requirements.txt
  • nodes/src/nodes/rerank_cohere/rerank_client.py
  • nodes/src/nodes/rerank_cohere/services.json
  • nodes/test/framework/pipeline.py
  • nodes/test/mocks/cohere/__init__.py
  • nodes/test/mocks/cohere/errors.py
  • packages/shared-ui/src/components/canvas/util/get-icon-path.ts
  • test/nodes/test_rerank_cohere.py

Comment thread nodes/src/nodes/rerank_cohere/IInstance.py
Comment thread nodes/src/nodes/rerank_cohere/services.json
Adds a `rerank_cohere` pipeline node that slots between vector search
and the LLM in RAG pipelines, improving answer quality by reranking
retrieved documents by relevance to the query.

Components:
- RerankClient — wraps Cohere ClientV2 with top_n/min_score validation
  and an exception hierarchy (RerankAuthenticationError, RerankRateLimitError,
  RerankBadRequestError, RerankServerError) named for circuit-breaker
  substring heuristics.
- IGlobal — standard validateConfig (no live API call) and beginGlobal
  lifecycle, with depends() for SDK auto-install.
- IInstance — deep-copies question (fan-out safe), maps rerank result
  indices back through original document positions so metadata stays
  aligned when min_score filters blank docs, always emits an answer.
- services.json — rerank-v3.5 (default), rerank-english-v3.0, custom
  profiles; top_n and min_score exposed in all preset shapes.

Resubmission of the rerank portion of closed PR rocketride-org#499 per maintainer
feedback: one PR per node, clean branch from develop, no unrelated
file changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nihalnihalani
nihalnihalani force-pushed the feat/cohere-rerank-clean branch from 475c9b5 to e5a1e90 Compare May 15, 2026 20:46

@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

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

53-80: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Trim query and document text before emptiness checks.

Whitespace-only strings still pass these guards, so blank queries/documents can reach the reranker instead of being rejected or filtered out locally.

Proposed fix
         query_text = ''
         if question.questions:
             first_question = question.questions[0]
             if hasattr(first_question, 'text'):
                 query_text = first_question.text
             elif isinstance(first_question, dict):
                 query_text = first_question.get('text', '')
+        if isinstance(query_text, str):
+            query_text = query_text.strip()

         if not query_text:
             raise ValueError('No query text found in question')
@@
         doc_texts = []
         original_indices = []
         for idx, doc in enumerate(question.documents):
-            if hasattr(doc, 'page_content') and doc.page_content:
+            if (
+                hasattr(doc, 'page_content')
+                and isinstance(doc.page_content, str)
+                and doc.page_content.strip()
+            ):
                 doc_texts.append(doc.page_content)
                 original_indices.append(idx)
-            elif isinstance(doc, dict) and doc.get('page_content'):
-                doc_texts.append(doc['page_content'])
-                original_indices.append(idx)
+            elif isinstance(doc, dict):
+                page_content = doc.get('page_content')
+                if isinstance(page_content, str) and page_content.strip():
+                    doc_texts.append(page_content)
+                    original_indices.append(idx)
🤖 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/rerank_cohere/IInstance.py` around lines 53 - 80, The guards
currently treat whitespace-only strings as valid; trim query and document
strings before checking emptiness. After extracting first_question.text (or
dict.get('text')) set query_text = query_text.strip() and then validate (raise
ValueError if empty) instead of checking raw string; likewise, when iterating
question.documents in the loop that builds doc_texts and original_indices, trim
each candidate (from doc.page_content or doc['page_content']) and only append if
the trimmed string is non-empty (store the trimmed value), ensuring the final
doc_texts check uses trimmed content. Use the existing variables query_text,
doc_texts, original_indices, and the question.questions/question.documents
accessors to locate the fixes.
nodes/src/nodes/rerank_cohere/rerank_client.py (1)

96-99: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject bool values for top_n and min_score.

bool still passes these numeric isinstance(...) checks, so top_n=True is accepted as 1 and min_score=False is accepted as 0.0 instead of being rejected as invalid input.

Proposed fix
-        if not isinstance(self._top_n, int) or self._top_n < 1:
+        if isinstance(self._top_n, bool) or not isinstance(self._top_n, int) or self._top_n < 1:
             raise ValueError(f'top_n must be an integer >= 1, got {self._top_n}')
-        if not isinstance(self._min_score, (int, float)) or not (0.0 <= self._min_score <= 1.0):
+        if (
+            isinstance(self._min_score, bool)
+            or not isinstance(self._min_score, (int, float))
+            or not (0.0 <= self._min_score <= 1.0)
+        ):
             raise ValueError(f'min_score must be a number between 0.0 and 1.0, got {self._min_score}')
@@
-        if not isinstance(effective_top_n, int) or effective_top_n < 1:
+        if isinstance(effective_top_n, bool) or not isinstance(effective_top_n, int) or effective_top_n < 1:
             raise ValueError(f'top_n must be an integer >= 1, got {effective_top_n}')
@@
-        if not isinstance(effective_min_score, (int, float)) or not (0.0 <= effective_min_score <= 1.0):
+        if (
+            isinstance(effective_min_score, bool)
+            or not isinstance(effective_min_score, (int, float))
+            or not (0.0 <= effective_min_score <= 1.0)
+        ):
             raise ValueError(f'min_score must be a number between 0.0 and 1.0, got {effective_min_score}')

Also applies to: 135-136, 188-189

🤖 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/rerank_cohere/rerank_client.py` around lines 96 - 99, The
current validation for self._top_n and self._min_score incorrectly accepts bools
because isinstance(True, int) and isinstance(False, int) return True; update the
checks in the rerank_cohere client where self._top_n and self._min_score are
validated (including the other occurrences around the blocks referencing
self._top_n and self._min_score) to explicitly reject bool by adding a condition
like "and not isinstance(value, bool)" (i.e., require int for top_n and
int/float for min_score while ensuring the value is not a bool) and keep the
existing range checks and error messages.
nodes/src/nodes/rerank_cohere/IGlobal.py (1)

57-66: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Harden validateConfig before calling .strip().

This still accepts whitespace-only API keys, and a non-string model falls through to the broad except with a generic attribute error instead of the intended user-facing warning. That makes config validation less useful and pushes the real failure to beginGlobal().

Proposed fix
-            apikey = config.get('apikey')
+            apikey = config.get('apikey', '')
             model = config.get('model', 'rerank-v3.5')

-            if not apikey:
+            if not isinstance(apikey, str) or not apikey.strip():
                 warning('Cohere API key is required')
                 return

-            if not model or not model.strip():
+            if not isinstance(model, str) or not model.strip():
                 warning('Cohere model name must not be empty')
                 return
🤖 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/rerank_cohere/IGlobal.py` around lines 57 - 66, The config
validation currently calls .strip() without ensuring types and accepts
whitespace-only keys; update the validateConfig logic (the block using
Config.getNodeConfig(self.glb.logicalType, self.glb.connConfig)) to explicitly
check that apikey is a non-empty string with non-whitespace characters (e.g.,
ensure isinstance(apikey, str) and apikey.strip()) and that model is a string
before calling model.strip(); if either check fails, call warning('Cohere API
key is required') or warning('Cohere model name must not be empty') respectively
and return so errors don't bubble to beginGlobal().
🤖 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 `@nodes/src/nodes/rerank_cohere/services.json`:
- Around line 97-99: The JSON defaults and profile names still reference
"rerank-v3.5" but the PR objective requires "rerank-english-v3.0"; update the
"default" value and the corresponding profile keys/objects to use
"rerank-english-v3.0" (replace occurrences of "rerank-v3.5" with
"rerank-english-v3.0" in the "default" property and the "profiles" entries in
nodes/src/nodes/rerank_cohere/services.json, including the other repeated
occurrences mentioned).

---

Duplicate comments:
In `@nodes/src/nodes/rerank_cohere/IGlobal.py`:
- Around line 57-66: The config validation currently calls .strip() without
ensuring types and accepts whitespace-only keys; update the validateConfig logic
(the block using Config.getNodeConfig(self.glb.logicalType,
self.glb.connConfig)) to explicitly check that apikey is a non-empty string with
non-whitespace characters (e.g., ensure isinstance(apikey, str) and
apikey.strip()) and that model is a string before calling model.strip(); if
either check fails, call warning('Cohere API key is required') or
warning('Cohere model name must not be empty') respectively and return so errors
don't bubble to beginGlobal().

In `@nodes/src/nodes/rerank_cohere/IInstance.py`:
- Around line 53-80: The guards currently treat whitespace-only strings as
valid; trim query and document strings before checking emptiness. After
extracting first_question.text (or dict.get('text')) set query_text =
query_text.strip() and then validate (raise ValueError if empty) instead of
checking raw string; likewise, when iterating question.documents in the loop
that builds doc_texts and original_indices, trim each candidate (from
doc.page_content or doc['page_content']) and only append if the trimmed string
is non-empty (store the trimmed value), ensuring the final doc_texts check uses
trimmed content. Use the existing variables query_text, doc_texts,
original_indices, and the question.questions/question.documents accessors to
locate the fixes.

In `@nodes/src/nodes/rerank_cohere/rerank_client.py`:
- Around line 96-99: The current validation for self._top_n and self._min_score
incorrectly accepts bools because isinstance(True, int) and isinstance(False,
int) return True; update the checks in the rerank_cohere client where
self._top_n and self._min_score are validated (including the other occurrences
around the blocks referencing self._top_n and self._min_score) to explicitly
reject bool by adding a condition like "and not isinstance(value, bool)" (i.e.,
require int for top_n and int/float for min_score while ensuring the value is
not a bool) and keep the existing range checks and error messages.
🪄 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: 8ce0e732-1aa9-4077-9a26-8771865b323b

📥 Commits

Reviewing files that changed from the base of the PR and between 475c9b5 and e5a1e90.

⛔ Files ignored due to path filters (1)
  • packages/shared-ui/src/assets/nodes/cohere.svg is excluded by !**/*.svg
📒 Files selected for processing (11)
  • nodes/src/nodes/rerank_cohere/IGlobal.py
  • nodes/src/nodes/rerank_cohere/IInstance.py
  • nodes/src/nodes/rerank_cohere/__init__.py
  • nodes/src/nodes/rerank_cohere/requirements.txt
  • nodes/src/nodes/rerank_cohere/rerank_client.py
  • nodes/src/nodes/rerank_cohere/services.json
  • nodes/test/framework/pipeline.py
  • nodes/test/mocks/cohere/__init__.py
  • nodes/test/mocks/cohere/errors.py
  • packages/shared-ui/src/components/canvas/util/get-icon-path.ts
  • test/nodes/test_rerank_cohere.py

Comment thread nodes/src/nodes/rerank_cohere/services.json Outdated
@nihalnihalani

Copy link
Copy Markdown
Collaborator Author

Heads-up on CI: the Ruff check is currently red on this PR because three pre-existing files on develop (nodes/src/nodes/llm_vision_gemini/IGlobal.py, nodes/src/nodes/llm_vision_gemini/gemini_vision.py, nodes/src/nodes/llm_vision_openai/openai_vision.py) fail ruff format --check. The format drift surfaced after PR #723's cleanup merged on 2026-05-04 and currently affects every open PR that branches from develop — develop's own CI run on sha 5362aad1 is in conclusion: failure for the same reason.

I've opened #900 (chore(format): apply ruff format to llm_vision_* files) as a focused 3-file mechanical ruff format pass to clear this. Once #900 merges, I'll rebase this branch onto fresh develop and Ruff will go green.

This PR's code-level CI is otherwise fully green: Build / Ubuntu, Build / Windows, Build / macOS, and gitleaks all pass.

Mirrors rocketride-org#900. These three pre-existing files on develop drifted out of
`ruff format --check` after PR rocketride-org#723's cleanup; including the fix here so
this PR's CI `ruff-check` (and the `CI OK` gatekeeper that depends on
it) goes green without waiting for rocketride-org#900 to merge first.

Pure mechanical `ruff format` output — line-break reformatting only,
no semantic changes. If rocketride-org#900 merges first, this commit becomes a no-op
on rebase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
nihalnihalani added a commit to nihalnihalani/rocketride-server that referenced this pull request May 15, 2026
Prior run failed on `from rocketlib import normalize_tool_input` —
the symbol IS exported from rocketlib/__init__.py line 63/141 on this
branch (identical to develop). Same import passes on companion PRs
rocketride-org#897/rocketride-org#898/rocketride-org#899/rocketride-org#900 with the same merge of develop, so this is a
stale build-cache flake.

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

nihalnihalani commented May 16, 2026 •

Copy link
Copy Markdown
Collaborator Author

Could you please review the new commit?

@stepmikhaylov stepmikhaylov 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.

Change Requests

  • CR-1 · Declare experimental capability — services.json:22
    "capabilities": [] must be "capabilities": ["experimental"]. All new nodes require this flag before production exposure. See guardrails, tool_exa_search, embedding_video as reference.

  • CR-2 · Test file is in the wrong directory — test/nodes/test_rerank_cohere.py
    The project convention is nodes/test/<node-name>/test_all.py (see nodes/test/guardrails/, nodes/test/tool_git/). The current path is outside the builder nodes:test discovery tree, so the tests never run in CI. Move to nodes/test/rerank_cohere/test_all.py.

  • CR-3 · sys.path manipulation in tests — test_rerank_cohere.py:44–46
    sys.path.insert(0, str(NODES_SRC)) is prohibited — all import paths are configured by the build interpreter. Remove the manual path injection entirely.

  • CR-4 · Built-in modules replaced in sys.modules — test_rerank_cohere.py:194–201
    rocketlib, ai, ai.common, ai.common.schema, ai.common.config, and depends are all injected as MagicMock() objects. These are provided by the built-in interpreter and must not be mocked. Import the real modules; restrict mocking to external SDK boundaries only (i.e., the cohere SDK client).

  • CR-5 · Inline Cohere mock duplicates nodes/test/mocks/cohere/ — test_rerank_cohere.py:168–201
    The test file defines its own _MockUnauthorizedError, _MockBadRequestError, etc. and injects them directly into sys.modules, bypassing the project's canonical mock already at nodes/test/mocks/cohere/. Remove the inline mock; use nodes/test/mocks/cohere/ as the single source of truth.

  • CR-6 · Missing requires in services.json test block — services.json:194
    Without "requires": ["ROCKETRIDE_COHERE_KEY"], running builder nodes:test-full without mocks will silently attempt a live API call instead of emitting a clear skip. Compare with llm_openai which declares "requires": ["ROCKETRIDE_OPENAI_KEY"].

  • CR-7 · top_n integer type check rejects valid JSON values — rerank_client.py:96, rerank_client.py:135
    isinstance(top_n, int) fails for 5.0 (float), which is a valid representation of an integer in JSON and common in pipeline config round-trips. Replace with a numeric check followed by int() coercion: isinstance(top_n, (int, float)) and top_n >= 1.

@stepmikhaylov

Copy link
Copy Markdown
Collaborator

Hi @nihalnihalani

Thank you for your efforts on the Cohere Rerank node — solid implementation with a nice regression test for the metadata alignment edge case. Could you please address the change requests above before merging?

nihalnihalani and others added 2 commits May 19, 2026 17:40
…v3.0

- services.json: mark capabilities=["experimental"] (stepmikhaylov CR-1)
- services.json: default preset rerank-english-v3.0 (CodeRabbit nit)
- services.json: add requires=["ROCKETRIDE_RERANK_COHERE_KEY"] on test
  block so missing keys skip dynamic tests cleanly (stepmikhaylov CR-6)
- rerank_client.py: accept int|float for top_n (excluding bool) and
  coerce JSON 5.0 -> 5; same bool-exclusion for min_score; centralized
  in _coerce_top_n / _validate_min_score helpers (stepmikhaylov CR-7 +
  CodeRabbit non-bool int check)
- IGlobal.py: validateConfig rejects non-str/whitespace-only apikey and
  model before .strip() (CodeRabbit)
- IInstance.py: trim query/document strings before emptiness checks so
  whitespace-only payloads are rejected (CodeRabbit)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per repo convention for node-specific unit tests. Stops mocking
engine-level modules (rocketlib, ai.*, depends) and drops the
sys.path.insert hack — IGlobal/IInstance are imported through the
build interpreter that already provides them, matching guardrails
and astra_db.

The external cohere SDK is the only dependency mocked, sourced from
the canonical nodes/test/mocks/cohere/ package. The mock is loaded
via a scoped sys.path so other tests are not contaminated.

Additional coverage:
- top_n: accepts JSON float 5.0 (coerced to int), rejects 2.5 and bool
- min_score: rejects bool (subclass of int) at init and override
- validateConfig: rejects non-string apikey/model before .strip()
- IInstance: whitespace-only query and whitespace-only page_content
  raise ValueError

Addresses stepmikhaylov CR-2..CR-5 and CodeRabbit nit re patch.dict.

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

nihalnihalani commented May 19, 2026 •

Copy link
Copy Markdown
Collaborator Author

Update — addressed @stepmikhaylov + CodeRabbit feedback (pushed e3b912f)

stepmikhaylov CRs:

  • ✅ CR-1 capabilities: ["experimental"]
  • ✅ CR-2 Test relocated to nodes/test/rerank_cohere/test_all.py; old path deleted
  • ✅ CR-3 Removed sys.path.insert(0, str(NODES_SRC)); modules loaded via importlib.util.spec_from_file_location under a private namespace
  • ✅ CR-4 Stopped mocking rocketlib/ai.*/depends; only cohere is now mocked
  • ✅ CR-5 Inline Cohere mock removed; tests use canonical nodes/test/mocks/cohere/
  • ✅ CR-6 Added "requires": ["ROCKETRIDE_RERANK_COHERE_KEY"] to the test block (verified env-var naming against nodes/test/framework/pipeline.py:_parse_credential_env_var)
  • ✅ CR-7 top_n accepts int or float (excluding bool via bool-first isinstance check); whole-number floats coerced; fractional rejected. Centralized in _coerce_top_n / _validate_min_score and applied at init + in rerank() / rerank_with_threshold() overrides.

CodeRabbit nits:

  • ✅ validateConfig rejects non-str and whitespace-only apikey/model (isinstance(v, str) before .strip())
  • ✅ bool exclusion applied to min_score too
  • ✅ Moot: patch.dict scoping became unnecessary after the test rewrite
  • ✅ Trim query/doc strings before emptiness check
  • ⚠️ Pushback on // comments in services.json — these are project convention. nodes/test/framework/discovery.py:102 _remove_json_comments strips them before json.loads, and 57 of 75 services.json files in the repo use them. Removing would diverge from every other node.
  • ✅ Defaults changed to rerank-english-v3.0 (preconfig.default, model field default, profile selector, test profile, IGlobal fallback, RerankClient.init fallback). rerank-v3.5 remains as an available profile.

Local pytest: 30 pure tests pass; 21 IGlobal/IInstance tests need the build interpreter (builder nodes:test) — same pattern as nodes/test/astra_db/. ruff clean. Devil's-advocate review of pushbacks ran.

Re-requesting @stepmikhaylov, @jmaionchi, @Rod-Christensen.

@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

♻️ Duplicate comments (1)
nodes/src/nodes/rerank_cohere/services.json (1)

2-236: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove // comments to comply with strict JSON parsing.

The file contains JavaScript-style line comments (//) throughout, causing Biome parse failures. This issue was previously flagged and remains unresolved. Either remove all comment lines or move documentation to external files if the parser requires strict JSON.

🤖 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/rerank_cohere/services.json` around lines 2 - 236, The file
contains JavaScript-style line comments (//) throughout which breaks strict JSON
parsing; remove all comment lines or convert them into valid JSON fields (e.g.,
add "_comment" properties) or move explanatory text out to docs so the JSON
objects like "title", "protocol", "preconfig", "fields", and "shape" contain
only valid JSON members—ensure every occurrence of leading // before description
blocks and inline comments is deleted so
nodes/src/nodes/rerank_cohere/services.json becomes strictly valid JSON.
🤖 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 `@nodes/src/nodes/rerank_cohere/rerank_client.py`:
- Around line 154-168: The rerank wrapper currently only checks truthiness and
can pass invalid runtime inputs to self._client.rerank; update rerank() to
validate inputs more strictly by: ensure query is a non-empty string (e.g.,
isinstance(query, str) and query.strip()), ensure model if provided is a
non-empty string (model is None or isinstance(model, str) and model.strip()),
ensure documents is a non-empty list/iterable and that every item is a non-empty
string after stripping (reject numbers, empty/whitespace-only strings, or
non-string items), keep using _coerce_top_n/self._top_n for top_n, and raise
ValueError with clear messages before calling self._client.rerank if any check
fails.

In `@nodes/src/nodes/rerank_cohere/services.json`:
- Around line 166-179: The conditional "value" strings in the conditional array
do not match the enum/profile titles used by the dropdown, so update the three
conditional entries under conditional (the ones referencing properties like
"rerank_cohere.rerank-v3.5", "rerank_cohere.rerank-v3.0",
"rerank_cohere.custom") to use the profile titles instead of keys — change
"rerank-v3.5" → "Rerank v3.5", "rerank-english-v3.0" → "Rerank v3.0", and
"custom" → "Custom" so the condition values match the enum on line 165 and the
UI will correctly show the related properties.

---

Duplicate comments:
In `@nodes/src/nodes/rerank_cohere/services.json`:
- Around line 2-236: The file contains JavaScript-style line comments (//)
throughout which breaks strict JSON parsing; remove all comment lines or convert
them into valid JSON fields (e.g., add "_comment" properties) or move
explanatory text out to docs so the JSON objects like "title", "protocol",
"preconfig", "fields", and "shape" contain only valid JSON members—ensure every
occurrence of leading // before description blocks and inline comments is
deleted so nodes/src/nodes/rerank_cohere/services.json becomes strictly valid
JSON.
🪄 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: 3d02304b-2c68-4195-8fea-05e421b7388a

📥 Commits

Reviewing files that changed from the base of the PR and between 22bf71e and e3b912f.

📒 Files selected for processing (6)
  • nodes/src/nodes/rerank_cohere/IGlobal.py
  • nodes/src/nodes/rerank_cohere/IInstance.py
  • nodes/src/nodes/rerank_cohere/rerank_client.py
  • nodes/src/nodes/rerank_cohere/services.json
  • nodes/test/rerank_cohere/__init__.py
  • nodes/test/rerank_cohere/test_all.py

Comment thread nodes/src/nodes/rerank_cohere/rerank_client.py Outdated
Comment thread nodes/src/nodes/rerank_cohere/services.json
nihalnihalani added a commit to nihalnihalani/rocketride-server that referenced this pull request May 19, 2026
- Set capabilities to ["experimental"] consistent with sibling new nodes
  (PRs rocketride-org#897/rocketride-org#898).
- Add a "test" block per docs/README-node-testing.md so the dynamic test
  runner exercises the node through the real server. The "balanced"
  profile re-ranks three pre-scored documents and asserts the top result
  mentions "machine learning".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
nihalnihalani and others added 2 commits May 19, 2026 18:06
…tests

Three IInstance tests (test_write_questions_preserves_metadata,
test_write_questions_metadata_alignment_after_filter,
test_write_questions_does_not_mutate_original) constructed Doc objects
with metadata dicts missing the required chunkId (and in one case
objectId) fields. The build interpreter's real pydantic DocMetadata
schema requires both, so the tests failed with ValidationError under
builder nodes:test even though they passed locally with the cohere mock.

Also tighten the dict-equality assertion to attribute-level since
DocMetadata is a pydantic model, not a dict.

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

Reject non-string and whitespace-only query/documents/model values before
calling the Cohere SDK, so callers get a deterministic local ValueError
instead of RerankBadRequestError/RerankServerError from the network. This
keeps the wrapper's stable validation contract aligned with the existing
validateConfig hardening.

Addresses CodeRabbit review on PR rocketride-org#898.

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

Copy link
Copy Markdown
Collaborator Author

All 7 structural review comments from @stepmikhaylov and 6 of the 7 CodeRabbit nits have been addressed across 846a572 and e729f6c. This covers: test relocation under nodes/test/rerank_cohere/, capabilities: experimental on the node manifest, canonical cohere SDK mock surface, ROCKETRIDE_RERANK_COHERE_KEY env-var support, int|float top_n coercion that explicitly rejects bool, rerank-english-v3.0 as the default profile/model, and validateConfig type/whitespace hardening for apikey and model. The latest commit (e729f6c) tightens RerankClient.rerank() to reject non-string and whitespace-only query/documents/model before calling the Cohere SDK, so callers always get a deterministic local ValueError instead of a downstream RerankBadRequestError.

One CodeRabbit nit was respectfully pushed back on: the // comments in services.json are an intentional project convention — the repo ships a custom stripping parser and every other node manifest uses the same JSONC dialect, so converting only this file would break consistency.

@stepmikhaylov @Rod-Christensen @jmaionchi — would appreciate another look when you have a moment.

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

Labels

module:nodes Python pipeline nodes module:ui Chat UI and Dropper UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants