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
|
||||
:models '("llamacpp"
|
||||
:capabilities (reasoning))
|
||||
:request-params '(:thinking t
|
||||
:enable_thinking t
|
||||
:include_reasoning t
|
||||
:request-params '(:thinking nil
|
||||
:enable_thinking nil
|
||||
:include_reasoning nil
|
||||
:parallel_tool_calls t)))
|
||||
|
||||
(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)
|
||||
#+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.
|
||||
|
@ -102,8 +105,20 @@ config.el:
|
|||
(require 'gptel-org-tools)
|
||||
(mapcar (lambda (tool) (cl-pushnew tool gptel-tools)) gptel-org-tools)
|
||||
#+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
|
||||
** 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
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (elisp)
|
||||
:function ((lambda () (interactive) ) (elisp)
|
||||
(unless (stringp elisp) (error "elisp code must be a string"))
|
||||
(with-temp-buffer
|
||||
(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
|
||||
(gptel-make-tool
|
||||
:function (lambda (arg)
|
||||
(with-temp-buffer
|
||||
(list-buffers)
|
||||
(let ((content (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
content)))
|
||||
(list-buffers-noselect)
|
||||
(with-current-buffer "*Buffer List*"
|
||||
(buffer-string)))
|
||||
:name "list-buffers"
|
||||
:description "Access the list of buffers open in Emacs, including file names and full paths."
|
||||
:args (list '(:name "arg"
|
||||
:type string
|
||||
:description "Does nothing."
|
||||
:optional t))
|
||||
:description "Does nothing."))
|
||||
:category "emacs"))
|
||||
#+end_src
|
||||
|
||||
**** 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.
|
||||
|
||||
|
@ -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.
|
||||
#+begin_src elisp
|
||||
(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
|
||||
(org-map-entries
|
||||
#'(buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(line-end-position))
|
||||
t
|
||||
'file)))
|
||||
'file))))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
|
@ -655,6 +670,96 @@ And, the "grab everything that matches" tool.
|
|||
#+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
|
||||
:PROPERTIES:
|
||||
: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; -*-
|
||||
|
||||
;; Copyright (C) 2025 Phil Bajsicki
|
||||
|
@ -39,17 +41,14 @@
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (arg)
|
||||
(with-temp-buffer
|
||||
(list-buffers)
|
||||
(let ((content (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
content)))
|
||||
(list-buffers-noselect)
|
||||
(with-current-buffer "*Buffer List*"
|
||||
(buffer-string)))
|
||||
:name "list-buffers"
|
||||
:description "Access the list of buffers open in Emacs, including file names and full paths."
|
||||
:args (list '(:name "arg"
|
||||
:type string
|
||||
:description "Does nothing."
|
||||
:optional t))
|
||||
:description "Does nothing."))
|
||||
:category "emacs"))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
|
@ -144,13 +143,15 @@
|
|||
:category "org-mode"))
|
||||
|
||||
(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
|
||||
(org-map-entries
|
||||
#'(buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(line-end-position))
|
||||
t
|
||||
'file)))
|
||||
'file))))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
|
@ -328,5 +329,73 @@
|
|||
:description "The 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-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)
|
||||
;;; gptel-org-tools.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue