mirror of
https://github.com/mims-harvard/ToolUniverse.git
synced 2026-09-19 07:31:47 +08:00
157 lines
4.5 KiB
Python
157 lines
4.5 KiB
Python
"""
|
|
Cache stress test for ToolUniverse.
|
|
|
|
This script repeatedly calls a real built-in tool (Tool_Finder_Keyword) with
|
|
different argument combinations to benchmark cache behaviour under load. It
|
|
reports timing statistics for first-time executions versus cached hits, along
|
|
with the final cache state.
|
|
|
|
Usage:
|
|
python examples/cache_stress_test.py --iterations 200 --seed 42
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import random
|
|
import statistics
|
|
import time
|
|
from collections import Counter, defaultdict
|
|
from pathlib import Path
|
|
|
|
os.environ.setdefault("TOOLUNIVERSE_LIGHT_IMPORT", "1")
|
|
|
|
from tooluniverse import ToolUniverse
|
|
|
|
|
|
QUERY_POOL = [
|
|
"drug discovery pipeline",
|
|
"literature search cancer",
|
|
"clinical trial adverse events",
|
|
"gene expression atlas",
|
|
"protein structure 3d",
|
|
"uniprot annotations",
|
|
"fda drug warnings",
|
|
"metabolomics enrichment",
|
|
"genomics england evidence",
|
|
"semantic scholar publications",
|
|
]
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="Stress test ToolUniverse cache.")
|
|
parser.add_argument(
|
|
"--iterations",
|
|
type=int,
|
|
default=int(os.environ.get("CACHE_STRESS_ITERATIONS", "120")),
|
|
help="Number of tool calls to issue (default: 120 or CACHE_STRESS_ITERATIONS)",
|
|
)
|
|
parser.add_argument(
|
|
"--seed",
|
|
type=int,
|
|
default=None,
|
|
help="Random seed for reproducible sequences",
|
|
)
|
|
parser.add_argument(
|
|
"--cache-path",
|
|
type=str,
|
|
default=None,
|
|
help="Optional custom cache path (overrides TOOLUNIVERSE_CACHE_PATH)",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def configure_cache(cache_path: str | None):
|
|
if cache_path:
|
|
path = Path(cache_path).expanduser()
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
os.environ["TOOLUNIVERSE_CACHE_PATH"] = str(path)
|
|
else:
|
|
if "TOOLUNIVERSE_CACHE_PATH" in os.environ:
|
|
return
|
|
default_dir = Path.cwd() / ".tooluniverse_cache"
|
|
default_dir.mkdir(parents=True, exist_ok=True)
|
|
os.environ["TOOLUNIVERSE_CACHE_PATH"] = str(default_dir / "stress_cache.sqlite")
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
|
|
if args.seed is not None:
|
|
random.seed(args.seed)
|
|
|
|
configure_cache(args.cache_path)
|
|
|
|
tu = ToolUniverse()
|
|
|
|
durations_by_key = defaultdict(list)
|
|
call_counter = Counter()
|
|
|
|
total_calls = args.iterations
|
|
print(f"Running {total_calls} iterations against Tool_Finder_Keyword (use_cache=True)")
|
|
|
|
for i in range(1, total_calls + 1):
|
|
query = random.choice(QUERY_POOL)
|
|
limit = random.choice([3, 5, 7, 10])
|
|
key = (query, limit)
|
|
|
|
start = time.time()
|
|
tu.run_one_function(
|
|
{
|
|
"name": "Tool_Finder_Keyword",
|
|
"arguments": {"description": query, "limit": limit},
|
|
},
|
|
use_cache=True,
|
|
)
|
|
elapsed = time.time() - start
|
|
|
|
durations_by_key[key].append(elapsed)
|
|
call_counter[key] += 1
|
|
|
|
if i % max(1, total_calls // 10) == 0:
|
|
print(f" Progress: {i}/{total_calls} calls complete")
|
|
|
|
tu_stats = tu.get_cache_stats()
|
|
tu.close()
|
|
|
|
first_call_times = []
|
|
cached_call_times = []
|
|
for timings in durations_by_key.values():
|
|
if timings:
|
|
first_call_times.append(timings[0])
|
|
cached_call_times.extend(timings[1:])
|
|
|
|
unique_requests = len(durations_by_key)
|
|
cache_hits = total_calls - unique_requests
|
|
|
|
print("\n=== Cache Stress Test Summary ===")
|
|
print(f"Total calls issued : {total_calls}")
|
|
print(f"Unique argument combos : {unique_requests}")
|
|
print(f"Cache hits (est.) : {cache_hits} ({cache_hits / total_calls:.1%})")
|
|
print(f"Cache misses (est.) : {unique_requests} ({unique_requests / total_calls:.1%})")
|
|
|
|
if first_call_times:
|
|
print(
|
|
f"Average first-call time : {statistics.mean(first_call_times):.3f}s "
|
|
f"(median {statistics.median(first_call_times):.3f}s)"
|
|
)
|
|
if cached_call_times:
|
|
print(
|
|
f"Average cached time : {statistics.mean(cached_call_times):.3f}s "
|
|
f"(median {statistics.median(cached_call_times):.3f}s)"
|
|
)
|
|
|
|
most_common = call_counter.most_common(5)
|
|
if most_common:
|
|
print("\nTop queries:")
|
|
for (query, limit), count in most_common:
|
|
print(f" '{query}' (limit {limit}) -> {count} calls")
|
|
|
|
print("\nCache backend stats:")
|
|
print(tu_stats)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|