Adding LeanExplore + Loogle#29
Conversation
…REPL memory fixes
Adds two new library-search back-ends for the proposer and a CLI preset
to ablate over them, plus memory hardening for the Lean REPL on
single-user machines.
Search tools:
- LeanExplore (src/ax_prover/tools/lean_explore.py): in-process semantic
search (BM25 + FAISS + cross-encoder reranker) returning source and
informal descriptions. Optional dep `pip install 'lean-explore[local]'`;
searches are serialized via a semaphore because the FAISS/torch backend
is not concurrency-safe.
- Loogle (src/ax_prover/tools/loogle.py): type/name/pattern search against
a local Loogle HTTP server (default http://localhost:8088).
- tools.yaml gains search_lean_explore and search_loogle configs.
CLI:
- `--tools {default,lean_explore,loogle,all}` preset on `prove` and
`experiment`, overriding prover.proposer_tools after config merge.
Cross-iteration proposer tool log (opt-in, prover.tool_log.enabled):
records every search (tool, query, summarized result) and re-injects it on
later iterations so the proposer avoids repeating identical queries.
Lean REPL memory hardening (utils/lean_interact.py, see lean4#6753):
- max_total_memory 0.8 -> 0.95 (the 80% system-wide trigger fires on the
agent's own memory on single-user boxes)
- disable parallel elaboration (amplifies the per-pass memory leak)
- memory_hard_limit_mb default 32000 (per-server RSS cap)
Dataset tooling:
- scripts/create_putnam_dataset.py (with --limit) builds the LangSmith
PutnamBench subset; uses src/ prefix for Putnam targets.
- scripts/test_lean_explore.py, scripts/test_lean_search.py smoke tests.
Docs: README section on the search tools, --tools presets, optional setup
for LeanExplore and Loogle, and the cross-iteration tool log.
| f"Cannot connect to Loogle server at {config.server_url}. " | ||
| "Make sure the local Loogle server is running." | ||
| ) | ||
| return _format_response(query, data, config.max_results) |
There was a problem hiding this comment.
Loogle startup raises uncaught exception
High Severity
When the Loogle JSON API reports a “starting up” error, _format_response raises LoogleNotReadyError, but loogle_search only catches aiohttp.ClientError. That exception propagates through the tool wrapper and can abort a proposer step instead of returning the friendly retry message described in the README.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 41c871c. Configure here.
|
Hi! Thanks for the thoughtful comment, we’ll review it and follow up soon! |
BorjaRequena
left a comment
There was a problem hiding this comment.
Thank you very much for contributing to this project, @tcz! We really appreciate the effort that you have put into not only adding new features, but also in evaluating them.
This PR introduces multiple new elements besides the search tools, such as a new config file, scripts, a tool log class, a tool wrapper, modifications to the existing tools, a new config class with an extension to the LeanInteract config, and a --tools flag to the CLI. Seeing everything together helped understand the experiments that you ran. Now, I believe it'll be easier if we decide the elements we want to proceed with the integration and then either trim down this PR or break it into smaller ones.
At a high level, I am happy to incorporate the support for the Loogle and LeanExplore tools. This is a net positive for the library and I am happy to see that LeanExplore can even provide an edge with respect to out-of-the-box LeanSearch. However, I am not convinced by the addition of the ToolLog. I believe it was a cool idea to try out and, given the results, it looks like it's not worth the complexity it introduces. I am also not convinced by the --tools flag in the CLI. I believe the tool control should come from the config file directly: one file for every different tool setup / experiment. This keeps a single source of truth and makes everything fully traceable and reproducible with a single interface.
Feel free to disagree and comment here if you believe some of these elements are worth it, or if I missed something. I'll be happy to see how to accommodate them then. Below I leave some high-level code comments and once we narrow down this contribution we can get into more details 😄
Thanks again, @tcz!!
There was a problem hiding this comment.
Ideally, I would like to have the tools be tested in tests/unit/tools/. See, for instance, tests/unit/tools/test_lean_search.py.
| search_lean_explore: | ||
| tool_type: search_lean_explore | ||
| max_results: 6 | ||
| rerank_top: 50 | ||
| packages: ["Mathlib"] |
There was a problem hiding this comment.
How does LeanExplore handle time outs? What's the time and does it retry? Also, can you comment on the OOM crashes? What was their origin or how could they be prevented? Should we handle these crashes gracefully in the integration?
| memory_hard_limit_mb=self._config.memory_hard_limit_mb, | ||
| enable_parallel_elaboration=self._config.enable_parallel_elaboration, | ||
| ) | ||
| self._server = AutoLeanServer( | ||
| repl_config, | ||
| max_total_memory=self._config.max_total_memory, | ||
| max_restart_attempts=self._config.max_restart_attempts, |
There was a problem hiding this comment.
What motivated changing these settings? We've had some issues with LeanInteract randomly going out of memory in our local environments (see #33), and I am curious to learn what other problems you encountered.
- Remove the tool log entirely (it did not improve results): delete the dedicated modules/tests and restore agent.py, lean_search.py, web_search.py, and the models to upstream byte-for-byte. - Refactor LeanExplore off module-level globals onto the Runtime lifespan pattern from Axiomatic-AI#11; add unit tests for LeanExplore and Loogle under tests/unit/tools/. - Align Loogle defaults with the other search tools (max_results 6, timeout 60). - Drop the ablation config (configs/sonnet.yaml) and ad-hoc test scripts.
# Conflicts: # src/ax_prover/utils/lean_interact.py
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit de94c06. Configure here.
| web = tool_configs["search_web"] | ||
| lean_search = tool_configs["search_lean_search"] | ||
| lean_explore = tool_configs["search_lean_explore"] | ||
| loogle = tool_configs["search_loogle"] |
There was a problem hiding this comment.
Preset requires all tool keys
Medium Severity
_apply_tools_preset always indexes search_lean_search, search_lean_explore, and search_loogle on the loaded tool_configs before it branches on the preset, so --tools loogle or --tools lean_explore still require every new entry to exist in the resolved tools.yaml. An older or minimal project tools.yaml (without the new blocks) makes the CLI fail with KeyError instead of applying the chosen preset.
Reviewed by Cursor Bugbot for commit de94c06. Configure here.
|
@BorjaRequena thanks for the review and leaving your observations. I addressed most of them. A few comments about OOM and other issues below. The OOM crashesThese came from the Lean REPL (lean_interact) path, not the search tools. The signature is: You can see see an example here. The Lean server's memory grows across proof iterations, and a handful of hard Putnam problems trigger very heavy elaboration. This is the same issue you reference in #33, and it's what motivated the lean_interact settings I address in your other comment (memory_hard_limit_mb, max_total_memory, max_restart_attempts). FWIW I've dropped my memory changes entirely and rebased onto your #33 fix. The PR no longer touches lean_interact memory handling, so config.py and lean_interact.py now match main exactly. Tool logThis is totally fair and I agree. I was hesitant to push it as it did not seem to have moved the needle. I pushed it anyway because it's an optional change and it demonstrates a possible path of improvement that was tried and failed, so I figured it was still useful to know. The idea was to show the proposer what it had already searched, but it didn't improve our results, at least not in the limited ablation set that I tried. I've removed it now. Smoke testsI ran few more smoke tests with these changes on a small, 5-item ablation set. This time with Opus.
(The total cost of the smoke tests $259.06 due to the upstream bugs I discovered, see below.) All finished fine, although as you'll notice this time all branches did equally well. It's possible that the improvement seen in the Sonnet runs is just noise, as pointed out before. BugsWhile ablating these on PutnamBench I ran into two proposer bugs. Both bisect cleanly to #31 and are the same root cause #34 addresses by removing the filter:
Since #34 is not yet merged, I did my own fix when running the ablations, but this is not committed. I created a simple regression test for these two bugs, I leave a comment in #34, feel free to add it there. Merging this PRI want to be explicit that I have no expectation to get this PR or any parts of it merged. This is your repo, your research, you are calling the shots, obviously. These features were not previously discussed and approved. I'm very sympathetic to the opinion that PRs without prior discussion are rude and counterproductive. Still, I hope you think my findings here are useful for your research and that I demonstrated my interest. If there's any more work to be done here please let me know, happy to chisel it more. |


Summary
Adds two new library-search back-ends for the proposer (LeanExplore and Loogle), a
--toolspreset flag to ablate over them, an opt-in cross-iteration tool log, and memory hardening for the Lean REPL on single-user machines. Includes README docs and unit tests.This is the code behind a tool-ablation study on a 20-problem PutnamBench subset (Claude Sonnet 4.5, 50 iterations) probing the paper's "search tools help only marginally" claim with search tools other than LeanSearch.
I spent about $1,000 on experiments and found encouraging ablation study results when using LeanExplore. Worth doing with a full set of programs and bigger models if you have the budget.
What's new
Library-search tools
tools/lean_explore.py) — in-process semantic search (BM25 + FAISS + cross-encoder reranker) returning source text and informal descriptions. Optional dependency:pip install 'lean-explore[local]'. Searches are serialized via anasyncio.Semaphorebecause the FAISS/torch backend is not concurrency-safe (deadlocks under parallel calls).tools/loogle.py) — type/name/pattern search against a local Loogle HTTP server (defaulthttp://localhost:8088).configs/tools.yamlgainssearch_lean_exploreandsearch_loogleentries.CLI
--tools {default,lean_explore,loogle,all}on bothproveandexperiment, overridingprover.proposer_toolsafter config merge (main.py:_apply_tools_preset).Cross-iteration tool log (opt-in via
prover.tool_log.enabled=true)Lean REPL memory hardening (
utils/lean_interact.py, cf.lean4#6753)max_total_memory0.8 → 0.95— the 80% system-wide trigger fires on the agent's own memory on a single-user box, restarting the Lean server frees nothing, and runs get killed mid-proof.memory_hard_limit_mbdefault32000— per-server RSS cap that kills a runaway compile without tripping the system-wide threshold.Dataset tooling
scripts/create_putnam_dataset.py(--limit) builds the LangSmith PutnamBench subset; uses thesrc/prefix for Putnam targets.scripts/test_lean_explore.py,scripts/test_lean_search.pysmoke tests.How to use
Results (Sonnet 4.5, 20-problem PutnamBench subset, 50 iterations)
To test the effects of the additional tools I created a 20-item random subset of the 100 PutnamBench samples the paper lists for ablation studies (Table 3). Running all 100 problems, or using Opus models was over my budget, but I suggest running all 100 if you can (and trying Opus models) in order to verify the lift from the added tools.
My results are not statistically sound due to the small item count.
putnam_1973_a3, proved every time across four independent runs with LeanExplore, and never by LeanSearch or Loogle even with the full 50-iteration budget. LeanExplore's richer output (source + informal description + reranking) surfaces a lemma the other two miss.allwas the cheapest clean run ($94.60) — tool descriptions are a fixed prompt cost and the agent substitutes between tools rather than calling all of them (Tavily web search was used zero times in theallrun). It proved the same 7 as LeanExplore alone.Cost footnote: figures are LangSmith's token-priced totals for the five final runs above ($531.02 combined). Total API spend across the whole study, including pre-fix OOM-truncated runs, re-runs, and superseded configurations, was ≈ $1,069.
Testing
pytest tests/unit— 250 passed (includes newtests/unit/prover/test_tool_log.py).Motivation
A note on motivation: I'm hoping to work with the Axiomatic AI team, and I figured the most honest way to introduce myself was through a real contribution. No expectations on this PR beyond an honest technical review. I will submit a job application a bit later.
Note
Low Risk
Changes extend optional proposer tooling and CLI presets without altering core prove/review logic; main operational risk is local LeanExplore memory/concurrency and reliance on an external Loogle server when those presets are used.
Overview
Adds LeanExplore (local semantic Mathlib search) and Loogle (HTTP pattern/signature search) as proposer tools, wired through
tools.yamland the tool registry.CLI:
proveandexperimentgain--tools {default,lean_explore,loogle,all}, which overridesprover.proposer_toolsvia_apply_tools_presetafter config merge. Non-default presets load entries fromtools.yamland swap or combine library search with existing web search.LeanExplore (
lean_explore.py): optional in-processlean-exploreservice with a runtime lifespan; searches are serialized with a semaphore when FAISS/torch is not safe under concurrency; failed init skips the tool instead of failing the run.Loogle (
loogle.py): queries a local/jsonendpoint (defaultlocalhost:8088), formats hits, and surfaces connection errors and “backend starting up” states.Docs/tests: README documents presets, setup, and server warm-up; new unit tests cover formatting, mocked search paths, and LeanExplore lifespan behavior.
Reviewed by Cursor Bugbot for commit de94c06. Bugbot is set up for automated code reviews on this repo. Configure here.