LLMs as Copilots for Theorem Proving in Lean
1,322
stars
499
commits
C++
primary language
Aug 22, 2026
updated
🚩News: Our paper is accepted to the International Conference on Neuro-symbolic Systems (NeuS), 2025. See you in Philadelphia!
Lean Copilot allows large language models (LLMs) to be used natively in Lean for proof automation, e.g., suggesting tactics/premises and searching for proofs. You can use our built-in models from LeanDojo or bring your own models that run either locally (w/ or w/o GPUs) or on the cloud.
https://github.com/lean-dojo/LeanCopilot/assets/114432581/ee0f56f8-849e-4099-9284-d8092cbd22a3
:warning: Your project must use a Lean version of at least lean4:v4.3.0-rc2.
moreLinkArgs := #["-L./.lake/packages/LeanCopilot/.lake/build/lib", "-lctranslate2"] to lakefile.lean. For example,package «my-package» {
moreLinkArgs := #[
"-L./.lake/packages/LeanCopilot/.lake/build/lib",
"-lctranslate2"
]
}
Alternatively, if your project uses lakefile.toml, it should include:
moreLinkArgs = ["-L./.lake/packages/LeanCopilot/.lake/build/lib", "-lctranslate2"]
require LeanCopilot from git "https://github.com/lean-dojo/LeanCopilot.git" @ "LEAN_COPILOT_VERSION"
For stable Lean versions (e.g., v4.33.0), set LEAN_COPILOT_VERSION to be that version. For the latest unstable Lean versions (e.g., v4.34.0-rc1), set LEAN_COPILOT_VERSION to main. In either case, make sure the version is compatible with other dependencies such as mathlib. If your project uses lakefile.toml instead of lakefile.lean, it should include:
[[require]]
name = "LeanCopilot"
git = "https://github.com/lean-dojo/LeanCopilot.git"
rev = "LEAN_COPILOT_VERSION"
If you are using native Windows, add <path_to_your_project>/.lake/packages/LeanCopilot/.lake/build/lib to your Path variable in Advanced System Settings > Environment Variables... > System variables.
Run lake update LeanCopilot.
Run lake exe LeanCopilot/download to download the built-in models from Hugging Face to ~/.cache/lean_copilot/. Alternatively, you can download the models from Hugging Face manually from
lake build.Here is an example of a Lean package depending on Lean Copilot. If you have problems building the project, our Dockerfile, build.sh or build_example.sh may be helpful.
After import LeanCopilot, you can use the tactic suggest_tactics to generate tactic suggestions. You can click on any of the suggested tactics to use it in the proof.
You can provide a prefix (e.g., simp) to constrain the generated tactics:
The tactic search_proof combines LLM-generated tactics with aesop to search for multi-tactic proofs. When a proof is found, you can click on it to insert it into the editor.
The select_premises tactic retrieves a list of potentially useful premises. Currently, it uses the retriever in LeanDojo to select premises from a fixed snapshot of Lean and mathlib4.
You can also run the inference of any LLMs in Lean, which can be used to build customized proof automation or other LLM-based applications (not limited to theorem proving). It's possible to run arbitrary models either locally or remotely (see Bring Your Own Model).
This section is only for advanced users who would like to change the default behavior of suggest_tactics, search_proof, or select_premises, e.g., to use different models or hyperparameters.
suggest_tactics, e.g., to use different models or generate different numbers of tactics.search_proof using options provided by aesop.select_premises.Examples in ModelAPIs.lean showcase how to run the inference of different models and configure their parameters (temperature, beam size, etc.).
Lean Copilot supports two kinds of models: generators and encoders. Generators must implement the TextToText interface:
class TextToText (τ : Type) where
generate (model : τ) (input : String) (targetPrefix : String) : IO $ Array (String × Float)
input is the input stringtargetPrefix is used to constrain the generator's output. "" means no constraint.generate should return an array of String × Float. Each String is an output from the model, and Float is the corresponding score.We provide three types of Generators:
NativeGenerator runs locally powered by CTranslate2 and is linked to Lean using Foreign Function Interface (FFI).ExternalGenerator is hosted either locally or remotely. See Bring Your Own Model for details.GenericGenerator can be anything that implements the generate function in the TextToText typeclass.Encoders must implement TextToVec:
class TextToVec (τ : Type) where
encode : τ → String → IO FloatArray
input is the input stringencode should return a vector embedding produced by the model.Similar to generators, we have NativeEncoder, ExternalEncoder, and GenericEncoder.
In principle, it is possible to run any model using Lean Copilot through ExternalGenerator or ExternalEncoder (examples in ModelAPIs.lean). To use a model, you need to wrap it properly to expose the APIs in external_model_api.yaml. As an example, we provide a Python API server and use it to run a few models.
By default, building Lean Copilot from source clones and compiles its native dependencies, OpenBLAS and CTranslate2, which can be slow or awkward on systems (e.g., Nix-based distros) that already package these libraries or make it difficult to compile them from source. If you already have compatible builds available, you can point Lean Copilot at them instead with lake's -K flag (add -R too if you already have a .lake/build from a previous build, so that the new options take effect):
-KsystemOpenblas=<path to your libopenblas.so/.dylib> skips cloning and building OpenBLAS (Linux/Windows only; macOS uses Apple's Accelerate framework instead of OpenBLAS).-KsystemCtranslate2Lib=<path to your libctranslate2.so/.dylib> together with -KsystemCtranslate2Include=<path to a directory containing the ctranslate2/, nlohmann/, and half_float/ header trees> skips cloning and building CTranslate2.For example, on Linux:
lake -R -KsystemOpenblas=/usr/lib/libopenblas.so \
-KsystemCtranslate2Lib=/usr/lib/libctranslate2.so \
-KsystemCtranslate2Include=/usr/include \
build
select_premises always retrieves the original form of a premise. For example, Nat.add_left_comm is a result of the theorem below. In this case, select_premises retrieves Nat.mul_left_comm instead of Nat.add_left_comm.@[to_additive]
theorem mul_left_comm : ∀ a b c : G, a * (b * c) = b * (a * c)
In some cases, search_proof produces an erroneous proof with error messages like fail to show termination for .... A temporary workaround is changing the theorem's name before applying search_proof. You can change it back after search_proof completes.
On Linux, a downstream lean_exe target (as opposed to a lean_lib) links against Lean's own bundled, statically-linked libc++, while Lean Copilot's native code (ct2.cpp) is compiled against the system's libstdc++. A lean_lib never hits this (its .so tolerates undefined symbols, resolved later at load time), but a plain executable link requires every symbol resolved up front, so without extra configuration a lean_exe that depends on Lean Copilot fails to link with undefined libstdc++ symbols. Lean Copilot cannot fully paper over this on its own: statically bundling libstdc++ itself would collide with Lean's already-statically-linked libc++ (both define the same ABI-mangled symbols for types like std::logic_error), so it can only be linked in dynamically, which downstream still has to opt into. If your project has a lean_exe target, add this to its lakefile.toml/lakefile.lean on Linux (adjust the -L path for your distro, e.g. via gcc -print-file-name=libstdc++.so):
moreLinkArgs = [
"-L./.lake/packages/LeanCopilot/.lake/build/lib", "-lctranslate2",
"-Wl,-L/usr/lib/gcc/x86_64-linux-gnu/13", "-Wl,-lstdc++"
]
(-Wl,-lstdc++, not a plain -lstdc++: Lean's bundled clang driver silently rewrites a literal -lstdc++ argument to link libc++ instead, so it must be passed through to the linker directly.)
If you find our work useful, please consider citing our paper:
@article{song2024lean,
title={Lean copilot: Large language models as copilots for theorem proving in lean},
author={Song, Peiyang and Yang, Kaiyu and Anandkumar, Anima},
journal={arXiv preprint arXiv:2404.12534},
year={2024}
}
C++
90.9%
Lean
5.9%
Python
2.9%
LLMs as Copilots for Theorem Proving in Lean
1,322
stars
499
commits
C++
primary language
Aug 22, 2026
updated
🚩News: Our paper is accepted to the International Conference on Neuro-symbolic Systems (NeuS), 2025. See you in Philadelphia!
Lean Copilot allows large language models (LLMs) to be used natively in Lean for proof automation, e.g., suggesting tactics/premises and searching for proofs. You can use our built-in models from LeanDojo or bring your own models that run either locally (w/ or w/o GPUs) or on the cloud.
https://github.com/lean-dojo/LeanCopilot/assets/114432581/ee0f56f8-849e-4099-9284-d8092cbd22a3
:warning: Your project must use a Lean version of at least lean4:v4.3.0-rc2.
moreLinkArgs := #["-L./.lake/packages/LeanCopilot/.lake/build/lib", "-lctranslate2"] to lakefile.lean. For example,package «my-package» {
moreLinkArgs := #[
"-L./.lake/packages/LeanCopilot/.lake/build/lib",
"-lctranslate2"
]
}
Alternatively, if your project uses lakefile.toml, it should include:
moreLinkArgs = ["-L./.lake/packages/LeanCopilot/.lake/build/lib", "-lctranslate2"]
require LeanCopilot from git "https://github.com/lean-dojo/LeanCopilot.git" @ "LEAN_COPILOT_VERSION"
For stable Lean versions (e.g., v4.33.0), set LEAN_COPILOT_VERSION to be that version. For the latest unstable Lean versions (e.g., v4.34.0-rc1), set LEAN_COPILOT_VERSION to main. In either case, make sure the version is compatible with other dependencies such as mathlib. If your project uses lakefile.toml instead of lakefile.lean, it should include:
[[require]]
name = "LeanCopilot"
git = "https://github.com/lean-dojo/LeanCopilot.git"
rev = "LEAN_COPILOT_VERSION"
If you are using native Windows, add <path_to_your_project>/.lake/packages/LeanCopilot/.lake/build/lib to your Path variable in Advanced System Settings > Environment Variables... > System variables.
Run lake update LeanCopilot.
Run lake exe LeanCopilot/download to download the built-in models from Hugging Face to ~/.cache/lean_copilot/. Alternatively, you can download the models from Hugging Face manually from
lake build.Here is an example of a Lean package depending on Lean Copilot. If you have problems building the project, our Dockerfile, build.sh or build_example.sh may be helpful.
After import LeanCopilot, you can use the tactic suggest_tactics to generate tactic suggestions. You can click on any of the suggested tactics to use it in the proof.
You can provide a prefix (e.g., simp) to constrain the generated tactics:
The tactic search_proof combines LLM-generated tactics with aesop to search for multi-tactic proofs. When a proof is found, you can click on it to insert it into the editor.
The select_premises tactic retrieves a list of potentially useful premises. Currently, it uses the retriever in LeanDojo to select premises from a fixed snapshot of Lean and mathlib4.
You can also run the inference of any LLMs in Lean, which can be used to build customized proof automation or other LLM-based applications (not limited to theorem proving). It's possible to run arbitrary models either locally or remotely (see Bring Your Own Model).
This section is only for advanced users who would like to change the default behavior of suggest_tactics, search_proof, or select_premises, e.g., to use different models or hyperparameters.
suggest_tactics, e.g., to use different models or generate different numbers of tactics.search_proof using options provided by aesop.select_premises.Examples in ModelAPIs.lean showcase how to run the inference of different models and configure their parameters (temperature, beam size, etc.).
Lean Copilot supports two kinds of models: generators and encoders. Generators must implement the TextToText interface:
class TextToText (τ : Type) where
generate (model : τ) (input : String) (targetPrefix : String) : IO $ Array (String × Float)
input is the input stringtargetPrefix is used to constrain the generator's output. "" means no constraint.generate should return an array of String × Float. Each String is an output from the model, and Float is the corresponding score.We provide three types of Generators:
NativeGenerator runs locally powered by CTranslate2 and is linked to Lean using Foreign Function Interface (FFI).ExternalGenerator is hosted either locally or remotely. See Bring Your Own Model for details.GenericGenerator can be anything that implements the generate function in the TextToText typeclass.Encoders must implement TextToVec:
class TextToVec (τ : Type) where
encode : τ → String → IO FloatArray
input is the input stringencode should return a vector embedding produced by the model.Similar to generators, we have NativeEncoder, ExternalEncoder, and GenericEncoder.
In principle, it is possible to run any model using Lean Copilot through ExternalGenerator or ExternalEncoder (examples in ModelAPIs.lean). To use a model, you need to wrap it properly to expose the APIs in external_model_api.yaml. As an example, we provide a Python API server and use it to run a few models.
By default, building Lean Copilot from source clones and compiles its native dependencies, OpenBLAS and CTranslate2, which can be slow or awkward on systems (e.g., Nix-based distros) that already package these libraries or make it difficult to compile them from source. If you already have compatible builds available, you can point Lean Copilot at them instead with lake's -K flag (add -R too if you already have a .lake/build from a previous build, so that the new options take effect):
-KsystemOpenblas=<path to your libopenblas.so/.dylib> skips cloning and building OpenBLAS (Linux/Windows only; macOS uses Apple's Accelerate framework instead of OpenBLAS).-KsystemCtranslate2Lib=<path to your libctranslate2.so/.dylib> together with -KsystemCtranslate2Include=<path to a directory containing the ctranslate2/, nlohmann/, and half_float/ header trees> skips cloning and building CTranslate2.For example, on Linux:
lake -R -KsystemOpenblas=/usr/lib/libopenblas.so \
-KsystemCtranslate2Lib=/usr/lib/libctranslate2.so \
-KsystemCtranslate2Include=/usr/include \
build
select_premises always retrieves the original form of a premise. For example, Nat.add_left_comm is a result of the theorem below. In this case, select_premises retrieves Nat.mul_left_comm instead of Nat.add_left_comm.@[to_additive]
theorem mul_left_comm : ∀ a b c : G, a * (b * c) = b * (a * c)
In some cases, search_proof produces an erroneous proof with error messages like fail to show termination for .... A temporary workaround is changing the theorem's name before applying search_proof. You can change it back after search_proof completes.
On Linux, a downstream lean_exe target (as opposed to a lean_lib) links against Lean's own bundled, statically-linked libc++, while Lean Copilot's native code (ct2.cpp) is compiled against the system's libstdc++. A lean_lib never hits this (its .so tolerates undefined symbols, resolved later at load time), but a plain executable link requires every symbol resolved up front, so without extra configuration a lean_exe that depends on Lean Copilot fails to link with undefined libstdc++ symbols. Lean Copilot cannot fully paper over this on its own: statically bundling libstdc++ itself would collide with Lean's already-statically-linked libc++ (both define the same ABI-mangled symbols for types like std::logic_error), so it can only be linked in dynamically, which downstream still has to opt into. If your project has a lean_exe target, add this to its lakefile.toml/lakefile.lean on Linux (adjust the -L path for your distro, e.g. via gcc -print-file-name=libstdc++.so):
moreLinkArgs = [
"-L./.lake/packages/LeanCopilot/.lake/build/lib", "-lctranslate2",
"-Wl,-L/usr/lib/gcc/x86_64-linux-gnu/13", "-Wl,-lstdc++"
]
(-Wl,-lstdc++, not a plain -lstdc++: Lean's bundled clang driver silently rewrites a literal -lstdc++ argument to link libc++ instead, so it must be passed through to the linker directly.)
If you find our work useful, please consider citing our paper:
@article{song2024lean,
title={Lean copilot: Large language models as copilots for theorem proving in lean},
author={Song, Peiyang and Yang, Kaiyu and Anandkumar, Anima},
journal={arXiv preprint arXiv:2404.12534},
year={2024}
}
C++
90.9%
Lean
5.9%
Python
2.9%