Skip to content

Commit 7fe1e80

Browse files
authored
Merge pull request #6 from NetX-lab/codex/benchmark-simulator-cleanup
Release NetOpsBench 0.2 scalable benchmark runtime
2 parents e70517e + 0e442f7 commit 7fe1e80

229 files changed

Lines changed: 21199 additions & 7670 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
.git
2+
.github
3+
.env
4+
.env.*
5+
!.env.example
6+
.netopsbench*
7+
.mypy_cache
8+
.pytest_cache
9+
.ruff_cache
10+
.venv
11+
venv
12+
build
13+
dist
14+
*.egg-info
15+
**/__pycache__
16+
**/*.py[cod]
17+
**/.DS_Store
18+
**/*.log
19+
20+
docs
21+
examples
22+
lab-topology
23+
scenario_results
24+
scenarios
25+
scripts
26+
tests
27+
native/client-agent/target

.env.example

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ NETOPSBENCH_INFLUXDB_BUCKET=netopsbench
3232
# NETOPSBENCH_LOG_LEVEL=INFO # DEBUG | INFO | WARNING | ERROR
3333
# NETOPSBENCH_NO_SUDO=0 # Set to 1 in CI/test environments without sudo
3434
# NETOPSBENCH_PYTHON=/path/to/python3 # Interpreter used by repository shell wrappers
35-
# NETOPSBENCH_TRAFFIC_PARALLELISM=32 # Parallel docker exec workers for benchmark traffic start/stop
3635

3736
# --- Optional semantic fault-type judge ---
3837
# NETOPSBENCH_FAULT_TYPE_JUDGE_ENABLED=0
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: client-agent-image
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
tags:
8+
- "v*"
9+
paths:
10+
- ".github/workflows/client-agent-image.yml"
11+
- ".dockerignore"
12+
- "containers/client/**"
13+
- "native/client-agent/**"
14+
workflow_dispatch:
15+
16+
concurrency:
17+
group: client-agent-image-${{ github.ref }}
18+
cancel-in-progress: true
19+
20+
permissions:
21+
contents: read
22+
23+
jobs:
24+
publish:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- uses: docker/login-action@v3
30+
with:
31+
username: ${{ secrets.DOCKERHUB_USERNAME }}
32+
password: ${{ secrets.DOCKERHUB_TOKEN }}
33+
34+
- uses: docker/setup-buildx-action@v3
35+
36+
- name: Set immutable image tags
37+
id: tags
38+
shell: bash
39+
run: |
40+
tags="yyyyyt123/netopsbench-client:sha-${GITHUB_SHA}"
41+
if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
42+
tags="${tags}"$'\n'"yyyyyt123/netopsbench-client:latest"
43+
fi
44+
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
45+
tags="${tags}"$'\n'"yyyyyt123/netopsbench-client:${GITHUB_REF_NAME}"
46+
fi
47+
{
48+
echo "value<<EOF"
49+
echo "${tags}"
50+
echo "EOF"
51+
} >> "${GITHUB_OUTPUT}"
52+
53+
- name: Build and publish linux/amd64 image
54+
id: build
55+
uses: docker/build-push-action@v6
56+
with:
57+
context: .
58+
file: containers/client/Dockerfile
59+
platforms: linux/amd64
60+
push: true
61+
provenance: false
62+
tags: ${{ steps.tags.outputs.value }}
63+
labels: |
64+
org.opencontainers.image.source=https://github.com/${{ github.repository }}
65+
org.opencontainers.image.revision=${{ github.sha }}
66+
67+
- name: Publish manifest digest
68+
run: echo "digest=${{ steps.build.outputs.digest }}" >> "${GITHUB_STEP_SUMMARY}"
69+
70+
- name: Verify anonymous digest pull
71+
env:
72+
IMAGE_DIGEST: ${{ steps.build.outputs.digest }}
73+
run: |
74+
docker logout
75+
for attempt in $(seq 1 12); do
76+
if docker manifest inspect "yyyyyt123/netopsbench-client@${IMAGE_DIGEST}" >/dev/null; then
77+
exit 0
78+
fi
79+
sleep 5
80+
done
81+
exit 1

.github/workflows/docs-pages.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ on:
44
push:
55
branches:
66
- main
7+
paths:
8+
- "docs/**"
9+
- ".github/workflows/docs-pages.yml"
10+
pull_request:
11+
paths:
12+
- "docs/**"
13+
- ".github/workflows/docs-pages.yml"
714
workflow_dispatch:
815

916
permissions:
@@ -12,7 +19,7 @@ permissions:
1219
id-token: write
1320

1421
concurrency:
15-
group: docs-pages
22+
group: docs-pages-${{ github.ref }}
1623
cancel-in-progress: true
1724

1825
jobs:
@@ -32,6 +39,7 @@ jobs:
3239
cache-dependency-path: docs/package-lock.json
3340

3441
- name: Configure Pages
42+
if: github.event_name != 'pull_request'
3543
uses: actions/configure-pages@v5
3644
with:
3745
enablement: true
@@ -45,11 +53,13 @@ jobs:
4553
run: npm run build
4654

4755
- name: Upload Pages artifact
56+
if: github.event_name != 'pull_request'
4857
uses: actions/upload-pages-artifact@v3
4958
with:
5059
path: docs/out
5160

5261
deploy:
62+
if: github.event_name != 'pull_request'
5363
needs: build
5464
runs-on: ubuntu-latest
5565
environment:

.github/workflows/test.yml

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,45 @@ on:
99
pull_request:
1010

1111
jobs:
12-
lint:
12+
rust:
13+
runs-on: ubuntu-latest
14+
defaults:
15+
run:
16+
working-directory: native/client-agent
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Install pinned Rust
22+
run: |
23+
rustup toolchain install 1.97.1 --profile minimal --component clippy,rustfmt
24+
rustup default 1.97.1
25+
26+
- name: Format
27+
run: cargo fmt --check
28+
29+
- name: Clippy
30+
run: cargo clippy --locked --all-targets -- -D warnings
31+
32+
- name: Test
33+
run: cargo test --locked
34+
35+
client-image:
36+
runs-on: ubuntu-latest
37+
steps:
38+
- name: Checkout
39+
uses: actions/checkout@v4
40+
41+
- name: Build client image
42+
run: docker build -f containers/client/Dockerfile -t netopsbench-client:test .
43+
44+
- name: Verify native-only runtime
45+
run: |
46+
docker run --rm netopsbench-client:test netopsbench-client-agent --version
47+
docker run --rm netopsbench-client:test sh -lc \
48+
'command -v ping && command -v traceroute && ! command -v python3 && ! command -v iperf3'
49+
50+
wheel:
1351
runs-on: ubuntu-latest
1452
steps:
1553
- name: Checkout
@@ -20,6 +58,46 @@ jobs:
2058
with:
2159
python-version: "3.12"
2260

61+
- name: Build wheel
62+
run: |
63+
python -m pip install --upgrade pip
64+
python -m pip wheel --no-deps --wheel-dir dist .
65+
66+
- name: Verify package contents and imports
67+
run: |
68+
python - <<'PY'
69+
import pathlib
70+
import zipfile
71+
72+
wheel = next(pathlib.Path("dist").glob("netopsbench-*.whl"))
73+
with zipfile.ZipFile(wheel) as archive:
74+
names = set(archive.namelist())
75+
forbidden = {
76+
"netopsbench/platform/simulator/contracts.py",
77+
"netopsbench/platform/simulator/engine.py",
78+
"netopsbench/platform/simulator/payloads.py",
79+
}
80+
unexpected = sorted(forbidden & names)
81+
if unexpected:
82+
raise SystemExit(f"wheel contains removed modules: {unexpected}")
83+
PY
84+
python -m venv /tmp/netopsbench-wheel-smoke
85+
/tmp/netopsbench-wheel-smoke/bin/pip install dist/netopsbench-*.whl
86+
cd /tmp
87+
/tmp/netopsbench-wheel-smoke/bin/python -c "from netopsbench.sdk import DiagnosticSession, IncidentEngine, NetOpsBench"
88+
/tmp/netopsbench-wheel-smoke/bin/netopsbench --help >/dev/null
89+
90+
lint:
91+
runs-on: ubuntu-latest
92+
steps:
93+
- name: Checkout
94+
uses: actions/checkout@v4
95+
96+
- name: Set up Python
97+
uses: actions/setup-python@v5
98+
with:
99+
python-version: "3.13"
100+
23101
- name: Install lint dependencies
24102
run: |
25103
python -m pip install --upgrade pip

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
__pycache__/
2+
native/client-agent/target/
23
*.py[cod]
34
*$py.class
45
.venv/

containers/client/Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# syntax=docker/dockerfile:1
2+
FROM rust:1.97.1-alpine3.24@sha256:3c38f3f82c2f3d73da3b38e18d279393a04cb43ddded0e35088a8c3324d40900 AS builder
3+
4+
RUN apk add --no-cache musl-dev
5+
WORKDIR /src/native/client-agent
6+
COPY native/client-agent/Cargo.toml native/client-agent/Cargo.lock native/client-agent/rust-toolchain.toml ./
7+
COPY native/client-agent/src ./src
8+
RUN cargo build --locked --release --target x86_64-unknown-linux-musl
9+
10+
FROM alpine:3.24.1@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
11+
12+
RUN apk add --no-cache \
13+
bash \
14+
ca-certificates \
15+
curl \
16+
ethtool \
17+
iproute2 \
18+
iputils \
19+
procps \
20+
tcpdump \
21+
traceroute
22+
23+
COPY --from=builder \
24+
/src/native/client-agent/target/x86_64-unknown-linux-musl/release/netopsbench-client-agent \
25+
/usr/local/bin/netopsbench-client-agent
26+
27+
CMD ["sleep", "infinity"]

docs/STRUCTURE.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ This documentation site follows a Harbor-Lite layout:
77
- `src/lib/` - shared site logic such as source loading, layout options, and base-path helpers
88
- `content/docs/` - the single source of truth for published documentation content
99
- `public/assets/` - static assets served by the site at runtime
10-
- `_legacy/` - archived historical markdown files kept for reference only
1110

1211
## Rules
1312

@@ -21,7 +20,6 @@ This documentation site follows a Harbor-Lite layout:
2120
## Asset guidance
2221

2322
- Use `public/assets/` for images that must be publicly addressable from pages or MDX.
24-
- Treat `assets/` as temporary or source-material storage only until it is fully audited.
2523
- If an asset is referenced via `/assets/...`, it should exist under `public/assets/`.
2624

2725
## Validation

docs/content/docs/build-your-agent/custom-agents.mdx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ with NetOpsBench(workspace=".") as bench:
4747
| `symptoms` | Episode observations, including Pingmesh summaries when available. |
4848
| `tools` | Optional runtime or MCP-backed tool access. |
4949
| `metadata` | Runtime metadata such as worker-specific environment values. |
50-
| `ground_truth` | Optional ground truth. Benchmark agents should not use it for diagnosis. |
5150

5251
The reference LLM agent builds a compact prompt from topology size, symptom keys, episode data, Pingmesh windows, and high-signal anomalies. A custom agent may instead use rules, symbolic logic, retrieval, additional tool calls, or another model wrapper.
5352

@@ -68,7 +67,7 @@ The reference LLM agent builds a compact prompt from topology size, symptom keys
6867

6968
Return structured fields rather than only natural language. Free-form explanations are useful for review, but scoring depends on verdict, fault type, and location fields.
7069

71-
For reproducibility, NetOpsBench saves a per-case runtime trace beside the raw scenario result. The bundled reference agent captures private LLM and tool events by attaching `context.trace.langchain_callback()` to its LangChain-compatible runtime. Custom non-LangChain agents can use advanced manual recorder methods such as `context.trace.record_llm_request(...)` and `context.trace.record_llm_response(...)` when they need private model calls in the trace. The harness writes ATIF v1.7 `trajectory.atif.json` artifacts for Harbor-style inspection while keeping ground truth out of the agent trajectory; scoring details are linked separately through `traces/results.jsonl`. Use `netopsbench trace view` to sync trace-enabled runs into the local Harbor viewer cache, or `netopsbench trace view <run_id>` to ensure a specific saved run is available in the viewer.
70+
For reproducibility, NetOpsBench saves a per-attempt runtime trace beside the raw scenario result. The bundled reference agent captures private LLM and tool events by attaching `context.trace.langchain_callback()` to its LangChain-compatible runtime. Custom non-LangChain agents can use advanced manual recorder methods such as `context.trace.record_llm_request(...)` and `context.trace.record_llm_response(...)` when they need private model calls in the trace. The harness writes uniquely named ATIF v1.7 `trajectory-<trace-id>.atif.json` artifacts for Harbor-style inspection while keeping ground truth out of the agent trajectory; scoring details are linked separately through `traces/results.jsonl`. Use `netopsbench trace view` to sync trace-enabled runs into the local Harbor viewer cache, or `netopsbench trace view <run_id>` to ensure a specific saved run is available in the viewer.
7271

7372
Trace storage preserves visible agent-environment interactions with secret redaction and per-field size limits. NetOpsBench does not monkeypatch arbitrary LLM SDKs, so fully private model prompts and responses are captured only when the agent uses a supported framework callback or the manual recorder methods.
7473

@@ -90,18 +89,17 @@ NetOpsBench scores the returned result along these dimensions:
9089

9190
| Dimension | What must be correct |
9291
|---|---|
93-
| Detection | Faulted cases should return `fault_detected`; healthy negative samples should return `network_healthy`. |
92+
| Detection | Faulted cases should return `fault_detected`; healthy cases should return `network_healthy`. |
9493
| Device localization | `findings.location.device` should match the ground-truth device or an accepted equivalent location. |
9594
| Interface localization | `findings.location.interface` should match when interface-level ground truth exists. |
9695
| Fault type | `findings.fault_type` should match the canonical fault label, or a semantically equivalent label when the optional judge is enabled. |
9796
| Efficiency | Reports may include diagnosis time, tool calls, and token usage from metadata. |
9897

99-
For interface-applicable faults, localization score is split between device and interface correctness. Healthy negative samples receive credit only for the correct `network_healthy` verdict.
98+
For interface-applicable faults, localization score is split between device and interface correctness. Healthy cases receive credit only for the correct `network_healthy` verdict.
10099

101100
## Practical checklist
102101

103102
- Use exact verdict strings: `fault_detected`, `network_healthy`, `inconclusive`.
104103
- Include device and interface when the evidence supports localization.
105104
- Prefer canonical fault names from the built-in fault catalog.
106-
- Treat `ground_truth` as evaluator data, not as diagnosis input.
107105
- Return `success=False` and an `inconclusive` verdict when the agent runtime fails.

0 commit comments

Comments
 (0)