Skip to content

feat(codec): add OCI Generative AI provider support - #554

Open
fede-kamel wants to merge 16 commits into
NVIDIA:mainfrom
fede-kamel:feat/oci-codec-1-types-response
Open

feat(codec): add OCI Generative AI provider support#554
fede-kamel wants to merge 16 commits into
NVIDIA:mainfrom
fede-kamel:feat/oci-codec-1-types-response

Conversation

@fede-kamel

@fede-kamel fede-kamel commented Jul 24, 2026

Copy link
Copy Markdown

Overview

Adds Oracle Cloud Infrastructure (OCI) Generative AI as a built-in provider across the Rust core and the Python/Node bindings. Originally opened as layer 1 of a four-part stacked series; per reviewer request (comment), the remaining three layers are now folded into this single PR, so it carries the complete feature.

The four capability layers, now all in this PR (one commit each, reviewable in sequence):

  1. Typed variants + response codecApiSpecific OCI variants and LlmResponseCodec for GENERIC, COHERE, and COHEREV2 ChatResult payloads
  2. Request codecLlmCodec decode plus merge-not-replace encode with encode(decode(x), x) == x identity and unmodeled-field preservation
  3. Provider surface + streamingProviderSurface::OCIGenAI registration and detection, OCIGenAIStreamingCodec (SSE for both formats), and OCI awareness in guardrails, the PII-redaction overlay, and adaptive request surfaces
  4. BindingsOCIGenAIChatCodec exposed to Python (pyo3, .pyi, codecs.py) and Node (napi, .d.ts), with binding tests
  • I confirm this contribution is my own work, or I have the right to submit it under this project's license.
  • I searched existing issues and open pull requests, and this does not duplicate existing work.

Details

What: ApiSpecificRequest::OCIGenAI { compartment_id, serving_mode, api_format } and ApiSpecificResponse::OCIGenAI { api_format, model_version } (serde tag "oci_genai") in crates/types, plus crates/core/src/codec/oci_genai.rs introducing OCIGenAIChatCodec:

  • Response decode covers all three documented apiFormats — GENERIC (choices-based; Meta Llama, Google, xAI, OpenAI, and imported open-weights models such as NVIDIA Nemotron on dedicated AI clusters), COHERE (text-based), and COHEREV2 (single assistant message with typed content parts and nested-function tool calls) — normalizing model, response id, message content, tool calls, finish reasons, and usage counters (including promptTokensDetails.cachedTokens). Envelope and chat-response fields outside the normalized shape (timeCreated, serviceTier, chatHistory, grounding metadata) are preserved in extra.
  • Request decode/encode normalizes OCI ChatDetails for both formats and re-encodes by merging into the original payload rather than replacing it, so unmodeled fields survive intercept round-trips; the encode/decode identity is asserted in tests.
  • Provider surface: ProviderSurface::OCIGenAI with detection keyed on the strongest-signal envelope fields (placed first in the resolver), so OCI payloads resolve without an explicit codec. Streaming is handled by OCIGenAIStreamingCodec (SSE lifecycle for GENERIC and COHERE). Guardrails, the PII-redaction overlay, and adaptive request surfaces gain OCI-format awareness with parity cases mirroring the existing providers.
  • Bindings: OCIGenAIChatCodec classes in Python and Node with the same construction and resolver-integration semantics as the other built-in codecs.

The codec accepts the REST wire format only (camelCase, as documented); converting alternate renderings produced by Oracle tooling is the caller's responsibility, per review discussion.

Why: OCI GenAI calls are currently opaque to Relay. This PR provides normalized LLMEnd annotations (model, finish reason, token usage), safe request editing for intercepts (redaction, policy), automatic provider detection including streaming, and access from every primary binding.

How: Follows the built-in provider pattern (anthropic.rs as template): unit-struct codec, FinishReason mapping for the three formats' vocabularies (stop/length/max_tokens/tool_calls; COMPLETE/MAX_TOKENS; TOOL_CALL/STOP_SEQUENCE), positional call_{index} fallback ids for COHERE tool calls (no id on the wire). Binding classes mirror AnthropicMessagesCodec/OpenAIChatCodec exposure.

Testing: Full workspace suite green, cargo clippy --workspace --all-targets zero warnings, cargo fmt --check clean, missing_docs satisfied; just test-python (567 passed) and just test-node (294 passed) green on the built bindings. OCI-specific coverage includes: GENERIC/COHERE fixtures from live OCI wire captures (simple, tool-call, mixed-content, tool-call-only, model-family sweep across Meta, OpenAI, Google, xAI, Cohere), COHEREV2 fixtures per the published CohereChatResponseV2 schema with the wire shape confirmed against the live service, request round-trip identity and unmodeled-field preservation, wire-format-only contract tests, streaming lifecycle tests for both formats, resolver detection and parity cases, PII-redaction overlay coverage, invalid-content error paths, and binding tests in both languages. The combined content was also validated end-to-end: the codec-encoded request was posted verbatim to the signed OCI REST chat endpoint (dedicated AI cluster serving an imported NVIDIA Nemotron 3 model) and the live reply decoded with correct finish reason and token usage. Known unrelated flake: install_registrations_covers_registry_error_edges (dynamic worker plugin) is parallelism-sensitive on main; the worker code here is byte-identical to main and the test passes serially and in isolation.

Breaking changes: None — additive enum variants, a new codec module, a new provider surface, and new binding classes.

Where should the reviewer start?

crates/core/src/codec/oci_genai.rs top-to-bottom (module docs explain the three formats and the wire-only contract), then the fixtures and round-trip tests in crates/core/tests/unit/codec/oci_genai_tests.rs, then the resolver registration in crates/core/src/codec/resolve.rs. The four commits after the main merge apply the layers in order if commit-by-commit review is preferred.

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

Summary by CodeRabbit

  • New Features

    • Added OCI Generative AI codec support for GENERIC, COHERE, and COHEREV2 requests and responses.
    • Added streaming support for text, tool calls, usage, and model metadata.
    • Added OCI codec access through Python and Node.js APIs.
    • Added OCI-specific request and response metadata handling.
    • Added OCI support to PII redaction and NeMo Guardrails integrations.
  • Bug Fixes

    • Improved provider detection, response normalization, finish reasons, and tool-call decoding for OCI payloads.

@copy-pr-bot

copy-pr-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions github-actions Bot added size:L PR is large Feature a new feature lang:rust PR changes/introduces Rust code labels Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

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

Adds OCI GenAI request and response variants, codecs for GENERIC, COHERE, and COHEREV2 formats, provider registration, middleware integrations, language bindings, observability mapping, and validation coverage.

Changes

OCI GenAI provider support

Layer / File(s) Summary
OCI contracts and codec implementation
crates/types/src/codec/*.rs, crates/core/src/codec/*
Adds typed OCI metadata and implements request, response, and streaming codecs with content, tool-call, usage, finish-reason, envelope, and extra-field handling.
Provider resolution and observability
crates/core/src/codec/resolve.rs, crates/core/src/observability/otel_genai.rs, crates/adaptive/src/acg/request_surfaces/mod.rs
Registers OCI as a provider surface, prioritizes OCI detection, maps its observability name, and excludes it from adaptive request surfaces.
Guardrails and PII redaction integration
crates/core/src/plugins/nemo_guardrails/*, crates/pii-redaction/src/*, python/nemo_relay/pii_redaction.*
Adds OCI codec configuration, guardrails mappings, stream extraction, response overlays, tool-call sanitization, and finish-reason mappings.
Node and Python codec exposure
crates/node/*, crates/python/*, python/nemo_relay/*
Exposes OCIGenAIChatCodec through native bindings, type declarations, module exports, and redaction configuration types.
Codec, integration, and parity validation
crates/core/tests/*, crates/pii-redaction/tests/*, python/tests/*
Tests request and response normalization, encoding preservation, streaming assembly, provider detection, pipeline annotations, overlays, parity, and binding protocols.

Estimated code review effort: 5 (Critical) | ~100 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client request
  participant Resolver as Provider resolver
  participant Codec as OCIGenAIChatCodec
  participant Middleware as Guardrails or PII redaction
  participant Pipeline as LLM pipeline
  participant Binding as Python or Node binding
  Client->>Resolver: submit OCI payload
  Resolver->>Codec: select OCIGenAI provider surface
  Codec->>Middleware: normalize request or response
  Middleware->>Pipeline: apply policy and annotations
  Binding->>Codec: invoke decode, encode, or decode_response
  Codec-->>Binding: return normalized JSON or error
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The implementation covers the main OCI objectives, but the evidence does not show COHEREV2 streaming or the required supported-provider documentation. Add COHEREV2 SSE handling and tests, and update the supported-provider documentation before merging.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The changes support OCI provider integration across codecs, surfaces, middleware, observability, bindings, and tests described by issue #548.
Docstring Coverage ✅ Passed Docstring coverage is 92.35% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title follows Conventional Commits format, uses an allowed lowercase type and scope, gives a concise imperative summary, and is under 72 characters.
Description check ✅ Passed The description includes all required sections, explains the complete change, identifies review starting points, and links related issues with valid action keywords.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch feat/oci-codec-1-types-response
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@fede-kamel

Copy link
Copy Markdown
Author

How this series fits together

This PR is layer 1 of 4. Each layer adds one capability, each is independently buildable and testable, and each later branch is a pure superset of the previous — so review feedback here rebases cleanly through the rest of the stack.

Layer Capability it adds Contents
1 — this PR (+562) Relay can read OCI responses: normalized LLMEnd annotations (model, finish reason, usage) when the codec is supplied explicitly Typed ApiSpecific variants + LlmResponseCodec for GENERIC and COHERE ChatResult payloads
2 (+1,414) Relay can edit OCI requests safely: intercepts (redaction, policy) with encode(decode(x), x) == x identity and unmodeled-field preservation LlmCodec decode + merge-not-replace encode, request-side test suite
3 (+1,060) Relay auto-detects OCI: provider-surface registration, streaming, and awareness in guardrails, PII-redaction overlay, and adaptive components ProviderSurface::OCIGenAI, detection (strongest-signal envelope keys, placed first), OCIGenAIStreamingCodec, parity cases
4 (+202) Every language gets it pyo3 + napi classes, .pyi/codecs.py exposure, binding tests — closes #548

Branch links and the full plan of record are on #548. Lineage: #549 (Python approach, closed per review) → #552 (this same content as a single Rust PR, closed in favor of this reviewable split).

Verification provenance

Every branch in the stack passes the full local gate on its own: cargo build --workspace, complete workspace test suite (3,369 tests at this layer, growing to 3,403 by layer 4), cargo clippy --workspace --all-targets with zero warnings, cargo fmt --check, and missing_docs. The four layers recombine byte-identically to the #552 content, which was validated end-to-end: the codec-encoded request was posted verbatim to the signed OCI REST chat endpoint and answered correctly by a dedicated AI cluster serving an imported NVIDIA Nemotron 3 model, with the response codec normalizing the reply including token usage.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@crates/core/src/codec/oci_genai.rs`:
- Around line 124-162: Update flatten_all_text_parts to return None for an empty
parts slice, while preserving Text concatenation for non-empty arrays containing
only valid TEXT parts. This ensures decode_generic_content returns None for
content: [] and does not mark tool-call-only messages as containing text.

In `@crates/core/tests/unit/codec/oci_genai_tests.rs`:
- Around line 131-167: Add error-path coverage alongside the existing OCI GenAI
codec tests for GENERIC responses: create a test such as
test_invalid_generic_content_shape_errors that passes a numeric or object
message.content to OCIGenAIChatCodec.decode_response and asserts it returns an
error. Also cover an array containing a non-object part if practical, ensuring
decode_generic_content rejects each invalid shape with
FlowError::InvalidArgument.
🪄 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: Enterprise

Run ID: a9645a77-4c88-4c9e-8a1c-90f1492e291a

📥 Commits

Reviewing files that changed from the base of the PR and between 6e13cfd and ccb3281.

📒 Files selected for processing (5)
  • crates/core/src/codec/mod.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/types/src/codec/request.rs
  • crates/types/src/codec/response.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (17)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/src/codec/oci_genai.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/src/codec/oci_genai.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
{crates/core/src/plugin/dynamic/**,crates/plugin/**,crates/worker/**,crates/worker-proto/**,crates/types/**,python/plugin/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Keep the stable boundary explicit: native plugins cross a C ABI, and worker plugins cross grpc-v1.

Files:

  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
crates/types/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Runtime helpers must cover marks, scopes, continuations, and isolated scope stacks.

Files:

  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
🔇 Additional comments (8)
crates/types/src/codec/request.rs (1)

537-549: LGTM!

crates/types/src/codec/response.rs (1)

329-339: LGTM!

crates/core/src/codec/mod.rs (1)

19-19: LGTM!

crates/core/src/codec/oci_genai.rs (3)

197-254: LGTM!


262-329: LGTM!


90-101: LGTM!

Also applies to: 332-341

crates/core/tests/unit/codec/oci_genai_tests.rs (2)

16-129: LGTM!


169-188: LGTM!

Comment thread crates/core/src/codec/oci_genai.rs
Comment thread crates/core/tests/unit/codec/oci_genai_tests.rs
@fede-kamel
fede-kamel force-pushed the feat/oci-codec-1-types-response branch from ccb3281 to 21d6e18 Compare July 24, 2026 17:59
@fede-kamel

Copy link
Copy Markdown
Author

Both review findings addressed in the updated commit:

  1. Empty content: []None: decode_generic_content now returns Ok(None) for an empty part list, so tool-call-only messages no longer decode to Some(Text("")). The tool-call fixture test now asserts annotated.message == None explicitly.
  2. Error-path coverage: added test_invalid_generic_content_shape_errors exercising InvalidArgument for non-string/array/null content and non-object parts.

Full workspace build and test suite re-run green after the change; the fix is propagated through the staged follow-up branches of the series.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@crates/core/src/codec/oci_genai.rs`:
- Around line 313-333: The decode_response_tool_call path silently discards
malformed tool calls when the name is missing or non-string. Add an appropriate
debug or warning log before returning None for this validation failure,
including enough context to identify the malformed OCI payload while preserving
the existing successful decoding behavior.

In `@crates/core/tests/unit/codec/oci_genai_tests.rs`:
- Around line 1-210: Add a test near test_invalid_generic_content_shape_errors
that decodes a GENERIC response with mixed TEXT and non-TEXT content through
OCIGenAIChatCodec::decode_response. Assert the message is MessageContent::Parts,
contains both parts, and specifically verify the non-TEXT part is represented as
ContentPart::ProviderNative with its original payload preserved.
🪄 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: Enterprise

Run ID: 09237b3d-efcb-4116-9173-2272097f9a8c

📥 Commits

Reviewing files that changed from the base of the PR and between ccb3281 and 21d6e18.

📒 Files selected for processing (5)
  • crates/core/src/codec/mod.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/types/src/codec/request.rs
  • crates/types/src/codec/response.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (17)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/src/codec/oci_genai.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/src/codec/oci_genai.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/core/src/plugin/dynamic/**,crates/plugin/**,crates/worker/**,crates/worker-proto/**,crates/types/**,python/plugin/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Keep the stable boundary explicit: native plugins cross a C ABI, and worker plugins cross grpc-v1.

Files:

  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
crates/types/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Runtime helpers must cover marks, scopes, continuations, and isolated scope stacks.

Files:

  • crates/types/src/codec/response.rs
  • crates/types/src/codec/request.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
🔇 Additional comments (4)
crates/types/src/codec/request.rs (1)

537-549: LGTM!

crates/types/src/codec/response.rs (1)

329-339: LGTM!

crates/core/src/codec/oci_genai.rs (1)

1-113: LGTM!

Also applies to: 201-258, 266-345

crates/core/src/codec/mod.rs (1)

19-19: LGTM!

Comment thread crates/core/tests/unit/codec/oci_genai_tests.rs
@fede-kamel

Copy link
Copy Markdown
Author

End-to-end validation re-run on the updated series content (including the empty-content fix above), via the Python binding built from the stack tip:

  1. decode normalized a live OCI ChatDetails request (dedicated serving mode)
  2. encode(decode(request), request) verified byte-identical to the original payload
  3. the codec-encoded request was posted verbatim to the signed OCI Generative AI REST endpoint (us-chicago-1) and answered correctly by a dedicated AI cluster serving an imported NVIDIA Nemotron 3 model
  4. decode_response normalized the live reply, including finish reason and token usage (prompt_tokens: 28, completion_tokens: 109, total_tokens: 137)

Unit suite on the same tip: 33 OCI codec tests green, full workspace build and tests passing, clippy zero warnings.

Introduce the first layer of the OCI Generative AI provider codec:
typed ApiSpecificRequest::OCIGenAI and ApiSpecificResponse::OCIGenAI
variants, plus an OCIGenAIChatCodec implementing LlmResponseCodec.
Response decode covers ChatResult ({modelId, chatResponse}) and bare
chat responses in both apiFormat variants (GENERIC choices-based and
COHERE text-based), tolerates camelCase, kebab-case, and snake_case
key conventions (SDK vs CLI shapes), parses GENERIC string-encoded
tool-call arguments, maps promptTokens/completionTokens/totalTokens
into Usage, and maps finish reasons (stop/COMPLETE -> complete,
length/MAX_TOKENS -> length, tool_calls -> tool_use, else unknown).

Request encode/decode, streaming, and provider-surface registration
follow in subsequent changes.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@fede-kamel
fede-kamel force-pushed the feat/oci-codec-1-types-response branch from 21d6e18 to 1b8a31f Compare July 24, 2026 20:26
@fede-kamel

Copy link
Copy Markdown
Author

Second CodeRabbit pass triaged:

  • Empty content: []None and error-path coverage — already fixed in the previous push; the re-posted comments anchor to the superseded diff. Current code returns Ok(None) for empty part lists (asserted in the tool-call test) and test_invalid_generic_content_shape_errors covers the InvalidArgument paths.
  • Missing ProviderNative fallback coverage — valid gap; added test_non_text_parts_preserved_as_provider_native (TEXT + IMAGE part list decodes to typed parts with the image preserved losslessly as a ProviderNative component).
  • Malformed tool calls silently dropped — declining intentionally: this mirrors the established built-in pattern (the Anthropic codec's tool-call decode uses the same filter_map drop semantics), and diverging here would make OCI behave inconsistently with the other providers. Happy to revisit codec-wide in a follow-up if maintainers prefer logging.

Suite on the updated tip: 34 OCI codec tests green, clippy zero warnings, full stack rebased.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@crates/core/src/codec/oci_genai.rs`:
- Around line 313-333: The decode_response_tool_call function currently assigns
an empty string when the OCI response omits id, causing parallel Cohere tool
calls to collide. Replace the unwrap_or_default fallback with a stable, unique
per-call identifier derived from the available tool-call data, while preserving
the provided id unchanged when present.
🪄 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: Enterprise

Run ID: bb97e075-db42-4f75-be55-471c3f8be155

📥 Commits

Reviewing files that changed from the base of the PR and between 21d6e18 and 1b8a31f.

📒 Files selected for processing (5)
  • crates/core/src/codec/mod.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/types/src/codec/request.rs
  • crates/types/src/codec/response.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (17)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/types/src/codec/request.rs
  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/types/src/codec/request.rs
  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/types/src/codec/request.rs
  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/types/src/codec/request.rs
  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/types/src/codec/request.rs
  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/core/src/plugin/dynamic/**,crates/plugin/**,crates/worker/**,crates/worker-proto/**,crates/types/**,python/plugin/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Keep the stable boundary explicit: native plugins cross a C ABI, and worker plugins cross grpc-v1.

Files:

  • crates/types/src/codec/request.rs
  • crates/types/src/codec/response.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/types/src/codec/request.rs
  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/core/src/codec/oci_genai.rs
crates/types/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Runtime helpers must cover marks, scopes, continuations, and isolated scope stacks.

Files:

  • crates/types/src/codec/request.rs
  • crates/types/src/codec/response.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/types/src/codec/request.rs
  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/types/src/codec/request.rs
  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/types/src/codec/request.rs
  • crates/core/src/codec/mod.rs
  • crates/types/src/codec/response.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/src/codec/mod.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
🔇 Additional comments (5)
crates/types/src/codec/request.rs (1)

537-549: LGTM!

crates/types/src/codec/response.rs (1)

329-339: LGTM!

crates/core/src/codec/oci_genai.rs (1)

1-354: Rest of the file (key-convention helpers, GENERIC content decoding, envelope/chat_response split, usage mapping, finish-reason mapping) is correct and the previously-flagged empty-content and error-path issues are now addressed.

crates/core/src/codec/mod.rs (1)

19-19: LGTM!

crates/core/tests/unit/codec/oci_genai_tests.rs (1)

1-247: LGTM! Both previously-flagged gaps (invalid-content error path, mixed-parts ProviderNative coverage) are now covered.

Comment thread crates/core/src/codec/oci_genai.rs Outdated
@willkill07

willkill07 commented Jul 27, 2026

Copy link
Copy Markdown
Member

@fede-kamel A few questions:

  1. Is Cohere adhering to v1 or v2:

If Oracle strictly adheres to Cohere, then I'd expect separate CohereV1 and CohereV2 Codecs rather than an Oracle-specific codec.

  1. I don't see any streaming codec support -- is this intentional?

  2. Where is the API definition/spec for Generic? Is it just using standard OpenAI? If so, why do we need a separate codec?

  3. A question regarding:

Key lookup tolerates the three conventions OCI emits (SDK camelCase, CLI kebab-case, snake_case).

This seems generally problematic to me. Schema specifications should be concrete and well-defined. If Oracle is extending the Cohere schema to be case/format insensitive, I really need good justification as to why a vendor-specific schema should live in Relay itself. Cohere's API is clearly defined with snake_case semantics. Why would that not suffice?

Validated the codec against live OCI Generative AI responses across
model families (Meta, OpenAI, Google, xAI, Cohere) and the three
renderings Oracle tooling emits for the documented camelCase schema.
Changes from that sweep:

- Unwrap the OCI CLI data envelope so captured CLI output decodes like
  wire and SDK payloads; document the transport matrix in the module
  doc (REST wire/SDKs camelCase, oci.util.to_dict() snake_case, CLI
  kebab-case in a data envelope)
- Map the lowercase max_tokens finish reason emitted by Gemini models
  to FinishReason::Length
- Map promptTokensDetails.cachedTokens (OpenAI/xAI models) to
  cache_read_tokens instead of dropping it
- Synthesize positional call_{index} ids for COHERE tool calls, which
  carry no id on the wire, so parallel calls stay distinguishable
- Add unit tests for each live-observed shape and a pipeline
  integration test decoding an OCI GENERIC tool-call response through
  llm_call_execute

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@fede-kamel

Copy link
Copy Markdown
Author

Thanks for the careful review @willkill07 — these are exactly the right questions. Rather than answer from the docs alone, I captured live responses from the service (us-chicago-1, one model from each family: Meta, OpenAI, Google, xAI, Cohere) and checked every claim below against them.

1. Cohere v1 or v2?

Neither, and that's the crux: Oracle does not strictly adhere to Cohere. The COHERE format borrows Cohere v1 semantics (single top-level text, chatHistory, uppercase finish reasons like COMPLETE/MAX_TOKENS), but the wire schema is Oracle's own CohereChatResponse: camelCase keys inside the ChatResult/chatResponse envelope, and none of Cohere's identifying fields (generation_id, response_id). Live capture from cohere.command-r-08-2024:

{
  "modelId": "cohere.command-r-08-2024",
  "modelVersion": "2.0",
  "chatResponse": {
    "apiFormat": "COHERE",
    "text": "...",
    "chatHistory": [{"role": "USER", "message": "..."}, {"role": "CHATBOT", "message": "..."}],
    "finishReason": "COMPLETE",
    "toolCalls": [{"name": "get_weather", "parameters": {"city": "Paris"}}],
    "usage": {"promptTokens": 29, "completionTokens": 37, "totalTokens": 66}
  }
}

A native CohereV1 codec (snake_case, no envelope) would decode none of this. If Relay later adds Cohere's own API as a provider surface, CohereV1/V2 codecs make sense there — but they'd be decoding a different wire format than this one. I also checked the newest Cohere model on OCI (cohere.command-a-03-2025): same v1-style Oracle schema, so no v2 drift so far.

2. Streaming

Intentional for this layer — streaming is layer 3 of the series (OCIGenAIStreamingCodec, SSE for both formats); the stack table in my first comment has the breakdown. I kept this PR to typed variants + response decode so each layer stays reviewable.

3. GENERIC spec

It's Oracle's own documented schema: GenericChatResponse. OpenAI-inspired, not OpenAI wire-compatible. From the live captures:

  • envelope: {modelId, modelVersion, chatResponse: {apiFormat, timeCreated, choices, usage}} — no id/object/created
  • uppercase enums: role: "ASSISTANT", content type: "TEXT", tool call type: "FUNCTION"
  • tool calls are flat {type, id, name, arguments} rather than OpenAI's nested function: {name, arguments} — our own OpenAIChatCodec rejects exactly this shape, since it requires the nested function object
  • usage is promptTokens/completionTokens/totalTokens

So routing GENERIC through the OpenAI codec isn't an option; it hard-errors on the tool-call shape and misses the envelope and usage fields. I also verified GENERIC is what every non-Cohere family returns on OCI (openai.*, google.*, xai.*, meta.*) — there is no hidden third format.

4. Key conventions

Agreed that a case/format-insensitive schema would be a smell, and the module doc described this poorly — I've reworded it in the latest push. To state it precisely: Oracle's schema is concrete, and the REST wire is strictly camelCase, always. The three spellings exist because Oracle's own tooling re-keys the same schema deterministically at different capture points:

Capture point Rendering
REST wire, Java/Go/TS SDKs camelCase (finishReason)
Python SDK oci.util.to_dict(response.data) snake_case (finish_reason)
OCI CLI output kebab-case in a data envelope (finish-reason)

I verified all three by running the same call through each path; the test fixtures now mirror those captures. The lookup is not fuzzy matching — the codec derives the kebab/snake spelling mechanically from the documented camelCase key, so there's exactly one accepted rendering per convention and no ambiguity. The snake_case path matters most in practice: Python is Relay's biggest binding, and to_dict() output is the payload shape instrumented OCI SDK apps actually hand over.

That said, if you'd rather the built-in codec accept only the wire format and leave CLI/SDK-dict renderings to the caller, I'm happy to trim to camelCase-only — small diff, and I'd rather match the project's conventions than defend a preference.


The live sweep also surfaced a few real-world shapes the first cut missed, all fixed with tests in the latest commit: Gemini models emit a lowercase max_tokens finish reason, OpenAI/xAI models report cache hits under promptTokensDetails.cachedTokens, the CLI's data envelope needed unwrapping, and Cohere tool calls carry no id on the wire so parallel calls now get positional synthesized ids. Also added a pipeline integration test decoding a live-shaped GENERIC tool-call response through llm_call_execute, matching the existing Anthropic/OpenAI coverage.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
crates/core/src/codec/oci_genai.rs (1)

245-249: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Handle COHEREV2 as a Cohere response
COHEREV2 also carries its generated text in message, but this branch sends it to the generic parser, which expects choices and drops valid V2 responses. Add an explicit COHEREV2 case here, or reject unsupported formats instead of treating them as generic.

🤖 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 `@crates/core/src/codec/oci_genai.rs` around lines 245 - 249, Update the
api_format branch in the response decoding flow to route both "COHERE" and
"COHEREV2" through decode_cohere_response_body; ensure COHEREV2 no longer
reaches decode_generic_response_body, which expects choices.
🤖 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 `@crates/core/tests/integration/pipeline_tests.rs`:
- Around line 1442-1515: Make subscriber cleanup in the OCI response codec test
panic-safe by introducing an RAII guard immediately after registering the
subscriber, using its drop behavior to call deregister_subscriber for
"oci_resp_codec_sub". Remove the explicit cleanup dependency at the end of the
test while preserving deregistration on successful completion and during
assertion unwinding.

---

Outside diff comments:
In `@crates/core/src/codec/oci_genai.rs`:
- Around line 245-249: Update the api_format branch in the response decoding
flow to route both "COHERE" and "COHEREV2" through decode_cohere_response_body;
ensure COHEREV2 no longer reaches decode_generic_response_body, which expects
choices.
🪄 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: Enterprise

Run ID: d86502fa-3656-483c-99da-0672bf8768be

📥 Commits

Reviewing files that changed from the base of the PR and between 1b8a31f and ad9f167.

📒 Files selected for processing (3)
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (15)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/codec/oci_genai.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/core/src/codec/oci_genai.rs
🔇 Additional comments (4)
crates/core/src/codec/oci_genai.rs (2)

13-18: 📐 Maintainability & Code Quality

Record the required core-runtime validation matrix.

Please attach CI evidence for cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, just test-rust, cargo deny check, validate-change, the Rust/Python/Go/Node matrix, and uv run pre-commit run --all-files.

As per coding guidelines, changed crates/core code requires these Rust checks, validate-change, dependency validation, and the full language matrix.

Source: Coding guidelines


219-224: LGTM!

Also applies to: 297-371

crates/core/tests/unit/codec/oci_genai_tests.rs (1)

131-206: LGTM!

Also applies to: 305-358, 368-369

crates/core/tests/integration/pipeline_tests.rs (1)

33-33: LGTM!

Comment thread crates/core/tests/integration/pipeline_tests.rs
@willkill07

Copy link
Copy Markdown
Member

That said, if you'd rather the built-in codec accept only the wire format and leave CLI/SDK-dict renderings to the caller

Relay only interfaces with the wire format, so yes.

Per review, Relay only interfaces with the wire format, so the codec
now accepts the documented camelCase schema only. Removes the
kebab-case/snake_case key derivation and the CLI data-envelope
unwrapping; converting alternate renderings produced by Oracle tooling
is the caller's responsibility. Adds a contract test asserting
non-wire renderings are not decoded.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@fede-kamel

Copy link
Copy Markdown
Author

Done in 7282cdd — the codec now accepts the camelCase wire format only. Dropped the kebab/snake key derivation and the CLI data-envelope handling (~130 lines removed), documented that converting alternate tooling renderings is the caller's job, and added a contract test asserting non-wire renderings decode to nothing. Full workspace suite, clippy, and fmt all green.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@crates/core/tests/unit/codec/oci_genai_tests.rs`:
- Around line 106-124: Extend test_non_wire_renderings_are_not_decoded with a
kebab-case CLI-shaped response wrapped in {"data": {...}}, using the documented
non-REST field names. Decode it with OCIGenAIChatCodec and assert model,
message, and finish_reason are all None, matching the existing snake_case case.
🪄 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: Enterprise

Run ID: 6bd01613-ee5b-40e4-9587-2f9cfce7b63d

📥 Commits

Reviewing files that changed from the base of the PR and between ad9f167 and 7282cdd.

📒 Files selected for processing (2)
  • crates/core/src/codec/oci_genai.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (15)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/unit/codec/oci_genai_tests.rs
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/codec/oci_genai.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/core/src/codec/oci_genai.rs
🔇 Additional comments (1)
crates/core/src/codec/oci_genai.rs (1)

168-196: 📐 Maintainability & Code Quality

Provide the required core validation evidence.

The summary reports formatting, workspace tests, and Clippy, but not just test-rust, cargo deny check, validate-change, uv run pre-commit run --all-files, or the required Rust/Python/Go/Node matrix for crates/core changes. Attach the corresponding CI or command logs before handoff.

As per coding guidelines, “If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js,” and core runtime changes must “satisfy cargo deny check per deny.toml.”

Source: Coding guidelines

Comment thread crates/core/tests/unit/codec/oci_genai_tests.rs
Extends the non-wire-rendering contract test with a kebab-case
data-enveloped CLI-shaped response, guarding the REST-only boundary
against regression on both alternate renderings.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>

@willkill07 willkill07 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for splitting this into a focused first step. I found two issues that need addressing before this response codec is ready to merge.

Comment thread crates/core/src/codec/oci_genai.rs Outdated
.unwrap_or("GENERIC")
.to_uppercase();

let (message, tool_calls, finish_reason) = if api_format == "COHERE" {

@willkill07 willkill07 Jul 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we handle COHEREV2 explicitly here? OCI lists it as a supported apiFormat, but this branch sends it through decode_generic_response_body.

V2 responses use message and finishReason rather than choices, so Relay currently records no response text, tool calls, or finish reason—without surfacing an error. Please add a V2 decoder (or return an explicit unsupported-format error) and cover it with a fixture.

If you are introducing OCI support, it seems odd to not also support COHEREV2

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done in 2ab7e34 — added a dedicated COHEREV2 decoder rather than an error, since the shape is fully specified. Two notes from verifying this:

  1. The live service doesn't accept the format yet: I probed us-chicago-1 yesterday with apiFormat: COHEREV2 against cohere.command-a-03-2025 and cohere.command-a-reasoning (both v2-messages and v1 shapes) and got HTTP 400 "Please pass in correct format of request" in all cases. It is, however, in the current SDK spec (oci 2.182.1 defines CohereChatResponseV2 and dispatches COHEREV2 to it), so the decoder and fixture mirror that published schema exactly: single assistant message with typed content parts, nested-function toolCalls ({id, type: FUNCTION, function: {name, arguments}}), top-level response id, and the V2 finish reasons TOOL_CALL/STOP_SEQUENCE.
  2. TEXT parts flatten like GENERIC; THINKING/IMAGE_URL/DOCUMENT parts are preserved losslessly as provider-native parts.

Covered by test_cohere_v2_chat_result and test_cohere_v2_text_only_flattens, plus the finish-reason mapping cases. I'll re-probe when the service starts accepting the format and adjust if the wire diverges from the spec.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Update with live-wire evidence: the service accepts apiFormat: COHEREV2 as of today (the same request returned HTTP 400 on 07-28), so I swept every Cohere chat model in the us-chicago-1 catalog with both formats:

Model COHERE (v1) COHEREV2
cohere.command-a-03-2025 ✅ accepted ✅ accepted
cohere.command-a-vision ❌ 400 apiFormat COHERE is not supported ✅ accepted
cohere.command-latest ✅ accepted ❌ 400 not supported
cohere.command-plus-latest ✅ accepted ❌ 400 not supported
cohere.command-r-08-2024 ✅ accepted ❌ 400 not supported
cohere.command-r-plus-08-2024 ✅ accepted ❌ 400 not supported
cohere.command-a-reasoning, command-r-16k, command-r-plus 404 (not served on-demand) 404

So the split is generational: the older command/command-r line is V1-only, the current command-a line is V2-first, and command-a-vision is V2-only — without this decoder Relay would record nothing for that model. command-a-03-2025 serves both formats and its wire responses were decoded through the codec for each.

Live V2 responses (text and parallel tool calls) match the CohereChatResponseV2 fixture exactly: nested-function tool calls with provider-supplied ids, JSON-encoded string arguments (parsed like GENERIC), TOOL_CALL finish reason, and message-level toolPlan/citations (now preserved in extra per the other thread). The fixture comment records the live confirmation as of 07ded78.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in 2ab7e34: COHEREV2 now decodes explicitly per the OCI CohereChatResponseV2 schema — single assistant message with typed content parts, nested-function tool calls, finishReason, and usage all normalized. Wire shape was confirmed against the live service (see 07ded78 test note).

Comment thread crates/core/src/codec/oci_genai.rs Outdated
api_format: Some(api_format),
model_version,
}),
extra: serde_json::Map::new(),

@willkill07 willkill07 Jul 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we retain the response fields that are not normalized instead of always returning an empty extra map?

This currently discards OCI data such as timeCreated, service-tier/grounding metadata, and future provider fields that are useful for observability.

Please preserve unknown envelope fields and/or retain the raw chatResponse in the OCI-specific response data, consistent with the other response codecs.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done in 2ab7e34 — decode now carries all unmodeled fields in extra, matching the flatten-passthrough behavior of the OpenAI/Anthropic response codecs: unknown envelope fields (anything besides modelId/modelVersion/chatResponse) plus chat-response fields outside the per-format modeled set. Concretely that preserves timeCreated, serviceTier, COHERE chatHistory, grounding metadata, and any future provider fields. Covered by test_unmodeled_response_fields_preserved_in_extra, which also asserts modeled keys aren't duplicated into extra.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Follow-up in 9cae2fb — auditing the preservation claim field-by-field against the OCI schema (ChatChoice, AssistantMessage, CohereAssistantMessageV2) exposed a second layer of the same gap: fields nested inside the decoded choice and assistant message were still dropped. Per the schema that's logprobs, serviceTier, groundingMetadata, usage (choice) and refusal, annotations, reasoningContent (message) for GENERIC, and toolPlan, citations for COHEREV2 — so the grounding metadata you called out actually lives one level deeper than the chat-response pass I'd added. These are now carried in extra, namespaced under "choice" and "message" so their origin stays unambiguous.

Re-validated against live wire responses (us-chicago-1): a GENERIC reply from an OpenAI-family model carries choice-level logprobs and message-level annotations on the wire, both now preserved (previously silently dropped); COHERE v1 behavior is unchanged. Same pass also mapped GENERIC content_filter finish reasons to the normalized ContentFilter, matching the OpenAI codec.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in 9cae2fb (plus the earlier envelope work): unmodeled envelope, chat-response, choice-, and message-level fields (timeCreated, serviceTier, grounding metadata, future provider fields) are now preserved in extra instead of being discarded, with tests asserting the passthrough.

Per review on the OCI codec:

- Add a COHEREV2 decoder matching the OCI CohereChatResponseV2 schema:
  single assistant message with typed content parts (TEXT flattened,
  THINKING/IMAGE_URL/DOCUMENT preserved as provider-native), nested
  OpenAI-style function tool calls, response id, and the V2 finish
  reasons TOOL_CALL and STOP_SEQUENCE. The live service does not accept
  the format yet (probed us-chicago-1: HTTP 400), so the fixture
  mirrors the published SDK schema.
- Preserve envelope and chat-response fields the normalized shape does
  not model (timeCreated, serviceTier, chatHistory, grounding metadata,
  future fields) in extra instead of discarding them, consistent with
  the other response codecs.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@github-actions github-actions Bot added size:XL PR is extra large and removed size:L PR is large labels Jul 29, 2026
@fede-kamel

Copy link
Copy Markdown
Author

Status after the branch update merging main: the merge itself broke the workspace build — the unified OTel exporter from #556 matches exhaustively on ApiSpecificRequest, which didn't cover the OCIGenAI variant this branch introduces. Fixed in 0766e46 by mapping it to oci.genai (dotted cloud-provider convention, like aws.bedrock / gcp.gemini).

Validation on the current tip: full workspace suite green (3,485 tests), clippy zero warnings, fmt --check clean, plus live-wire decode verification of GENERIC and COHERE responses through the codec (details in the review thread). One note for CI watchers: install_registrations_covers_registry_error_edges (dynamic worker plugin) is a parallelism-sensitive flake inherited from main — the worker code on this branch is byte-identical to main, it passes serially and in isolation, and it's unrelated to this PR.

The service now accepts apiFormat COHEREV2 (it rejected it with HTTP
400 the day before); live text and parallel tool-call responses match
the spec-mirrored fixture exactly, so the fixture comment no longer
claims the format is spec-only.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@fede-kamel fede-kamel changed the title feat(codec): add OCI Generative AI typed variants and response codec (series 1/4) feat(codec): add OCI GenAI typed variants and response codec (1/4) Jul 29, 2026
@fede-kamel

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
crates/core/src/codec/oci_genai.rs (1)

148-159: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject non-string TEXT.text values.

{"type":"TEXT","text":1} bypasses text flattening, then becomes Text("") here via unwrap_or_default(). Return InvalidArgument instead of silently altering provider content, and add a regression fixture.

Proposed fix
         Some("TEXT") => Ok(ContentPart::Text {
-            text: obj
-                .get("text")
-                .and_then(Json::as_str)
-                .unwrap_or_default()
-                .to_string(),
+            text: match obj.get("text") {
+                None | Some(Json::Null) => String::new(),
+                Some(Json::String(text)) => text.clone(),
+                Some(_) => {
+                    return Err(FlowError::InvalidArgument(
+                        "OCI GenAI TEXT content part text must be a string or null".into(),
+                    ));
+                }
+            },
🤖 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 `@crates/core/src/codec/oci_genai.rs` around lines 148 - 159, Update the TEXT
branch of the OCI content decoder to distinguish a missing text field from a
present non-string value: reject non-string TEXT.text values with
InvalidArgument instead of defaulting them to an empty string. Preserve the
existing handling for valid strings and absent fields as intended, and add a
regression fixture covering {"type":"TEXT","text":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.

Outside diff comments:
In `@crates/core/src/codec/oci_genai.rs`:
- Around line 148-159: Update the TEXT branch of the OCI content decoder to
distinguish a missing text field from a present non-string value: reject
non-string TEXT.text values with InvalidArgument instead of defaulting them to
an empty string. Preserve the existing handling for valid strings and absent
fields as intended, and add a regression fixture covering
{"type":"TEXT","text":1}.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: dcdc699c-5639-4eb9-bffb-94647cbc0b4f

📥 Commits

Reviewing files that changed from the base of the PR and between ad9f167 and 07ded78.

📒 Files selected for processing (4)
  • crates/core/src/codec/oci_genai.rs
  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (15)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/src/codec/oci_genai.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/codec/oci_genai.rs
🧠 Learnings (1)
📚 Learning: 2026-07-28T20:07:29.880Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Relay PR: 571
File: crates/core/src/api/runtime/state.rs:996-1020
Timestamp: 2026-07-28T20:07:29.880Z
Learning: In NeMo Relay (RELAY-509), sanitizer callback failures must be treated as intentional fail-open behavior. When an event/tool (request/response) or LLM (request/response) sanitizer callback fails, the sanitizer chain should retain and publish the last valid event/payload snapshot (rather than dropping/invalidating the data) and log the failure including callback context (e.g., which sanitizer/callback failed and relevant identifiers). Apply this consistently across all sanitizer chains mentioned in the RELAY-509 documentation/migration guide.

Applied to files:

  • crates/core/src/observability/otel_genai.rs
  • crates/core/src/codec/oci_genai.rs
🔇 Additional comments (8)
crates/core/src/codec/oci_genai.rs (2)

11-64: LGTM!

Also applies to: 126-139, 189-257, 269-445


176-260: 📐 Maintainability & Code Quality

Attach the required core validation evidence.

The supplied summary reports workspace Rust checks, but not the required full core matrix. Please provide results for just test-rust, cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, cargo deny check, uv run pre-commit run --all-files, and the configured Python, Go, and Node test targets.

  • crates/core/src/codec/oci_genai.rs#L176-L260: validate OCI response decoding through the required core matrix.
  • crates/core/src/observability/otel_genai.rs#L390-L401: validate the OCI provider projection through the same matrix.

Source: Coding guidelines

crates/core/src/observability/otel_genai.rs (1)

1-388: LGTM!

Also applies to: 404-523

crates/core/tests/unit/codec/oci_genai_tests.rs (2)

106-146: LGTM!


300-507: LGTM!

crates/core/tests/integration/pipeline_tests.rs (3)

33-33: LGTM!

Also applies to: 1476-1517


1536-1536: LGTM!

Also applies to: 1736-1741, 2004-2004


1451-1475: 🗄️ Data Integrity & Integration

No issue: GENERIC toolCalls shape matches the decoder
OCIGenAIChatCodec accepts direct id/name/arguments entries for toolCalls, and also falls back to a nested function object for CohereV2-style payloads.

			> Likely an incorrect or invalid review comment.

@willkill07

Copy link
Copy Markdown
Member

@fede-kamel believe it or not, it will actually be easier for me to review and get merged if all parts were in one PR.

Implement LlmCodec for OCIGenAIChatCodec so request intercepts (PII
redaction, guardrails, policy) can operate on OCI Generative AI chat
traffic. Requests are accepted as a full ChatDetails envelope
({compartmentId, servingMode, chatRequest}) or as a bare chatRequest,
in both chatRequest.apiFormat variants: GENERIC (UPPERCASE roles,
typed TEXT content-part lists, flat toolCalls {id, type: FUNCTION,
name, arguments}, toolCallId on tool messages) and COHERE
(preambleOverride, chatHistory USER/CHATBOT/SYSTEM turns, current
message string). GENERIC maxTokens/temperature/topP/stop and COHERE
stopSequences normalize into GenerationParams; unmodeled params
(topK, seed, penalties, ...) stay in the raw payload untouched.

encode() uses baseline-compare-and-patch semantics per the LlmCodec
contract: the original request is re-decoded as the baseline and only
fields an intercept actually changed are rewritten, so unchanged
messages pass through from the raw payload verbatim, per-message
provider fields survive edits, and
encode(decode(original), original) == original at the JSON level.
Model identity is read from servingMode.modelId/endpointId and model
edits are rejected as unsupported.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
Register ProviderSurface::OCIGenAI as the fourth built-in provider
surface, placed first in BUILTIN_PROVIDER_SURFACES: the ChatDetails
envelope and apiFormat markers are the strongest request signal and
never appear in the other surfaces' shapes. Request detection accepts
the envelope, a bare apiFormat chat request, or an oci/oci.genai
provider hint; response detection requires the apiFormat marker so
bare GENERIC responses stay unambiguous with OpenAI Chat.

OCIGenAIStreamingCodec assembles OCI SSE choice deltas (GENERIC) and
text fragments (COHERE) back into a non-streaming ChatResult shape
that decode_response can consume, matching the streaming strategy of
the existing provider codecs and satisfying the mandatory
streaming_codec field of the surface descriptor.

Extend every surface-exhaustive consumer: nemo_guardrails codec enum,
schema, and local stream-text extraction; pii-redaction codec enum
and response overlay (GENERIC text parts, tool calls, and finish
reasons plus COHERE text); adaptive ACG request-surface resolution
(no semantic applier exists for OCI GenAI yet, so it resolves to
None). Add detection, non-shadowing, resolver-registry, overlay, and
cross-surface parity tests.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
Bindings for the OCI Generative AI chat codec: OCIGenAIChatCodec
pyclass with decode/encode/decode_response, module registration, and
the fast-path builtin response-codec downcast in py_api so the native
implementation is used without a Python round trip; the matching napi
class for Node; type stubs (_native.pyi, pii_redaction.d.ts) and the
nemo_relay.codecs re-export; oci_genai added to the pii-redaction
config codec literals; Python test coverage for construction,
method presence, response decoding, and the LlmResponseCodec
protocol.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@github-actions github-actions Bot added lang:js PR changes/introduces Javascript/Typescript code lang:python PR changes/introduces Python code labels Aug 7, 2026
@fede-kamel fede-kamel changed the title feat(codec): add OCI GenAI typed variants and response codec (1/4) feat(codec): add OCI Generative AI provider support (typed variants, request/response codecs, streaming, provider surface, bindings) Aug 7, 2026
@fede-kamel

Copy link
Copy Markdown
Author

Done — the PR now carries all four parts, per your request. The three follow-up layers (request codec, provider surface + streaming, Python/Node bindings) are folded in as the last three commits, so the branch is the complete feature: typed variants, response + request codecs with round-trip identity, ProviderSurface::OCIGenAI detection, OCIGenAIStreamingCodec, guardrail/PII-redaction/adaptive awareness, and the binding classes. Title and description updated to match; commit-by-commit review still works if you prefer that granularity.

Validation on the consolidated tip: full workspace build and test suite green, clippy --workspace --all-targets zero warnings, fmt --check clean, just test-python (567 passed) and just test-node (294 passed) green on the built bindings. The same caveat from before still applies: install_registrations_covers_registry_error_edges (dynamic worker plugin) is a parallelism-sensitive flake whose code is byte-identical to main here; it passes serially and in isolation.

@fede-kamel

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

The typed stub python/nemo_relay/codecs.pyi was missed when the class
was exposed through codecs.py, so type checkers rejected the import
used by the binding tests.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 15

Caution

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

⚠️ Outside diff range comments (1)
crates/adaptive/src/acg/request_surfaces/mod.rs (1)

92-102: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Distinguish "shape not detected" from "surface not supported by ACG".

OCI GenAI requests now reach this function with a successfully detected provider surface. They still fail with "unable to resolve request surface from request shape". That message misstates the cause and will slow diagnosis of an expected, newly reachable failure path. Report the detected surface when the mapping returns None.

♻️ Proposed refactor
-    detect_request_surface(&request.content)
-        .and_then(RequestSurface::from_provider_surface)
-        .ok_or_else(|| {
-            crate::acg::AcgError::Internal(
-                "unable to resolve request surface from request shape".to_string(),
-            )
-        })
+    let Some(provider_surface) = detect_request_surface(&request.content) else {
+        return Err(crate::acg::AcgError::Internal(
+            "unable to resolve request surface from request shape".to_string(),
+        ));
+    };
+    RequestSurface::from_provider_surface(provider_surface).ok_or_else(|| {
+        crate::acg::AcgError::Internal(format!(
+            "adaptive request surfaces do not support provider surface {provider_surface:?}"
+        ))
+    })
🤖 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 `@crates/adaptive/src/acg/request_surfaces/mod.rs` around lines 92 - 102,
Update resolve_request_surface_from_request to separate detection from mapping:
retain the detected provider surface, then return the existing success path when
RequestSurface::from_provider_surface succeeds and report the detected surface
in the AcgError::Internal message when mapping returns None. Use wording that
identifies the surface as unsupported by ACG rather than claiming the request
shape could not be resolved.
🤖 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 `@crates/core/src/codec/oci_genai.rs`:
- Around line 465-484: Update the assistant-message encoding branch in the
Message::Assistant handler so content: None emits an empty JSON array, or omits
the content field, instead of Json::Null. Preserve the existing
encode_generic_content behavior for present content and ensure tool-call-only
messages retain the OCI typed-part-list shape during re-encoding.
- Around line 876-944: Update patch_oci_api_specific to compare the edited and
baseline OCIGenAI api_format values and synchronize the chat request’s apiFormat
whenever the format changes, using the same edited format that encode_api_format
targets. Preserve existing compartmentId and servingMode handling, and ensure
the apiFormat field is patched through the envelope before returning.
- Around line 613-644: Update encode_cohere_messages so it does not default the
required “message” field to an empty string when the remaining messages do not
end with Message::User. Return FlowError::InvalidArgument for any edited message
list lacking a trailing user turn, while preserving the existing extraction and
history encoding for valid requests.
- Around line 355-381: Update encode_oci_tool_call so it omits the "id" field
when tool_call.id is empty, while preserving the existing ID field for non-empty
values. Build the JSON object conditionally rather than always emitting the key,
and leave decode_oci_tool_call unchanged.
- Around line 1632-1649: Update OCIChoiceState::finalize to insert the content
field only when self.text contains accumulated text. For tool-call-only
responses, omit content entirely so decoding yields no assistant text; preserve
the existing TEXT content structure when text is present.
- Around line 646-665: Update encode_cohere_turn and the corresponding Cohere
decoding logic to preserve Message::Tool.tool_call_id using OCI’s required wire
key while retaining the existing role and message fields. Ensure TOOL turns
decode back into Message::Tool with the identifier and content, and add a
round-trip test covering a tool result with a tool-call ID.
- Around line 1616-1630: Update observe_tool_call and its caller
observe_generic_choice to key tool-call state by the stable OCI id rather than
the event-local position, preserving separate state when parallel calls arrive
at position 0. Adjust the tool-call map and lookup flow so identity fields and
argument fragments remain associated with the correct id, and add coverage for
parallel-call fragments.

In `@crates/core/src/plugins/nemo_guardrails/python.rs`:
- Around line 1269-1290: Update the LocalGuardrailsCodec::OCIGenAI branch in
extract_stream_text to inspect a choices array and process each choice’s nested
message content using the existing TEXT-part collection logic. Retain the
current direct message fallback for chunks without choices, and return the first
non-empty extracted text.

In `@crates/core/tests/unit/codec/oci_genai_tests.rs`:
- Around line 353-430: Add a COHERE-specific test alongside
test_cohere_edit_rebuilds_modeled_fields that replaces the final annotated
message content with MessageContent::Parts, calls OCIGenAIChatCodec::encode, and
asserts the result is FlowError::InvalidArgument(_). Reuse the existing
cohere_chat_details and make_request setup.

In `@crates/core/tests/unit/codec/resolve_tests.rs`:
- Line 26: Update the ALL_SURFACES collection to include
ProviderSurface::OCIGenAI and change its declared length to 4, ensuring both
codec_name_round_trips_for_every_surface and
streaming_codec_constructs_a_usable_codec_for_every_surface exercise the OCI
codec.

In `@crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs`:
- Around line 636-638: Extend the remote-mode rejection coverage in the relevant
codec test to include codec = "oci_genai". Configure the case so input and
output retain their default true values, then assert the existing diagnostic
message alongside the other non-openai_chat codecs.

In `@crates/pii-redaction/src/overlay.rs`:
- Around line 198-223: Update the text-part construction in the block overlay
logic to use splitn with text_part_count instead of split, ensuring the final
TEXT block retains any remaining newline-separated content. Keep the existing
text_part_index mapping and fallback behavior unchanged.
- Around line 242-253: The tool-call overlay currently updates only top-level
fields, leaving OCI’s nested function values unsanitized. In the loop over
raw_calls and sanitized_call, update the existing raw_call.function object’s
name and arguments using the sanitized values, and use the top-level name and
arguments fields only when that nested object is absent.

In `@crates/pii-redaction/tests/coverage/overlay_tests.rs`:
- Around line 137-180: Add a focused test alongside
oci_genai_overlay_rewrites_generic_text_and_tool_calls using multiple
message.content blocks: include two TEXT blocks separated by a non-TEXT block,
provide matching redacted message lines in AnnotatedLlmResponse, and assert both
TEXT blocks are rewritten while the intervening non-TEXT block remains
unchanged.

In `@crates/python/src/py_api/mod.rs`:
- Around line 75-77: Update the PyOCIGenAIChatCodec fallback in py_llm_codec to
return builtin.inner_codec.clone() directly instead of inner_response_codec,
avoiding Python method dispatch. Add an OCI request integration test covering
this codec path.

---

Outside diff comments:
In `@crates/adaptive/src/acg/request_surfaces/mod.rs`:
- Around line 92-102: Update resolve_request_surface_from_request to separate
detection from mapping: retain the detected provider surface, then return the
existing success path when RequestSurface::from_provider_surface succeeds and
report the detected surface in the AcgError::Internal message when mapping
returns None. Use wording that identifies the surface as unsupported by ACG
rather than claiming the request shape could not be resolved.
🪄 Autofix

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: Enterprise

Run ID: 5883d735-69f6-46e9-ad4b-0fe5b2ed4382

📥 Commits

Reviewing files that changed from the base of the PR and between 07ded78 and 311977a.

📒 Files selected for processing (22)
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/core/src/codec/resolve.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/node/pii_redaction.d.ts
  • crates/node/src/types/mod.rs
  • crates/pii-redaction/src/component.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/python/src/py_types/mod.rs
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • python/nemo_relay/pii_redaction.pyi
  • python/tests/test_builtin_codecs.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (37)
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • crates/python/src/py_api/mod.rs
  • python/nemo_relay/pii_redaction.pyi
  • python/nemo_relay/_native.pyi
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • python/nemo_relay/pii_redaction.pyi
  • python/nemo_relay/_native.pyi
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

**/*: Use release tags in raw Rust-compatible SemVer without a leading v; tags such as v0.1.0 are prohibited.
Use branch prefixes feat/, fix/, docs/, test/, or refactor/ according to the change purpose.
Every commit in a pull request must include a DCO Signed-off-by: sign-off.
Before submitting a pull request, ensure pre-commit hooks, relevant tests, target-specific builds, documentation updates, and a rebase on the latest main are complete.
Use commit messages in the form type: short description, with a valid type and a first line under 72 characters.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • python/nemo_relay/pii_redaction.pyi
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • python/nemo_relay/_native.pyi
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/node/pii_redaction.d.ts
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/node/pii_redaction.d.ts
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/node/pii_redaction.d.ts
**/*.{rs,py,js,jsx,ts,tsx,go,c,h,cc,cpp,md,toml,yml,yaml,sh}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, documentation, scripts, and configuration files; the project is Apache-2.0.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/node/pii_redaction.d.ts
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Use snake_case naming in Rust and Python.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,mjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Preserve the existing Tokio-based asynchronous model and callback/future lifetimes; do not unexpectedly block or hide async work in bindings.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/node/pii_redaction.d.ts
python/nemo_relay/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep Python wrapper modules under python/nemo_relay/; the native extension is built from crates/python with maturin.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: Lint Python with Ruff using rule sets E, F, W, and I.
Format Python with the Ruff formatter, using a 120-character line length and double quotes.
Run ty for Python type checking.
Use Python snake_case naming conventions.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • python/tests/test_builtin_codecs.py
**/*.{rs,py,go,js,jsx,ts,tsx,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{rs,py,go,js,jsx,ts,tsx,c,h}: Run tests for every language affected by a change; changes to the core Rust crate require tests across all bindings.
Use SONAR_IGNORE_START / SONAR_IGNORE_END only for documented false positives, keep ignored blocks minimal, explain them with a comment, and obtain reviewer sign-off.
Preserve the layered architecture in which Rust provides the core runtime and C FFI, PyO3, and NAPI provide bindings that mirror the full API surface.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/node/pii_redaction.d.ts
**/*.{rs,py,go,js,jsx,ts,tsx,c,h,html,md,mdx,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include the appropriate SPDX copyright and Apache-2.0 license header in every source file.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/node/pii_redaction.d.ts
**/*.{rs,py,go,js,ts,mdx}

📄 CodeRabbit inference engine (.agents/skills/maintain-observability/SKILL.md)

**/*.{rs,py,go,js,ts,mdx}: When changing observability event fields, exporter behavior, subscriber configuration, or binding parity, keep the core event model, ATIF, typed OpenTelemetry projections, FFI wrappers, language bindings, configuration, and documentation synchronized.
Examples and documentation must use each exporter's documented flush/deregister order before shutdown.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
  • crates/node/pii_redaction.d.ts
**/*.{py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/maintain-observability/SKILL.md)

**/*.{py,go,js,ts}: Ensure Python, Go, and Node.js configuration objects and subscriber/exporter methods expose the same logical knobs and semantics as the core implementation.
Run just test-python, just test-go, and just test-node when binding-native configuration or lifecycle changes.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • python/tests/test_builtin_codecs.py
  • crates/node/pii_redaction.d.ts
python/**/*.py

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

python/**/*.py: Format changed Python wrapper and test files with uv run ruff format python python/plugin.
Run the full Python suite with just test-python before review.
Use just build-python for an explicit editable-package and native-extension build pass.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • python/tests/test_builtin_codecs.py
python/nemo_relay/**/*

⚙️ CodeRabbit configuration file

python/nemo_relay/**/*: Review Python wrapper changes for typed API consistency, contextvars-based scope isolation, async behavior, and parity with the native extension.
Stubs and runtime implementations should stay aligned.

Files:

  • python/nemo_relay/codecs.py
  • python/nemo_relay/pii_redaction.py
  • python/nemo_relay/pii_redaction.pyi
  • python/nemo_relay/_native.pyi
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

**/*.rs: Format Rust code with rustfmt defaults using cargo fmt.
Run cargo clippy -- -D warnings; all Rust warnings must be treated as errors.
Use Rust snake_case naming conventions.

Files:

  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Use Json = serde_json::Value in Rust-facing runtime APIs where existing code expects JSON payloads.
Treat Rust as the source of truth for runtime behavior; binding APIs should mirror Rust semantics unless a language-specific wrapper intentionally improves ergonomics.

Files:

  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees.

Files:

  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
crates/python/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

crates/python/**/*.rs: When Rust files change as part of Python work, run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings.
When the native Rust bridge changes, add and run the Rust crate tests for nemo-relay-python, including cargo test -p nemo-relay-python.

Files:

  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/node/pii_redaction.d.ts
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/adaptive/**

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Keep crates/adaptive aligned with the canonical adaptive config schema, built-in section helpers, plugin lifecycle, and validation/report behavior.

Files:

  • crates/adaptive/src/acg/request_surfaces/mod.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive,plugin,worker,worker-proto,types}/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-rust-core/SKILL.md)

crates/{core,adaptive,plugin,worker,worker-proto,types}/**/*.rs: For changes in the Rust core, adaptive, plugin, worker, worker-proto, or types crates, run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings.
Use narrower crate tests only as a local debugging loop; final Rust validation must use the shared just test-rust workflow.
For changes to crates/core, crates/adaptive, or shared runtime semantics, expand validation to the full binding matrix with validate-change or just ci=true test-rust.
When public APIs, event shapes, middleware behavior, plugin semantics, or core/adaptive behavior change, also run validate-change.
If a change is isolated to one binding wrapper with unchanged Rust semantics, prefer that binding's build and test skill instead.

Files:

  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Use Result<T> with FlowError in core runtime paths, keeping wrapper-layer errors explicit and binding-appropriate.

Run affected Rust crate tests, and run just test-rust when event fields change.

Files:

  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/src/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

crates/core/src/**/*.rs: plugins list, plugins inspect, and plugins validate must report lifecycle and compatibility status without leaking secret configuration.
Top-level doctor must report resolved dynamic-plugin and host-configuration status.

Files:

  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/src/codec/resolve.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/src/codec/oci_genai.rs
{crates/plugin/**/*.rs,crates/worker-proto/**/*.rs,crates/worker/**/*.rs,crates/types/**/*.rs,crates/core/**/*.{rs,toml},python/plugin/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Run the targeted Rust, Python plugin, integration, documentation, and broader validation commands when validating dynamic-plugin changes.

Files:

  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/{test,tests}/**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When adding functionality, include tests in the appropriate test files for each affected language binding.

Files:

  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/tests/unit/codec/oci_genai_tests.rs
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/codec/resolve.rs
  • crates/core/src/codec/oci_genai.rs
python/tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain test coverage for Python binding and wrapper changes with the Python test suite.

python/tests/**/*.py: Use pytest to run Python tests.
Do not add @pytest.mark.asyncio; async tests are automatically detected by the async runner.
Do not add -> None return annotations to test functions.
When mocking a class, use unittest.mock.MagicMock or unittest.mock.AsyncMock, using the spec argument when necessary; do not define a new mock class.
Prefix mocked class names with mock, not fake.
Prefer pytest fixtures over helper methods.
Define fixtures needed by multiple test files in conftest.py rather than repeating them.
Define fixtures using @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and a <fixture_name>_fixture function; specify scope only when it is not function.
Prefer pytest.mark.parametrize over separate tests for different input types.

Files:

  • python/tests/test_builtin_codecs.py
crates/node/**/*.{js,ts,jsx,tsx,json}

📄 CodeRabbit inference engine (.agents/skills/test-node-binding/SKILL.md)

Format changed Node files with npm run format --workspace=nemo-relay-node

Files:

  • crates/node/pii_redaction.d.ts
crates/node/**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.agents/skills/test-node-binding/SKILL.md)

Use npm run check:docstrings --workspace=nemo-relay-node to validate public API docstring checks when surface docs changed

Files:

  • crates/node/pii_redaction.d.ts
crates/node/**/*.{js,mjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use camelCase naming for Node.js APIs.

Files:

  • crates/node/pii_redaction.d.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use Node.js camelCase naming conventions.

Files:

  • crates/node/pii_redaction.d.ts
🧠 Learnings (6)
📚 Learning: 2026-08-03T19:55:03.931Z
Learnt from: afourniernv
Repo: NVIDIA/NeMo-Relay PR: 558
File: crates/pii-redaction/src/rampart/mod.rs:265-274
Timestamp: 2026-08-03T19:55:03.931Z
Learning: In NeMo Relay first-party plugin registration helpers, treat the documented duplicate-registration `PluginError::RegistrationFailed` result from `register_plugin` as success when registration is intended to be idempotent. Do not locally reclassify this as `PluginError::Conflict`; changing the classification requires a core-wide review of the public API and FFI behavior.

Applied to files:

  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_types/codecs.rs
  • crates/node/src/types/mod.rs
  • crates/adaptive/src/acg/request_surfaces/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/codec/resolve.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/pii-redaction/src/component.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/codec/parity_tests.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
📚 Learning: 2026-07-28T20:07:29.880Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Relay PR: 571
File: crates/core/src/api/runtime/state.rs:996-1020
Timestamp: 2026-07-28T20:07:29.880Z
Learning: In NeMo Relay (RELAY-509), sanitizer callback failures must be treated as intentional fail-open behavior. When an event/tool (request/response) or LLM (request/response) sanitizer callback fails, the sanitizer chain should retain and publish the last valid event/payload snapshot (rather than dropping/invalidating the data) and log the failure including callback context (e.g., which sanitizer/callback failed and relevant identifiers). Apply this consistently across all sanitizer chains mentioned in the RELAY-509 documentation/migration guide.

Applied to files:

  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/src/codec/resolve.rs
  • crates/core/src/plugins/nemo_guardrails/component.rs
  • crates/core/src/codec/oci_genai.rs
📚 Learning: 2026-08-03T19:54:17.292Z
Learnt from: afourniernv
Repo: NVIDIA/NeMo-Relay PR: 558
File: crates/pii-redaction/src/rampart/mod.rs:486-492
Timestamp: 2026-08-03T19:54:17.292Z
Learning: In the Rust plugin configuration editor, use literal enum values for codec lists because `nemo_relay::editor_config!` generates static editor metadata. Do not replace these literals with a shared source such as `supported_codec_names()` unless the core editor-metadata API is first changed to support dynamic values.

Applied to files:

  • crates/pii-redaction/src/component.rs
  • crates/pii-redaction/src/overlay.rs
📚 Learning: 2026-08-03T19:54:44.552Z
Learnt from: afourniernv
Repo: NVIDIA/NeMo-Relay PR: 558
File: crates/pii-redaction/src/rampart/mod.rs:670-792
Timestamp: 2026-08-03T19:54:44.552Z
Learning: In the Rust `crates/pii-redaction` crate, treat `pii_rampart` as a first-party built-in component. Preserve private `#[cfg(test)]` unit-test modules under `src`, including Rampart tests, according to the crate’s existing convention. Do not request moving these tests to `tests/` when doing so would require widening implementation visibility solely for test access.

Applied to files:

  • crates/pii-redaction/src/component.rs
  • crates/pii-redaction/src/overlay.rs
📚 Learning: 2026-07-02T18:33:06.638Z
Learnt from: yczhang-nv
Repo: NVIDIA/NeMo-Relay PR: 355
File: crates/core/tests/unit/codec/parity_tests.rs:500-548
Timestamp: 2026-07-02T18:33:06.638Z
Learning: In Rust codec parity unit tests, when comparing a computed float/cost against a hand-derived literal (typically documented in a nearby comment), use a tolerance-based assertion rather than `assert_eq!` (e.g., `assert!((total - expected).abs() < 1e-9)`) to avoid harmless floating-point/rounding differences. Keep cross-codec parity assertions (e.g., `assert_eq!(chat_cost, anthropic_cost)`) exact with `assert_eq!` when the intention is a strict parity contract across codecs, since they are expected to share the same estimation path and produce bit-identical output across codecs.

Applied to files:

  • crates/core/tests/unit/codec/parity_tests.rs
📚 Learning: 2026-05-07T18:04:44.387Z
Learnt from: mnajafian-nv
Repo: NVIDIA/NeMo-Flow PR: 67
File: integrations/openclaw/src/modules.ts:1-2
Timestamp: 2026-05-07T18:04:44.387Z
Learning: In NVIDIA/NeMo-Flow, TypeScript source files should use `//` line comments for SPDX headers (e.g., `// SPDX-FileCopyrightText: ...` and `// SPDX-License-Identifier: ...`) rather than C-style block comments (`/* ... */`). The repo’s copyright checker enforces this mapping, so `//` SPDX headers in `.ts` files should not be flagged as a style violation.

Applied to files:

  • crates/node/pii_redaction.d.ts
🔇 Additional comments (34)
crates/node/src/types/mod.rs (1)

535-593: LGTM!

crates/python/src/py_api/mod.rs (1)

46-48: LGTM!

crates/python/src/py_types/codecs.rs (1)

1018-1082: LGTM!

crates/python/src/py_types/mod.rs (1)

185-190: LGTM!

python/nemo_relay/_native.pyi (1)

1151-1170: LGTM!

python/nemo_relay/codecs.py (1)

51-51: LGTM!

Also applies to: 162-170

crates/core/src/plugins/nemo_guardrails/component.rs (1)

285-285: LGTM!

Also applies to: 433-438, 929-930

crates/core/src/plugins/nemo_guardrails/python.rs (1)

894-912: LGTM!

crates/pii-redaction/src/overlay.rs (1)

15-37: LGTM!

Also applies to: 505-523

crates/node/pii_redaction.d.ts (1)

42-42: LGTM!

crates/pii-redaction/src/component.rs (1)

277-277: LGTM!

Also applies to: 503-508, 1145-1146

python/nemo_relay/pii_redaction.py (1)

139-139: LGTM!

python/nemo_relay/pii_redaction.pyi (1)

61-61: LGTM!

crates/core/src/codec/oci_genai.rs (10)

66-91: LGTM!


97-149: LGTM!


181-213: LGTM!


310-338: LGTM!


387-452: LGTM!

Also applies to: 509-532


671-749: LGTM!


751-872: LGTM!


950-1109: LGTM!


1390-1456: LGTM!


1586-1612: 🩺 Stability & Availability

No issue: empty choices are handled safely. decode_generic_response_body returns Ok when choices is empty, so the close path does not panic or return an error.

			> Likely an incorrect or invalid review comment.
crates/core/src/codec/resolve.rs (3)

17-17: LGTM!

Also applies to: 28-29


69-71: LGTM!


160-160: LGTM!

crates/adaptive/src/acg/request_surfaces/mod.rs (2)

46-53: LGTM!


46-53: 📐 Maintainability & Code Quality

Confirm the Go binding tests ran for this crates/adaptive change.

The PR report lists Rust, Python, and Node results. It does not list Go. The guidelines require the full Rust, Python, Go, and Node.js matrix when crates/adaptive changes. Run just test-go, or state why it does not apply.

As per coding guidelines: "If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js."

Source: Coding guidelines

crates/core/tests/unit/codec/oci_genai_tests.rs (1)

9-146: LGTM!

Also applies to: 152-249, 255-347, 436-540, 1002-1091, 1097-1203

crates/core/tests/unit/codec/parity_tests.rs (1)

832-870: LGTM!

Also applies to: 872-921, 923-968, 970-988, 990-1027, 1029-1062

crates/core/tests/unit/codec/resolve_tests.rs (1)

427-427: LGTM!

Also applies to: 441-446

crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs (1)

402-407: LGTM!

crates/pii-redaction/tests/coverage/overlay_tests.rs (1)

182-201: LGTM!

python/tests/test_builtin_codecs.py (1)

7-8: LGTM!

Also applies to: 28-33, 77-88, 295-320, 341-341

Comment thread crates/core/src/codec/oci_genai.rs
Comment thread crates/core/src/codec/oci_genai.rs
Comment thread crates/core/src/codec/oci_genai.rs
Comment thread crates/core/src/codec/oci_genai.rs
Comment thread crates/core/src/codec/oci_genai.rs Outdated
Comment thread crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
Comment thread crates/pii-redaction/src/overlay.rs Outdated
Comment thread crates/pii-redaction/src/overlay.rs Outdated
Comment thread crates/pii-redaction/tests/coverage/overlay_tests.rs
Comment thread crates/python/src/py_api/mod.rs
…rlays

Review-driven fixes across the OCI GenAI surface:

- encode omits an empty tool-call id instead of emitting "id": "", and
  re-encodes a tool-call-only assistant message with content: [] rather
  than null, matching the OCI typed-part-list wire shape
- COHERE encode fails loudly (InvalidArgument) when the edited messages
  do not end with a user turn instead of silently sending an empty
  prompt, and rejects normalized tool messages outright since OCI's
  CohereToolMessage has no field for the normalized tool_call_id
- an edited api_format now patches the wire apiFormat so the declared
  format can no longer contradict the rebuilt body
- streamed tool-call fragments are keyed by their stable id (falling
  back to array position) so parallel calls arriving in separate events
  no longer merge, and a tool-call-only stream finalizes with an empty
  content list so it decodes without a synthetic empty message
- the guardrails stream-text extractor handles choices-wrapped GENERIC
  deltas in addition to bare choice deltas
- the PII overlay sanitizes nested function tool-call objects (the shape
  the decoder already accepts) and uses splitn so multi-part TEXT
  overlays never drop sanitized text containing newlines
- the Python binding extracts the native OCI request codec directly,
  avoiding Python method dispatch on the managed-call path

New coverage: parallel streamed tool calls, tool-call-only streams,
apiFormat edits, COHERE trailing-user/multimodal/tool-message encode
errors, empty-id omission, choices-wrapped and COHERE stream text
extraction, remote-mode oci_genai rejection diagnostics, multi-part and
nested-function overlays, an OCI request round-trip through the Python
binding, and ProviderSurface::OCIGenAI in the every-surface resolver
tests.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
crates/core/tests/fixtures/native_plugin/Cargo.lock is already tracked
on main with its own contents; the locally generated copy only added
diff noise to the PR.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@fede-kamel

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@crates/core/src/codec/oci_genai.rs`:
- Around line 1072-1077: Make an api_format change trigger the same body rebuild
path as an annotated.messages change, converting between GENERIC, COHERE, and
COHEREV2 request shapes as needed. Before updating apiFormat, validate that the
required format-specific encoder exists and return FlowError::InvalidArgument
when it does not. Preserve the existing COHERE encoder behavior and ensure
COHEREV2 uses the intended GENERIC-path semantics. Add coverage for format-only
transitions in both directions, including COHEREV2.
🪄 Autofix

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: Enterprise

Run ID: aa866bd2-4013-4c28-b68b-47151f547c6e

📥 Commits

Reviewing files that changed from the base of the PR and between 311977a and 2fec51b.

📒 Files selected for processing (11)
  • crates/core/src/codec/oci_genai.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/python/src/py_api/mod.rs
  • python/nemo_relay/codecs.pyi
  • python/tests/test_builtin_codecs.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (31)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

**/*.rs: Format Rust code with rustfmt defaults using cargo fmt.
Run cargo clippy -- -D warnings; all Rust warnings must be treated as errors.
Use Rust snake_case naming conventions.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • crates/python/src/py_api/mod.rs
  • python/nemo_relay/codecs.pyi
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

**/*: Use release tags in raw Rust-compatible SemVer without a leading v; tags such as v0.1.0 are prohibited.
Use branch prefixes feat/, fix/, docs/, test/, or refactor/ according to the change purpose.
Every commit in a pull request must include a DCO Signed-off-by: sign-off.
Before submitting a pull request, ensure pre-commit hooks, relevant tests, target-specific builds, documentation updates, and a rebase on the latest main are complete.
Use commit messages in the form type: short description, with a valid type and a first line under 72 characters.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • python/nemo_relay/codecs.pyi
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,jsx,ts,tsx,go,c,h,cc,cpp,md,toml,yml,yaml,sh}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, documentation, scripts, and configuration files; the project is Apache-2.0.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Use snake_case naming in Rust and Python.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Use Json = serde_json::Value in Rust-facing runtime APIs where existing code expects JSON payloads.
Treat Rust as the source of truth for runtime behavior; binding APIs should mirror Rust semantics unless a language-specific wrapper intentionally improves ergonomics.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,js,mjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Preserve the existing Tokio-based asynchronous model and callback/future lifetimes; do not unexpectedly block or hide async work in bindings.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,jsx,ts,tsx,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{rs,py,go,js,jsx,ts,tsx,c,h}: Run tests for every language affected by a change; changes to the core Rust crate require tests across all bindings.
Use SONAR_IGNORE_START / SONAR_IGNORE_END only for documented false positives, keep ignored blocks minimal, explain them with a comment, and obtain reviewer sign-off.
Preserve the layered architecture in which Rust provides the core runtime and C FFI, PyO3, and NAPI provide bindings that mirror the full API surface.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,jsx,ts,tsx,c,h,html,md,mdx,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include the appropriate SPDX copyright and Apache-2.0 license header in every source file.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/src/codec/oci_genai.rs
**/*.{rs,py,go,js,ts,mdx}

📄 CodeRabbit inference engine (.agents/skills/maintain-observability/SKILL.md)

**/*.{rs,py,go,js,ts,mdx}: When changing observability event fields, exporter behavior, subscriber configuration, or binding parity, keep the core event model, ATIF, typed OpenTelemetry projections, FFI wrappers, language bindings, configuration, and documentation synchronized.
Examples and documentation must use each exporter's documented flush/deregister order before shutdown.

Files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/python/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

crates/python/**/*.rs: When Rust files change as part of Python work, run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings.
When the native Rust bridge changes, add and run the Rust crate tests for nemo-relay-python, including cargo test -p nemo-relay-python.

Files:

  • crates/python/src/py_api/mod.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/python/src/py_api/mod.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Use Result<T> with FlowError in core runtime paths, keeping wrapper-layer errors explicit and binding-appropriate.

Run affected Rust crate tests, and run just test-rust when event fields change.

Files:

  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
**/{test,tests}/**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When adding functionality, include tests in the appropriate test files for each affected language binding.

Files:

  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
{crates/plugin/**/*.rs,crates/worker-proto/**/*.rs,crates/worker/**/*.rs,crates/types/**/*.rs,crates/core/**/*.{rs,toml},python/plugin/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Run the targeted Rust, Python plugin, integration, documentation, and broader validation commands when validating dynamic-plugin changes.

Files:

  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive,plugin,worker,worker-proto,types}/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-rust-core/SKILL.md)

crates/{core,adaptive,plugin,worker,worker-proto,types}/**/*.rs: For changes in the Rust core, adaptive, plugin, worker, worker-proto, or types crates, run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings.
Use narrower crate tests only as a local debugging loop; final Rust validation must use the shared just test-rust workflow.
For changes to crates/core, crates/adaptive, or shared runtime semantics, expand validation to the full binding matrix with validate-change or just ci=true test-rust.
When public APIs, event shapes, middleware behavior, plugin semantics, or core/adaptive behavior change, also run validate-change.
If a change is isolated to one binding wrapper with unchanged Rust semantics, prefer that binding's build and test skill instead.

Files:

  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • python/tests/test_builtin_codecs.py
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
python/tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain test coverage for Python binding and wrapper changes with the Python test suite.

python/tests/**/*.py: Use pytest to run Python tests.
Do not add @pytest.mark.asyncio; async tests are automatically detected by the async runner.
Do not add -> None return annotations to test functions.
When mocking a class, use unittest.mock.MagicMock or unittest.mock.AsyncMock, using the spec argument when necessary; do not define a new mock class.
Prefix mocked class names with mock, not fake.
Prefer pytest fixtures over helper methods.
Define fixtures needed by multiple test files in conftest.py rather than repeating them.
Define fixtures using @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and a <fixture_name>_fixture function; specify scope only when it is not function.
Prefer pytest.mark.parametrize over separate tests for different input types.

Files:

  • python/tests/test_builtin_codecs.py
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: Lint Python with Ruff using rule sets E, F, W, and I.
Format Python with the Ruff formatter, using a 120-character line length and double quotes.
Run ty for Python type checking.
Use Python snake_case naming conventions.

Files:

  • python/tests/test_builtin_codecs.py
**/*.{py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/maintain-observability/SKILL.md)

**/*.{py,go,js,ts}: Ensure Python, Go, and Node.js configuration objects and subscriber/exporter methods expose the same logical knobs and semantics as the core implementation.
Run just test-python, just test-go, and just test-node when binding-native configuration or lifecycle changes.

Files:

  • python/tests/test_builtin_codecs.py
python/**/*.py

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

python/**/*.py: Format changed Python wrapper and test files with uv run ruff format python python/plugin.
Run the full Python suite with just test-python before review.
Use just build-python for an explicit editable-package and native-extension build pass.

Files:

  • python/tests/test_builtin_codecs.py
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • python/nemo_relay/codecs.pyi
python/nemo_relay/**/*

⚙️ CodeRabbit configuration file

python/nemo_relay/**/*: Review Python wrapper changes for typed API consistency, contextvars-based scope isolation, async behavior, and parity with the native extension.
Stubs and runtime implementations should stay aligned.

Files:

  • python/nemo_relay/codecs.pyi
crates/core/src/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

crates/core/src/**/*.rs: plugins list, plugins inspect, and plugins validate must report lifecycle and compatibility status without leaking secret configuration.
Top-level doctor must report resolved dynamic-plugin and host-configuration status.

Files:

  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/src/codec/oci_genai.rs
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/codec/oci_genai.rs
🧠 Learnings (4)
📚 Learning: 2026-08-03T19:55:03.931Z
Learnt from: afourniernv
Repo: NVIDIA/NeMo-Relay PR: 558
File: crates/pii-redaction/src/rampart/mod.rs:265-274
Timestamp: 2026-08-03T19:55:03.931Z
Learning: In NeMo Relay first-party plugin registration helpers, treat the documented duplicate-registration `PluginError::RegistrationFailed` result from `register_plugin` as success when registration is intended to be idempotent. Do not locally reclassify this as `PluginError::Conflict`; changing the classification requires a core-wide review of the public API and FFI behavior.

Applied to files:

  • crates/python/src/py_api/mod.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs
  • crates/core/tests/unit/codec/resolve_tests.rs
  • crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs
  • crates/pii-redaction/tests/coverage/overlay_tests.rs
  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/pii-redaction/src/overlay.rs
  • crates/core/tests/unit/codec/oci_genai_tests.rs
  • crates/core/src/codec/oci_genai.rs
📚 Learning: 2026-07-28T20:07:29.880Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Relay PR: 571
File: crates/core/src/api/runtime/state.rs:996-1020
Timestamp: 2026-07-28T20:07:29.880Z
Learning: In NeMo Relay (RELAY-509), sanitizer callback failures must be treated as intentional fail-open behavior. When an event/tool (request/response) or LLM (request/response) sanitizer callback fails, the sanitizer chain should retain and publish the last valid event/payload snapshot (rather than dropping/invalidating the data) and log the failure including callback context (e.g., which sanitizer/callback failed and relevant identifiers). Apply this consistently across all sanitizer chains mentioned in the RELAY-509 documentation/migration guide.

Applied to files:

  • crates/core/src/plugins/nemo_guardrails/python.rs
  • crates/core/src/codec/oci_genai.rs
📚 Learning: 2026-08-03T19:54:17.292Z
Learnt from: afourniernv
Repo: NVIDIA/NeMo-Relay PR: 558
File: crates/pii-redaction/src/rampart/mod.rs:486-492
Timestamp: 2026-08-03T19:54:17.292Z
Learning: In the Rust plugin configuration editor, use literal enum values for codec lists because `nemo_relay::editor_config!` generates static editor metadata. Do not replace these literals with a shared source such as `supported_codec_names()` unless the core editor-metadata API is first changed to support dynamic values.

Applied to files:

  • crates/pii-redaction/src/overlay.rs
📚 Learning: 2026-08-03T19:54:44.552Z
Learnt from: afourniernv
Repo: NVIDIA/NeMo-Relay PR: 558
File: crates/pii-redaction/src/rampart/mod.rs:670-792
Timestamp: 2026-08-03T19:54:44.552Z
Learning: In the Rust `crates/pii-redaction` crate, treat `pii_rampart` as a first-party built-in component. Preserve private `#[cfg(test)]` unit-test modules under `src`, including Rampart tests, according to the crate’s existing convention. Do not request moving these tests to `tests/` when doing so would require widening implementation visibility solely for test access.

Applied to files:

  • crates/pii-redaction/src/overlay.rs
🔇 Additional comments (11)
crates/core/src/plugins/nemo_guardrails/python.rs (1)

894-894: LGTM!

Also applies to: 903-903, 912-912, 1280-1311

crates/pii-redaction/src/overlay.rs (1)

193-229: LGTM!

Also applies to: 231-271

crates/core/src/codec/oci_genai.rs (1)

374-388: LGTM!

Also applies to: 479-487, 639-678, 1507-1511, 1649-1703

crates/python/src/py_api/mod.rs (1)

99-101: LGTM!

python/nemo_relay/codecs.pyi (1)

166-209: LGTM!

crates/core/tests/unit/codec/oci_genai_tests.rs (1)

1209-1226: LGTM!

Also applies to: 1228-1242, 1244-1261, 1263-1307, 1309-1328, 1330-1372, 1374-1395

crates/core/tests/unit/codec/resolve_tests.rs (1)

399-403: LGTM!

crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs (1)

636-638: LGTM!

Also applies to: 674-687

crates/core/tests/unit/plugins/nemo_guardrails/local_python_tests.rs (1)

576-599: LGTM!

crates/pii-redaction/tests/coverage/overlay_tests.rs (1)

203-238: LGTM!

Also applies to: 240-280

python/tests/test_builtin_codecs.py (1)

295-328: LGTM!

Also applies to: 330-354

Comment thread crates/core/src/codec/oci_genai.rs Outdated
A format switch can never be encoded soundly with merge-not-replace
semantics: rebuilding the body for the new format would still leave the
previous format's modeled fields (messages vs message/chatHistory) in
the patched request. Treat api_format as read-only — patch_oci_api_specific
returns InvalidArgument when an intercept changes it — and drop the
partial apiFormat/body rewrite added in the previous commit. Covered for
GENERIC->COHERE, GENERIC->COHEREV2, and COHERE->GENERIC transitions.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@fede-kamel

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@willkill07 willkill07 changed the title feat(codec): add OCI Generative AI provider support (typed variants, request/response codecs, streaming, provider surface, bindings) feat(codec): add OCI Generative AI provider support Aug 7, 2026
@willkill07

Copy link
Copy Markdown
Member

please address merge conflicts when you get a chance. I can then start to exhaustively review

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

Labels

Feature a new feature lang:js PR changes/introduces Javascript/Typescript code lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code size:XL PR is extra large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement]: Support Oracle Cloud Infrastructure (OCI) Generative AI as a provider

2 participants