Skip to content

Feat/mlperf_k8s_support - #124

Open
aas008 wants to merge 8 commits into
dev/reusable-components+benchmark-tunablesfrom
feat/mlperf-gpt-oss-support
Open

Feat/mlperf_k8s_support#124
aas008 wants to merge 8 commits into
dev/reusable-components+benchmark-tunablesfrom
feat/mlperf-gpt-oss-support

Conversation

@aas008

@aas008 aas008 commented Jan 26, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added Helm-based deployment backend for remote vLLM server management with per-trial release isolation
    • Added Kubernetes direct deployment backend for vLLM workload execution
    • Introduced MLPerf harness benchmark provider with offline and server scenarios
    • Added benchmark tunable parameters for dynamic trial-level configuration
  • Documentation

    • Added comprehensive Helm deployment guide with configuration examples
    • Added Kubernetes setup documentation and MLPerf benchmarking guides
    • Added dataset storage guidance for containerized and PVC-based deployments

✏️ Tip: You can customize this high-level summary in your review settings.

This commit adds comprehensive MLPerf inference harness integration for vLLM optimization:

## Core Changes:

### MLPerf Benchmark Provider (auto_tune_vllm/benchmarks/providers.py)
- Implement full MLPerf result parsing from mlperf_log_summary.txt
- Extract output_tokens_per_second and samples_per_second metrics
- Add validation for required metrics
- Support both Offline and Server scenarios

### Kubernetes Execution Backend (auto_tune_vllm/execution/helm_utils.py)
- Add AWS credentials injection for MLflow S3 artifact uploads
  - AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION from k8s secret
- Add HuggingFace environment variables for tokenizer initialization
  - HF_HOME, TRANSFORMERS_CACHE for cached tokenizer access
- Add Python debugging environment variables (PYTHONUNBUFFERED, PYTHONFAULTHANDLER)
- Add model PVC support for benchmark pods to access cached models

## New Files:

### Documentation
- docs/DATASET_STORAGE.md: Guide for dataset storage approaches (Docker image vs PVC)
  - Includes dataset download instructions from MLPerf repository

### Scripts
- scripts/upload-datasets-to-pvc.sh: Upload MLPerf datasets to Kubernetes PVC
- build-and-push-harness.sh: Build and push MLPerf harness Docker image

### Examples
- examples/study_config_test_gpt_oss.yaml: Full GPT-OSS-120B optimization config (6396 samples)
- examples/study_config_test_gpt_oss_QUICK_TEST.yaml: Quick test config (10 samples)
- results-pvc-mlperf.yaml: Kubernetes PVC for benchmark results storage

### Docker
- Dockerfile.harness-updated: MLPerf harness container with dataset and tokenizer caching

## Key Features:

1. **MLflow Integration**: Full S3 artifact storage support with AWS credentials
2. **Tokenizer Support**: Pre-cached HuggingFace tokenizers for GPT-OSS-120B (200K vocab)
3. **Dataset Management**: Both Docker image and PVC-based dataset storage approaches
4. **Large Context Support**: Configured for GPT-OSS-120B's 131K token context (YaRN scaling)
5. **Timeout Handling**: Dynamic timeout calculation based on benchmark duration

## Tested With:
- Model: openai/gpt-oss-120b (120B parameters, MoE, MXFP4 quantization)
- vLLM: v0.14.1
- MLPerf: v6.0 (commit 2a90efe)
- Scenario: Offline (batch processing)
- Expected Performance: ~9K tokens/second on 8xH100

## Note:
Dataset files (>500MB) are not included in this commit due to GitHub size limits.
See docs/DATASET_STORAGE.md for download instructions.

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jan 26, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Introduces Helm-based and Kubernetes-backed execution backends for vLLM with MLPerf benchmark provider support, extended configuration for container deployments, tunable benchmark parameters, comprehensive Helm chart examples, and CLI extensions for orchestrated trial execution across distributed infrastructure.

Changes

Cohort / File(s) Summary
Helm & Kubernetes Backend Implementation
auto_tune_vllm/execution/backends.py, auto_tune_vllm/execution/helm_utils.py, auto_tune_vllm/execution/k8s_utils.py, auto_tune_vllm/execution/trial_controller.py
New HelmExecutionBackend and KubernetesExecutionBackend classes with release/deployment lifecycle management; Helm values generation and Kubernetes utilities for vLLM Deployment/Service creation, service discovery, health monitoring, and resource cleanup; HelmTrialController and KubernetesTrialController managing remote server startup, benchmark Job submission, result extraction; expanded RayExecutionBackend with multi-actor architecture (vLLM + Workload actors)
Configuration & Trial Extensions
auto_tune_vllm/core/config.py, auto_tune_vllm/core/trial.py, auto_tune_vllm/core/study_controller.py
New HelmConfig and KubernetesConfig dataclasses; StudyConfig extended with benchmark_tunables, gaie_parameters, static_gaie_parameters, helm_config, k8s_config; ExecutionInfo enhanced with helm_release_name and benchmark_job_name; TrialConfig adds gaie_parameters and gaie_parameter_configs; ConfigValidator validates and parses GAIE and benchmark tunables; StudyController enforces Helm backend concurrency limits, merges benchmark tunables, respects skip_baseline flag
Benchmark Extensions
auto_tune_vllm/benchmarks/config.py, auto_tune_vllm/benchmarks/providers.py
BenchmarkConfig extends with scenario, dataset_path, lg_model_name, batch_size, num_samples, output_dir, mlflow_experiment_name, mlflow_host, server_target_qps, server_coalesce_queries, test_mode; new merge_tunables() method for tunable value merging; MLPerfBenchmark provider implementing harness-based benchmark execution with command building and JSON result parsing
CLI & Execution Orchestration
auto_tune_vllm/cli/main.py, auto_tune_vllm/execution/__init__.py
optimize_command extended with helm_chart_path, helm_chart_repo, helm_chart_name, helm_chart_version, k8s_namespace, kubeconfig, override parameters; backend option broadened to include 'helm' and 'k8s'; override flag enables study deletion and Helm release cleanup; max_concurrent_trials validation enforces parallelism restrictions for Helm/K8s backends; signal handling added for graceful trial cleanup on interruption
Helm Chart Templates & Configuration
examples/helm/Chart.yaml, examples/helm/values.yaml, examples/helm/templates/*.yaml, examples/helm/_helpers.tpl
Complete Helm chart for vLLM server deployment with templated Deployment, Service, and PostgreSQL StatefulSet/ConfigMap/Secret resources; chart supports modelCommand modes (vllmServe, imageDefault, custom) with environment, resource, and health probe configuration; PostgreSQL integration with configurable credentials and persistence
Helm Deployment & Test Scripts
examples/helm/deploy-postgres.sh, examples/helm/deploy-postgres-standalone.sh, examples/helm/test-postgres.sh, examples/helm/README.md
Automated Helm/Kubernetes PostgreSQL deployment and verification scripts; comprehensive setup guide covering prerequisites, cluster configuration, study integration, and troubleshooting; mapping and templating documentation for vLLM parameter integration with Helm values
PostgreSQL Kubernetes Manifests
examples/helm/postgres-standalone.yaml, examples/helm/postgres-test-pod-helm.yaml, examples/helm/postgres-test-pod.yaml
Kubernetes manifests for standalone PostgreSQL StatefulSet with Secret/Service/headless Service, and test Pods for connectivity verification with environment variable wiring to service endpoints
Study Configuration Examples
examples/study_config*.yaml, examples/helm/study_config.yaml, examples/trial_config_comprehensive.yaml
New Helm and Kubernetes-backed study configuration examples (MLPerf Server/Offline, GuideLLM, GPT-OSS); benchmark tunables sections for rate, server_target_qps, server_coalesce_queries; updated logging_level and tunable parameter definitions; local example updates with tunable rate configurations and PostgreSQL credential adjustments
Kubernetes & Deployment Utilities
scripts/cleanup_study_resources.sh, scripts/upload-datasets-to-pvc.sh, build-and-push-harness.sh, Dockerfile.harness-updated
New cleanup script for study Kubernetes resources (Helm releases, services, deployments, jobs); dataset upload automation to PVCs; MLPerf harness Docker image build/push script with dataset artifact integration; updated Dockerfile with MLflow installation and v4 dataset setup
Documentation & Configuration Files
docs/helm_deployment.md, docs/configuration.md, docs/ray_cluster_setup.md, README.md, .gitignore
New Helm deployment documentation with architecture, configuration, usage, troubleshooting; enhanced configuration guide for benchmark tunables, MLPerf scenarios, dataset identifiers (hf:// format); KubeRay setup guidance; installation instructions updated for optional dependencies (local, helm groups); gitignore refined to exclude only optuna_studies/v4/
Project Dependencies
pyproject.toml
Optional dependency groups introduced: local (vllm, guidellm, ray, optuna-integration, gpytorch), helm (kubernetes), postgresql (psycopg2-binary), kuberay (kubernetes); core dependencies simplified by moving optional packages into groups
Kubernetes Resources
results-pvc-mlperf.yaml, examples/readiness-check-pod-debug.yaml
PersistentVolumeClaim for MLPerf results storage; debug Pod manifest for readiness probe testing with curl-based HTTP health checks and URL normalization

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant StudyController as StudyController
    participant HelmBackend as HelmExecutionBackend
    participant HelmUtils as helm_utils
    participant K8sAPI as Kubernetes API
    participant VLLMSvc as vLLM Service
    participant BenchmarkJob as Benchmark Job
    
    User->>StudyController: submit_trial(trial_config)
    activate StudyController
    StudyController->>HelmBackend: submit_trial(trial_config)
    activate HelmBackend
    
    HelmBackend->>HelmUtils: generate_helm_values(trial_config)
    HelmUtils-->>HelmBackend: values_dict
    
    HelmBackend->>K8sAPI: helm upgrade --install release_name
    K8sAPI->>K8sAPI: Deploy vLLM via chart
    K8sAPI-->>HelmBackend: release_created
    
    HelmBackend->>HelmUtils: wait_for_service_ready(service_url)
    HelmUtils->>VLLMSvc: Poll /v1/models until ready
    VLLMSvc-->>HelmUtils: 200 OK
    HelmUtils-->>HelmBackend: service_ready
    
    HelmBackend->>HelmUtils: create_benchmark_job(service_url, benchmark_config)
    HelmUtils->>K8sAPI: kubectl apply benchmark_job_manifest
    K8sAPI->>BenchmarkJob: Schedule Job
    K8sAPI-->>HelmUtils: job_created
    HelmUtils-->>HelmBackend: job_name
    
    HelmBackend->>HelmUtils: wait_for_job_completion(job_name, timeout)
    HelmUtils->>BenchmarkJob: Poll job.status
    BenchmarkJob-->>HelmUtils: job_completed
    HelmUtils-->>HelmBackend: completed
    
    HelmBackend->>HelmUtils: extract_job_results(job_name)
    HelmUtils->>BenchmarkJob: Read logs/results
    HelmUtils-->>HelmBackend: results_dict
    
    HelmBackend->>K8sAPI: helm uninstall release_name
    K8sAPI->>K8sAPI: Clean up vLLM release
    K8sAPI-->>HelmBackend: release_deleted
    
    HelmBackend-->>StudyController: TrialResult
    deactivate HelmBackend
    StudyController-->>User: trial_complete
    deactivate StudyController
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • chore: run ruff fix for all obvious items #73: Modifies execution/backends.py to introduce abstract cleanup_all_trials method in ExecutionBackend; directly related to the cleanup implementations added in this PR's Helm and Kubernetes backends.
  • Dev/refactor #20: Refactors execution, trial controller, benchmark provider, CLI, and core config modules—touches ExecutionBackend/TrialController/BenchmarkProvider surfaces that this PR extends with Helm/Kubernetes backends and MLPerf support.
  • FIX: Avoid killing the parent process group #92: Extends trial_controller.py and benchmarks/providers.py with cancellation handling (request_cancellation) and process termination logic; related to the trial lifecycle management and resource cleanup flow in this PR's new trial controllers.

Suggested reviewers

  • thameem-abbas
  • ephoris

Poem

🐰 Hops through clouds with Helm and Keys,
vLLM servers dance with ease,
Kubernetes Jobs hop and leap,
Tunable burrows, secrets deep,
From local warren to distant farm,
Optimization brings warm charm! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'Feat/mlperf_k8s_support' is inconsistent with the stated objective 'Feat/mlperf gpt oss support' and does not accurately reflect the primary changes, which include Helm-based deployment, MLPerf benchmark provider, Kubernetes support, and broader infrastructure enhancements beyond just MLPerf K8s. Revise the PR title to better reflect the main scope, e.g., 'Add Helm and Kubernetes execution backends with MLPerf benchmark support' or align with the stated objective by using 'Add MLPerf GPT-OSS support with K8s backends'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 80.91% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/mlperf-gpt-oss-support

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🤖 Fix all issues with AI agents
In `@auto_tune_vllm/execution/trial_controller.py`:
- Around line 2325-2331: The WorkloadActor Ray class defines run_benchmark but
RayExecutionBackend.submit_trial expects run_workload, causing AttributeError;
fix by aligning names — either add a run_workload method to the WorkloadActor
that delegates to run_benchmark (or rename run_benchmark to run_workload) so the
actor exposes the method called by RayExecutionBackend.submit_trial; update only
the actor definition (class WorkloadActor) or the caller (method submit_trial)
to use the same method name consistently.
🟠 Major comments (24)
.gitignore-33-33 (1)

33-33: Revert .gitignore to ignore entire optuna_studies/ directory, not just v4/.

The code dynamically creates study directories under optuna_studies/{study_name}/ for any study name (e.g., vllm_quick_test, helm_optimization_example). Narrowing the ignore pattern to only optuna_studies/v4/ means studies created with other names will be tracked by git, causing large SQLite databases and study artifacts to be committed unintentionally.

The v4 reference in this PR relates to MLPerf dataset versions, not optuna_studies directory structure. Change line 33 back to optuna_studies/.

examples/study_config_guidellm_k8s_direct.yaml-5-9 (1)

5-9: Avoid hardcoded database credentials in the example config.

Even in examples, embedding credentials encourages insecure usage and can leak into real configs. Prefer placeholders and note that real values should be sourced from secrets or env injection at runtime.

🔒 Suggested placeholder update
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna-k8s-direct"
+  database_url: "postgresql://<db_user>:<db_password>@localhost:5432/optuna-k8s-direct"
...
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna-k8s-direct"
+  database_url: "postgresql://<db_user>:<db_password>@localhost:5432/optuna-k8s-direct"

Also applies to: 77-80

Dockerfile.harness-updated-13-13 (1)

13-13: Avoid world-writable dataset permissions.

chmod -R 777 is overly permissive; prefer group-writable with a known group to keep OpenShift-friendly behavior while reducing risk.

🔒 Suggested hardening
-RUN chmod -R 777 /vllm-workspace/mlperf-inference-6.0-redhat/harness/data/v4
+RUN chgrp -R 0 /vllm-workspace/mlperf-inference-6.0-redhat/harness/data/v4 \
+ && chmod -R g+rwX /vllm-workspace/mlperf-inference-6.0-redhat/harness/data/v4
examples/helm/postgres-test-pod.yaml-7-37 (1)

7-37: Harden the pod securityContext (least privilege).

Static analysis flags allowPrivilegeEscalation and root usage; add explicit securityContext to align with cluster policies.

🔒 Suggested securityContext
 spec:
+  securityContext:
+    runAsNonRoot: true
+    seccompProfile:
+      type: RuntimeDefault
   containers:
   - name: postgres-client
     image: postgres:15
+    securityContext:
+      allowPrivilegeEscalation: false
+      capabilities:
+        drop: ["ALL"]
     command: ["/bin/bash"]
     args: ["-c", "sleep infinity"]
examples/study_config_test_gpt_oss_QUICK_TEST.yaml-9-10 (1)

9-10: Replace hardcoded database credentials with environment variable references.
Credentials should not be committed to the repository, even in test/example configs. The config loader supports ${VAR_NAME} and ${VAR_NAME:-default} patterns for environment variable expansion.

Suggested change
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna-k8s-direct"
+  database_url: "${DATABASE_URL:-postgresql://user:password@localhost:5432/optuna-k8s-direct}"

Apply to both lines 9-10 (study.database_url) and 51-53 (logging.database_url).

scripts/upload-datasets-to-pvc.sh-1-33 (1)

1-33: Fail fast and guarantee uploader pod cleanup.

Without error handling, if any kubectl command fails (lines 12-46), the script either continues executing or exits without running cleanup. The uploader pod persists in the cluster indefinitely, wasting resources.

🛡️ Suggested reliability fix
 #!/bin/bash
 # Upload MLPerf datasets to model PVC
 # This is an alternative to baking datasets into the Docker image
 
+set -euo pipefail
+
 NAMESPACE="autotune-aanya"
 PVC_NAME="model-cache-pvc"
 LOCAL_DATASET_DIR="/Users/aansharm/mlperf-build/mlperf-datasets"
+
+cleanup() {
+  kubectl delete pod dataset-uploader -n "$NAMESPACE" --ignore-not-found
+}
+trap cleanup EXIT
examples/readiness-check-pod-debug.yaml-9-12 (1)

9-12: Harden the pod's security context (non‑root, no privilege escalation).
This pod runs with default privileges; add a strict securityContext to enforce least-privilege operation.

🔐 Suggested hardening
 spec:
   restartPolicy: Never
+  securityContext:
+    seccompProfile:
+      type: RuntimeDefault
   containers:
     - name: readiness-check
       image: quay.io/rh-ee-aansharm/curl:latest  # Using quay.io to avoid Docker Hub rate limits
+      securityContext:
+        allowPrivilegeEscalation: false
+        runAsNonRoot: true
+        runAsUser: 65532
+        readOnlyRootFilesystem: true
+        capabilities:
+          drop: ["ALL"]
examples/helm/templates/deployment.yaml-22-25 (1)

22-25: Fail fast when container image is unset.
If .image is missing, Helm renders <no value> and Kubernetes attempts to pull an invalid image. Use required to surface the error early.

🛠️ Proposed fix
-          image: {{ .image }}
+          image: {{ required "decode.containers[].image is required" .image }}
examples/helm/values.yaml-85-89 (1)

85-89: Avoid shipping a known default PostgreSQL password.
postgres/postgres is an insecure default and can leak into real deployments. Prefer leaving it unset and requiring an explicit override or secret.

🔒 Proposed fix
   auth:
     username: postgres
-    password: postgres  # Change this in production!
+    password: ""  # REQUIRED: set via --set or a secret; avoid known defaults
pyproject.toml-35-43 (1)

35-43: Ray must be in core dependencies, not optional.

Ray is explicitly validated as a required package (raising RuntimeError if missing) and is mandatory for all execution modes. Moving ray[default] to the optional local extra breaks default installations, causing runtime validation failures. Moving Ray to core dependencies and removing it from the local extra is required.

🛠️ Proposed fix
 dependencies = [
     "optuna>=3.0.0",
     "pyyaml>=6.0",
     "pydantic>=2.0.0",
     "typer>=0.9.0",
     "rich>=13.0.0",
     "requests>=2.28.0",
+    "ray[default]>=2.0.0",
 ]
 
 [project.optional-dependencies]
 # Core dependencies for local/Ray execution
 local = [
     "vllm>=0.11.0",
     "guidellm>=0.1.0",
-    "ray[default]>=2.0.0",
     "optuna-integration[botorch]>=4.0.0",
     "gpytorch>=1.1",
 ]
auto_tune_vllm/benchmarks/providers.py-505-512 (1)

505-512: Avoid piping stdout/stderr without consumption.

stdout/stderr=PIPE can deadlock if the harness writes enough output. Either stream logs in a background reader or inherit parent streams.

🔧 Minimal safe change
         self._process = subprocess.Popen(
             cmd,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE,
+            stdout=None,
+            stderr=None,
             text=True,
             env=env,
             start_new_session=True
         )
auto_tune_vllm/benchmarks/providers.py-642-650 (1)

642-650: Fix Offline scenario batch_size validation.

The current comparison checks num_samples against itself, so mismatches are never detected. This can silently produce invalid runs.

🐛 Proposed fix
         if config.scenario == "Offline":
-            if config.num_samples is not None and config.num_samples != config.num_samples:
+            if (
+                config.batch_size is not None
+                and config.batch_size != config.num_samples
+            ):
                 raise ValueError(
                     "num_samples must be equal to batch_size for Offline scenario"
                 )
             if config.batch_size is None:
                 config.batch_size = config.num_samples
             cmd.extend(["--batch-size", str(config.batch_size)])
auto_tune_vllm/benchmarks/providers.py-556-563 (1)

556-563: Ruff E501: wrap long error/log lines.

These lines exceed the configured limit and will keep CI red.

✂️ Suggested wrap
         if "output_tokens_per_second" not in data:
             raise RuntimeError(
-                f"Required metric 'output_tokens_per_second' not found in MLPerf results. "
-                f"Available keys: {list(data.keys())}"
+                "Required metric 'output_tokens_per_second' not found in MLPerf results. "
+                f"Available keys: {list(data.keys())}"
             )

-        self._logger.info(
-            f"Parsed MLPerf results: output_tokens_per_second={data['output_tokens_per_second']}"
-        )
+        self._logger.info(
+            "Parsed MLPerf results: output_tokens_per_second=%s",
+            data["output_tokens_per_second"],
+        )
auto_tune_vllm/benchmarks/providers.py-461-463 (1)

461-463: Parameterize subprocess.Popen with [str] to match the text=True argument.

The start_benchmark method instantiates subprocess.Popen with text=True (line 509), which means stdout and stderr operate in text mode. The return type should be subprocess.Popen[str] to properly reflect this in the type system.

🔤 Typing fix
-    def start_benchmark(
-        self, model_url: str, config: BenchmarkConfig
-    ) -> subprocess.Popen:
+    def start_benchmark(
+        self, model_url: str, config: BenchmarkConfig
+    ) -> subprocess.Popen[str]:
examples/study_config_mlperf_offline_k8s.yaml-7-12 (1)

7-12: Remove plaintext DB credentials from example config.

Both database URLs embed credentials; this will trigger secret scanners and encourages committing secrets. Use placeholders instead.

🔒 Suggested change
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna"
+  database_url: "postgresql://<user>:<password>@<host>:<port>/<db>"

...

-  database_url: "postgresql://postgres:postgres@localhost:5432/tuner-user"
+  database_url: "postgresql://<user>:<password>@<host>:<port>/<db>"

Also applies to: 70-73

examples/study_config_test_gpt_oss.yaml-6-8 (1)

6-8: Remove credentials and user-specific paths from example config.

These values will trigger secret scanning and leak personal info. Prefer placeholders.

🔒 Suggested change
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna-k8s-direct"
+  database_url: "postgresql://<user>:<password>@<host>:<port>/<db>"

...

-    kubeconfig: "/Users/aansharm/.kube/config"  # Optional: custom kubeconfig path
+    kubeconfig: "/path/to/your/kubeconfig"  # Optional: custom kubeconfig path

...

-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna-k8s-direct"
+  database_url: "postgresql://<user>:<password>@<host>:<port>/<db>"

Also applies to: 20-21, 59-61

auto_tune_vllm/execution/k8s_utils.py-104-112 (1)

104-112: Fix model-argument detection to avoid false positives.

any("--model" in arg for arg in args) will match flags like --max-model-len, causing the actual --model to be skipped and vLLM to fail.

🐛 Proposed fix
-    model_in_args = any("--model" in arg for arg in args)
+    model_in_args = any(
+        arg == "--model" or arg.startswith("--model=")
+        for arg in args
+    )
scripts/cleanup_study_resources.sh-147-153 (1)

147-153: Auto-detected namespace always picks the first project.

head -1 means the “only one project” check always passes, so a multi-project cluster can silently select the wrong namespace—dangerous for a cleanup script.

🛠️ Safer detection
-                projects=$($KUBECTL_CMD get projects -o name 2>/dev/null | head -1 | sed 's|project.project.openshift.io/||' || echo "")
-                if [[ -n "$projects" ]] && [[ $(echo "$projects" | wc -l) -eq 1 ]]; then
-                    NAMESPACE="$projects"
+                projects=$($KUBECTL_CMD get projects -o name 2>/dev/null | sed 's|project.project.openshift.io/||' || echo "")
+                project_count=$(echo "$projects" | sed '/^$/d' | wc -l | tr -d ' ')
+                if [[ "$project_count" -eq 1 ]]; then
+                    NAMESPACE="$(echo "$projects" | head -1)"
                     verbose "Using only available project: $NAMESPACE"
+                elif [[ "$project_count" -gt 1 ]]; then
+                    error "Multiple projects detected; please specify --namespace"
+                    exit 1
                 fi
scripts/cleanup_study_resources.sh-329-350 (1)

329-350: Filter readiness-check job cleanup by study to prevent interfering with concurrent studies.

The cleanup_readiness_jobs() function accepts a sanitized_study parameter but does not use it. The function currently deletes all readiness-check jobs in the namespace with label app=auto-tune-vllm-readiness-check, regardless of which study they belong to. Since job names are UUID-based and jobs lack study-specific labels, this will delete readiness-check jobs from other studies running in the same namespace.

Add a study-specific label when creating readiness-check jobs and filter by it during cleanup, or use the job name pattern to match the study context.

examples/helm/test-postgres.sh-114-116 (1)

114-116: Add namespace flag to kubectl apply for consistency.

The manifest postgres-test-pod.yaml has metadata.namespace: llm-d-trials hardcoded. The kubectl apply at line 116 lacks the -n flag, which works currently only because NAMESPACE is hardcoded to match. To ensure consistency with subsequent kubectl wait and kubectl exec commands that explicitly target $NAMESPACE, add the namespace flag:

Proposed fix
-kubectl apply -f "$TEST_POD_FILE"
+kubectl apply -n "$NAMESPACE" -f "$TEST_POD_FILE"
auto_tune_vllm/cli/main.py-231-285 (1)

231-285: Avoid deleting the entire SQLite DB and guard conn initialization.
If the study name isn’t found, the code deletes the whole DB file, which can wipe unrelated studies. Also, conn is referenced in finally without being initialized if sqlite3.connect() fails.

🔧 Safer deletion flow
                 if storage_path.exists():
-                            # Connect to SQLite and check/delete study
-                            conn = sqlite3.connect(str(storage_path))
+                            # Connect to SQLite and check/delete study
+                            conn = None
+                            conn = sqlite3.connect(str(storage_path))
                             try:
                                 cursor = conn.cursor()
                                 ...
                                 if study_row:
                                     ...
                                 else:
-                                    # Study not found in database, but file exists - delete the file to clear any cached state
-                                    conn.close()
-                                    storage_path.unlink()
-                                    console.print(f"[green]✅ Deleted SQLite database file to clear cached study state[/green]")
+                                    console.print(
+                                        f"[yellow]Study '{study_config.study_name}' not found in SQLite storage; skipping delete[/yellow]"
+                                    )
                             finally:
-                                if conn:
+                                if conn is not None:
                                     conn.close()
auto_tune_vllm/execution/backends.py-806-990 (1)

806-990: Pass --kubeconfig to all Helm CLI calls when provided.

self.kubeconfig is stored in __init__ but none of the helm commands in the deploy method pass it—creating an inconsistency with the cleanup methods which correctly include --kubeconfig. This causes deploy operations to use the default kubeconfig (~/.kube/config) instead of the provided one, risking operations against the wrong cluster.

🔧 Suggested helper
+        def _helm(cmd: list[str]) -> list[str]:
+            if self.kubeconfig:
+                return cmd + ["--kubeconfig", self.kubeconfig]
+            return cmd
...
-                result = subprocess.run(
-                    ["helm", "list", "-n", self.namespace, "-q"],
+                result = subprocess.run(
+                    _helm(["helm", "list", "-n", self.namespace, "-q"]),
                     capture_output=True,
                     text=True,
                 )
...
-                    result = subprocess.run(infra_cmd, check=True, capture_output=True, text=True)
+                    result = subprocess.run(_helm(infra_cmd), check=True, capture_output=True, text=True)
...
-                result = subprocess.run(gaie_cmd, check=True, capture_output=True, text=True)
+                result = subprocess.run(_helm(gaie_cmd), check=True, capture_output=True, text=True)
...
-            result = subprocess.run(
-                helm_cmd,
+            result = subprocess.run(
+                _helm(helm_cmd),
                 check=True,
                 capture_output=True,
                 text=True,
             )
auto_tune_vllm/execution/trial_controller.py-1489-1701 (1)

1489-1701: Add kubeconfig parameter to HelmTrialController and propagate through benchmark operations.

HelmExecutionBackend accepts a kubeconfig parameter but cannot pass it to HelmTrialController because the controller's __init__ doesn't accept it. This causes _start_benchmark to hardcode kubeconfig=None, ignoring the specified cluster configuration. The backend calls site in backends.py must also be updated to pass the kubeconfig.

🔧 Suggested changes

Update HelmTrialController.__init__ to accept and store kubeconfig:

-    def __init__(self, release_name: str, namespace: str = "default", benchmark_image: Optional[str] = None, helm_config: Optional[Dict[str, Any]] = None, benchmark_pvc: Optional[str] = None, model_pvc: Optional[str] = None):
+    def __init__(
+        self,
+        release_name: str,
+        namespace: str = "default",
+        benchmark_image: Optional[str] = None,
+        helm_config: Optional[Dict[str, Any]] = None,
+        benchmark_pvc: Optional[str] = None,
+        model_pvc: Optional[str] = None,
+        kubeconfig: Optional[str] = None,
+    ):

Store kubeconfig in __init__:

         self.benchmark_pvc = benchmark_pvc
         self.model_pvc = model_pvc
+        self.kubeconfig = kubeconfig

Update _start_benchmark to pass it:

-        self.benchmark_job_name = create_benchmark_job(
-            trial_config, self.server_url, self.namespace, benchmark_image, kubeconfig=None, benchmark_pvc=self.benchmark_pvc, model_pvc=self.model_pvc
-        )
+        self.benchmark_job_name = create_benchmark_job(
+            trial_config,
+            self.server_url,
+            self.namespace,
+            benchmark_image,
+            kubeconfig=self.kubeconfig,
+            benchmark_pvc=self.benchmark_pvc,
+            model_pvc=self.model_pvc,
+        )

Update the instantiation in backends.py:

-            controller = HelmTrialController(release_name, self.namespace, benchmark_image, self.helm_config, benchmark_pvc=benchmark_pvc, model_pvc=model_pvc)
+            controller = HelmTrialController(
+                release_name,
+                self.namespace,
+                benchmark_image,
+                self.helm_config,
+                benchmark_pvc=benchmark_pvc,
+                model_pvc=model_pvc,
+                kubeconfig=self.kubeconfig,
+            )
auto_tune_vllm/cli/main.py-305-329 (1)

305-329: Honor --kubeconfig when listing/uninstalling Helm releases.

When a kubeconfig is provided, the cleanup currently uses the default Kubernetes context. This can clean up releases from the wrong cluster.

🔧 Suggested change
-                        result = subprocess.run(
-                            ["helm", "list", "-n", namespace, "-q"],
+                        helm_list_cmd = ["helm", "list", "-n", namespace, "-q"]
+                        if kubeconfig:
+                            helm_list_cmd.extend(["--kubeconfig", kubeconfig])
+                        result = subprocess.run(
+                            helm_list_cmd,
                             capture_output=True,
                             text=True,
                         )
...
-                                    subprocess.run(
-                                        ["helm", "uninstall", release, "-n", namespace],
+                                    uninstall_cmd = ["helm", "uninstall", release, "-n", namespace]
+                                    if kubeconfig:
+                                        uninstall_cmd.extend(["--kubeconfig", kubeconfig])
+                                    subprocess.run(
+                                        uninstall_cmd,
                                         capture_output=True,
                                         text=True,
                                     )
🟡 Minor comments (14)
docs/ray_cluster_setup.md-372-395 (1)

372-395: Fix namespaceSelector label key in NetworkPolicy example.

Most clusters label namespaces with kubernetes.io/metadata.name, not name, so this policy won’t match and can block traffic.

✅ Suggested fix
   ingress:
   - from:
     - namespaceSelector:
         matchLabels:
-          name: <your-namespace>
+          kubernetes.io/metadata.name: <your-namespace>
   egress:
   - to:
     - namespaceSelector:
         matchLabels:
-          name: <your-namespace>
+          kubernetes.io/metadata.name: <your-namespace>
docs/DATASET_STORAGE.md-86-98 (1)

86-98: Replace user‑specific paths/namespaces with placeholders.
These examples hardcode a personal namespace and local path, which will mislead most users.

✍️ Suggested doc edits
-kubectl run dataset-uploader --image=busybox -n autotune-aanya \
+kubectl run dataset-uploader --image=busybox -n <your-namespace> \
@@
-kubectl cp mlperf-build/mlperf-datasets/v4 \
-  autotune-aanya/dataset-uploader:/mnt/models/datasets/v4
+kubectl cp /path/to/mlperf-build/mlperf-datasets/v4 \
+  <your-namespace>/dataset-uploader:/mnt/models/datasets/v4
@@
-cd /Users/aansharm/mlperf-build
+cd /path/to/mlperf-build

Also applies to: 125-127

examples/helm/README.md-879-894 (1)

879-894: Add language tags to fenced blocks to satisfy MD040.
markdownlint flags these two fences because they lack a language identifier. Adding text keeps the docs lint-clean.

🛠️ Proposed fix
-   ```
+   ```text
    --model Qwen/Qwen3-0.6B
    --port 8000
    --tensor-parallel-size 2
    --data-parallel-size 1
    --served-model-name Qwen/Qwen3-0.6B
    ```
@@
-   ```
+   ```text
    --max-num-seqs 128
    --gpu-memory-utilization 0.9
    --max-model-len 16384
    ```
examples/study_config.yaml-37-37 (1)

37-37: Database name mismatch between study and logging sections.

The study section uses optuna while the logging section uses tuner-user. This inconsistency appears systematically across multiple config files. However, Kubernetes and Helm deployment variants (e.g., study_config_mlperf_helm.yaml) consistently use matching database names in both sections, suggesting this may be unintentional. Either clarify if separate databases are required or align the logging database name with the study database to match the pattern used in the k8s/helm configs.

auto_tune_vllm/execution/__init__.py-3-8 (1)

3-8: Add HelmExecutionBackend to exports for API consistency.

HelmExecutionBackend is defined in backends.py and is used throughout the codebase (e.g., in study_controller.py and cli/main.py), but it is not imported or exported from this module. All other execution backend implementations are exported, so HelmExecutionBackend should be added to the import statement (lines 3-8) and the __all__ list (lines 11-17) for consistency.

examples/study_config_mlperf_server_k8s.yaml-7-9 (1)

7-9: Inconsistent database names between study and logging sections.

The study section uses database optuna (line 9) while the logging section uses tuner-user (line 79). This may be intentional for separation of concerns, but could confuse users who expect a single database. Consider using the same database or adding a comment explaining why they differ.

Suggested fix if using same database
 logging:
   # At least one of the following must be provided and must be valid
-  database_url: "postgresql://postgres:postgres@localhost:5432/tuner-user"
+  database_url: "postgresql://postgres:postgres@localhost:5432/optuna"
   file_path: "/tmp/auto-tune-vllm-logs"  # Local file path (on the machine running auto-tune)
   log_level: "INFO"

Also applies to: 77-79

examples/study_config_mlperf_server_k8s_direct.yaml-88-89 (1)

88-89: Comment contradicts the actual value.

The comment states "Use 1 GPU per trial" but tensor_parallel_size is set to 8.

Suggested fix
 static_parameters:
-  tensor_parallel_size: 8  # Use 1 GPU per trial
+  tensor_parallel_size: 8  # Use 8 GPUs per trial (tensor parallelism)
examples/study_config_mlperf_server.yaml-5-8 (1)

5-8: Avoid hardcoded database credentials in example config.

Using default creds in examples can lead to unsafe copy‑paste usage. Placeholders are safer.

🔐 Proposed edit (placeholders)
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna"
+  database_url: "postgresql://<user>:<password>@localhost:5432/optuna"
...
-  database_url: "postgresql://postgres:postgres@localhost:5432/tuner-user"
+  database_url: "postgresql://<user>:<password>@localhost:5432/tuner-user"

Also applies to: 44-48

examples/study_config_mlperf_offline.yaml-5-8 (1)

5-8: Avoid hardcoded database credentials in example configs.

Even in examples, baked‑in credentials tend to get copied into real deployments. Consider using placeholders to avoid accidental reuse.

🔐 Proposed edit (placeholders)
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna"
+  database_url: "postgresql://<user>:<password>@localhost:5432/optuna"
...
-  database_url: "postgresql://postgres:postgres@localhost:5432/tuner-user"
+  database_url: "postgresql://<user>:<password>@localhost:5432/tuner-user"

Also applies to: 37-40

examples/study_config_mlperf_helm.yaml-4-12 (1)

4-12: Avoid hardcoded database credentials in example config.

These values are likely to be copy‑pasted into real deployments. Consider placeholders to prevent accidental reuse.

🔐 Proposed edit (placeholders)
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna"
+  database_url: "postgresql://<user>:<password>@localhost:5432/optuna"
...
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna"
+  database_url: "postgresql://<user>:<password>@localhost:5432/optuna"

Also applies to: 70-74

auto_tune_vllm/execution/trial_controller.py-2094-2101 (1)

2094-2101: Same validation gap in KubernetesTrialController.run_trial().
Apply the same _validate_environment() call here for parity and early failure.

auto_tune_vllm/execution/trial_controller.py-1726-1734 (1)

1726-1734: Run environment validation in Helm/K8s controllers.
BaseTrialController.run_trial() validates dependencies, but Helm/K8s override run_trial() and skip _validate_environment(), so missing dependencies only surface later.

🔧 Suggested addition
         try:
             # Setup trial-specific logging
             self._setup_trial_logging(trial_config)
+            # Validate controller-side dependencies
+            self._validate_environment(trial_config)
auto_tune_vllm/cli/main.py-155-191 (1)

155-191: Ruff E501: wrap long option/help lines.
CI flags these lines as too long (E501). Consider wrapping the help strings and multi-part prints.

🧹 Example wrapping
-    helm_chart_name: Optional[str] = typer.Option(
-        None, "--helm-chart-name", help="Helm chart name from repository (for Helm backend)"
-    ),
+    helm_chart_name: Optional[str] = typer.Option(
+        None,
+        "--helm-chart-name",
+        help="Helm chart name from repository (for Helm backend)",
+    ),
...
-            console.print(
-                "[bold red]"
-                "Error: At least one Python environment option must be specified for Ray backend"
-                "[/bold red]"
-            )
+            console.print(
+                "[bold red]"
+                "Error: At least one Python environment option must be specified "
+                "for Ray backend"
+                "[/bold red]"
+            )
auto_tune_vllm/core/config.py-700-729 (1)

700-729: Extend dataset-vs-synthetic validation to all synthetic-only fields.
Only prompt_tokens/output_tokens are blocked when dataset is set; the stdev/min/max synthetic fields can still slip through and create contradictory configs.

🔧 Suggested validation
-        # Validate dataset vs synthetic data parameters
+        # Validate dataset vs synthetic data parameters
         # If dataset is provided, prompt_tokens and output_tokens cannot be specified
         dataset_value = benchmark_constants.get("dataset")
         if dataset_value is not None:
-            # Check constants - these are synthetic data parameters
-            synthetic_params_in_constants = []
-            if "prompt_tokens" in benchmark_constants:
-                synthetic_params_in_constants.append("prompt_tokens")
-            if "output_tokens" in benchmark_constants:
-                synthetic_params_in_constants.append("output_tokens")
+            synthetic_fields = {
+                "prompt_tokens",
+                "output_tokens",
+                "prompt_tokens_stdev",
+                "prompt_tokens_min",
+                "prompt_tokens_max",
+                "output_tokens_stdev",
+                "output_tokens_min",
+                "output_tokens_max",
+            }
+            synthetic_params_in_constants = [
+                k for k in benchmark_constants.keys() if k in synthetic_fields
+            ]
             ...
-            synthetic_params_in_tunables = []
-            if "prompt_tokens" in benchmark_tunables_raw:
-                synthetic_params_in_tunables.append("prompt_tokens")
-            if "output_tokens" in benchmark_tunables_raw:
-                synthetic_params_in_tunables.append("output_tokens")
+            synthetic_params_in_tunables = [
+                k for k in benchmark_tunables_raw.keys() if k in synthetic_fields
+            ]
🧹 Nitpick comments (23)
docs/ray_cluster_setup.md (1)

315-403: Add explicit vLLM bind-address guidance for multi-node Ray.

Please add a short note that vLLM servers should bind to 0.0.0.0 in distributed/KubeRay setups so other worker nodes can reach them (otherwise “localhost” binding breaks cross-node access). Based on learnings, ...

examples/study_config_guidellm_k8s_direct.yaml (1)

16-17: Pin the vLLM image tag instead of latest for reproducible benchmarking.

The latest tag is non-deterministic and can change behavior unexpectedly across runs. Pin to a specific stable release (e.g., v0.14.0, the current stable as of January 2026) for consistent results.

♻️ Example pinning
-    vllm_image: "vllm/vllm-openai:latest"  # vLLM container image
+    vllm_image: "vllm/vllm-openai:v0.14.0"  # vLLM container image

Alternatively, use a platform/CUDA-specific variant like v0.14.0-x86_64-cu130 for tighter control.

results-pvc-mlperf.yaml (1)

3-9: Make namespace and StorageClass explicitly configurable.

Hardcoding these values can break in other clusters; add guidance or placeholders to improve portability.

✏️ Suggested clarification
 metadata:
   name: results-pvc-mlperf
-  namespace: autotune-aanya
+  # TODO: replace with your target namespace
+  namespace: autotune-aanya
 spec:
   accessModes:
     - ReadWriteOnce
-  storageClassName: lvms-lvm-vg-nvme-dgx
+  # TODO: replace with your cluster's StorageClass
+  storageClassName: lvms-lvm-vg-nvme-dgx
README.md (1)

31-38: Add Helm/Kubectl prerequisites for the Helm-based path.

Readers may miss required CLI tooling and kubeconfig context.

📝 Suggested doc note
 #### For Helm-based Kubernetes Execution (Remote vLLM)
 ```bash
 git clone https://github.com/openshift-psap/auto-tuning-vllm.git
 cd auto-tuning-vllm
 pip install -e .[helm]

+Prereqs: Helm v3+, kubectl, and a kubeconfig pointing to the target cluster.
+
Note: For Helm deployments, vLLM and benchmarks run remotely in Kubernetes, so they don't need to be installed on the controller.

</details>

</blockquote></details>
<details>
<summary>examples/study_config_optimization_examples.yaml (1)</summary><blockquote>

`167-181`: **Clarify the difference between benchmark vs framework log levels.**

A short comment helps avoid misconfiguration.  

<details>
<summary>📝 Suggested clarification</summary>

```diff
 benchmark:
   # Constants
   benchmark_type: "guidellm"
   model: "facebook/opt-125m"
   max_seconds: 180
   dataset: null
-  logging_level: "INFO"
+  # Benchmark runner verbosity
+  logging_level: "INFO"
   
   # Tunables (empty dict if no tunables needed)
   tunables: {}
 
 logging:
   file_path: "/tmp/auto-tune-vllm-logs"
+  # Auto-tune-vllm framework log level
   log_level: "INFO"
Dockerfile.harness-updated (1)

6-6: Pin MLflow and Blinker versions for reproducible builds.

Unpinned installs can break builds over time. Consider pinning to known-good versions (via constraints or a requirements file) to keep images deterministic.

build-and-push-harness.sh (1)

10-21: Make the image/tag configurable and build relative to the script path.
Hardcoded values and relative Dockerfile paths make the script brittle when run from another directory or for other registries.

♻️ Suggested refactor
+# Resolve script directory for portable paths
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
 # Configuration
-IMAGE_NAME="quay.io/rh-ee-nmiriyal/mlperf-6.0"
-IMAGE_TAG="harness-updated"
-FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
+IMAGE_NAME="${IMAGE_NAME:-quay.io/rh-ee-nmiriyal/mlperf-6.0}"
+IMAGE_TAG="${IMAGE_TAG:-harness-updated}"
+FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
@@
-# Build the image
 echo "Step 1: Building Docker image..."
-docker build -f Dockerfile.harness-updated -t ${FULL_IMAGE} .
+docker build -f "${SCRIPT_DIR}/Dockerfile.harness-updated" -t "${FULL_IMAGE}" "${SCRIPT_DIR}"
scripts/upload-datasets-to-pvc.sh (1)

5-8: Make namespace/PVC/path configurable and validate the dataset path.
Hardcoded values and a user‑specific local path will break for most users.

♻️ Suggested refactor
-NAMESPACE="autotune-aanya"
-PVC_NAME="model-cache-pvc"
-LOCAL_DATASET_DIR="/Users/aansharm/mlperf-build/mlperf-datasets"
+NAMESPACE="${NAMESPACE:-autotune-aanya}"
+PVC_NAME="${PVC_NAME:-model-cache-pvc}"
+LOCAL_DATASET_DIR="${LOCAL_DATASET_DIR:-/path/to/mlperf-build/mlperf-datasets}"
+
+if [ ! -d "$LOCAL_DATASET_DIR/v4" ]; then
+  echo "Error: dataset directory not found: $LOCAL_DATASET_DIR/v4"
+  exit 1
+fi

Also applies to: 39-42

examples/study_config_test_gpt_oss_QUICK_TEST.yaml (1)

14-20: Replace local paths/IPs with placeholders for portability.
The kubeconfig path and MLflow host are environment‑specific; placeholders avoid confusion for new users.

✍️ Suggested doc-style placeholders
-    kubeconfig: "/Users/aansharm/.kube/config"
+    kubeconfig: "/path/to/kubeconfig"
@@
-  mlflow_host: "150.239.115.202"
+  mlflow_host: "<mlflow-host-or-ip>"

Also applies to: 46-47

examples/helm/templates/deployment.yaml (1)

28-46: Make the --port value configurable to avoid service/command mismatches.
Port is hardcoded to 8000 for vllmServe/imageDefault. If service.targetPort (or container ports) differ, the service can route to the wrong port.

♻️ Proposed refactor
           args:
             - --port
-            - "8000"
+            - {{ $.Values.service.targetPort | quote }}
             - --host
             - "0.0.0.0"
@@
           args:
             - --port
-            - "8000"
+            - {{ $.Values.service.targetPort | quote }}
             - --host
             - "0.0.0.0"
auto_tune_vllm/benchmarks/config.py (2)

4-4: Consider updating type hints to modern Python 3.10+ syntax.

Pyright reports that Optional and Dict are deprecated as of Python 3.9/3.10. While functional, consider updating to modern syntax for consistency.

♻️ Proposed modernization
-from typing import Any, Dict, Literal, Optional
+from typing import Any, Literal

Then update usages throughout the file:

  • Optional[str]str | None
  • Optional[int]int | None
  • Dict[str, Any]dict[str, Any]

25-26: Fix line length violations flagged by Ruff.

Lines 25, 49, and 50 exceed the 88-character limit. Move comments to separate lines.

♻️ Proposed fix for line length
-    rate: int = 50  # Single rate value for concurrent requests (can be overridden by tunables)
-    samples: int = 1000  # Number of samples to take (can be overridden by tunables)
+    # Single rate value for concurrent requests (can be overridden by tunables)
+    rate: int = 50
+    # Number of samples to take (can be overridden by tunables)
+    samples: int = 1000
-    server_target_qps: Optional[float] = None  # Target QPS for Server scenario (tunable)
-    server_coalesce_queries: Optional[bool] = None  # Coalesce queries flag for Server scenario (tunable)
+    # Target QPS for Server scenario (tunable)
+    server_target_qps: Optional[float] = None
+    # Coalesce queries flag for Server scenario (tunable)
+    server_coalesce_queries: Optional[bool] = None
examples/study_config.yaml (1)

4-4: Consider using environment variable placeholders for credentials in examples.

Example configuration files often get copied directly into production. Using hardcoded credentials like postgres:postgres may inadvertently encourage insecure practices. The documentation already shows environment variable expansion (${POSTGRES_PASSWORD}).

♻️ Proposed fix
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna"
+  database_url: "postgresql://postgres:${POSTGRES_PASSWORD:-postgres}@localhost:5432/optuna"

This demonstrates the environment variable pattern while still working out-of-the-box with a default value for quick local testing.

examples/trial_config_comprehensive.yaml (1)

18-18: Clarify relationship between logging configurations.

There are two logging-related configurations: logging_level within the benchmark block (line 18) and log_level within the dedicated logging block (line 45). If these serve different purposes (e.g., benchmark-specific vs. global application logging), consider adding a brief comment to clarify their scopes.

Also applies to: 43-45

examples/helm/deploy-postgres-standalone.sh (1)

68-68: Add trailing newline.

The file is missing a trailing newline, which is a POSIX convention and can cause issues with some tools.

Proposed fix
 echo "Service endpoint:"
 echo "  postgresql.${NAMESPACE}.svc.cluster.local:5432"
+
examples/helm/templates/postgres-secret.yaml (1)

12-13: Add validation for required credentials.

If .Values.postgresql.auth.username or .Values.postgresql.auth.password is not set, the Secret will contain empty credentials, which could cause PostgreSQL authentication failures. Consider using the required function to fail fast during template rendering.

Proposed fix
 data:
-  postgres-user: {{ .Values.postgresql.auth.username | b64enc | quote }}
-  postgres-password: {{ .Values.postgresql.auth.password | b64enc | quote }}
+  postgres-user: {{ required "postgresql.auth.username is required" .Values.postgresql.auth.username | b64enc | quote }}
+  postgres-password: {{ required "postgresql.auth.password is required" .Values.postgresql.auth.password | b64enc | quote }}
examples/study_config_mlperf_server_k8s_direct.yaml (1)

20-20: User-specific path should be a placeholder in example configs.

The kubeconfig path /Users/aansharm/.kube/config is user-specific and won't work for others. Use a placeholder like $HOME/.kube/config or "/path/to/your/kubeconfig" to make it clear this needs customization.

Suggested fix
-    kubeconfig: "/Users/aansharm/.kube/config"  # Optional: custom kubeconfig path
+    kubeconfig: "$HOME/.kube/config"  # Optional: custom kubeconfig path
docs/helm_deployment.md (1)

179-182: Consider noting the security implications of piping curl to bash.

While this is the official Helm installation method, security-conscious users may prefer verifying the script first or using package managers. Consider adding an alternative:

# Or use package manager (safer alternative)
# macOS: brew install helm
# Linux: snap install helm --classic
examples/helm/postgres-standalone.yaml (2)

72-105: Add securityContext to follow Kubernetes security best practices.

Even for example files, including a security context sets a good precedent and avoids privilege escalation risks when users copy this configuration.

Suggested addition after line 71
     spec:
+      securityContext:
+        runAsNonRoot: true
+        runAsUser: 999  # postgres user
+        fsGroup: 999
       containers:
       - name: postgresql
         image: postgres:15
         imagePullPolicy: IfNotPresent
+        securityContext:
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: false
+          capabilities:
+            drop:
+              - ALL

126-135: Storage class nfs-storage may not exist in all clusters.

Consider adding a comment noting this should be changed to match the target cluster's available storage classes, or use a more common default.

Suggested improvement
   volumeClaimTemplates:
   - metadata:
       name: postgresql-data
     spec:
       accessModes:
       - ReadWriteOnce
-      storageClassName: nfs-storage
+      storageClassName: nfs-storage  # CHANGE THIS: Use your cluster's available storage class (e.g., standard, gp2, managed-premium)
       resources:
         requests:
           storage: 20Gi
examples/study_config_mlperf_server_k8s.yaml (1)

1-6: File name vs content mismatch.

The filename is study_config_mlperf_server_k8s.yaml suggesting direct Kubernetes, but the content configures backend: "helm". Consider renaming to study_config_mlperf_server_helm.yaml for consistency with other example files, or update the header comments to clarify this is for Helm deployment.

examples/helm/templates/postgres-statefulset.yaml (1)

23-74: Consider exposing container securityContext via values.

This helps deploy cleanly on restricted/PSA clusters and documents least‑privilege defaults.

🔧 Optional values-driven securityContext
       containers:
       - name: postgresql
         image: {{ .Values.postgresql.image.repository }}:{{ .Values.postgresql.image.tag }}
         imagePullPolicy: {{ .Values.postgresql.image.pullPolicy | default "IfNotPresent" }}
+        {{- if .Values.postgresql.securityContext }}
+        securityContext:
+          {{- toYaml .Values.postgresql.securityContext | nindent 10 }}
+        {{- end }}
         ports:
examples/helm/postgres-test-pod-helm.yaml (1)

15-41: Add a minimal securityContext to align with K8s security checks.

Even test pods often run under restricted policies; adding a minimal securityContext avoids failures and improves posture.

🔒 Suggested securityContext
   containers:
   - name: postgres-client
     image: postgres:15
+    securityContext:
+      allowPrivilegeEscalation: false
+      runAsNonRoot: true
+      seccompProfile:
+        type: RuntimeDefault
+      capabilities:
+        drop: ["ALL"]

Comment on lines +2325 to +2331
if RAY_AVAILABLE:
@ray.remote
class WorkloadActor:
"""Ray actor that runs benchmark workload."""
def __init__(self): pass
def run_benchmark(self, *args, **kwargs): return {}
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.

⚠️ Potential issue | 🔴 Critical

WorkloadActor method name mismatch will crash Ray trials.
RayExecutionBackend.submit_trial() calls workload_actor.run_workload.remote(...), but the actor defines run_benchmark. This will raise AttributeError at runtime.

🔧 Align method name
 if RAY_AVAILABLE:
     `@ray.remote`
     class WorkloadActor:
         """Ray actor that runs benchmark workload."""
         def __init__(self): pass
-        def run_benchmark(self, *args, **kwargs): return {}
+        def run_workload(self, *args, **kwargs): return {}
 else:
     class WorkloadActor:
         """Dummy WorkloadActor when Ray is not available."""
         pass
📝 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 RAY_AVAILABLE:
@ray.remote
class WorkloadActor:
"""Ray actor that runs benchmark workload."""
def __init__(self): pass
def run_benchmark(self, *args, **kwargs): return {}
else:
if RAY_AVAILABLE:
`@ray.remote`
class WorkloadActor:
"""Ray actor that runs benchmark workload."""
def __init__(self): pass
def run_workload(self, *args, **kwargs): return {}
else:
🤖 Prompt for AI Agents
In `@auto_tune_vllm/execution/trial_controller.py` around lines 2325 - 2331, The
WorkloadActor Ray class defines run_benchmark but
RayExecutionBackend.submit_trial expects run_workload, causing AttributeError;
fix by aligning names — either add a run_workload method to the WorkloadActor
that delegates to run_benchmark (or rename run_benchmark to run_workload) so the
actor exposes the method called by RayExecutionBackend.submit_trial; update only
the actor definition (class WorkloadActor) or the caller (method submit_trial)
to use the same method name consistently.

aas008 and others added 2 commits January 26, 2026 15:45
- Fix server_coalesce_queries argument passing to accept boolean value
- Add trial-specific output directories to prevent MLflow artifact accumulation
- Update benchmark image to version with api_result parameter fix

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

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

🤖 Fix all issues with AI agents
In `@auto_tune_vllm/benchmarks/providers.py`:
- Around line 643-650: The current check mistakenly compares config.num_samples
to itself; change the condition to compare config.batch_size !=
config.num_samples when config.num_samples is not None to enforce equality for
the "Offline" scenario, and do not mutate the passed-in BenchmarkConfig: compute
a local variable (e.g., batch_size = config.batch_size if config.batch_size is
not None else config.num_samples) and use that local when calling
cmd.extend(["--batch-size", str(batch_size)]) and when validating equality
between config.num_samples and config.batch_size.

In `@examples/study_config_test_gpt_oss.yaml`:
- Line 20: Replace the user-specific hardcoded kubeconfig value by changing the
"kubeconfig" YAML field to a generic placeholder or commented instruction (e.g.,
set kubeconfig: "<PATH_TO_YOUR_KUBECONFIG>" or comment that users should replace
with their own path) so the examples/study_config_test_gpt_oss.yaml is portable
and not tied to a specific user's home directory.
- Around line 1-3: Update the top header comment to match the actual configured
scenario: replace the phrase "Server Scenario" in the header comment with
"Offline Scenario" (the configuration uses scenario: "Offline"); ensure the
header consistently describes the file's purpose and references the scenario key
scenario: "Offline" so the comment and the config stay in sync.
🧹 Nitpick comments (5)
auto_tune_vllm/benchmarks/providers.py (3)

556-564: Fix line length violations.

Lines 558 and 563 exceed the 88-character limit per ruff E501.

✨ Proposed fix
         # Validate required metric for optimization
         if "output_tokens_per_second" not in data:
             raise RuntimeError(
-                f"Required metric 'output_tokens_per_second' not found in MLPerf results. "
-                f"Available keys: {list(data.keys())}"
+                "Required metric 'output_tokens_per_second' not found in "
+                f"MLPerf results. Available keys: {list(data.keys())}"
             )
 
-        self._logger.info(
-            f"Parsed MLPerf results: output_tokens_per_second={data['output_tokens_per_second']}"
-        )
+        tokens_per_sec = data['output_tokens_per_second']
+        self._logger.info(f"Parsed MLPerf results: output_tokens_per_second={tokens_per_sec}")
         return data

567-604: Extract duplicate _get_results_file_path to base class.

This method is identical to GuideLLMBenchmark._get_results_file_path (lines 276-313). Moving it to the BenchmarkProvider base class would eliminate duplication and ensure consistent behavior across providers.


656-660: Fix line length violations.

Lines 644 and 658 exceed the 88-character limit per ruff E501.

✨ Proposed fix for line 658
             if config.server_coalesce_queries is not None:
-                cmd.extend(["--server-coalesce-queries", str(config.server_coalesce_queries).lower()])
+                coalesce = str(config.server_coalesce_queries).lower()
+                cmd.extend(["--server-coalesce-queries", coalesce])
examples/study_config_test_gpt_oss.yaml (2)

5-9: Consider using environment variable references for credentials.

Plain-text credentials in configuration files (even examples) can be accidentally committed to production. Consider documenting that these are placeholders or using environment variable substitution syntax.

✨ Suggested improvement
 study:
   prefix: "autotune_test_gpt_oss"
-  database_url: "postgresql://postgres:postgres@localhost:5432/optuna-k8s-direct"
+  database_url: "${OPTUNA_DATABASE_URL}"  # e.g., postgresql://user:pass@host:5432/db
   # For Kubernetes: Use port-forwarding or cluster-internal database URL
   # Example with port-forward: kubectl port-forward -n <namespace> svc/postgresql 5432:5432

51-52: Replace hardcoded IP address with placeholder or hostname.

The hardcoded IP 150.239.115.202 for mlflow_host may become stale. Consider using a hostname, environment variable, or clearly documenting this as a placeholder.

✨ Suggested improvement
   mlflow_experiment_name: "autotune-gpt-oss-120b-offline"  # MLflow experiment name
-  mlflow_host: "150.239.115.202"  # MLflow host address (must be accessible from pods)
+  mlflow_host: "${MLFLOW_HOST}"  # MLflow host address (must be accessible from pods)

Comment on lines +643 to +650
if config.scenario == "Offline":
if config.num_samples is not None and config.num_samples != config.num_samples:
raise ValueError(
"num_samples must be equal to batch_size for Offline scenario"
)
if config.batch_size is None:
config.batch_size = config.num_samples
cmd.extend(["--batch-size", str(config.batch_size)])

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.

⚠️ Potential issue | 🔴 Critical

Critical logic error: condition always evaluates to False.

Line 644 compares config.num_samples to itself (config.num_samples != config.num_samples), which is always False. This should likely compare config.batch_size to config.num_samples.

Additionally, line 649 directly mutates the passed-in config object, which can cause unexpected side effects since BenchmarkConfig may be reused.

🐛 Proposed fix
         # Scenario-specific parameters
         if config.scenario == "Offline":
-            if config.num_samples is not None and config.num_samples != config.num_samples:
+            if config.batch_size is not None and config.batch_size != config.num_samples:
                 raise ValueError(
                     "num_samples must be equal to batch_size for Offline scenario"
                 )
-            if config.batch_size is None:
-                config.batch_size = config.num_samples
-            cmd.extend(["--batch-size", str(config.batch_size)])
+            batch_size = config.batch_size if config.batch_size is not None else config.num_samples
+            cmd.extend(["--batch-size", str(batch_size)])
📝 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 config.scenario == "Offline":
if config.num_samples is not None and config.num_samples != config.num_samples:
raise ValueError(
"num_samples must be equal to batch_size for Offline scenario"
)
if config.batch_size is None:
config.batch_size = config.num_samples
cmd.extend(["--batch-size", str(config.batch_size)])
if config.scenario == "Offline":
if config.batch_size is not None and config.batch_size != config.num_samples:
raise ValueError(
"num_samples must be equal to batch_size for Offline scenario"
)
batch_size = config.batch_size if config.batch_size is not None else config.num_samples
cmd.extend(["--batch-size", str(batch_size)])
🧰 Tools
🪛 GitHub Check: ruff

[failure] 644-644: Ruff (E501)
auto_tune_vllm/benchmarks/providers.py:644:89: E501 Line too long (91 > 88)

🤖 Prompt for AI Agents
In `@auto_tune_vllm/benchmarks/providers.py` around lines 643 - 650, The current
check mistakenly compares config.num_samples to itself; change the condition to
compare config.batch_size != config.num_samples when config.num_samples is not
None to enforce equality for the "Offline" scenario, and do not mutate the
passed-in BenchmarkConfig: compute a local variable (e.g., batch_size =
config.batch_size if config.batch_size is not None else config.num_samples) and
use that local when calling cmd.extend(["--batch-size", str(batch_size)]) and
when validating equality between config.num_samples and config.batch_size.

Comment on lines +1 to +3
# Example study configuration for MLPerf Harness - Server Scenario on Kubernetes
# This uses the direct Kubernetes Deployment/Pod backend (no Helm)
# Simpler than Helm backend - creates Deployments and Services directly

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.

⚠️ Potential issue | 🟡 Minor

Fix inconsistent header comment.

The header states "Server Scenario" but line 46 configures scenario: "Offline". Update the comment to match the actual configuration.

✏️ Proposed fix
-# Example study configuration for MLPerf Harness - Server Scenario on Kubernetes
+# Example study configuration for MLPerf Harness - Offline Scenario on Kubernetes
 # This uses the direct Kubernetes Deployment/Pod backend (no Helm)
 # Simpler than Helm backend - creates Deployments and Services directly
📝 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
# Example study configuration for MLPerf Harness - Server Scenario on Kubernetes
# This uses the direct Kubernetes Deployment/Pod backend (no Helm)
# Simpler than Helm backend - creates Deployments and Services directly
# Example study configuration for MLPerf Harness - Offline Scenario on Kubernetes
# This uses the direct Kubernetes Deployment/Pod backend (no Helm)
# Simpler than Helm backend - creates Deployments and Services directly
🤖 Prompt for AI Agents
In `@examples/study_config_test_gpt_oss.yaml` around lines 1 - 3, Update the top
header comment to match the actual configured scenario: replace the phrase
"Server Scenario" in the header comment with "Offline Scenario" (the
configuration uses scenario: "Offline"); ensure the header consistently
describes the file's purpose and references the scenario key scenario: "Offline"
so the comment and the config stay in sync.

benchmark_image: "quay.io/rh-ee-aansharm/mlperf-6.0@sha256:ac63fa7a124c6901ce6538f7f1fd52ef6d7c328a1bbd300fbe187d2104901b8b"
service_type: "ClusterIP" # Service type: ClusterIP, NodePort, or LoadBalancer
service_port: 8000 # Service port for vLLM server
kubeconfig: "/Users/aansharm/.kube/config" # Optional: custom kubeconfig path

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.

⚠️ Potential issue | 🟡 Minor

Replace user-specific path with placeholder.

The hardcoded path /Users/aansharm/.kube/config is user-specific and not portable. Use a placeholder or comment to indicate this should be customized.

✏️ Proposed fix
-    kubeconfig: "/Users/aansharm/.kube/config"  # Optional: custom kubeconfig path
+    kubeconfig: "~/.kube/config"  # Optional: custom kubeconfig path (update for your environment)
📝 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
kubeconfig: "/Users/aansharm/.kube/config" # Optional: custom kubeconfig path
kubeconfig: "~/.kube/config" # Optional: custom kubeconfig path (update for your environment)
🤖 Prompt for AI Agents
In `@examples/study_config_test_gpt_oss.yaml` at line 20, Replace the
user-specific hardcoded kubeconfig value by changing the "kubeconfig" YAML field
to a generic placeholder or commented instruction (e.g., set kubeconfig:
"<PATH_TO_YOUR_KUBECONFIG>" or comment that users should replace with their own
path) so the examples/study_config_test_gpt_oss.yaml is portable and not tied to
a specific user's home directory.

@aas008
aas008 changed the base branch from main to dev/reusable-components+benchmark-tunables January 27, 2026 15:12
aas008 and others added 5 commits January 27, 2026 10:19
- Reformatted 18 files with ruff format
- Fixed 31 auto-fixable linting errors
- Remaining: 307 E501 line-too-long warnings (mostly strings/comments)
- Add tensor_parallel_size field to BenchmarkConfig for MLflow tagging
- Inject TP from static_params and pass to MLflow via --mlflow-description
- Add enable_metrics flag to BenchmarkConfig for metrics collection
- Improve trial log management with component-based organization (controller, vllm, benchmark)
- Enhance PostgreSQL logging with structured trial_logs table
- Update version to 0.1.1

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement automatic collection of vLLM and benchmark pod logs to PostgreSQL
before pod deletion, preventing log loss in Kubernetes deployments.

Key changes:
- Add pod_log_collector.py module with functions to collect logs from Job
  and Deployment pods via Kubernetes API
- Extend trial_logs table schema with pod_name and container_name columns
  for pod metadata tracking
- Integrate log collection in HelmTrialController and
  KubernetesTrialController finally blocks (before pod deletion)
- Use line-by-line insertion in single transaction for simplicity
- Non-blocking: log collection failures do not fail trial cleanup

New component types in trial_logs:
- 'vllm-pod': Logs from vLLM Deployment pods
- 'benchmark-pod': Logs from benchmark Job pods

This enables post-mortem debugging of failed trials and log analysis
even after pods are deleted by the trial cleanup process.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add result validation tracking (result_valid, result_status user attributes in Optuna)
- Add MLflow enhancements: mlflow_port, system tagging, custom mlflow_tags support
- Add MLPerf harness debug logging support (log_level, enable_trace flags)
- Add num_workers parameter for async request processing in Server scenario
- Add mlflow>=2.0.0 dependency to pyproject.toml

These changes enable better tracking of VALID/INVALID runs in Optuna dashboard
and provide more comprehensive MLflow experiment tracking capabilities.
@aas008 aas008 changed the title Feat/mlperf gpt oss support Feat/mlperf_k8s_support Feb 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant