mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
194 lines
6.4 KiB
Bash
Executable File
194 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# bench-memory-retrieval.sh — benchmark memory retrieval quality and latency.
|
|
#
|
|
# Tests the core's memory query (semantic search) and tree file-based content
|
|
# retrieval against a set of benchmark queries. Measures wall-clock time per
|
|
# query, result count, and content quality.
|
|
#
|
|
# Usage:
|
|
# ./scripts/bench-memory-retrieval.sh
|
|
# ./scripts/bench-memory-retrieval.sh --verbose
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
VERBOSE=0
|
|
[[ "${1:-}" == "--verbose" ]] && VERBOSE=1
|
|
|
|
CORE_BIN="$REPO_ROOT/target/debug/openhuman-core"
|
|
if [[ ! -x "$CORE_BIN" ]]; then
|
|
echo "ERROR: Build openhuman-core first: cargo build --bin openhuman-core"
|
|
exit 1
|
|
fi
|
|
|
|
WORKSPACE_DIR="${OPENHUMAN_WORKSPACE:-$HOME/.openhuman-staging}"
|
|
USERS_DIR="$WORKSPACE_DIR/users"
|
|
|
|
if [[ ! -d "$USERS_DIR" ]]; then
|
|
echo "ERROR: Workspace users dir not found: $USERS_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Find first user workspace with memory_tree content
|
|
CONTENT_ROOT=$(find "$USERS_DIR" -path "*/workspace/memory_tree/content" -type d 2>/dev/null | head -1 || true)
|
|
if [[ -z "$CONTENT_ROOT" ]]; then
|
|
echo "ERROR: No memory_tree content found under $USERS_DIR/"
|
|
exit 1
|
|
fi
|
|
RESULTS_DIR="$REPO_ROOT/target/bench-memory"
|
|
mkdir -p "$RESULTS_DIR"
|
|
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
RESULTS_FILE="$RESULTS_DIR/retrieval-$TIMESTAMP.txt"
|
|
|
|
# Inventory
|
|
FILE_COUNT=$(find "$CONTENT_ROOT" -type f -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
TOTAL_SIZE=$(du -sh "$CONTENT_ROOT" 2>/dev/null | cut -f1)
|
|
CHAT_COUNT=$(find "$CONTENT_ROOT/chat" -type f -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
EPISODIC_COUNT=$(find "$CONTENT_ROOT/episodic" -type f -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
RAW_COUNT=$(find "$CONTENT_ROOT/raw" -type f -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
WIKI_COUNT=$(find "$CONTENT_ROOT/wiki" -type f -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
|
|
cat <<EOF | tee "$RESULTS_FILE"
|
|
==============================================================
|
|
Memory Retrieval Benchmark
|
|
$(date)
|
|
==============================================================
|
|
|
|
Content root: $CONTENT_ROOT
|
|
Total files: $FILE_COUNT markdown files ($TOTAL_SIZE)
|
|
chat: $CHAT_COUNT files
|
|
episodic: $EPISODIC_COUNT files
|
|
raw: $RAW_COUNT files
|
|
wiki: $WIKI_COUNT files
|
|
|
|
--------------------------------------------------------------
|
|
1. KV Memory Recall (semantic search via SQLite)
|
|
--------------------------------------------------------------
|
|
EOF
|
|
|
|
QUERIES=(
|
|
"what are the most important things to work on"
|
|
"what projects have I been discussing recently"
|
|
"what are my preferences and settings"
|
|
"what people have I interacted with"
|
|
"what technical decisions have been made"
|
|
)
|
|
|
|
export OPENHUMAN_APP_ENV=staging
|
|
export OPENHUMAN_KEYRING_BACKEND=file
|
|
|
|
for query in "${QUERIES[@]}"; do
|
|
echo "" | tee -a "$RESULTS_FILE"
|
|
echo "Query: $query" | tee -a "$RESULTS_FILE"
|
|
|
|
START_NS=$(python3 -c "import time; print(int(time.time()*1e9))")
|
|
|
|
OUTPUT=$(RUST_LOG=error "$CORE_BIN" memory query -n global -q "$query" 2>&1) || true
|
|
|
|
END_NS=$(python3 -c "import time; print(int(time.time()*1e9))")
|
|
ELAPSED_MS=$(python3 -c "print(($END_NS - $START_NS) / 1_000_000)")
|
|
|
|
RESULT_COUNT=$(echo "$OUTPUT" | grep -c '^\[' || true)
|
|
FIRST_RESULT=$(echo "$OUTPUT" | grep '^\[' | head -1 | cut -c1-120)
|
|
|
|
echo " -> ${ELAPSED_MS}ms, $RESULT_COUNT results" | tee -a "$RESULTS_FILE"
|
|
if [[ -n "$FIRST_RESULT" ]]; then
|
|
echo " -> Top: $FIRST_RESULT..." | tee -a "$RESULTS_FILE"
|
|
fi
|
|
|
|
if [[ $VERBOSE -eq 1 ]]; then
|
|
echo "$OUTPUT" | head -20
|
|
fi
|
|
done
|
|
|
|
cat <<EOF | tee -a "$RESULTS_FILE"
|
|
|
|
--------------------------------------------------------------
|
|
2. Tree Content File Walk (direct file scan)
|
|
--------------------------------------------------------------
|
|
EOF
|
|
|
|
# Benchmark file-based tree walking: grep across content files
|
|
TREE_QUERIES=(
|
|
"OpenHuman"
|
|
"memory"
|
|
"important"
|
|
"project"
|
|
"preference"
|
|
)
|
|
|
|
for pattern in "${TREE_QUERIES[@]}"; do
|
|
echo "" | tee -a "$RESULTS_FILE"
|
|
echo "Pattern: $pattern" | tee -a "$RESULTS_FILE"
|
|
|
|
START_NS=$(python3 -c "import time; print(int(time.time()*1e9))")
|
|
|
|
HIT_COUNT=$(grep -rl "$pattern" "$CONTENT_ROOT" 2>/dev/null | wc -l | tr -d ' ')
|
|
SAMPLE=$(grep -rl "$pattern" "$CONTENT_ROOT" 2>/dev/null | head -3 | while read f; do
|
|
basename "$f" | tr '\n' ' '
|
|
done)
|
|
|
|
END_NS=$(python3 -c "import time; print(int(time.time()*1e9))")
|
|
ELAPSED_MS=$(python3 -c "print(($END_NS - $START_NS) / 1_000_000)")
|
|
|
|
echo " -> ${ELAPSED_MS}ms, $HIT_COUNT files matched" | tee -a "$RESULTS_FILE"
|
|
if [[ -n "$SAMPLE" ]]; then
|
|
echo " -> Sample: $SAMPLE" | tee -a "$RESULTS_FILE"
|
|
fi
|
|
done
|
|
|
|
cat <<EOF | tee -a "$RESULTS_FILE"
|
|
|
|
--------------------------------------------------------------
|
|
3. RPC Memory Tree API (structured retrieval)
|
|
--------------------------------------------------------------
|
|
EOF
|
|
|
|
# Test the tree RPC APIs
|
|
RPC_METHODS=(
|
|
"openhuman.memory_tree_search_entities::{\"query\":\"OpenHuman project\",\"limit\":5}"
|
|
"openhuman.memory_tree_query_source::{\"source_kind\":\"chat\",\"limit\":5}"
|
|
"openhuman.memory_tree_query_source::{\"source_kind\":\"episodic\",\"limit\":5}"
|
|
)
|
|
|
|
for spec in "${RPC_METHODS[@]}"; do
|
|
METHOD="${spec%%::*}"
|
|
PARAMS="${spec##*::}"
|
|
|
|
echo "" | tee -a "$RESULTS_FILE"
|
|
echo "RPC: $METHOD" | tee -a "$RESULTS_FILE"
|
|
echo " params: $PARAMS" | tee -a "$RESULTS_FILE"
|
|
|
|
START_NS=$(python3 -c "import time; print(int(time.time()*1e9))")
|
|
|
|
OUTPUT=$(RUST_LOG=error "$CORE_BIN" call --method "$METHOD" --params "$PARAMS" 2>&1) || true
|
|
|
|
END_NS=$(python3 -c "import time; print(int(time.time()*1e9))")
|
|
ELAPSED_MS=$(python3 -c "print(($END_NS - $START_NS) / 1_000_000)")
|
|
|
|
# Extract hit count from JSON
|
|
HITS=$(echo "$OUTPUT" | python3 -c "
|
|
import sys,json
|
|
raw = sys.stdin.read()
|
|
last_line = [l for l in raw.split('\n') if '{' in l]
|
|
d = json.loads(last_line[-1]) if last_line else {}
|
|
print(d.get('result',{}).get('total',0))
|
|
" 2>/dev/null || echo "parse-error")
|
|
|
|
echo " -> ${ELAPSED_MS}ms, hits=$HITS" | tee -a "$RESULTS_FILE"
|
|
|
|
if [[ $VERBOSE -eq 1 ]]; then
|
|
echo "$OUTPUT" | tail -5
|
|
fi
|
|
done
|
|
|
|
cat <<EOF | tee -a "$RESULTS_FILE"
|
|
|
|
==============================================================
|
|
Summary
|
|
==============================================================
|
|
Results saved to: $RESULTS_FILE
|
|
EOF
|