mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 05:12:33 +00:00
+16




![github-actions[bot] <github-actions[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)




Mega Mind
GitHub
YellowSnnowmann
Steven Enamakel
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Cyrus Gray
Horst1993
Cursor
James Gentes
Sam
Sami Rusani
oxoxDev
Muhammad Ismail
Claude Fable 5
nb213
binyangzhu000-sudo
Steven Enamakel
CodeGhost21
sanil-23
M3gA-Mind
oxoxDev
mysma-9403
mwakidenis
NgoQuocViet2001
viet.ngo
Maciej Myszkiewicz
2e5b5e7b23
Co-authored-by: YellowSnnowmann <167776381+YellowSnnowmann@users.noreply.github.com> Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Cyrus Gray <144336577+graycyrus@users.noreply.github.com> Co-authored-by: Horst1993 <horst.w@gmicloud.ai> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: James Gentes <jgentes@users.noreply.github.com> Co-authored-by: Sam <samrusani@users.noreply.github.com> Co-authored-by: Sami Rusani <14844597+samrusani@users.noreply.github.com> Co-authored-by: oxoxDev <164490987+oxoxDev@users.noreply.github.com> Co-authored-by: Muhammad Ismail <78064250+myi1@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: nb213 <binyangzhu000@gmail.com> Co-authored-by: binyangzhu000-sudo <224954946+binyangzhu000-sudo@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> Co-authored-by: CodeGhost21 <164498022+CodeGhost21@users.noreply.github.com> Co-authored-by: sanil-23 <sanil@tinyhumans.ai> Co-authored-by: M3gA-Mind <elvin@mahadao.com> Co-authored-by: oxoxDev <oxoxdev@users.noreply.github.com> Co-authored-by: mysma-9403 <64923976+mysma-9403@users.noreply.github.com> Co-authored-by: mwakidenis <mwakidenice@gmail.com> Co-authored-by: NgoQuocViet2001 <123613986+NgoQuocViet2001@users.noreply.github.com> Co-authored-by: viet.ngo <viet.ngo@sotatek.com> Co-authored-by: Maciej Myszkiewicz <mmyszkiewicz@bwcoders.com>
372 lines
13 KiB
Bash
Executable File
372 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# library-fleet.sh — sweep the `fleet` scenario of library-profile across
|
|
# agent counts and gate the result against the 2 GB RAM / 2 vCPU server
|
|
# budget (100-1000 live agents). Companion scripts: library-bench.sh
|
|
# (per-scenario RSS/duration), library-cpu.sh (samply), library-heap.sh
|
|
# (dhat).
|
|
#
|
|
# Usage:
|
|
# ./scripts/profile/library-fleet.sh [options]
|
|
#
|
|
# Options:
|
|
# --agents "50,100,500" Comma-separated agent-count sweep (default: "50,100,500")
|
|
# --turns N OPENHUMAN_PROFILE_TURNS per agent (default: 3)
|
|
# --latency-ms N OPENHUMAN_PROFILE_MOCK_LATENCY_MS (default: 200)
|
|
# --workers N OPENHUMAN_PROFILE_WORKER_THREADS, simulates the
|
|
# 2 vCPU box (default: 2)
|
|
# --repeat N Fresh-process repeats per agent count (default: 3)
|
|
# --target N OPENHUMAN_PROFILE_TARGET_AGENTS (default: 1000)
|
|
# --budget-mib N OPENHUMAN_PROFILE_RAM_BUDGET_MIB (default: 2048)
|
|
# --skip-build Reuse the existing target/release binaries
|
|
# --slim Build with --no-default-features (slim library recipe)
|
|
# --out DIR Output directory (default: target/profile/rust-library/fleet-<timestamp>)
|
|
# --no-gate Do not fail the exit code on fits==false (report only)
|
|
# -h, --help Show this help
|
|
#
|
|
# Examples:
|
|
# ./scripts/profile/library-fleet.sh
|
|
# ./scripts/profile/library-fleet.sh --agents 100 --latency-ms 200
|
|
# ./scripts/profile/library-fleet.sh --agents "100,1000" --target 1000 --budget-mib 2048
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
AGENTS="50,100,500"
|
|
TURNS=3
|
|
LATENCY_MS=200
|
|
WORKERS=2
|
|
REPEAT=3
|
|
TARGET=1000
|
|
BUDGET_MIB=2048
|
|
SKIP_BUILD=0
|
|
SLIM=0
|
|
OUT_DIR=""
|
|
GATE=1
|
|
|
|
# Idle CPU is measured over a 10s parked window and should be ~flat
|
|
# regardless of agent count (idle agents cost ~zero CPU). 500ms of CPU
|
|
# across that window (5% of one core) is the "low" threshold used in the
|
|
# PASS/FAIL verdict line; see docs/library-benchmarking.md.
|
|
IDLE_CPU_MS_MAX=500
|
|
|
|
usage() {
|
|
sed -n '2,27p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
exit "${1:-0}"
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--agents)
|
|
AGENTS="${2:?--agents requires a value}"; shift 2 ;;
|
|
--turns)
|
|
TURNS="${2:?--turns requires a value}"; shift 2 ;;
|
|
--latency-ms)
|
|
LATENCY_MS="${2:?--latency-ms requires a value}"; shift 2 ;;
|
|
--workers)
|
|
WORKERS="${2:?--workers requires a value}"; shift 2 ;;
|
|
--repeat)
|
|
REPEAT="${2:?--repeat requires a value}"; shift 2 ;;
|
|
--target)
|
|
TARGET="${2:?--target requires a value}"; shift 2 ;;
|
|
--budget-mib)
|
|
BUDGET_MIB="${2:?--budget-mib requires a value}"; shift 2 ;;
|
|
--skip-build) SKIP_BUILD=1; shift ;;
|
|
--slim) SLIM=1; shift ;;
|
|
--out)
|
|
OUT_DIR="${2:?--out requires a value}"; shift 2 ;;
|
|
--no-gate) GATE=0; shift ;;
|
|
-h|--help) usage 0 ;;
|
|
*)
|
|
echo "ERROR: unknown argument: $1" >&2
|
|
usage 1 ;;
|
|
esac
|
|
done
|
|
|
|
if ! command -v jq >/dev/null 2>&1; then
|
|
echo "ERROR: jq is required (aggregation over run JSON). Install it (e.g. 'brew install jq')." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "$OUT_DIR" ]]; then
|
|
OUT_DIR="$REPO_ROOT/target/profile/rust-library/fleet-$(date +%Y%m%d-%H%M%S)"
|
|
fi
|
|
mkdir -p "$OUT_DIR"
|
|
|
|
BIN="$REPO_ROOT/target/release/library-profile"
|
|
|
|
log() { echo "[library-fleet] $*" >&2; }
|
|
|
|
build_binaries() {
|
|
if [[ "$SKIP_BUILD" -eq 1 ]]; then
|
|
log "skipping build (--skip-build)"
|
|
return
|
|
fi
|
|
local feature_args=(--features rss-bench)
|
|
if [[ "$SLIM" -eq 1 ]]; then
|
|
feature_args=(--no-default-features --features rss-bench)
|
|
log "building slim library-profile + rss-bench (GGML_NATIVE=OFF)"
|
|
else
|
|
log "building default-feature library-profile + rss-bench (GGML_NATIVE=OFF)"
|
|
fi
|
|
(
|
|
cd "$REPO_ROOT"
|
|
GGML_NATIVE=OFF cargo build --release \
|
|
"${feature_args[@]}" \
|
|
--bin library-profile --bin rss-bench
|
|
)
|
|
}
|
|
|
|
run_sweep_point() {
|
|
local n="$1"
|
|
local point_dir="$OUT_DIR/fleet-$n"
|
|
mkdir -p "$point_dir"
|
|
|
|
log "running fleet scenario with $n agents x$REPEAT (fresh process each run)"
|
|
|
|
local i
|
|
for ((i = 1; i <= REPEAT; i++)); do
|
|
local run_file="$point_dir/run-$i.json"
|
|
|
|
env \
|
|
"OPENHUMAN_PROFILE_AGENTS=$n" \
|
|
"OPENHUMAN_PROFILE_TURNS=$TURNS" \
|
|
"OPENHUMAN_PROFILE_MOCK_LATENCY_MS=$LATENCY_MS" \
|
|
"OPENHUMAN_PROFILE_WORKER_THREADS=$WORKERS" \
|
|
"OPENHUMAN_PROFILE_TARGET_AGENTS=$TARGET" \
|
|
"OPENHUMAN_PROFILE_RAM_BUDGET_MIB=$BUDGET_MIB" \
|
|
"$BIN" fleet >"$run_file"
|
|
|
|
if ! jq empty "$run_file" >/dev/null 2>&1; then
|
|
echo "ERROR: run $i for agents=$n did not produce valid JSON: $run_file" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# library-heap.sh clobbers target/release/library-profile with the
|
|
# rss-bench-dhat build, whose allocator perturbs RSS/timing. The dhat
|
|
# binary marks its output, so refuse to benchmark it.
|
|
if [[ "$(jq -r '.dhat // false' "$run_file")" == "true" ]]; then
|
|
echo "ERROR: $BIN was built with rss-bench-dhat (library-heap.sh clobbered it)." >&2
|
|
echo " Re-run without --skip-build to rebuild the plain rss-bench binary." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Emit the median (lower-middle for even N), min, and max of a jq numeric
|
|
# field across the run files for one sweep point, as a JSON object on stdout.
|
|
aggregate_field() {
|
|
local point_dir="$1"
|
|
local jq_path="$2"
|
|
jq -s "
|
|
[ .[] | $jq_path | select(. != null) ] as \$vals |
|
|
(\$vals | sort) as \$sorted |
|
|
(\$sorted | length) as \$n |
|
|
{
|
|
median: (if \$n == 0 then null else \$sorted[(( \$n - 1) / 2 | floor)] end),
|
|
min: (\$sorted[0] // null),
|
|
max: (\$sorted[-1] // null),
|
|
n: \$n
|
|
}
|
|
" "$point_dir"/run-*.json
|
|
}
|
|
|
|
# fits is a boolean per run; report it as "fits" only when every repeat
|
|
# agreed it fits (a single unlucky repeat should not hide a marginal case).
|
|
aggregate_fits() {
|
|
local point_dir="$1"
|
|
jq -s '
|
|
[ .[] | .budget.fits // false ] as $vals |
|
|
($vals | length) as $n |
|
|
($vals | map(select(. == true)) | length) as $true_n |
|
|
{ all_fit: ($n > 0 and $true_n == $n), true_n: $true_n, n: $n }
|
|
' "$point_dir"/run-*.json
|
|
}
|
|
|
|
aggregate_point() {
|
|
local n="$1"
|
|
local point_dir="$OUT_DIR/fleet-$n"
|
|
|
|
local marginal settled_rss idle_cpu threads open_fds p50 p95 p99 projected fits
|
|
marginal=$(aggregate_field "$point_dir" ".marginal_rss_kib_per_agent")
|
|
settled_rss=$(aggregate_field "$point_dir" ".settled.rss_kib")
|
|
idle_cpu=$(aggregate_field "$point_dir" ".idle_cpu_ms")
|
|
threads=$(aggregate_field "$point_dir" ".settled.threads")
|
|
open_fds=$(aggregate_field "$point_dir" ".settled.open_fds")
|
|
p50=$(aggregate_field "$point_dir" ".turn_latency_ms.p50")
|
|
p95=$(aggregate_field "$point_dir" ".turn_latency_ms.p95")
|
|
p99=$(aggregate_field "$point_dir" ".turn_latency_ms.p99")
|
|
projected=$(aggregate_field "$point_dir" ".budget.projected_rss_mib_at_target")
|
|
fits=$(aggregate_fits "$point_dir")
|
|
|
|
jq -n \
|
|
--argjson agents "$n" \
|
|
--argjson marginal_rss_kib_per_agent "$marginal" \
|
|
--argjson settled_rss_kib "$settled_rss" \
|
|
--argjson idle_cpu_ms "$idle_cpu" \
|
|
--argjson threads "$threads" \
|
|
--argjson open_fds "$open_fds" \
|
|
--argjson p50 "$p50" \
|
|
--argjson p95 "$p95" \
|
|
--argjson p99 "$p99" \
|
|
--argjson projected_rss_mib_at_target "$projected" \
|
|
--argjson fits "$fits" \
|
|
'{
|
|
agents: $agents,
|
|
marginal_rss_kib_per_agent: $marginal_rss_kib_per_agent,
|
|
settled_rss_kib: $settled_rss_kib,
|
|
idle_cpu_ms: $idle_cpu_ms,
|
|
threads: $threads,
|
|
open_fds: $open_fds,
|
|
turn_latency_ms: { p50: $p50, p95: $p95, p99: $p99 },
|
|
projected_rss_mib_at_target: $projected_rss_mib_at_target,
|
|
fits: $fits
|
|
}'
|
|
}
|
|
|
|
kib_to_mib() {
|
|
# $1: kib value (may be null/"null"). Prints "n/a" for null.
|
|
local kib="$1"
|
|
if [[ "$kib" == "null" || -z "$kib" ]]; then
|
|
echo "n/a"
|
|
return
|
|
fi
|
|
jq -n --argjson kib "$kib" '($kib / 1024 * 100 | round) / 100'
|
|
}
|
|
|
|
num_or_na() {
|
|
local v="$1"
|
|
[[ "$v" == "null" || -z "$v" ]] && echo "n/a" || echo "$v"
|
|
}
|
|
|
|
write_summary() {
|
|
local summary_json="$OUT_DIR/summary.json"
|
|
local summary_md="$OUT_DIR/summary.md"
|
|
|
|
jq -s \
|
|
--argjson turns "$TURNS" \
|
|
--argjson latency_ms "$LATENCY_MS" \
|
|
--argjson workers "$WORKERS" \
|
|
--argjson repeat "$REPEAT" \
|
|
--argjson target "$TARGET" \
|
|
--argjson budget_mib "$BUDGET_MIB" \
|
|
--arg build "$([[ "$SLIM" -eq 1 ]] && echo "slim" || echo "default")" \
|
|
'{
|
|
generated_at: (now | todate),
|
|
build: $build,
|
|
config: {
|
|
turns: $turns,
|
|
mock_latency_ms: $latency_ms,
|
|
worker_threads: $workers,
|
|
repeat: $repeat,
|
|
target_agents: $target,
|
|
ram_budget_mib: $budget_mib
|
|
},
|
|
sweep: .
|
|
}' \
|
|
"$OUT_DIR"/*.point.json >"$summary_json"
|
|
|
|
local any_fail=0
|
|
|
|
{
|
|
echo "# Fleet benchmark summary"
|
|
echo
|
|
echo "Build: \`$([[ "$SLIM" -eq 1 ]] && echo "slim" || echo "default")\` "
|
|
echo "Repeats per agent count: ${REPEAT} "
|
|
echo "Turns/agent: ${TURNS}, mock latency: ${LATENCY_MS}ms, worker threads: ${WORKERS} "
|
|
echo "Target: ${TARGET} agents, budget: ${BUDGET_MIB} MiB "
|
|
echo "Generated: $(date)"
|
|
echo
|
|
echo "| N | marginal KiB/agent | settled MiB | idle CPU ms/10s | threads | fds | p95 ms | projected MiB @ target | fits |"
|
|
echo "| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | :---: |"
|
|
|
|
local f
|
|
for f in "$OUT_DIR"/*.point.json; do
|
|
local n marginal settled idle threads fds p95 projected fits_all
|
|
n=$(jq -r '.agents' "$f")
|
|
marginal=$(num_or_na "$(jq -r '.marginal_rss_kib_per_agent.median' "$f")")
|
|
settled=$(kib_to_mib "$(jq -r '.settled_rss_kib.median' "$f")")
|
|
idle=$(num_or_na "$(jq -r '.idle_cpu_ms.median' "$f")")
|
|
threads=$(num_or_na "$(jq -r '.threads.median' "$f")")
|
|
fds=$(num_or_na "$(jq -r '.open_fds.median' "$f")")
|
|
p95=$(num_or_na "$(jq -r '.turn_latency_ms.p95.median' "$f")")
|
|
projected=$(num_or_na "$(jq -r '.projected_rss_mib_at_target.median' "$f")")
|
|
fits_all=$(jq -r '.fits.all_fit' "$f")
|
|
echo "| $n | $marginal | $settled | $idle | $threads | $fds | $p95 | $projected | $fits_all |"
|
|
done
|
|
|
|
echo
|
|
echo "## Verdict"
|
|
echo
|
|
|
|
for f in "$OUT_DIR"/*.point.json; do
|
|
local n fits_all idle_median verdict reasons
|
|
n=$(jq -r '.agents' "$f")
|
|
fits_all=$(jq -r '.fits.all_fit' "$f")
|
|
idle_median=$(jq -r '.idle_cpu_ms.median // "null"' "$f")
|
|
|
|
reasons=""
|
|
verdict="PASS"
|
|
if [[ "$fits_all" != "true" ]]; then
|
|
verdict="FAIL"
|
|
reasons="${reasons}projected RSS at target does not fit ${BUDGET_MIB} MiB budget; "
|
|
any_fail=1
|
|
fi
|
|
local idle_over
|
|
idle_over=0
|
|
if [[ "$idle_median" != "null" ]]; then
|
|
idle_over=$(jq -n --argjson v "$idle_median" --argjson max "$IDLE_CPU_MS_MAX" 'if $v > $max then 1 else 0 end')
|
|
fi
|
|
if [[ "$idle_over" -eq 1 ]]; then
|
|
verdict="FAIL"
|
|
reasons="${reasons}idle CPU ${idle_median}ms/10s exceeds ${IDLE_CPU_MS_MAX}ms threshold; "
|
|
fi
|
|
|
|
if [[ "$verdict" == "PASS" ]]; then
|
|
echo "- **N=$n: PASS** — fits budget, idle CPU low."
|
|
else
|
|
echo "- **N=$n: FAIL** — ${reasons}"
|
|
fi
|
|
done
|
|
} >"$summary_md"
|
|
|
|
if [[ "$any_fail" -eq 1 ]]; then
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
main() {
|
|
build_binaries
|
|
|
|
if [[ ! -x "$BIN" ]]; then
|
|
echo "ERROR: $BIN not found or not executable. Build it or drop --skip-build." >&2
|
|
exit 1
|
|
fi
|
|
|
|
IFS=',' read -r -a agent_list <<<"$AGENTS"
|
|
|
|
local n
|
|
for n in "${agent_list[@]}"; do
|
|
run_sweep_point "$n"
|
|
done
|
|
|
|
for n in "${agent_list[@]}"; do
|
|
aggregate_point "$n" >"$OUT_DIR/fleet-$n.point.json"
|
|
done
|
|
|
|
local gate_status=0
|
|
write_summary || gate_status=1
|
|
|
|
log "results: $OUT_DIR"
|
|
cat "$OUT_DIR/summary.md"
|
|
|
|
if [[ "$GATE" -eq 1 && "$gate_status" -ne 0 ]]; then
|
|
log "GATE FAILED: at least one swept N does not fit the ${BUDGET_MIB} MiB / ${TARGET}-agent budget"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
main "$@"
|