mcp-halflist 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
halflist/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
halflist/bench.py ADDED
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+
3
+ import statistics
4
+ import time
5
+ from typing import Callable
6
+
7
+ from mcp import types
8
+
9
+ from halflist.client import HalflistClient
10
+ from halflist.models import ToolBenchmark
11
+ from halflist.schema_gen import generate_args
12
+
13
+
14
+ async def bench_tool(
15
+ client: HalflistClient,
16
+ tool: types.Tool,
17
+ iterations: int,
18
+ warmup: int,
19
+ on_call: Callable[[int], None] | None = None,
20
+ ) -> ToolBenchmark:
21
+ args = generate_args(tool.inputSchema)
22
+ errors = 0
23
+
24
+ for _ in range(warmup):
25
+ try:
26
+ await client.call_tool(tool.name, args)
27
+ except Exception:
28
+ pass
29
+
30
+ latencies: list[float] = []
31
+ for i in range(iterations):
32
+ start = time.monotonic()
33
+ try:
34
+ result = await client.call_tool(tool.name, args)
35
+ elapsed = (time.monotonic() - start) * 1000
36
+ if getattr(result, "isError", False):
37
+ errors += 1
38
+ latencies.append(elapsed)
39
+ except Exception:
40
+ elapsed = (time.monotonic() - start) * 1000
41
+ errors += 1
42
+ latencies.append(elapsed)
43
+ if on_call:
44
+ on_call(i + 1)
45
+
46
+ if not latencies:
47
+ return ToolBenchmark(
48
+ tool_name=tool.name,
49
+ iterations=iterations,
50
+ min_ms=0, max_ms=0, mean_ms=0, median_ms=0,
51
+ p95_ms=0, p99_ms=0, errors=errors,
52
+ )
53
+
54
+ latencies.sort()
55
+ quantiles = statistics.quantiles(latencies, n=100) if len(latencies) >= 2 else latencies
56
+ p95_idx = min(94, len(quantiles) - 1) if quantiles else 0
57
+ p99_idx = min(98, len(quantiles) - 1) if quantiles else 0
58
+
59
+ max_val = latencies[-1]
60
+ p95_raw = quantiles[p95_idx] if quantiles else max_val
61
+ p99_raw = quantiles[p99_idx] if quantiles else max_val
62
+
63
+ return ToolBenchmark(
64
+ tool_name=tool.name,
65
+ iterations=iterations,
66
+ min_ms=round(latencies[0], 2),
67
+ max_ms=round(max_val, 2),
68
+ mean_ms=round(statistics.mean(latencies), 2),
69
+ median_ms=round(statistics.median(latencies), 2),
70
+ p95_ms=round(min(p95_raw, max_val), 2),
71
+ p99_ms=round(min(p99_raw, max_val), 2),
72
+ errors=errors,
73
+ )
74
+
75
+
76
+ def select_tools(
77
+ all_tools: list[types.Tool],
78
+ tool_names: list[str] | None,
79
+ bench_all: bool,
80
+ default_limit: int = 5,
81
+ ) -> list[types.Tool]:
82
+ if tool_names:
83
+ name_set = set(tool_names)
84
+ selected = [t for t in all_tools if t.name in name_set]
85
+ return selected
86
+ if bench_all:
87
+ return list(all_tools)
88
+ return list(all_tools[:default_limit])