Skip to content

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

Merged
rapids-bot[bot] merged 31 commits into
NVIDIA:mainfrom
fede-kamel:feat/oci-codec-1-types-response
Aug 12, 2026
Merged

feat(codec): add OCI Generative AI provider support#554
rapids-bot[bot] merged 31 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
Contributor

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.
  • Codec identity: BuiltinLlmCodec::OCIGenAI surfaced end to end — codec_identity() on both codec trait impls, worker SDK proto decoding (crates/worker), native plugin SDK enum and async invocation context (crates/plugin), the Node LlmCodecIdentity typing, and PII-redaction surface routing — so sanitize callbacks in every SDK receive { kind: "builtin", id: "oci_genai" } instead of opaque.

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, codec-identity tests across core, worker, plugin, and pii-redaction, 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 support for GENERIC, COHERE, and COHEREV2 requests, responses, and streaming.
    • Added Python and Node.js APIs for encoding and decoding OCI payloads.
    • Added support for OCI metadata, tool calls, usage, finish reasons, and model details.
    • Added OCI compatibility to PII redaction, NeMo Guardrails, caching, replay, and configuration schemas.
  • Bug Fixes

    • Improved OCI payload detection, normalization, response processing, tool-call handling, and streaming extraction.

@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, adaptive cache handling, 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/*, crates/core/tests/unit/codec/oci_genai_tests.rs
Adds OCI metadata and implements request, response, and streaming codecs with envelope, content, tool-call, usage, finish-reason, streaming, and extra-field handling.
Provider resolution and adaptive handling
crates/core/src/codec/resolve.rs, crates/core/src/observability/otel_genai.rs, crates/adaptive/..., crates/core/tests/unit/codec/resolve_tests.rs, crates/core/tests/unit/codec/parity_tests.rs, crates/core/tests/integration/pipeline_tests.rs
Registers OCI detection and codec construction, maps its observability name, and adds loss-aware cache-key, replay, resolution, parity, and pipeline behavior.
Guardrails and PII redaction integration
crates/core/src/plugins/nemo_guardrails/*, crates/pii-redaction/src/*, crates/pii-redaction/tests/*
Adds OCI codec configuration, Guardrails stream extraction, response overlays, tool-call sanitization, finish-reason mappings, and validation.
Node and Python codec exposure
crates/node/*, crates/python/*, python/nemo_relay/*, python/tests/*
Exposes OCIGenAIChatCodec through native bindings, type declarations, module exports, and redaction configuration types.

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 OCI GenAI 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 PR covers most objectives in [#548], but the evidence shows streaming only for GENERIC and COHERE, not COHEREV2, and no provider documentation. Add COHEREV2 streaming support and documentation for the supported-provider list, or explicitly defer those acceptance criteria.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The changes remain within [#548], covering OCI codecs, detection, streaming, bindings, observability, middleware integrations, caching, and tests.
Docstring Coverage ✅ Passed Docstring coverage is 94.22% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title follows Conventional Commits format, uses an allowed type and lowercase scope, summarizes the OCI provider change, and is under 72 characters.
Description check ✅ Passed The description includes all required sections, explains the complete OCI GenAI implementation, identifies review starting points, and links related issues.
✨ Finishing Touches
🧪 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Comment thread crates/core/src/codec/oci_genai.rs Outdated
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>
@willkill07

Copy link
Copy Markdown
Member

/ok to test 3573bc2

@willkill07

Copy link
Copy Markdown
Member

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

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

I think this is my last round of changes

Comment thread crates/core/src/codec/oci_genai.rs Outdated
Comment thread crates/core/src/plugins/nemo_guardrails/python.rs
Comment thread crates/core/src/codec/resolve.rs
Comment thread crates/pii-redaction/src/component.rs
- Use set-or-remove semantics in the OCI patch_params so an intercept
  clearing temperature, topP, maxTokens, or stop removes the provider
  field, matching the other provider codecs; cover clears in tests
- Suppress the terminal COHERE full-text snapshot in the local
  guardrails stream extractor so output rails do not evaluate doubled
  text, mirroring the OCI streaming collector deduplication
- Add oci_genai to the Node LlmCodecIdentity builtin union
- Refresh stale codec-support lists: pii-redaction config table and
  README, NeMo Guardrails support table and codec boundary, middleware
  concepts, and native dynamic plugin docs

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@fede-kamel
fede-kamel requested a review from a team as a code owner August 11, 2026 18:40
@willkill07

Copy link
Copy Markdown
Member

/ok to test 895842a

@willkill07

Copy link
Copy Markdown
Member

@fede-kamel can you please update:

@willkill07

Copy link
Copy Markdown
Member

/ok to test 98b4c75

@willkill07

willkill07 commented Aug 12, 2026

Copy link
Copy Markdown
Member

@fede-kamel I also notice this PR doesn't update other areas of the codebase related to plugins: https://github.com/search?q=repo%3ANVIDIA%2FNeMo-Relay+BuiltinLlmCodec&type=code

  • crates/worker
  • crates/plugin

- Add the OCIGenAI variant to the core BuiltinLlmCodec enum and implement
  codec_identity on OCIGenAIChatCodec so sanitize contexts report the
  builtin oci_genai identity instead of opaque
- Map oci_genai in the worker SDK proto decoding and in the native plugin
  SDK enum and async invocation context, matching the other built-ins
- Route the BuiltIn(OCIGenAI) identity to the OCI provider surface in the
  pii-redaction normalized sanitize path
- Document the OCI GenAI codec in the provider codec guides: built-in
  codec lists, per-binding import table, usage normalization table, and
  the gateway route note

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

Copy link
Copy Markdown
Contributor Author

@willkill07 both follow-ups are covered in c2048f2:

Docsprovider-codecs.mdx now lists OCIGenAIChatCodec (bullet list, the Python/Node import table, the api_specific provider list, and the gateway-route note), and using-codecs.mdx includes OCI Generative AI chat in the provider-codec table. I also updated provider-response-codecs.mdx, which had the same staleness: the built-in response codec list and the usage normalization table (OCI columns: promptTokens / completionTokens / totalTokens / promptTokensDetails.cachedTokens).

BuiltinLlmCodec in crates/worker and crates/plugin — the gap ran deeper than the two SDK enums. Core's own BuiltinLlmCodec had no OCI variant and OCIGenAIChatCodec never overrode codec_identity(), so it reported Opaque through every surface. The commit adds the variant end to end:

  • core: BuiltinLlmCodec::OCIGenAI + id() mapping, codec_identity() on both LlmCodec and LlmResponseCodec impls
  • crates/worker: enum variant + oci_genai proto decoding
  • crates/plugin: serde-renamed enum variant + the async SDK invocation-context mapping
  • crates/pii-redaction: BuiltIn(OCIGenAI) now routes to the OCI provider surface in the normalized sanitize path

New tests cover each mapping: codec-identity tests for both trait impls, OCI rows in the request/response sanitizer context state tests, worker proto decoding, a plugin async-SDK context test (fails with unknown built-in LLM codec without the fix), and a pii-redaction test showing the builtin identity redacts an OCI GENERIC payload end to end. Python and Node needed no changes — their identity surfaces already listed oci_genai and pass identities through as strings.

Validated locally: full test suites for core, worker, plugin, and pii-redaction, clippy with -D warnings, rustfmt, pre-commit on all changed files, and just docs.

@willkill07

Copy link
Copy Markdown
Member

/ok to test a9492c8

@willkill07

Copy link
Copy Markdown
Member

The sanitize context's retained codec capability is released on the SDK
executor thread after the result completion is delivered, so the
immediate live_host_strings() == 0 assertion raced the release under
nextest on CI. Poll with a bounded deadline instead, matching the
wait_for_release idiom used elsewhere in this suite.

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

Copy link
Copy Markdown
Contributor Author

The Rust / Test (linux-amd64) failure on c2048f2 was a race in the new typed_async_llm_sanitize_context_decodes_oci_genai_builtin_identity test, not a runtime issue: the sanitize context's retained codec capability is released on the SDK executor thread after the result completion is delivered, so the immediate live_host_strings() == 0 assertion could observe the string before that release under nextest. e88e4e4 polls with a bounded deadline instead, matching the wait_for_release idiom used elsewhere in the suite. Verified 30 consecutive runs plus a nextest run locally.

@willkill07

Copy link
Copy Markdown
Member

/ok to test e88e4e4

@willkill07

Copy link
Copy Markdown
Member

/merge

@rapids-bot
rapids-bot Bot merged commit 22d0f49 into NVIDIA:main Aug 12, 2026
94 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Aug 12, 2026
2 tasks
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:XXL PR is very 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