Skip to content

feat: Isolate benchmark workloads with per-deployment lifecycle#129

Merged
openshift-merge-bot[bot] merged 13 commits into
openshift-psap:mainfrom
albertoperdomo2:feat/benchmark-matrix-isolation
Jul 20, 2026
Merged

feat: Isolate benchmark workloads with per-deployment lifecycle#129
openshift-merge-bot[bot] merged 13 commits into
openshift-psap:mainfrom
albertoperdomo2:feat/benchmark-matrix-isolation

Conversation

@albertoperdomo2

@albertoperdomo2 albertoperdomo2 commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features
    • Added benchmark isolation per workload using fresh inference service deployments, dedicated namespaces/artifacts, smoke tests, runs, and teardown.
    • Extended run planning to support model × deployment profile × benchmark combinations with workload-specific deployment overrides and required runtime environment settings.
    • Kept backward compatibility for benchmark-disabled, single, and multi-benchmark modes.
  • Bug Fixes
    • Updated CI/test orchestration to execute once per configured run spec and aggregate results.
    • Refined cleanup and benchmark job handling to align with the active run spec.
    • Improved Kubernetes resource deletion with explicit timeouts and better failure diagnostics.
  • Documentation
    • Added a new benchmark isolation guide covering architecture and the 3D run matrix.
  • Tests
    • Expanded validation for run-spec generation/activation, override merging, naming uniqueness, and benchmark job-name behavior.
  • Chores
    • Made Kubernetes name truncation collision-resistant.

Signed-off-by: Alberto Perdomo <aperdomo@redhat.com>
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@albertoperdomo2, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e031cf10-846b-4762-9b21-3304faa26836

📥 Commits

Reviewing files that changed from the base of the PR and between 7f1382c and 3bc2997.

📒 Files selected for processing (1)
  • projects/llm_d/orchestration/test_phase.py
📝 Walkthrough

Walkthrough

The PR expands llm_d execution into benchmark-aware RunSpecs across models, deployment profiles, and benchmark keys. CI runs each specification with isolated artifacts and active configuration, while deployment overrides, cleanup, documentation, and profile tests are updated.

Changes

Benchmark isolation

Layer / File(s) Summary
Benchmark-aware RunSpec matrix
projects/llm_d/orchestration/runtime_config.py, projects/llm_d/docs/benchmark-isolation.md, projects/llm_d/tests/test_profiles.py, projects/core/dsl/utils/__init__.py
Run specifications now include benchmark keys and slugs, support activation-scoped configuration, deep-merged deployment overrides, backward-compatible benchmark inputs, collision-resistant Kubernetes names, and unique namespaces and artifact directories.
Per-RunSpec execution and cleanup
projects/llm_d/orchestration/ci.py, projects/llm_d/orchestration/test_phase.py, projects/llm_d/orchestration/cleanup_phase.py, projects/llm_d/toolbox/cleanup_test_resources/main.py
CI iterates through RunSpecs, isolates artifacts, applies active benchmark overrides, runs one benchmark, and cleans up with the active job name and explicit deletion failure handling and timeouts.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CI
  participant RuntimeConfig
  participant TestPhase
  participant CleanupPhase
  CI->>RuntimeConfig: get_run_specs()
  CI->>RuntimeConfig: activate_run_spec(run_spec)
  CI->>TestPhase: run test with isolated artifacts
  TestPhase->>TestPhase: deploy with workload overrides
  TestPhase->>TestPhase: run active benchmark
  CI->>CleanupPhase: clean up active benchmark resources
Loading

Possibly related PRs

  • openshift-psap/forge#72: Updates the same benchmark configuration and orchestration paths for benchmark/job selection.
  • openshift-psap/forge#126: Implements related benchmark-aware RunSpec expansion and per-benchmark orchestration changes.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: isolating benchmark workloads with a per-deployment lifecycle.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

@albertoperdomo2

Copy link
Copy Markdown
Collaborator Author

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-full

@psap-forge-bot

Copy link
Copy Markdown
🔴 Submission of llm_d smoke failed after 1 hour, 46 seconds 🔴

Error: FournosJobFailureError: FOURNOS Job 'forge-llm-d-20260716-091206' failed: PipelineRun "forge-llm-d-20260716-091206" failed to finish within "1h0m0s"

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-full

@albertoperdomo2

Copy link
Copy Markdown
Collaborator Author

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-full

@psap-forge-bot

Copy link
Copy Markdown
🔴 Submission of llm_d smoke failed after 1 hour, 41 seconds 🔴

Error: FournosJobFailureError: FOURNOS Job 'forge-llm-d-20260716-104344' failed: PipelineRun "forge-llm-d-20260716-104344" failed to finish within "1h0m0s"

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-full

@albertoperdomo2

Copy link
Copy Markdown
Collaborator Author

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-test-only

@psap-forge-bot

Copy link
Copy Markdown
🔴 Submission of llm_d smoke failed after 35 minutes, 40 seconds 🔴

Error: FournosJobFailureError: FOURNOS Job 'forge-llm-d-20260716-120712' failed: Job not found - may have been deleted or never created

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-test-only

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
projects/llm_d/docs/benchmark-isolation.md (2)

57-61: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Specify a language for the fenced code block.

Add a language identifier (e.g., text or yaml) to ensure proper syntax highlighting and avoid linter warnings.

♻️ Proposed fix
-```
+```text
 /var runtime.model_name: Qwen/Qwen3-0.6B
 /var runtime.deployment_profile: [approximate-prefix-cache, distributed-default]
 /var runtime.benchmark_key: [short, multi-turn]
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In @projects/llm_d/docs/benchmark-isolation.md around lines 57 - 61, Update the
fenced code block near the runtime.model_name, runtime.deployment_profile, and
runtime.benchmark_key entries to include an appropriate language identifier,
such as text, while preserving its contents.


</details>

<!-- cr-comment:v1:5fc7fda2abe221bf60e30b7f -->

_Source: Linters/SAST tools_

---

`44-46`: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _💤 Low value_

**Specify a language for the fenced code block.**

Add a language identifier (e.g., `text`) to ensure proper syntax highlighting and avoid linter warnings.





<details>
<summary>♻️ Proposed fix</summary>

```diff
-```
+```text
 model_name x deployment_profile x benchmark_key
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In @projects/llm_d/docs/benchmark-isolation.md around lines 44 - 46, Update the
fenced code block containing “model_name x deployment_profile x benchmark_key”
to include an explicit text language identifier, such as text, while preserving
its contents.


</details>

<!-- cr-comment:v1:2fee857ec22396fa9d3bbd00 -->

_Source: Linters/SAST tools_

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

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 @projects/llm_d/orchestration/runtime_config.py:

  • Around line 104-114: Update deep_merge so when both the existing merged value
    and the override value are lists, it concatenates the base list with the
    override list instead of replacing the base contents. Preserve recursive
    dictionary merging and existing override behavior for all other value types.
  • Around line 353-356: The namespace construction around truncate_k8s_name must
    preserve uniqueness when the combined namespace parts exceed
    namespace_max_length. Add a deterministic short hash derived from the full
    joined namespace parts as a suffix, truncate the readable prefix as needed to
    fit the Kubernetes limit, and ensure different run specifications with distinct
    trailing identifiers produce distinct namespaces.

Nitpick comments:
In @projects/llm_d/docs/benchmark-isolation.md:

  • Around line 57-61: Update the fenced code block near the runtime.model_name,
    runtime.deployment_profile, and runtime.benchmark_key entries to include an
    appropriate language identifier, such as text, while preserving its contents.
  • Around line 44-46: Update the fenced code block containing “model_name x
    deployment_profile x benchmark_key” to include an explicit text language
    identifier, such as text, while preserving its contents.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `567096ea-f6bd-48a0-b531-d5e7cd6a51ac`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 441ea2d6d389ae31c2022954cf256b678885e832 and 4e4ad1a659e72d8abef5e0689ae065b3c5e512f7.

</details>

<details>
<summary>📒 Files selected for processing (6)</summary>

* `projects/llm_d/docs/benchmark-isolation.md`
* `projects/llm_d/orchestration/ci.py`
* `projects/llm_d/orchestration/cleanup_phase.py`
* `projects/llm_d/orchestration/runtime_config.py`
* `projects/llm_d/orchestration/test_phase.py`
* `projects/llm_d/tests/test_profiles.py`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +104 to 114
def deep_merge(base: Any, override: Any) -> Any:
if not isinstance(base, dict) or not isinstance(override, dict):
return copy.deepcopy(override)

merged = copy.deepcopy(base)
for key, value in override.items():
if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
merged[key] = _deep_merge(merged[key], value)
merged[key] = deep_merge(merged[key], value)
else:
merged[key] = copy.deepcopy(value)
return merged

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

deep_merge overwrites lists instead of extending them.

Because deep_merge only recurses on dictionaries, any list in the override dictionary will completely replace the corresponding list in the base dictionary.

For instance, if a deployment profile specifies vllm_args: ["--tensor-parallel-size=4"] and the active benchmark defines deployment_overrides: {vllm_args: ["--max-model-len=32768"]}, the base vllm_args will be wiped out entirely. This can lead to unexpected deployment failures (e.g., OOM crashes) due to missing required base arguments.

Consider updating the merge logic to concatenate lists, or clearly document that list fields must be comprehensively redefined in deployment_overrides.

🤖 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 `@projects/llm_d/orchestration/runtime_config.py` around lines 104 - 114,
Update deep_merge so when both the existing merged value and the override value
are lists, it concatenates the base list with the override list instead of
replacing the base contents. Preserve recursive dictionary merging and existing
override behavior for all other value types.

Comment on lines 353 to 356
namespace = truncate_k8s_name(
f"{base_namespace}-{model_slug}-{profile_slug}",
"-".join(ns_parts),
max_length=namespace_max_length,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

Namespace truncation drops identifying slugs, causing collisions.

Because truncate_k8s_name truncates strings from the right, distinguishing trailing identifiers like bench_slug will be lost if the preceding segments approach the 63-character limit (e.g., due to long model or profile names). This will generate identical namespaces for different run specs, leading to resource collisions and failures during test executions.

Consider computing a deterministic short hash of the combined parts to ensure uniqueness when the raw string exceeds the namespace length limit.

🛡️ Proposed fix (using a hashed suffix for uniqueness)
-            namespace = truncate_k8s_name(
-                "-".join(ns_parts),
-                max_length=namespace_max_length,
-            )
+            raw_namespace = "-".join(ns_parts)
+            if len(raw_namespace) <= namespace_max_length:
+                namespace = raw_namespace
+            else:
+                import hashlib
+                # Keep the beginning and append a deterministic short hash to avoid collisions
+                prefix = truncate_k8s_name(raw_namespace, max_length=namespace_max_length - 7)
+                suffix = hashlib.md5(raw_namespace.encode()).hexdigest()[:6]
+                namespace = f"{prefix}-{suffix}"
📝 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
namespace = truncate_k8s_name(
f"{base_namespace}-{model_slug}-{profile_slug}",
"-".join(ns_parts),
max_length=namespace_max_length,
)
raw_namespace = "-".join(ns_parts)
if len(raw_namespace) <= namespace_max_length:
namespace = raw_namespace
else:
import hashlib
# Keep the beginning and append a deterministic short hash to avoid collisions
prefix = truncate_k8s_name(raw_namespace, max_length=namespace_max_length - 7)
suffix = hashlib.md5(raw_namespace.encode()).hexdigest()[:6]
namespace = f"{prefix}-{suffix}"
🤖 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 `@projects/llm_d/orchestration/runtime_config.py` around lines 353 - 356, The
namespace construction around truncate_k8s_name must preserve uniqueness when
the combined namespace parts exceed namespace_max_length. Add a deterministic
short hash derived from the full joined namespace parts as a suffix, truncate
the readable prefix as needed to fit the Kubernetes limit, and ensure different
run specifications with distinct trailing identifiers produce distinct
namespaces.

Signed-off-by: Alberto Perdomo <aperdomo@redhat.com>
…erdomo2/forge into feat/benchmark-matrix-isolation

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@projects/llm_d/toolbox/cleanup_test_resources/main.py`:
- Line 223: Update the oc cleanup invocation to capture its CommandResult, then
log a warning when returncode is non-zero while preserving the existing
--timeout=120s and check=False behavior. Use the surrounding cleanup function’s
established logging mechanism and reference the captured result so delete
failures are no longer silently ignored.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7177bd67-b5e8-48ca-b0d1-9f848705758e

📥 Commits

Reviewing files that changed from the base of the PR and between 4e4ad1a and efa8b03.

📒 Files selected for processing (1)
  • projects/llm_d/toolbox/cleanup_test_resources/main.py

"""Best effort deletion of Kubernetes resources with timeout"""
try:
oc(*oc_args, check=False)
oc(*oc_args, "--timeout=120s", check=False)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the target file and nearby definitions.
git ls-files 'projects/llm_d/toolbox/cleanup_test_resources/main.py' 'projects/llm_d/toolbox/cleanup_test_resources/*' | sed -n '1,20p'

echo '--- main.py around line 223 ---'
nl -ba projects/llm_d/toolbox/cleanup_test_resources/main.py | sed -n '190,250p'

echo '--- search for oc helper and callers ---'
rg -n "def oc\b|oc\(" projects/llm_d/toolbox/cleanup_test_resources -S

echo '--- broader references to subprocess timeout handling ---'
rg -n "TimeoutExpired|CalledProcessError|check=False|check=True" projects/llm_d/toolbox/cleanup_test_resources -S

Repository: openshift-psap/forge

Length of output: 284


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- main.py lines 190-250 ---'
sed -n '190,250p' projects/llm_d/toolbox/cleanup_test_resources/main.py

echo '--- definitions/usages of oc helper in cleanup_test_resources ---'
rg -n "def oc\b|oc_args|TimeoutExpired|CalledProcessError|check=False|check=True" projects/llm_d/toolbox/cleanup_test_resources -S

echo '--- list files in cleanup_test_resources ---'
git ls-files 'projects/llm_d/toolbox/cleanup_test_resources/*'

Repository: openshift-psap/forge

Length of output: 2632


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- find oc helper definitions ---'
rg -n "^def oc\b|^async def oc\b|oc =|CommandResult|returncode|raise_for_status" projects -S

echo '--- find subprocess wrapper implementations that may define check=False behavior ---'
rg -n "class .*CommandResult|def run\(|check=False|TimeoutExpired|CalledProcessError" projects -S

Repository: openshift-psap/forge

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- core/dsl/utils/k8s.py (oc helper) ---'
sed -n '1,140p' projects/core/dsl/utils/k8s.py

echo '--- cleanup_test_resources/main.py around best-effort delete callers ---'
sed -n '1,240p' projects/llm_d/toolbox/cleanup_test_resources/main.py

echo '--- cleanup_phase best-effort delete pattern for comparison ---'
sed -n '120,160p' projects/llm_d/orchestration/cleanup_phase.py

Repository: openshift-psap/forge

Length of output: 11342


Warn on oc delete failures here. check=False swallows non-zero exits, and subprocess.TimeoutExpired won’t fire for the --timeout=120s flag because that only applies to oc itself. Capture the CommandResult and log on returncode != 0, otherwise cleanup failures are silently ignored.

🤖 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 `@projects/llm_d/toolbox/cleanup_test_resources/main.py` at line 223, Update
the oc cleanup invocation to capture its CommandResult, then log a warning when
returncode is non-zero while preserving the existing --timeout=120s and
check=False behavior. Use the surrounding cleanup function’s established logging
mechanism and reference the captured result so delete failures are no longer
silently ignored.

Signed-off-by: Alberto Perdomo <aperdomo@redhat.com>
Signed-off-by: Alberto Perdomo <aperdomo@redhat.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
projects/llm_d/toolbox/cleanup_test_resources/main.py (1)

222-231: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove unreachable try-except block for timeouts.

The subprocess.TimeoutExpired exception will not be raised here. The underlying shell.run wrapper calls subprocess.run without a timeout argument. The --timeout=300s argument applies directly to the oc CLI, which will simply terminate and return a non-zero exit code if it times out. This timeout failure is already perfectly handled by your new if result.returncode != 0: check, making the try...except block obsolete.

Additionally, as a minor caveat: if oc_args contains delete-specific flags (e.g., --force or --grace-period), the subsequent oc describe command will reject them and fail silently due to check=False. This is generally acceptable for a best-effort debug function, but you might want to keep it in mind if you notice missing debug logs.

♻️ Proposed refactor to remove the unreachable exception handling
-    try:
-        result = oc(*oc_args, "--timeout=300s", check=False)
-        if result.returncode != 0:
-            logger.warning(
-                "Failed to delete %s (rc=%d), capturing describe", description, result.returncode
-            )
-            describe_args = tuple(a for a in oc_args if a != "delete")
-            oc("describe", *describe_args, check=False)
-    except subprocess.TimeoutExpired:
-        logger.warning("Timed out deleting %s: oc %s", description, " ".join(oc_args))
+    result = oc(*oc_args, "--timeout=300s", check=False)
+    if result.returncode != 0:
+        logger.warning(
+            "Failed to delete %s (rc=%d), capturing describe", description, result.returncode
+        )
+        describe_args = tuple(a for a in oc_args if a != "delete")
+        oc("describe", *describe_args, check=False)
🤖 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 `@projects/llm_d/toolbox/cleanup_test_resources/main.py` around lines 222 -
231, Remove the unnecessary try-except wrapper around the oc invocation in the
cleanup flow. Keep the existing result.returncode check, warning, describe
capture, and timeout-related --timeout=300s argument unchanged so CLI timeouts
continue through the nonzero return-code handling; do not add replacement
exception handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@projects/llm_d/toolbox/cleanup_test_resources/main.py`:
- Around line 222-231: Remove the unnecessary try-except wrapper around the oc
invocation in the cleanup flow. Keep the existing result.returncode check,
warning, describe capture, and timeout-related --timeout=300s argument unchanged
so CLI timeouts continue through the nonzero return-code handling; do not add
replacement exception handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: df042792-9930-4db6-bb76-4b995185ab22

📥 Commits

Reviewing files that changed from the base of the PR and between efa8b03 and 22cf894.

📒 Files selected for processing (3)
  • projects/core/dsl/utils/__init__.py
  • projects/llm_d/docs/benchmark-isolation.md
  • projects/llm_d/toolbox/cleanup_test_resources/main.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • projects/llm_d/docs/benchmark-isolation.md

@albertoperdomo2

Copy link
Copy Markdown
Collaborator Author

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-full

@psap-forge-bot

Copy link
Copy Markdown

🔴 Execution of llm_d smoke 🔴

Execution Engine Configuration

forge:
  args:
  - smoke
  configOverrides: {}
  project: llm_d

Artifact Links

Test Logs

00 Pre-Cleanup 21 minutes, 27 seconds

Failure Review 001 Cleanup Test Resources

001__cleanup_test_resources

The test teardown failed because the wait_for_inference_service_deletion task exhausted 90 retries without removing the llminferenceservice/llm-d resource from the forge-llm-d namespace. This occurred because the cleanup script ignored a timeout error (exit code 1) from the oc delete command and proceeded to poll, causing the waiter to fail on a resource permanently stuck in a Terminating state.

04 Post-Cleanup

🔄 05 Export-Artifacts

Step Sync Failed

Step sync failed: Completion file did not appear within 600s
Waited for step: 04__post-cleanup
Expected file: /workspace/artifacts/04__post-cleanup/000__ci_metadata/test_duration.yaml

@psap-forge-bot

Copy link
Copy Markdown
🔴 Submission of llm_d smoke failed after 47 minutes, 22 seconds 🔴

Error: FournosJobFailureError: FOURNOS Job 'forge-llm-d-20260716-174300' failed: Tasks Completed: 3 (Failed: 2, Cancelled 0), Skipped: 3

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-full

Signed-off-by: Alberto Perdomo <aperdomo@redhat.com>
…erdomo2/forge into feat/benchmark-matrix-isolation
@albertoperdomo2

Copy link
Copy Markdown
Collaborator Author

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-full

@psap-forge-bot

Copy link
Copy Markdown
🔴 Submission of llm_d smoke failed after 1 hour, 6 minutes, 33 seconds 🔴

Error: RetryFailure: All 360 attempts failed for task wait_for_job_completion : Wait for FOURNOS job to complete (last result: False)

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-full

@kpouget

kpouget commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

/test fournos llm_d smoke
/cluster forge-smoke-testing
/pipeline forge-test-only

@psap-forge-bot

Copy link
Copy Markdown

🟢 Execution of llm_d smoke 🟢

Execution Engine Configuration

forge:
  args:
  - smoke
  configOverrides: {}
  project: llm_d

Artifact Links

Test Logs

00 Pre-Cleanup 17 seconds

01 Prepare 2 minutes, 30 seconds

02 Preflight 1 second

03 Test 11 minutes, 13 seconds

Test Description

This test validates the llm_d project by running a smoke check and short concurrent benchmark on a Qwen/Qwen3-0.6B deployment configured with the approximate-prefix-cache profile. It specifically verifies inference functionality and measures throughput/latency under light workloads (256 prompt tokens, 128 output tokens) to evaluate the approximate prefix-caching deployment strategy.

04 Post-Cleanup 17 seconds

🔄 05 Export-Artifacts

Post-processing Status

@psap-forge-bot

Copy link
Copy Markdown

Comment on lines +117 to +119
with runtime_config.activate_run_spec(run_spec):
with env.NextArtifactDir(run_spec.artifact_dirname):
exit_code = max(exit_code, test_toolbox_run())

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.

I think you can merge the two with ..., ....:
and can you add a config flag abort_on_failure or something like that?
most of the time we want to abort on failure, instead of continuing with a broken setup. But not always

@kpouget

kpouget commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

thanks @albertoperdomo2 ,
/lgtm
I will test this further in the next weeks with the ongoing P/D testing work

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jul 18, 2026
@openshift-ci openshift-ci Bot removed the lgtm Indicates that a PR is ready to be merged. label Jul 20, 2026
@kpouget

kpouget commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

/approve
/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jul 20, 2026
@openshift-ci

openshift-ci Bot commented Jul 20, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: kpouget

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 20, 2026
@openshift-merge-bot
openshift-merge-bot Bot merged commit 8df998c into openshift-psap:main Jul 20, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants