CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
162 lines
5.6 KiB
Python
162 lines
5.6 KiB
Python
"""Run the current agentic analysis surface on a fixture and compare the
|
|
observed result to the fixture's expected contract.
|
|
|
|
The pre-agentic ``perfxpert.ai_analysis`` module is gone. The parity gate now
|
|
validates the current analysis + recommendation pipeline against the canonical
|
|
fixture expectations that guard the staged refactor.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any, Optional
|
|
|
|
from perfxpert.agents import schemas
|
|
from perfxpert.agents.runtime import build_session
|
|
from perfxpert.analysis.payload import build_analysis_payload
|
|
|
|
from .fixtures_inventory import ParityFixture
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ObservedResult:
|
|
primary_bottleneck: Optional[str]
|
|
primary_rec_type: Optional[str]
|
|
primary_rec_technique: Optional[str]
|
|
raw_analysis: object
|
|
raw_recommendation: object
|
|
duration_s: float
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ParityResult:
|
|
fixture_id: str
|
|
expected_bottleneck: Optional[str]
|
|
expected_rec_type: Optional[str]
|
|
expected_rec_technique: Optional[str]
|
|
observed: ObservedResult
|
|
|
|
def agree_bottleneck(self) -> bool:
|
|
return self.observed.primary_bottleneck == self.expected_bottleneck
|
|
|
|
def agree_rec_type(self) -> bool:
|
|
return self.observed.primary_rec_type == self.expected_rec_type
|
|
|
|
def agree_rec_technique(self) -> bool:
|
|
return self.observed.primary_rec_technique == self.expected_rec_technique
|
|
|
|
def agreements(self) -> dict[str, bool]:
|
|
return {
|
|
"bottleneck": self.agree_bottleneck(),
|
|
"rec_type": self.agree_rec_type(),
|
|
"rec_technique": self.agree_rec_technique(),
|
|
}
|
|
|
|
|
|
_SOURCE_CATEGORY_TO_BOTTLENECK = {
|
|
"Memory Transfer": "memory_transfer",
|
|
"Synchronization": "latency",
|
|
"No Streams": "latency",
|
|
}
|
|
_SOURCE_CATEGORY_TO_REC_TYPE = {
|
|
"Memory Transfer": "memory",
|
|
"Synchronization": "latency",
|
|
"No Streams": "latency",
|
|
}
|
|
_SOURCE_CATEGORY_TO_TECHNIQUE = {
|
|
"Memory Transfer": "hip_stream_overlap",
|
|
"Synchronization": "device_sync_removal",
|
|
"No Streams": "hip_stream_overlap",
|
|
}
|
|
|
|
|
|
class ParityRunner:
|
|
"""Run the current analysis surface on a fixture and compare to expectations."""
|
|
|
|
def run_fixture(self, fx: ParityFixture) -> ParityResult:
|
|
import time
|
|
|
|
start = time.time()
|
|
if fx.source_dir:
|
|
payload = build_analysis_payload(None, source_dir=str(fx.source_dir))
|
|
observed = ObservedResult(
|
|
primary_bottleneck=_extract_source_bottleneck(payload),
|
|
primary_rec_type=_extract_source_rec_type(payload),
|
|
primary_rec_technique=_extract_source_rec_technique(payload),
|
|
raw_analysis=payload,
|
|
raw_recommendation=None,
|
|
duration_s=time.time() - start,
|
|
)
|
|
else:
|
|
session = build_session(airgap=True)
|
|
analysis_output = session.run_analysis(
|
|
schemas.AnalysisInput(database_path=str(fx.db_path))
|
|
)
|
|
recommendation_output = session.run_recommendation(
|
|
schemas.RecommendationInput(findings=analysis_output, gfx_id=fx.gfx_id)
|
|
)
|
|
observed = ObservedResult(
|
|
primary_bottleneck=analysis_output.primary_bottleneck,
|
|
primary_rec_type=_extract_db_rec_type(recommendation_output),
|
|
primary_rec_technique=_extract_db_rec_technique(recommendation_output),
|
|
raw_analysis=analysis_output,
|
|
raw_recommendation=recommendation_output,
|
|
duration_s=time.time() - start,
|
|
)
|
|
|
|
return ParityResult(
|
|
fixture_id=fx.id,
|
|
expected_bottleneck=fx.expected_bottleneck,
|
|
expected_rec_type=fx.expected_rec_type,
|
|
expected_rec_technique=fx.expected_rec_technique,
|
|
observed=observed,
|
|
)
|
|
|
|
|
|
def _primary_source_recommendation(payload: dict[str, Any]) -> Optional[dict[str, Any]]:
|
|
tier0 = payload.get("tier0_findings") or {}
|
|
recs = tier0.get("code_patterns") or tier0.get("recommendations") or []
|
|
if not isinstance(recs, list) or not recs:
|
|
return None
|
|
first = recs[0]
|
|
return first if isinstance(first, dict) else None
|
|
|
|
|
|
def _extract_source_bottleneck(payload: dict[str, Any]) -> Optional[str]:
|
|
source_rec = _primary_source_recommendation(payload)
|
|
if source_rec is None:
|
|
return None
|
|
return _SOURCE_CATEGORY_TO_BOTTLENECK.get(str(source_rec.get("category")))
|
|
|
|
|
|
def _extract_source_rec_type(payload: dict[str, Any]) -> Optional[str]:
|
|
source_rec = _primary_source_recommendation(payload)
|
|
if source_rec is None:
|
|
return None
|
|
return _SOURCE_CATEGORY_TO_REC_TYPE.get(str(source_rec.get("category")))
|
|
|
|
|
|
def _extract_source_rec_technique(payload: dict[str, Any]) -> Optional[str]:
|
|
source_rec = _primary_source_recommendation(payload)
|
|
if source_rec is None:
|
|
return None
|
|
return _SOURCE_CATEGORY_TO_TECHNIQUE.get(str(source_rec.get("category")))
|
|
|
|
|
|
def _extract_db_rec_type(result: schemas.RecommendationOutput) -> Optional[str]:
|
|
recs = list(result.recommendations or [])
|
|
if recs:
|
|
category = str(recs[0].get("category", "")).strip().lower()
|
|
if category in {"compute", "memory", "latency"}:
|
|
return category
|
|
if result.specialist_used in {"compute", "memory", "latency"}:
|
|
return result.specialist_used
|
|
return None
|
|
|
|
|
|
def _extract_db_rec_technique(result: schemas.RecommendationOutput) -> Optional[str]:
|
|
recs = list(result.recommendations or [])
|
|
if not recs:
|
|
return None
|
|
return recs[0].get("name") or recs[0].get("id")
|