From 2d71e2f1e451a84239ae9d013275bf29c47ddad1 Mon Sep 17 00:00:00 2001 From: ethernet Date: Mon, 13 Jul 2026 15:35:53 -0400 Subject: [PATCH] perf(tools): text prefilter before AST parse in tool discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_module_registers_tools()` reads each `tools/*.py` file and fully AST-parses it to check for a top-level `registry.register()` call. 90 files are scanned on every process start — but only 32 actually register tools. Add a cheap text prefilter: after reading the file (which we need to do anyway for AST), check that both `"registry"` and `"register"` appear in the source before calling `ast.parse`. A file with a top-level `registry.register()` call must contain both strings, so this is a perfect superset — zero false negatives. 50 of 90 files skip the AST parse entirely. The `source=` parameter is not threaded through `discover_builtin_tools`; the prefilter lives entirely inside `_module_registers_tools`, keeping the public API unchanged. Benchmark (median of 10 runs, scanning 90 files): before (read + ast.parse all): 305.9ms after (text prefilter + ast): 187.8ms speedup: 1.6x (118ms saved) Identical module set: 32 modules, same names, same order. --- tools/registry.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/registry.py b/tools/registry.py index 9b6611fb4..354da7123 100644 --- a/tools/registry.py +++ b/tools/registry.py @@ -45,11 +45,20 @@ def _module_registers_tools(module_path: Path) -> bool: Only inspects module-body statements so that helper modules which happen to call ``registry.register()`` inside a function are not picked up. + + A cheap text prefilter avoids the ``ast.parse`` cost for files that do not + mention both ``registry`` and ``register`` — a necessary condition for a + top-level ``registry.register()`` call to exist. """ try: source = module_path.read_text(encoding="utf-8") + except OSError: + return False + if "registry" not in source or "register" not in source: + return False + try: tree = ast.parse(source, filename=str(module_path)) - except (OSError, SyntaxError): + except SyntaxError: return False return any(_is_registry_register_call(stmt) for stmt in tree.body)