Change name
This commit is contained in:
parent
7a4ac1dab3
commit
bb70c340c5
2 changed files with 235 additions and 235 deletions
298
README.org
298
README.org
|
@ -1,11 +1,11 @@
|
||||||
#+title: gptel-org-tools
|
#+title: gptel-org
|
||||||
#+author: Phil Bajsicki
|
#+author: Phil Bajsicki
|
||||||
#+auto_tangle: t
|
#+auto_tangle: t
|
||||||
#+PROPERTY: header-args :elisp :tangle gptel-org-tools.el
|
#+PROPERTY: header-args :elisp :tangle gptel-org.el
|
||||||
* Notes
|
* Notes
|
||||||
1. This repository previously lived on my personal forge. I have since moved it to codeberg. My personal forge will remain reasonably up-to-date, however [[https://codeberg.org/bajsicki/gptel-org-tools][codeberg]] is the reference repository.
|
1. This repository previously lived on my personal forge. I have since moved it to codeberg. My personal forge will remain reasonably up-to-date, however [[https://codeberg.org/bajsicki/gptel-org-tools][codeberg]] is the reference repository.
|
||||||
2. The =gptel.org= file exists for tracking issues in my set-up, and is fundamentally a way for me to maintain some semblance of sanity in testing, as GitHub/ forges aren't really easy to work with for this.
|
2. The =gptel.org= file exists for tracking issues in my set-up, and is fundamentally a way for me to maintain some semblance of sanity in testing, as GitHub/ forges aren't really easy to work with for this.
|
||||||
|
3. [2025-05-25 Sun] Package renamed to gptel-org, since typing =-tools= is a mess.
|
||||||
* Disclaimer
|
* Disclaimer
|
||||||
- ~This package is an active privacy risk. It allows the LLM to autonomously expand its context in any direction it chooses.~
|
- ~This package is an active privacy risk. It allows the LLM to autonomously expand its context in any direction it chooses.~
|
||||||
- ~Only connect to third-party systems if you understand and accept the risk of *any* of your files becoming publicly accessible.~
|
- ~Only connect to third-party systems if you understand and accept the risk of *any* of your files becoming publicly accessible.~
|
||||||
|
@ -14,7 +14,7 @@ This is a collection of tools I wrote to help me review my life.
|
||||||
|
|
||||||
Summary:
|
Summary:
|
||||||
- An explanation of each tool is below.
|
- An explanation of each tool is below.
|
||||||
- The tools are tangled into =gptel-org-tools.el= *from this file*.
|
- The tools are tangled into =gptel-org.el= *from this file*.
|
||||||
|
|
||||||
Premise:
|
Premise:
|
||||||
- LLMs are /not very smart/ and /unreliable/, but they do okay with basic text comprehension, and can generate completions/ responses which /fit the vibe/ of the request.
|
- LLMs are /not very smart/ and /unreliable/, but they do okay with basic text comprehension, and can generate completions/ responses which /fit the vibe/ of the request.
|
||||||
|
@ -41,15 +41,15 @@ I only use Doom Emacs, so here's how I load:
|
||||||
|
|
||||||
packages.el:
|
packages.el:
|
||||||
#+begin_src elisp :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(package! gptel-org-tools
|
(package! gptel-org
|
||||||
:recipe (:host nil
|
:recipe (:host nil
|
||||||
:repo "https://codeberg.org/bajsicki/gptel-org-tools"))
|
:repo "https://codeberg.org/bajsicki/gptel-org"))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
config.el:
|
config.el:
|
||||||
#+begin_src elisp :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(require 'gptel-org-tools)
|
(require 'gptel-org)
|
||||||
(setq gptel-tools gptel-org-tools)
|
(setq gptel-tools gptel-org)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
This /will/ overwrite any other tools you have defined before this call takes place.
|
This /will/ overwrite any other tools you have defined before this call takes place.
|
||||||
|
@ -58,8 +58,8 @@ If you want to just append them to your existing tool list:
|
||||||
|
|
||||||
config.el:
|
config.el:
|
||||||
#+begin_src elisp :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(require 'gptel-org-tools)
|
(require 'gptel-org)
|
||||||
(mapcar (lambda (tool) (cl-pushnew tool gptel-tools)) gptel-org-tools)
|
(mapcar (lambda (tool) (cl-pushnew tool gptel-tools)) gptel-org)
|
||||||
#+end_src
|
#+end_src
|
||||||
* Code
|
* Code
|
||||||
** Preamble
|
** Preamble
|
||||||
|
@ -68,7 +68,7 @@ config.el:
|
||||||
:END:
|
:END:
|
||||||
Stuff, headers, etc.
|
Stuff, headers, etc.
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
;;; gptel-org-tools.el --- LLM Tools for org-mode interaction. -*- lexical-binding: t; -*-
|
;;; gptel-org.el --- LLM Tools for org-mode interaction. -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
;; Copyright (C) 2025 Phil Bajsicki
|
;; Copyright (C) 2025 Phil Bajsicki
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ Stuff, headers, etc.
|
||||||
;; Author: Phil Bajsicki <phil@bajsicki.com>
|
;; Author: Phil Bajsicki <phil@bajsicki.com>
|
||||||
;; Version: 0.0.2
|
;; Version: 0.0.2
|
||||||
;; Package-Requires: ((emacs "30.1") (gptel 0.9.8) (org-ql 0.9))
|
;; Package-Requires: ((emacs "30.1") (gptel 0.9.8) (org-ql 0.9))
|
||||||
;; URL: https://github.com/phil/gptel-org-tools
|
;; URL: https://github.com/phil/gptel-org
|
||||||
;; SPDX-License-Identifier: GPL-3.0
|
;; SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
;; This program is free software; you can redistribute it and/or modify
|
;; This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -100,14 +100,14 @@ Stuff, headers, etc.
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
|
|
||||||
;; 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
|
||||||
|
|
||||||
;;; Code:)
|
;;; Code:)
|
||||||
#+end_src
|
#+end_src
|
||||||
** gptel-org-tools
|
** gptel-org
|
||||||
Collects into =gptel-org-tools= list, distinct from =gptel-tools=
|
Collects into =gptel-org= list, distinct from =gptel-tools=
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defvar gptel-org-tools '())
|
(defvar gptel-org '())
|
||||||
#+end_src
|
#+end_src
|
||||||
** Variables
|
** Variables
|
||||||
*** skip-heading-extraction
|
*** skip-heading-extraction
|
||||||
|
@ -116,13 +116,13 @@ If you're like me, you may have structured headings based on timestamps in your
|
||||||
To that end, there are a few variables that may help you from blowing out the context, specifically for the ~org-extract-headings~ tool.
|
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
|
#+begin_src elisp
|
||||||
(defvar gptel-org-tools-skip-heading-extraction '())
|
(defvar gptel-org-skip-heading-extraction '())
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
Use ~setq~ in your configuration, with a list of buffer names whose headings you *never* want to extract. E.g.
|
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
|
#+begin_src elisp :tangle no
|
||||||
(setq gptel-org-tools-skip-heading-extraction '("journal.org" ".org"))
|
(setq gptel-org-skip-heading-extraction '("journal.org" ".org"))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
*** result-limit
|
*** result-limit
|
||||||
|
@ -137,11 +137,11 @@ To that end, the stop-gap solution is to implement a hard limit on the amount of
|
||||||
I don't think there's much reason to ever allow partial results, which leads me to this:
|
I don't think there's much reason to ever allow partial results, which leads me to this:
|
||||||
|
|
||||||
#+begin_src elisp :results none
|
#+begin_src elisp :results none
|
||||||
(defvar gptel-org-tools-result-limit 40000)
|
(defvar gptel-org-result-limit 40000)
|
||||||
|
|
||||||
(defun gptel-org-tools--result-limit (result)
|
(defun gptel-org--result-limit (result)
|
||||||
(if (>= (length (format "%s" result)) gptel-org-tools-result-limit)
|
(if (>= (length (format "%s" result)) gptel-org-result-limit)
|
||||||
(format "Results over %s character. Stop. Analyze. Find a different solution, or use a more specific query." gptel-org-tools-result-limit)
|
(format "Results over %s character. Stop. Analyze. Find a different solution, or use a more specific query." gptel-org-result-limit)
|
||||||
result))
|
result))
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ I don't think there's much reason to ever allow partial results, which leads me
|
||||||
Use ~setq~ in your configuration, e.g.:
|
Use ~setq~ in your configuration, e.g.:
|
||||||
|
|
||||||
#+begin_src elisp :tangle no :results none
|
#+begin_src elisp :tangle no :results none
|
||||||
(setq gptel-org-tools-result-limit 40000)
|
(setq gptel-org-result-limit 40000)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
This will *prevent* tools from returning results longer than 40,000 characters. Instead, the LLM will receive a message saying it should be much more specific in its queries, which will hopefully guide it to be more specific.
|
This will *prevent* tools from returning results longer than 40,000 characters. Instead, the LLM will receive a message saying it should be much more specific in its queries, which will hopefully guide it to be more specific.
|
||||||
|
@ -163,7 +163,7 @@ These abstract away some of the tool definitions. They're called from each funct
|
||||||
|
|
||||||
*** Return heading (line)
|
*** Return heading (line)
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--heading ()
|
(defun gptel-org--heading ()
|
||||||
"Return the org-mode heading."
|
"Return the org-mode heading."
|
||||||
(concat
|
(concat
|
||||||
(buffer-substring-no-properties
|
(buffer-substring-no-properties
|
||||||
|
@ -173,7 +173,7 @@ These abstract away some of the tool definitions. They're called from each funct
|
||||||
|
|
||||||
*** Return heading and body (without subheadings)
|
*** Return heading and body (without subheadings)
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--heading-body ()
|
(defun gptel-org--heading-body ()
|
||||||
"Return the org-mode heading and body text."
|
"Return the org-mode heading and body text."
|
||||||
(concat
|
(concat
|
||||||
(buffer-substring-no-properties
|
(buffer-substring-no-properties
|
||||||
|
@ -185,7 +185,7 @@ These abstract away some of the tool definitions. They're called from each funct
|
||||||
|
|
||||||
*** Return heading and subheadings (until next same-level heading)
|
*** Return heading and subheadings (until next same-level heading)
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--heading-subtree ()
|
(defun gptel-org--heading-subtree ()
|
||||||
"Return the org-mode heading and all subheadings, with their body text."
|
"Return the org-mode heading and all subheadings, with their body text."
|
||||||
(concat
|
(concat
|
||||||
(buffer-substring-no-properties
|
(buffer-substring-no-properties
|
||||||
|
@ -204,16 +204,16 @@ 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
|
||||||
(defun gptel-org-tools--eval (elisp)
|
(defun gptel-org--eval (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)))
|
||||||
(buffer-string)))
|
(buffer-string)))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--eval
|
:function #'gptel-org--eval
|
||||||
:name "gptel-org-tools--eval"
|
:name "gptel-org--eval"
|
||||||
: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."))
|
||||||
|
@ -232,7 +232,7 @@ Not using ~ibuffer~ to avoid customization differences between users. Argument r
|
||||||
(let ((content (buffer-string)))
|
(let ((content (buffer-string)))
|
||||||
content)))
|
content)))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tool--list-buffers
|
:function #'gptel-org-tool--list-buffers
|
||||||
:name "gptel-org-tool--list-buffers"
|
:name "gptel-org-tool--list-buffers"
|
||||||
|
@ -252,7 +252,7 @@ You can customize the function to point to your org directory, if you wish. I'm
|
||||||
#+end_comment
|
#+end_comment
|
||||||
|
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--dir (dir)
|
(defun gptel-org--dir (dir)
|
||||||
"Return directory listing."
|
"Return directory listing."
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(dired (or dir "~"))
|
(dired (or dir "~"))
|
||||||
|
@ -260,10 +260,10 @@ You can customize the function to point to your org directory, if you wish. I'm
|
||||||
(kill-buffer (current-buffer))
|
(kill-buffer (current-buffer))
|
||||||
content)))
|
content)))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--dir
|
:function #'gptel-org--dir
|
||||||
:name "gptel-org-tools--dir"
|
:name "gptel-org--dir"
|
||||||
:description "List directory contents. After using this, stop. Evaluate for relevance. Then use your findings to fulfill user's request."
|
:description "List directory contents. After using this, stop. Evaluate for relevance. Then use your findings to fulfill user's request."
|
||||||
:args (list '(:name "dir"
|
:args (list '(:name "dir"
|
||||||
:type string
|
:type string
|
||||||
|
@ -275,17 +275,17 @@ You can customize the function to point to your org directory, if you wish. I'm
|
||||||
**** find-buffer-visiting [disabled by default]
|
**** find-buffer-visiting [disabled by default]
|
||||||
Disabled for now, as it's causing some issues.
|
Disabled for now, as it's causing some issues.
|
||||||
#+begin_src elisp :tangle no :results none
|
#+begin_src elisp :tangle no :results none
|
||||||
(defun gptel-org-tools--find-buffer-visiting (filename)
|
(defun gptel-org--find-buffer-visiting (filename)
|
||||||
"Return the buffer visiting file FILENAME."
|
"Return the buffer visiting file FILENAME."
|
||||||
(concat
|
(concat
|
||||||
(bufferp
|
(bufferp
|
||||||
(find-buffer-visiting
|
(find-buffer-visiting
|
||||||
(expand-file-name filename)))))
|
(expand-file-name filename)))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--find-buffer-visiting
|
:function #'gptel-org--find-buffer-visiting
|
||||||
:name "gptel-org-tools--find-buffer-visiting"
|
:name "gptel-org--find-buffer-visiting"
|
||||||
:description "Check if the file is open in a buffer. Usage (find-buffer-visiting filename)"
|
:description "Check if the file is open in a buffer. Usage (find-buffer-visiting filename)"
|
||||||
:args (list '(:name "filename"
|
:args (list '(:name "filename"
|
||||||
:type string
|
:type string
|
||||||
|
@ -296,15 +296,15 @@ Disabled for now, as it's causing some issues.
|
||||||
**** open-file-inactive
|
**** open-file-inactive
|
||||||
Opens a file into an inactive (background) buffer for processing.
|
Opens a file into an inactive (background) buffer for processing.
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--open-file-inactive (file)
|
(defun gptel-org--open-file-inactive (file)
|
||||||
"Open FILE in a background buffer without modifying its contents."
|
"Open FILE in a background buffer without modifying its contents."
|
||||||
(find-file-noselect file)
|
(find-file-noselect file)
|
||||||
(set-buffer-modified-p nil))
|
(set-buffer-modified-p nil))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--open-file-inactive
|
:function #'gptel-org--open-file-inactive
|
||||||
:name "gptel-org-tools--open-file-inactive"
|
:name "gptel-org--open-file-inactive"
|
||||||
:description "Open the file in a background buffer. This does NOT return the contents of the file. After calling this tool, stop. Then continue fulfilling user's request."
|
:description "Open the file in a background buffer. This does NOT return the contents of the file. After calling this tool, stop. Then continue fulfilling user's request."
|
||||||
:args (list '(:name "file"
|
:args (list '(:name "file"
|
||||||
:type string
|
:type string
|
||||||
|
@ -314,17 +314,17 @@ Opens a file into an inactive (background) buffer for processing.
|
||||||
|
|
||||||
**** read-file-contents
|
**** read-file-contents
|
||||||
#+begin_src elisp :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(defun gptel-org-tools--read-file-contents (file)
|
(defun gptel-org--read-file-contents (file)
|
||||||
"Return contents of FILE."
|
"Return contents of FILE."
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(insert-file-contents (expand-file-name filename))
|
(insert-file-contents (expand-file-name filename))
|
||||||
(concat
|
(concat
|
||||||
(buffer-string))))
|
(buffer-string))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--read-file-contents
|
:function #'gptel-org--read-file-contents
|
||||||
:name "gptel-org-tools--read-file-contents"
|
:name "gptel-org--read-file-contents"
|
||||||
:description "Reads and return the contents of a specified file. After calling this tool, stop. Then continue fulfilling user's request."
|
:description "Reads and return the contents of a specified file. After calling this tool, stop. Then continue fulfilling user's request."
|
||||||
:args (list '(:name "filename"
|
:args (list '(:name "filename"
|
||||||
:type string
|
:type string
|
||||||
|
@ -334,17 +334,17 @@ Opens a file into an inactive (background) buffer for processing.
|
||||||
#+end_src
|
#+end_src
|
||||||
**** describe-variable
|
**** describe-variable
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--describe-variable (var)
|
(defun gptel-org--describe-variable (var)
|
||||||
"Return documentation for VAR."
|
"Return documentation for VAR."
|
||||||
(let ((symbol (intern var)))
|
(let ((symbol (intern var)))
|
||||||
(if (boundp symbol)
|
(if (boundp symbol)
|
||||||
(prin1-to-string (symbol-value symbol))
|
(prin1-to-string (symbol-value symbol))
|
||||||
(format "Variable %s is not bound. This means the variable doesn't exist. Stop. Reassess what you're trying to do, examine the situation, and continue. " var))))
|
(format "Variable %s is not bound. This means the variable doesn't exist. Stop. Reassess what you're trying to do, examine the situation, and continue. " var))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--describe-variable
|
:function #'gptel-org--describe-variable
|
||||||
:name "gptel-org-tools--describe-variable"
|
:name "gptel-org--describe-variable"
|
||||||
:description "Returns variable contents. After calling this tool, stop. Evaluate if the result helps. Then continue fulfilling user's request."
|
:description "Returns variable contents. After calling this tool, stop. Evaluate if the result helps. Then continue fulfilling user's request."
|
||||||
:args (list '(:name "var"
|
:args (list '(:name "var"
|
||||||
:type string
|
:type string
|
||||||
|
@ -354,17 +354,17 @@ Opens a file into an inactive (background) buffer for processing.
|
||||||
|
|
||||||
**** describe-function
|
**** describe-function
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--describe-function (fun)
|
(defun gptel-org--describe-function (fun)
|
||||||
"Return documentation for FUN."
|
"Return documentation for FUN."
|
||||||
(let ((symbol (intern fun)))
|
(let ((symbol (intern fun)))
|
||||||
(if (fboundp symbol)
|
(if (fboundp symbol)
|
||||||
(prin1-to-string (documentation symbol 'function))
|
(prin1-to-string (documentation symbol 'function))
|
||||||
(format "Function %s is not defined." fun))))
|
(format "Function %s is not defined." fun))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--describe-function
|
:function #'gptel-org--describe-function
|
||||||
:name "gptel-org-tools--describe-function"
|
:name "gptel-org--describe-function"
|
||||||
:description "Returns function description. After calling this tool, stop. Evaluate if the result helps. Then continue fulfilling user's request."
|
:description "Returns function description. After calling this tool, stop. Evaluate if the result helps. Then continue fulfilling user's request."
|
||||||
:args (list '(:name "fun"
|
:args (list '(:name "fun"
|
||||||
:type string
|
:type string
|
||||||
|
@ -388,7 +388,7 @@ LLMs are not intelligent, despite claims to the contrary.
|
||||||
|
|
||||||
**** org-extract-tags
|
**** org-extract-tags
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--org-extract-tags (buffer)
|
(defun gptel-org--org-extract-tags (buffer)
|
||||||
"Return all tags from BUFFER."
|
"Return all tags from BUFFER."
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(let ((tags '()))
|
(let ((tags '()))
|
||||||
|
@ -401,10 +401,10 @@ LLMs are not intelligent, despite claims to the contrary.
|
||||||
(push tag tags))))))
|
(push tag tags))))))
|
||||||
(sort (-uniq tags) #'string<))))
|
(sort (-uniq tags) #'string<))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-extract-tags
|
:function #'gptel-org--org-extract-tags
|
||||||
:name "gptel-org-tools--org-extract-tags"
|
:name "gptel-org--org-extract-tags"
|
||||||
: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."))
|
||||||
|
@ -413,20 +413,20 @@ LLMs are not intelligent, despite claims to the contrary.
|
||||||
#+end_src
|
#+end_src
|
||||||
**** org-extract-headings
|
**** org-extract-headings
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--org-extract-headings (buffer)
|
(defun gptel-org--org-extract-headings (buffer)
|
||||||
"Return all headings from BUFFER."
|
"Return all headings from BUFFER."
|
||||||
(if (member buffer gptel-org-tools-skip-heading-extraction)
|
(if (member buffer gptel-org-skip-heading-extraction)
|
||||||
(user-error "Buffer %s has been blocked from this function by the user with reason: headings contain timestamps, no useful information. Use a different tool." buffer)
|
(user-error "Buffer %s has been blocked from this function by the user with reason: headings contain timestamps, no useful information. Use a different tool." buffer)
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(org-map-entries
|
(org-map-entries
|
||||||
#'gptel-org-tools--heading
|
#'gptel-org--heading
|
||||||
t
|
t
|
||||||
'file))))
|
'file))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-extract-headings
|
:function #'gptel-org--org-extract-headings
|
||||||
:name "gptel-org-tools--org-extract-headings"
|
:name "gptel-org--org-extract-headings"
|
||||||
:description "Returns all headings from an org-mode buffer. After using this, stop. Then evaluate the relevance of the headings to the user's request. Then continue."
|
:description "Returns all headings from an org-mode buffer. After using this, stop. Then evaluate the relevance of the headings to the user's request. Then continue."
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
|
@ -439,7 +439,7 @@ Ugly. LLMs sometimes return a sexp, and sometimes a quoted string for =query=. S
|
||||||
In general, this is deprecated, but left here for historical reasons. I may resurrect this if I find a good way of prompting the LLM to structure queries well.
|
In general, this is deprecated, but left here for historical reasons. I may resurrect this if I find a good way of prompting the LLM to structure queries well.
|
||||||
|
|
||||||
#+begin_src elisp :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(defun gptel-org-tools--org-ql-select (buf query)
|
(defun gptel-org--org-ql-select (buf query)
|
||||||
"Return entries matching QUERY from BUFFER.
|
"Return entries matching QUERY from BUFFER.
|
||||||
|
|
||||||
QUERY can be any valid org-ql-select query."
|
QUERY can be any valid org-ql-select query."
|
||||||
|
@ -448,13 +448,13 @@ QUERY can be any valid org-ql-select query."
|
||||||
(if (stringp query)
|
(if (stringp query)
|
||||||
(read query)
|
(read query)
|
||||||
query
|
query
|
||||||
:action #'gptel-org-tools--heading-body))))
|
:action #'gptel-org--heading-body))))
|
||||||
(gptel-org-tools--result-limit result)))
|
(gptel-org--result-limit result)))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select
|
:function #'gptel-org--org-ql-select
|
||||||
:name "gptel-org-tools--org-ql-select"
|
:name "gptel-org--org-ql-select"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. Can be multiple buffers. See the NAME column in `list-buffers`.")
|
:description "The name of the buffer. Can be multiple buffers. See the NAME column in `list-buffers`.")
|
||||||
|
@ -483,7 +483,7 @@ The reason for this tool existing (it's the same as select-heading), is that it
|
||||||
|
|
||||||
|
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--org-ql-select-date (buf date)
|
(defun gptel-org--org-ql-select-date (buf date)
|
||||||
"Returns all timestamped headings matching the specified date or date range.
|
"Returns all timestamped headings matching the specified date or date range.
|
||||||
The date can be in the format YYYY, YYYY-MM, or YYYY-MM-DD.
|
The date can be in the format YYYY, YYYY-MM, or YYYY-MM-DD.
|
||||||
|
|
||||||
|
@ -496,20 +496,20 @@ DATE is the date or date range to match."
|
||||||
(let ((result
|
(let ((result
|
||||||
(org-ql-select buffer
|
(org-ql-select buffer
|
||||||
`(heading ,date)
|
`(heading ,date)
|
||||||
:action #'gptel-org-tools--heading-subtree)))
|
:action #'gptel-org--heading-subtree)))
|
||||||
(concat (gptel-org-tools--result-limit result) "Results end here. Proceed with the next action."))
|
(concat (gptel-org--result-limit result) "Results end here. Proceed with the next action."))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
#+RESULTS:
|
#+RESULTS:
|
||||||
: gptel-org-tools--org-ql-select-date
|
: gptel-org--org-ql-select-date
|
||||||
|
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-date
|
:function #'gptel-org--org-ql-select-date
|
||||||
:name "gptel-org-tools--org-ql-select-date"
|
:name "gptel-org--org-ql-select-date"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "Buffer name.")
|
:description "Buffer name.")
|
||||||
|
@ -530,18 +530,18 @@ This is still work in progress, the idea is to have the LLM check my calendar an
|
||||||
|
|
||||||
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
|
||||||
(defun gptel-org-tools--org-agenda-seek (days)
|
(defun gptel-org--org-agenda-seek (days)
|
||||||
"Return the results of org-agenda-list spanning now to DAYS."
|
"Return the results of org-agenda-list spanning now to DAYS."
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(org-agenda-list (or days 14))
|
(org-agenda-list (or days 14))
|
||||||
(let ((content (buffer-string)))
|
(let ((content (buffer-string)))
|
||||||
(kill-buffer (current-buffer))
|
(kill-buffer (current-buffer))
|
||||||
(gptel-org-tools--result-limit content))))
|
(gptel-org--result-limit content))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-agenda-seek
|
:function #'gptel-org--org-agenda-seek
|
||||||
:name "gptel-org-tools--org-agenda-seek"
|
:name "gptel-org--org-agenda-seek"
|
||||||
:args (list '(:name "days"
|
:args (list '(:name "days"
|
||||||
:type integer
|
:type integer
|
||||||
:description "Days. Positive = future. Negative = past. Default: 14"))
|
:description "Days. Positive = future. Negative = past. Default: 14"))
|
||||||
|
@ -555,7 +555,7 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
***** org-ql-select-headings
|
***** org-ql-select-headings
|
||||||
Retrieve the headings where the heading matches query. This is very much the same as ~org-ql-select-by-date~, but the descriptions are different to ensure the LLM knows what to do.
|
Retrieve the headings where the heading matches query. This is very much the same as ~org-ql-select-by-date~, but the descriptions are different to ensure the LLM knows what to do.
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--org-ql-select-headings (buf query)
|
(defun gptel-org--org-ql-select-headings (buf query)
|
||||||
"Return headings matching QUERY from BUFFER."
|
"Return headings matching QUERY from BUFFER."
|
||||||
(let* ((buffer (get-buffer buf))
|
(let* ((buffer (get-buffer buf))
|
||||||
(mode (buffer-local-value 'major-mode buffer)))
|
(mode (buffer-local-value 'major-mode buffer)))
|
||||||
|
@ -565,17 +565,17 @@ Retrieve the headings where the heading matches query. This is very much the sam
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(get-buffer buf)
|
(get-buffer buf)
|
||||||
`(heading ,query)
|
`(heading ,query)
|
||||||
:action #''gptel-org-tools--heading)))
|
:action #''gptel-org--heading)))
|
||||||
(concat (gptel-org-tools--result-limit result) "Results end here. Proceed with the next action."))
|
(concat (gptel-org--result-limit result) "Results end here. Proceed with the next action."))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-headings
|
:function #'gptel-org--org-ql-select-headings
|
||||||
:name "gptel-org-tools--org-ql-select-headings"
|
:name "gptel-org--org-ql-select-headings"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. See the NAME column in ~list-buffers~.")
|
:description "The name of the buffer. See the NAME column in ~list-buffers~.")
|
||||||
|
@ -589,20 +589,20 @@ Retrieve the headings where the heading matches query. This is very much the sam
|
||||||
***** 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--org-ql-select-headings-rifle (buf query)
|
||||||
"Return headings of entries (body included) that match keyword QUERY from BUFFER."
|
"Return headings of entries (body included) that match keyword QUERY from BUFFER."
|
||||||
(let ((result
|
(let ((result
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(get-buffer buf)
|
(get-buffer buf)
|
||||||
`(rifle ,query)
|
`(rifle ,query)
|
||||||
:action #'gptel-org-tools--heading)))
|
:action #'gptel-org--heading)))
|
||||||
(gptel-org-tools--result-limit result)))
|
(gptel-org--result-limit result)))
|
||||||
|
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-headings-rifle
|
:function #'gptel-org--org-ql-select-headings-rifle
|
||||||
:name "gptel-org-tools--org-ql-select-headings-rifle"
|
:name "gptel-org--org-ql-select-headings-rifle"
|
||||||
:description "Returns headings matching QUERY from BUFFER. Matches against both heading and content, but only returns headings. After using this, stop. Evaluate results. Then continue completing user's request."
|
:description "Returns headings matching QUERY from BUFFER. Matches against both heading and content, but only returns headings. After using this, stop. Evaluate results. Then continue completing user's request."
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
|
@ -626,16 +626,16 @@ This pulls all the headings (and their contents) when they match tags (without i
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(get-buffer buf)
|
(get-buffer buf)
|
||||||
`(tags-local ,query)
|
`(tags-local ,query)
|
||||||
:action #'gptel-org-tools--heading-body)))
|
:action #'gptel-org--heading-body)))
|
||||||
(gptel-org-tools--result-limit result))
|
(gptel-org--result-limit result))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-tags-local
|
:function #'gptel-org--org-ql-select-tags-local
|
||||||
:name "gptel-org-tools--org-ql-select-tags-local"
|
:name "gptel-org--org-ql-select-tags-local"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
||||||
|
@ -649,7 +649,7 @@ This pulls all the headings (and their contents) when they match tags (without i
|
||||||
***** org-ql-select-tags-local-count
|
***** org-ql-select-tags-local-count
|
||||||
This pulls all the local tags (without inheritance) from buffer, and returns the number of these tagged headings.
|
This pulls all the local tags (without inheritance) from buffer, and returns the number of these tagged headings.
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(defun gptel-org-tools--org-ql-select-tags-local-count (buf query)
|
(defun gptel-org--org-ql-select-tags-local-count (buf query)
|
||||||
"Return count of entries tagged QUERY in BUFFER."
|
"Return count of entries tagged QUERY in BUFFER."
|
||||||
(let* ((buffer (get-buffer buf))
|
(let* ((buffer (get-buffer buf))
|
||||||
(mode (buffer-local-value 'major-mode buffer)))
|
(mode (buffer-local-value 'major-mode buffer)))
|
||||||
|
@ -658,14 +658,14 @@ This pulls all the local tags (without inheritance) from buffer, and returns the
|
||||||
(length (org-ql-select
|
(length (org-ql-select
|
||||||
(get-buffer buf)
|
(get-buffer buf)
|
||||||
`(tags-local ,query)
|
`(tags-local ,query)
|
||||||
:action #'gptel-org-tools--heading-body))
|
:action #'gptel-org--heading-body))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-tags-local-count
|
:function #'gptel-org--org-ql-select-tags-local-count
|
||||||
:name "gptel-org-tools--org-ql-select-tags-local"
|
:name "gptel-org--org-ql-select-tags-local"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
||||||
|
@ -679,7 +679,7 @@ This pulls all the local tags (without inheritance) from buffer, and returns the
|
||||||
***** 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--org-ql-select-tags (buf query)
|
||||||
"Return every entry tagged QUERY from BUFFER."
|
"Return every entry tagged QUERY from BUFFER."
|
||||||
(let* ((buffer (get-buffer buf))
|
(let* ((buffer (get-buffer buf))
|
||||||
(mode (buffer-local-value 'major-mode buffer)))
|
(mode (buffer-local-value 'major-mode buffer)))
|
||||||
|
@ -689,15 +689,15 @@ This pulls all the headings (and their contents) when they match tags (with inhe
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(get-buffer buf)
|
(get-buffer buf)
|
||||||
`(tags ,query)
|
`(tags ,query)
|
||||||
:action #'gptel-org-tools--heading-body))
|
:action #'gptel-org--heading-body))
|
||||||
(gptel-org-tools--result-limit result)))
|
(gptel-org--result-limit result)))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-tags
|
:function #'gptel-org--org-ql-select-tags
|
||||||
:name "gptel-org-tools--org-ql-select-tags"
|
:name "gptel-org--org-ql-select-tags"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
||||||
|
@ -711,7 +711,7 @@ This pulls all the headings (and their contents) when they match tags (with inhe
|
||||||
***** 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--org-ql-select-rifle (buf query)
|
||||||
"Return every entry matching keyword QUERY from BUFFER."
|
"Return every entry matching keyword QUERY from BUFFER."
|
||||||
(let* ((buffer (get-buffer buf))
|
(let* ((buffer (get-buffer buf))
|
||||||
(mode (buffer-local-value 'major-mode buffer)))
|
(mode (buffer-local-value 'major-mode buffer)))
|
||||||
|
@ -721,15 +721,15 @@ And, the "grab everything that matches" tool.
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
buffer
|
buffer
|
||||||
`(rifle ,query)
|
`(rifle ,query)
|
||||||
:action #'gptel-org-tools--heading-body)))
|
:action #'gptel-org--heading-body)))
|
||||||
(gptel-org-tools--result-limit result))
|
(gptel-org--result-limit result))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-rifle
|
:function #'gptel-org--org-ql-select-rifle
|
||||||
:name "gptel-org-tools--org-ql-select-rifle"
|
:name "gptel-org--org-ql-select-rifle"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
||||||
|
@ -742,7 +742,7 @@ And, the "grab everything that matches" tool.
|
||||||
|
|
||||||
***** org-ql-select-regexp
|
***** org-ql-select-regexp
|
||||||
#+begin_src elisp :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(defun gptel-org-tools--org-ql-select-regexp (buf query)
|
(defun gptel-org--org-ql-select-regexp (buf query)
|
||||||
(let* ((buffer (get-buffer buf))
|
(let* ((buffer (get-buffer buf))
|
||||||
(mode (buffer-local-value 'major-mode buffer)))
|
(mode (buffer-local-value 'major-mode buffer)))
|
||||||
(if buffer
|
(if buffer
|
||||||
|
@ -751,16 +751,16 @@ And, the "grab everything that matches" tool.
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
buffer
|
buffer
|
||||||
`(regexp ,query)
|
`(regexp ,query)
|
||||||
:action #'gptel-org-tools--heading-body)))
|
:action #'gptel-org--heading-body)))
|
||||||
(gptel-org-tools--result-limit result))
|
(gptel-org--result-limit result))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-regexp
|
:function #'gptel-org--org-ql-select-regexp
|
||||||
:name "gptel-org-tools--org-ql-select-regexp"
|
:name "gptel-org--org-ql-select-regexp"
|
||||||
:description "Run regexp on ALL files at once."
|
:description "Run regexp on ALL files at once."
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
|
@ -775,21 +775,21 @@ And, the "grab everything that matches" tool.
|
||||||
***** org-ql-select-all-tags-local
|
***** org-ql-select-all-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 :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(defun gptel-org-tools--org-ql-select-all-tags-local (query)
|
(defun gptel-org--org-ql-select-all-tags-local (query)
|
||||||
"Return entries whose tags match QUERY in org-agenda-files.
|
"Return entries whose tags match QUERY in org-agenda-files.
|
||||||
QUERY is the tag to search for."
|
QUERY is the tag to search for."
|
||||||
(let ((result
|
(let ((result
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(org-agenda-files)
|
(org-agenda-files)
|
||||||
`(tags-local ,query)
|
`(tags-local ,query)
|
||||||
:action #'gptel-org-tools--heading-body)))
|
:action #'gptel-org--heading-body)))
|
||||||
(gptel-org-tools--result-limit result)))
|
(gptel-org--result-limit result)))
|
||||||
|
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-all-tags-local
|
:function #'gptel-org--org-ql-select-all-tags-local
|
||||||
:name "gptel-org-tools--org-ql-select-all-tags-local"
|
:name "gptel-org--org-ql-select-all-tags-local"
|
||||||
:args (list '(:name "query"
|
:args (list '(:name "query"
|
||||||
:type string
|
:type string
|
||||||
:description "A single word to scan for."))
|
:description "A single word to scan for."))
|
||||||
|
@ -800,7 +800,7 @@ This pulls all the headings (and their contents) when they match tags (without i
|
||||||
***** org-ql-select-all-tags
|
***** org-ql-select-all-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 :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(defun gptel-org-tools--org-ql-select-all-tags (query)
|
(defun gptel-org--org-ql-select-all-tags (query)
|
||||||
"Return entries whose tags match QUERY,
|
"Return entries whose tags match QUERY,
|
||||||
with inheritance, in org-agenda-files.
|
with inheritance, in org-agenda-files.
|
||||||
QUERY is the tag to search for."
|
QUERY is the tag to search for."
|
||||||
|
@ -808,13 +808,13 @@ with inheritance, in org-agenda-files.
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(org-agenda-files)
|
(org-agenda-files)
|
||||||
`(tags ,query)
|
`(tags ,query)
|
||||||
:action #'gptel-org-tools--heading-body)))
|
:action #'gptel-org--heading-body)))
|
||||||
(gptel-org-tools--result-limit result)))
|
(gptel-org--result-limit result)))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-all-tags
|
:function #'gptel-org--org-ql-select-all-tags
|
||||||
:name "gptel-org-tools--org-ql-select-all-tags"
|
:name "gptel-org--org-ql-select-all-tags"
|
||||||
:args (list '(:name "query"
|
:args (list '(:name "query"
|
||||||
:type string
|
:type string
|
||||||
:description "A simple (single) string to scan for."))
|
:description "A simple (single) string to scan for."))
|
||||||
|
@ -834,20 +834,20 @@ Note that I define my agenda in this way:
|
||||||
This means that /every org-mode file I have/ is part of this search. If you're using a different set-up, or want to only use the LLM on specific files, then you should modify this function appropriately.
|
This means that /every org-mode file I have/ is part of this search. If you're using a different set-up, or want to only use the LLM on specific files, then you should modify this function appropriately.
|
||||||
|
|
||||||
#+begin_src elisp :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(defun gptel-org-tools--org-ql-select-all-rifle (query)
|
(defun gptel-org--org-ql-select-all-rifle (query)
|
||||||
"Return entries containing QUERY from org-agenda-files.
|
"Return entries containing QUERY from org-agenda-files.
|
||||||
QUERY is the keyword to search for."
|
QUERY is the keyword to search for."
|
||||||
(let ((result
|
(let ((result
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(org-agenda-files)
|
(org-agenda-files)
|
||||||
`(rifle ,query)
|
`(rifle ,query)
|
||||||
:action #'gptel-org-tools--heading-body)))
|
:action #'gptel-org--heading-body)))
|
||||||
(gptel-org-tools--result-limit result)))
|
(gptel-org--result-limit result)))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-all-rifle
|
:function #'gptel-org--org-ql-select-all-rifle
|
||||||
:name "gptel-org-tools--org-ql-select-all-rifle"
|
:name "gptel-org--org-ql-select-all-rifle"
|
||||||
:description "Run simple (single) string against ALL org-mode files (notes)."
|
:description "Run simple (single) string against ALL org-mode files (notes)."
|
||||||
:args (list '(:name "query"
|
:args (list '(:name "query"
|
||||||
:type string
|
:type string
|
||||||
|
@ -859,20 +859,20 @@ This means that /every org-mode file I have/ is part of this search. If you're u
|
||||||
|
|
||||||
***** org-ql-select-all-regexp
|
***** org-ql-select-all-regexp
|
||||||
#+begin_src elisp :tangle no
|
#+begin_src elisp :tangle no
|
||||||
(defun gptel-org-tools--org-ql-select-all-regexp (query)
|
(defun gptel-org--org-ql-select-all-regexp (query)
|
||||||
"Return all entries matching regexp QUERY in org-agenda-files.
|
"Return all entries matching regexp QUERY in org-agenda-files.
|
||||||
QUERY is a regular expression."
|
QUERY is a regular expression."
|
||||||
(let ((result
|
(let ((result
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(org-agenda-files)
|
(org-agenda-files)
|
||||||
`(regexp ,query)
|
`(regexp ,query)
|
||||||
:action #'gptel-org-tools--heading-body)))
|
:action #'gptel-org--heading-body)))
|
||||||
(gptel-org-tools--result-limit result)))
|
(gptel-org--result-limit result)))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-all-regexp
|
:function #'gptel-org--org-ql-select-all-regexp
|
||||||
:name "gptel-org-tools--org-ql-select-all-regexp"
|
:name "gptel-org--org-ql-select-all-regexp"
|
||||||
:description "Run regexp on ALL files at once."
|
:description "Run regexp on ALL files at once."
|
||||||
:args (list '(:name "query"
|
:args (list '(:name "query"
|
||||||
:type string
|
:type string
|
||||||
|
@ -883,6 +883,6 @@ This means that /every org-mode file I have/ is part of this search. If you're u
|
||||||
|
|
||||||
** End
|
** End
|
||||||
#+begin_src elisp
|
#+begin_src elisp
|
||||||
(provide 'gptel-org-tools)
|
(provide 'gptel-org)
|
||||||
;;; gptel-org-tools.el ends here
|
;;; gptel-org.el ends here
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
;;; gptel-org-tools.el --- LLM Tools for org-mode interaction. -*- lexical-binding: t; -*-
|
;;; gptel-org.el --- LLM Tools for org-mode interaction. -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
;; Copyright (C) 2025 Phil Bajsicki
|
;; Copyright (C) 2025 Phil Bajsicki
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
;; Author: Phil Bajsicki <phil@bajsicki.com>
|
;; Author: Phil Bajsicki <phil@bajsicki.com>
|
||||||
;; Version: 0.0.2
|
;; Version: 0.0.2
|
||||||
;; Package-Requires: ((emacs "30.1") (gptel 0.9.8) (org-ql 0.9))
|
;; Package-Requires: ((emacs "30.1") (gptel 0.9.8) (org-ql 0.9))
|
||||||
;; URL: https://github.com/phil/gptel-org-tools
|
;; URL: https://github.com/phil/gptel-org
|
||||||
;; SPDX-License-Identifier: GPL-3.0
|
;; SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
;; This program is free software; you can redistribute it and/or modify
|
;; This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -30,29 +30,29 @@
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
|
|
||||||
;; 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
|
||||||
|
|
||||||
;;; Code:)
|
;;; Code:)
|
||||||
|
|
||||||
(defvar gptel-org-tools '())
|
(defvar gptel-org '())
|
||||||
|
|
||||||
(defvar gptel-org-tools-skip-heading-extraction '())
|
(defvar gptel-org-skip-heading-extraction '())
|
||||||
|
|
||||||
(defvar gptel-org-tools-result-limit 40000)
|
(defvar gptel-org-result-limit 40000)
|
||||||
|
|
||||||
(defun gptel-org-tools--result-limit (result)
|
(defun gptel-org--result-limit (result)
|
||||||
(if (>= (length (format "%s" result)) gptel-org-tools-result-limit)
|
(if (>= (length (format "%s" result)) gptel-org-result-limit)
|
||||||
(format "Results over %s character. Stop. Analyze. Find a different solution, or use a more specific query." gptel-org-tools-result-limit)
|
(format "Results over %s character. Stop. Analyze. Find a different solution, or use a more specific query." gptel-org-result-limit)
|
||||||
result))
|
result))
|
||||||
|
|
||||||
(defun gptel-org-tools--heading ()
|
(defun gptel-org--heading ()
|
||||||
"Return the org-mode heading."
|
"Return the org-mode heading."
|
||||||
(concat
|
(concat
|
||||||
(buffer-substring-no-properties
|
(buffer-substring-no-properties
|
||||||
(line-beginning-position)
|
(line-beginning-position)
|
||||||
(line-end-position))))
|
(line-end-position))))
|
||||||
|
|
||||||
(defun gptel-org-tools--heading-body ()
|
(defun gptel-org--heading-body ()
|
||||||
"Return the org-mode heading and body text."
|
"Return the org-mode heading and body text."
|
||||||
(concat
|
(concat
|
||||||
(buffer-substring-no-properties
|
(buffer-substring-no-properties
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
(outline-next-heading)
|
(outline-next-heading)
|
||||||
(line-beginning-position)))))
|
(line-beginning-position)))))
|
||||||
|
|
||||||
(defun gptel-org-tools--heading-subtree ()
|
(defun gptel-org--heading-subtree ()
|
||||||
"Return the org-mode heading and all subheadings, with their body text."
|
"Return the org-mode heading and all subheadings, with their body text."
|
||||||
(concat
|
(concat
|
||||||
(buffer-substring-no-properties
|
(buffer-substring-no-properties
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
(let ((content (buffer-string)))
|
(let ((content (buffer-string)))
|
||||||
content)))
|
content)))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tool--list-buffers
|
:function #'gptel-org-tool--list-buffers
|
||||||
:name "gptel-org-tool--list-buffers"
|
:name "gptel-org-tool--list-buffers"
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
:category "emacs"
|
:category "emacs"
|
||||||
:description "List buffers open in Emacs, including file names and full paths. After using this, stop. Then evaluate which files are most likely to be relevant to the user's request."))
|
:description "List buffers open in Emacs, including file names and full paths. After using this, stop. Then evaluate which files are most likely to be relevant to the user's request."))
|
||||||
|
|
||||||
(defun gptel-org-tools--dir (dir)
|
(defun gptel-org--dir (dir)
|
||||||
"Return directory listing."
|
"Return directory listing."
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(dired (or dir "~"))
|
(dired (or dir "~"))
|
||||||
|
@ -94,10 +94,10 @@
|
||||||
(kill-buffer (current-buffer))
|
(kill-buffer (current-buffer))
|
||||||
content)))
|
content)))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--dir
|
:function #'gptel-org--dir
|
||||||
:name "gptel-org-tools--dir"
|
:name "gptel-org--dir"
|
||||||
:description "List directory contents. After using this, stop. Evaluate for relevance. Then use your findings to fulfill user's request."
|
:description "List directory contents. After using this, stop. Evaluate for relevance. Then use your findings to fulfill user's request."
|
||||||
:args (list '(:name "dir"
|
:args (list '(:name "dir"
|
||||||
:type string
|
:type string
|
||||||
|
@ -105,49 +105,49 @@
|
||||||
:optional t))
|
:optional t))
|
||||||
:category "filesystem"))
|
:category "filesystem"))
|
||||||
|
|
||||||
(defun gptel-org-tools--open-file-inactive (file)
|
(defun gptel-org--open-file-inactive (file)
|
||||||
"Open FILE in a background buffer without modifying its contents."
|
"Open FILE in a background buffer without modifying its contents."
|
||||||
(find-file-noselect file)
|
(find-file-noselect file)
|
||||||
(set-buffer-modified-p nil))
|
(set-buffer-modified-p nil))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--open-file-inactive
|
:function #'gptel-org--open-file-inactive
|
||||||
:name "gptel-org-tools--open-file-inactive"
|
:name "gptel-org--open-file-inactive"
|
||||||
:description "Open the file in a background buffer. This does NOT return the contents of the file. After calling this tool, stop. Then continue fulfilling user's request."
|
:description "Open the file in a background buffer. This does NOT return the contents of the file. After calling this tool, stop. Then continue fulfilling user's request."
|
||||||
: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--describe-variable (var)
|
(defun gptel-org--describe-variable (var)
|
||||||
"Return documentation for VAR."
|
"Return documentation for VAR."
|
||||||
(let ((symbol (intern var)))
|
(let ((symbol (intern var)))
|
||||||
(if (boundp symbol)
|
(if (boundp symbol)
|
||||||
(prin1-to-string (symbol-value symbol))
|
(prin1-to-string (symbol-value symbol))
|
||||||
(format "Variable %s is not bound. This means the variable doesn't exist. Stop. Reassess what you're trying to do, examine the situation, and continue. " var))))
|
(format "Variable %s is not bound. This means the variable doesn't exist. Stop. Reassess what you're trying to do, examine the situation, and continue. " var))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--describe-variable
|
:function #'gptel-org--describe-variable
|
||||||
:name "gptel-org-tools--describe-variable"
|
:name "gptel-org--describe-variable"
|
||||||
:description "Returns variable contents. After calling this tool, stop. Evaluate if the result helps. Then continue fulfilling user's request."
|
:description "Returns variable contents. After calling this tool, stop. Evaluate if the result helps. Then continue fulfilling user's request."
|
||||||
:args (list '(:name "var"
|
:args (list '(:name "var"
|
||||||
:type string
|
:type string
|
||||||
:description "Variable name"))
|
:description "Variable name"))
|
||||||
:category "emacs"))
|
:category "emacs"))
|
||||||
|
|
||||||
(defun gptel-org-tools--describe-function (fun)
|
(defun gptel-org--describe-function (fun)
|
||||||
"Return documentation for FUN."
|
"Return documentation for FUN."
|
||||||
(let ((symbol (intern fun)))
|
(let ((symbol (intern fun)))
|
||||||
(if (fboundp symbol)
|
(if (fboundp symbol)
|
||||||
(prin1-to-string (documentation symbol 'function))
|
(prin1-to-string (documentation symbol 'function))
|
||||||
(format "Function %s is not defined." fun))))
|
(format "Function %s is not defined." fun))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--describe-function
|
:function #'gptel-org--describe-function
|
||||||
:name "gptel-org-tools--describe-function"
|
:name "gptel-org--describe-function"
|
||||||
:description "Returns function description. After calling this tool, stop. Evaluate if the result helps. Then continue fulfilling user's request."
|
:description "Returns function description. After calling this tool, stop. Evaluate if the result helps. Then continue fulfilling user's request."
|
||||||
:args (list '(:name "fun"
|
:args (list '(:name "fun"
|
||||||
:type string
|
:type string
|
||||||
|
@ -155,7 +155,7 @@
|
||||||
:optional t))
|
:optional t))
|
||||||
:category "emacs"))
|
:category "emacs"))
|
||||||
|
|
||||||
(defun gptel-org-tools--org-extract-tags (buffer)
|
(defun gptel-org--org-extract-tags (buffer)
|
||||||
"Return all tags from BUFFER."
|
"Return all tags from BUFFER."
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(let ((tags '()))
|
(let ((tags '()))
|
||||||
|
@ -168,37 +168,37 @@
|
||||||
(push tag tags))))))
|
(push tag tags))))))
|
||||||
(sort (-uniq tags) #'string<))))
|
(sort (-uniq tags) #'string<))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-extract-tags
|
:function #'gptel-org--org-extract-tags
|
||||||
:name "gptel-org-tools--org-extract-tags"
|
:name "gptel-org--org-extract-tags"
|
||||||
: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"
|
||||||
:description "Returns all tags from an org-mode buffer. When using this, evaluate the relevance of each tag to the user's request. After calling this tool, stop. Then continue fulfilling user's request."))
|
:description "Returns all tags from an org-mode buffer. When using this, evaluate the relevance of each tag to the user's request. After calling this tool, stop. Then continue fulfilling user's request."))
|
||||||
|
|
||||||
(defun gptel-org-tools--org-extract-headings (buffer)
|
(defun gptel-org--org-extract-headings (buffer)
|
||||||
"Return all headings from BUFFER."
|
"Return all headings from BUFFER."
|
||||||
(if (member buffer gptel-org-tools-skip-heading-extraction)
|
(if (member buffer gptel-org-skip-heading-extraction)
|
||||||
(user-error "Buffer %s has been blocked from this function by the user with reason: headings contain timestamps, no useful information. Use a different tool." buffer)
|
(user-error "Buffer %s has been blocked from this function by the user with reason: headings contain timestamps, no useful information. Use a different tool." buffer)
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(org-map-entries
|
(org-map-entries
|
||||||
#'gptel-org-tools--heading
|
#'gptel-org--heading
|
||||||
t
|
t
|
||||||
'file))))
|
'file))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-extract-headings
|
:function #'gptel-org--org-extract-headings
|
||||||
:name "gptel-org-tools--org-extract-headings"
|
:name "gptel-org--org-extract-headings"
|
||||||
:description "Returns all headings from an org-mode buffer. After using this, stop. Then evaluate the relevance of the headings to the user's request. Then continue."
|
:description "Returns all headings from an org-mode buffer. After using this, stop. Then evaluate the relevance of the headings to the user's request. Then continue."
|
||||||
: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-date (buf date)
|
(defun gptel-org--org-ql-select-date (buf date)
|
||||||
"Returns all timestamped headings matching the specified date or date range.
|
"Returns all timestamped headings matching the specified date or date range.
|
||||||
The date can be in the format YYYY, YYYY-MM, or YYYY-MM-DD.
|
The date can be in the format YYYY, YYYY-MM, or YYYY-MM-DD.
|
||||||
|
|
||||||
|
@ -211,15 +211,15 @@ DATE is the date or date range to match."
|
||||||
(let ((result
|
(let ((result
|
||||||
(org-ql-select buffer
|
(org-ql-select buffer
|
||||||
`(heading ,date)
|
`(heading ,date)
|
||||||
:action #'gptel-org-tools--heading-subtree)))
|
:action #'gptel-org--heading-subtree)))
|
||||||
(concat (gptel-org-tools--result-limit result) "Results end here. Proceed with the next action."))
|
(concat (gptel-org--result-limit result) "Results end here. Proceed with the next action."))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-date
|
:function #'gptel-org--org-ql-select-date
|
||||||
:name "gptel-org-tools--org-ql-select-date"
|
:name "gptel-org--org-ql-select-date"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "Buffer name.")
|
:description "Buffer name.")
|
||||||
|
@ -234,18 +234,18 @@ Examples:
|
||||||
|
|
||||||
After using this, stop. Then evaluate the relevance of the entries to the user's request. Then continue."))
|
After using this, stop. Then evaluate the relevance of the entries to the user's request. Then continue."))
|
||||||
|
|
||||||
(defun gptel-org-tools--org-agenda-seek (days)
|
(defun gptel-org--org-agenda-seek (days)
|
||||||
"Return the results of org-agenda-list spanning now to DAYS."
|
"Return the results of org-agenda-list spanning now to DAYS."
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(org-agenda-list (or days 14))
|
(org-agenda-list (or days 14))
|
||||||
(let ((content (buffer-string)))
|
(let ((content (buffer-string)))
|
||||||
(kill-buffer (current-buffer))
|
(kill-buffer (current-buffer))
|
||||||
(gptel-org-tools--result-limit content))))
|
(gptel-org--result-limit content))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-agenda-seek
|
:function #'gptel-org--org-agenda-seek
|
||||||
:name "gptel-org-tools--org-agenda-seek"
|
:name "gptel-org--org-agenda-seek"
|
||||||
:args (list '(:name "days"
|
:args (list '(:name "days"
|
||||||
:type integer
|
:type integer
|
||||||
:description "Days. Positive = future. Negative = past. Default: 14"))
|
:description "Days. Positive = future. Negative = past. Default: 14"))
|
||||||
|
@ -255,7 +255,7 @@ After using this, stop. Then evaluate the relevance of the entries to the user's
|
||||||
- Get all tasks in the last 14 days: \"-14\"
|
- Get all tasks in the last 14 days: \"-14\"
|
||||||
After using this, stop. Evaluate the relevance of the results. Then continue."))
|
After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
|
|
||||||
(defun gptel-org-tools--org-ql-select-headings (buf query)
|
(defun gptel-org--org-ql-select-headings (buf query)
|
||||||
"Return headings matching QUERY from BUFFER."
|
"Return headings matching QUERY from BUFFER."
|
||||||
(let* ((buffer (get-buffer buf))
|
(let* ((buffer (get-buffer buf))
|
||||||
(mode (buffer-local-value 'major-mode buffer)))
|
(mode (buffer-local-value 'major-mode buffer)))
|
||||||
|
@ -265,17 +265,17 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(get-buffer buf)
|
(get-buffer buf)
|
||||||
`(heading ,query)
|
`(heading ,query)
|
||||||
:action #''gptel-org-tools--heading)))
|
:action #''gptel-org--heading)))
|
||||||
(concat (gptel-org-tools--result-limit result) "Results end here. Proceed with the next action."))
|
(concat (gptel-org--result-limit result) "Results end here. Proceed with the next action."))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-headings
|
:function #'gptel-org--org-ql-select-headings
|
||||||
:name "gptel-org-tools--org-ql-select-headings"
|
:name "gptel-org--org-ql-select-headings"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. See the NAME column in ~list-buffers~.")
|
:description "The name of the buffer. See the NAME column in ~list-buffers~.")
|
||||||
|
@ -285,20 +285,20 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
:category "org-ql"
|
:category "org-ql"
|
||||||
:description "Returns entries matching QUERY from BUFFER. Matches only a single string. After using this, evaluate which entries are relevant, and continue with user's request."))
|
:description "Returns entries matching QUERY from BUFFER. Matches only a single string. After using this, evaluate which entries are relevant, and continue with user's request."))
|
||||||
|
|
||||||
(defun gptel-org-tools--org-ql-select-headings-rifle (buf query)
|
(defun gptel-org--org-ql-select-headings-rifle (buf query)
|
||||||
"Return headings of entries (body included) that match keyword QUERY from BUFFER."
|
"Return headings of entries (body included) that match keyword QUERY from BUFFER."
|
||||||
(let ((result
|
(let ((result
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(get-buffer buf)
|
(get-buffer buf)
|
||||||
`(rifle ,query)
|
`(rifle ,query)
|
||||||
:action #'gptel-org-tools--heading)))
|
:action #'gptel-org--heading)))
|
||||||
(gptel-org-tools--result-limit result)))
|
(gptel-org--result-limit result)))
|
||||||
|
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-headings-rifle
|
:function #'gptel-org--org-ql-select-headings-rifle
|
||||||
:name "gptel-org-tools--org-ql-select-headings-rifle"
|
:name "gptel-org--org-ql-select-headings-rifle"
|
||||||
:description "Returns headings matching QUERY from BUFFER. Matches against both heading and content, but only returns headings. After using this, stop. Evaluate results. Then continue completing user's request."
|
:description "Returns headings matching QUERY from BUFFER. Matches against both heading and content, but only returns headings. After using this, stop. Evaluate results. Then continue completing user's request."
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
|
@ -318,16 +318,16 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(get-buffer buf)
|
(get-buffer buf)
|
||||||
`(tags-local ,query)
|
`(tags-local ,query)
|
||||||
:action #'gptel-org-tools--heading-body)))
|
:action #'gptel-org--heading-body)))
|
||||||
(gptel-org-tools--result-limit result))
|
(gptel-org--result-limit result))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-tags-local
|
:function #'gptel-org--org-ql-select-tags-local
|
||||||
:name "gptel-org-tools--org-ql-select-tags-local"
|
:name "gptel-org--org-ql-select-tags-local"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
||||||
|
@ -337,7 +337,7 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
:category "org-ql"
|
:category "org-ql"
|
||||||
:description "Returns entries whose tags match QUERY from BUFFER, without tag inheritance. After using this, stop. Evaluate results for relevance, then proceed with completing user's request."))
|
:description "Returns entries whose tags match QUERY from BUFFER, without tag inheritance. After using this, stop. Evaluate results for relevance, then proceed with completing user's request."))
|
||||||
|
|
||||||
(defun gptel-org-tools--org-ql-select-tags-local-count (buf query)
|
(defun gptel-org--org-ql-select-tags-local-count (buf query)
|
||||||
"Return count of entries tagged QUERY in BUFFER."
|
"Return count of entries tagged QUERY in BUFFER."
|
||||||
(let* ((buffer (get-buffer buf))
|
(let* ((buffer (get-buffer buf))
|
||||||
(mode (buffer-local-value 'major-mode buffer)))
|
(mode (buffer-local-value 'major-mode buffer)))
|
||||||
|
@ -346,14 +346,14 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
(length (org-ql-select
|
(length (org-ql-select
|
||||||
(get-buffer buf)
|
(get-buffer buf)
|
||||||
`(tags-local ,query)
|
`(tags-local ,query)
|
||||||
:action #'gptel-org-tools--heading-body))
|
:action #'gptel-org--heading-body))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-tags-local-count
|
:function #'gptel-org--org-ql-select-tags-local-count
|
||||||
:name "gptel-org-tools--org-ql-select-tags-local"
|
:name "gptel-org--org-ql-select-tags-local"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
||||||
|
@ -363,7 +363,7 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
:category "org-ql"
|
:category "org-ql"
|
||||||
:description "Returns count of entries tagged with tag QUERY from BUFFER, without tag inheritance. After using this, stop. Evaluate results. Then proceed."))
|
:description "Returns count of entries tagged with tag QUERY from BUFFER, without tag inheritance. After using this, stop. Evaluate results. Then proceed."))
|
||||||
|
|
||||||
(defun gptel-org-tools--org-ql-select-tags (buf query)
|
(defun gptel-org--org-ql-select-tags (buf query)
|
||||||
"Return every entry tagged QUERY from BUFFER."
|
"Return every entry tagged QUERY from BUFFER."
|
||||||
(let* ((buffer (get-buffer buf))
|
(let* ((buffer (get-buffer buf))
|
||||||
(mode (buffer-local-value 'major-mode buffer)))
|
(mode (buffer-local-value 'major-mode buffer)))
|
||||||
|
@ -373,15 +373,15 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
(get-buffer buf)
|
(get-buffer buf)
|
||||||
`(tags ,query)
|
`(tags ,query)
|
||||||
:action #'gptel-org-tools--heading-body))
|
:action #'gptel-org--heading-body))
|
||||||
(gptel-org-tools--result-limit result)))
|
(gptel-org--result-limit result)))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-tags
|
:function #'gptel-org--org-ql-select-tags
|
||||||
:name "gptel-org-tools--org-ql-select-tags"
|
:name "gptel-org--org-ql-select-tags"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
||||||
|
@ -391,7 +391,7 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
:category "org-ql"
|
:category "org-ql"
|
||||||
:description "Returns entries tagged QUERY from BUFFER, with tag inheritance. After using this, stop. Evaluate results. Only then proceed."))
|
:description "Returns entries tagged QUERY from BUFFER, with tag inheritance. After using this, stop. Evaluate results. Only then proceed."))
|
||||||
|
|
||||||
(defun gptel-org-tools--org-ql-select-rifle (buf query)
|
(defun gptel-org--org-ql-select-rifle (buf query)
|
||||||
"Return every entry matching keyword QUERY from BUFFER."
|
"Return every entry matching keyword QUERY from BUFFER."
|
||||||
(let* ((buffer (get-buffer buf))
|
(let* ((buffer (get-buffer buf))
|
||||||
(mode (buffer-local-value 'major-mode buffer)))
|
(mode (buffer-local-value 'major-mode buffer)))
|
||||||
|
@ -401,15 +401,15 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
(org-ql-select
|
(org-ql-select
|
||||||
buffer
|
buffer
|
||||||
`(rifle ,query)
|
`(rifle ,query)
|
||||||
:action #'gptel-org-tools--heading-body)))
|
:action #'gptel-org--heading-body)))
|
||||||
(gptel-org-tools--result-limit result))
|
(gptel-org--result-limit result))
|
||||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||||
(message "Buffer '%s' does not exist." buf))))
|
(message "Buffer '%s' does not exist." buf))))
|
||||||
|
|
||||||
(add-to-list 'gptel-org-tools
|
(add-to-list 'gptel-org
|
||||||
(gptel-make-tool
|
(gptel-make-tool
|
||||||
:function #'gptel-org-tools--org-ql-select-rifle
|
:function #'gptel-org--org-ql-select-rifle
|
||||||
:name "gptel-org-tools--org-ql-select-rifle"
|
:name "gptel-org--org-ql-select-rifle"
|
||||||
:args (list '(:name "buffer"
|
:args (list '(:name "buffer"
|
||||||
:type string
|
:type string
|
||||||
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
:description "The name of the buffer. See the NAME column in `list-buffers`. Using filename fails.")
|
||||||
|
@ -419,5 +419,5 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
||||||
:category "org-ql"
|
:category "org-ql"
|
||||||
:description "Returns entries (heading and body) matching QUERY from BUFFER. This may pull too many results, only use if other tools fail. After using this, stop. Evaluate results. If necessary, re-plan. Only then proceed."))
|
:description "Returns entries (heading and body) matching QUERY from BUFFER. This may pull too many results, only use if other tools fail. After using this, stop. Evaluate results. If necessary, re-plan. Only then proceed."))
|
||||||
|
|
||||||
(provide 'gptel-org-tools)
|
(provide 'gptel-org)
|
||||||
;;; gptel-org-tools.el ends here
|
;;; gptel-org.el ends here
|
Loading…
Add table
Reference in a new issue