fix: Improved LangChain serialization - #165
Conversation
Signed-off-by: David Gardner <dagardner@nvidia.com>
…utes are present Signed-off-by: David Gardner <dagardner@nvidia.com>
…ngChainModelRequestCodec Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
WalkthroughThis PR introduces a LangChainCodec for LangChain↔NeMo Relay translation, updates middleware to use the codec and accept immutable LLMRequest objects, revises Python examples to return new LLMRequest instances when modifying headers/annotated fields, and expands tests to cover codec round-trips and interceptor behavior. ChangesLangChain Integration with Request Immutability
sequenceDiagram
participant LangChainApp
participant NemoRelayMiddleware
participant LangChainCodec
participant Interceptor
participant ModelHandler
LangChainApp->>NemoRelayMiddleware: wrap_model_call(model)
NemoRelayMiddleware->>NemoRelayMiddleware: _prepare_model_call(codec=LangChainCodec())
LangChainApp->>NemoRelayMiddleware: _call(llm_request)
NemoRelayMiddleware->>LangChainCodec: decode(model_request)
LangChainCodec->>NemoRelayMiddleware: AnnotatedLLMRequest
NemoRelayMiddleware->>Interceptor: apply_llm_request_intercepts(annotated)
Interceptor->>NemoRelayMiddleware: return modified/new LLMRequest
NemoRelayMiddleware->>ModelHandler: handler(updated_payload)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
…-LC-serialization Signed-off-by: David Gardner <dagardner@nvidia.com>
There was a problem hiding this comment.
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 `@python/nemo_relay/integrations/langchain/_serialization.py`:
- Around line 240-249: The code aliases the incoming model_settings dict causing
mutations to the original ModelRequest when headers are merged; in
model_request_to_payload() make a copy of model_settings before assigning to
overrides (e.g., overrides["model_settings"] = model_settings.copy() or deepcopy
for nested structures) and also create extra_headers as a copy (extra_headers =
overrides["model_settings"].get("extra_headers", {}).copy()) before updating
with llm_request.headers so you never modify llm_request or its model_settings
in-place.
- Around line 107-141: _langchain_message_to_annotated flattens and discards
LangChain structured/multimodal content blocks (and
_annotated_message_to_langchain only reconstructs simple fields), causing loss
of content_blocks/typed metadata; change both functions to preserve and
round-trip BaseMessage.content_blocks (and any non-text block fields) by
encoding the entire message.data or content_blocks into the annotated dict
(e.g., include a "data" or "content_blocks" entry when present) and when
rebuilding in _annotated_message_to_langchain use that field to populate
BaseMessage.data/content_blocks instead of only
role/content/name/tool_calls/tool_call_id so that structured blocks (type,
image_url, etc.) survive the serialization round-trip.
🪄 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: 2cb62b6d-d00c-4b49-8b98-aaf4c6cf66e1
📒 Files selected for processing (9)
docs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdxdocs/integrate-into-frameworks/provider-codecs.mdxpython/nemo_relay/__init__.pypython/nemo_relay/integrations/langchain/_serialization.pypython/nemo_relay/integrations/langchain/middleware.pypython/nemo_relay/intercepts.pypython/tests/integrations/deepagents_tests/test_deepagents_integration.pypython/tests/integrations/langchain_tests/test_middleware.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (22)
{docs/**,README.md,CONTRIBUTING.md}
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
{docs/**,README.md,CONTRIBUTING.md}: For docs-only changes, run targeted checks only if commands, package names, or examples changed. Usejust docsfor docs-site builds andjust docs-linkcheckwhen links changed
Run docs site build withjust docs
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdx
{docs/**,README.md,CONTRIBUTING.md,**/*.md}
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
Run docs link validation with
just docs-linkcheckwhen links change
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdx
{docs/**,README.md}
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
Verify README and docs entry points still match current package names and paths for large or public-facing changes
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdx
{docs/**,examples/**,README.md}
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
Verify examples still run with documented commands for large or public-facing changes
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdx
{docs/**,README.md,**/Cargo.toml,**/package.json,**/*.md}
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
Ensure renamed public surfaces are reflected consistently in manifests and docs for large or public-facing changes
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdx
**/*.{md,mdx,py,sh,yaml,yml,toml,json}
📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)
Keep package names, repo references, and build commands current
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxpython/nemo_relay/__init__.pydocs/build-plugins/register-behavior.mdxpython/nemo_relay/intercepts.pypython/tests/integrations/deepagents_tests/test_deepagents_integration.pypython/nemo_relay/integrations/langchain/middleware.pypython/tests/integrations/langchain_tests/test_middleware.pypython/nemo_relay/integrations/langchain/_serialization.py
**/*.mdx
📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)
In MDX files, top-of-file comments must use JSX comment delimiters: {/* to open and */} to close. Do not use HTML comments for MDX SPDX headers.
MDX top-of-file SPDX comments must use {/* ... */} delimiters instead of HTML comment delimiters (Must-Fix)
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdx
**/*.{html,md,mdx}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Include SPDX license header in HTML and Markdown files using HTML comment syntax
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdx
docs/**/*.{md,mdx}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Update embedded documentation snippets, patch docs, and binding-support notes if examples or supported bindings changed
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdx
docs/**
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Run
just docsor./scripts/build-docs.sh htmlto regenerate ignored Fern API reference pages before validation for documentation site changes
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdx
{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}
⚙️ CodeRabbit configuration file
{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}: Review documentation for technical accuracy against the current API, command correctness, and consistency across language bindings.
Flag stale examples, missing SPDX headers where required, and instructions that no longer match CI or pre-commit behavior.
Files:
docs/integrate-into-frameworks/provider-codecs.mdxdocs/build-plugins/code-examples.mdxdocs/build-plugins/register-behavior.mdx
{crates/python/src/py_api/**/*.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi}
📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)
Update Python native binding in
crates/python/src/py_api/mod.rswith Python wrapper docstring inpython/nemo_relay/<module>.pyand type stubs inpython/nemo_relay/*.pyimodules
Files:
python/nemo_relay/__init__.pypython/nemo_relay/intercepts.pypython/nemo_relay/integrations/langchain/middleware.pypython/nemo_relay/integrations/langchain/_serialization.py
python/nemo_relay/**/*.py
📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)
Use
snake_casenaming convention for Python identifiers (e.g.,nemo_relay.tools.call)Format changed Python wrapper and test files with
uv run ruff format python
Files:
python/nemo_relay/__init__.pypython/nemo_relay/intercepts.pypython/nemo_relay/integrations/langchain/middleware.pypython/nemo_relay/integrations/langchain/_serialization.py
{pyproject.toml,**/*.py}
📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)
Maintain consistency between Python package names in
pyproject.tomland import paths used throughout the codebase
Files:
python/nemo_relay/__init__.pypython/nemo_relay/intercepts.pypython/tests/integrations/deepagents_tests/test_deepagents_integration.pypython/nemo_relay/integrations/langchain/middleware.pypython/tests/integrations/langchain_tests/test_middleware.pypython/nemo_relay/integrations/langchain/_serialization.py
**/*.{py,txt,toml,cfg,yaml,yml}
📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)
Update Python package names and top-level module imports during coordinated rename operations
Files:
python/nemo_relay/__init__.pypython/nemo_relay/intercepts.pypython/tests/integrations/deepagents_tests/test_deepagents_integration.pypython/nemo_relay/integrations/langchain/middleware.pypython/tests/integrations/langchain_tests/test_middleware.pypython/nemo_relay/integrations/langchain/_serialization.py
**/*.py
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
**/*.py: Run Python formatting withuv run ruff format python
Run Python testing withuv run pytest -k "<pattern>"
**/*.py: Keep SPDX headers on Python source files. The project is Apache-2.0.
Usesnake_casefor Python binding naming conventions.
Runjust test-pythonfor Python binding or wrapper changes.
**/*.py: Use Ruff with rule sets E, F, W, I for Python linting
Use Ruff formatter with line length 120 and double quotes for Python code formatting
Runtyfor Python type checking
Use Python snake_case naming convention for Python identifiers
Include SPDX license header in all Python source files using hash comment syntax
Validate Python code withuv run pre-commit run --all-filesto enforce Ruff linting and formatting, and ty type checking
Files:
python/nemo_relay/__init__.pypython/nemo_relay/intercepts.pypython/tests/integrations/deepagents_tests/test_deepagents_integration.pypython/nemo_relay/integrations/langchain/middleware.pypython/tests/integrations/langchain_tests/test_middleware.pypython/nemo_relay/integrations/langchain/_serialization.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/__init__.pypython/nemo_relay/intercepts.pypython/nemo_relay/integrations/langchain/middleware.pypython/nemo_relay/integrations/langchain/_serialization.py
**/test_*.{py,py}
📄 CodeRabbit inference engine (.agents/skills/add-integration/SKILL.md)
Relevant integration tests or smoke coverage must exist for the integration path
Files:
python/tests/integrations/deepagents_tests/test_deepagents_integration.pypython/tests/integrations/langchain_tests/test_middleware.py
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}
📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)
Maintain documented and tested validation and report behavior for adaptive surfaces
Files:
python/tests/integrations/deepagents_tests/test_deepagents_integration.pypython/tests/integrations/langchain_tests/test_middleware.py
python/**/*test*.py
📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)
python/**/*test*.py: Do not add@pytest.mark.asyncioto any test in Python test files
Do not add a-> Nonereturn type annotation to test functions
When mocking a class, useunittest.mock.MagicMockorunittest.mock.AsyncMockwith thespecconstructor argument when necessary, rather than defining a new class
Prefix mocked class names withmock, notfake
Prefer pytest fixtures over helper methods in Python tests
Preferpytest.mark.parametrizeover creating individual tests for different input types
Files:
python/tests/integrations/deepagents_tests/test_deepagents_integration.pypython/tests/integrations/langchain_tests/test_middleware.py
python/**/{conftest.py,*test*.py}
📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)
When creating a fixture follow the pattern:
@pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) def <fixture_name>_fixture() -> <return_type>:and only specify the scope argument when the value is something other than "function"
Files:
python/tests/integrations/deepagents_tests/test_deepagents_integration.pypython/tests/integrations/langchain_tests/test_middleware.py
{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:
python/tests/integrations/deepagents_tests/test_deepagents_integration.pypython/tests/integrations/langchain_tests/test_middleware.py
🪛 Ruff (0.15.14)
python/nemo_relay/integrations/langchain/middleware.py
[warning] 86-86: Dynamically typed expressions (typing.Any) are disallowed in _call
(ANN401)
[warning] 109-109: Dynamically typed expressions (typing.Any) are disallowed in _call
(ANN401)
python/tests/integrations/langchain_tests/test_middleware.py
[warning] 249-249: Boolean-typed positional argument in function definition
(FBT001)
[warning] 259-259: Missing return type annotation for private function change_request
(ANN202)
[warning] 259-259: Dynamically typed expressions (typing.Any) are disallowed in annotated
(ANN401)
[warning] 274-274: Boolean positional value in function call
(FBT003)
python/nemo_relay/integrations/langchain/_serialization.py
[warning] 69-69: Dynamically typed expressions (typing.Any) are disallowed in tool_calls
(ANN401)
[warning] 127-127: Prefer TypeError exception for invalid type
(TRY004)
[warning] 127-127: Avoid specifying long messages outside the exception class
(TRY003)
[warning] 158-158: Avoid specifying long messages outside the exception class
(TRY003)
🔇 Additional comments (7)
python/tests/integrations/deepagents_tests/test_deepagents_integration.py (1)
237-237: LGTM!docs/build-plugins/code-examples.mdx (1)
16-17: LGTM!Also applies to: 35-37
docs/build-plugins/register-behavior.mdx (1)
125-127: LGTM!docs/integrate-into-frameworks/provider-codecs.mdx (1)
92-94: LGTM!python/nemo_relay/__init__.py (1)
41-43: LGTM!python/nemo_relay/intercepts.py (1)
14-16: LGTM!Also applies to: 182-184
python/nemo_relay/integrations/langchain/_serialization.py (1)
182-185: ⚡ Quick winGuard
annotated.extrabefore merging it into the payload.
payload.update(annotated.extra)will throwTypeErrorifannotated.extraisNone(e.g.,dict.update(None)raisesTypeError: 'NoneType' object is not iterable), so add a guard before updating.Suggested fix
def encode(self, annotated: AnnotatedLLMRequest, original: LLMRequest) -> LLMRequest: """Encode annotated request edits back into a LangChain-shaped payload.""" payload = dict(original.content) - payload.update(annotated.extra) + if annotated.extra: + payload.update(annotated.extra) payload["messages"] = messages_to_dict( [self._annotated_message_to_langchain(message) for message in annotated.messages] )
willkill07
left a comment
There was a problem hiding this comment.
Coderabbit feedback seems sensible to address.
Otherwise, I checked out the branch and tested locally!
Conditionally approving. Due to low risk, I still think this can target 0.3
Signed-off-by: David Gardner <dagardner@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
python/nemo_relay/integrations/langchain/_serialization.py (1)
242-254:⚠️ Potential issue | 🟠 Major | ⚡ Quick winCopy
extra_headersbefore mutating it.
dict(model_settings)is only a shallow copy. If Line 244 returns an existingdict, Line 253 still updates the same nestedextra_headersobject held byoriginal.model_settings, so interceptor header injection is still mutating the original request.Suggested fix
if isinstance(model_settings, dict): # Using dict() to ensure we have a copy model_settings_copy = dict(model_settings) - extra_headers = model_settings_copy.get("extra_headers") - if not isinstance(extra_headers, dict): - extra_headers = {} + extra_headers_value = model_settings_copy.get("extra_headers") + extra_headers = dict(extra_headers_value) if isinstance(extra_headers_value, dict) else {} + model_settings_copy["extra_headers"] = extra_headers overrides["model_settings"] = model_settings_copy🤖 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 `@python/nemo_relay/integrations/langchain/_serialization.py` around lines 242 - 254, The code currently shallow-copies model_settings into model_settings_copy but then mutates the nested extra_headers in place; to avoid mutating the original request, make a defensive copy of extra_headers when it's a dict (e.g., replace extra_headers = extra_headers with extra_headers = dict(extra_headers)) before calling extra_headers.update(llm_request.headers), and then assign overrides["model_settings"]["extra_headers"] = extra_headers; update logic in the block that sets model_settings_copy, extra_headers, and the final merge with llm_request.headers to ensure only the copied objects are mutated.
🤖 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.
Duplicate comments:
In `@python/nemo_relay/integrations/langchain/_serialization.py`:
- Around line 242-254: The code currently shallow-copies model_settings into
model_settings_copy but then mutates the nested extra_headers in place; to avoid
mutating the original request, make a defensive copy of extra_headers when it's
a dict (e.g., replace extra_headers = extra_headers with extra_headers =
dict(extra_headers)) before calling extra_headers.update(llm_request.headers),
and then assign overrides["model_settings"]["extra_headers"] = extra_headers;
update logic in the block that sets model_settings_copy, extra_headers, and the
final merge with llm_request.headers to ensure only the copied objects are
mutated.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 0e338ef5-3294-4df1-bcdb-ec22ae54299d
📒 Files selected for processing (1)
python/nemo_relay/integrations/langchain/_serialization.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
{crates/python/src/py_api/**/*.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi}
📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)
Update Python native binding in
crates/python/src/py_api/mod.rswith Python wrapper docstring inpython/nemo_relay/<module>.pyand type stubs inpython/nemo_relay/*.pyimodules
Files:
python/nemo_relay/integrations/langchain/_serialization.py
python/nemo_relay/**/*.py
📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)
Use
snake_casenaming convention for Python identifiers (e.g.,nemo_relay.tools.call)Format changed Python wrapper and test files with
uv run ruff format python
Files:
python/nemo_relay/integrations/langchain/_serialization.py
{pyproject.toml,**/*.py}
📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)
Maintain consistency between Python package names in
pyproject.tomland import paths used throughout the codebase
Files:
python/nemo_relay/integrations/langchain/_serialization.py
**/*.{py,txt,toml,cfg,yaml,yml}
📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)
Update Python package names and top-level module imports during coordinated rename operations
Files:
python/nemo_relay/integrations/langchain/_serialization.py
**/*.py
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
**/*.py: Run Python formatting withuv run ruff format python
Run Python testing withuv run pytest -k "<pattern>"
**/*.py: Keep SPDX headers on Python source files. The project is Apache-2.0.
Usesnake_casefor Python binding naming conventions.
Runjust test-pythonfor Python binding or wrapper changes.
**/*.py: Use Ruff with rule sets E, F, W, I for Python linting
Use Ruff formatter with line length 120 and double quotes for Python code formatting
Runtyfor Python type checking
Use Python snake_case naming convention for Python identifiers
Include SPDX license header in all Python source files using hash comment syntax
Validate Python code withuv run pre-commit run --all-filesto enforce Ruff linting and formatting, and ty type checking
Files:
python/nemo_relay/integrations/langchain/_serialization.py
**/*.{md,mdx,py,sh,yaml,yml,toml,json}
📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)
Keep package names, repo references, and build commands current
Files:
python/nemo_relay/integrations/langchain/_serialization.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/integrations/langchain/_serialization.py
|
/merge |
Overview
Fix LangChain serialization, by adding a LangChain specific codec
Details
LLMRequestand the immutability ofAnnotatedLLMRequest(I need to go back and updagte this for the other language bindings)Where should the reviewer start?
python/nemo_relay/integrations/langchain/_serialization.pyRelated Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)
Summary by CodeRabbit
Documentation
Integrations
Tests