Skip to content

Commit e769344

Browse files
committed
Merge branch 'main' into cedric/migrate-braintrust-langchain-into-main-pkg
2 parents ab1c01b + 8b15ad6 commit e769344

113 files changed

Lines changed: 9207 additions & 4217 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.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
name: sdk-benchmarking
3+
description: Run, compare, and extend Braintrust Python SDK pyperf benchmarks. Use when touching hot-path code in `py/src/braintrust/` such as serialization, deep-copy, span creation, or logging; when adding or updating files under `py/benchmarks/`; or when you need baseline/branch performance measurements with `cd py && make bench` and `make bench-compare`.
4+
---
5+
6+
# SDK Benchmarking
7+
8+
Use this skill for benchmark work in the Braintrust Python SDK repository.
9+
10+
Benchmark support already exists in `py/benchmarks/`. Use the current repo workflow, not commit archaeology, once you have identified the relevant benchmark surface.
11+
12+
## Read First
13+
14+
Always read:
15+
16+
- `AGENTS.md`
17+
- `CONTRIBUTING.md`
18+
- `py/Makefile`
19+
- `py/benchmarks/__main__.py`
20+
- `py/benchmarks/_utils.py`
21+
- `py/benchmarks/benches/__init__.py`
22+
23+
Read when relevant:
24+
25+
- `py/benchmarks/benches/bench_bt_json.py` for the module pattern
26+
- `py/benchmarks/fixtures.py` for shared payload builders
27+
- `py/setup.py` when benchmarking the optional `orjson` fast path
28+
- `references/benchmark-patterns.md` in this skill for command and module templates
29+
30+
## Workflow
31+
32+
1. Identify the hot path or API surface that changed.
33+
2. Find the nearest existing benchmark module under `py/benchmarks/benches/`.
34+
3. Run the narrowest useful benchmark first.
35+
4. Add or update a `bench_*.py` module only if the current suite does not cover the changed path.
36+
5. Reuse or extend `py/benchmarks/fixtures.py` for realistic shared payloads instead of inlining bulky test data.
37+
6. Save before/after results and compare them when the task is about regression detection or improvement claims.
38+
39+
## Commands
40+
41+
Run benchmarks from `py/`:
42+
43+
```bash
44+
cd py
45+
make bench
46+
make bench BENCH_ARGS="--fast"
47+
make bench BENCH_ARGS="-o /tmp/before.json"
48+
make bench BENCH_ARGS="-o /tmp/after.json"
49+
make bench-compare BENCH_BASE=/tmp/before.json BENCH_NEW=/tmp/after.json
50+
python -m benchmarks.benches.bench_bt_json
51+
```
52+
53+
Use `python -m benchmarks --help` for extra `pyperf` flags.
54+
55+
If the benchmark should measure the optional `orjson` path, install the performance extra first:
56+
57+
```bash
58+
cd py
59+
python -m uv pip install -e '.[performance]'
60+
```
61+
62+
## Adding Benchmarks
63+
64+
Put new modules in `py/benchmarks/benches/` and name them `bench_<name>.py`.
65+
66+
Every benchmark module must:
67+
68+
- expose `main(runner: pyperf.Runner | None = None) -> None`
69+
- create its own `pyperf.Runner()` only when `runner` is `None`
70+
- call `disable_pyperf_psutil()` before creating that runner
71+
- register benchmarks with stable, descriptive names via `runner.bench_func(...)`
72+
- remain executable directly with `python -m benchmarks.benches.bench_<name>`
73+
74+
Do not add manual registration. `python -m benchmarks` auto-discovers every `bench_*.py` module in `py/benchmarks/benches/`.
75+
76+
## Fixtures
77+
78+
Keep reusable payload builders and synthetic objects in `py/benchmarks/fixtures.py`.
79+
80+
Prefer fixture helpers when:
81+
82+
- several benchmark cases share similar payloads
83+
- the inputs are large enough to distract from the benchmark itself
84+
- you need variants such as small, medium, large, circular, or non-string-key cases
85+
86+
Keep fixture builders deterministic and focused on representative data shapes.
87+
88+
## Validation
89+
90+
- Run the narrowest affected benchmark first.
91+
- Use `BENCH_ARGS="--fast"` for quick local sanity checks while iterating.
92+
- Save JSON outputs and use `make bench-compare` for baseline versus branch comparisons.
93+
- If you changed code paths that also have correctness tests, run the smallest relevant test target in addition to the benchmark.
94+
95+
## Pitfalls
96+
97+
- Measuring import/setup overhead instead of the hot function under test.
98+
- Inlining ad hoc payload construction in each benchmark instead of reusing fixtures.
99+
- Forgetting the standalone `main()` pattern, which breaks auto-discovery and direct execution symmetry.
100+
- Claiming performance changes from a single unsaved local run instead of comparing saved results.
101+
- Benchmarking the `orjson` fast path without explicitly installing `.[performance]`.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
interface:
2+
display_name: "SDK Benchmarking"
3+
short_description: "Run and extend Braintrust SDK benchmarks"
4+
default_prompt: "Use $sdk-benchmarking to run, compare, or add Braintrust Python SDK benchmarks."
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Benchmark Patterns
2+
3+
Use this reference when adding or updating SDK benchmarks.
4+
5+
## Command Cheatsheet
6+
7+
```bash
8+
cd py
9+
10+
# Run everything
11+
make bench
12+
13+
# Faster local iteration
14+
make bench BENCH_ARGS="--fast"
15+
16+
# Save results for comparison
17+
make bench BENCH_ARGS="-o /tmp/before.json"
18+
make bench BENCH_ARGS="-o /tmp/after.json"
19+
make bench-compare BENCH_BASE=/tmp/before.json BENCH_NEW=/tmp/after.json
20+
21+
# Run one module directly
22+
python -m benchmarks.benches.bench_bt_json
23+
24+
# Inspect all forwarded pyperf flags
25+
python -m benchmarks --help
26+
```
27+
28+
## Module Skeleton
29+
30+
```python
31+
import pathlib
32+
import sys
33+
34+
import pyperf
35+
36+
37+
if __package__ in (None, ""):
38+
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2]))
39+
40+
from benchmarks._utils import disable_pyperf_psutil
41+
42+
43+
def target(value):
44+
return value
45+
46+
47+
def main(runner: pyperf.Runner | None = None) -> None:
48+
if runner is None:
49+
disable_pyperf_psutil()
50+
runner = pyperf.Runner()
51+
52+
runner.bench_func("example.target[case-name]", target, "value")
53+
54+
55+
if __name__ == "__main__":
56+
main()
57+
```
58+
59+
Follow the existing `py/benchmarks/benches/bench_bt_json.py` pattern when importing repo code. The `sys.path` adjustment keeps direct module execution working from inside `py/`.
60+
61+
## Fixture Guidance
62+
63+
Put reusable builders in `py/benchmarks/fixtures.py` when:
64+
65+
- several benchmark cases need the same payload shape
66+
- the payload should model realistic nested SDK inputs
67+
- the benchmark should cover edge cases such as circular references or non-string keys
68+
69+
Current fixture patterns already cover:
70+
71+
- small, medium, and large nested payloads
72+
- circular structures
73+
- non-string dictionary keys
74+
- dataclass-like and pydantic-like values
75+
76+
Extend those helpers before creating one-off payload factories in a new benchmark module.
77+
78+
## Comparison Workflow
79+
80+
For branch-to-branch comparisons:
81+
82+
```bash
83+
cd py
84+
git checkout main
85+
make bench BENCH_ARGS="-o /tmp/main.json"
86+
87+
git checkout my-branch
88+
make bench BENCH_ARGS="-o /tmp/branch.json"
89+
90+
make bench-compare BENCH_BASE=/tmp/main.json BENCH_NEW=/tmp/branch.json
91+
```
92+
93+
Use `--rigorous` only when you need lower-noise final numbers; use `--fast` while iterating.

0 commit comments

Comments
 (0)