Add gptel-org-tools-result-limit

This commit is contained in:
Phil Bajsicki 2025-04-25 23:36:25 +02:00
parent a6f1e628ad
commit 771970808c
2 changed files with 168 additions and 86 deletions

View file

@ -150,6 +150,7 @@ Collects into =gptel-org-tools= list, distinct from =gptel-tools=
(defvar gptel-org-tools '())
#+end_src
** Variables
*** skip-heading-extraction
If you're like me, you may have structured headings based on timestamps in your org-mode notes.
To that end, there are a few variables that may help you from blowing out the context, specifically for the ~org-extract-headings~ tool.
@ -164,6 +165,37 @@ Use ~setq~ in your configuration, with a list of buffer names whose headings you
(setq gptel-org-tools-skip-heading-extraction '("journal.org" ".org"))
#+end_src
*** result-limit
It is with great disappointment that I admit to a terrible flaw of this package.
When in local use, with a large dataset, a lot of the commands which return entire org-mode entries... end up grabbing way, way too much text.
This can end up with Emacs freezing as it tries to parse immeasurable amounts of text (for fonts, formatting, etc) at the same time as the LLM is screeching in electric pain trying to actually process all that data.
To that end, the stop-gap solution is to implement a hard limit on the amount of text a tool can return.
I don't think there's much reason to ever allow partial results, which leads me to this:
#+begin_src elisp :results none
(defvar gptel-org-tools-result-limit 20000)
(defun gptel-org-tools--result-limit (result)
(if (>= (length (format "%s" result)) gptel-org-tools-result-limit)
(format "Results over %s character. Please try with a more specific query." gptel-org-tools-result-limit)
result))
#+end_src
Use ~setq~ in your configuration, e.g.:
#+begin_src elisp :tangle no :results none
(setq gptel-org-tools-result-limit 12000)
#+end_src
This will *prevent* tools from returning results longer than 20,000 characters. Instead, the LLM will receive a message saying it should be much more specific in its queries, which will hopefully alleviate the issue.
The functionality for withholding results is only applied to select functions that are known to cause issues.
** Helper Functions
These abstract away some of the tool definitions.
@ -501,12 +533,15 @@ Currently *not* tangled, as I'm testing breaking out each type of query into its
"Return entries matching QUERY from BUFFER.
QUERY can be any valid org-ql-select query."
(org-ql-select
(get-buffer buf)
(if (stringp query)
(read query)
query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select (get-buffer buf)
(if (stringp query)
(read query)
query
:action #'gptel-org-tools--heading-body))))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
(gptel-make-tool
@ -554,9 +589,11 @@ DATE is the date or date range to match."
(mode (buffer-local-value 'major-mode buffer)))
(if buffer
(if (eq mode 'org-mode)
(org-ql-select buffer
`(heading ,date)
:action #'gptel-org-tools--heading-subtree)
(let ((result
(org-ql-select buffer
`(heading ,date)
:action #'gptel-org-tools--heading-subtree)))
(gptel-org-tools--result-limit result))
(message "Buffer '%s' isn't an org-mode buffer." buf))
(message "Buffer '%s' does not exist." buf))))
@ -613,7 +650,7 @@ It works, in principle, but I haven't been able to find a use for it yet. The re
(org-agenda-list (or days 14))
(let ((content (buffer-string)))
(kill-buffer (current-buffer))
content)))
(gptel-org-tools--result-limit content))))
(add-to-list 'gptel-org-tools
(gptel-make-tool
@ -635,10 +672,12 @@ Retrieve the headings where the heading matches query..
#+begin_src elisp
(defun gptel-org-tools--org-ql-select-headings (buf query)
"Return headings matching QUERY from BUFFER."
(org-ql-select
(get-buffer buf)
`(heading ,query)
:action #''gptel-org-tools--heading))
(let ((result
(org-ql-select
(get-buffer buf)
`(heading ,query)
:action #''gptel-org-tools--heading)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
@ -660,10 +699,12 @@ 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)
"Return headings of entries (body included) that match keyword QUERY from BUFFER."
(org-ql-select
(get-buffer buf)
`(rifle ,query)
:action #'gptel-org-tools--heading))
(let ((result
(org-ql-select
(get-buffer buf)
`(rifle ,query)
:action #'gptel-org-tools--heading)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
@ -685,10 +726,12 @@ This pulls all the headings (and their contents) when they match tags (without i
#+begin_src elisp
(defun gptel-org-tools--org-ql-select-tags-local (buf query)
"Return entries whose tags match QUERY in BUFFER, without inheritance."
(org-ql-select
(get-buffer buf)
`(tags-local ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(get-buffer buf)
`(tags-local ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
@ -735,10 +778,12 @@ This pulls all the headings (and their contents) when they match tags (with inhe
#+begin_src elisp
(defun gptel-org-tools--org-ql-select-tags (buf query)
"Return every entry tagged QUERY from BUFFER."
(org-ql-select
(get-buffer buf)
`(tags ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(get-buffer buf)
`(tags ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
(gptel-make-tool
@ -761,10 +806,12 @@ And, the "grab everything that matches" tool.
"Return every entry matching keyword QUERY from BUFFER."
(let ((buffer (get-buffer buf)))
(if buffer
(org-ql-select
buffer
`(rifle ,query)
:action #'gptel-org-tools--heading-body)
(let ((result
(org-ql-select
buffer
`(rifle ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result))
(message "Buffer '%s' does not exist." buf))))
(add-to-list 'gptel-org-tools
@ -787,10 +834,12 @@ This pulls all the headings (and their contents) when they match tags (without i
(defun gptel-org-tools--org-ql-select-all-tags-local (query)
"Return entries whose tags match QUERY in org-agenda-files.
QUERY is the tag to search for."
(org-ql-select
(org-agenda-files)
`(tags-local ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(org-agenda-files)
`(tags-local ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
@ -811,10 +860,12 @@ This pulls all the headings (and their contents) when they match tags (with inhe
"Return entries whose tags match QUERY,
with inheritance, in org-agenda-files.
QUERY is the tag to search for."
(org-ql-select
(org-agenda-files)
`(tags ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(org-agenda-files)
`(tags ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
(gptel-make-tool
@ -842,10 +893,12 @@ This means that /every org-mode file I have/ is part of this search.
(defun gptel-org-tools--org-ql-select-all-rifle (query)
"Return entries containing QUERY from org-agenda-files.
QUERY is the keyword to search for."
(org-ql-select
(org-agenda-files)
`(rifle ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(org-agenda-files)
`(rifle ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
(gptel-make-tool
@ -864,10 +917,12 @@ This means that /every org-mode file I have/ is part of this search.
(defun gptel-org-tools--org-ql-select-all-regexp (query)
"Return all entries matching regexp QUERY in org-agenda-files.
QUERY is a regular expression."
(org-ql-select
(org-agenda-files)
`(regexp ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(org-agenda-files)
`(regexp ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
(gptel-make-tool

View file

@ -38,6 +38,13 @@
(defvar gptel-org-tools-skip-heading-extraction '())
(defvar gptel-org-tools-result-limit 20000)
(defun gptel-org-tools--result-limit (result)
(if (>= (length (format "%s" result)) gptel-org-tools-result-limit)
(format "Results over %s character. Please try with a more specific query." gptel-org-tools-result-limit)
result))
(defun gptel-org-tools--heading ()
(concat
(buffer-substring-no-properties
@ -202,9 +209,11 @@ DATE is the date or date range to match."
(mode (buffer-local-value 'major-mode buffer)))
(if buffer
(if (eq mode 'org-mode)
(org-ql-select buffer
`(heading ,date)
:action #'gptel-org-tools--heading-subtree)
(let ((result
(org-ql-select buffer
`(heading ,date)
:action #'gptel-org-tools--heading-subtree)))
(gptel-org-tools--result-limit result))
(message "Buffer '%s' isn't an org-mode buffer." buf))
(message "Buffer '%s' does not exist." buf))))
@ -228,7 +237,7 @@ DATE is the date or date range to match."
(org-agenda-list (or days 14))
(let ((content (buffer-string)))
(kill-buffer (current-buffer))
content)))
(gptel-org-tools--result-limit content))))
(add-to-list 'gptel-org-tools
(gptel-make-tool
@ -242,10 +251,12 @@ DATE is the date or date range to match."
(defun gptel-org-tools--org-ql-select-headings (buf query)
"Return headings matching QUERY from BUFFER."
(org-ql-select
(get-buffer buf)
`(heading ,query)
:action #''gptel-org-tools--heading))
(let ((result
(org-ql-select
(get-buffer buf)
`(heading ,query)
:action #''gptel-org-tools--heading)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
@ -263,10 +274,12 @@ DATE is the date or date range to match."
(defun gptel-org-tools--org-ql-select-headings-rifle (buf query)
"Return headings of entries (body included) that match keyword QUERY from BUFFER."
(org-ql-select
(get-buffer buf)
`(rifle ,query)
:action #'gptel-org-tools--heading))
(let ((result
(org-ql-select
(get-buffer buf)
`(rifle ,query)
:action #'gptel-org-tools--heading)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
@ -284,10 +297,12 @@ DATE is the date or date range to match."
(defun gptel-org-tools--org-ql-select-tags-local (buf query)
"Return entries whose tags match QUERY in BUFFER, without inheritance."
(org-ql-select
(get-buffer buf)
`(tags-local ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(get-buffer buf)
`(tags-local ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
@ -326,10 +341,12 @@ DATE is the date or date range to match."
(defun gptel-org-tools--org-ql-select-tags (buf query)
"Return every entry tagged QUERY from BUFFER."
(org-ql-select
(get-buffer buf)
`(tags ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(get-buffer buf)
`(tags ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
(gptel-make-tool
@ -348,10 +365,12 @@ DATE is the date or date range to match."
"Return every entry matching keyword QUERY from BUFFER."
(let ((buffer (get-buffer buf)))
(if buffer
(org-ql-select
buffer
`(rifle ,query)
:action #'gptel-org-tools--heading-body)
(let ((result
(org-ql-select
buffer
`(rifle ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result))
(message "Buffer '%s' does not exist." buf))))
(add-to-list 'gptel-org-tools
@ -370,10 +389,12 @@ DATE is the date or date range to match."
(defun gptel-org-tools--org-ql-select-all-tags-local (query)
"Return entries whose tags match QUERY in org-agenda-files.
QUERY is the tag to search for."
(org-ql-select
(org-agenda-files)
`(tags-local ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(org-agenda-files)
`(tags-local ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
@ -390,10 +411,12 @@ DATE is the date or date range to match."
"Return entries whose tags match QUERY,
with inheritance, in org-agenda-files.
QUERY is the tag to search for."
(org-ql-select
(org-agenda-files)
`(tags ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(org-agenda-files)
`(tags ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
(gptel-make-tool
@ -408,10 +431,12 @@ with inheritance, in org-agenda-files.
(defun gptel-org-tools--org-ql-select-all-rifle (query)
"Return entries containing QUERY from org-agenda-files.
QUERY is the keyword to search for."
(org-ql-select
(org-agenda-files)
`(rifle ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(org-agenda-files)
`(rifle ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
(gptel-make-tool
@ -426,10 +451,12 @@ with inheritance, in org-agenda-files.
(defun gptel-org-tools--org-ql-select-all-regexp (query)
"Return all entries matching regexp QUERY in org-agenda-files.
QUERY is a regular expression."
(org-ql-select
(org-agenda-files)
`(regexp ,query)
:action #'gptel-org-tools--heading-body))
(let ((result
(org-ql-select
(org-agenda-files)
`(regexp ,query)
:action #'gptel-org-tools--heading-body)))
(gptel-org-tools--result-limit result)))
(add-to-list 'gptel-org-tools
(gptel-make-tool