9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
317 lines
11 KiB
Python
317 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
"""Run a problem set with a team manifest and produce leaderboard-ready results.
|
|
|
|
One command wraps ``harbor run`` with only leaderboard-legal settings — no
|
|
timeout or resource overrides are accepted or forwarded, so the resulting job
|
|
directory passes Harbor's static validation as produced. After the run it
|
|
writes a ``metadata.yaml`` template derived from the manifest and prints the
|
|
exact upload/submit commands.
|
|
|
|
Run inside the testbed environment so ``harbor`` and the adapter are
|
|
importable:
|
|
|
|
uv run --project benchmarks/harbor-buzz-orchestra/testbed \
|
|
benchmarks/harbor-buzz-orchestra/scripts/run_leaderboard.py \
|
|
--dataset terminal-bench/terminal-bench-2-1 \
|
|
--attempts 5 \
|
|
--manifest benchmarks/harbor-buzz-orchestra/manifests/<TEAM>.yaml \
|
|
--endpoint-config benchmarks/harbor-buzz-orchestra/testbed/endpoints/<ENDPOINTS>.json \
|
|
--provisioner-config <PROVISIONER.json> \
|
|
--agent-bin-dir <DIR with Linux buzz-acp/buzz-agent/buzz-dev-mcp>
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import datetime as dt
|
|
import json
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
PACKAGE_ROOT = Path(__file__).resolve().parent.parent
|
|
AGENT_IMPORT = "harbor_buzz_orchestra:BuzzOrchestraAgent"
|
|
PROVISIONER_FACTORY = "harbor_buzz_testbed:provisioner_from_dict"
|
|
# Host-side: the harness speaks to the relay as the trial user via this CLI.
|
|
BINARIES = ("buzz",)
|
|
# Container-side: the production stack uploaded into each task container.
|
|
# These must be Linux builds matching the task image architecture.
|
|
AGENT_BINARIES = ("buzz-acp", "buzz-agent", "buzz-dev-mcp")
|
|
# Uploaded alongside the stack when --relay-gateway is set: bridges the
|
|
# agents' canonical relay address to the host gateway (the relay is
|
|
# host-header tenant-bound, so agents must present its canonical Host).
|
|
FORWARDER_BINARY = "relay-forwarder"
|
|
|
|
PROVIDER_ORGS = {
|
|
"anthropic": "Anthropic",
|
|
"openai": "OpenAI",
|
|
"databricks": "Databricks",
|
|
}
|
|
|
|
|
|
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(
|
|
description=__doc__.splitlines()[0],
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
)
|
|
problems = parser.add_mutually_exclusive_group(required=True)
|
|
problems.add_argument(
|
|
"--dataset",
|
|
"-d",
|
|
help="Registry dataset (e.g. terminal-bench/terminal-bench-2-1)",
|
|
)
|
|
problems.add_argument(
|
|
"--path", "-p", type=Path, help="Local task or dataset directory"
|
|
)
|
|
parser.add_argument(
|
|
"--include-task",
|
|
"-i",
|
|
action="append",
|
|
default=[],
|
|
help="Task name to include from the dataset (glob, repeatable)",
|
|
)
|
|
parser.add_argument(
|
|
"--exclude-task",
|
|
"-x",
|
|
action="append",
|
|
default=[],
|
|
help="Task name to exclude from the dataset (glob, repeatable)",
|
|
)
|
|
parser.add_argument(
|
|
"--attempts",
|
|
"-k",
|
|
type=int,
|
|
required=True,
|
|
help="Runs per problem (leaderboards require 5)",
|
|
)
|
|
parser.add_argument(
|
|
"--manifest", type=Path, required=True, help="Team manifest YAML"
|
|
)
|
|
parser.add_argument(
|
|
"--endpoint-config",
|
|
type=Path,
|
|
required=True,
|
|
help="JSON mapping manifest endpoint names to providers/API keys",
|
|
)
|
|
parser.add_argument(
|
|
"--provisioner-config",
|
|
type=Path,
|
|
required=True,
|
|
help="JSON config for the Buzz relay/Postgres provisioner",
|
|
)
|
|
parser.add_argument(
|
|
"--buzz-bin-dir",
|
|
type=Path,
|
|
default=None,
|
|
help="Directory with the host buzz CLI (default: repo target/release, then target/debug)",
|
|
)
|
|
parser.add_argument(
|
|
"--agent-bin-dir",
|
|
type=Path,
|
|
required=True,
|
|
help="Directory with Linux builds of buzz-acp/buzz-agent/buzz-dev-mcp "
|
|
"to upload into each task container",
|
|
)
|
|
parser.add_argument(
|
|
"--relay-gateway",
|
|
default="",
|
|
help="host:port of the benchmark relay as reachable from inside the "
|
|
"task container (e.g. host.docker.internal:3600). When set, a "
|
|
"loopback forwarder from --agent-bin-dir bridges the canonical "
|
|
"relay address to this gateway",
|
|
)
|
|
parser.add_argument(
|
|
"--n-concurrent", "-n", type=int, default=4, help="Concurrent trials"
|
|
)
|
|
parser.add_argument(
|
|
"--jobs-dir", type=Path, default=Path("jobs"), help="Job output root"
|
|
)
|
|
parser.add_argument(
|
|
"--job-name", default=None, help="Job name (default: lb-<condition>-<UTC>)"
|
|
)
|
|
parser.add_argument(
|
|
"--upload",
|
|
action="store_true",
|
|
help="Upload to Harbor Hub when the job finishes",
|
|
)
|
|
parser.add_argument(
|
|
"--dry-run", action="store_true", help="Print the harbor command and exit"
|
|
)
|
|
return parser.parse_args(argv)
|
|
|
|
|
|
def find_binaries(bin_dir: Path | None) -> dict[str, Path]:
|
|
candidates = (
|
|
[bin_dir]
|
|
if bin_dir is not None
|
|
else [
|
|
PACKAGE_ROOT.parents[1] / "target" / kind for kind in ("release", "debug")
|
|
]
|
|
)
|
|
for candidate in candidates:
|
|
found = {name: candidate / name for name in BINARIES}
|
|
if all(path.is_file() for path in found.values()):
|
|
return found
|
|
searched = ", ".join(str(c) for c in candidates)
|
|
raise SystemExit(
|
|
f"buzz binaries not found (need {', '.join(BINARIES)}; searched {searched}). "
|
|
"Build them with `cargo build` or pass --buzz-bin-dir."
|
|
)
|
|
|
|
|
|
def find_agent_binaries(bin_dir: Path, with_forwarder: bool = False) -> dict[str, Path]:
|
|
"""The Linux agent stack uploaded into each task container."""
|
|
names = AGENT_BINARIES + ((FORWARDER_BINARY,) if with_forwarder else ())
|
|
found = {name: bin_dir / name for name in names}
|
|
missing = [name for name, path in found.items() if not path.is_file()]
|
|
if missing:
|
|
raise SystemExit(
|
|
f"Linux agent binaries not found in {bin_dir}: {', '.join(missing)}. "
|
|
"`just benchmark` builds them; or cross-compile with "
|
|
"cargo --target <arch>-unknown-linux-musl and pass --agent-bin-dir."
|
|
)
|
|
return found
|
|
|
|
|
|
def build_command(
|
|
args: argparse.Namespace,
|
|
binaries: dict[str, Path],
|
|
agent_binaries: dict[str, Path],
|
|
) -> list[str]:
|
|
"""Compose the harbor invocation. Standard settings only: any timeout or
|
|
resource override would fail leaderboard static validation, so none are
|
|
accepted or forwarded."""
|
|
command = [
|
|
"harbor",
|
|
"run",
|
|
"--yes",
|
|
"--job-name",
|
|
args.job_name,
|
|
"--jobs-dir",
|
|
str(args.jobs_dir),
|
|
"-k",
|
|
str(args.attempts),
|
|
"--n-concurrent",
|
|
str(args.n_concurrent),
|
|
]
|
|
if args.dataset:
|
|
command += ["--dataset", args.dataset]
|
|
else:
|
|
command += ["--path", str(args.path)]
|
|
for pattern in args.include_task:
|
|
command += ["--include-task-name", pattern]
|
|
for pattern in args.exclude_task:
|
|
command += ["--exclude-task-name", pattern]
|
|
if args.upload:
|
|
command.append("--upload")
|
|
command += ["--agent", AGENT_IMPORT]
|
|
kwargs = {
|
|
"manifest": args.manifest,
|
|
"provisioner_factory": PROVISIONER_FACTORY,
|
|
"provisioner_config": args.provisioner_config,
|
|
"artifact_root": PACKAGE_ROOT,
|
|
"endpoint_config": args.endpoint_config,
|
|
"buzz_acp_binary": agent_binaries["buzz-acp"],
|
|
"buzz_agent_binary": agent_binaries["buzz-agent"],
|
|
"buzz_dev_mcp_binary": agent_binaries["buzz-dev-mcp"],
|
|
"buzz_cli_binary": binaries["buzz"],
|
|
"run_id": args.job_name,
|
|
}
|
|
if args.relay_gateway:
|
|
kwargs["relay_gateway"] = args.relay_gateway
|
|
kwargs["forwarder_binary"] = agent_binaries[FORWARDER_BINARY]
|
|
for key, value in kwargs.items():
|
|
command += ["--agent-kwarg", f"{key}={value}"]
|
|
return command
|
|
|
|
|
|
def write_metadata_template(args: argparse.Namespace, job_dir: Path) -> Path:
|
|
"""Derive a metadata.yaml template (harbor.leaderboard.metadata schema)
|
|
from the manifest roster; display-name placeholders are for the submitter
|
|
to confirm."""
|
|
manifest = yaml.safe_load(args.manifest.read_text())
|
|
endpoints = json.loads(args.endpoint_config.read_text())
|
|
models, seen = [], set()
|
|
for entry in manifest.get("roster", []):
|
|
model = entry.get("model_revision") or entry["endpoint"]
|
|
if model in seen:
|
|
continue
|
|
seen.add(model)
|
|
provider = endpoints.get(entry["endpoint"], {}).get("provider", "FILL_ME")
|
|
models.append(
|
|
{
|
|
"model_name": model,
|
|
"model_provider": provider,
|
|
"model_display_name": model,
|
|
"model_org_display_name": PROVIDER_ORGS.get(provider, "FILL_ME"),
|
|
}
|
|
)
|
|
metadata = {
|
|
"agent_url": "https://github.com/block/buzz",
|
|
"agent_display_name": f"Buzz Orchestra ({manifest.get('condition', 'team')})",
|
|
"agent_org_display_name": "Block",
|
|
"models": models,
|
|
}
|
|
path = job_dir / "metadata.yaml"
|
|
path.write_text(yaml.safe_dump(metadata, sort_keys=False))
|
|
return path
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
args = parse_args(argv)
|
|
for label, path in (
|
|
("manifest", args.manifest),
|
|
("endpoint config", args.endpoint_config),
|
|
("provisioner config", args.provisioner_config),
|
|
):
|
|
if not path.is_file():
|
|
raise SystemExit(f"{label} not found: {path}")
|
|
if args.job_name is None:
|
|
condition = yaml.safe_load(args.manifest.read_text()).get("condition", "team")
|
|
stamp = dt.datetime.now(dt.UTC).strftime("%Y%m%dT%H%M%SZ")
|
|
args.job_name = f"lb-{condition}-{stamp}"
|
|
|
|
if args.dry_run:
|
|
# Dry runs print the command without requiring built binaries.
|
|
bin_dir = args.buzz_bin_dir or PACKAGE_ROOT.parents[1] / "target" / "release"
|
|
binaries = {name: bin_dir / name for name in BINARIES}
|
|
agent_binaries = {
|
|
name: args.agent_bin_dir / name
|
|
for name in AGENT_BINARIES + (FORWARDER_BINARY,)
|
|
}
|
|
print(" ".join(build_command(args, binaries, agent_binaries)))
|
|
return 0
|
|
binaries = find_binaries(args.buzz_bin_dir)
|
|
agent_binaries = find_agent_binaries(
|
|
args.agent_bin_dir, with_forwarder=bool(args.relay_gateway)
|
|
)
|
|
command = build_command(args, binaries, agent_binaries)
|
|
if shutil.which("harbor") is None:
|
|
raise SystemExit(
|
|
"harbor not on PATH — run via: uv run --project "
|
|
f"{PACKAGE_ROOT / 'testbed'} {Path(__file__).resolve()} ..."
|
|
)
|
|
|
|
result = subprocess.run(command, check=False)
|
|
job_dir = args.jobs_dir / args.job_name
|
|
if result.returncode != 0:
|
|
print(f"harbor run failed (exit {result.returncode}); job dir: {job_dir}")
|
|
return result.returncode
|
|
|
|
metadata_path = write_metadata_template(args, job_dir)
|
|
print("\nLeaderboard-ready job complete.")
|
|
print(f" 1. Review submitter details in {metadata_path}")
|
|
print(f" 2. harbor upload {job_dir}")
|
|
print(
|
|
" 3. harbor leaderboard submit -l terminal-bench/terminal-bench-2-1 "
|
|
f"-j <job UUID from upload> -m {metadata_path}"
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|