Skip to content

[llmd] Finish the KPI regression and version comparison - #163

Open
kpouget wants to merge 13 commits into
openshift-psap:mainfrom
kpouget:kpis
Open

[llmd] Finish the KPI regression and version comparison#163
kpouget wants to merge 13 commits into
openshift-psap:mainfrom
kpouget:kpis

Conversation

@kpouget

@kpouget kpouget commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added optional verbose parsing details to supported commands.
    • Added deployment profile reports with comparison charts, throughput sections, and HTML navigation.
    • KPI reports now include baseline values and improved analysis statuses.
    • Added a release regression visualization group.
  • Bug Fixes

    • Improved artifact discovery, validation, error reporting, and exit-code handling.
    • Corrected benchmark ordering by intended concurrency.
    • Removed the obsolete request-concurrency KPI.
    • Improved report path handling and visualization error reporting.
  • Documentation

    • Clarified KPI validation, missing-data handling, regression rules, and optional comparison settings.

@openshift-ci

openshift-ci Bot commented Aug 5, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign ashishkamra for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found 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

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds verbose parsing controls to Caliper commands and engines, refines KPI analysis and S3 export statuses, and adds GuideLLM deployment-profile reports with parser ordering and visualization-group updates.

Changes

Caliper parsing and analysis

Layer / File(s) Summary
Verbose parsing propagation and logging
projects/caliper/cli/commands.py, projects/caliper/engine/parse.py, projects/caliper/engine/visualize.py, projects/caliper/engine/ai_eval.py, projects/caliper/engine/kpi/generate.py
The CLI exposes --verbose-parsing and forwards it through parsing, visualization, AI evaluation, and KPI generation. Parse timing and plugin diagnostics are conditional on the option.
KPI analysis results and status propagation
projects/caliper/engine/kpi/analyze.py, projects/caliper/cli/kpi_analysis.py, projects/caliper/orchestration/caliper_invocation.py, projects/caliper/orchestration/postprocess.py, docs/caliper/plugin_kpis.md, docs/caliper/plugin_regression.md
KPI analysis validates configuration, records baseline values, preserves engine exit codes and status fields, removes test-directory metadata, and documents updated KPI and regression behavior.
S3 discovery and upload status
projects/caliper/cli/s3_export.py
Artifact paths are validated, discovery errors are tracked, upload plans use discovered file membership, and discovery or upload failures produce a unified failed status.

GuideLLM parsing and deployment reports

Layer / File(s) Summary
Parser ordering and diagnostics
projects/guidellm/postprocess/guidellm/parsing/kpis.py, projects/guidellm/postprocess/guidellm/parsing/parsers.py, projects/guidellm/tests/test_postprocess_parser.py
The concurrency KPI is removed. Parser diagnostics use a module logger, aggregated benchmarks sort by intended concurrency, and tests cover the resulting metric order.
Deployment profile report generation
projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py
Deployment profiles are grouped by comparison conditions, rendered into five plot types, written to sanitized paths, and assembled into an HTML report.
Report registration and visualization groups
projects/guidellm/postprocess/guidellm/plugin.py, projects/guidellm/postprocess/guidellm/visualize-groups.yaml, projects/llm_d/orchestration/config.yaml
The deployment-profile report is registered, included in visualization groups, and selected through the comprehensive group configuration.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant CaliperEngine
  participant ParseEngine
  participant PluginParser
  participant DeploymentProfileReport
  CLI->>CaliperEngine: pass verbose_parsing
  CaliperEngine->>ParseEngine: run parsing with verbosity
  ParseEngine->>PluginParser: parse records and control diagnostics
  PluginParser-->>ParseEngine: return parsed records
  ParseEngine-->>CaliperEngine: return records and timing output
  CaliperEngine->>DeploymentProfileReport: provide benchmark records
  DeploymentProfileReport->>DeploymentProfileReport: group records and generate plots
  DeploymentProfileReport-->>CLI: return HTML report path
Loading

Possibly related PRs

🚥 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 clearly summarizes the main changes: completing KPI regression analysis and version comparison reporting.
Docstring Coverage ✅ Passed Docstring coverage is 91.89% 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

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.

@kpouget
kpouget force-pushed the kpis branch 2 times, most recently from 84881c8 to 64eeb87 Compare August 5, 2026 15:34
@kpouget

kpouget commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

/test fournos llm_d janus cpt-xks
/var caliper.postprocess.filtering.include_labels: [version=3.5.0-ea.2]
/var caliper.postprocess.s3.export.enabled: false
/var caliper.replot.keep: true
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/8a13e32925dd44fda4103fe56cfc09d0?workspace=forge-llmd
/var caliper.postprocess.visualize.report_group: release_regression

@psap-forge-bot

psap-forge-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

🟢 Execution of llm_d janus cpt-xks 🟢

Execution Engine Configuration

forge:
  args:
  - janus
  - cpt-xks
  configOverrides:
    caliper.postprocess.filtering.include_labels:
    - version=3.5.0-ea.2
    caliper.postprocess.s3.export.enabled: false
    caliper.postprocess.visualize.report_group: release_regression
    caliper.replot.keep: true
    caliper.replot.url: https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/8a13e32925dd44fda4103fe56cfc09d0?workspace=forge-llmd
  project: llm_d

Artifact Links

Test Logs

00 Replot 3 minutes, 12 seconds

🔄 01 Export-Artifacts

Post-processing Status

@psap-forge-bot

psap-forge-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown
🟢 Submission of llm_d janus cpt-xks succeeded after 6 minutes, 24 seconds 🟢
/test fournos llm_d janus cpt-xks
/var caliper.postprocess.filtering.include_labels: [version=3.5.0-ea.2]
/var caliper.postprocess.s3.export.enabled: false
/var caliper.replot.keep: true
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/8a13e32925dd44fda4103fe56cfc09d0?workspace=forge-llmd

@kpouget

kpouget commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

/test fournos llm_d janus cpt-xks
/var caliper.postprocess.filtering.include_labels: [version=3.5.0-ea.2]
/var caliper.postprocess.s3.export.enabled: false
/var caliper.replot.keep: true
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/c2088e30278243c99576c8ed118ce0e6/artifacts?workspace=forge-llmd
/var caliper.postprocess.visualize.report_group: release_regression

@psap-forge-bot

psap-forge-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

🟢 Execution of llm_d janus cpt-xks 🟢

Execution Engine Configuration

forge:
  args:
  - janus
  - cpt-xks
  configOverrides:
    caliper.postprocess.filtering.include_labels:
    - version=3.5.0-ea.2
    caliper.postprocess.s3.export.enabled: false
    caliper.postprocess.visualize.report_group: release_regression
    caliper.replot.keep: true
    caliper.replot.url: https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/c2088e30278243c99576c8ed118ce0e6/artifacts?workspace=forge-llmd
  project: llm_d

Artifact Links

Test Logs

00 Replot 3 minutes, 21 seconds

🔄 01 Export-Artifacts

Post-processing Status

@psap-forge-bot

psap-forge-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown
🟢 Submission of llm_d janus cpt-xks succeeded after 5 minutes, 10 seconds 🟢
/test fournos llm_d janus cpt-xks
/var caliper.postprocess.filtering.include_labels: [version=3.5.0-ea.2]
/var caliper.postprocess.s3.export.enabled: false
/var caliper.replot.keep: true
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/c2088e30278243c99576c8ed118ce0e6/artifacts?workspace=forge-llmd

@kpouget

kpouget commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

/test fournos llm_d janus cpt-xks
/var caliper.postprocess.s3.export.enabled: false
/var caliper.replot.keep: true
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/c2088e30278243c99576c8ed118ce0e6/artifacts?workspace=forge-llmd

@psap-forge-bot

psap-forge-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

🟢 Execution of llm_d janus cpt-xks 🟢

Execution Engine Configuration

forge:
  args:
  - janus
  - cpt-xks
  configOverrides:
    caliper.postprocess.s3.export.enabled: false
    caliper.postprocess.visualize.report_group: release_regression
    caliper.replot.keep: true
    caliper.replot.url: https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/c2088e30278243c99576c8ed118ce0e6/artifacts?workspace=forge-llmd
  project: llm_d

Artifact Links

Test Logs

00 Replot 3 minutes, 12 seconds

🔄 01 Export-Artifacts

Post-processing Status

@psap-forge-bot

psap-forge-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown
🟢 Submission of llm_d janus cpt-xks succeeded after 4 minutes, 59 seconds 🟢
/test fournos llm_d janus cpt-xks
/var caliper.postprocess.s3.export.enabled: false
/var caliper.replot.keep: true
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/c2088e30278243c99576c8ed118ce0e6/artifacts?workspace=forge-llmd

@kpouget

kpouget commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

/test fournos llm_d janus cpt-xks
/var caliper.postprocess.s3.export.enabled: false
/var caliper.replot.keep: true
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd

@psap-forge-bot

psap-forge-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

🟢 Execution of llm_d janus cpt-xks 🟢

Execution Engine Configuration

forge:
  args:
  - janus
  - cpt-xks
  configOverrides:
    caliper.postprocess.s3.export.enabled: false
    caliper.postprocess.visualize.report_group: release_regression
    caliper.replot.keep: true
    caliper.replot.url: https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd
  project: llm_d

Artifact Links

Test Logs

00 Replot 19 minutes, 44 seconds

🔄 01 Export-Artifacts

Post-processing Status

@psap-forge-bot

psap-forge-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown
🟢 Submission of llm_d janus cpt-xks succeeded after 21 minutes, 46 seconds 🟢
/test fournos llm_d janus cpt-xks
/var caliper.postprocess.s3.export.enabled: false
/var caliper.replot.keep: true
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd

@openshift-psap openshift-psap deleted a comment from psap-forge-bot Bot Aug 5, 2026
@openshift-psap openshift-psap deleted a comment from openshift-ci Bot Aug 5, 2026
@kpouget
kpouget force-pushed the kpis branch 2 times, most recently from 6f31a1b to b6479ca Compare August 5, 2026 20:20

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

🧹 Nitpick comments (2)
projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py (2)

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

container_id is unused in this report.

This function emits only the throughput plot per group and no tabbed container. container_id is initialized at Line 1100 and incremented at Line 1163, but no generated markup references it. Remove both statements.

Also applies to: 1163-1163

🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py` at
line 1100, Remove the unused container_id initialization and increment from the
report-generating function, while leaving the throughput plot generation
unchanged.

671-679: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the per-record 🐛 DEBUG log lines, or lower them to logger.debug.

These two statements log one line for every record at info level. The information they carry is already summarized at Lines 686-694. In a run with many records the output becomes noisy.

♻️ Proposed change
-            # Debug: Print version values found
-            if "version" in labels:
-                logger.info(
-                    f"🐛 DEBUG: Found version='{labels['version']}' in record {record.test_base_path}"
-                )
-            else:
-                logger.info(
-                    f"🐛 DEBUG: No 'version' key found in record {record.test_base_path}, keys: {list(labels.keys())}"
-                )
+            if "version" in labels:
+                logger.debug(
+                    "Found version='%s' in record %s", labels["version"], record.test_base_path
+                )
+            else:
+                logger.debug(
+                    "No 'version' key in record %s, keys: %s",
+                    record.test_base_path,
+                    sorted(labels.keys()),
+                )
🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py`
around lines 671 - 679, Update the per-record logging block around the
version-label check to remove both `logger.info` calls or change them to
`logger.debug`, while preserving the existing summarized version reporting
elsewhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/caliper/plugin_kpis.md`:
- Around line 191-198: Define a module-level logger in the handler example
before the exception handling uses it, alongside the existing imports and setup.
Ensure both logger.debug and logger.warning in the KPI processing flow reference
this declared logger so the documented error handling executes without
NameError.
- Around line 191-198: Update the KPI exception handling around the KPI
invocation to catch only the dedicated missing-data exception when metrics are
absent, rather than every ValueError; preserve the existing debug-and-continue
behavior for that exception and let conversion ValueErrors, such as float
failures, reach the visible warning and re-raise path.

In `@projects/caliper/cli/s3_export.py`:
- Around line 765-773: The dry-run summary counts KPI JSON files by a fixed
filename instead of the discovered file lists. Update the summary logic near the
CSV/KPI classification and the referenced later count to use len(csv_files) and
len(kpi_json_files), while preserving the existing output-path handling for
filenames and paths.

In `@projects/caliper/engine/kpi/analyze.py`:
- Around line 134-145: Update the validation around max_relative_regression and
min_baseline_points to reject booleans explicitly. Require
max_relative_regression to be a finite numeric value greater than or equal to
zero, while preserving the existing validation error context; require
min_baseline_points to be an integer at least 1 but not a bool.
- Around line 824-855: Update the status handling in the wrapper around the
visible NO_DATA branch so engine results with status "warning" and success=True
are normalized to the same warning response instead of falling through to
failure. Preserve the existing NO_DATA behavior and ensure successful
no-baseline analyses retain status "warning" and success=True.

In `@projects/caliper/engine/visualize.py`:
- Around line 92-93: Update the show_parameter_matrix argument in run_visualize
to use verbose_parsing instead of always enabling it, so parameter-matrix output
is disabled when verbose_parsing=False and matches the AI evaluation export and
KPI generation paths.

In `@projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py`:
- Around line 1585-1587: Update both report-generator exception handlers in
projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py at lines
1585-1587 and 1187-1190 to remove {e} from logger.exception messages, then
enforce one failure policy for both generators. Ensure plugin.visualize in
projects/guidellm/postprocess/guidellm/plugin.py does not swallow
report-generation exceptions; let failures propagate to the CLI so the run fails
instead of silently producing missing output.
- Around line 738-748: Update the comparison-group construction around
comparison_groups to derive core_labels from the previously collected all_keys
set, not the leaked labels loop variable. Keep the existing filter in the record
loop, so grouping works for empty records and preserves all distinguishing keys
across heterogeneous records.
- Around line 1354-1362: Correct the five corrupted CSS property names in the
report HTML template: restore font-family, text-align, background-color,
list-style, and text-decoration within the relevant styling block. Use the
correctly formatted declarations in generate_deployment_profile_report as the
reference, leaving the surrounding CSS unchanged.
- Around line 1558-1561: Move the closing markup append and container_id
increment back inside the loadshape loop that opens each .loadshape-section and
.tabs-container. Ensure each loadshape emits its own closing div pair and
increments container_id before the next iteration so tab and container IDs
remain unique.

---

Nitpick comments:
In `@projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py`:
- Line 1100: Remove the unused container_id initialization and increment from
the report-generating function, while leaving the throughput plot generation
unchanged.
- Around line 671-679: Update the per-record logging block around the
version-label check to remove both `logger.info` calls or change them to
`logger.debug`, while preserving the existing summarized version reporting
elsewhere.
🪄 Autofix

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 Plus

Run ID: 727496bd-ee59-437d-94cb-b73c55b7e385

📥 Commits

Reviewing files that changed from the base of the PR and between de20451 and 6f31a1b.

📒 Files selected for processing (17)
  • docs/caliper/plugin_kpis.md
  • docs/caliper/plugin_regression.md
  • projects/caliper/cli/commands.py
  • projects/caliper/cli/s3_export.py
  • projects/caliper/engine/ai_eval.py
  • projects/caliper/engine/kpi/analyze.py
  • projects/caliper/engine/kpi/generate.py
  • projects/caliper/engine/parse.py
  • projects/caliper/engine/visualize.py
  • projects/cluster/toolbox/deploy_custom_catalog/main.py
  • projects/guidellm/postprocess/guidellm/parsing/kpis.py
  • projects/guidellm/postprocess/guidellm/parsing/parsers.py
  • projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py
  • projects/guidellm/postprocess/guidellm/plugin.py
  • projects/guidellm/postprocess/guidellm/visualize-groups.yaml
  • projects/guidellm/tests/test_postprocess_parser.py
  • projects/llm_d/orchestration/config.yaml
💤 Files with no reviewable changes (1)
  • projects/guidellm/postprocess/guidellm/parsing/kpis.py

Comment thread docs/caliper/plugin_kpis.md
Comment thread projects/caliper/cli/s3_export.py
Comment thread projects/caliper/engine/kpi/analyze.py
Comment thread projects/caliper/engine/kpi/analyze.py Outdated
Comment thread projects/caliper/engine/visualize.py
Comment thread projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py Outdated
Comment thread projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py Outdated
Comment thread projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py Outdated
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@kpouget
kpouget force-pushed the kpis branch 3 times, most recently from 9eb2c9c to e32d52f Compare August 6, 2026 11:54

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

♻️ Duplicate comments (2)
projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py (2)

1556-1559: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

The closing markup and the container_id increment are still outside the loadshape loop.

The earlier review marked this fixed, but Lines 1556-1559 are still at 8-space indentation while the body of the for loadshape, plots in all_plots_data: loop at Line 1490 is at 12 spaces. Two defects follow when more than one loadshape exists:

  1. Only one </div></div> pair is emitted, so every later section nests inside the previous one.
  2. container_id stays 0 for all loadshapes, so all tab buttons and tab contents share tabs-container-0 and tab-0-<idx>. document.getElementById(tabId) at Line 1461 resolves to the first match, so a tab click in the second loadshape switches the tab in the first.
🐛 Proposed fix
-        html_content += """
+            html_content += """
         </div>
     </div>"""
-        container_id += 1
+            container_id += 1
🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py`
around lines 1556 - 1559, Move the closing markup append and container_id
increment into the for loadshape, plots in all_plots_data loop, matching the
loop body’s indentation. Ensure each loadshape emits its own closing
</div></div> pair and increments container_id before the next iteration so tab
container and tab content IDs remain unique.

1352-1360: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Five CSS property names are still corrupted.

The earlier review marked this fixed, but the current code still contains font - family (Line 1352), text - align (Line 1353), background - color (Line 1356), list - style (Line 1357), and text - decoration (Line 1360). Browsers discard these declarations. The equivalent block in generate_deployment_profile_report at Lines 963-971 has the correct names.

🐛 Proposed fix
-        body {{font - family: Arial, sans-serif; margin: 40px; }}
-        .header {{text - align: center; margin-bottom: 30px; }}
+        body {{ font-family: Arial, sans-serif; margin: 40px; }}
+        .header {{ text-align: center; margin-bottom: 30px; }}
         .loadshape-section {{margin: 40px 0; }}
         .loadshape-title {{color: `#333`; font-size: 24px; margin-bottom: 20px; border-bottom: 2px solid `#007acc`; padding-bottom: 10px; }}
-        .navigation {{background - color: `#f5f5f5`; padding: 15px; border-radius: 4px; margin-bottom: 20px; }}
-        .navigation ul {{list - style: none; padding: 0; margin: 0; }}
+        .navigation {{ background-color: `#f5f5f5`; padding: 15px; border-radius: 4px; margin-bottom: 20px; }}
+        .navigation ul {{ list-style: none; padding: 0; margin: 0; }}
         .navigation li {{display: inline-block; margin-right: 20px; }}
         .navigation a {{color: `#007acc`; text-decoration: none; font-weight: bold; }}
-        .navigation a:hover {{text - decoration: underline; }}
+        .navigation a:hover {{ text-decoration: underline; }}
🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py`
around lines 1352 - 1360, Correct the five malformed CSS property names in the
HTML template block: update font - family, text - align, background - color,
list - style, and text - decoration to valid CSS property syntax. Use the
corresponding declarations in generate_deployment_profile_report as the
reference, while preserving the existing values and formatting.
🧹 Nitpick comments (1)
projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py (1)

840-856: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move sanitize_for_path to module scope.

The function is defined inside the else branch of the per-group loop, so Python rebuilds it on every group iteration. It has no closure over loop state. A module-level helper also lets generate_comprehensive_performance_report reuse it at Line 1266, where the raw loadshape value is used directly as a directory name.

♻️ Proposed refactor

Define once near the top of the module:

_PATH_UNSAFE_CHARS = '/\\:*?|<>"'


def sanitize_for_path(text: str) -> str:
    """Replace filesystem-unsafe characters in label values."""
    return "".join("_" if c in _PATH_UNSAFE_CHARS else c for c in str(text))

Then drop the nested definition:

                 group_desc = ", ".join(f"{k}={v}" for k, v in group_key)
-
-                # Sanitize values for filesystem safety (replace problematic characters)
-                def sanitize_for_path(text: str) -> str:
-                    """Replace filesystem-unsafe characters in label values."""
-                    return (
-                        str(text)
-                        .replace("/", "_")
-                        .replace("\\", "_")
-                        .replace(":", "_")
-                        .replace("*", "_")
-                        .replace("?", "_")
-                        .replace("|", "_")
-                        .replace("<", "_")
-                        .replace(">", "_")
-                        .replace('"', "_")
-                    )
-
                 group_name = "__".join(f"{k}_{sanitize_for_path(v)}" for k, v in group_key)
🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py`
around lines 840 - 856, Move sanitize_for_path from the per-group loop to module
scope, defining the shared _PATH_UNSAFE_CHARS constant there and preserving its
filesystem-safe replacement behavior. Remove the nested definition and use
sanitize_for_path when generate_comprehensive_performance_report builds the
loadshape directory name at the existing raw-value path.
🤖 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/caliper/cli/s3_export.py`:
- Around line 751-755: Update the include_ai_data branch in run_s3_export to
handle ai_data_dir being None or missing before calling list_ai_data_files,
skipping AI-data discovery and upload in that case; alternatively, update
list_ai_data_files to accept an optional path and return an empty list for None
or nonexistent directories. Preserve error_detected behavior for genuinely empty
discovered data.
- Line 863: Remove the `error_detected = False` reset before final status
evaluation so missing-input errors from the discovery branches are preserved.
Update both the no-file and dry-run return paths to derive their status from
`error_detected`, returning `failed` when it is true while retaining their
existing `skipped` or `success` statuses otherwise.
- Around line 102-117: Update the configured artifact discovery branches for
CSV, KPI JSON, and analysis paths to use is_file() instead of exists() before
appending paths to upload sets, while preserving their existing missing-path
logging. Also update explicit path validation in
run_s3_export_with_explicit_paths to reject directories using the same file
check.
- Around line 774-790: Initialize csv_files, kpi_json_files, and ai_data_files
to empty collections before the conditional discovery logic, while ensuring
analysis_files is always initialized and discovered as needed. Update the
file-classification flow around the membership checks so exports with only
analysis, KPIs, or AI data never reference an unbound discovery list.

In `@projects/caliper/engine/kpi/analyze.py`:
- Around line 400-407: Update the CLI consumer in commands.py to apply
status_dict_to_exit_code after writing the analysis status file, so the
no-baseline result from the analysis branch preserves warning exit code 2
despite success=True. Keep successful and failure exit behavior unchanged.
- Around line 93-97: Update the AnalysisConfig construction in the
raw-dictionary branch to reject unsupported keys instead of filtering them
through AnalysisConfig.__dataclass_fields__. Pass raw directly to AnalysisConfig
or explicitly validate and raise for unknown keys, while preserving normal
construction for recognized settings.
- Around line 117-145: Update _validate_analysis_config so validation errors
retain the plugin module, field name, expected type, actual type, and item index
where applicable, but omit all interpolated raw configuration values such as
field contents, invalid items, and numeric values. Ensure run_kpi_analysis
receives only sanitized error messages.

In `@projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py`:
- Around line 936-939: Update the group_labels_dict construction in the plotting
flow to also recognize the all_data fallback key used around the existing
detection near line 833, avoiding dict(group_key) for that flat string tuple.
Preserve the current empty-label behavior for unified_comparison and continue
converting normal (key, value) pair group keys with dict().
- Around line 1091-1093: Escape all group-description text before inserting it
into generated HTML and derive anchors through a shared id-safe helper. Add the
necessary module imports and _html_anchor_id(text) helper, use it wherever
group_id is generated, and apply html.escape to displayed group_desc values in
the navigation, list items, and heading while keeping href fragments and id
attributes based on the same sanitized group_id.
- Around line 764-771: Coerce every distinguishing label value to str when
extracting it for comparison tuples in the performance analysis function. Update
the initial comparison-key loop and the two later tuple-rebuilding sites to
apply the same conversion, preserving the existing "unknown" default so all
values passed to the subsequent join operations are strings.

In `@projects/rhoai/library/pr_args.py`:
- Around line 38-47: Update the directive parsing logic around `parts`, `image`,
and `channel` to reject inputs containing more than two tokens. Preserve the
existing validation and default `beta` channel behavior for one-token inputs,
while accepting only the documented `IMAGE [CHANNEL]` format.

---

Duplicate comments:
In `@projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py`:
- Around line 1556-1559: Move the closing markup append and container_id
increment into the for loadshape, plots in all_plots_data loop, matching the
loop body’s indentation. Ensure each loadshape emits its own closing
</div></div> pair and increments container_id before the next iteration so tab
container and tab content IDs remain unique.
- Around line 1352-1360: Correct the five malformed CSS property names in the
HTML template block: update font - family, text - align, background - color,
list - style, and text - decoration to valid CSS property syntax. Use the
corresponding declarations in generate_deployment_profile_report as the
reference, while preserving the existing values and formatting.

---

Nitpick comments:
In `@projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py`:
- Around line 840-856: Move sanitize_for_path from the per-group loop to module
scope, defining the shared _PATH_UNSAFE_CHARS constant there and preserving its
filesystem-safe replacement behavior. Remove the nested definition and use
sanitize_for_path when generate_comprehensive_performance_report builds the
loadshape directory name at the existing raw-value path.
🪄 Autofix

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 Plus

Run ID: 466c2e1a-4c8d-4291-976b-54fc95771e8e

📥 Commits

Reviewing files that changed from the base of the PR and between 01c4aa5 and 9eb2c9c.

📒 Files selected for processing (19)
  • docs/caliper/plugin_kpis.md
  • docs/caliper/plugin_regression.md
  • projects/caliper/cli/commands.py
  • projects/caliper/cli/s3_export.py
  • projects/caliper/engine/ai_eval.py
  • projects/caliper/engine/kpi/analyze.py
  • projects/caliper/engine/kpi/generate.py
  • projects/caliper/engine/parse.py
  • projects/caliper/engine/visualize.py
  • projects/cluster/toolbox/deploy_custom_catalog/main.py
  • projects/cluster/toolbox/deploy_custom_catalog/templates/catalogsource.yaml.j2
  • projects/guidellm/postprocess/guidellm/parsing/kpis.py
  • projects/guidellm/postprocess/guidellm/parsing/parsers.py
  • projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py
  • projects/guidellm/postprocess/guidellm/plugin.py
  • projects/guidellm/postprocess/guidellm/visualize-groups.yaml
  • projects/guidellm/tests/test_postprocess_parser.py
  • projects/llm_d/orchestration/config.yaml
  • projects/rhoai/library/pr_args.py
💤 Files with no reviewable changes (1)
  • projects/guidellm/postprocess/guidellm/parsing/kpis.py
🚧 Files skipped from review as they are similar to previous changes (13)
  • docs/caliper/plugin_regression.md
  • projects/caliper/engine/visualize.py
  • projects/caliper/engine/ai_eval.py
  • projects/cluster/toolbox/deploy_custom_catalog/main.py
  • projects/guidellm/postprocess/guidellm/visualize-groups.yaml
  • projects/llm_d/orchestration/config.yaml
  • projects/guidellm/postprocess/guidellm/plugin.py
  • projects/caliper/engine/kpi/generate.py
  • projects/guidellm/postprocess/guidellm/parsing/parsers.py
  • projects/guidellm/tests/test_postprocess_parser.py
  • docs/caliper/plugin_kpis.md
  • projects/caliper/cli/commands.py
  • projects/caliper/engine/parse.py

Comment thread projects/caliper/cli/s3_export.py
Comment thread projects/caliper/cli/s3_export.py
Comment thread projects/caliper/cli/s3_export.py
Comment thread projects/caliper/cli/s3_export.py Outdated
Comment thread projects/caliper/engine/kpi/analyze.py
Comment thread projects/caliper/engine/kpi/analyze.py
Comment thread projects/rhoai/library/pr_args.py
@kpouget

kpouget commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

/test fournos llm_d janus cpt-xks
/var caliper.postprocess.s3.export.enabled: false
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd

@psap-forge-bot

psap-forge-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

🟢 Execution of llm_d janus cpt-xks 🟢

Execution Engine Configuration

forge:
  args:
  - janus
  - cpt-xks
  configOverrides:
    caliper.postprocess.s3.export.enabled: false
    caliper.postprocess.visualize.report_group: release_regression
    caliper.replot.url: https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd
  project: llm_d

Artifact Links

Test Logs

00 Replot 18 minutes, 13 seconds

🔄 01 Export-Artifacts

Post-processing Status

@psap-forge-bot

psap-forge-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown
🟢 Submission of llm_d janus cpt-xks succeeded after 22 minutes, 20 seconds 🟢
/test fournos llm_d janus cpt-xks
/var caliper.postprocess.s3.export.enabled: false
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd

@kpouget

kpouget commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

/test fournos llm_d janus cpt-xks
/var caliper.postprocess.s3.export.enabled: false
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

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

♻️ Duplicate comments (6)
projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py (6)

25-34: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

sanitize_for_path still allows ., .., and empty results.

The character list omits path segments. A label value of ".." returns unchanged. Line 866 and Line 1261 then build a directory outside report_dir, and mkdir(exist_ok=True) succeeds. An empty or whitespace-only value collapses to the parent directory.

🛡️ Proposed hardening
 def sanitize_for_path(text: str) -> str:
     """Replace filesystem-unsafe characters in label values."""
     result = str(text)
     for char in _PATH_UNSAFE_CHARS:
         result = result.replace(char, "_")
-    return result
+    result = result.strip()
+    if result in ("", ".", ".."):
+        return "_"
+    return result
🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py`
around lines 25 - 34, Harden sanitize_for_path so it never returns "." or "..",
nor an empty or whitespace-only path component after sanitization. Update the
function to normalize whitespace and provide a safe non-empty fallback for
collapsed values, while preserving replacement of the existing filesystem-unsafe
characters used by its callers.

931-934: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

dict(group_key) still raises ValueError for the all_data fallback key.

Line 797 sets the fallback group key to ("all_data", warning_message), a flat 2-tuple of strings. Line 933 excludes only ("unified_comparison",), so the fallback key reaches dict(...). dict() requires each element to be a 2-length sequence, so it raises ValueError: dictionary update sequence element #0 has length 8; 2 is required. This path runs on single-version runs. Line 1183 re-raises and no report is produced.

Reuse the group_name value already computed at Line 847.

🐛 Proposed fix
             if group_plots:
                 # Include group_key for displaying identical labels
-                group_labels_dict = dict(group_key) if group_key != ("unified_comparison",) else {}
+                if group_name in ("all_data", "unified_comparison"):
+                    group_labels_dict = {}
+                else:
+                    group_labels_dict = dict(group_key)
                 all_plots_data.append((group_desc, group_plots, group_labels_dict))
🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py`
around lines 931 - 934, Update the group_labels_dict construction in the
plotting flow to reuse the already computed group_name value from the
surrounding logic, rather than calling dict(group_key) for the flat all_data
fallback key. Preserve the existing empty-label behavior for the
unified_comparison key and ensure single-version all_data runs no longer raise
ValueError.

1261-1261: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Only the directory is sanitized, so the image links still break.

Line 1261 sanitizes the directory name. Line 1295 still passes the raw loadshape as the base filename, and Lines 1312-1313 still build the relative link paths from the raw value. If the value contains any character in _PATH_UNSAFE_CHARS, the link path does not match the directory on disk, and the report shows broken images. save_figure also writes an unsafe filename inside the sanitized directory.

Compute the sanitized name once. Use it for the directory, the filename, and the link paths.

🐛 Proposed fix
-            loadshape_dir = report_dir / sanitize_for_path(loadshape)
+            safe_loadshape = sanitize_for_path(loadshape)
+            loadshape_dir = report_dir / safe_loadshape
             loadshape_dir.mkdir(exist_ok=True)

At Line 1295:

-                        filename = f"{loadshape}_{plot_name.lower().replace(' ', '_')}"
+                        filename = f"{safe_loadshape}_{plot_name.lower().replace(' ', '_')}"

At Lines 1312-1313:

-                                    f"{report_dir_name}/{loadshape}/{Path(png_path).name}",  # PNG path
-                                    f"{report_dir_name}/{loadshape}/{Path(html_path).name}",  # HTML path
+                                    f"{report_dir_name}/{safe_loadshape}/{Path(png_path).name}",  # PNG path
+                                    f"{report_dir_name}/{safe_loadshape}/{Path(html_path).name}",  # HTML path
🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py` at
line 1261, Compute the sanitized loadshape name once in the relevant plotting
flow, then reuse it for the loadshape_dir path, save_figure filename, and both
relative image link paths. Replace all remaining raw loadshape values in these
directory, filename, and link constructions so generated files and report links
consistently match.

779-783: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Coerce label values to str when you build comparison tuples.

UnifiedResultRecord.distinguishing_labels is typed dict[str, Any], so a value can be an int, float, bool, or None. Line 782 stores the raw value. Line 817 and Line 876 then call ":".join(cv), and str.join raises TypeError on a non-string member. sorted(comp_values) also compares mixed types. Line 1183 re-raises, so one non-string label value aborts the report.

Coerce at each extraction point.

🐛 Proposed fix
                 comp_values = []
                 for key in comparison_keys:
                     value = record.distinguishing_labels.get(key, "unknown")
-                    comp_values.append(value)
+                    comp_values.append(str(value))
                 comparison_values.add(tuple(comp_values))

Apply the same coercion at Lines 814-815 and Lines 872-874.

🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py`
around lines 779 - 783, Coerce each value extracted from
UnifiedResultRecord.distinguishing_labels to str when building comparison
tuples, including the extraction paths near the current comparison_values
construction and the corresponding paths near lines 814-815 and 872-874.
Preserve the existing "unknown" fallback while ensuring tuple sorting and
subsequent ":".join operations only receive strings.

1086-1088: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Escape label-derived text before you interpolate it into HTML.

Line 1087 derives group_id from group_desc, which is built from label values. The replacement covers only " ", "=", and ",". A value that contains ", <, >, &, or # breaks the href fragment at Line 1088, the id attribute at Line 1122, the heading at Line 1123, and the list items at Line 1113. The href and the id can also diverge.

Restrict group_id to an id-safe character set, and escape displayed text.

🐛 Proposed fix

Add module-level imports and a helper:

import html
import re


def _html_anchor_id(text: str) -> str:
    return re.sub(r"[^A-Za-z0-9_-]", "_", text)

Then:

         for group_desc, _, _ in all_plots_data:
-            group_id = group_desc.replace(" ", "_").replace("=", "_").replace(",", "_")
-            html_content += f'\n            <li><a href="#{group_id}">{group_desc}</a></li>'
+            group_id = _html_anchor_id(group_desc)
+            html_content += (
+                f'\n            <li><a href="#{group_id}">{html.escape(group_desc)}</a></li>'
+            )

Apply _html_anchor_id at Line 1096, and html.escape at Lines 1113 and 1123.

🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py`
around lines 1086 - 1088, Update the plotting HTML generation around the group
ID construction to derive IDs through a shared id-safe helper using only
letters, digits, underscores, and hyphens, and use that same result for both
links and anchors. Escape label-derived text when interpolating list items and
headings, including the rendering near the group navigation and heading
generation, by adding the required html and re imports and applying html.escape
to displayed labels.

805-808: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

The ("unified_comparison",) sentinel still reaches (k, v) unpacking.

Line 766 stores the 1-tuple ("unified_comparison",) as a group key. Line 808 iterates that key with for k, v in group_key, so Python unpacks the string "unified_comparison" and raises ValueError: too many values to unpack (expected 2). Line 850 and Line 851 repeat the same unpacking. This path runs whenever no record carries a non-comparison label key. Line 1183 re-raises, so no report is produced.

Add a branch for the sentinel at both sites. A single helper that maps a group_key to (group_desc, group_name, warning_msg) removes the duplicated branching and keeps Line 933 consistent.

🐛 Proposed fix
+def _describe_group_key(group_key: tuple) -> tuple[str, str, str | None]:
+    """Map a group key (including sentinel shapes) to description, name, and warning."""
+    if group_key == ("unified_comparison",):
+        return "All Test Configurations", "unified_comparison", None
+    if len(group_key) == 2 and group_key[0] == "all_data":
+        return "All Available Data", "all_data", group_key[1]
+    group_desc = ", ".join(f"{k}={v}" for k, v in group_key)
+    group_name = "__".join(f"{k}_{sanitize_for_path(v)}" for k, v in group_key)
+    return group_desc, group_name, None

Then at Lines 805-808:

-            # Handle special case for warning message
-            if len(group_key) == 2 and group_key[0] == "all_data":
-                group_desc = "All Available Data"
-            else:
-                group_desc = ", ".join(f"{k}={v}" for k, v in group_key)
+            group_desc, _, _ = _describe_group_key(group_key)

And at Lines 845-852:

-            if len(group_key) == 2 and group_key[0] == "all_data":
-                group_desc = "All Available Data"
-                group_name = "all_data"
-                warning_msg = group_key[1]  # Extract warning message
-            else:
-                group_desc = ", ".join(f"{k}={v}" for k, v in group_key)
-                group_name = "__".join(f"{k}_{sanitize_for_path(v)}" for k, v in group_key)
-                warning_msg = None
+            group_desc, group_name, warning_msg = _describe_group_key(group_key)
🤖 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/guidellm/postprocess/guidellm/plotting/performance_analysis.py`
around lines 805 - 808, Update the group-key formatting logic around the
existing description/name/warning branches to explicitly handle the one-element
("unified_comparison",) sentinel before any key/value unpacking. Extract the
mapping into a shared helper returning group_desc, group_name, and warning_msg,
then use it at both affected sites and the corresponding logic near the report
generation path so all branches remain consistent.
🤖 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.

Duplicate comments:
In `@projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py`:
- Around line 25-34: Harden sanitize_for_path so it never returns "." or "..",
nor an empty or whitespace-only path component after sanitization. Update the
function to normalize whitespace and provide a safe non-empty fallback for
collapsed values, while preserving replacement of the existing filesystem-unsafe
characters used by its callers.
- Around line 931-934: Update the group_labels_dict construction in the plotting
flow to reuse the already computed group_name value from the surrounding logic,
rather than calling dict(group_key) for the flat all_data fallback key. Preserve
the existing empty-label behavior for the unified_comparison key and ensure
single-version all_data runs no longer raise ValueError.
- Line 1261: Compute the sanitized loadshape name once in the relevant plotting
flow, then reuse it for the loadshape_dir path, save_figure filename, and both
relative image link paths. Replace all remaining raw loadshape values in these
directory, filename, and link constructions so generated files and report links
consistently match.
- Around line 779-783: Coerce each value extracted from
UnifiedResultRecord.distinguishing_labels to str when building comparison
tuples, including the extraction paths near the current comparison_values
construction and the corresponding paths near lines 814-815 and 872-874.
Preserve the existing "unknown" fallback while ensuring tuple sorting and
subsequent ":".join operations only receive strings.
- Around line 1086-1088: Update the plotting HTML generation around the group ID
construction to derive IDs through a shared id-safe helper using only letters,
digits, underscores, and hyphens, and use that same result for both links and
anchors. Escape label-derived text when interpolating list items and headings,
including the rendering near the group navigation and heading generation, by
adding the required html and re imports and applying html.escape to displayed
labels.
- Around line 805-808: Update the group-key formatting logic around the existing
description/name/warning branches to explicitly handle the one-element
("unified_comparison",) sentinel before any key/value unpacking. Extract the
mapping into a shared helper returning group_desc, group_name, and warning_msg,
then use it at both affected sites and the corresponding logic near the report
generation path so all branches remain consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 84e68f09-7e66-48b3-9a1c-9d98cf6a5bcc

📥 Commits

Reviewing files that changed from the base of the PR and between 9bdfb87 and 878f8dd.

📒 Files selected for processing (19)
  • docs/caliper/plugin_kpis.md
  • docs/caliper/plugin_regression.md
  • projects/caliper/cli/commands.py
  • projects/caliper/cli/kpi_analysis.py
  • projects/caliper/cli/s3_export.py
  • projects/caliper/engine/ai_eval.py
  • projects/caliper/engine/kpi/analyze.py
  • projects/caliper/engine/kpi/generate.py
  • projects/caliper/engine/parse.py
  • projects/caliper/engine/visualize.py
  • projects/caliper/orchestration/caliper_invocation.py
  • projects/caliper/orchestration/postprocess.py
  • projects/guidellm/postprocess/guidellm/parsing/kpis.py
  • projects/guidellm/postprocess/guidellm/parsing/parsers.py
  • projects/guidellm/postprocess/guidellm/plotting/performance_analysis.py
  • projects/guidellm/postprocess/guidellm/plugin.py
  • projects/guidellm/postprocess/guidellm/visualize-groups.yaml
  • projects/guidellm/tests/test_postprocess_parser.py
  • projects/llm_d/orchestration/config.yaml
💤 Files with no reviewable changes (2)
  • projects/guidellm/postprocess/guidellm/parsing/kpis.py
  • projects/caliper/orchestration/postprocess.py
🚧 Files skipped from review as they are similar to previous changes (16)
  • projects/llm_d/orchestration/config.yaml
  • projects/caliper/engine/visualize.py
  • projects/caliper/orchestration/caliper_invocation.py
  • projects/guidellm/postprocess/guidellm/plugin.py
  • projects/guidellm/postprocess/guidellm/visualize-groups.yaml
  • projects/guidellm/tests/test_postprocess_parser.py
  • projects/caliper/cli/kpi_analysis.py
  • projects/caliper/engine/kpi/generate.py
  • projects/caliper/engine/parse.py
  • projects/caliper/engine/ai_eval.py
  • docs/caliper/plugin_kpis.md
  • projects/guidellm/postprocess/guidellm/parsing/parsers.py
  • projects/caliper/engine/kpi/analyze.py
  • docs/caliper/plugin_regression.md
  • projects/caliper/cli/s3_export.py
  • projects/caliper/cli/commands.py

@kpouget

kpouget commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

/test fournos llm_d janus cpt-xks
/var caliper.postprocess.s3.export.enabled: false
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd

@psap-forge-bot

psap-forge-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

🛑 Execution of llm_d janus cpt-xks 🛑
🛑 JOB ABORTED - spec.shutdown=Stop

Execution Engine Configuration

forge:
  args:
  - janus
  - cpt-xks
  configOverrides:
    caliper.postprocess.s3.export.enabled: false
    caliper.postprocess.visualize.report_group: release_regression
    caliper.replot.url: https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd
  project: llm_d

Artifact Links

Test Logs

00 Replot

🔄 01 Export-Artifacts

@psap-forge-bot

psap-forge-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

🔴 Execution of llm_d janus cpt-xks 🔴

Execution Engine Configuration

forge:
  args:
  - janus
  - cpt-xks
  configOverrides:
    caliper.postprocess.s3.export.enabled: false
    caliper.postprocess.visualize.report_group: release_regression
    caliper.replot.url: https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd
  project: llm_d

Artifact Links

Test Logs

00 Replot 22 minutes, 18 seconds

🔄 01 Export-Artifacts

Post-processing Status

@psap-forge-bot

psap-forge-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown
🔴 Submission of llm_d janus cpt-xks failed after 24 minutes, 12 seconds 🔴

Error: FournosJobFailureError: FOURNOS Job 'forge-llm-d-20260806-165322' failed: Tasks Completed: 2 (Failed: 1, Cancelled 0), Skipped: 0

/test fournos llm_d janus cpt-xks
/var caliper.postprocess.s3.export.enabled: false
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd

@openshift-ci

openshift-ci Bot commented Aug 6, 2026

Copy link
Copy Markdown

@kpouget: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/fournos 3df90ab link true /test fournos

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@kpouget

kpouget commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

/test fournos llm_d janus cpt-xks
/var caliper.postprocess.s3.export.enabled: false
/var caliper.postprocess.visualize.report_group: release_regression
/pipeline forge-replot
/clusterless
/replot.url https://mlflow.apps.psap-automation.ibm.rhperfscale.org/#/experiments/322/runs/661cb41498d84f1bb360767d99adfc75?workspace=forge-llmd

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