Skip to content

feat: add LangGraph example workflows - #184

Closed
zhongxuanwang-nv wants to merge 1 commit into
NVIDIA:mainfrom
zhongxuanwang-nv:feat/langgraph-examples
Closed

feat: add LangGraph example workflows#184
zhongxuanwang-nv wants to merge 1 commit into
NVIDIA:mainfrom
zhongxuanwang-nv:feat/langgraph-examples

Conversation

@zhongxuanwang-nv

@zhongxuanwang-nv zhongxuanwang-nv commented Aug 6, 2026

Copy link
Copy Markdown
Member

Overview

Adds two runnable native LangGraph examples: a per-user calculator with an MCP math server and a structured email-phishing analyzer. These examples run directly with LangGraph. NVIDIA NeMo Fabric does not yet ship a LangGraph adapter, and the example YAML files are not FabricConfig input.

Details

  • Adds validated source configuration and a local streamable-HTTP MCP math server.
  • Adds an isolated per-user ReAct graph and a purpose-built structured-output phishing graph.
  • Documents the adapter boundaries required before these workflows can run through Fabric.
  • No published Fabric API, adapter descriptor, or package dependency changes.

Validation

  • .venv/bin/python -m pytest tests/examples/test_langgraph_examples.py
  • .venv/bin/pre-commit run ruff-check --files examples/langgraph/init.py examples/langgraph/config.py examples/langgraph/calculator_mcp.py examples/langgraph/email_phishing_analyzer.py examples/langgraph/mcp_math_server.py tests/examples/test_langgraph_examples.py
  • .venv/bin/pre-commit run copyright-header --files examples/README.md examples/langgraph/README.md examples/langgraph/init.py examples/langgraph/config.py examples/langgraph/calculator_mcp.py examples/langgraph/email_phishing_analyzer.py examples/langgraph/mcp_math_server.py examples/langgraph/configs/calculator_mcp.yaml examples/langgraph/configs/email_phishing_analyzer.yaml tests/examples/test_langgraph_examples.py
  • Live NIM and MCP checks: the calculator returned 63 for 9 × 7; the phishing analyzer returned a structured phishing assessment.
  • Not run: just test-python, because uv is unavailable in this local environment.

Where should the reviewer start?

Start with examples/langgraph/README.md for the support boundary and adapter requirements, then review the two graph factories and their focused offline tests.

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

  • Relates to FABRIC-166

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

Summary by CodeRabbit

  • New Features
    • Added LangGraph examples for per-user calculator workflows and email phishing analysis.
    • Added a standalone MCP math server with arithmetic tools.
    • Added configurable NVIDIA NIM models, MCP servers, workflows, retries, and local tools.
    • Added structured JSON phishing assessments and conversation persistence by user.
  • Documentation
    • Added setup, execution, offline validation, and adapter guidance for the examples.
  • Tests
    • Added offline coverage for calculator isolation, configuration, timezone handling, and phishing-analysis output.

Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Added shared configuration and two native LangGraph examples: a per-user calculator workflow with MCP tools and a structured email-phishing analyzer. Added MCP server support, CLI entry points, documentation, and offline tests.

Changes

Native LangGraph examples

Layer / File(s) Summary
Configuration contracts and workflow settings
examples/langgraph/config.py, examples/langgraph/configs/*
Added validated Pydantic configuration, YAML loading, NVIDIA NIM model construction, and workflow settings for both examples.
Per-user calculator and MCP workflow
examples/langgraph/mcp_math_server.py, examples/langgraph/calculator_mcp.py
Added an HTTP MCP calculator server and a per-user ReAct agent with isolated clients, graphs, checkpoints, tool selection, and CLI execution.
Structured phishing analysis and validation
examples/langgraph/email_phishing_analyzer.py, tests/examples/test_langgraph_examples.py
Added a structured phishing assessment graph, JSON output, CLI execution, and offline tests with mocked models and clients.
Example package and adapter documentation
examples/langgraph/__init__.py, examples/langgraph/README.md, examples/README.md
Documented setup, execution, validation, workflow differences, and requirements for a complete Fabric adapter.

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

Sequence Diagram(s)

sequenceDiagram
  participant CalculatorCLI
  participant PerUserReactAgent
  participant MCPMathServer
  participant NIMChatModel
  CalculatorCLI->>PerUserReactAgent: Invoke with user ID and message
  PerUserReactAgent->>MCPMathServer: Load configured calculator tools
  PerUserReactAgent->>NIMChatModel: Execute ReAct graph
  NIMChatModel->>MCPMathServer: Call calculator tool
  PerUserReactAgent-->>CalculatorCLI: Return final message
Loading
sequenceDiagram
  participant AnalyzerCLI
  participant EmailPhishingGraph
  participant NIMChatModel
  participant PhishingAssessment
  AnalyzerCLI->>EmailPhishingGraph: Submit email content
  EmailPhishingGraph->>NIMChatModel: Analyze phishing indicators
  NIMChatModel-->>EmailPhishingGraph: Return structured assessment
  EmailPhishingGraph->>PhishingAssessment: Validate and serialize result
  EmailPhishingGraph-->>AnalyzerCLI: Print JSON assessment
Loading

Possibly related PRs

  • NVIDIA/NeMo-Fabric#183: Adds closely related LangGraph examples, configuration classes, workflows, and tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title uses valid Conventional Commits syntax, clearly describes the LangGraph workflow examples, and meets the length and punctuation requirements.
Description check ✅ Passed The description includes the required overview, reviewer starting point, related issue keyword, validation details, and completed contribution checkboxes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/langgraph/calculator_mcp.py`:
- Around line 101-108: Make the exposed retry settings functional or remove
them: in examples/langgraph/calculator_mcp.py lines 101-108, update ainvoke to
read and apply retry_parsing_errors and max_retries around calculator
invocation; in examples/langgraph/email_phishing_analyzer.py lines 56-66, apply
the same settings around structured-model invocation. Ensure both workflows
either honor the configured retry behavior or remove the unsupported
configuration keys.
- Around line 81-99: The graph_for initialization path can create duplicate
graphs for the same user under concurrent calls. Add per-user synchronization
around the cache-miss initialization, recheck _sessions after acquiring the
lock, and cache only the single successfully created graph; clear or remove any
failed initialization state so later calls can retry. Add a concurrent
regression test covering simultaneous graph_for calls for one user.

In `@examples/langgraph/config.py`:
- Around line 6-7: Update the first product reference in
examples/langgraph/config.py lines 6-7 to use “NVIDIA NeMo Fabric,” and rename
the advertised server in examples/langgraph/mcp_math_server.py line 16 to
“NVIDIA NeMo Fabric calculator example.”

In `@examples/langgraph/README.md`:
- Line 79: Update the product references in the README passages around
“following additional work,” including the corresponding references later in the
document, to use “NeMo Fabric” after the full introduction at line 16. Replace
any standalone capitalized “Fabric” used as the product name, while preserving
the existing wording otherwise.
- Around line 46-47: Update the README text describing user ID reuse to state
that conversation resumption is process-local and works only while the same
PerUserReactAgent remains alive; clarify that each CLI invocation creates a new
agent, _sessions, and InMemorySaver, so a new process has no prior state. Do not
imply cross-process resumption unless durable checkpointing is implemented
first.
- Line 34: Quote the <your-api-key> placeholder in both NVIDIA_API_KEY export
commands in the README so the shell treats it as a literal value; update each
command to use the same quoted placeholder syntax.

In `@examples/README.md`:
- Line 40: Update the “LangGraph examples” heading in the examples README to use
title case: “LangGraph Examples.”

In `@tests/examples/test_langgraph_examples.py`:
- Around line 23-101: Extend the tests around load_config and PerUserReactAgent
to cover unknown llm_name, unknown tool source, blank user_id, and missing
configured MCP tools, asserting each raises the intended error. Add a concurrent
same-user graph_for test that verifies synchronization returns the same cached
graph and avoids duplicate model/client creation, and include lifecycle cleanup
assertions where the changed public API requires it.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: a7126ed0-ce40-411c-8e6a-5cae977431d9

📥 Commits

Reviewing files that changed from the base of the PR and between 7a3272d and 8b94df7.

📒 Files selected for processing (10)
  • examples/README.md
  • examples/langgraph/README.md
  • examples/langgraph/__init__.py
  • examples/langgraph/calculator_mcp.py
  • examples/langgraph/config.py
  • examples/langgraph/configs/calculator_mcp.yaml
  • examples/langgraph/configs/email_phishing_analyzer.yaml
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/mcp_math_server.py
  • tests/examples/test_langgraph_examples.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (25)
**/*.{rs,py,pyi,json,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/contribute-api/SKILL.md)

Determine and update every affected public surface, including the CLI, PyO3 bindings, Python SDK, type stubs, schemas, and adapter contract, so they remain in parity.

Files:

  • examples/langgraph/__init__.py
  • examples/langgraph/configs/calculator_mcp.yaml
  • tests/examples/test_langgraph_examples.py
  • examples/langgraph/mcp_math_server.py
  • examples/langgraph/configs/email_phishing_analyzer.yaml
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
**/*

📄 CodeRabbit inference engine (.agents/skills/karpathy-guidelines/SKILL.md)

**/*: Before implementing, explicitly state assumptions, surface ambiguity and tradeoffs, present multiple interpretations when relevant, and ask for clarification rather than silently deciding or proceeding when requirements are unclear.
Prefer the minimum code needed to solve the requested problem: avoid speculative features, unnecessary abstractions, unrequested flexibility, and handling of impossible scenarios; simplify overcomplicated solutions.
When editing existing code, make surgical changes only: do not modify unrelated code, comments, formatting, or pre-existing dead code; match the existing style, and remove only unused imports, variables, or functions introduced by your changes.
Define verifiable success criteria for each task, such as writing regression tests for bugs and invalid-input tests for validation, then verify the implementation against those criteria. For multi-step work, state a brief plan with a verification check for each step.

**/*: Always spell NVIDIA in all caps; do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun, because the name begins with an “en” sound.
Do not add a registered trademark symbol after NVIDIA when referring to the company; use trademark symbols with product names only when required by the document type or legal guidance.
Verify official capitalization, spacing, hyphenation, and spelling for NVIDIA and third-party product names; do not rewrite official product names for grammar or title-case rules.
Precede NVIDIA product names with NVIDIA on first mention when natural and accurate, and link the first mention when the destination helps the reader.
On first use, include the company name and full model qualifier when it helps identify the model; preserve official capitalization and punctuation, and use shorter family names only after establishing the full name.
For learning-oriented and developer content, do not force trademark symbols unless explicitly required; for press, ...

Files:

  • examples/langgraph/__init__.py
  • examples/langgraph/configs/calculator_mcp.yaml
  • tests/examples/test_langgraph_examples.py
  • examples/langgraph/mcp_math_server.py
  • examples/README.md
  • examples/langgraph/configs/email_phishing_analyzer.yaml
  • examples/langgraph/README.md
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
**/*.{rs,py}

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

For native binding changes, run cargo check -p fabric-python --locked.

Use snake_case for functions and variables; use PascalCase for Rust types and Python classes.

Files:

  • examples/langgraph/__init__.py
  • tests/examples/test_langgraph_examples.py
  • examples/langgraph/mcp_math_server.py
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
**/*.{py,pyi}

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

If Python code or a Python-facing adapter changes, run just test-python.

In Python SDK, adapters, examples, and tests, follow the existing style, use type annotations for public APIs, and keep native binding declarations synchronized with their Rust implementations.

Files:

  • examples/langgraph/__init__.py
  • tests/examples/test_langgraph_examples.py
  • examples/langgraph/mcp_math_server.py
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
**/*.{rs,py,pyi}

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

**/*.{rs,py,pyi}: If public configuration types change, confirm schema snapshot tests in just test-rust pass and review generated schema diffs.
For schema or public contract changes, run both language suites and review changes under schemas/ and generated API references.

Files:

  • examples/langgraph/__init__.py
  • tests/examples/test_langgraph_examples.py
  • examples/langgraph/mcp_math_server.py
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
**/*.{py,pyi,rs}

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

For Python SDK or PyO3 binding changes, use python-tests, run focused pytest tests first, then just test-python; rebuild with just build-python when native code or packaging changes.

Public contract changes must keep native Python binding declarations synchronized with their Rust implementations.

Files:

  • examples/langgraph/__init__.py
  • tests/examples/test_langgraph_examples.py
  • examples/langgraph/mcp_math_server.py
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
**/*.{rs,py,toml}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

When editing version helpers, verify every nemo-fabric-* workspace package through Cargo metadata and reject a static version in python/pyproject.toml.

Files:

  • examples/langgraph/__init__.py
  • tests/examples/test_langgraph_examples.py
  • examples/langgraph/mcp_math_server.py
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
**/*.{toml,rs,py}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

Avoid blind repository-wide replacement of version-like strings; distinguish package-version references from examples and unrelated dependency versions.

Files:

  • examples/langgraph/__init__.py
  • tests/examples/test_langgraph_examples.py
  • examples/langgraph/mcp_math_server.py
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
examples/{README.md,**/*}

📄 CodeRabbit inference engine (AGENTS.md)

Update examples documentation and relevant examples when public behavior, the package, or supported bindings change.

Files:

  • examples/langgraph/__init__.py
  • examples/langgraph/configs/calculator_mcp.yaml
  • examples/langgraph/mcp_math_server.py
  • examples/README.md
  • examples/langgraph/configs/email_phishing_analyzer.yaml
  • examples/langgraph/README.md
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
**/*.{md,mdx,yml,py,rs,sh}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

Keep documentation aligned with current NeMo Fabric behavior, repository layout, entry points, commands, package names, APIs, bindings, and support claims.

Files:

  • examples/langgraph/__init__.py
  • tests/examples/test_langgraph_examples.py
  • examples/langgraph/mcp_math_server.py
  • examples/README.md
  • examples/langgraph/README.md
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
{adapters/**,examples/**}

⚙️ CodeRabbit configuration file

{adapters/**,examples/**}: Review adapter and example changes for command correctness, config/schema consistency, artifact handling, and compatibility with the public NeMo Fabric contracts.

Files:

  • examples/langgraph/__init__.py
  • examples/langgraph/configs/calculator_mcp.yaml
  • examples/langgraph/mcp_math_server.py
  • examples/README.md
  • examples/langgraph/configs/email_phishing_analyzer.yaml
  • examples/langgraph/README.md
  • examples/langgraph/config.py
  • examples/langgraph/email_phishing_analyzer.py
  • examples/langgraph/calculator_mcp.py
**/*.{yml,yaml,toml,lock}

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

For CI or packaging changes, use maintain-ci or maintain-packaging, then run recipes and checks whose behavior changed.

Files:

  • examples/langgraph/configs/calculator_mcp.yaml
  • examples/langgraph/configs/email_phishing_analyzer.yaml
**/*.{toml,yaml,yml,sh,bash}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

TOML, YAML, and shell files must use the specified # SPDX copyright and Apache-2.0 license headers.

Files:

  • examples/langgraph/configs/calculator_mcp.yaml
  • examples/langgraph/configs/email_phishing_analyzer.yaml
tests/**/*.{rs,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When adding functionality, include tests in the corresponding Rust crate or the relevant area under tests/.

Files:

  • tests/examples/test_langgraph_examples.py
tests/**/*.py

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

tests/**/*.py: Use pytest to run Python tests.
Do not add @pytest.mark.asyncio to tests; async tests are automatically detected by the async runner.
Do not add -> None return type annotations to test functions.
When mocking a class, use unittest.mock.MagicMock or AsyncMock, using the spec argument when necessary, rather than defining a new class.
Prefix mocked class names with mock, not fake.
Prefer pytest fixtures over helper methods.
If a fixture is needed in multiple test files, define it once in conftest.py rather than repeating it.
Define fixtures using @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and a <fixture_name>_fixture function; specify scope only when it is not function.
Prefer pytest.mark.parametrize over separate tests for different input types.
Use @pytest.mark.usefixtures when a fixture is needed but its returned value is unused or it returns no value.
Avoid defensive programming in tests; access expected values directly so missing data raises a clear failure, such as using results["data"] instead of results.get("data").
When adapter installation metadata changes, packaging metadata tests must directly assert that the root project depends unconditionally on the exact-version nemo-fabric-runtime distribution.
Packaging metadata tests must verify that each root harness extra delegates to the matching version of the leaf adapter's harness extra.
Packaging metadata tests must verify that bare leaf dependencies remain adapter-owned and that the root adapter-tests dependency group installs each leaf through its harness extra.
Packaging metadata tests must verify that every leaf provides full; only adapters importing NeMo Relay Python APIs provide relay, while adapters using an external Relay executable have full equal to harness.

Files:

  • tests/examples/test_langgraph_examples.py
{tests/**,python/tests/**}

⚙️ CodeRabbit configuration file

{tests/**,python/tests/**}: Tests should cover the behavior promised by the changed API surface, including error paths, lifecycle cleanup, and SDK/native parity where relevant.

Files:

  • tests/examples/test_langgraph_examples.py
**/*.{md,rst}

📄 CodeRabbit inference engine (.agents/skills/contribute-api/SKILL.md)

Update documentation and examples in the same branch as the public API change.

Files:

  • examples/README.md
  • examples/langgraph/README.md
{README.md,docs/**/*.{md,mdx,yml},examples/**/*.{md,mdx,yml}}

📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)

Keep package names, repository references, and build commands current in documentation and examples.

Files:

  • examples/README.md
  • examples/langgraph/README.md
{docs/**/*.{md,mdx,yml},examples/**/*.{md,mdx,yml}}

📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)

Update relevant getting-started, reference, adapter, and example documentation when the corresponding examples or adapters change.

Files:

  • examples/README.md
  • examples/langgraph/README.md
**/*.{md,mdx,rst}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-guide.md)

**/*.{md,mdx,rst}: For NeMo Fabric documentation, verify technical claims against the current repository, public API, or documented command before reviewing style.
Always spell NVIDIA in all caps; do not use Nvidia, nvidia, or NV.
Format commands, code elements, expressions, package names, file names, and paths as inline code.
Use descriptive link text; avoid raw URLs and weak anchors such as here or read more.
Use title case consistently for technical documentation headings.
Introduce code blocks, lists, tables, and images with complete sentences.
Write procedures as imperative, parallel steps; split long procedures into smaller tasks.
Prefer active voice, present tense, short sentences, contractions, and plain English while preserving necessary technical precision.
Use can for possibility and reserve may for permission.
Use after for temporal relationships instead of once, and prefer refer to over see when directing readers to another resource.
Avoid culture-specific idioms, unnecessary Latinisms, jokes, and marketing exaggeration in technical documentation.
Spell out months in body text, avoid ordinal dates, and use clear time zones.
Spell out whole numbers from zero through nine unless they are technical values, parameters, versions, or UI values; use numerals for 10 or greater and commas in thousands.
Do not add trademark symbols to learning-oriented documentation unless the source, platform, or legal guidance explicitly requires them.
Do not replace precise technical terms with simpler words when doing so would lose precision.
Do not flag passive voice when the actor is unknown or the action is the important part.
Do not rewrite API names, package names, command flags, or code literals for style.

**/*.{md,mdx,rst}: Use consistent title case for technical-document headings and table headers; avoid quotation marks, ampersands, and exclamation marks in headings, while preserving official product, event, research, and whitepaper title ...

Files:

  • examples/README.md
  • examples/langgraph/README.md
**/*.{md,rst,txt,adoc}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-language-mechanics.md)

**/*.{md,rst,txt,adoc}: For technical documentation, use professional, active, conversational, engaging, precise, and plain-English prose. Prefer active voice, present tense, short sentences, and scannable paragraphs. Avoid casual or imprecise language, swearing, threats, insults, jokes, puns, culture-specific idioms, marketing exaggeration, and unsupported third-party comparisons.
Use can for possibility and reserve may for permission; use after for temporal order; use refer to for cross-references; prefer short direct sentences and specific verbs; avoid unnecessary please in technical documentation.
Prefer active voice when the actor matters. Passive voice is acceptable when the actor is unknown or irrelevant, when the action or result is the focus, or in programmer documentation.
Use natural contractions in conversational technical prose, but do not force them in formal legal copy, API references, or generated text.
Prefer simpler English over Latinisms: use for example or such as instead of e.g., and so on instead of etc., that is instead of i.e., compared to instead of vs., and by, through, or using instead of via. Use industry-standard terms such as in silico, in vitro, and in vivo when appropriate, and italicize them in running text.
Use that without commas for essential clauses, and which with commas for nonessential clauses.
Format dates and times clearly: spell out months in body text; use forms such as June 12, 2025; avoid numeric or ordinal dates; capitalize days; use 12-hour time when appropriate; include a space before a.m. or p.m.; use ET and PT for needed time zones; avoid 24/7; and prefer from 12:30 to 1:00 p.m. for prose ranges.
Format numbers consistently: spell out zero through nine in body text, use numerals for 10 or greater and for technical values, use commas in thousands, do not begin a sentence with a numeral, spell out ordinals, and use numerals consistently within a category wh...

Files:

  • examples/README.md
  • examples/langgraph/README.md
**/*.{md,mdx}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

**/*.{md,mdx}: Use the full product name NVIDIA NeMo Fabric on first use, typically in the title and H1; use NeMo Fabric thereafter. Use fabric alone only for the CLI tool and surround it with backticks.
Treat incorrect or stale commands, package names, paths, APIs, support claims, procedures, examples, terminology, or public behavior documentation as blocking issues.
Capitalize NVIDIA correctly and format code, commands, paths, and filenames as inline code where needed.
Use title case for technical-documentation headings.
Introduce code blocks, tables, and lists with complete lead-in sentences; ensure examples match current APIs and build commands.
Use descriptive anchor text, avoid raw URLs and generic labels such as here, and use repository-relative .mdx paths for links within docs/.
Prefer active voice, present tense, short sentences, plain English, consistent terminology, and imperative, parallel, scannable procedures.
Use after instead of once when expressing temporal sequence, and use can rather than may when describing possibility rather than permission.
Avoid ambiguous numeric dates and ordinal dates in body text.
For learning-oriented documentation, do not force trademark symbols unless the source document explicitly requires them.
When reporting documentation-review findings, lead with Must fix, Should fix, and Nice to have categories; include file path, line reference, current problem, rationale, and a concrete rewrite or direction.

Files:

  • examples/README.md
  • examples/langgraph/README.md
**/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update relevant SDK, API reference, adapter, example, integration, and support documentation when the corresponding public surface changes.

Files:

  • examples/README.md
  • examples/langgraph/README.md
**/*.{html,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

HTML and Markdown files must use the specified SPDX HTML-comment header.

Files:

  • examples/README.md
  • examples/langgraph/README.md
{*.md,**/*.md,**/*.mdx,**/*.ipynb}

⚙️ CodeRabbit configuration file

{*.md,**/*.md,**/*.mdx,**/*.ipynb}: Enforce the product name in user-facing prose: use "NVIDIA NeMo Fabric" on first use and "NeMo Fabric" thereafter. Flag standalone capitalized "Fabric" when it refers to the product. Do not flag the lowercase fabric CLI command, package/import/crate names, code identifiers, API symbols, configuration keys, file paths, or unrelated generic uses of the word.

Files:

  • examples/README.md
  • examples/langgraph/README.md
🪛 ast-grep (0.45.0)
examples/langgraph/email_phishing_analyzer.py

[info] 87-87: use jsonify instead of json.dumps for JSON output
Context: json.dumps(result["assessment"], indent=2)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🪛 Ruff (0.16.1)
examples/langgraph/mcp_math_server.py

[warning] 41-41: Avoid specifying long messages outside the exception class

(TRY003)

examples/langgraph/config.py

[warning] 77-77: Remove quotes from type annotation

Remove quotes

(UP037)


[warning] 80-80: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 86-86: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 110-110: Prefer TypeError exception for invalid type

(TRY004)


[warning] 110-110: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 119-121: Avoid specifying long messages outside the exception class

(TRY003)

examples/langgraph/email_phishing_analyzer.py

[warning] 50-50: Dynamically typed expressions (typing.Any) are disallowed in model

(ANN401)


[warning] 51-51: Dynamically typed expressions (typing.Any) are disallowed in build_email_phishing_analyzer

(ANN401)


[warning] 55-55: Avoid specifying long messages outside the exception class

(TRY003)

examples/langgraph/calculator_mcp.py

[warning] 64-64: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 66-66: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 68-68: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 76-76: Dynamically typed expressions (typing.Any) are disallowed in graph_for

(ANN401)


[warning] 80-80: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (4)
examples/langgraph/config.py (1)

17-22: 🩺 Stability & Availability

No dependency declaration issue.

The documented setup installs these dependencies through the adapters, adapter-tests, and test groups.

			> Likely an incorrect or invalid review comment.
examples/langgraph/__init__.py (1)

1-4: LGTM!

examples/langgraph/README.md (1)

1-18: LGTM!

Also applies to: 20-33, 35-45, 48-56, 58-78, 80-87, 89-94, 96-108

examples/README.md (1)

42-45: LGTM!

Comment on lines +81 to +99
graph = self._sessions.get(user_id)
if graph is not None:
return graph

server = self._config.mcp.servers["mcp_math"] # validated in __init__
client = self._mcp_client_factory(
{"mcp_math": mcp_connection(server)}, tool_name_prefix=False
)
mcp_tools = _selected_mcp_tools(list(await client.get_tools()), server)
model = self._model_factory(self._config.selected_model())
graph = self._graph_factory(
model,
[current_timezone, *mcp_tools],
checkpointer=InMemorySaver(),
debug=bool(self._config.workflow.settings.get("verbose", False)),
name="per_user_calculator",
)
self._sessions[user_id] = graph
return graph

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Synchronize first graph creation for each user.

Two concurrent graph_for(user_id) calls can both miss _sessions, await client.get_tools(), and create different graphs. The later call overwrites the cached graph. The earlier call then uses a separate checkpoint store for the same user.

Use a per-user initialization lock or shared initialization task. Recheck _sessions after acquiring the lock. Remove failed initialization state. Add a concurrent regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/langgraph/calculator_mcp.py` around lines 81 - 99, The graph_for
initialization path can create duplicate graphs for the same user under
concurrent calls. Add per-user synchronization around the cache-miss
initialization, recheck _sessions after acquiring the lock, and cache only the
single successfully created graph; clear or remove any failed initialization
state so later calls can retry. Add a concurrent regression test covering
simultaneous graph_for calls for one user.

Comment on lines +101 to +108
async def ainvoke(self, user_id: str, message: str) -> dict[str, Any]:
"""Run a message in the graph and persisted conversation for ``user_id``."""

graph = await self.graph_for(user_id)
return await graph.ainvoke(
{"messages": [{"role": "user", "content": message}]},
{"configurable": {"thread_id": user_id}},
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Make retry settings functional or remove them.

Both YAML configurations expose retry_parsing_errors and max_retries, but neither workflow consumes them. Users cannot obtain the configured retry behavior.

  • examples/langgraph/calculator_mcp.py#L101-L108: Read and apply the retry settings around calculator invocation, or remove the unsupported settings.
  • examples/langgraph/email_phishing_analyzer.py#L56-L66: Read and apply the retry settings around structured-model invocation, or remove the unsupported settings.
📍 Affects 2 files
  • examples/langgraph/calculator_mcp.py#L101-L108 (this comment)
  • examples/langgraph/email_phishing_analyzer.py#L56-L66
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/langgraph/calculator_mcp.py` around lines 101 - 108, Make the
exposed retry settings functional or remove them: in
examples/langgraph/calculator_mcp.py lines 101-108, update ainvoke to read and
apply retry_parsing_errors and max_retries around calculator invocation; in
examples/langgraph/email_phishing_analyzer.py lines 56-66, apply the same
settings around structured-model invocation. Ensure both workflows either honor
the configured retry behavior or remove the unsupported configuration keys.

Comment on lines +6 to +7
This is application configuration for the examples, not a NeMo Fabric adapter
descriptor or a supported Fabric configuration format.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use NVIDIA NeMo Fabric on first reference.

  • examples/langgraph/config.py#L6-L7: Change the first product reference to NVIDIA NeMo Fabric.
  • examples/langgraph/mcp_math_server.py#L16-L16: Change the advertised server name to NVIDIA NeMo Fabric calculator example.

As per coding guidelines, “Precede NVIDIA product names with NVIDIA on first mention when natural and accurate.”

📍 Affects 2 files
  • examples/langgraph/config.py#L6-L7 (this comment)
  • examples/langgraph/mcp_math_server.py#L16-L16
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/langgraph/config.py` around lines 6 - 7, Update the first product
reference in examples/langgraph/config.py lines 6-7 to use “NVIDIA NeMo Fabric,”
and rename the advertised server in examples/langgraph/mcp_math_server.py line
16 to “NVIDIA NeMo Fabric calculator example.”

Source: Coding guidelines

Set an NVIDIA API key and start the included MCP server in one terminal:

```bash
export NVIDIA_API_KEY=<your-api-key>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -u

file="examples/langgraph/README.md"
printf '%s\n' '--- relevant lines ---'
sed -n '29,60p' "$file"

printf '%s\n' '--- shell syntax checks ---'
for command in \
  'export NVIDIA_API_KEY=<your-api-key>' \
  'export NVIDIA_API_KEY="<your-api-key>"'
do
  if bash -n <(printf '%s\n' "$command") 2>/tmp/bash-syntax.err; then
    printf 'PASS: %s\n' "$command"
  else
    printf 'FAIL: %s\n' "$command"
    cat /tmp/bash-syntax.err
  fi
done

Repository: NVIDIA/NeMo-Fabric

Length of output: 1445


🏁 Script executed:

#!/bin/bash
set -u

for command in \
  'export NVIDIA_API_KEY=<your-api-key>' \
  'export NVIDIA_API_KEY="<your-api-key>"'
do
  err=$(mktemp)
  if printf '%s\n' "$command" | bash -n 2>"$err"; then
    printf 'PASS: %s\n' "$command"
  else
    printf 'FAIL: %s\n' "$command"
    cat "$err"
  fi
  rm -f "$err"
done

Repository: NVIDIA/NeMo-Fabric

Length of output: 356


Quote the API key placeholders in both shell commands.

export NVIDIA_API_KEY=<your-api-key> is invalid shell syntax because < is parsed as redirection. Use export NVIDIA_API_KEY="<your-api-key>" at lines 34 and 57.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/langgraph/README.md` at line 34, Quote the <your-api-key>
placeholder in both NVIDIA_API_KEY export commands in the README so the shell
treats it as a literal value; update each command to use the same quoted
placeholder syntax.

Source: Coding guidelines

Comment on lines +46 to +47
Each user ID creates a separate graph, `InMemorySaver`, and
`MultiServerMCPClient`. Reusing a user ID resumes only that user's conversation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline examples/langgraph/calculator_mcp.py --items all
rg -n -C 8 'PerUserReactAgent|InMemorySaver|asyncio\.run|def main|__main__|user-id' \
  examples/langgraph/calculator_mcp.py

Repository: NVIDIA/NeMo-Fabric

Length of output: 4476


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- README context ---'
cat -n examples/langgraph/README.md | sed -n '1,75p'

printf '%s\n' '--- lifecycle and state references ---'
rg -n -C 6 '_sessions|InMemorySaver|thread_id|build_per_user_react_agent|asyncio\.run|--user-id|ainvoke' \
  examples/langgraph tests 2>/dev/null || true

printf '%s\n' '--- source section ---'
cat -n examples/langgraph/calculator_mcp.py | sed -n '52,145p'

Repository: NVIDIA/NeMo-Fabric

Length of output: 47813


Must fix: Document process-local conversation resumption.

State that reusing --user-id resumes a conversation only while the same PerUserReactAgent remains alive. Each CLI invocation creates a new agent, _sessions, and InMemorySaver, so a new process starts without prior conversation state. Add durable checkpointing before documenting cross-process resumption.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/langgraph/README.md` around lines 46 - 47, Update the README text
describing user ID reuse to state that conversation resumption is process-local
and works only while the same PerUserReactAgent remains alive; clarify that each
CLI invocation creates a new agent, _sessions, and InMemorySaver, so a new
process has no prior state. Do not imply cross-process resumption unless durable
checkpointing is implemented first.

Source: Coding guidelines

## Work Needed for a Full Adapter

The examples validate the two workflow shapes, but a generic adapter needs the
following additional work before it can support them through NVIDIA NeMo Fabric:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Should fix: Use NeMo Fabric after the first full mention.

In examples/langgraph/README.md, Lines 79, 88, and 95 repeat NVIDIA NeMo Fabric or use standalone Fabric for the product. Line 16 already introduces the full name.

Proposed wording
- before it can support them through NVIDIA NeMo Fabric:
+ before it can support them through NeMo Fabric:
- Map a selected Fabric model alias to an NIM binding.
+ Map a selected NeMo Fabric model alias to an NIM binding.
- The calculator shows per-user state, but Fabric currently scopes a runtime
+ The calculator shows per-user state, but NeMo Fabric currently scopes a runtime

As per coding guidelines, use the full product name on first use and NeMo Fabric thereafter. As per path instructions, do not use standalone capitalized Fabric for the product.

Also applies to: 88-88, 95-95

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/langgraph/README.md` at line 79, Update the product references in
the README passages around “following additional work,” including the
corresponding references later in the document, to use “NeMo Fabric” after the
full introduction at line 16. Replace any standalone capitalized “Fabric” used
as the product name, while preserving the existing wording otherwise.

Sources: Coding guidelines, Path instructions

Comment thread examples/README.md
--input "Reply with exactly: NeMo Fabric works"
```

## LangGraph examples

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Should fix: Use title case for the new heading.

In examples/README.md, Line 40 uses ## LangGraph examples. Change it to ## LangGraph Examples.

As per coding guidelines, technical-documentation headings must use title case.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/README.md` at line 40, Update the “LangGraph examples” heading in
the examples README to use title case: “LangGraph Examples.”

Source: Coding guidelines

Comment on lines +23 to +101
def test_calculator_config_preserves_requested_mcp_and_workflow_shape():
config = load_config(CALCULATOR_CONFIG)

assert config.selected_model().model == "meta/llama-3.1-70b-instruct"
assert config.mcp is not None
assert config.mcp.servers["mcp_math"].transport == "streamable-http"
assert config.workflow.entrypoint == "langgraph:per_user_react_agent"
assert config.workflow.settings["tool_names"] == ["current_timezone", "mcp_math"]


async def test_calculator_creates_isolated_graph_and_mcp_client_per_user():
config = load_config(CALCULATOR_CONFIG)
mock_tools = []
for name in config.mcp.servers["mcp_math"].include: # validated by the example
mock_tool = MagicMock()
mock_tool.name = name
mock_tools.append(mock_tool)
mock_client_factory = MagicMock()
mock_client_factory.side_effect = [
MagicMock(get_tools=AsyncMock(return_value=mock_tools))
for _ in range(2)
]
mock_graph_factory = MagicMock(side_effect=[MagicMock(), MagicMock()])
mock_model_factory = MagicMock(side_effect=[MagicMock(), MagicMock()])

agent = PerUserReactAgent(
config,
model_factory=mock_model_factory,
mcp_client_factory=mock_client_factory,
graph_factory=mock_graph_factory,
)
alice_first = await agent.graph_for("alice")
alice_second = await agent.graph_for("alice")
hatter = await agent.graph_for("hatter")

assert alice_first is alice_second
assert alice_first is not hatter
assert mock_model_factory.call_count == 2
assert mock_client_factory.call_count == 2
connection = mock_client_factory.call_args.args[0]["mcp_math"]
assert connection == {
"transport": "streamable_http",
"url": "http://localhost:9901/mcp",
}
for call in mock_graph_factory.call_args_list:
assert call.kwargs["checkpointer"] is not None
assert call.kwargs["name"] == "per_user_calculator"


def test_current_timezone_uses_the_explicit_server_configuration(restore_environ):
restore_environ["TZ"] = "America/Los_Angeles"

assert current_timezone.invoke({}) == "America/Los_Angeles"


async def test_phishing_graph_projects_a_json_safe_structured_assessment():
config = load_config(PHISHING_CONFIG)
mock_structured_model = MagicMock()
mock_structured_model.ainvoke = AsyncMock(
return_value=PhishingAssessment(
is_likely_phishing=True,
explanation="It asks for banking information to complete a refund.",
)
)
mock_model = MagicMock()
mock_model.with_structured_output.return_value = mock_structured_model

graph = build_email_phishing_analyzer(config, model=mock_model)
result = await graph.ainvoke(
{"body": "Provide your routing number so we can issue a refund."}
)

assert result["assessment"] == {
"is_likely_phishing": True,
"explanation": "It asks for banking information to complete a refund.",
}
mock_model.with_structured_output.assert_called_once_with(
PhishingAssessment, method="function_calling"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add error-path coverage for the new public configuration and workflows.

Add tests for an unknown llm_name, unknown tool source, blank user_id, and missing configured MCP tools. Add a concurrent same-user test for graph_for after synchronization is added.

As per path instructions, tests should cover changed API behavior, including error paths and lifecycle cleanup where relevant.

🤖 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 `@tests/examples/test_langgraph_examples.py` around lines 23 - 101, Extend the
tests around load_config and PerUserReactAgent to cover unknown llm_name,
unknown tool source, blank user_id, and missing configured MCP tools, asserting
each raises the intended error. Add a concurrent same-user graph_for test that
verifies synchronization returns the same cached graph and avoids duplicate
model/client creation, and include lifecycle cleanup assertions where the
changed public API requires it.

Source: Path instructions

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant