Skip to content

feat(client): NemoClient platform provider + request-scoped FastAPI DI#800

Open
maxdubrinsky wants to merge 5 commits into
mainfrom
aircore-904-add-nemoclient-fastapi-di/md
Open

feat(client): NemoClient platform provider + request-scoped FastAPI DI#800
maxdubrinsky wants to merge 5 commits into
mainfrom
aircore-904-add-nemoclient-fastapi-di/md

Conversation

@maxdubrinsky

@maxdubrinsky maxdubrinsky commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Consolidates the NemoClient foundation into one PR: the platform provider (AIRCORE-883) plus the request-scoped FastAPI DI seam (AIRCORE-904). This was previously split across #799 (provider) and #800 (DI, stacked on #799's branch). #800 is now retargeted to main and carries both halves; #799 is closed in favor of this PR.

Provider foundation (AIRCORE-883)

  • Plugin-side NemoClientProvider protocol with explicit override, entry-point discovery, and a zero-nmp-common env-var default (DefaultNemoClientProvider).
  • Platform provider (PlatformNemoClientProvider) backed by per-service URL routing, shared HTTP transports, request principal headers, and OTEL propagation.
  • Provider entry points registered in nmp-common and the bundled nemo-platform package.
  • Fails closed on broken/conflicting providers; OTEL context cannot replace authoritative identity headers.

Request-scoped DI (AIRCORE-904)

  • Canonical FastAPI get_nemo_client dependency + exact common-package re-export.
  • Fresh request-scoped AsyncNemoClient with current principal + trace headers, sharing one provider-owned transport.
  • DependencyProvider transport/SDK init made thread-safe and order-independent; provider-owned transport closed exactly once on shutdown.

How it's used (downstream)

In-process (service / middleware / controller), async:

from nemo_platform_plugin.client_provider import get_async_nemo_client
from nemo_platform_plugin.files import endpoints

client = get_async_nemo_client(as_service="data-designer", internal=True)
data = (await client.send(endpoints.download_file(name="fs", path="x.csv"))).read()

In a job's task container the same call falls back to the env-var DefaultNemoClientProvider (NMP_BASE_URL / NMP_PRINCIPAL injected by the jobs backend). The plugin imports only nemo_platform_plugin.client_provider — never nmp-common, never Stainless.

Per-service migration playbook: packages/nemo_platform_plugin/src/nemo_platform_plugin/client/MIGRATION.md.

Unblocks

  • Phase-2 consumer cleanup: swap client_from_platform(...) for Depends(get_nemo_client).
  • AIRCORE-882 (generate FastAPI routes from endpoints.py), AIRCORE-893 (CLI generator → typed clients).

Follow-up (not in this PR)

  • Typed-client ergonomics on the provider: a client_cls param so get_async_nemo_client(AsyncFilesClient) returns a typed client directly (today it returns a base client; use .send(endpoints.*)).
  • Ownership-aware client close() / context-manager support — AIRCORE-933.

Validation

  • Provider foundation: 81 focused provider + client-factory tests.
  • DI: 50 focused dependency, lifecycle, concurrency, and client-factory tests.
  • Ruff, formatting, git diff --check, focused ty, and wheel entry-point inspection passed. Independent adversarial review found no blocker on either half.
  • Full CI now runs against main (while stacked on a feature branch, most CI jobs were suppressed).

Linear: https://linear.app/nvidia/issue/AIRCORE-883 , https://linear.app/nvidia/issue/AIRCORE-904

Summary by CodeRabbit

  • New Features

    • Added configurable Nemo client providers with automatic provider discovery.
    • Added workspace-aware synchronous and asynchronous client creation.
    • Added platform client construction with service routing, authentication, delegation, and tracing support.
    • Added request-scoped Nemo client dependency support for services.
  • Bug Fixes

    • Improved delegated identity header handling.
    • Prevented duplicate client and transport creation during concurrent requests.
    • Improved provider conflict and loading error handling.
  • Tests

    • Expanded coverage for client creation, routing, dependencies, provider resolution, concurrency, and lifecycle behavior.

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
@github-actions github-actions Bot added the feat label Jul 20, 2026
Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
…-fastapi-di/md

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
@maxdubrinsky maxdubrinsky changed the title feat(client): add request-scoped NemoClient dependency feat(client): NemoClient platform provider + request-scoped FastAPI DI Jul 21, 2026
@maxdubrinsky
maxdubrinsky changed the base branch from aircore-883-nemoclient-provider-foundation/md to main July 21, 2026 15:49
@maxdubrinsky maxdubrinsky reopened this Jul 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 26521/34136 77.7% 62.0%
Integration Tests 15223/32761 46.5% 18.7%

@maxdubrinsky
maxdubrinsky marked this pull request as ready for review July 21, 2026 16:25
@maxdubrinsky
maxdubrinsky requested review from a team as code owners July 21, 2026 16:25
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Nemo client creation now resolves pluggable providers through entry points or explicit overrides. nmp-common supplies a platform provider with routing, headers, workspace support, and shared transports. Service dependencies expose request-scoped clients with synchronized lifecycle management.

Changes

Nemo client provider flow

Layer / File(s) Summary
Provider contract and entry-point resolution
packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py, packages/nemo_platform_plugin/src/nemo_platform_plugin/sdk_provider.py, packages/nemo_platform_plugin/tests/*
Adds the provider protocol, default env-based provider, workspace forwarding, explicit overrides, deterministic entry-point discovery, validation, caching, conflict handling, and stricter SDK provider failures.
Platform factory and routing
packages/nmp_common/src/nmp/common/client_factory.py, packages/nmp_common/pyproject.toml, packages/nemo_platform/pyproject.toml, packages/nmp_common/tests/client_factory/*
Adds sync and async platform client factories with workspace-aware routing, composed authentication and OTEL headers, shared or explicit HTTP clients, and the registered platform provider.
Request-scoped dependency wiring
packages/nemo_platform_plugin/src/nemo_platform_plugin/dependencies.py, packages/nmp_common/src/nmp/common/service/*, packages/nmp_common/tests/nmp_common/test_common_service.py, packages/nemo_platform_plugin/tests/test_dependencies.py
Adds the plugin dependency placeholder and service export, wires request-scoped Nemo clients into FastAPI, synchronizes shared client creation, and updates transport shutdown behavior and coverage.

Sequence Diagram(s)

sequenceDiagram
  participant ServiceRequest
  participant FastAPI
  participant DependencyProvider
  participant PlatformNemoClientProvider
  participant AsyncNemoClient
  ServiceRequest->>FastAPI: resolve get_nemo_client
  FastAPI->>DependencyProvider: request-scoped client dependency
  DependencyProvider->>PlatformNemoClientProvider: create async client
  PlatformNemoClientProvider->>AsyncNemoClient: construct with workspace, headers, and router
  AsyncNemoClient-->>FastAPI: return request-scoped client
  FastAPI-->>ServiceRequest: inject client
Loading

Suggested labels: refactor

Suggested reviewers: crookedstorm, mckornfield, svvarom

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: a NemoClient platform provider plus request-scoped FastAPI dependency injection.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch aircore-904-add-nemoclient-fastapi-di/md

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py (1)

187-253: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract shared entry-point resolution helper. _resolve_provider is duplicated verbatim between the two files (dedup, deterministic-conflict RuntimeError, load/construct, protocol validation) — only the group name, protocol type, and default class differ. One shared helper avoids future divergence when either copy gets a bug fix, and is a natural place to add a lock around the scan+load+construct+cache sequence.

  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py#L187-L253: replace with a call to a shared _resolve_provider(group, protocol, default_factory, kind_label) helper.
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/sdk_provider.py#L298-L337: replace with a call to the same shared helper, passing "nemo.sdk_provider", SDKProvider, DefaultSDKProvider.
🤖 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 `@packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py`
around lines 187 - 253, Extract the duplicated entry-point resolution logic from
_resolve_provider in
packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py#L187-L253
and
packages/nemo_platform_plugin/src/nemo_platform_plugin/sdk_provider.py#L298-L337
into one shared _resolve_provider(group, protocol, default_factory, kind_label)
helper. Preserve deterministic conflict detection, entry-point loading and
construction, protocol validation, default fallback, caching, and add locking
around the scan/load/construct/cache sequence; update both providers to call it
with their respective group, protocol, and default factory.
🤖 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.

Nitpick comments:
In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py`:
- Around line 187-253: Extract the duplicated entry-point resolution logic from
_resolve_provider in
packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py#L187-L253
and
packages/nemo_platform_plugin/src/nemo_platform_plugin/sdk_provider.py#L298-L337
into one shared _resolve_provider(group, protocol, default_factory, kind_label)
helper. Preserve deterministic conflict detection, entry-point loading and
construction, protocol validation, default fallback, caching, and add locking
around the scan/load/construct/cache sequence; update both providers to call it
with their respective group, protocol, and default factory.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 72cf3fb6-a3a0-4a76-b118-1f0adb68251b

📥 Commits

Reviewing files that changed from the base of the PR and between 54983be and fa95fa3.

📒 Files selected for processing (14)
  • packages/nemo_platform/pyproject.toml
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/dependencies.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/sdk_provider.py
  • packages/nemo_platform_plugin/tests/test_client_provider.py
  • packages/nemo_platform_plugin/tests/test_dependencies.py
  • packages/nemo_platform_plugin/tests/test_sdk_provider.py
  • packages/nmp_common/pyproject.toml
  • packages/nmp_common/src/nmp/common/client_factory.py
  • packages/nmp_common/src/nmp/common/service/__init__.py
  • packages/nmp_common/src/nmp/common/service/base.py
  • packages/nmp_common/src/nmp/common/service/dependencies.py
  • packages/nmp_common/tests/client_factory/test_client_factory.py
  • packages/nmp_common/tests/nmp_common/test_common_service.py

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant