0ef5fcb1c5
Security / Dependency audit (pip-audit) (push) Has been cancelled
Security / CodeQL (javascript-typescript) (push) Has been cancelled
Security / CodeQL (python) (push) Has been cancelled
Security / Secret scan (gitleaks) (push) Has been cancelled
rust / test (ubuntu) (push) Has been cancelled
rust / simulator e2e (macos-latest) (push) Has been cancelled
rust / simulator e2e (ubuntu-latest) (push) Has been cancelled
rust / simulator e2e (windows-latest) (push) Has been cancelled
rust / wheels (aarch64-apple-darwin) (push) Has been cancelled
rust / wheels (x86_64-unknown-linux-gnu) (push) Has been cancelled
rust / wheels (x86_64-apple-darwin) (push) Has been cancelled
rust / audit (push) Has been cancelled
rust / parity (nightly, allowed to fail during Phase 0) (push) Has been cancelled
CI / commitlint (push) Has been skipped
Dev Containers / validate (.devcontainer/devcontainer.json, default) (push) Failing after 0s
Dev Containers / validate (.devcontainer/memory-stack/devcontainer.json, memory-stack) (push) Failing after 0s
Dev Containers / validate-worktree (push) Failing after 0s
CI / changes (push) Failing after 4s
Deploy Documentation / validate (push) Has been skipped
Deploy Documentation / deploy (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, claude) (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, codex) (push) Failing after 1s
Install Native E2E / install-native (ubuntu-latest) (push) Failing after 1s
OpenCode Plugin / typecheck + build + test (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, copilot) (push) Failing after 1s
Release Please / release-please (push) Failing after 1s
Wrap E2E / docker-wrap-e2e (push) Failing after 1s
Wrap Native E2E / wrap-native (ubuntu-latest) (push) Failing after 1s
Init E2E / docker-init-e2e (push) Failing after 4s
Merge Conflicts / merge-conflicts (push) Failing after 4s
CI / lint (push) Has been cancelled
CI / build-wheel (push) Has been cancelled
CI / build-wheel-windows (push) Has been cancelled
CI / prefetch-model (push) Has been cancelled
CI / test-dashboard-ui (push) Has been cancelled
CI / test (1) (push) Has been cancelled
CI / test (2) (push) Has been cancelled
CI / test (3) (push) Has been cancelled
CI / test (4) (push) Has been cancelled
CI / test-extras (push) Has been cancelled
CI / test-agno (push) Has been cancelled
CI / build (push) Has been cancelled
CI / workflow-validation (push) Has been cancelled
CI / docker-native-e2e (push) Has been cancelled
CI / windows-native-wrapper (push) Has been cancelled
CI / macos-native-wrapper (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / promote-latest (push) Has been cancelled
Init Native E2E / init-native (macos-latest, claude) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, codex) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, copilot) (push) Has been cancelled
Install Native E2E / install-native (macos-latest) (push) Has been cancelled
Wrap Native E2E / wrap-native (macos-latest) (push) Has been cancelled
369 lines
10 KiB
Python
369 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
"""CLI runner for Headroom benchmark suite.
|
|
|
|
This script provides a convenient interface for running benchmarks and
|
|
generating reports. It wraps pytest-benchmark with Headroom-specific
|
|
options and markdown report generation.
|
|
|
|
Usage:
|
|
# Run all benchmarks
|
|
python benchmarks/run_benchmarks.py
|
|
|
|
# Run specific suite
|
|
python benchmarks/run_benchmarks.py --suite transforms
|
|
|
|
# Generate markdown report
|
|
python benchmarks/run_benchmarks.py --output report.md
|
|
|
|
# Compare against baseline
|
|
python benchmarks/run_benchmarks.py --compare baseline.json
|
|
|
|
# Save results as new baseline
|
|
python benchmarks/run_benchmarks.py --save-baseline baseline.json
|
|
|
|
Available Suites:
|
|
all - Run all benchmark suites (transforms + relevance)
|
|
latency - Compression overhead & cost-benefit analysis (standalone)
|
|
transforms - SmartCrusher, CacheAligner
|
|
relevance - BM25Scorer, HybridScorer
|
|
crusher - SmartCrusher only
|
|
pipeline - Full transform pipeline
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
# Benchmark suite definitions
|
|
BENCHMARK_SUITES = {
|
|
"all": [
|
|
"benchmarks/bench_transforms.py",
|
|
"benchmarks/bench_relevance.py",
|
|
],
|
|
"latency": [], # Standalone script: python benchmarks/bench_latency.py
|
|
"transforms": [
|
|
"benchmarks/bench_transforms.py",
|
|
],
|
|
"relevance": [
|
|
"benchmarks/bench_relevance.py",
|
|
],
|
|
"crusher": [
|
|
"benchmarks/bench_transforms.py::TestSmartCrusherBenchmarks",
|
|
],
|
|
"aligner": [
|
|
"benchmarks/bench_transforms.py::TestCacheAlignerBenchmarks",
|
|
],
|
|
"pipeline": [
|
|
"benchmarks/bench_transforms.py::TestTransformPipelineBenchmarks",
|
|
],
|
|
"bm25": [
|
|
"benchmarks/bench_relevance.py::TestBM25Benchmarks",
|
|
],
|
|
"hybrid": [
|
|
"benchmarks/bench_relevance.py::TestHybridBenchmarks",
|
|
],
|
|
}
|
|
|
|
# Performance targets (mean time in microseconds)
|
|
PERFORMANCE_TARGETS = {
|
|
"test_compress_100_items": 2000, # 2ms
|
|
"test_compress_1000_items": 10000, # 10ms
|
|
"test_compress_10000_items": 100000, # 100ms
|
|
"test_date_extraction": 1000, # 1ms
|
|
"test_hash_computation": 500, # 0.5ms
|
|
"test_window_50_turns": 5000, # 5ms
|
|
"test_window_200_turns": 20000, # 20ms
|
|
"test_single_item": 100, # 0.1ms
|
|
"test_batch_100": 1000, # 1ms
|
|
"test_batch_1000": 10000, # 10ms
|
|
"test_pipeline_simple": 5000, # 5ms
|
|
"test_pipeline_agentic": 30000, # 30ms
|
|
"test_pipeline_rag": 50000, # 50ms
|
|
}
|
|
|
|
|
|
def run_benchmarks(
|
|
suite: str,
|
|
output_json: str | None = None,
|
|
compare: str | None = None,
|
|
verbose: bool = False,
|
|
extra_args: list[str] | None = None,
|
|
) -> tuple[int, dict[str, Any] | None]:
|
|
"""Run benchmark suite via pytest.
|
|
|
|
Args:
|
|
suite: Name of benchmark suite to run.
|
|
output_json: Path to save JSON results.
|
|
compare: Path to baseline JSON for comparison.
|
|
verbose: Enable verbose output.
|
|
extra_args: Additional pytest arguments.
|
|
|
|
Returns:
|
|
Tuple of (exit_code, results_dict).
|
|
"""
|
|
if suite not in BENCHMARK_SUITES:
|
|
print(f"Error: Unknown suite '{suite}'")
|
|
print(f"Available suites: {', '.join(BENCHMARK_SUITES.keys())}")
|
|
return 1, None
|
|
|
|
# Build pytest command
|
|
cmd = [
|
|
sys.executable,
|
|
"-m",
|
|
"pytest",
|
|
"--benchmark-only",
|
|
"--benchmark-sort=name",
|
|
]
|
|
|
|
# Add test files/patterns
|
|
cmd.extend(BENCHMARK_SUITES[suite])
|
|
|
|
# Add output options
|
|
if output_json:
|
|
cmd.extend(["--benchmark-json", output_json])
|
|
|
|
# Add comparison
|
|
if compare:
|
|
cmd.extend(["--benchmark-compare", compare])
|
|
|
|
# Add verbosity
|
|
if verbose:
|
|
cmd.append("-v")
|
|
else:
|
|
cmd.append("-q")
|
|
|
|
# Add extra args
|
|
if extra_args:
|
|
cmd.extend(extra_args)
|
|
|
|
# Run benchmarks
|
|
print(f"Running {suite} benchmarks...")
|
|
print(f"Command: {' '.join(cmd)}")
|
|
print("-" * 60)
|
|
|
|
result = subprocess.run(cmd, capture_output=False)
|
|
|
|
# Load results if saved
|
|
results = None
|
|
if output_json and Path(output_json).exists():
|
|
with open(output_json) as f:
|
|
results = json.load(f)
|
|
|
|
return result.returncode, results
|
|
|
|
|
|
def generate_markdown_report(
|
|
results: dict[str, Any],
|
|
output_path: str,
|
|
include_targets: bool = True,
|
|
) -> None:
|
|
"""Generate markdown report from benchmark results.
|
|
|
|
Args:
|
|
results: Benchmark results dictionary (from pytest-benchmark JSON).
|
|
output_path: Path to write markdown file.
|
|
include_targets: Include performance target comparison.
|
|
"""
|
|
lines = []
|
|
|
|
# Header
|
|
lines.append("# Headroom SDK Benchmark Report")
|
|
lines.append("")
|
|
lines.append(f"Generated: {datetime.now().isoformat()}")
|
|
lines.append("")
|
|
|
|
# Machine info
|
|
if "machine_info" in results:
|
|
info = results["machine_info"]
|
|
lines.append("## Environment")
|
|
lines.append("")
|
|
lines.append(f"- **Machine**: {info.get('machine', 'unknown')}")
|
|
lines.append(f"- **Processor**: {info.get('processor', 'unknown')}")
|
|
lines.append(f"- **Python**: {info.get('python_version', 'unknown')}")
|
|
lines.append("")
|
|
|
|
# Summary table
|
|
lines.append("## Results Summary")
|
|
lines.append("")
|
|
lines.append("| Test | Mean | StdDev | Min | Max | Target | Status |")
|
|
lines.append("|------|------|--------|-----|-----|--------|--------|")
|
|
|
|
benchmarks = results.get("benchmarks", [])
|
|
passed = 0
|
|
failed = 0
|
|
|
|
for bench in benchmarks:
|
|
name = bench["name"]
|
|
stats = bench["stats"]
|
|
|
|
mean_us = stats["mean"] * 1_000_000 # Convert to microseconds
|
|
stddev_us = stats["stddev"] * 1_000_000
|
|
min_us = stats["min"] * 1_000_000
|
|
max_us = stats["max"] * 1_000_000
|
|
|
|
# Format times
|
|
mean_str = _format_time(mean_us)
|
|
stddev_str = _format_time(stddev_us)
|
|
min_str = _format_time(min_us)
|
|
max_str = _format_time(max_us)
|
|
|
|
# Check target
|
|
test_name = name.split("::")[-1]
|
|
target = PERFORMANCE_TARGETS.get(test_name)
|
|
|
|
if target:
|
|
target_str = _format_time(target)
|
|
if mean_us <= target:
|
|
status = "PASS"
|
|
passed += 1
|
|
else:
|
|
status = "FAIL"
|
|
failed += 1
|
|
else:
|
|
target_str = "-"
|
|
status = "-"
|
|
|
|
lines.append(
|
|
f"| `{test_name}` | {mean_str} | {stddev_str} | {min_str} | {max_str} | {target_str} | {status} |"
|
|
)
|
|
|
|
lines.append("")
|
|
|
|
# Summary stats
|
|
total = passed + failed
|
|
if total > 0:
|
|
lines.append("## Summary")
|
|
lines.append("")
|
|
lines.append(f"- **Passed**: {passed}/{total} ({100 * passed / total:.0f}%)")
|
|
lines.append(f"- **Failed**: {failed}/{total} ({100 * failed / total:.0f}%)")
|
|
lines.append("")
|
|
|
|
# Performance notes
|
|
lines.append("## Performance Targets")
|
|
lines.append("")
|
|
lines.append("| Component | Target | Notes |")
|
|
lines.append("|-----------|--------|-------|")
|
|
lines.append("| SmartCrusher (100 items) | < 2ms | Typical API response |")
|
|
lines.append("| SmartCrusher (1000 items) | < 10ms | Large tool output |")
|
|
lines.append("| SmartCrusher (10000 items) | < 100ms | Stress test |")
|
|
lines.append("| CacheAligner | < 1ms | Date extraction + hash |")
|
|
lines.append("| BM25Scorer (batch 100) | < 1ms | Zero dependencies |")
|
|
lines.append("| HybridScorer (batch 100) | < 50ms | With embeddings |")
|
|
lines.append("")
|
|
|
|
# Write file
|
|
with open(output_path, "w") as f:
|
|
f.write("\n".join(lines))
|
|
|
|
print(f"Report written to: {output_path}")
|
|
|
|
|
|
def _format_time(microseconds: float) -> str:
|
|
"""Format time value with appropriate unit."""
|
|
if microseconds < 1000:
|
|
return f"{microseconds:.1f}us"
|
|
elif microseconds < 1_000_000:
|
|
return f"{microseconds / 1000:.2f}ms"
|
|
else:
|
|
return f"{microseconds / 1_000_000:.2f}s"
|
|
|
|
|
|
def main() -> int:
|
|
"""Main entry point."""
|
|
parser = argparse.ArgumentParser(
|
|
description="Run Headroom SDK benchmarks",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog=__doc__,
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--suite",
|
|
"-s",
|
|
choices=list(BENCHMARK_SUITES.keys()),
|
|
default="all",
|
|
help="Benchmark suite to run (default: all)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--output",
|
|
"-o",
|
|
help="Output markdown report path",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--json",
|
|
"-j",
|
|
help="Save raw JSON results to path",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--compare",
|
|
"-c",
|
|
help="Compare against baseline JSON",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--save-baseline",
|
|
help="Save results as baseline (alias for --json)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--verbose",
|
|
"-v",
|
|
action="store_true",
|
|
help="Verbose output",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"pytest_args",
|
|
nargs="*",
|
|
help="Additional pytest arguments",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Handle save-baseline as alias
|
|
json_output = args.json or args.save_baseline
|
|
|
|
# Latency suite is a standalone script, not pytest-benchmark
|
|
if args.suite == "latency":
|
|
cmd = [sys.executable, "benchmarks/bench_latency.py"]
|
|
if args.output:
|
|
cmd.extend(["--output", args.output])
|
|
if json_output:
|
|
cmd.extend(["--json", json_output])
|
|
if args.verbose:
|
|
cmd.append("-v")
|
|
print("Delegating to latency benchmark script...")
|
|
return subprocess.run(cmd).returncode
|
|
|
|
# Run benchmarks
|
|
exit_code, results = run_benchmarks(
|
|
suite=args.suite,
|
|
output_json=json_output,
|
|
compare=args.compare,
|
|
verbose=args.verbose,
|
|
extra_args=args.pytest_args,
|
|
)
|
|
|
|
# Generate markdown report if requested
|
|
if args.output and results:
|
|
generate_markdown_report(results, args.output)
|
|
elif args.output and json_output:
|
|
# Load results from saved JSON
|
|
with open(json_output) as f:
|
|
results = json.load(f)
|
|
generate_markdown_report(results, args.output)
|
|
|
|
return exit_code
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|