Skip to content

fix(api): declare the entity-store name pattern on create DTOs [ASTD-349] - #978

Merged
marcusds merged 7 commits into
mainfrom
astd-349-entity-name-pattern-dtos/mschwab
Jul 30, 2026
Merged

marcusds merged 7 commits into
mainfrom
astd-349-entity-name-pattern-dtos/mschwab

Conversation

@marcusds

@marcusds marcusds commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The OpenAPI spec advertises a name pattern the platform does not accept.

Create-request DTOs declare ^[\w\-.]+$ (max 255), but the entity store enforces the stricter RFC-1035-ish NAME_PATTERN downstream:

^[a-z](?!.*--)[a-z0-9\-@.+_]{1,62}(?<!-)$

So Sparl or My_Provider validates against the published spec, passes any generated client's validation, and then fails with a 422. That hits every SDK consumer, not just Studio.

Raised by @steramae-nvidia on #919:

Is this a problem for the openapi schema? Anyone relying on the openapi schema should expect that the regex pattern in the spec should be valid.

#919 works around this on the frontend with a hand-mirrored copy of NAME_PATTERN. That mirror exists only because the spec is wrong.

Change

Two commits.

1. Declare the real pattern on the create DTOs

DTO Before After
CreateModelProviderRequest.name ^[\w\-.]+$, max 255 NAME_PATTERN, max 63
CreateFilesetRequest.name ^[\w\-.]+$, max 255 NAME_PATTERN, max 63
PlatformSecretCreateRequest.name field_validator only — no pattern in the spec at all pattern=, max 63

Three things worth calling out:

regex_engine="python-re" is required. NAME_PATTERN uses lookahead and lookbehind; Pydantic's default Rust engine rejects them outright. The entity-store schemas already set this — without it the models don't build.

max_length 255 → 63. The regex caps at 63 by construction (^[a-z] + {1,62}), so the old pair contradicted itself.

@ is legal under NAME_PATTERN. Secrets previously rejected a@b via its own validator; it now accepts it, matching what the entity store always allowed. A loosening in one direction alongside the tightening.

2. Collapse the mirrored constants

The first commit made an existing problem worse, so the second one fixes it. NAME_PATTERN was copy-pasted verbatim in four Python modules: nmp_common, plus files/types.py, secrets/types.py, and jobs/spec.py inside nemo_platform_plugin. Each carried a comment explaining it was inlined to avoid an nmp_common dependency, and jobs/spec.py cited files/types.py as the precedent — the duplication was self-propagating.

The dependency only runs one way: nmp_common/pyproject.toml depends on nemo-platform-plugin, and nmp_common/entities/client.py imports EntityBase from it. So the plugin cannot import nmp_common without a cycle, which makes the plugin the correct home — as the no-nmp-common-in-plugins pre-commit hook's own comment prescribes.

New leaf module nemo_platform_plugin/entity_naming.py holds the single definition and imports nothing, so modules that need to stay leaf nodes still can. nmp_common re-exports it, leaving every existing constants.NAME_PATTERN call site untouched.

Regenerating the OpenAPI spec after this commit produces no diff — it's a pure refactor.

Testing

  • All three DTOs verified directly: Sparl, my--provider, myprovider-, 1provider, 64-char, My_Provider rejected; ok-name, a@b accepted
  • 4895 Python tests pass after the refactor — plugin, secrets, files, models, entities, jobs, nmp_common. A wider sweep including evaluator, data-designer, and ext also passes, aside from 8 pre-existing failures in nemo_platform_ext daemonize/socket tests plus 2 evaluator errors, all confirmed identical on a clean tree
  • Re-export identity asserted: nmp.common.entities.constants.NAME_PATTERN is nemo_platform_plugin.entity_naming.NAME_PATTERN
  • 44 Studio tests pass on the three name-entry forms
  • ruff, ruff format, ty clean on changed files
  • Web SDK regenerated: filesCreateFilesetBodyNameMax = 63 and the new regex. That output is gitignored, so nothing to commit

Two secrets tests changed. test_create_secret_with_invalid_name_returns_friendly_error asserted a message that no longer exists and listed a@b as invalid — replaced with a reject-list plus an accept-list. The two uppercase regression tests in test_secrets_with_auth.py were failing because the shared request model now rejects locally before the HTTP call; they use model_construct to bypass it so the original 500-vs-422 server-side coverage stays intact.

Known remaining drift (not fixed here)

packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/values/models.py:31 carries a hand-rewritten lookaround-free variant commented "Keep this aligned with nmp.common.entities.constants.NAME_PATTERN". It has drifted:

input entity store evaluator SDK
a-b-b-… (81 chars) reject accept
a-b-b-… (125 chars) reject accept

Each hyphen branch consumes two characters per repetition, so {1,62} admits up to 125 characters instead of 63. nemo_evaluator_sdk has no dependency on nemo_platform_plugin, so fixing it needs either a new dependency or a test asserting equivalence — out of scope here, happy to file it.

On the frontend, main today has two copies (filesetName.ts and CreateSecretModal/constants.ts). #919 collapses both into one — entityName.ts — so once that merges there is a single frontend copy left. It can drop its hand-written regex in favour of the generated filesCreateFilesetBodyNameRegExp once this PR lands; the per-rule error messages and the sanitizer's character classes have no generated equivalent and stay.

Follow-up

web/packages/common/src/utils/entityName.ts (added in #919) can be reduced to the generated schema.

Summary by CodeRabbit

  • Validation Improvements
    • Tightened name validation across filesets, model providers, platform secrets, and related entity inputs.
    • Enforced max length of 63, with lowercase-first names, approved characters including @ . + _, plus existing constraints: no consecutive hyphens and no trailing hyphen.
    • Updated OpenAPI and CLI/help text to reflect the stricter, consistent rules.
  • Tests
    • Expanded API boundary tests for broader invalid-name rejection.
    • Added/strengthened checks to ensure validation behavior results in consistent HTTP 422 responses for invalid inputs, while allowing valid entity-store-style names.

@marcusds
marcusds requested review from a team as code owners July 29, 2026 21:26
@github-actions github-actions Bot added the fix label Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

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

Walkthrough

Name validation is tightened across fileset, model provider, and platform secret requests. Shared constants, Pydantic models, OpenAPI schemas, CLI messages, documentation, and secret API tests now reflect lowercase-starting names, restricted punctuation, and 63-character limits where enforced.

Changes

Entity name validation

Layer / File(s) Summary
Shared name validation contracts
packages/nemo_platform_plugin/.../entity_naming.py, packages/nmp_common/.../constants.py, packages/nemo_platform_plugin/.../jobs/spec.py
Defines shared name constraints and re-exports them for common entity validation and job specifications.
Request model enforcement
packages/nemo_platform_plugin/.../files/types.py, services/core/models/.../schemas.py
Applies shared constraints to fileset and model provider requests using Pydantic’s Python regex engine.
Secret request validation and tests
packages/nemo_platform_plugin/.../secrets/types.py, services/core/secrets/tests/*
Replaces custom secret-name validation with Pydantic field constraints and tests rejected and accepted name forms.
OpenAPI schema alignment
openapi/openapi.yaml, openapi/ga/openapi.yaml, openapi/ga/individual/platform.openapi.yaml
Updates affected schemas and descriptions to reflect the 63-character lowercase-starting rule and expanded punctuation.
CLI and documentation alignment
packages/nemo_platform_ext/.../cli/commands/api/*, docs/cli/reference.mdx
Updates CLI help, required-field messages, and reference documentation for the revised naming rules.

Possibly related PRs

Suggested reviewers: mckornfield, mikeknep, maxdubrinsky, benmccown

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% 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 Title matches the main change: publishing the entity-store name pattern on create DTOs.
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 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch astd-349-entity-name-pattern-dtos/mschwab
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch astd-349-entity-name-pattern-dtos/mschwab

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/files/types.py (1)

77-87: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated pattern risks drifting from nmp_common's source of truth.

Comment acknowledges the duplication, but nothing enforces it stays in sync with nmp.common.entities.constants.NAME_PATTERN.

Add a unit test in either package asserting the two pattern strings are identical, so future changes to one side fail loudly.

🤖 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/files/types.py` around
lines 77 - 87, Add a unit test that imports the local NAME_PATTERN and
nmp.common.entities.constants.NAME_PATTERN, then asserts their pattern strings
are identical. Place the test in either package’s existing unit-test suite and
keep the assertion focused on detecting future drift between these two symbols.
🤖 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/files/types.py`:
- Around line 77-87: Add a unit test that imports the local NAME_PATTERN and
nmp.common.entities.constants.NAME_PATTERN, then asserts their pattern strings
are identical. Place the test in either package’s existing unit-test suite and
keep the assertion focused on detecting future drift between these two symbols.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 38bac438-c76f-4eab-92a8-dee90ecffd9e

📥 Commits

Reviewing files that changed from the base of the PR and between 9aaf35d and ed2c717.

📒 Files selected for processing (9)
  • openapi/ga/individual/platform.openapi.yaml
  • openapi/ga/openapi.yaml
  • openapi/openapi.yaml
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/files/types.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/secrets/types.py
  • packages/nmp_common/src/nmp/common/entities/constants.py
  • services/core/models/src/nmp/core/models/schemas.py
  • services/core/secrets/tests/integration/test_secrets_with_auth.py
  • services/core/secrets/tests/test_secrets.py

@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 28211/36046 78.3% 62.7%
Integration Tests 16765/34764 48.2% 20.8%

@marcusds
marcusds requested a review from mckornfield July 29, 2026 21:44

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

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 `@packages/nemo_platform_plugin/src/nemo_platform_plugin/entity_naming.py`:
- Around line 29-33: Update NAME_PATTERN_DESCRIPTION to accurately document the
characters accepted by NAME_PATTERN, including @, ., +, and _, while retaining
the existing length, starting-character, consecutive-hyphen, and trailing-hyphen
rules.
🪄 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: CHILL

Plan: Enterprise

Run ID: 1d12a3f9-a12d-4f90-b463-eaec65b4a1ee

📥 Commits

Reviewing files that changed from the base of the PR and between ed2c717 and e0e4a8a.

📒 Files selected for processing (5)
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/entity_naming.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/files/types.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/spec.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/secrets/types.py
  • packages/nmp_common/src/nmp/common/entities/constants.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/secrets/types.py

marcusds added a commit that referenced this pull request Jul 29, 2026
#978 makes the create DTOs declare NAME_PATTERN, so the generated zod now
carries the strict pattern and entityName.ts no longer has to hand-mirror
it. Take ENTITY_NAME_REGEXP from entitiesCreateEntityBodyNameRegExp — the
generic entity-store endpoint, not one of the per-resource copies.

The max length stays local because the entity-store schema declares no
maxLength; a test pins it against the create DTOs that do. That test also
asserts the entity, fileset, secret, and model-provider schemas still
agree on the pattern, so a future drift between them fails CI.

The per-rule error messages and the sanitizer's character classes have no
generated equivalent and remain hand-written.

Signed-off-by: mschwab <mschwab@nvidia.com>
marcusds added a commit that referenced this pull request Jul 29, 2026
…349]

NAME_PATTERN_DESCRIPTION claimed names may contain "only lowercase
letters, digits, and hyphens", but the pattern has allowed @, ., + and _
since it was written — see the TODO(#3530) directly above it. The spec
therefore told SDK consumers a name like llama-3.2-3b@v1.0.0 was invalid
when the service accepts it, and contradicted Studio's own help text,
which lists the special characters correctly.

All nine name fields in the spec now carry wording that matches the nine
patterns beside them.

Reported by CodeRabbit on #978.

Signed-off-by: mschwab <mschwab@nvidia.com>

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

feel like we should leave off those special characters if versioning is going to happen, even eventually

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

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 `@docs/cli/reference.mdx`:
- Line 1103: Update the fileset name descriptions at the referenced repeated
locations to document the accepted underscore character as “_”, not “\__”.
Preserve the existing allowed-character list and formatting, escaping the
underscore only as needed for MDX rendering.
- Line 1103: Update the generator source that produces the fileset name
documentation instead of editing the generated reference page directly. Apply
the change at the source corresponding to the entries near the fileset name
references, then run the designated reference-page regeneration command so all
affected occurrences, including the additional referenced entries, are
regenerated consistently.
🪄 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: CHILL

Plan: Enterprise

Run ID: 27331ff6-2de2-4d20-bd9d-65b59f9fcfb1

📥 Commits

Reviewing files that changed from the base of the PR and between 8715b68 and f87f793.

⛔ Files ignored due to path filters (21)
  • sdk/python/nemo-platform/.nmpcontext/openapi.yaml is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/files/filesets.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/inference/providers.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/projects.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/workspaces/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/entities/entities.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/files/filesets.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/inference/providers.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/inference/virtual_models.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/projects/projects.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/secrets/secrets.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/workspaces/workspaces.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/entities/entity_create_params.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/entities/entity_update_entity_by_name_params.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/files/fileset_create_params.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/inference/provider_create_params.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/jobs/platform_job_step_spec.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/jobs/platform_job_step_spec_param.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/projects/project_create_params.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/secrets/secret_create_params.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/workspaces/workspace_create_params.py is excluded by !sdk/**
📒 Files selected for processing (5)
  • docs/cli/reference.mdx
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/files/filesets.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/inference/providers.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/projects.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/workspaces/__init__.py

Comment thread docs/cli/reference.mdx Outdated
marcusds added a commit that referenced this pull request Jul 30, 2026
…349]

NAME_PATTERN_DESCRIPTION claimed names may contain "only lowercase
letters, digits, and hyphens", but the pattern has allowed @, ., + and _
since it was written — see the TODO(#3530) directly above it. The spec
therefore told SDK consumers a name like llama-3.2-3b@v1.0.0 was invalid
when the service accepts it, and contradicted Studio's own help text,
which lists the special characters correctly.

All nine name fields in the spec now carry wording that matches the nine
patterns beside them.

Reported by CodeRabbit on #978.

Signed-off-by: mschwab <mschwab@nvidia.com>
@marcusds
marcusds force-pushed the astd-349-entity-name-pattern-dtos/mschwab branch from f87f793 to cc399c9 Compare July 30, 2026 08:00
@github-actions

Copy link
Copy Markdown
Contributor

marcusds added a commit that referenced this pull request Jul 30, 2026
#978 makes the create DTOs declare NAME_PATTERN, so the generated zod now
carries the strict pattern and entityName.ts no longer has to hand-mirror
it. Take ENTITY_NAME_REGEXP from entitiesCreateEntityBodyNameRegExp — the
generic entity-store endpoint, not one of the per-resource copies.

The max length stays local because the entity-store schema declares no
maxLength; a test pins it against the create DTOs that do. That test also
asserts the entity, fileset, secret, and model-provider schemas still
agree on the pattern, so a future drift between them fails CI.

The per-rule error messages and the sanitizer's character classes have no
generated equivalent and remain hand-written.

Signed-off-by: mschwab <mschwab@nvidia.com>
marcusds added 7 commits July 30, 2026 09:29
…349]

The spec advertised `^[\w\-.]+$` (max 255) for fileset, model provider,
and secret names while the entity store enforces the stricter RFC-1035-ish
NAME_PATTERN downstream. Names like `Sparl` or `My_Provider` passed every
generated client's validation and then failed with a 422, so the published
contract was wrong for all SDK consumers, not just Studio.

Declare NAME_PATTERN on the three create-request DTOs. Each needs
regex_engine="python-re" — the pattern uses lookaround, which Pydantic's
default Rust engine rejects — matching what the entity-store schemas
already do. max_length drops 255 -> 63 to agree with the regex, which caps
at 63 by construction.

The secrets DTO enforced its rule in a field_validator, so no pattern
reached the spec at all; moving it to `pattern=` publishes it. Its
uppercase-name regression tests now bypass the request model with
model_construct so they still exercise the server-side 422 path.

`@` is legal under NAME_PATTERN, so secrets now accept `a@b`. The entity
store always did.

Signed-off-by: mschwab <mschwab@nvidia.com>
NAME_PATTERN was copy-pasted verbatim in four Python modules — nmp_common,
plus files/types.py, secrets/types.py, and jobs/spec.py inside
nemo_platform_plugin. Each carried a comment explaining it was inlined to
avoid an nmp_common dependency, and jobs/spec.py cited files/types.py as
the precedent, so the duplication was self-propagating.

The dependency only runs one way: nmp_common depends on
nemo-platform-plugin, so the plugin cannot import nmp_common without a
cycle. That makes the plugin the correct home. New leaf module
entity_naming.py holds the single definition and imports nothing, so
modules that need to stay leaf nodes still can. nmp_common re-exports it,
leaving every existing constants.NAME_PATTERN call site untouched.

Pure refactor: regenerating the OpenAPI spec produces no diff.

Signed-off-by: mschwab <mschwab@nvidia.com>
Signed-off-by: mschwab <mschwab@nvidia.com>
…349]

NAME_PATTERN_DESCRIPTION claimed names may contain "only lowercase
letters, digits, and hyphens", but the pattern has allowed @, ., + and _
since it was written — see the TODO(#3530) directly above it. The spec
therefore told SDK consumers a name like llama-3.2-3b@v1.0.0 was invalid
when the service accepts it, and contradicted Studio's own help text,
which lists the special characters correctly.

All nine name fields in the spec now carry wording that matches the nine
patterns beside them.

Reported by CodeRabbit on #978.

Signed-off-by: mschwab <mschwab@nvidia.com>
Picks up the NAME_PATTERN_DESCRIPTION change across the vendored Python
SDK, the generated CLI commands, and the CLI reference docs.

plugins/nemo-customizer/openapi/openapi.yaml is deliberately excluded:
regenerating it locally drops the whole rl/jobs surface, which comes from
an optional dependency that isn't installed here.

Signed-off-by: mschwab <mschwab@nvidia.com>
Two problems with the previous wording, "contain only lowercase letters,
digits, hyphens, and @ . + _".

The trailing underscore is markdown-escaped downstream, so the CLI
reference rendered "@ . + \__". Dropping the literal characters removes
the artifact.

Second, @, + and _ are slated for removal (TODO(#3530)), and advertising
them invites adoption ahead of that migration — mckornfield's point on
this PR. Dots are not in the same category: llama-3.1-8b is the example
in CreateModelProviderRequest and dotted versions are how model names are
written, so those stay documented.

"use" rather than "contain only" keeps this from being a false claim
about what the service rejects; the machine-readable pattern remains the
contract.

Signed-off-by: mschwab <mschwab@nvidia.com>
…-349]

Propagates the NAME_PATTERN_DESCRIPTION rewording through the vendored
Python SDK, the generated CLI commands, and the CLI reference docs.

Also clears the "@ . + \__" escaping artifact the previous wording left in
docs/cli/reference.mdx.

Signed-off-by: mschwab <mschwab@nvidia.com>
@marcusds
marcusds force-pushed the astd-349-entity-name-pattern-dtos/mschwab branch from 2df2443 to 1f7db48 Compare July 30, 2026 16:37
marcusds added a commit that referenced this pull request Jul 30, 2026
#978 makes the create DTOs declare NAME_PATTERN, so the generated zod now
carries the strict pattern and entityName.ts no longer has to hand-mirror
it. Take ENTITY_NAME_REGEXP from entitiesCreateEntityBodyNameRegExp — the
generic entity-store endpoint, not one of the per-resource copies.

The max length stays local because the entity-store schema declares no
maxLength; a test pins it against the create DTOs that do. That test also
asserts the entity, fileset, secret, and model-provider schemas still
agree on the pattern, so a future drift between them fails CI.

The per-rule error messages and the sanitizer's character classes have no
generated equivalent and remain hand-written.

Signed-off-by: mschwab <mschwab@nvidia.com>
@marcusds
marcusds requested a review from mckornfield July 30, 2026 17:36
@marcusds
marcusds added this pull request to the merge queue Jul 30, 2026
Merged via the queue into main with commit 0729f2c Jul 30, 2026
58 checks passed
@marcusds
marcusds deleted the astd-349-entity-name-pattern-dtos/mschwab branch July 30, 2026 18:28
marcusds added a commit that referenced this pull request Aug 12, 2026
…ls DTOs

#978 tightened three DTOs — `CreateModelProviderRequest`, `CreateFilesetRequest`,
`PlatformSecretCreateRequest` — and collapsed NAME_PATTERN into a single
definition in `nemo_platform_plugin/entity_naming.py`. The rest of the models
service was left on the loose rule.

`CreatePromptRequest`, `CreateModelEntityRequest`, `CreateModelAdapterRequest`,
`CreateModelDeploymentConfigRequest`, `CreateModelDeploymentRequest`, and the
shared `ModelEntityBaseModel` still advertise `^[\w\-.]+$` with max_length 255,
while the entity store enforces `^[a-z](?!.*--)[a-z0-9\-@.+_]{1,62}(?<!-)$` with
max 63. So `Sparl` passes request validation, passes any generated client's
validation, and then 422s from the entity store with `loc: ['body', 'name']`
pointing at a body the caller never sent.

Declare NAME_PATTERN and NAME_MAX_LENGTH on those six. Read and lookup DTOs keep
the looser pattern so entities created before the rule stay reachable.

NAME_PATTERN uses lookaround, which pydantic's default rust-regex engine cannot
compile, so the affected models need `regex_engine="python-re"`. #978 set that
inline on the one DTO it touched; this lifts it to a module-level
ENTITY_NAME_CONFIG rather than repeating the ConfigDict seven times.

Not a compatibility break: any name that passes the loose pattern but fails the
strict one already 422s at the entity store today. This moves the rejection to
the edge, where the error can name the actual field.

Regenerates openapi/openapi.yaml and the two GA variants — the diff is
exclusively pattern, maxLength, and description. Plugin specs regenerate with no
diff.

Signed-off-by: mschwab <mschwab@nvidia.com>
AnuradhaKaruppiah pushed a commit to AnuradhaKaruppiah/nemo-platform that referenced this pull request Aug 27, 2026
…VIDIA-NeMo#919)

* fix(api): declare the entity-store name pattern on create DTOs [ASTD-349]

The spec advertised `^[\w\-.]+$` (max 255) for fileset, model provider,
and secret names while the entity store enforces the stricter RFC-1035-ish
NAME_PATTERN downstream. Names like `Sparl` or `My_Provider` passed every
generated client's validation and then failed with a 422, so the published
contract was wrong for all SDK consumers, not just Studio.

Declare NAME_PATTERN on the three create-request DTOs. Each needs
regex_engine="python-re" — the pattern uses lookaround, which Pydantic's
default Rust engine rejects — matching what the entity-store schemas
already do. max_length drops 255 -> 63 to agree with the regex, which caps
at 63 by construction.

The secrets DTO enforced its rule in a field_validator, so no pattern
reached the spec at all; moving it to `pattern=` publishes it. Its
uppercase-name regression tests now bypass the request model with
model_construct so they still exercise the server-side 422 path.

`@` is legal under NAME_PATTERN, so secrets now accept `a@b`. The entity
store always did.

Signed-off-by: mschwab <mschwab@nvidia.com>

* refactor(api): collapse the mirrored entity name constants [ASTD-349]

NAME_PATTERN was copy-pasted verbatim in four Python modules — nmp_common,
plus files/types.py, secrets/types.py, and jobs/spec.py inside
nemo_platform_plugin. Each carried a comment explaining it was inlined to
avoid an nmp_common dependency, and jobs/spec.py cited files/types.py as
the precedent, so the duplication was self-propagating.

The dependency only runs one way: nmp_common depends on
nemo-platform-plugin, so the plugin cannot import nmp_common without a
cycle. That makes the plugin the correct home. New leaf module
entity_naming.py holds the single definition and imports nothing, so
modules that need to stay leaf nodes still can. nmp_common re-exports it,
leaving every existing constants.NAME_PATTERN call site untouched.

Pure refactor: regenerating the OpenAPI spec produces no diff.

Signed-off-by: mschwab <mschwab@nvidia.com>

* refactor(api): trim comments on entity name validation [ASTD-349]

Signed-off-by: mschwab <mschwab@nvidia.com>

* fix(api): document the special characters NAME_PATTERN accepts [ASTD-349]

NAME_PATTERN_DESCRIPTION claimed names may contain "only lowercase
letters, digits, and hyphens", but the pattern has allowed @, ., + and _
since it was written — see the TODO(#3530) directly above it. The spec
therefore told SDK consumers a name like llama-3.2-3b@v1.0.0 was invalid
when the service accepts it, and contradicted Studio's own help text,
which lists the special characters correctly.

All nine name fields in the spec now carry wording that matches the nine
patterns beside them.

Reported by CodeRabbit on NVIDIA-NeMo#978.

Signed-off-by: mschwab <mschwab@nvidia.com>

* chore(sdk): regenerate SDK and CLI for the name description [ASTD-349]

Picks up the NAME_PATTERN_DESCRIPTION change across the vendored Python
SDK, the generated CLI commands, and the CLI reference docs.

plugins/nemo-customizer/openapi/openapi.yaml is deliberately excluded:
regenerating it locally drops the whole rl/jobs surface, which comes from
an optional dependency that isn't installed here.

Signed-off-by: mschwab <mschwab@nvidia.com>

* fix(api): reword the entity name description [ASTD-349]

Two problems with the previous wording, "contain only lowercase letters,
digits, hyphens, and @ . + _".

The trailing underscore is markdown-escaped downstream, so the CLI
reference rendered "@ . + \__". Dropping the literal characters removes
the artifact.

Second, @, + and _ are slated for removal (TODO(#3530)), and advertising
them invites adoption ahead of that migration — mckornfield's point on
this PR. Dots are not in the same category: llama-3.1-8b is the example
in CreateModelProviderRequest and dotted versions are how model names are
written, so those stay documented.

"use" rather than "contain only" keeps this from being a false claim
about what the service rejects; the machine-readable pattern remains the
contract.

Signed-off-by: mschwab <mschwab@nvidia.com>

* chore(sdk): regenerate SDK and CLI for the reworded description [ASTD-349]

Propagates the NAME_PATTERN_DESCRIPTION rewording through the vendored
Python SDK, the generated CLI commands, and the CLI reference docs.

Also clears the "@ . + \__" escaping artifact the previous wording left in
docs/cli/reference.mdx.

Signed-off-by: mschwab <mschwab@nvidia.com>

* fix(studio): validate entity names against the entity-store pattern

Forms validated names against the SDK-generated regex `^[\w\-.]+$`, which is
what the service DTOs advertise in the OpenAPI spec. The entity store actually
enforces a stricter RFC-1035-ish pattern downstream, so names like `Sparl`
passed client-side validation and then 422'd on submit.

Add `entityName` to common: mirrors `NAME_PATTERN` from
packages/nmp_common/src/nmp/common/entities/constants.py and reports the
specific rule a value breaks, with a repaired-name suggestion where one exists
(`Sparl` -> `Name must be lowercase. Try "sparl".`).

Adopt it in the inference provider, secret, and fileset forms, which each
carried their own copy of the pattern or their own wording of the same rule.
The inference provider form also pre-empts the duplicate-name 409 using the
provider list it already fetches.

The underlying fix belongs in the OpenAPI spec: once the request DTOs declare
NAME_PATTERN, the generated zod is correct and this mirror can go away.

Signed-off-by: mschwab <mschwab@nvidia.com>

* refactor(studio): trim comments on entity name validation

Signed-off-by: mschwab <mschwab@nvidia.com>

* fix(studio): block provider submit until the provider list loads

The duplicate-name refinement runs against an empty set while the
providers query is in flight, so a duplicate could reach the API and
come back as a 409. Disable Add Provider until the list resolves.

Also give entityNameSchema an explicit return type.

Signed-off-by: mschwab <mschwab@nvidia.com>

* refactor(studio): source the entity name regex from generated zod

NVIDIA-NeMo#978 makes the create DTOs declare NAME_PATTERN, so the generated zod now
carries the strict pattern and entityName.ts no longer has to hand-mirror
it. Take ENTITY_NAME_REGEXP from entitiesCreateEntityBodyNameRegExp — the
generic entity-store endpoint, not one of the per-resource copies.

The max length stays local because the entity-store schema declares no
maxLength; a test pins it against the create DTOs that do. That test also
asserts the entity, fileset, secret, and model-provider schemas still
agree on the pattern, so a future drift between them fails CI.

The per-rule error messages and the sanitizer's character classes have no
generated equivalent and remain hand-written.

Signed-off-by: mschwab <mschwab@nvidia.com>

* refactor(studio): correct the stale name-override comments

Both claimed the generated zod uses a looser pattern than the entity
store. That stopped being true once the create DTOs started declaring
NAME_PATTERN — the generated schema now carries the identical rule.

The override survives only because generated zod reports a generic
pattern mismatch, while entityNameSchema names the rule the value breaks
and suggests a repair.

ExperimentCreateModal keeps its version of the comment: CreateExperimentBody.name
is still a bare zod.string() with no pattern at all.

Signed-off-by: mschwab <mschwab@nvidia.com>

---------

Signed-off-by: mschwab <mschwab@nvidia.com>
AnuradhaKaruppiah pushed a commit to AnuradhaKaruppiah/nemo-platform that referenced this pull request Aug 27, 2026
…ls DTOs [ASTD-420] (NVIDIA-NeMo#921)

* fix(api): declare the entity-store name pattern on the remaining models DTOs

NVIDIA-NeMo#978 tightened three DTOs — `CreateModelProviderRequest`, `CreateFilesetRequest`,
`PlatformSecretCreateRequest` — and collapsed NAME_PATTERN into a single
definition in `nemo_platform_plugin/entity_naming.py`. The rest of the models
service was left on the loose rule.

`CreatePromptRequest`, `CreateModelEntityRequest`, `CreateModelAdapterRequest`,
`CreateModelDeploymentConfigRequest`, `CreateModelDeploymentRequest`, and the
shared `ModelEntityBaseModel` still advertise `^[\w\-.]+$` with max_length 255,
while the entity store enforces `^[a-z](?!.*--)[a-z0-9\-@.+_]{1,62}(?<!-)$` with
max 63. So `Sparl` passes request validation, passes any generated client's
validation, and then 422s from the entity store with `loc: ['body', 'name']`
pointing at a body the caller never sent.

Declare NAME_PATTERN and NAME_MAX_LENGTH on those six. Read and lookup DTOs keep
the looser pattern so entities created before the rule stay reachable.

NAME_PATTERN uses lookaround, which pydantic's default rust-regex engine cannot
compile, so the affected models need `regex_engine="python-re"`. NVIDIA-NeMo#978 set that
inline on the one DTO it touched; this lifts it to a module-level
ENTITY_NAME_CONFIG rather than repeating the ConfigDict seven times.

Not a compatibility break: any name that passes the loose pattern but fails the
strict one already 422s at the entity store today. This moves the rejection to
the edge, where the error can name the actual field.

Regenerates openapi/openapi.yaml and the two GA variants — the diff is
exclusively pattern, maxLength, and description. Plugin specs regenerate with no
diff.

Signed-off-by: mschwab <mschwab@nvidia.com>

* chore(sdk): regenerate SDK for the entity-store name pattern

The name pattern and 63-character limit declared on the models DTOs had not
been propagated to the generated Python SDK, so lint-python-sdk failed with
the OpenAPI spec out of sync.

Signed-off-by: mschwab <mschwab@nvidia.com>

* chore(cli): regenerate CLI commands and reference for the name pattern

Signed-off-by: mschwab <mschwab@nvidia.com>

---------

Signed-off-by: mschwab <mschwab@nvidia.com>
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.

3 participants