Add describe-{variable|function}

This commit is contained in:
Phil Bajsicki 2025-04-15 19:36:19 +02:00
parent 9174b29437
commit f7b3de3789
2 changed files with 191 additions and 71 deletions

View file

@ -118,13 +118,12 @@ Stuff, headers, etc.
;; All documentation regarding these functions is in the README.org file. ;; All documentation regarding these functions is in the README.org file.
;; Repository: https://git.bajsicki.com/phil/gptel-org-tools ;; Repository: https://git.bajsicki.com/phil/gptel-org-tools
;;; Code: ;;; Code:)
#+end_src #+end_src
** The tools ** The tools
*** Emacs *** Emacs
These tools are primarily concerned with Emacs, Emacs Lisp, and files-or-buffers. These tools are primarily concerned with Emacs, Emacs Lisp, and files-or-buffers.
**** eval **** eval
Dangerous, but occasionally useful for pure chaos and amusement... Dangerous, but occasionally useful for pure chaos and amusement...
I would like to say. But in actuality, especially with the 'smarter' models, they can surprise with the varied approaches they have to problem-solving. I would like to say. But in actuality, especially with the 'smarter' models, they can surprise with the varied approaches they have to problem-solving.
@ -134,6 +133,7 @@ E.g. can't find the right ~org-ql-select~ query? Let's use an eldritch abominati
Highly not recommended, but sometimes an LLM can pull a rabbit out of pure entropy. Highly not recommended, but sometimes an LLM can pull a rabbit out of pure entropy.
#+begin_src elisp :tangle no #+begin_src elisp :tangle no
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (elisp) :function (lambda (elisp)
(unless (stringp elisp) (error "elisp code must be a string")) (unless (stringp elisp) (error "elisp code must be a string"))
@ -145,7 +145,7 @@ Highly not recommended, but sometimes an LLM can pull a rabbit out of pure entro
:args (list '(:name "eval" :args (list '(:name "eval"
:type string :type string
:description "The Emacs Lisp code to evaluate.")) :description "The Emacs Lisp code to evaluate."))
:category "emacs") :category "emacs"))
#+end_src #+end_src
**** list-buffers **** list-buffers
@ -155,6 +155,7 @@ The rationale behind using ~ibuffer~ is the same as with dired. They both displa
Seems to be one of the most reliable tools in the basket... mostly because Seems to be one of the most reliable tools in the basket... mostly because
#+begin_src elisp #+begin_src elisp
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (arg) :function (lambda (arg)
(with-temp-buffer (with-temp-buffer
@ -168,7 +169,7 @@ Seems to be one of the most reliable tools in the basket... mostly because
:type string :type string
:description "Does nothing." :description "Does nothing."
:optional t)) :optional t))
:category "emacs") :category "emacs"))
#+end_src #+end_src
**** dired **** dired
See above, same reasoning. There's very little reason to use the ~directory-files~ function for this (as in another tool I saw.) The reason is, ~directory-files~ doesn't provide nearly as much information about the items in that directory. Not even a distinction between files and directories. See above, same reasoning. There's very little reason to use the ~directory-files~ function for this (as in another tool I saw.) The reason is, ~directory-files~ doesn't provide nearly as much information about the items in that directory. Not even a distinction between files and directories.
@ -180,6 +181,7 @@ Be sure to customize the function to point to your org directory, if you wish. I
#+end_comment #+end_comment
#+begin_src elisp #+begin_src elisp
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (dir) :function (lambda (dir)
(with-temp-buffer (with-temp-buffer
@ -193,11 +195,12 @@ Be sure to customize the function to point to your org directory, if you wish. I
:type string :type string
:description "Directory path" :description "Directory path"
:optional t)) :optional t))
:category "filesystem") :category "filesystem"))
#+end_src #+end_src
**** find-buffer-visiting **** find-buffer-visiting
#+begin_src elisp #+begin_src elisp
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (filename) :function (lambda (filename)
(bufferp (find-buffer-visiting (expand-file-name filename)))) (bufferp (find-buffer-visiting (expand-file-name filename))))
@ -206,11 +209,12 @@ Be sure to customize the function to point to your org directory, if you wish. I
:args (list '(:name "filename" :args (list '(:name "filename"
:type string :type string
:description "The filename to compare to open buffers.")) :description "The filename to compare to open buffers."))
:category "org-mode") :category "org-mode"))
#+end_src #+end_src
**** find-file-noselect **** find-file-noselect
Continuation from above. Open a file into a buffer for processing. Onec it's found by dired-list. Continuation from above. Open a file into a buffer for processing. Onec it's found by dired-list.
#+begin_src elisp #+begin_src elisp
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (file) :function (lambda (file)
(find-file-noselect file)) (find-file-noselect file))
@ -219,7 +223,57 @@ Continuation from above. Open a file into a buffer for processing. Onec it's fou
:args (list '(:name "file" :args (list '(:name "file"
:type string :type string
:description "Path to file..")) :description "Path to file.."))
:category "filesystem") :category "filesystem"))
#+end_src
**** read-file-contents
This reads file contents,
#+begin_src elisp
(add-to-list 'gptel-tools
(gptel-make-tool
:function (lambda (filename)
(with-temp-buffer
(insert-file-contents (expand-file-name filename))
(buffer-string)))
:name "read-file-contents"
:description "Read and return the contents of a specified file."
:args (list '(:name "filename"
:type string
:description "The filename to read."))
:category "org-mode"))
#+end_src
**** describe-variable
#+begin_src elisp
(add-to-list 'gptel-tools
(gptel-make-tool
:function (lambda (var)
(let ((symbol (intern var)))
(if (boundp symbol)
(prin1-to-string (symbol-value symbol))
(format "Variable %s is not bound." var))))
:name "describe-variable"
:description "See variable contents"
:args (list '(:name "var"
:type string
:description "Variable name"))
:category "emacs"))
#+end_src
**** describe-function
#+begin_src elisp
(add-to-list 'gptel-tools
(gptel-make-tool
:function (lambda (fun)
(let ((symbol (intern fun)))
(if (fboundp symbol)
(prin1-to-string (documentation symbol 'function))
(format "Function %s is not defined." fun))))
:name "describe-function"
:description "See function description"
:args (list '(:name "fun"
:type string
:description "Function name"
:optional t))
:category "emacs"))
#+end_src #+end_src
*** Org-mode *** Org-mode
@ -252,7 +306,7 @@ Pretty simple, does what it says on the tin. It gets all the tags from the =buff
This is not, by any means, sufficient, but I do tag people and specific events frequently enough that it helps save on the context window. This is not, by any means, sufficient, but I do tag people and specific events frequently enough that it helps save on the context window.
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools-org-extract-tags (buffer) (defun gptel-org-tools--org-extract-tags (buffer)
(interactive "bBuffer: ") (interactive "bBuffer: ")
(with-current-buffer buffer (with-current-buffer buffer
(let ((tags '())) (let ((tags '()))
@ -265,14 +319,15 @@ This is not, by any means, sufficient, but I do tag people and specific events f
(push tag tags)))))) (push tag tags))))))
(sort (-uniq tags) #'string<)))) (sort (-uniq tags) #'string<))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-extract-tags :function #'gptel-org-tools--org-extract-tags
:name "org-extract-tags" :name "org-extract-tags"
:description "Extract all unique tags from an org-mode buffer" :description "Extract all unique tags from an org-mode buffer"
:args (list '(:name "buffer" :args (list '(:name "buffer"
:type string :type string
:description "The Org buffer to extract tags from.")) :description "The Org buffer to extract tags from."))
:category "org-mode") :category "org-mode"))
#+end_src #+end_src
**** org-extract-headings **** org-extract-headings
But what if there's no tags related to the topic? But what if there's no tags related to the topic?
@ -281,7 +336,7 @@ Then we need to pull /some/ information from the buffer, without dragging the en
Therefore, headings. A reasonable amount of information, and still keeping the signal-to-noise ratio pretty decent. Therefore, headings. A reasonable amount of information, and still keeping the signal-to-noise ratio pretty decent.
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools-org-extract-headings (buffer) (defun gptel-org-tools--org-extract-headings (buffer)
(interactive "bBuffer: ") (interactive "bBuffer: ")
(with-current-buffer buffer (with-current-buffer buffer
(org-map-entries (org-map-entries
@ -291,14 +346,15 @@ Therefore, headings. A reasonable amount of information, and still keeping the s
t t
'file))) 'file)))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-extract-headings :function #'gptel-org-tools--org-extract-headings
:name "org-extract-headings" :name "org-extract-headings"
:description "Extract all headings from an org-mode buffer" :description "Extract all headings from an org-mode buffer"
:args (list '(:name "buffer" :args (list '(:name "buffer"
:type string :type string
:description "The Org buffer to extract headings from.")) :description "The Org buffer to extract headings from."))
:category "org-mode") :category "org-mode"))
#+end_src #+end_src
**** org-ql-select **** org-ql-select
@ -311,7 +367,7 @@ My current goal is to replace this monstrosity with individual functions for eac
But in the interim, this works. Kind of. But in the interim, this works. Kind of.
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools-org-ql-select (buf query) (defun gptel-org-tools--org-ql-select (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
(if (stringp query) (if (stringp query)
@ -325,8 +381,9 @@ But in the interim, this works. Kind of.
(outline-next-heading) (outline-next-heading)
(line-beginning-position))))))) (line-beginning-position)))))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select :function #'gptel-org-tools--org-ql-select
:name "org-ql-select" :name "org-ql-select"
:description "Run org-ql-select against buffer with query. Using filename fails." :description "Run org-ql-select against buffer with query. Using filename fails."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -335,7 +392,7 @@ But in the interim, this works. Kind of.
'(:name "query" '(:name "query"
:type string :type string
:description "The query to pass into org-ql-select. See org-ql documentation for syntax. Usually `(tags \"tag1\" \"tag2\")` is sufficient. Possible predicates: `tags` (finds both local and inherited tags), `tags-local` (finds only local tags), `rifle` (matches against both heading and body text). This is a sexp, not a string.")) :description "The query to pass into org-ql-select. See org-ql documentation for syntax. Usually `(tags \"tag1\" \"tag2\")` is sufficient. Possible predicates: `tags` (finds both local and inherited tags), `tags-local` (finds only local tags), `rifle` (matches against both heading and body text). This is a sexp, not a string."))
:category "org") :category "org"))
#+end_src #+end_src
**** Somewhat working tools **** Somewhat working tools
***** org-ql-select-dates ***** org-ql-select-dates
@ -344,7 +401,7 @@ My journal is a single file, with a hierarchy like so:
#+begin_src org :tangle no #+begin_src org :tangle no
,* [YYYY] ,* [YYYY]
,** [YYYY-MM] ,** [YYYY-MM]
,*** [YYYY-MM-DD Day HH:MM] ,*** [YYYY-MM-DD Day HH:MM])
#+end_src #+end_src
I wanted to make sure that if I asked about a time period, the LLM would be able to pull at least roughly around the right time from my journal, without blowing out its context window. I wanted to make sure that if I asked about a time period, the LLM would be able to pull at least roughly around the right time from my journal, without blowing out its context window.
@ -359,7 +416,7 @@ But, any customizations to tweak this is left to the user, as everyone has their
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools-org-ql-select-dates (buf date) (defun gptel-org-tools--org-ql-select-dates (buf date)
(interactive "fBuffer: \nsDate (YYYY or YYYY-MM): ") (interactive "fBuffer: \nsDate (YYYY or YYYY-MM): ")
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
@ -370,8 +427,9 @@ But, any customizations to tweak this is left to the user, as everyone has their
(org-end-of-subtree))))) (org-end-of-subtree)))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select-dates :function #'gptel-org-tools--org-ql-select-dates
:name "org-ql-select-dates" :name "org-ql-select-dates"
:description "Extract org subtree by date in YYYY or YYYY-MM format" :description "Extract org subtree by date in YYYY or YYYY-MM format"
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -380,13 +438,14 @@ But, any customizations to tweak this is left to the user, as everyone has their
'(:name "date" '(:name "date"
:type string :type string
:description "Date in YYYY or YYYY-MM format. Can add multiple like so: \"YYYY-MM\" \"YYYY-MM\"")) :description "Date in YYYY or YYYY-MM format. Can add multiple like so: \"YYYY-MM\" \"YYYY-MM\""))
:category "org") :category "org"))
#+end_src #+end_src
***** org-agenda-fortnight ***** org-agenda-fortnight
This is still work in progress, the idea is to have the LLM check my calendar and see what my plans are. I have not had time to really dig into this yet. This is still work in progress, the idea is to have the LLM check my calendar and see what my plans are. I have not had time to really dig into this yet.
It works, in principle, but I haven't been able to find a use for it yet. The real challenge is in building a context where the tools integrate with each-other in a way that makes sense. For now, this exists. It works, in principle, but I haven't been able to find a use for it yet. The real challenge is in building a context where the tools integrate with each-other in a way that makes sense. For now, this exists.
#+begin_src elisp #+begin_src elisp
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (days) :function (lambda (days)
(with-temp-buffer (with-temp-buffer
@ -399,7 +458,7 @@ It works, in principle, but I haven't been able to find a use for it yet. The re
:args (list '(:name "days" :args (list '(:name "days"
:type integer :type integer
:description "The number of days to look ahead. Default: 14")) :description "The number of days to look ahead. Default: 14"))
:category "org") :category "org"))
#+end_src #+end_src
**** Completely WIP tools **** Completely WIP tools
The following tools are still very much WIP, and I think they're self-explanatory enough. They have /NOT/ been tested in any way, shape, form, or capacity. The following tools are still very much WIP, and I think they're self-explanatory enough. They have /NOT/ been tested in any way, shape, form, or capacity.
@ -409,7 +468,7 @@ The following tools are still very much WIP, and I think they're self-explanator
***** org-ql-select-headings ***** org-ql-select-headings
Retrieve the headings where the heading matches query.. Retrieve the headings where the heading matches query..
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools-org-ql-select-headings (buf query) (defun gptel-org-tools--org-ql-select-headings (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(heading ,query) `(heading ,query)
@ -420,8 +479,9 @@ Retrieve the headings where the heading matches query..
(line-end-position)))))) (line-end-position))))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select-headings :function #'gptel-org-tools--org-ql-select-headings
:name "org-ql-select-headings" :name "org-ql-select-headings"
:description "Retreive matching headings from buffer using org-ql-select. Matches only against heading. Using filename fails." :description "Retreive matching headings from buffer using org-ql-select. Matches only against heading. Using filename fails."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -430,13 +490,13 @@ Retrieve the headings where the heading matches query..
'(:name "query" '(:name "query"
:type string :type string
:description "The string to pass into org-ql-select-headings. This is a bare string. Example: \"searchterm\"")) :description "The string to pass into org-ql-select-headings. This is a bare string. Example: \"searchterm\""))
:category "org-ql") :category "org-ql"))
#+end_src #+end_src
***** org-ql-select-headings-rifle ***** org-ql-select-headings-rifle
Retrieve all the headings where either heading or content matches query. Retrieve all the headings where either heading or content matches query.
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools-org-ql-select-headings-rifle (buf query) (defun gptel-org-tools--org-ql-select-headings-rifle (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(rifle ,query) `(rifle ,query)
@ -447,8 +507,9 @@ Retrieve all the headings where either heading or content matches query.
(line-end-position)))))) (line-end-position))))))
(gptel-make-tool (add-to-list 'gptel-tools
:function #'gptel-org-tools-org-ql-select-headings-rifle (gptel-make-tool
:function #'gptel-org-tools--org-ql-select-headings-rifle
:name "org-ql-select-headings-rifle" :name "org-ql-select-headings-rifle"
:description "Retreive headings from buffer using org-ql-select. Matches against both heading and content. Using filename fails." :description "Retreive headings from buffer using org-ql-select. Matches against both heading and content. Using filename fails."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -457,13 +518,13 @@ Retrieve all the headings where either heading or content matches query.
'(:name "query" '(:name "query"
:type string :type string
:description "The string to pass into org-ql-select-headings-rifle. This is a bare string. Example: \"searchterm\"")) :description "The string to pass into org-ql-select-headings-rifle. This is a bare string. Example: \"searchterm\""))
:category "org-ql") :category "org-ql"))
#+end_src #+end_src
***** org-ql-select-tags-local ***** org-ql-select-tags-local
This pulls all the headings (and their contents) when they match tags (without inheritance.) This pulls all the headings (and their contents) when they match tags (without inheritance.)
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools-org-ql-select-tags-local (buf query) (defun gptel-org-tools--org-ql-select-tags-local (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(tags-local ,query) `(tags-local ,query)
@ -476,8 +537,9 @@ This pulls all the headings (and their contents) when they match tags (without i
(line-beginning-position))))))) (line-beginning-position)))))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select-tags-local :function #'gptel-org-tools--org-ql-select-tags-local
:name "org-ql-select-tags-local" :name "org-ql-select-tags-local"
:description "Run org-ql-select-tags-local against buffer with query. No tag inheritance." :description "Run org-ql-select-tags-local against buffer with query. No tag inheritance."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -486,13 +548,13 @@ This pulls all the headings (and their contents) when they match tags (without i
'(:name "query" '(:name "query"
:type string :type string
:description "The tags to match entry headings against. Example: \"tag1\" \"tag2\"")) :description "The tags to match entry headings against. Example: \"tag1\" \"tag2\""))
:category "org-ql") :category "org-ql"))
#+end_src #+end_src
***** org-ql-select-tags ***** org-ql-select-tags
This pulls all the headings (and their contents) when they match tags (with inheritance; if a parent entry has the tag, descendant entries do, too.) This pulls all the headings (and their contents) when they match tags (with inheritance; if a parent entry has the tag, descendant entries do, too.)
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools-org-ql-select-tags (buf query) (defun gptel-org-tools--org-ql-select-tags (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(tags ,query) `(tags ,query)
@ -505,8 +567,9 @@ This pulls all the headings (and their contents) when they match tags (with inhe
(line-beginning-position))))))) (line-beginning-position)))))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select-tags :function #'gptel-org-tools--org-ql-select-tags
:name "org-ql-select-tags" :name "org-ql-select-tags"
:description "Run org-ql-select-tags against buffer with query. Supports tag inheritance." :description "Run org-ql-select-tags against buffer with query. Supports tag inheritance."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -515,13 +578,13 @@ This pulls all the headings (and their contents) when they match tags (with inhe
'(:name "query" '(:name "query"
:type string :type string
:description "The tags to match entry headings against. Example: \"tag1\" \"tag2\"")) :description "The tags to match entry headings against. Example: \"tag1\" \"tag2\""))
:category "org-ql") :category "org-ql"))
#+end_src #+end_src
***** org-ql-select-rifle ***** org-ql-select-rifle
And, the "grab everything that matches" tool. And, the "grab everything that matches" tool.
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools-org-ql-select-rifle (buf query) (defun gptel-org-tools--org-ql-select-rifle (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(rifle ,query) `(rifle ,query)
@ -533,8 +596,9 @@ And, the "grab everything that matches" tool.
(outline-next-heading) (outline-next-heading)
(line-beginning-position))))))) (line-beginning-position)))))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select-rifle :function #'gptel-org-tools--org-ql-select-rifle
:name "org-ql-select-rifle" :name "org-ql-select-rifle"
:description "Run org-ql-select-rifle against buffer with query." :description "Run org-ql-select-rifle against buffer with query."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -543,7 +607,7 @@ And, the "grab everything that matches" tool.
'(:name "query" '(:name "query"
:type string :type string
:description "The strings to match entry headings and content against. Example: \"tag1\" \"tag2\"")) :description "The strings to match entry headings and content against. Example: \"tag1\" \"tag2\""))
:category "org-ql") :category "org-ql"))
#+end_src #+end_src

View file

@ -32,8 +32,9 @@
;; All documentation regarding these functions is in the README.org file. ;; All documentation regarding these functions is in the README.org file.
;; Repository: https://git.bajsicki.com/phil/gptel-org-tools ;; Repository: https://git.bajsicki.com/phil/gptel-org-tools
;;; Code: ;;; Code:)
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (arg) :function (lambda (arg)
(with-temp-buffer (with-temp-buffer
@ -47,8 +48,9 @@
:type string :type string
:description "Does nothing." :description "Does nothing."
:optional t)) :optional t))
:category "emacs") :category "emacs"))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (dir) :function (lambda (dir)
(with-temp-buffer (with-temp-buffer
@ -62,8 +64,9 @@
:type string :type string
:description "Directory path" :description "Directory path"
:optional t)) :optional t))
:category "filesystem") :category "filesystem"))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (filename) :function (lambda (filename)
(bufferp (find-buffer-visiting (expand-file-name filename)))) (bufferp (find-buffer-visiting (expand-file-name filename))))
@ -72,8 +75,9 @@
:args (list '(:name "filename" :args (list '(:name "filename"
:type string :type string
:description "The filename to compare to open buffers.")) :description "The filename to compare to open buffers."))
:category "org-mode") :category "org-mode"))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (file) :function (lambda (file)
(find-file-noselect file)) (find-file-noselect file))
@ -82,9 +86,51 @@
:args (list '(:name "file" :args (list '(:name "file"
:type string :type string
:description "Path to file..")) :description "Path to file.."))
:category "filesystem") :category "filesystem"))
(defun gptel-org-tools-org-extract-tags (buffer) (add-to-list 'gptel-tools
(gptel-make-tool
:function (lambda (filename)
(with-temp-buffer
(insert-file-contents (expand-file-name filename))
(buffer-string)))
:name "read-file-contents"
:description "Read and return the contents of a specified file."
:args (list '(:name "filename"
:type string
:description "The filename to read."))
:category "org-mode"))
(add-to-list 'gptel-tools
(gptel-make-tool
:function (lambda (var)
(let ((symbol (intern var)))
(if (boundp symbol)
(prin1-to-string (symbol-value symbol))
(format "Variable %s is not bound." var))))
:name "describe-variable"
:description "See variable contents"
:args (list '(:name "var"
:type string
:description "Variable name"))
:category "emacs"))
(add-to-list 'gptel-tools
(gptel-make-tool
:function (lambda (fun)
(let ((symbol (intern fun)))
(if (fboundp symbol)
(prin1-to-string (documentation symbol 'function))
(format "Function %s is not defined." fun))))
:name "describe-function"
:description "See function description"
:args (list '(:name "fun"
:type string
:description "Function name"
:optional t))
:category "emacs"))
(defun gptel-org-tools--org-extract-tags (buffer)
(interactive "bBuffer: ") (interactive "bBuffer: ")
(with-current-buffer buffer (with-current-buffer buffer
(let ((tags '())) (let ((tags '()))
@ -97,16 +143,17 @@
(push tag tags)))))) (push tag tags))))))
(sort (-uniq tags) #'string<)))) (sort (-uniq tags) #'string<))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-extract-tags :function #'gptel-org-tools--org-extract-tags
:name "org-extract-tags" :name "org-extract-tags"
:description "Extract all unique tags from an org-mode buffer" :description "Extract all unique tags from an org-mode buffer"
:args (list '(:name "buffer" :args (list '(:name "buffer"
:type string :type string
:description "The Org buffer to extract tags from.")) :description "The Org buffer to extract tags from."))
:category "org-mode") :category "org-mode"))
(defun gptel-org-tools-org-extract-headings (buffer) (defun gptel-org-tools--org-extract-headings (buffer)
(interactive "bBuffer: ") (interactive "bBuffer: ")
(with-current-buffer buffer (with-current-buffer buffer
(org-map-entries (org-map-entries
@ -116,16 +163,17 @@
t t
'file))) 'file)))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-extract-headings :function #'gptel-org-tools--org-extract-headings
:name "org-extract-headings" :name "org-extract-headings"
:description "Extract all headings from an org-mode buffer" :description "Extract all headings from an org-mode buffer"
:args (list '(:name "buffer" :args (list '(:name "buffer"
:type string :type string
:description "The Org buffer to extract headings from.")) :description "The Org buffer to extract headings from."))
:category "org-mode") :category "org-mode"))
(defun gptel-org-tools-org-ql-select (buf query) (defun gptel-org-tools--org-ql-select (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
(if (stringp query) (if (stringp query)
@ -139,8 +187,9 @@
(outline-next-heading) (outline-next-heading)
(line-beginning-position))))))) (line-beginning-position)))))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select :function #'gptel-org-tools--org-ql-select
:name "org-ql-select" :name "org-ql-select"
:description "Run org-ql-select against buffer with query. Using filename fails." :description "Run org-ql-select against buffer with query. Using filename fails."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -149,9 +198,9 @@
'(:name "query" '(:name "query"
:type string :type string
:description "The query to pass into org-ql-select. See org-ql documentation for syntax. Usually `(tags \"tag1\" \"tag2\")` is sufficient. Possible predicates: `tags` (finds both local and inherited tags), `tags-local` (finds only local tags), `rifle` (matches against both heading and body text). This is a sexp, not a string.")) :description "The query to pass into org-ql-select. See org-ql documentation for syntax. Usually `(tags \"tag1\" \"tag2\")` is sufficient. Possible predicates: `tags` (finds both local and inherited tags), `tags-local` (finds only local tags), `rifle` (matches against both heading and body text). This is a sexp, not a string."))
:category "org") :category "org"))
(defun gptel-org-tools-org-ql-select-dates (buf date) (defun gptel-org-tools--org-ql-select-dates (buf date)
(interactive "fBuffer: \nsDate (YYYY or YYYY-MM): ") (interactive "fBuffer: \nsDate (YYYY or YYYY-MM): ")
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
@ -162,8 +211,9 @@
(org-end-of-subtree))))) (org-end-of-subtree)))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select-dates :function #'gptel-org-tools--org-ql-select-dates
:name "org-ql-select-dates" :name "org-ql-select-dates"
:description "Extract org subtree by date in YYYY or YYYY-MM format" :description "Extract org subtree by date in YYYY or YYYY-MM format"
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -172,8 +222,9 @@
'(:name "date" '(:name "date"
:type string :type string
:description "Date in YYYY or YYYY-MM format. Can add multiple like so: \"YYYY-MM\" \"YYYY-MM\"")) :description "Date in YYYY or YYYY-MM format. Can add multiple like so: \"YYYY-MM\" \"YYYY-MM\""))
:category "org") :category "org"))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (days) :function (lambda (days)
(with-temp-buffer (with-temp-buffer
@ -186,9 +237,9 @@
:args (list '(:name "days" :args (list '(:name "days"
:type integer :type integer
:description "The number of days to look ahead. Default: 14")) :description "The number of days to look ahead. Default: 14"))
:category "org") :category "org"))
(defun gptel-org-tools-org-ql-select-headings (buf query) (defun gptel-org-tools--org-ql-select-headings (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(heading ,query) `(heading ,query)
@ -199,8 +250,9 @@
(line-end-position)))))) (line-end-position))))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select-headings :function #'gptel-org-tools--org-ql-select-headings
:name "org-ql-select-headings" :name "org-ql-select-headings"
:description "Retreive matching headings from buffer using org-ql-select. Matches only against heading. Using filename fails." :description "Retreive matching headings from buffer using org-ql-select. Matches only against heading. Using filename fails."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -209,9 +261,9 @@
'(:name "query" '(:name "query"
:type string :type string
:description "The string to pass into org-ql-select-headings. This is a bare string. Example: \"searchterm\"")) :description "The string to pass into org-ql-select-headings. This is a bare string. Example: \"searchterm\""))
:category "org-ql") :category "org-ql"))
(defun gptel-org-tools-org-ql-select-headings-rifle (buf query) (defun gptel-org-tools--org-ql-select-headings-rifle (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(rifle ,query) `(rifle ,query)
@ -222,8 +274,9 @@
(line-end-position)))))) (line-end-position))))))
(gptel-make-tool (add-to-list 'gptel-tools
:function #'gptel-org-tools-org-ql-select-headings-rifle (gptel-make-tool
:function #'gptel-org-tools--org-ql-select-headings-rifle
:name "org-ql-select-headings-rifle" :name "org-ql-select-headings-rifle"
:description "Retreive headings from buffer using org-ql-select. Matches against both heading and content. Using filename fails." :description "Retreive headings from buffer using org-ql-select. Matches against both heading and content. Using filename fails."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -232,9 +285,9 @@
'(:name "query" '(:name "query"
:type string :type string
:description "The string to pass into org-ql-select-headings-rifle. This is a bare string. Example: \"searchterm\"")) :description "The string to pass into org-ql-select-headings-rifle. This is a bare string. Example: \"searchterm\""))
:category "org-ql") :category "org-ql"))
(defun gptel-org-tools-org-ql-select-tags-local (buf query) (defun gptel-org-tools--org-ql-select-tags-local (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(tags-local ,query) `(tags-local ,query)
@ -247,8 +300,9 @@
(line-beginning-position))))))) (line-beginning-position)))))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select-tags-local :function #'gptel-org-tools--org-ql-select-tags-local
:name "org-ql-select-tags-local" :name "org-ql-select-tags-local"
:description "Run org-ql-select-tags-local against buffer with query. No tag inheritance." :description "Run org-ql-select-tags-local against buffer with query. No tag inheritance."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -257,9 +311,9 @@
'(:name "query" '(:name "query"
:type string :type string
:description "The tags to match entry headings against. Example: \"tag1\" \"tag2\"")) :description "The tags to match entry headings against. Example: \"tag1\" \"tag2\""))
:category "org-ql") :category "org-ql"))
(defun gptel-org-tools-org-ql-select-tags (buf query) (defun gptel-org-tools--org-ql-select-tags (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(tags ,query) `(tags ,query)
@ -272,8 +326,9 @@
(line-beginning-position))))))) (line-beginning-position)))))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select-tags :function #'gptel-org-tools--org-ql-select-tags
:name "org-ql-select-tags" :name "org-ql-select-tags"
:description "Run org-ql-select-tags against buffer with query. Supports tag inheritance." :description "Run org-ql-select-tags against buffer with query. Supports tag inheritance."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -282,9 +337,9 @@
'(:name "query" '(:name "query"
:type string :type string
:description "The tags to match entry headings against. Example: \"tag1\" \"tag2\"")) :description "The tags to match entry headings against. Example: \"tag1\" \"tag2\""))
:category "org-ql") :category "org-ql"))
(defun gptel-org-tools-org-ql-select-rifle (buf query) (defun gptel-org-tools--org-ql-select-rifle (buf query)
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(rifle ,query) `(rifle ,query)
@ -296,8 +351,9 @@
(outline-next-heading) (outline-next-heading)
(line-beginning-position))))))) (line-beginning-position)))))))
(add-to-list 'gptel-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools-org-ql-select-rifle :function #'gptel-org-tools--org-ql-select-rifle
:name "org-ql-select-rifle" :name "org-ql-select-rifle"
:description "Run org-ql-select-rifle against buffer with query." :description "Run org-ql-select-rifle against buffer with query."
:args (list '(:name "buffer" :args (list '(:name "buffer"
@ -306,7 +362,7 @@
'(:name "query" '(:name "query"
:type string :type string
:description "The strings to match entry headings and content against. Example: \"tag1\" \"tag2\"")) :description "The strings to match entry headings and content against. Example: \"tag1\" \"tag2\""))
:category "org-ql") :category "org-ql"))
(provide 'gptel-org-tools) (provide 'gptel-org-tools)
;;; gptel-org-tools.el ends here ;;; gptel-org-tools.el ends here