Skip to content

feat: support OSS-backed model deployment#1352

Draft
yangeer wants to merge 15 commits into
ai-dynamo:mainfrom
yangeer:codex/webapp-oss
Draft

feat: support OSS-backed model deployment#1352
yangeer wants to merge 15 commits into
ai-dynamo:mainfrom
yangeer:codex/webapp-oss

Conversation

@yangeer

@yangeer yangeer commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What changed

  • add an AIConfigurator service entry point and Docker image
  • discover versioned models from S3-compatible OSS storage
  • generate vLLM DynamoGraphDeployment manifests that load models through ModelExpress
  • expose OSS model selection and deployment controls in the service UI
  • add unit coverage and deployment documentation

Why

The service previously assumed local or Hugging Face model paths. This adds an OSS-backed workflow for environments where model artifacts are stored in an S3-compatible object store and streamed into vLLM through ModelExpress.

Impact

Users can browse models from OSS, estimate configurations, and generate/deploy vLLM manifests without embedding credentials or mounting a local model directory. Existing non-OSS generator behavior remains the fallback.

Validation

  • TMPDIR=/tmp .venv/bin/pytest -s -q tests/unit/generator/test_aggregators.py tests/unit/generator/test_vllm_oss_deployment.py tests/unit/sdk/test_s3_models.py tests/unit/service (24 passed)
  • targeted ruff check for changed Python modules and tests
  • git diff --check

Summary by CodeRabbit

  • New Features

    • Added a web-based AIConfigurator service for estimates, Kubernetes artifact generation, deployment management, run history, and health checks.
    • Added containerized service deployment with non-root execution and readiness monitoring.
    • Added support for discovering and loading model configurations from S3-compatible storage.
    • Added Kubernetes deployment support for S3-hosted vLLM models and ModelExpress workflows.
    • Added configurable deployment names, node selectors, storage endpoints, regions, and secrets.
  • Bug Fixes

    • Improved model name resolution for local and S3 model paths.
    • Filtered unsupported vLLM deployment options to produce safer Kubernetes configurations.
  • Documentation

    • Added Kubernetes and S3-compatible storage deployment guidance.

@copy-pr-bot

copy-pr-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions github-actions Bot added the feat label Jul 14, 2026
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds a FastAPI deployment service and web UI, S3-compatible model discovery, OSS-aware vLLM Kubernetes generation, safer vLLM argument rendering, documentation, tests, and a multi-stage Docker image.

Changes

Service API and deployment UI

Layer / File(s) Summary
Service API, persistence, and request contracts
src/aiconfigurator/service/*, pyproject.toml, tests/unit/service/*
Adds typed request models, FastAPI endpoints, estimate and artifact workflows, Kubernetes apply/delete handling, run persistence, trace-aware errors, CLI startup, packaging metadata, and API coverage.
Deployment web interface
src/aiconfigurator/service/static/index.html
Adds the deployment-generation and deployment-records UI, including YAML preview, candidate selection, record lookup, and apply actions.

S3 model source

Layer / File(s) Summary
S3 model discovery and loading
src/aiconfigurator/sdk/{common.py,s3_models.py,utils.py}, tests/unit/sdk/test_s3_models.py
Adds versioned S3 model URI parsing, paginated discovery, JSON metadata loading, configured model selection, and S3 model-config support.
OSS deployment documentation
docs/dynamo_deployment_guide.md
Documents S3-compatible model catalogs, environment variables, runtime requirements, generator settings, and Kubernetes Secret requirements.

vLLM Kubernetes generation

Layer / File(s) Summary
OSS defaults and Kubernetes template
src/aiconfigurator/generator/config/deployment_config.yaml, src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2, src/aiconfigurator/generator/aggregators.py, tests/unit/generator/test_vllm_oss_deployment.py, tests/unit/generator/test_aggregators.py
Adds explicit deployment names, S3-derived defaults, OSS configuration fields, node selectors, metadata volumes, secret references, S3 bootstrap commands, and frontend/worker OSS rendering.
vLLM argument filtering
src/aiconfigurator/generator/rendering/engine.py, tests/unit/generator/test_vllm_minimal_k8s_args.py
Filters vLLM Kubernetes CLI arguments to an allowed set before Dynamo rendering and validates the retained and removed flags.

Service container

Layer / File(s) Summary
Multi-stage Docker runtime
docker/Dockerfile.service
Builds the service wheel, detects Git LFS pointers, installs service dependencies, creates a non-root runtime, configures S3/AWS settings, and adds healthchecked startup on port 8000.

Estimated code review effort: 5 (Critical) | ~120 minutes

Poem

S3 models sail through metadata seas,
vLLM pods boot with config keys.
A service UI lights the way,
Docker keeps the night at bay—
YAML blooms where deployments stay.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.91% 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 is concise and accurately summarizes the main change: adding OSS-backed model deployment support.
Description check ✅ Passed It covers the overview, rationale, impact, and validation, so the core required information is present.
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
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch codex/webapp-oss

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.

Actionable comments posted: 17

🧹 Nitpick comments (3)
tests/unit/sdk/test_s3_models.py (1)

35-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the default-source switch.

No test pins is_s3_model_source_configured()'s behavior when AIC_MODEL_SOURCE is unset, nor get_configured_s3_model_uris(). Given this default materially changes get_default_models() behavior app-wide, a test here would make the intended default explicit and guard against accidental flips.

🤖 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/unit/sdk/test_s3_models.py` around lines 35 - 68, Add unit coverage for
is_s3_model_source_configured() when AIC_MODEL_SOURCE is unset, asserting the
intended default source behavior, and cover get_configured_s3_model_uris() for
the corresponding configuration. Use environment monkeypatching and preserve the
existing cache-clearing fixture so the tests verify the default that
get_default_models() will use.
src/aiconfigurator/generator/rendering/engine.py (1)

538-544: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

k8s_context can be silently reset, dropping the vLLM filter if engine templates are ever added for vLLM.

The has_engine_templates branch reassigns k8s_context = dict(context) (568-570 region) without reapplying _filter_vllm_k8s_cli_args. Currently harmless since vLLM ships no extra_engine_args*.yaml.j2 templates, but if that changes later this silently reintroduces unsafe flags into the vLLM K8s manifest. Consider re-applying the filter after any k8s_context reset, or guarding the reset to skip vLLM.

Also applies to: 562-578

🤖 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 `@src/aiconfigurator/generator/rendering/engine.py` around lines 538 - 544,
Ensure the has_engine_templates branch in the rendering flow does not discard
the vLLM CLI-argument filtering applied to k8s_context. When it reassigns
k8s_context from context, reapply _filter_vllm_k8s_cli_args for each worker when
backend is vllm and deployment_target is dynamo-j2 or dynamo-python, or avoid
that reset for this case while preserving existing template handling.
src/aiconfigurator/service/app.py (1)

787-806: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Sync kubectl calls with up to 1800s timeout can starve FastAPI's threadpool under concurrent deploys.

ApplyDeploymentRequest.timeout_seconds allows up to 1800s, and these are sync def routes, so each long apply/delete/health-check call occupies a threadpool worker for its full duration. A handful of concurrent long deployments could exhaust the pool and stall unrelated requests.

Consider capping the practical timeout lower, or moving to asyncio.create_subprocess_exec / a background job queue so long-running kubectl operations don't block request-handling threads.

🤖 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 `@src/aiconfigurator/service/app.py` around lines 787 - 806, Update the
synchronous kubectl execution flow centered on _run_kubectl so deployment,
deletion, and health-check operations cannot occupy FastAPI’s request threadpool
for the full 1800-second request limit. Prefer moving kubectl execution to an
asynchronous subprocess or background-job mechanism; otherwise enforce a
substantially lower practical timeout consistently with
ApplyDeploymentRequest.timeout_seconds and the affected routes.
🤖 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 `@docker/Dockerfile.service`:
- Around line 26-28: Remove internal infrastructure defaults from
docker/Dockerfile.service lines 26-28 by replacing AIC_MODEL_S3_BUCKET,
AWS_ENDPOINT_URL, and AWS_DEFAULT_REGION with generic placeholders or requiring
explicit build/runtime configuration. Also update
src/aiconfigurator/service/static/index.html lines 605-643 to remove the
hardcoded internal image registry, node hostnames, OSS endpoint, region, and
secret name, using generic placeholders or blank values that require explicit
configuration.

In `@src/aiconfigurator/generator/aggregators.py`:
- Around line 73-75: Update the explicit-name handling near `explicit_name` and
`name` to enforce RFC 1123 DGD naming: lowercase alphanumeric characters and
hyphens only, with a maximum length of 63 characters. Choose and consistently
implement either rejecting invalid explicit names or normalizing them, and add
coverage for invalid characters and overlong names while preserving generated
fallback names.

In
`@src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2`:
- Around line 315-318: Remove the explicit --router-mode round-robin argument
from the OSS frontend command in the vllm deployment template, allowing
DYN_ROUTER_MODE=kv to control routing when enable_router is enabled. Preserve
the existing model-name and model-path arguments.
- Around line 158-164: Update the metadata download loop in the vLLM deployment
template so tokenizer.json and tokenizer_config.json are optional, matching the
existing optional handling for generation_config.json; only config.json must
remain required and download failures for it should still propagate. Apply the
same behavior to both affected loop blocks.

In `@src/aiconfigurator/generator/config/deployment_config.yaml`:
- Around line 23-29: Remove the circular defaults between
ServiceConfig.model_name and ServiceConfig.served_model_name in the deployment
configuration. Make one field authoritative and have the other derive from it in
only one direction, ensuring schema evaluation cannot leave both values unset.
- Around line 80-83: Update the vllm image selection around
ServiceConfig.model_path and generator_dynamo_version so OSS deployments do not
always use the hardcoded Dynamo 1.1.1 image. Resolve the runtime image using the
backend-version compatibility matrix for the requested generator_dynamo_version,
and reject unsupported backend/version combinations instead of emitting an
incompatible image.
- Around line 136-141: Update the deployment entries for
K8sConfig.frontend_node_selector and K8sConfig.worker_node_selector to declare
vLLM as their supported backend, while preserving their optional status and
empty-object defaults.

In `@src/aiconfigurator/sdk/s3_models.py`:
- Around line 72-101: The list_s3_models function performs an uncached
full-bucket scan on every invocation. Add a short-lived or per-process
memoization strategy around list_s3_models, keyed by its bucket input, while
preserving the existing pagination, sorting, and S3 error behavior; provide the
project’s established cache invalidation mechanism if one exists.
- Around line 123-124: Require explicit S3 opt-in in
is_s3_model_source_configured() by removing the implicit "s3" environment
default. In get_s3_client() and list_s3_models(), stop falling back to the
hardcoded endpoint, region, and bucket; instead require configuration and raise
clear errors when values are absent. Update docs/dynamo_deployment_guide.md to
use generic placeholders rather than those backend defaults.
- Around line 46-60: Update get_s3_client to configure explicit, bounded
botocore connect and read timeouts plus a capped retry policy in its Config,
while preserving the existing endpoint, region, and path-style addressing
behavior.

In `@src/aiconfigurator/service/app.py`:
- Around line 453-461: Update _attach_and_store_jid to persist each newly
generated run through _save_run_record instead of only calling
_cache_transient_run. Ensure generation-only runs are available after process
restarts or on other replicas, and remove or stop relying on the unbounded
_TRANSIENT_RUNS cache for this flow while preserving the existing JID attachment
behavior.
- Around line 69-79: Sanitize the attacker-controlled request path before
passing it to logger.exception in the add_trace_id middleware. Escape or strip
newline and other control characters from request.url.path, then use the
sanitized value in the extra metadata while preserving the existing trace ID and
error handling.
- Around line 824-837: Update the deployment/statefulset/daemonset branch in
_check_k8s_health to catch APIError from the rollout-status _run_kubectl call
and return an unhealthy health entry containing the rollout failure details
instead of propagating the exception. Preserve the successful response for
completed rollouts so _apply_k8s_deployment can still build its payload and mark
the deployment record as deployed, and add coverage for the failed or timed-out
rollout path.
- Around line 486-503: Redact sensitive credentials before storing run records:
update both _cache_transient_run and _save_run_record to pass request data
through the existing secret-redaction wrapper before normalization and
persistence. Ensure hf_token and any other supported secrets are removed or
masked consistently, so get_run cannot return plaintext credentials while
preserving the rest of the request record.
- Around line 713-763: Add an in-app authorization check at the entry points for
_apply_k8s_deployment and _delete_k8s_deployment before invoking kubectl, using
the service’s existing authz mechanism. Validate the resources returned by
_parse_k8s_resources, including kind, name, and namespace, and reject flag-like
or otherwise invalid identifiers before constructing any kubectl argv. Ensure
the same validated identifiers are used by _check_k8s_health and related
rollout/get commands.

In `@src/aiconfigurator/service/static/index.html`:
- Around line 827-842: Update deriveServedModelName and deriveDeploymentName to
generate RFC1123-compliant names: lowercase letters, digits, and hyphens only,
with each resulting name capped at 63 characters. Replace the current sanitizer
behavior that preserves dots and underscores, and ensure truncation does not
leave invalid leading or trailing hyphens.

In `@tests/unit/generator/test_vllm_oss_deployment.py`:
- Around line 33-73: Add a generator-level test alongside
test_vllm_oss_deployment_uses_model_express_and_secret_refs that uses an s3://
model path and runs apply_defaults followed by generate_backend_artifacts.
Validate the emitted k8s_deploy.yaml or golden artifact contains the expected
OSS endpoint, secret reference, model URI, and ModelExpress settings instead of
only rendering k8s_deploy.yaml.j2 directly.

---

Nitpick comments:
In `@src/aiconfigurator/generator/rendering/engine.py`:
- Around line 538-544: Ensure the has_engine_templates branch in the rendering
flow does not discard the vLLM CLI-argument filtering applied to k8s_context.
When it reassigns k8s_context from context, reapply _filter_vllm_k8s_cli_args
for each worker when backend is vllm and deployment_target is dynamo-j2 or
dynamo-python, or avoid that reset for this case while preserving existing
template handling.

In `@src/aiconfigurator/service/app.py`:
- Around line 787-806: Update the synchronous kubectl execution flow centered on
_run_kubectl so deployment, deletion, and health-check operations cannot occupy
FastAPI’s request threadpool for the full 1800-second request limit. Prefer
moving kubectl execution to an asynchronous subprocess or background-job
mechanism; otherwise enforce a substantially lower practical timeout
consistently with ApplyDeploymentRequest.timeout_seconds and the affected
routes.

In `@tests/unit/sdk/test_s3_models.py`:
- Around line 35-68: Add unit coverage for is_s3_model_source_configured() when
AIC_MODEL_SOURCE is unset, asserting the intended default source behavior, and
cover get_configured_s3_model_uris() for the corresponding configuration. Use
environment monkeypatching and preserve the existing cache-clearing fixture so
the tests verify the default that get_default_models() will use.
🪄 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: 9a224db8-66bc-494a-9273-55ed7d722df2

📥 Commits

Reviewing files that changed from the base of the PR and between 2feebac and 99b3a75.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock and included by none
📒 Files selected for processing (20)
  • docker/Dockerfile.service
  • docs/dynamo_deployment_guide.md
  • pyproject.toml
  • src/aiconfigurator/generator/aggregators.py
  • src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2
  • src/aiconfigurator/generator/config/deployment_config.yaml
  • src/aiconfigurator/generator/rendering/engine.py
  • src/aiconfigurator/sdk/common.py
  • src/aiconfigurator/sdk/s3_models.py
  • src/aiconfigurator/sdk/utils.py
  • src/aiconfigurator/service/__init__.py
  • src/aiconfigurator/service/app.py
  • src/aiconfigurator/service/main.py
  • src/aiconfigurator/service/models.py
  • src/aiconfigurator/service/static/index.html
  • tests/unit/generator/test_aggregators.py
  • tests/unit/generator/test_vllm_minimal_k8s_args.py
  • tests/unit/generator/test_vllm_oss_deployment.py
  • tests/unit/sdk/test_s3_models.py
  • tests/unit/service/test_api.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (24)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Use the project’s uv-managed environment and run linting with ruff check . and ruff format --check ..

Files:

  • src/aiconfigurator/service/__init__.py
  • src/aiconfigurator/generator/aggregators.py
  • src/aiconfigurator/service/main.py
  • tests/unit/generator/test_vllm_oss_deployment.py
  • src/aiconfigurator/sdk/utils.py
  • tests/unit/sdk/test_s3_models.py
  • tests/unit/generator/test_aggregators.py
  • src/aiconfigurator/sdk/s3_models.py
  • src/aiconfigurator/sdk/common.py
  • tests/unit/generator/test_vllm_minimal_k8s_args.py
  • src/aiconfigurator/service/models.py
  • src/aiconfigurator/generator/rendering/engine.py
  • tests/unit/service/test_api.py
  • src/aiconfigurator/service/app.py
**/*

⚙️ CodeRabbit configuration file

**/*: - Prefer applicable inline comments. When the correct fix is clear, small, and limited to the commented diff hunk, include it as a GitHub Suggested Change so the author can apply it with one click.

  • Do not use a suggested change when the fix requires broader design choices, multiple files, generated artifacts, unavailable context, or validation that cannot be inferred from the diff.
  • If a comment is not directly applicable, state the smallest concrete next step and why a one-click suggestion is not safe.

Files:

  • src/aiconfigurator/service/__init__.py
  • src/aiconfigurator/generator/aggregators.py
  • src/aiconfigurator/service/main.py
  • tests/unit/generator/test_vllm_oss_deployment.py
  • docs/dynamo_deployment_guide.md
  • src/aiconfigurator/sdk/utils.py
  • tests/unit/sdk/test_s3_models.py
  • tests/unit/generator/test_aggregators.py
  • docker/Dockerfile.service
  • src/aiconfigurator/service/static/index.html
  • src/aiconfigurator/sdk/s3_models.py
  • src/aiconfigurator/sdk/common.py
  • src/aiconfigurator/generator/config/deployment_config.yaml
  • tests/unit/generator/test_vllm_minimal_k8s_args.py
  • src/aiconfigurator/service/models.py
  • pyproject.toml
  • src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2
  • src/aiconfigurator/generator/rendering/engine.py
  • tests/unit/service/test_api.py
  • src/aiconfigurator/service/app.py
src/aiconfigurator/generator/**

📄 CodeRabbit inference engine (.claude/rules/generator/config_schema.md)

src/aiconfigurator/generator/**: Update artifact templates when a parameter requires special handling in generated outputs.
Validate configuration changes by running the generator with the parameter set and omitted, running the generator validator against backend API schemas, and checking --generator-help output.

When modifying generator code, review the cross-module impact map for affected modules outside src/aiconfigurator/generator/.

src/aiconfigurator/generator/**: For generator bugs, trace backwards from the generated output through template rendering, rule evaluation, parameter mapping, default application, input parsing, and schema loading.
Apply fixes at the stage where the defect originates rather than patching templates for rule or data-mapping bugs.
When fixing a generator issue, check all backends and all version templates for the same problem pattern.
After a generator fix, reproduce the original failure, run the generator validator, check other backends and versions, and add a regression test.

src/aiconfigurator/generator/**: TRT-LLM: Place build_config according to the selected version: nested before 1.2.0rc5 and top-level from 1.2.0rc5 onward.
TRT-LLM: Keep template variables as top-level keys because engine.py flattens the context.
TRT-LLM: Emit cache_transceiver_config according to the applicable version and interface.
TRT-LLM: Set cache_transceiver_config.backend to DEFAULT.
TRT-LLM: Put engine parameters in --override-engine-args JSON rather than unsupported direct CLI flags.
Ensure max_num_tokens % tokens_per_block == 0.
Ensure cache_transceiver_max_tokens_in_buffer is aligned to the block size.
Set disable_overlap_scheduler=true for prefill and false for decode/aggregate modes.
For MoE models, set TP = moe_tp * moe_ep.
Map KV cache dtype float16 or bfloat16 to auto for TRT-LLM.
For SGLang, do not emit --moe-dense-tp-size unless its value is 1 or absent.
Use KV transfer backend nixl by default for SGL...

Files:

  • src/aiconfigurator/generator/aggregators.py
  • src/aiconfigurator/generator/config/deployment_config.yaml
  • src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2
  • src/aiconfigurator/generator/rendering/engine.py

⚙️ CodeRabbit configuration file

src/aiconfigurator/generator/**: - Enforce the generator development rules from .claude/rules/generator-development.md.

  • Trace changes across the pipeline: input parsing, defaults, rule evaluation, parameter mapping, template rendering, and emitted artifacts.
  • For .j2 templates and .rule files, verify # Guard: and # Why: comments are preserved and followed. Flag removals or bypasses unless the PR explicitly justifies them.
  • Check cross-backend consistency across TRT-LLM, vLLM, and SGLang when a backend-specific rule, template, parameter, or versioned artifact changes.
  • For new user-facing generator parameters, require matching CLI, SDK/profiler bridge, generator validator, docs, and test coverage updates where applicable.
  • For versioned template changes, verify the affected backend versions against src/aiconfigurator/generator/config/backend_version_matrix.yaml.

Files:

  • src/aiconfigurator/generator/aggregators.py
  • src/aiconfigurator/generator/config/deployment_config.yaml
  • src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2
  • src/aiconfigurator/generator/rendering/engine.py
src/aiconfigurator/generator/aggregators.py

📄 CodeRabbit inference engine (.claude/rules/generator/cross_module_impact.md)

Update aggregators when renamed parameters are aggregated, and add backward-compatible aliases here when an old parameter name was user-facing.

Files:

  • src/aiconfigurator/generator/aggregators.py
src/aiconfigurator/generator/**/*.py

📄 CodeRabbit inference engine (.claude/rules/generator/new_backend_version.md)

Do not hardcode backend version checks in the rendering engine; use version-template naming and fallback logic instead of conditions such as if version >= ... in Python.

src/aiconfigurator/generator/**/*.py: For pure logic changes involving expressions or calculations, add unit-test coverage.
For new parameters or flags, add validator-test coverage.

Files:

  • src/aiconfigurator/generator/aggregators.py
  • src/aiconfigurator/generator/rendering/engine.py
src/aiconfigurator/generator/**/*.{py,jinja,j2,yaml,yml,json}

📄 CodeRabbit inference engine (.claude/rules/generator/testing.md)

For template, rule, or schema changes that affect generated output, add integration tests and update golden snapshots.

Files:

  • src/aiconfigurator/generator/aggregators.py
  • src/aiconfigurator/generator/config/deployment_config.yaml
  • src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2
  • src/aiconfigurator/generator/rendering/engine.py
src/aiconfigurator/generator/**/*

📄 CodeRabbit inference engine (.claude/rules/repo-guide.md)

src/aiconfigurator/generator/**/*: When editing or reviewing src/aiconfigurator/generator/**, load and apply the generator rules, including .claude/rules/generator-development.md.
Do not drift into deployment-config topics unless the task actually targets src/aiconfigurator/generator/.

Files:

  • src/aiconfigurator/generator/aggregators.py
  • src/aiconfigurator/generator/config/deployment_config.yaml
  • src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2
  • src/aiconfigurator/generator/rendering/engine.py
tests/unit/generator/**

📄 CodeRabbit inference engine (.claude/rules/generator/cross_module_impact.md)

Update generator test fixtures and assertions when parameters are renamed.

Files:

  • tests/unit/generator/test_vllm_oss_deployment.py
  • tests/unit/generator/test_aggregators.py
  • tests/unit/generator/test_vllm_minimal_k8s_args.py
tests/unit/generator/**/*.py

📄 CodeRabbit inference engine (.claude/rules/generator/testing.md)

tests/unit/generator/**/*.py: Use fast unit tests for pure generator logic, testing individual functions in isolation and mocking schema loading and template rendering.
Test generator edge cases including MoE, disaggregated mode, aggregated mode, speculative decoding, prefix caching, minimal configuration, PVC configuration, each backend, and benchmark mode.

Files:

  • tests/unit/generator/test_vllm_oss_deployment.py
  • tests/unit/generator/test_aggregators.py
  • tests/unit/generator/test_vllm_minimal_k8s_args.py
tests/**/*.{py,yaml,txt,sh}

📄 CodeRabbit inference engine (.claude/rules/generator/testing.md)

Use integration tests for the full input-to-artifacts pipeline, comparing output against golden snapshots without external dependencies.

Files:

  • tests/unit/generator/test_vllm_oss_deployment.py
  • tests/unit/sdk/test_s3_models.py
  • tests/unit/generator/test_aggregators.py
  • tests/unit/generator/test_vllm_minimal_k8s_args.py
  • tests/unit/service/test_api.py
tests/unit/generator/test_*.py

📄 CodeRabbit inference engine (.claude/rules/generator/testing.md)

Place generator unit tests in tests/unit/generator/ and use descriptive test files such as test_aggregators.py, test_rule_engine.py, and backend-specific CLI argument tests.

Files:

  • tests/unit/generator/test_vllm_oss_deployment.py
  • tests/unit/generator/test_aggregators.py
  • tests/unit/generator/test_vllm_minimal_k8s_args.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Run unit tests with pytest -m unit; run the PR build subset with pytest -m "unit or build" when applicable.

Files:

  • tests/unit/generator/test_vllm_oss_deployment.py
  • tests/unit/sdk/test_s3_models.py
  • tests/unit/generator/test_aggregators.py
  • tests/unit/generator/test_vllm_minimal_k8s_args.py
  • tests/unit/service/test_api.py
tests/**

⚙️ CodeRabbit configuration file

tests/**: - Check that tests cover the changed behavior rather than only the happy path.

  • Watch for fixtures or golden outputs that mask backend drift, support-matrix ordering changes, or CLI output regressions.

Files:

  • tests/unit/generator/test_vllm_oss_deployment.py
  • tests/unit/sdk/test_s3_models.py
  • tests/unit/generator/test_aggregators.py
  • tests/unit/generator/test_vllm_minimal_k8s_args.py
  • tests/unit/service/test_api.py
docs/**

⚙️ CodeRabbit configuration file

docs/**: - Check that docs match changed CLI, SDK, generator, backend, and support-matrix behavior.

  • Flag docs that describe unsupported runtimes, stale command names, or behavior not covered by tests or support-matrix evidence.

Files:

  • docs/dynamo_deployment_guide.md
src/aiconfigurator/sdk/**

⚙️ CodeRabbit configuration file

src/aiconfigurator/sdk/**: - Verify SDK API changes remain compatible with generator inputs, profiler data flow, and documented examples.

  • Flag silent schema or field-name drift between SDK models and generator/module bridge code.

Files:

  • src/aiconfigurator/sdk/utils.py
  • src/aiconfigurator/sdk/s3_models.py
  • src/aiconfigurator/sdk/common.py
tests/unit/generator/test_aggregators.py

📄 CodeRabbit inference engine (.claude/rules/generator/testing.md)

Cover parameter collection and default behavior in aggregator tests.

Files:

  • tests/unit/generator/test_aggregators.py
src/aiconfigurator/generator/config/deployment_config.yaml

📄 CodeRabbit inference engine (.claude/rules/generator/config_schema.md)

src/aiconfigurator/generator/config/deployment_config.yaml: Define deployment configuration entries with a section-qualified key, required status, default, optional backend_defaults, and optional backend restrictions.
Place new parameters in the correct section, determine whether they are required or optional, define static or computed defaults, and specify backend support.
For computed defaults, verify referenced variables are available during default evaluation and handle missing or None values safely.
Quote string Jinja2 defaults with double quotes inside single quotes, such as default: '"my-string"'.
Guard default expressions against missing values, for example use model_path.split("/")[-1] if model_path else "".
Avoid circular default references between parameters because the schema loader may produce None for both; verify the dependency chain is acyclic.

Define new parameters, defaults, and backend support in deployment_config.yaml. Update parameter keys when renaming parameters.

Verify parameter default expressions and applicable backend_defaults when a value is missing or None.

For new user-facing parameters, add a schema entry with section, default, required status, and backend-specific defaults where behavior differs; verify default container-image expressions and version-tag resolution.

Add new input parameters and their defaults to the deployment configuration schema.

Files:

  • src/aiconfigurator/generator/config/deployment_config.yaml
src/aiconfigurator/generator/config/**/*.yaml

📄 CodeRabbit inference engine (.claude/rules/generator/cross_module_impact.md)

When modifying template output, verify generated Kubernetes manifests and engine configuration files against their required schemas.

Files:

  • src/aiconfigurator/generator/config/deployment_config.yaml
src/aiconfigurator/generator/config/*.{j2,yaml}

📄 CodeRabbit inference engine (.claude/rules/generator/new_backend_version.md)

Create version-specific templates only when the backend CLI interface, engine configuration format, or startup command changes; do not create templates for no-op version bumps.

Files:

  • src/aiconfigurator/generator/config/deployment_config.yaml
src/aiconfigurator/generator/**/*.{j2,rule}

📄 CodeRabbit inference engine (.claude/rules/generator-development.md)

Before modifying any .j2 template or .rule file, read ALL comments in the file. Do not silently remove or bypass guard comments marked with # Guard: or # Why:. If a requested change contradicts a guarded comment, quote the comment, explain why it exists, and ask for confirmation before proceeding.

Files:

  • src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2
src/aiconfigurator/generator/config/backend_templates/**

📄 CodeRabbit inference engine (.claude/rules/generator/cross_module_impact.md)

Add or update version-specific backend templates when a new backend version changes CLI behavior.

Files:

  • src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2
src/aiconfigurator/generator/**/*.j2

📄 CodeRabbit inference engine (.claude/rules/generator/cross_module_impact.md)

When changing generated run scripts, verify Bash syntax with bash -n generated_run.sh; when changing Kubernetes output, verify YAML with kubectl apply --dry-run.

src/aiconfigurator/generator/**/*.j2: For a changed backend interface, copy the closest prior template, update flags or conditionals, and preserve the prior version template unchanged.
For deprecated backend CLI flags, retain the old flag in templates before the compatibility boundary and use the replacement flag in templates at or after the boundary.
Do not modify old version templates when introducing a replacement for a deprecated or renamed flag; create a new version-specific template instead.
Rely on template selection order—exact version, closest prior version, then base template—instead of creating a template for every version.

Files:

  • src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2
src/aiconfigurator/generator/config/backend_templates/**/*.j2

📄 CodeRabbit inference engine (.claude/rules/generator/debugging.md)

src/aiconfigurator/generator/config/backend_templates/**/*.j2: When investigating incorrect generated flags, blocks, or YAML structure, inspect the responsible Jinja template and verify its variables match the rendering context.
When changing templates, inspect every version-specific template for stale variable references, deprecated flags, and mode guards.

src/aiconfigurator/generator/config/backend_templates/**/*.j2: Keep backend-specific Jinja2 templates standalone; do not extract shared templates or macros.
When adding a new backend version template, copy the closest prior version and modify the copy; never edit prior-version templates.
Apply fixes to all affected version-specific templates and enumerate them explicitly.
Do not add a template variable unless its source can be traced through the pipeline.
Verify every variable referenced by a modified template comes from deployment input, a rule plugin, backend mapping, or the rendering context.
For each affected parameter, compare all backend templates and identify missing, inconsistent, or intentionally different handling.
When changing a parameter, verify all affected backends, artifact types, and version-specific templates.
List all version-specific templates affected by a fix, using a pattern such as backend_templates/<backend>/<artifact>*.j2.
Every template variable must be traceable through the pipeline: input, default, rule, mapping, context, and template.
Compare generated output against golden artifacts when available.

Files:

  • src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2
src/aiconfigurator/generator/rendering/engine.py

📄 CodeRabbit inference engine (.claude/rules/generator/debugging.md)

Trace template variables through make_worker_context() to verify how values enter the rendering context.

Inspect and update make_worker_context() when template variables are injected or context shape changes.

Files:

  • src/aiconfigurator/generator/rendering/engine.py
🧠 Learnings (1)
📚 Learning: 2026-05-01T00:39:37.334Z
Learnt from: simone-chen
Repo: ai-dynamo/aiconfigurator PR: 956
File: src/aiconfigurator/sdk/perf_database.py:4144-4151
Timestamp: 2026-05-01T00:39:37.334Z
Learning: In src/aiconfigurator/sdk/**/*.py, preserve upstream metadata for PerformanceResult.source: since PerformanceResult defaults source to "silicon", callers should not force-set result.source (e.g., PerfDatabase._query_silicon_or_hybrid should rely on the default and keep any existing source information rather than overwriting it). Only set source explicitly when you truly intend to change it.

Applied to files:

  • src/aiconfigurator/sdk/utils.py
  • src/aiconfigurator/sdk/s3_models.py
  • src/aiconfigurator/sdk/common.py
🪛 ast-grep (0.44.1)
tests/unit/generator/test_vllm_oss_deployment.py

[warning] 43-43: Do not make http calls without encryption
Context: "http://model-express:8001"
Note: [CWE-319] Cleartext Transmission of Sensitive Information.

(requests-http)

src/aiconfigurator/service/static/index.html

[warning] 799-799: Avoid using the initial state variable in setState
Context: setText("run-status", value)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)


[warning] 1449-1449: Avoid using the initial state variable in setState
Context: setCode("deployment-selected-output", content)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)


[warning] 1458-1458: Avoid using the initial state variable in setState
Context: setCode("records-yaml-output", content)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)


[warning] 1493-1493: Avoid using the initial state variable in setState
Context: setSelectedDgd(recommendedArtifact, 推荐方案:${state.selected.mode} Top 1。)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)


[warning] 1561-1561: Avoid using the initial state variable in setState
Context: setRecordsDgd(artifact, deployed ? Final deployed DGD: ${artifact.dgd_id} : Generated only. Showing recommended top1 DGD: ${artifact.dgd_id})
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)


[warning] 1586-1586: Avoid using the initial state variable in setState
Context: setSelectedDgd(payload, note || ${mode} row ${rowIndex}.)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)


[error] 799-799: React's useState should not be directly called
Context: setText("run-status", value)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1449-1449: React's useState should not be directly called
Context: setCode("deployment-selected-output", content)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1450-1450: React's useState should not be directly called
Context: setText("current-dgd-id", artifact && artifact.dgd_id ? artifact.dgd_id : "无")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1458-1458: React's useState should not be directly called
Context: setCode("records-yaml-output", content)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1485-1485: React's useState should not be directly called
Context: setText("current-jid", state.jid || "尚未生成")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1493-1493: React's useState should not be directly called
Context: setSelectedDgd(recommendedArtifact, 推荐方案:${state.selected.mode} Top 1。)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1496-1496: React's useState should not be directly called
Context: setSelectedDgd(null)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1508-1508: React's useState should not be directly called
Context: setStatus("正在加载记录")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1509-1509: React's useState should not be directly called
Context: setError("")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1517-1517: React's useState should not be directly called
Context: setCode("estimate-output", jsonPretty(record))
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1530-1530: React's useState should not be directly called
Context: setStatus(deployed ? "已部署" : "已生成")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1545-1545: React's useState should not be directly called
Context: setStatus("正在加载记录")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1546-1546: React's useState should not be directly called
Context: setError("")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1547-1547: React's useState should not be directly called
Context: setRecordsDgd(null, "正在加载记录...")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1561-1561: React's useState should not be directly called
Context: setRecordsDgd(artifact, deployed ? Final deployed DGD: ${artifact.dgd_id} : Generated only. Showing recommended top1 DGD: ${artifact.dgd_id})
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1563-1563: React's useState should not be directly called
Context: setStatus(deployed ? "已部署" : "已生成")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1576-1576: React's useState should not be directly called
Context: setStatus("正在查看 DGD")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1577-1577: React's useState should not be directly called
Context: setError("")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1586-1586: React's useState should not be directly called
Context: setSelectedDgd(payload, note || ${mode} row ${rowIndex}.)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1594-1594: React's useState should not be directly called
Context: setStatus("已生成")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1645-1645: React's useState should not be directly called
Context: setStatus("正在估算")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1646-1646: React's useState should not be directly called
Context: setError("")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1647-1647: React's useState should not be directly called
Context: setText("current-jid", "生成中...")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1648-1648: React's useState should not be directly called
Context: setText("current-dgd-id", "无")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1649-1649: React's useState should not be directly called
Context: setSelectedDgd(null, "正在估算,请稍候。")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1662-1662: React's useState should not be directly called
Context: setStatus("已生成")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1665-1665: React's useState should not be directly called
Context: setCode("estimate-output", jsonPretty(payload.estimate))
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1678-1678: React's useState should not be directly called
Context: setStatus("正在部署")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1679-1679: React's useState should not be directly called
Context: setError("")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1680-1680: React's useState should not be directly called
Context: setCode("apply-output", 正在部署 ${selectedArtifact.dgd_id || selectedArtifact.mode}...)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1693-1693: React's useState should not be directly called
Context: setStatus("已部署")
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1694-1694: React's useState should not be directly called
Context: setCode("apply-output", jsonPretty(payload))
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)


[error] 1826-1826: React's useState should not be directly called
Context: setCode("estimate-output", 加载选项失败:${error.message})
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)

tests/unit/service/test_api.py

[info] 222-222: Do not hardcode temporary file or directory names
Context: "/tmp"
Note: [CWE-377] Insecure Temporary File.

(hardcoded-tmp-file)

src/aiconfigurator/service/app.py

[info] 49-49: Do not hardcode temporary file or directory names
Context: "/tmp/aiconfigurator-service/runs.json"
Note: [CWE-377] Insecure Temporary File.

(hardcoded-tmp-file)


[warning] 75-75: Logging request-derived input unsanitized allows log forging (CRLF injection); strip newlines / encode the value before logging.
Context: logger.exception("Unhandled error", extra={"trace_id": trace_id, "path": request.url.path})
Note: [CWE-117] Improper Output Neutralization for Logs. OWASP A09:2021 Security Logging and Monitoring Failures.

(log-injection-python)


[error] 788-795: Use of unsanitized data to create processes
Context: subprocess.run(
args,
input=stdin,
text=True,
capture_output=True,
timeout=timeout,
check=False,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(os-system-unsanitized-data)


[error] 788-795: Command coming from incoming request
Context: subprocess.run(
args,
input=stdin,
text=True,
capture_output=True,
timeout=timeout,
check=False,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

🔇 Additional comments (15)
src/aiconfigurator/sdk/utils.py (1)

1102-1108: LGTM!

src/aiconfigurator/generator/rendering/engine.py (1)

34-48: 🎯 Functional Correctness

No issue: --kv-transfer-config is injected by the vLLM templates. This allowlist omission doesn't strip it from disagg K8s output.

			> Likely an incorrect or invalid review comment.
docs/dynamo_deployment_guide.md (1)

510-517: 🗄️ Data Integrity & Integration

No change needed: K8sConfig.oss_* is already in the deployment schema and scoped to vllm.

src/aiconfigurator/sdk/common.py (1)

390-402: 🚀 Performance & Scalability

Drop the caching concern here. get_support_matrix() and get_configured_s3_model_uris() are already memoized, so this wrapper only rebuilds a small set.

			> Likely an incorrect or invalid review comment.
tests/unit/generator/test_aggregators.py (1)

11-42: LGTM!

tests/unit/generator/test_vllm_minimal_k8s_args.py (1)

9-58: LGTM!

pyproject.toml (1)

43-43: LGTM!

Also applies to: 63-63, 95-95, 109-109

src/aiconfigurator/service/__init__.py (1)

1-7: LGTM!

src/aiconfigurator/service/models.py (1)

1-173: LGTM!

src/aiconfigurator/service/main.py (1)

1-39: LGTM!

src/aiconfigurator/service/app.py (1)

100-266: LGTM!

Also applies to: 295-374, 673-711, 980-1046

src/aiconfigurator/service/static/index.html (1)

1-599: LGTM!

Also applies to: 644-1835

tests/unit/service/test_api.py (1)

1-351: LGTM!

docker/Dockerfile.service (2)

1-25: LGTM!

Also applies to: 29-33, 37-60


34-36: 🎯 Functional Correctness

No issue: pyproject.toml already defines the service extra.

			> Likely an incorrect or invalid review comment.

Comment thread docker/Dockerfile.service
Comment on lines +26 to +28
ARG AIC_MODEL_S3_BUCKET=aiplat
ARG AWS_ENDPOINT_URL=https://oss-s3.haiercash.com
ARG AWS_DEFAULT_REGION=cn-east-1

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Internal company infrastructure identifiers hardcoded as shipped defaults. Both the container image and the demo UI bake in the same internal org's domain (haiercash.com), a private registry mirror path, specific node hostnames, and a secret name — this looks like leftover internal/demo config that shouldn't ship as the default in a public repo.

  • docker/Dockerfile.service#L26-L28: replace https://oss-s3.haiercash.com / cn-east-1 / aiplat with generic placeholder values (e.g. https://s3.example.com, us-east-1) or require them to be supplied at build/run time with no baked-in org-specific default.
  • src/aiconfigurator/service/static/index.html#L605-L643: replace the default image (parties-hub.haiercash.com/...), frontend-node/worker-node (kubeflow-master2), oss-endpoint, oss-region, and oss-secret-name values with generic placeholders (or leave blank and require explicit configuration).
📍 Affects 2 files
  • docker/Dockerfile.service#L26-L28 (this comment)
  • src/aiconfigurator/service/static/index.html#L605-L643
🤖 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 `@docker/Dockerfile.service` around lines 26 - 28, Remove internal
infrastructure defaults from docker/Dockerfile.service lines 26-28 by replacing
AIC_MODEL_S3_BUCKET, AWS_ENDPOINT_URL, and AWS_DEFAULT_REGION with generic
placeholders or requiring explicit build/runtime configuration. Also update
src/aiconfigurator/service/static/index.html lines 605-643 to remove the
hardcoded internal image registry, node hostnames, OSS endpoint, region, and
secret name, using generic placeholders or blank values that require explicit
configuration.

Comment on lines +73 to +75
explicit_name = k8s.get("name")
explicit_name = explicit_name.strip() if isinstance(explicit_name, str) else ""
name = explicit_name or f"{name_prefix}-{mode_tag}{('-router' if enable_router else '')}"

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate explicit deployment names as RFC 1123 identifiers.

A value such as UI_Model_1 now reaches metadata.name unchanged and makes the generated DGD invalid. Reject or normalize explicit names and test invalid characters and the 63-character limit. A one-click fix is unsafe because the API must choose between rejection and normalization.

As per coding guidelines, “Generate RFC 1123-compliant DGD names: lowercase alphanumeric characters and hyphens, no longer than 63 characters.”

🤖 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 `@src/aiconfigurator/generator/aggregators.py` around lines 73 - 75, Update the
explicit-name handling near `explicit_name` and `name` to enforce RFC 1123 DGD
naming: lowercase alphanumeric characters and hyphens only, with a maximum
length of 63 characters. Choose and consistently implement either rejecting
invalid explicit names or normalizing them, and add coverage for invalid
characters and overlong names while preserving generated fallback names.

Sources: Coding guidelines, Path instructions

Comment on lines +158 to +164
for name in ("config.json", "tokenizer.json", "tokenizer_config.json", "generation_config.json"):
try:
s3.download_file(bucket, f"{prefix}/{name}", os.path.join(meta_dir, name))
except Exception:
if name == "generation_config.json":
continue
raise

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -t f 's3_models.py|test_s3_models.py' -x \
  rg -n -C3 'tokenizer\.json|tokenizer_config\.json|config\.json' {}

Repository: ai-dynamo/aiconfigurator

Length of output: 2052


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the vLLM deployment template around both bootstrap loops.
FILE="src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2"
nl -ba "$FILE" | sed -n '140,180p'
printf '\n---\n'
nl -ba "$FILE" | sed -n '290,325p'

printf '\n=== related S3/model-loading code ===\n'
fd -t f 's3_models.py|*s3*model*|*model*loader*|*model*contract*|*tokenizer*' src | sed -n '1,200p'
printf '\n--- matches ---\n'
rg -n -C3 'tokenizer\.json|tokenizer_config\.json|generation_config\.json|config\.json|download_file|list_s3_models|S3ModelError' src tests

Repository: ai-dynamo/aiconfigurator

Length of output: 202


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2"

printf '=== template excerpt 1 ===\n'
sed -n '150,175p' "$FILE" | cat -n
printf '\n=== template excerpt 2 ===\n'
sed -n '300,320p' "$FILE" | cat -n

printf '\n=== relevant files ===\n'
fd -t f 's3_models.py|test_s3_models.py|*model*loader*|*tokenizer*' src tests

printf '\n=== symbol/text matches ===\n'
rg -n -C3 'tokenizer\.json|tokenizer_config\.json|generation_config\.json|config\.json|download_file|list_s3_models|S3ModelError' src tests

Repository: ai-dynamo/aiconfigurator

Length of output: 3132


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '=== candidate files ===\n'
fd -t f 's3_models.py' src tests
fd -t f 'test_s3_models.py' src tests

printf '\n=== s3_models.py ===\n'
sed -n '1,220p' src/aiconfigurator/generator/config/s3_models.py

printf '\n=== tests matching s3_models ===\n'
rg -n -C3 'list_s3_models|load_s3_json|tokenizer\.json|tokenizer_config\.json|generation_config\.json|config\.json' tests src/aiconfigurator/generator/config

Repository: ai-dynamo/aiconfigurator

Length of output: 402


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '=== s3_models.py ===\n'
sed -n '1,260p' src/aiconfigurator/sdk/s3_models.py

printf '\n=== test_s3_models.py ===\n'
sed -n '1,240p' tests/unit/sdk/test_s3_models.py

printf '\n=== tokenizer / config matches in repo ===\n'
rg -n -C2 'tokenizer\.json|tokenizer_config\.json|generation_config\.json|config\.json|list_s3_models|load_s3_json|S3ModelError' src tests

Repository: ai-dynamo/aiconfigurator

Length of output: 37537


OSS bootstrap should not require tokenizer files by default
src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2:158-164,307-313 only enforces config.json; the current loop turns tokenizer.json and tokenizer_config.json into hard requirements even though OSS discovery/model loading don’t. Either reject such models earlier or make these downloads optional.

🤖 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
`@src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2`
around lines 158 - 164, Update the metadata download loop in the vLLM deployment
template so tokenizer.json and tokenizer_config.json are optional, matching the
existing optional handling for generation_config.json; only config.json must
remain required and download failures for it should still propagate. Apply the
same behavior to both affected loop blocks.

Source: Path instructions

Comment on lines +315 to +318
exec python3 -m dynamo.frontend \
--model-name {{ served_model_name | tojson }} \
--model-path "${META_DIR}" \
--router-mode round-robin

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve KV routing in the OSS frontend command.

The explicit round-robin argument conflicts with DYN_ROUTER_MODE=kv when enable_router is true, so OSS deployments cannot use the requested KV router.

Proposed fix
               exec python3 -m dynamo.frontend \
                 --model-name {{ served_model_name | tojson }} \
                 --model-path "${META_DIR}" \
-                --router-mode round-robin
+                --router-mode {{ ('kv' if enable_router else 'round-robin') | tojson }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exec python3 -m dynamo.frontend \
--model-name {{ served_model_name | tojson }} \
--model-path "${META_DIR}" \
--router-mode round-robin
exec python3 -m dynamo.frontend \
--model-name {{ served_model_name | tojson }} \
--model-path "${META_DIR}" \
--router-mode {{ ('kv' if enable_router else 'round-robin') | tojson }}
🤖 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
`@src/aiconfigurator/generator/config/backend_templates/vllm/k8s_deploy.yaml.j2`
around lines 315 - 318, Remove the explicit --router-mode round-robin argument
from the OSS frontend command in the vllm deployment template, allowing
DYN_ROUTER_MODE=kv to control routing when enable_router is enabled. Preserve
the existing model-name and model-path arguments.

Source: Path instructions

Comment on lines +23 to 29
default: >-
ServiceConfig.model_name or
(ServiceConfig.model_path.split('/')[-2] if ServiceConfig.model_path.startswith('s3://') else
(ServiceConfig.served_model_path or ServiceConfig.model_path))
- key: ServiceConfig.model_name
required: false
default: "ServiceConfig.served_model_name"

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove the circular model-name defaults.

served_model_name reads model_name, while model_name defaults back to served_model_name. Make one field authoritative and derive the other in a single direction; otherwise schema evaluation order can leave both unset.

As per coding guidelines, “Avoid circular default references between parameters because the schema loader may produce None for both.”

🤖 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 `@src/aiconfigurator/generator/config/deployment_config.yaml` around lines 23 -
29, Remove the circular defaults between ServiceConfig.model_name and
ServiceConfig.served_model_name in the deployment configuration. Make one field
authoritative and have the other derive from it in only one direction, ensuring
schema evaluation cannot leave both values unset.

Source: Coding guidelines

Comment on lines +486 to +503
def _cache_transient_run(jid: str, request: ArtifactRequest, payload: dict[str, Any]) -> None:
_TRANSIENT_RUNS[jid] = {
"jid": jid,
"created_at": datetime.now(timezone.utc).isoformat(),
"request": _normalize_obj(request.model_dump(exclude_none=True)),
"payload": _normalize_obj(payload),
}


def _save_run_record(jid: str, request: ArtifactRequest, payload: dict[str, Any]) -> None:
record = {
"jid": jid,
"created_at": datetime.now(timezone.utc).isoformat(),
"request": _normalize_obj(request.model_dump(exclude_none=True)),
"payload": _normalize_obj(payload),
}
_save_record(jid, record)

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.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Credential (hf_token) is persisted to disk and echoed back by GET /api/v1/runs/{jid}.

_cache_transient_run/_save_run_record dump the raw ArtifactRequest (including generator_overrides.service_config.hf_token when supplied) into the run store. get_run (Line 217-222) later returns that record verbatim, so any caller who learns a jid receives the Hugging Face token in plaintext, and it also lands unredacted in the run-store JSON file on disk.

🔒 Redact secrets before persisting/returning run records
+def _redact_secrets(data: dict[str, Any]) -> dict[str, Any]:
+    redacted = copy.deepcopy(data)
+    service_cfg = (redacted.get("generator_overrides") or {}).get("service_config")
+    if isinstance(service_cfg, dict) and service_cfg.get("hf_token"):
+        service_cfg["hf_token"] = "***redacted***"
+    return redacted
+
+
 def _cache_transient_run(jid: str, request: ArtifactRequest, payload: dict[str, Any]) -> None:
     _TRANSIENT_RUNS[jid] = {
         "jid": jid,
         "created_at": datetime.now(timezone.utc).isoformat(),
-        "request": _normalize_obj(request.model_dump(exclude_none=True)),
+        "request": _redact_secrets(_normalize_obj(request.model_dump(exclude_none=True))),
         "payload": _normalize_obj(payload),
     }

Apply the same wrapper in _save_run_record.

🤖 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 `@src/aiconfigurator/service/app.py` around lines 486 - 503, Redact sensitive
credentials before storing run records: update both _cache_transient_run and
_save_run_record to pass request data through the existing secret-redaction
wrapper before normalization and persistence. Ensure hf_token and any other
supported secrets are removed or masked consistently, so get_run cannot return
plaintext credentials while preserving the rest of the request record.

Comment on lines +713 to +763
def _apply_k8s_deployment(request: ApplyDeploymentRequest) -> dict[str, Any]:
if shutil.which("kubectl") is None:
raise APIError(
503,
"kubectl_not_available",
"kubectl is not available in the service runtime.",
{"hint": "Install kubectl in the AIC service image and grant its ServiceAccount apply/get permissions."},
)

resources = _parse_k8s_resources(request.content, request.namespace)
apply_result = _run_kubectl(
["kubectl", "apply", "-f", "-"],
stdin=request.content,
timeout=request.timeout_seconds,
)
health = _check_k8s_health(resources, request.timeout_seconds)
payload = {
"applied": apply_result.returncode == 0,
"stdout": apply_result.stdout,
"stderr": apply_result.stderr,
"resources": resources,
"health": health,
"healthy": all(item.get("exists") for item in health),
}
_mark_deployed_dgd(request, payload)
return payload


def _delete_k8s_deployment(request: DeleteDeploymentRequest) -> dict[str, Any]:
if shutil.which("kubectl") is None:
raise APIError(
503,
"kubectl_not_available",
"kubectl is not available in the service runtime.",
{"hint": "Install kubectl in the AIC service image and grant its ServiceAccount delete/get permissions."},
)

resources = _parse_k8s_resources(request.content, request.namespace)
delete_result = _run_kubectl(
["kubectl", "delete", "-f", "-", "--ignore-not-found=true"],
stdin=request.content,
timeout=request.timeout_seconds,
)
payload = {
"deleted": delete_result.returncode == 0,
"stdout": delete_result.stdout,
"stderr": delete_result.stderr,
"resources": resources,
}
_mark_deleted_dgd(request, payload)
return payload

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.

🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file first, then inspect the route and helper definitions around the cited lines.
ast-grep outline src/aiconfigurator/service/app.py --view expanded || true

printf '\n--- lines 650-900 ---\n'
sed -n '650,900p' src/aiconfigurator/service/app.py

Repository: ai-dynamo/aiconfigurator

Length of output: 13815


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect request models and any auth-related middleware/decorators used by the app.
rg -n "create_app|APIRouter|Depends|OAuth|auth|authorize|permission|kubectl|get rollout|rollout status|_parse_k8s_resources|K8sConfigInput|ApplyDeploymentRequest|DeleteDeploymentRequest" src/aiconfigurator/service/app.py src/aiconfigurator -S

Repository: ai-dynamo/aiconfigurator

Length of output: 8206


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether the manifest parser or request model constrains Kubernetes kinds/namespaces.
sed -n '1,220p' src/aiconfigurator/service/app.py

Repository: ai-dynamo/aiconfigurator

Length of output: 8802


🏁 Script executed:

#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import pathlib, re
path = pathlib.Path('src/aiconfigurator/service/app.py')
text = path.read_text()
for needle in ['def create_app', 'def _parse_k8s_resources', 'ApplyDeploymentRequest', 'DeleteDeploymentRequest', 'K8sConfigInput', 'kubectl get', 'rollout status']:
    print(f'## {needle}')
    for m in re.finditer(re.escape(needle), text):
        start = max(0, m.start()-400)
        end = min(len(text), m.end()+800)
        print(text[start:end])
        print('\n---\n')
        break
PY

Repository: ai-dynamo/aiconfigurator

Length of output: 5223


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the request models for validation on manifest content, kind, name, namespace, and timeout.
ast-grep outline src/aiconfigurator/service/models.py --view expanded || true
printf '\n--- models excerpt ---\n'
sed -n '1,260p' src/aiconfigurator/service/models.py

Repository: ai-dynamo/aiconfigurator

Length of output: 12109


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for any authn/authz middleware, dependencies, API keys, or security schemes in the service package.
rg -n "Depends\(|APIKey|Authorization|authn|authz|oauth|JWT|Bearer|security|permission|role|middleware" src/aiconfigurator/service -S

Repository: ai-dynamo/aiconfigurator

Length of output: 1079


🏁 Script executed:

#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path('src/aiconfigurator/service/models.py').read_text()
for needle in ['class K8sConfigInput', 'class ApplyDeploymentRequest', 'class DeleteDeploymentRequest']:
    i = text.find(needle)
    if i != -1:
        print(f'## {needle}')
        print(text[i:i+2200])
        print('\n---\n')
PY

Repository: ai-dynamo/aiconfigurator

Length of output: 3080


Restrict the Kubernetes deploy routes and sanitize resource identifiers. src/aiconfigurator/service/app.py:713-859 accepts raw manifest content and runs kubectl apply/delete with the service account’s RBAC, with no in-app authz gate. The follow-up kubectl get/rollout status calls also trust manifest-derived kind/name/namespace; reject flag-like values (for example --help) before building the argv.

🤖 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 `@src/aiconfigurator/service/app.py` around lines 713 - 763, Add an in-app
authorization check at the entry points for _apply_k8s_deployment and
_delete_k8s_deployment before invoking kubectl, using the service’s existing
authz mechanism. Validate the resources returned by _parse_k8s_resources,
including kind, name, and namespace, and reject flag-like or otherwise invalid
identifiers before constructing any kubectl argv. Ensure the same validated
identifiers are used by _check_k8s_health and related rollout/get commands.

Comment on lines +824 to +837
if kind.lower() in {"deployment", "statefulset", "daemonset"}:
rollout = _run_kubectl(
[
"kubectl",
"rollout",
"status",
f"{kind}/{name}",
"-n",
namespace,
f"--timeout={timeout_seconds}s",
],
timeout=timeout_seconds + 5,
)
return {**base, "exists": True, "ready": True, "message": rollout.stdout.strip()}

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

A slow/failed rollout aborts the whole apply response, losing the fact that kubectl apply already succeeded.

For Deployment/StatefulSet/DaemonSet, _run_kubectl's rollout status call raises APIError on non-zero exit (including a legitimate "not ready within timeout" exit code). That exception propagates out of _check_k8s_health and _apply_k8s_deployment before payload/_mark_deployed_dgd are ever built, so a merely-slow-to-roll-out Deployment is reported as a hard 502 failure and the deployment record is never marked deployed — even though the resources were created.

🩹 Turn rollout failures into a health entry instead of raising
     if kind.lower() in {"deployment", "statefulset", "daemonset"}:
-        rollout = _run_kubectl(
-            [
-                "kubectl",
-                "rollout",
-                "status",
-                f"{kind}/{name}",
-                "-n",
-                namespace,
-                f"--timeout={timeout_seconds}s",
-            ],
-            timeout=timeout_seconds + 5,
-        )
-        return {**base, "exists": True, "ready": True, "message": rollout.stdout.strip()}
+        try:
+            rollout = _run_kubectl(
+                ["kubectl", "rollout", "status", f"{kind}/{name}", "-n", namespace, f"--timeout={timeout_seconds}s"],
+                timeout=timeout_seconds + 5,
+            )
+        except APIError as exc:
+            return {**base, "exists": True, "ready": False, "message": str(exc)}
+        return {**base, "exists": True, "ready": True, "message": rollout.stdout.strip()}

This path is also untested — test_delete_k8s_deployment_removes_deployed_record only exercises the success case via a generic mock. As per path instructions, tests should cover the changed behavior, not only the happy path.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if kind.lower() in {"deployment", "statefulset", "daemonset"}:
rollout = _run_kubectl(
[
"kubectl",
"rollout",
"status",
f"{kind}/{name}",
"-n",
namespace,
f"--timeout={timeout_seconds}s",
],
timeout=timeout_seconds + 5,
)
return {**base, "exists": True, "ready": True, "message": rollout.stdout.strip()}
if kind.lower() in {"deployment", "statefulset", "daemonset"}:
try:
rollout = _run_kubectl(
["kubectl", "rollout", "status", f"{kind}/{name}", "-n", namespace, f"--timeout={timeout_seconds}s"],
timeout=timeout_seconds + 5,
)
except APIError as exc:
return {**base, "exists": True, "ready": False, "message": str(exc)}
return {**base, "exists": True, "ready": True, "message": rollout.stdout.strip()}
🤖 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 `@src/aiconfigurator/service/app.py` around lines 824 - 837, Update the
deployment/statefulset/daemonset branch in _check_k8s_health to catch APIError
from the rollout-status _run_kubectl call and return an unhealthy health entry
containing the rollout failure details instead of propagating the exception.
Preserve the successful response for completed rollouts so _apply_k8s_deployment
can still build its payload and mark the deployment record as deployed, and add
coverage for the failed or timed-out rollout path.

Source: Path instructions

Comment on lines +827 to +842
function deriveServedModelName(modelPath) {
const cleaned = modelPath.trim().replace(/[\\/]+$/, "");
if (!cleaned) {
return "";
}
const segments = cleaned.split(/[\\/]/).filter(Boolean);
const modelSegment = cleaned.startsWith("s3://") && segments.length >= 2
? segments.at(-2)
: segments.at(-1) || cleaned;
return modelSegment.replace(/[^A-Za-z0-9._-]+/g, "-").toLowerCase();
}

function deriveDeploymentName(servedModelName) {
const cleaned = servedModelName.trim().replace(/[^A-Za-z0-9._-]+/g, "-").toLowerCase();
return cleaned ? `dynamo-${cleaned}` : "dynamo";
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Served-model/deployment-name derivation can still produce non-RFC1123 names.

The sanitizer regex [^A-Za-z0-9._-]+ keeps . and _ and applies no length cap, so a model path containing an underscore (e.g. Qwen3_8B) or a long path produces a k8s resource name that's not lowercase-alphanumeric-and-hyphens-only and can exceed 63 chars.

🏷️ Strip to RFC1123-safe characters and cap length
 function deriveServedModelName(modelPath) {
   const cleaned = modelPath.trim().replace(/[\\/]+$/, "");
   if (!cleaned) {
     return "";
   }
   const segments = cleaned.split(/[\\/]/).filter(Boolean);
   const modelSegment = cleaned.startsWith("s3://") && segments.length >= 2
     ? segments.at(-2)
     : segments.at(-1) || cleaned;
-  return modelSegment.replace(/[^A-Za-z0-9._-]+/g, "-").toLowerCase();
+  return modelSegment
+    .replace(/[^A-Za-z0-9-]+/g, "-")
+    .toLowerCase()
+    .replace(/^-+|-+$/g, "")
+    .slice(0, 63);
 }

 function deriveDeploymentName(servedModelName) {
-  const cleaned = servedModelName.trim().replace(/[^A-Za-z0-9._-]+/g, "-").toLowerCase();
-  return cleaned ? `dynamo-${cleaned}` : "dynamo";
+  const cleaned = servedModelName.trim().replace(/[^A-Za-z0-9-]+/g, "-").toLowerCase().replace(/^-+|-+$/g, "");
+  return (cleaned ? `dynamo-${cleaned}` : "dynamo").slice(0, 63);
 }

As per path instructions, "Generate RFC 1123-compliant DGD names: lowercase alphanumeric characters and hyphens, no longer than 63 characters."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function deriveServedModelName(modelPath) {
const cleaned = modelPath.trim().replace(/[\\/]+$/, "");
if (!cleaned) {
return "";
}
const segments = cleaned.split(/[\\/]/).filter(Boolean);
const modelSegment = cleaned.startsWith("s3://") && segments.length >= 2
? segments.at(-2)
: segments.at(-1) || cleaned;
return modelSegment.replace(/[^A-Za-z0-9._-]+/g, "-").toLowerCase();
}
function deriveDeploymentName(servedModelName) {
const cleaned = servedModelName.trim().replace(/[^A-Za-z0-9._-]+/g, "-").toLowerCase();
return cleaned ? `dynamo-${cleaned}` : "dynamo";
}
function deriveServedModelName(modelPath) {
const cleaned = modelPath.trim().replace(/[\\/]+$/, "");
if (!cleaned) {
return "";
}
const segments = cleaned.split(/[\\/]/).filter(Boolean);
const modelSegment = cleaned.startsWith("s3://") && segments.length >= 2
? segments.at(-2)
: segments.at(-1) || cleaned;
return modelSegment
.replace(/[^A-Za-z0-9-]+/g, "-")
.toLowerCase()
.replace(/^-+|-+$/g, "")
.slice(0, 63);
}
function deriveDeploymentName(servedModelName) {
const cleaned = servedModelName.trim().replace(/[^A-Za-z0-9-]+/g, "-").toLowerCase().replace(/^-+|-+$/g, "");
return (cleaned ? `dynamo-${cleaned}` : "dynamo").slice(0, 63);
}
🤖 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 `@src/aiconfigurator/service/static/index.html` around lines 827 - 842, Update
deriveServedModelName and deriveDeploymentName to generate RFC1123-compliant
names: lowercase letters, digits, and hyphens only, with each resulting name
capped at 63 characters. Replace the current sanitizer behavior that preserves
dots and underscores, and ensure truncation does not leave invalid leading or
trailing hyphens.

Source: Path instructions

Comment on lines +33 to +73
@pytest.mark.unit
def test_vllm_oss_deployment_uses_model_express_and_secret_refs():
template_dir = Path("src/aiconfigurator/generator/config/backend_templates/vllm")
template = Environment(loader=FileSystemLoader(template_dir)).get_template("k8s_deploy.yaml.j2")
rendered = template.render(
working_dir="/workspace/examples/backends/vllm",
K8sConfig={
"oss_enabled": True,
"oss_endpoint_url": "https://oss-s3.haiercash.com",
"oss_region": "cn-east-1",
"oss_secret_name": "oss-s3-secret",
"oss_model_express_url": "http://model-express:8001",
"oss_streamer_concurrency": 4,
"k8s_namespace": "aic-system",
"k8s_image": "example/vllm:modelexpress",
"frontend_node_selector": {"kubernetes.io/hostname": "master2"},
"worker_node_selector": {"kubernetes.io/hostname": "master2"},
},
ServiceConfig={
"model_path": "s3://aiplat/ai-lab/qwen3-0.6b/2026-07-01",
"served_model_name": "qwen3-0.6b",
},
DynConfig={"mode": "agg", "enable_router": False},
name="dynamo-qwen3-oss",
frontend_replicas=1,
agg_workers=1,
agg_gpu=1,
agg_cli_args_list=["--tensor-parallel-size", "1", "--max-model-len", "4096"],
)

deployment = yaml.safe_load(rendered)
worker = deployment["spec"]["services"]["VllmWorker"]["extraPodSpec"]["mainContainer"]
script = worker["args"][0]
env = {item["name"]: item for item in worker["env"]}

assert env["MX_MODEL_URI"]["value"] == "s3://aiplat/ai-lab/qwen3-0.6b/2026-07-01"
assert env["AWS_ENDPOINT_URL"]["value"] == "https://oss-s3.haiercash.com"
assert env["AWS_ACCESS_KEY_ID"]["valueFrom"]["secretKeyRef"]["name"] == "oss-s3-secret"
assert "--load-format modelexpress" in script
assert 'worker_cmd+=( "--tensor-parallel-size" )' in script
assert "config.json" in script

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 \
  'test_vllm_oss|generate_backend_artifacts|golden|generator_validator|kubectl.*dry-run' \
  tests src/aiconfigurator/generator

Repository: ai-dynamo/aiconfigurator

Length of output: 13488


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## test_vllm_oss_deployment.py\n'
sed -n '1,220p' tests/unit/generator/test_vllm_oss_deployment.py

printf '\n## golden backend contracts around vllm\n'
sed -n '1,260p' tests/golden/generator/test_release_1_2_0_backend_contracts.py

printf '\n## generator api render path\n'
sed -n '240,330p' src/aiconfigurator/generator/api.py

printf '\n## search for vllm OSS / model express / golden references\n'
rg -n -C2 'oss_enabled|model_express|OSS|vllm_oss|k8s_deploy.yaml|generate_backend_artifacts|generator_validator|dry-run' tests src tools

Repository: ai-dynamo/aiconfigurator

Length of output: 50381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '## OSS-specific references in tests'
rg -n -C3 'oss_enabled|model_express|oss_endpoint_url|oss_secret_name|oss_streamer_concurrency|load-format modelexpress' tests

echo
echo '## vllm-related golden/integration tests'
rg -n -C2 'vllm|backend_artifacts|k8s_deploy|run.sh|cli_args_agg|extra_engine_args' tests/golden tests/integration tests/unit/generator tests/unit/generator_validator

echo
echo '## file list for generator tests'
git ls-files 'tests/**' | rg 'vllm|generator|golden|integration'

Repository: ai-dynamo/aiconfigurator

Length of output: 50380


Add OSS coverage through the generator pipeline
This test only renders k8s_deploy.yaml.j2 directly. Add a generator-level case for an s3:// model path that exercises apply_defaults + generate_backend_artifacts and checks the emitted k8s_deploy.yaml/golden artifact for the OSS and ModelExpress fields.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 43-43: Do not make http calls without encryption
Context: "http://model-express:8001"
Note: [CWE-319] Cleartext Transmission of Sensitive Information.

(requests-http)

🤖 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/unit/generator/test_vllm_oss_deployment.py` around lines 33 - 73, Add a
generator-level test alongside
test_vllm_oss_deployment_uses_model_express_and_secret_refs that uses an s3://
model path and runs apply_defaults followed by generate_backend_artifacts.
Validate the emitted k8s_deploy.yaml or golden artifact contains the expected
OSS endpoint, secret reference, model URI, and ModelExpress settings instead of
only rendering k8s_deploy.yaml.j2 directly.

Sources: Coding guidelines, Path instructions

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