A crowdsourced collection of tools to empower Large Language Models in Emacs.
This package is not yet in any repositories. Install it with your favorite from-Git method!
Quick and dirty: clone the repository and add it to load-path
.
(add-to-list 'load-path "/path/to/llm-tool-collection/")
(require 'llm-tool-collection)
Quick and dirty: register every tool.
;; For gptel:
(mapcar (apply-partially #'apply #'gptel-make-tool)
(llm-tool-collection-get-all))
;; For llm:
(mapcar (apply-partially #'apply #'llm-make-tool)
(llm-tool-collection-get-all))
Every tool is defined with a symbol llm-tc/tool-name
that has both a variable value and a function value. The variable value contains the tool specification, which can be passed to any compliant Emacs LLM client. The function value contains the function that runs the given tool. This can be instrumented or run manually.
To register just one tool:
;; For gptel:
(apply #'gptel-make-tool llm-tc/list-directory)
;; For llm:
(apply #'llm-make-tool llm-tc/list-directory)
Use llm-tool-collection-get-category
to map over a list of tools pertaining to a specific task.
;; For gptel:
(mapcar (apply-partially #'apply #'gptel-make-tool)
(llm-tool-collection-get-category "filesystem"))
;; For llm:
(mapcar (apply-partially #'apply #'llm-make-tool)
(llm-tool-collection-get-category "filesystem"))
Use llm-tool-collection-get-tag
to map over a list of tools with a specific tag.
;; For gptel:
(mapcar (apply-partially #'apply #'gptel-make-tool)
(llm-tool-collection-get-tag 'editing))
;; For llm:
(mapcar (apply-partially #'apply #'llm-make-tool)
(llm-tool-collection-get-tag 'editing))
See Tool List for a list of tool names, descriptions, and categories.
- Author
- @skissue
- Tags
- filesystem, editing
Allows the LLM to open a file and read its contents.
- Author
- @skissue
- Tags
- filesystem
Allows the LLM to list the contents of a directory.
- Author
- @skissue
- Tags
- filesystem, editing
Allows the LLM to create a new file with specified content. Returns an error if the file already exists.
- Author
- @skissue
- Tags
- filesystem
Allows the LLM to create a new directory. Returns an error if the directory already exists.
- Author
- @ultronozm
- Tags
- buffers, editing
Allows the LLM to view the contents of a buffer. The LLM can optionally specify a line offset to start from, as well as a limit on the number of lines to return.
- Author
- @ultronozm
- Tags
- buffers, editing
Allows the LLM to edit a buffer by replacing a search string with a replacement string. Returns an error if the text to replace is not found or is found multiple times.
Contributions to this project are welcome and encouraged! After all, this collection can’t be crowdsourced if there’s no crowd 🙃.
To write a new tool, use the llm-tool-collection-deftool
macro. For details on its usage, see its docstring as well as the existing tools.
(llm-tool-collection-deftool read-file ; Tool name
;; Specs
(:category "filesystem" :tags (filesystem editing) :confirm t :include t)
;; Arguments, with LLM-friendly documentation and types
((path "Path to the file to read. Supports relative paths and '~'."
:type string))
;; LLM-friendly tool documentation
"Read the contents of a file and return its content as a string."
;; Implementation body
(with-temp-buffer
(insert-file-contents (expand-file-name path))
(buffer-string)))
It’s highly recommended to include :tags
, as well as appropriate values for the :confirm
and :include
parameters, depending on how dangerous the tool may be. Additionally, docstrings should be as LLM-friendly; consider instructing models on when to call a tool, and what tools it may want to chain together.
After defining a tool, make sure to add it to the README! Use the existing documentation structure as an example.
For non-trivial/complex tools, it’s recommended to include a short screencast or demo of the tool in action. If able to test, also consider adding a note on which models tend to perform the best with the tool.
When ready, submit a PR!
There will likely be many iterations necessary to get a tool to a good state. To speed up the feedback loop, functions to immediately update the tools in an LLM interface can be added to llm-tool-collection-post-define-functions
. For example, to immediately add (or re-add) a tool to gptel upon re-evaluating the definition:
(defun llm-tool-collection-register-with-gptel (tool-spec)
"Register a tool defined by TOOL-SPEC with gptel.
TOOL-SPEC is a plist that can be passed to `gptel-make-tool'."
(let ((tool (apply #'gptel-make-tool tool-spec)))
(setq gptel-tools
(cons tool (seq-remove
(lambda (existing)
(string= (gptel-tool-name existing)
(gptel-tool-name tool)))
gptel-tools)))))
(add-hook 'llm-tool-collection-post-define-functions
#'llm-tool-collection-register-with-gptel)