Files
wehub-resource-sync 4b6817381b
Benchmark image — build + push to ECR (any adapter) / build + push (push) Waiting to run
CI / quality (ubuntu-latest) (push) Waiting to run
CI / test (tools-runtime) (push) Waiting to run
CI / test (e2e-general) (push) Waiting to run
CI / test (cli-runtime) (push) Waiting to run
CI / test (e2e-provider-and-openclaw) (push) Waiting to run
CI / test (integrations-and-misc) (push) Waiting to run
CI / coverage-report (push) Blocked by required conditions
CI / test-kubernetes (push) Waiting to run
CI / should-run-thorough (push) Waiting to run
CI / test-thorough (cloudwatch-demo) (push) Blocked by required conditions
CI / test-thorough (flink-ecs) (push) Blocked by required conditions
CI / test-thorough (upstream-lambda) (push) Blocked by required conditions
CI / test-thorough (prefect-ecs-fargate) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Blocked by required conditions
Release / publish-release (push) Blocked by required conditions
Release / publish-main-release (push) Blocked by required conditions
Release / prepare (push) Waiting to run
Release / verify (push) Blocked by required conditions
Release / build-python-dist (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Blocked by required conditions
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Waiting to run
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

225 lines
8.4 KiB
Python

"""End-to-end smoke test for the benchmark framework + CloudOpsBench adapter.
Two stages, exposed via flags:
1. ``--adapter-only`` (default): load 1 case, build alert + integrations,
exercise score_case with a fake RunResult. No LLM, no cost, no opensre
pipeline. Verifies the adapter wiring end-to-end.
2. ``--run-investigation``: actually invoke ``run_investigation`` from
``tools.investigation.capability`` against the loaded case. Requires an LLM to
be configured. Costs a few cents per case. Verifies the full chain.
Usage::
uv run python -m tests.benchmarks._framework.smoke # adapter-only
uv run python -m tests.benchmarks._framework.smoke --run-investigation # full chain
uv run python -m tests.benchmarks._framework.smoke --limit 3 --seed 42 # 3 cases
"""
from __future__ import annotations
import argparse
import sys
import time
from datetime import UTC, datetime
from typing import Any, cast
from tests.benchmarks._framework.adapters import (
BenchmarkAdapter,
BenchmarkCase,
CaseFilters,
RunContext,
RunResult,
build_adapter,
)
def _fake_run_result(case: BenchmarkCase) -> RunResult:
"""A plausible RunResult for adapter-only smoke testing.
The fake claims a wrong root cause so we can confirm the scorer
actually penalizes it (rather than silently returning all 1.0s).
"""
now = datetime.now(UTC).isoformat()
return RunResult(
case_id=case.case_id,
mode="opensre+llm",
llm="fake-llm",
model_version="fake-llm-0.0",
opensre_sha="HEAD-uncommitted",
started_at=now,
ended_at=now,
ok=True,
error=None,
final_diagnosis={
"stage": "Runtime",
"component": "wrong-pod",
"root_cause": "deliberately-wrong-cause",
},
evidence_entries=[],
tokens_in=0,
tokens_out=0,
cost_usd=0.0,
latency_ms=0,
)
def _real_run_result(case: BenchmarkCase, adapter: BenchmarkAdapter) -> RunResult:
"""Invoke opensre's run_investigation for real. Requires LLM credentials."""
# Late import — only needed in this branch, keeps adapter-only path
# importable without the full opensre dep tree.
from tools.investigation.capability import run_investigation
alert = adapter.build_alert(case)
integrations = adapter.build_opensre_integrations(case)
started = datetime.now(UTC)
t0 = time.monotonic()
final_state = run_investigation(
alert.raw,
resolved_integrations=integrations,
)
latency_ms = int((time.monotonic() - t0) * 1000)
ended = datetime.now(UTC)
final_state_dict = dict(final_state)
return RunResult(
case_id=case.case_id,
mode="opensre+llm",
llm="(opensre-default)", # llm_dispatch not yet implemented; uses opensre's config
model_version="(unpinned)", # llm_dispatch not yet implemented
opensre_sha="HEAD-uncommitted",
started_at=started.isoformat(),
ended_at=ended.isoformat(),
ok=True,
error=None,
final_diagnosis={
"stage": final_state_dict.get("root_cause_category") or "",
"component": "", # opensre doesn't return this field directly
"root_cause": final_state_dict.get("root_cause") or "",
"report": final_state_dict.get("report") or "",
},
evidence_entries=list(cast(list[Any], final_state_dict.get("evidence_entries") or [])),
tokens_in=0, # cost.py will fill this in later
tokens_out=0,
cost_usd=0.0,
latency_ms=latency_ms,
)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--limit", type=int, default=1, help="Number of cases to load.")
parser.add_argument("--seed", type=int, default=42, help="Seed for case selection.")
parser.add_argument(
"--run-investigation",
action="store_true",
help="Actually invoke opensre's run_investigation (requires LLM config + costs $).",
)
parser.add_argument(
"--adapter-only",
action="store_true",
help="Skip run_investigation; use a fake RunResult to test the adapter only.",
)
parser.add_argument(
"--adapter",
default="cloudopsbench",
help=(
"Name of the registered benchmark adapter to smoke-test. Default "
"is ``cloudopsbench`` (the only adapter shipped today)."
),
)
args = parser.parse_args(argv)
use_real_runner = args.run_investigation and not args.adapter_only
# Resolved through the registry so this smoke does not hold a direct
# import to any specific adapter. The adapter name comes from a CLI
# flag; default is CloudOpsBench because it's the only registered
# adapter today, and a non-CloudOpsBench user can pass
# ``--adapter <name>`` without source changes here.
adapter_name = args.adapter
print(f"==> {adapter_name} adapter end-to-end smoke")
print(
f" limit={args.limit} seed={args.seed} mode={'real' if use_real_runner else 'adapter-only'}"
)
print()
adapter = build_adapter(adapter_name)
print("==> Loading cases")
cases = list(adapter.load_cases(CaseFilters(limit=args.limit, seed=args.seed)))
if not cases:
print(f" ✗ No cases loaded from adapter {adapter_name!r}.")
print(f" Check that the {adapter_name} corpus is available on disk")
print(" (for cloudopsbench: ``make download-cloudopsbench-hf``).")
return 1
print(f" ✓ loaded {len(cases)} case(s)")
for c in cases:
gt: dict[str, Any] = c.metadata.get("ground_truth", {})
print(
f" {c.case_id}\n"
f" system={c.metadata.get('system')} "
f"fault_category={c.metadata.get('fault_category')}\n"
f" true root_cause={gt.get('root_cause')!r}"
)
print()
for case in cases:
print(f"==> Case {case.case_id}")
# 1. build_alert
alert = adapter.build_alert(case)
print(f" ✓ build_alert: raw_keys={sorted(alert.raw.keys())[:6]}...")
# 2. build_opensre_integrations
integrations = adapter.build_opensre_integrations(case)
backend_obj = integrations.get("eks", {}).get("_backend")
backend_type = type(backend_obj).__name__ if backend_obj is not None else "MISSING"
print(
f" ✓ build_opensre_integrations: integration keys={sorted(integrations.keys())}; "
f"eks._backend type={backend_type}"
)
# 3. Run (real or fake)
if use_real_runner:
print(" ▶ running opensre investigation (this takes time + costs $)...")
try:
run = _real_run_result(case, adapter)
print(f" ✓ run completed in {run.latency_ms}ms")
print(f" final_diagnosis.root_cause={run.final_diagnosis.get('root_cause')!r}")
except Exception as exc:
print(f" ✗ run_investigation failed: {exc}")
print(" Check: ~/.opensre/integrations.json + LLM API key")
return 2
else:
run = _fake_run_result(case)
print(" ✓ fake RunResult built (claims wrong root cause to verify scoring)")
# 4. score_case — pass the same integrations dict via RunContext
score = adapter.score_case(case, run, RunContext(integrations=integrations))
if score.failure_reason:
print(f" ✗ scoring failed: {score.failure_reason}")
return 3
print(f" ✓ score_case: {len(score.metrics)} metrics emitted")
for metric_name in sorted(score.metrics.keys()):
value = score.metrics[metric_name]
print(f" {metric_name:>14} = {value:.3f}")
print()
print("==> ✓ End-to-end smoke passed")
print()
print("Per-case sanity check on the metrics above:")
print(" * adapter-only mode injects a wrong root cause →")
print(" expect a1=0, a3=0, partial_a1=0, partial_a3=0, tcr=1 (output well-formed)")
print(" * process metrics (exact, in_order, any_order, rel, cov) measure tool-use")
print(" coverage — fake run has 0 tool calls so these should be near 0")
print(" * iac, rar, ztdr should be 0 (no actions = no invalid/redundant/zero-tool)")
return 0
if __name__ == "__main__":
sys.exit(main())