Add (org-agenda-files): tags, tags-local, rifle
This commit is contained in:
parent
5c39f9348d
commit
c781b00be4
2 changed files with 194 additions and 20 deletions
129
README.org
129
README.org
|
@ -62,9 +62,9 @@ I change models a lot, and this /just works/ for most models, even if some aren'
|
||||||
:stream nil
|
:stream nil
|
||||||
:models '("llamacpp"
|
:models '("llamacpp"
|
||||||
:capabilities (reasoning))
|
:capabilities (reasoning))
|
||||||
:request-params '(:thinking t
|
:request-params '(:thinking nil
|
||||||
:enable_thinking t
|
:enable_thinking nil
|
||||||
:include_reasoning t
|
:include_reasoning nil
|
||||||
:parallel_tool_calls t)))
|
:parallel_tool_calls t)))
|
||||||
|
|
||||||
(setf (alist-get 'org-mode gptel-prompt-prefix-alist) "@user\n")
|
(setf (alist-get 'org-mode gptel-prompt-prefix-alist) "@user\n")
|
||||||
|
@ -76,6 +76,9 @@ I change models a lot, and this /just works/ for most models, even if some aren'
|
||||||
(require 'gptel)
|
(require 'gptel)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
: gptel
|
||||||
|
|
||||||
With that out of the way, let's get to the tools.
|
With that out of the way, let's get to the tools.
|
||||||
* Usage options:
|
* Usage options:
|
||||||
I only use Doom Emacs, so here's how I do it.
|
I only use Doom Emacs, so here's how I do it.
|
||||||
|
@ -102,8 +105,20 @@ config.el:
|
||||||
(require 'gptel-org-tools)
|
(require 'gptel-org-tools)
|
||||||
(mapcar (lambda (tool) (cl-pushnew tool gptel-tools)) gptel-org-tools)
|
(mapcar (lambda (tool) (cl-pushnew tool gptel-tools)) gptel-org-tools)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
** Variables
|
||||||
|
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.
|
||||||
|
|
||||||
|
#+begin_src elisp
|
||||||
|
(defvar gptel-org-tools-skip-heading-extraction '())
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Use ~setq~ in your configuration, with a list of buffer names whose headings you *never* want to extract. E.g.
|
||||||
|
|
||||||
|
#+begin_src elisp :tangle no
|
||||||
|
(setq gptel-org-tools-skip-heading-extraction '("journal.org" ".org"))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
* Code
|
* Code
|
||||||
** Preamble
|
** Preamble
|
||||||
|
@ -169,7 +184,7 @@ Highly not recommended, but sometimes an LLM can pull a rabbit out of pure entro
|
||||||
#+begin_src elisp :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org-tools
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function (lambda (elisp)
|
:function ((lambda () (interactive) ) (elisp)
|
||||||
(unless (stringp elisp) (error "elisp code must be a string"))
|
(unless (stringp elisp) (error "elisp code must be a string"))
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(insert (eval (read elisp)))
|
(insert (eval (read elisp)))
|
||||||
|
@ -193,19 +208,17 @@ Seems to be one of the most reliable tools in the basket... mostly because
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org-tools
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function (lambda (arg)
|
:function (lambda (arg)
|
||||||
(with-temp-buffer
|
(list-buffers-noselect)
|
||||||
(list-buffers)
|
(with-current-buffer "*Buffer List*"
|
||||||
(let ((content (buffer-string)))
|
(buffer-string)))
|
||||||
(kill-buffer (current-buffer))
|
|
||||||
content)))
|
|
||||||
:name "list-buffers"
|
:name "list-buffers"
|
||||||
:description "Access the list of buffers open in Emacs, including file names and full paths."
|
:description "Access the list of buffers open in Emacs, including file names and full paths."
|
||||||
:args (list '(:name "arg"
|
:args (list '(:name "arg"
|
||||||
:type string
|
:type string
|
||||||
:description "Does nothing."
|
:description "Does nothing."))
|
||||||
: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.
|
||||||
|
|
||||||
|
@ -375,13 +388,15 @@ 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)
|
||||||
|
(if (member buffer gptel-org-tools-skip-heading-extraction)
|
||||||
|
(user-error "Buffer %s has too many headings, use org-extract-tags or org-ql-select-rifle." buffer)
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(org-map-entries
|
(org-map-entries
|
||||||
#'(buffer-substring-no-properties
|
#'(buffer-substring-no-properties
|
||||||
(line-beginning-position)
|
(line-beginning-position)
|
||||||
(line-end-position))
|
(line-end-position))
|
||||||
t
|
t
|
||||||
'file)))
|
'file))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org-tools
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
|
@ -655,6 +670,96 @@ And, the "grab everything that matches" tool.
|
||||||
#+end_src
|
#+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)
|
||||||
|
(org-ql-select
|
||||||
|
(org-agenda-files)
|
||||||
|
`(tags-local ,query)
|
||||||
|
:action #'(lambda ()
|
||||||
|
(concat
|
||||||
|
(buffer-substring-no-properties
|
||||||
|
(line-beginning-position)
|
||||||
|
(progn
|
||||||
|
(outline-next-heading)
|
||||||
|
(line-beginning-position)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(add-to-list 'gptel-org-tools
|
||||||
|
(gptel-make-tool
|
||||||
|
:function #'gptel-org-tools--org-ql-select-agenda-tags-local
|
||||||
|
:name "org-ql-select-agenda-tags-local"
|
||||||
|
: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\""))
|
||||||
|
:category "org-ql"))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
***** org-ql-select-agenda-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.)
|
||||||
|
#+begin_src elisp
|
||||||
|
(defun gptel-org-tools--org-ql-select-agenda-tags (query)
|
||||||
|
(org-ql-select
|
||||||
|
(org-agenda-files)
|
||||||
|
`(tags ,query)
|
||||||
|
:action #'(lambda ()
|
||||||
|
(concat
|
||||||
|
(buffer-substring-no-properties
|
||||||
|
(line-beginning-position)
|
||||||
|
(progn
|
||||||
|
(outline-next-heading)
|
||||||
|
(line-beginning-position)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(add-to-list 'gptel-org-tools
|
||||||
|
(gptel-make-tool
|
||||||
|
:function #'gptel-org-tools--org-ql-select-agenda-tags
|
||||||
|
:name "org-ql-select-agenda-tags"
|
||||||
|
: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\""))
|
||||||
|
:category "org-ql"))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
***** org-ql-select-agenda-rifle
|
||||||
|
And for the entire =(org-agenda-files)=.
|
||||||
|
|
||||||
|
Note that I define my agenda in this way:
|
||||||
|
|
||||||
|
#+begin_src elisp :tangle no
|
||||||
|
(setq org-agenda-files (directory-files-recursively "~/enc/org/" ".org$"))1
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
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)
|
||||||
|
(org-ql-select
|
||||||
|
(org-agenda-files)
|
||||||
|
`(rifle ,query)
|
||||||
|
:action #'(lambda ()
|
||||||
|
(concat
|
||||||
|
(buffer-substring-no-properties
|
||||||
|
(line-beginning-position)
|
||||||
|
(progn
|
||||||
|
(outline-next-heading)
|
||||||
|
(line-beginning-position)))))))
|
||||||
|
|
||||||
|
(add-to-list 'gptel-org-tools
|
||||||
|
(gptel-make-tool
|
||||||
|
:function #'gptel-org-tools--org-ql-select-agenda-rifle
|
||||||
|
:name "org-ql-select-agenda-rifle"
|
||||||
|
: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\""))
|
||||||
|
:category "org-ql"))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
|
||||||
** End
|
** End
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CREATED: <2025-04-14 Mon 22:46>
|
:CREATED: <2025-04-14 Mon 22:46>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
(defvar gptel-org-tools-skip-heading-extraction '())
|
||||||
|
|
||||||
;;; gptel-org-tools.el --- LLM Tools for org-mode interaction. -*- lexical-binding: t; -*-
|
;;; gptel-org-tools.el --- LLM Tools for org-mode interaction. -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
;; Copyright (C) 2025 Phil Bajsicki
|
;; Copyright (C) 2025 Phil Bajsicki
|
||||||
|
@ -39,17 +41,14 @@
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org-tools
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function (lambda (arg)
|
:function (lambda (arg)
|
||||||
(with-temp-buffer
|
(list-buffers-noselect)
|
||||||
(list-buffers)
|
(with-current-buffer "*Buffer List*"
|
||||||
(let ((content (buffer-string)))
|
(buffer-string)))
|
||||||
(kill-buffer (current-buffer))
|
|
||||||
content)))
|
|
||||||
:name "list-buffers"
|
:name "list-buffers"
|
||||||
:description "Access the list of buffers open in Emacs, including file names and full paths."
|
:description "Access the list of buffers open in Emacs, including file names and full paths."
|
||||||
:args (list '(:name "arg"
|
:args (list '(:name "arg"
|
||||||
:type string
|
:type string
|
||||||
:description "Does nothing."
|
:description "Does nothing."))
|
||||||
:optional t))
|
|
||||||
:category "emacs"))
|
:category "emacs"))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org-tools
|
||||||
|
@ -144,13 +143,15 @@
|
||||||
:category "org-mode"))
|
:category "org-mode"))
|
||||||
|
|
||||||
(defun gptel-org-tools--org-extract-headings (buffer)
|
(defun gptel-org-tools--org-extract-headings (buffer)
|
||||||
|
(if (member buffer gptel-org-tools-skip-heading-extraction)
|
||||||
|
(user-error "Buffer %s has too many headings, use org-extract-tags or org-ql-select-rifle." buffer)
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(org-map-entries
|
(org-map-entries
|
||||||
#'(buffer-substring-no-properties
|
#'(buffer-substring-no-properties
|
||||||
(line-beginning-position)
|
(line-beginning-position)
|
||||||
(line-end-position))
|
(line-end-position))
|
||||||
t
|
t
|
||||||
'file)))
|
'file))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org-tools
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
|
@ -328,5 +329,73 @@
|
||||||
: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"))
|
||||||
|
|
||||||
|
(defun gptel-org-tools--org-ql-select-agenda-tags (query)
|
||||||
|
(org-ql-select
|
||||||
|
(org-agenda-files)
|
||||||
|
`(tags-local ,query)
|
||||||
|
:action #'(lambda ()
|
||||||
|
(concat
|
||||||
|
(buffer-substring-no-properties
|
||||||
|
(line-beginning-position)
|
||||||
|
(progn
|
||||||
|
(outline-next-heading)
|
||||||
|
(line-beginning-position)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(add-to-list 'gptel-org-tools
|
||||||
|
(gptel-make-tool
|
||||||
|
:function #'gptel-org-tools--org-ql-select-agenda-tags-local
|
||||||
|
:name "org-ql-select-agenda-tags-local"
|
||||||
|
: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\""))
|
||||||
|
:category "org-ql"))
|
||||||
|
|
||||||
|
(defun gptel-org-tools--org-ql-select-agenda-tags (query)
|
||||||
|
(org-ql-select
|
||||||
|
(org-agenda-files)
|
||||||
|
`(tags ,query)
|
||||||
|
:action #'(lambda ()
|
||||||
|
(concat
|
||||||
|
(buffer-substring-no-properties
|
||||||
|
(line-beginning-position)
|
||||||
|
(progn
|
||||||
|
(outline-next-heading)
|
||||||
|
(line-beginning-position)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(add-to-list 'gptel-org-tools
|
||||||
|
(gptel-make-tool
|
||||||
|
:function #'gptel-org-tools--org-ql-select-agenda-tags
|
||||||
|
:name "org-ql-select-agenda-tags"
|
||||||
|
: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\""))
|
||||||
|
:category "org-ql"))
|
||||||
|
|
||||||
|
(defun gptel-org-tools--org-ql-select-agenda-rifle (query)
|
||||||
|
(org-ql-select
|
||||||
|
(org-agenda-files)
|
||||||
|
`(rifle ,query)
|
||||||
|
:action #'(lambda ()
|
||||||
|
(concat
|
||||||
|
(buffer-substring-no-properties
|
||||||
|
(line-beginning-position)
|
||||||
|
(progn
|
||||||
|
(outline-next-heading)
|
||||||
|
(line-beginning-position)))))))
|
||||||
|
|
||||||
|
(add-to-list 'gptel-org-tools
|
||||||
|
(gptel-make-tool
|
||||||
|
:function #'gptel-org-tools--org-ql-select-agenda-rifle
|
||||||
|
:name "org-ql-select-agenda-rifle"
|
||||||
|
: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\""))
|
||||||
|
:category "org-ql"))
|
||||||
|
|
||||||
(provide 'gptel-org-tools)
|
(provide 'gptel-org-tools)
|
||||||
;;; gptel-org-tools.el ends here
|
;;; gptel-org-tools.el ends here
|
||||||
|
|
Loading…
Add table
Reference in a new issue