diff --git a/README.org b/README.org index af3b63b..9eebfb8 100644 --- a/README.org +++ b/README.org @@ -76,9 +76,6 @@ I change models a lot, and this /just works/ for most models, even if some aren' (require 'gptel) #+end_src -#+RESULTS: -: gptel - With that out of the way, let's get to the tools. * Usage options: I only use Doom Emacs, so here's how I do it. @@ -168,7 +165,40 @@ Collects into =gptel-org-tools= list, distinct from =gptel-tools= #+begin_src elisp (defvar gptel-org-tools '()) #+end_src +** Helper Functions +These abstract away some of the tool definitions. +Both of these clear the org-ql-cache to work around issues where a file may be updated between tool calls. +*** Retrieve heading and body (without subheadings) +#+begin_src elisp +(defun gptel-org-tools--heading-body () + (concat + (buffer-substring-no-properties + (line-beginning-position) + (progn + (outline-next-heading) + (line-beginning-position))) + "---\n")) +#+end_src + +*** Retrieve heading and subheadings (until next same-level heading) +#+begin_src elisp +(defun gptel-org-tools--heading-subtree () + (concat + (buffer-substring-no-properties + (line-beginning-position) + (org-end-of-subtree)) + "---\n")) +#+end_src +** Note on org-ql +Given that there isn't (yet?) a built-in way of disabling caching, every (org-ql-select) call is wrapped like so. + +See [[https://github.com/alphapapa/org-ql/issues/437][this issue]] for details. + +#+begin_src elisp :tangle no + (let ((org-ql-cache (make-hash-table))) + (org-ql-select ...)) +#+end_src ** The tools *** Emacs These tools are primarily concerned with Emacs, Emacs Lisp, and files-or-buffers. @@ -430,13 +460,7 @@ Currently *not* tangled, as I'm testing breaking out each type of query into its (if (stringp query) (read query) query) - :action #'(lambda () - (concat - (buffer-substring-no-properties - (line-beginning-position) - (progn - (outline-next-heading) - (line-beginning-position))))))) + :action #'gptel-org-tools--heading-body (add-to-list 'gptel-org-tools (gptel-make-tool @@ -477,10 +501,7 @@ But, any customizations to tweak this is left to the user, as everyone has their (org-ql-select (get-buffer buf) `(heading ,date) - :action #'(lambda () - (buffer-substring-no-properties - (line-beginning-position) - (org-end-of-subtree))))) + :action #'gptel-org-tools--heading-subtree)) (add-to-list 'gptel-org-tools @@ -525,14 +546,15 @@ The following tools are still very much WIP, and I think they're self-explanator Retrieve the headings where the heading matches query.. #+begin_src elisp (defun gptel-org-tools--org-ql-select-headings (buf query) - (org-ql-select + (let ((org-ql-cache (make-hash-table))) + (org-ql-select (get-buffer buf) `(heading ,query) :action #'(lambda () (concat (buffer-substring-no-properties (line-beginning-position) - (line-end-position)))))) + (line-end-position))))))) (add-to-list 'gptel-org-tools @@ -545,7 +567,7 @@ Retrieve the headings where the heading matches query.. :description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.") '(:name "query" :type string - :description "The string to pass into org-ql-select-headings. This is a bare string. Example: \"searchterm\"")) + :description "The string to match entry headings against.")) :category "org-ql")) #+end_src @@ -553,14 +575,15 @@ Retrieve the headings where the heading matches query.. Retrieve all the headings where either heading or content matches query. #+begin_src elisp (defun gptel-org-tools--org-ql-select-headings-rifle (buf query) - (org-ql-select + (let ((org-ql-cache (make-hash-table))) + (org-ql-select (get-buffer buf) `(rifle ,query) :action #'(lambda () (concat (buffer-substring-no-properties (line-beginning-position) - (line-end-position)))))) + (line-end-position))))))) (add-to-list 'gptel-org-tools @@ -573,7 +596,7 @@ Retrieve all the headings where either heading or content matches query. :description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.") '(:name "query" :type string - :description "The string to pass into org-ql-select-headings-rifle. This is a bare string. Example: \"searchterm\"")) + :description "The string to match entry headings against.")) :category "org-ql")) #+end_src @@ -581,7 +604,8 @@ Retrieve all the headings where either heading or content matches query. This pulls all the headings (and their contents) when they match tags (without inheritance.) #+begin_src elisp (defun gptel-org-tools--org-ql-select-tags-local (buf query) - (org-ql-select + (let ((org-ql-cache (make-hash-table))) + (org-ql-select (get-buffer buf) `(tags-local ,query) :action #'(lambda () @@ -590,7 +614,7 @@ This pulls all the headings (and their contents) when they match tags (without i (line-beginning-position) (progn (outline-next-heading) - (line-beginning-position))))))) + (line-beginning-position)))))))) (add-to-list 'gptel-org-tools @@ -603,7 +627,7 @@ This pulls all the headings (and their contents) when they match tags (without i :description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.") '(:name "query" :type string - :description "The tags to match entry headings against. Example: \"tag1\" \"tag2\"")) + :description "The string match entry tags against.")) :category "org-ql")) #+end_src @@ -611,7 +635,8 @@ This pulls all the headings (and their contents) when they match tags (without i 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 (defun gptel-org-tools--org-ql-select-tags (buf query) - (org-ql-select + (let ((org-ql-cache (make-hash-table))) + (org-ql-select (get-buffer buf) `(tags ,query) :action #'(lambda () @@ -620,7 +645,7 @@ This pulls all the headings (and their contents) when they match tags (with inhe (line-beginning-position) (progn (outline-next-heading) - (line-beginning-position))))))) + (line-beginning-position)))))))) (add-to-list 'gptel-org-tools @@ -633,15 +658,16 @@ This pulls all the headings (and their contents) when they match tags (with inhe :description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.") '(:name "query" :type string - :description "The tags to match entry headings against. Example: \"tag1\" \"tag2\"")) + :description "The string to match entry headings against.")) :category "org-ql")) #+end_src ***** org-ql-select-rifle And, the "grab everything that matches" tool. #+begin_src elisp -(defun gptel-org-tools--org-ql-select-rifle (buf query) - (let ((buffer (get-buffer buf))) +(defun hash-gptl-org-tools--org-ql-select-rifle () + (let ((org-ql-cache (make-hash-table)) + (buffer (get-buffer buf))) (if buffer (org-ql-select buffer @@ -665,15 +691,15 @@ And, the "grab everything that matches" tool. :description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.") '(:name "query" :type string - :description "The strings to match entry headings and content against. Example: \"tag1\" \"tag2\"")) + :description "The string to match entry headings and content against.")) :category "org-ql")) #+end_src - ***** org-ql-select-agenda-tags-local This pulls all the headings (and their contents) when they match tags (without inheritance.) #+begin_src elisp (defun gptel-org-tools--org-ql-select-agenda-tags (query) + (let ((org-ql-cache (make-hash-table))) (org-ql-select (org-agenda-files) `(tags-local ,query) @@ -683,7 +709,7 @@ This pulls all the headings (and their contents) when they match tags (without i (line-beginning-position) (progn (outline-next-heading) - (line-beginning-position))))))) + (line-beginning-position)))))))) (add-to-list 'gptel-org-tools @@ -693,7 +719,7 @@ This pulls all the headings (and their contents) when they match tags (without i :description "Run simple word query against all files in (org-agenda-files). WITHOUT tag inheritance, only directly tagged headings." :args (list '(:name "query" :type string - :description "Plain list of strings to match entry headings and content against. Example: \"tag1\" \"tag2\"")) + :description "The string to match entry tags against.")) :category "org-ql")) #+end_src @@ -701,6 +727,7 @@ This pulls all the headings (and their contents) when they match tags (without i 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 (defun gptel-org-tools--org-ql-select-agenda-tags (query) + (let ((org-ql-cache (make-hash-table))) (org-ql-select (org-agenda-files) `(tags ,query) @@ -710,7 +737,7 @@ This pulls all the headings (and their contents) when they match tags (with inhe (line-beginning-position) (progn (outline-next-heading) - (line-beginning-position))))))) + (line-beginning-position)))))))) (add-to-list 'gptel-org-tools @@ -720,7 +747,7 @@ This pulls all the headings (and their contents) when they match tags (with inhe :description "Run simple word query against all files in (org-agenda-files). WITH tag inheritance." :args (list '(:name "query" :type string - :description "Plain list of strings to match entry headings and content against. Example: \"tag1\" \"tag2\"")) + :description "The string to match entry tags against.")) :category "org-ql")) #+end_src @@ -737,6 +764,7 @@ This means that /every org-mode file I have/ is part of this search. #+begin_src elisp (defun gptel-org-tools--org-ql-select-agenda-rifle (query) + (let ((org-ql-cache (make-hash-table))) (org-ql-select (org-agenda-files) `(rifle ,query) @@ -746,7 +774,7 @@ This means that /every org-mode file I have/ is part of this search. (line-beginning-position) (progn (outline-next-heading) - (line-beginning-position))))))) + (line-beginning-position)))))))) (add-to-list 'gptel-org-tools (gptel-make-tool @@ -755,7 +783,7 @@ This means that /every org-mode file I have/ is part of this search. :description "Run simple word query against all files in (org-agenda-files)" :args (list '(:name "query" :type string - :description "Plain list of strings to match entry headings and content against. Example: \"tag1\" \"tag2\"")) + :description "The string to match entry headings and content against.")) :category "org-ql")) #+end_src diff --git a/gptel-org-tools.el b/gptel-org-tools.el index 5abd253..800c57b 100644 --- a/gptel-org-tools.el +++ b/gptel-org-tools.el @@ -38,6 +38,22 @@ (defvar gptel-org-tools '()) +(defun gptel-org-tools--heading-body () + (concat + (buffer-substring-no-properties + (line-beginning-position) + (progn + (outline-next-heading) + (line-beginning-position))) + "---\n")) + +(defun gptel-org-tools--heading-subtree () + (concat + (buffer-substring-no-properties + (line-beginning-position) + (org-end-of-subtree)) + "---\n")) + (add-to-list 'gptel-org-tools (gptel-make-tool :function (lambda (arg) @@ -167,10 +183,7 @@ (org-ql-select (get-buffer buf) `(heading ,date) - :action #'(lambda () - (buffer-substring-no-properties - (line-beginning-position) - (org-end-of-subtree))))) + :action #'gptel-org-tools--heading-subtree)) (add-to-list 'gptel-org-tools @@ -202,14 +215,15 @@ :category "org")) (defun gptel-org-tools--org-ql-select-headings (buf query) - (org-ql-select + (let ((org-ql-cache (make-hash-table))) + (org-ql-select (get-buffer buf) `(heading ,query) :action #'(lambda () (concat (buffer-substring-no-properties (line-beginning-position) - (line-end-position)))))) + (line-end-position))))))) (add-to-list 'gptel-org-tools @@ -222,18 +236,19 @@ :description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.") '(:name "query" :type string - :description "The string to pass into org-ql-select-headings. This is a bare string. Example: \"searchterm\"")) + :description "The string to match entry headings against.")) :category "org-ql")) (defun gptel-org-tools--org-ql-select-headings-rifle (buf query) - (org-ql-select + (let ((org-ql-cache (make-hash-table))) + (org-ql-select (get-buffer buf) `(rifle ,query) :action #'(lambda () (concat (buffer-substring-no-properties (line-beginning-position) - (line-end-position)))))) + (line-end-position))))))) (add-to-list 'gptel-org-tools @@ -246,11 +261,12 @@ :description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.") '(:name "query" :type string - :description "The string to pass into org-ql-select-headings-rifle. This is a bare string. Example: \"searchterm\"")) + :description "The string to match entry headings against.")) :category "org-ql")) (defun gptel-org-tools--org-ql-select-tags-local (buf query) - (org-ql-select + (let ((org-ql-cache (make-hash-table))) + (org-ql-select (get-buffer buf) `(tags-local ,query) :action #'(lambda () @@ -259,7 +275,7 @@ (line-beginning-position) (progn (outline-next-heading) - (line-beginning-position))))))) + (line-beginning-position)))))))) (add-to-list 'gptel-org-tools @@ -272,11 +288,12 @@ :description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.") '(:name "query" :type string - :description "The tags to match entry headings against. Example: \"tag1\" \"tag2\"")) + :description "The string match entry tags against.")) :category "org-ql")) (defun gptel-org-tools--org-ql-select-tags (buf query) - (org-ql-select + (let ((org-ql-cache (make-hash-table))) + (org-ql-select (get-buffer buf) `(tags ,query) :action #'(lambda () @@ -285,7 +302,7 @@ (line-beginning-position) (progn (outline-next-heading) - (line-beginning-position))))))) + (line-beginning-position)))))))) (add-to-list 'gptel-org-tools @@ -298,11 +315,12 @@ :description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.") '(:name "query" :type string - :description "The tags to match entry headings against. Example: \"tag1\" \"tag2\"")) + :description "The string to match entry headings against.")) :category "org-ql")) -(defun gptel-org-tools--org-ql-select-rifle (buf query) - (let ((buffer (get-buffer buf))) +(defun hash-gptl-org-tools--org-ql-select-rifle () + (let ((org-ql-cache (make-hash-table)) + (buffer (get-buffer buf))) (if buffer (org-ql-select buffer @@ -326,10 +344,11 @@ :description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.") '(:name "query" :type string - :description "The strings to match entry headings and content against. Example: \"tag1\" \"tag2\"")) + :description "The string to match entry headings and content against.")) :category "org-ql")) (defun gptel-org-tools--org-ql-select-agenda-tags (query) + (let ((org-ql-cache (make-hash-table))) (org-ql-select (org-agenda-files) `(tags-local ,query) @@ -339,7 +358,7 @@ (line-beginning-position) (progn (outline-next-heading) - (line-beginning-position))))))) + (line-beginning-position)))))))) (add-to-list 'gptel-org-tools @@ -349,10 +368,11 @@ :description "Run simple word query against all files in (org-agenda-files). WITHOUT tag inheritance, only directly tagged headings." :args (list '(:name "query" :type string - :description "Plain list of strings to match entry headings and content against. Example: \"tag1\" \"tag2\"")) + :description "The string to match entry tags against.")) :category "org-ql")) (defun gptel-org-tools--org-ql-select-agenda-tags (query) + (let ((org-ql-cache (make-hash-table))) (org-ql-select (org-agenda-files) `(tags ,query) @@ -362,7 +382,7 @@ (line-beginning-position) (progn (outline-next-heading) - (line-beginning-position))))))) + (line-beginning-position)))))))) (add-to-list 'gptel-org-tools @@ -372,10 +392,11 @@ :description "Run simple word query against all files in (org-agenda-files). WITH tag inheritance." :args (list '(:name "query" :type string - :description "Plain list of strings to match entry headings and content against. Example: \"tag1\" \"tag2\"")) + :description "The string to match entry tags against.")) :category "org-ql")) (defun gptel-org-tools--org-ql-select-agenda-rifle (query) + (let ((org-ql-cache (make-hash-table))) (org-ql-select (org-agenda-files) `(rifle ,query) @@ -385,7 +406,7 @@ (line-beginning-position) (progn (outline-next-heading) - (line-beginning-position))))))) + (line-beginning-position)))))))) (add-to-list 'gptel-org-tools (gptel-make-tool @@ -394,7 +415,7 @@ :description "Run simple word query against all files in (org-agenda-files)" :args (list '(:name "query" :type string - :description "Plain list of strings to match entry headings and content against. Example: \"tag1\" \"tag2\"")) + :description "The string to match entry headings and content against.")) :category "org-ql")) (provide 'gptel-org-tools)