branch: elpa/gptel
commit b7eb4fc7ad62d5a02eb318f4c5882ede06bbd4fd
Author: Karthik Chikmagalur <[email protected]>
Commit: Karthik Chikmagalur <[email protected]>
gptel-org: Strip property drawers from the prompt
* gptel-org.el: (gptel-org--create-prompt,
gptel-org-ignore-elements, gptel-org--element-end,
gptel-org--strip-elements): Now that prompts in Org mode are
always generated from a temp buffer, we are free to modify the
buffer as we wish. Add user option `gptel-org-ignore-elements' to
remove specified Org elements from the prompt, and strip property
drawers by default. (#408, #503)
This behavior can be reverted by setting this
user option to nil, or by selecting a region of text. (No
filtering is done when selecting a region.)
Eventually prompt filtering will be user-customizable via a hook.
* README.org (Additional Configuration): Mention
`gptel-org-ignore-elements'.
---
README.org | 1 +
gptel-org.el | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 52 insertions(+), 5 deletions(-)
diff --git a/README.org b/README.org
index 0928457a62..663d550b7c 100644
--- a/README.org
+++ b/README.org
@@ -1340,6 +1340,7 @@ Other Emacs clients for LLMs prescribe the format of the
interaction (a comint s
| *Org mode UI options* |
|
|-------------------------------+-------------------------------------------------------|
| =gptel-org-branching-context= | Make each outline path a separate
conversation branch |
+| =gptel-org-ignore-elements= | Ignore parts of the buffer when sending a
query |
|-------------------------------+-------------------------------------------------------|
|---------------------------------+-------------------------------------------------------------|
diff --git a/gptel-org.el b/gptel-org.el
index 0ff0b70877..fc20baf6e5 100644
--- a/gptel-org.el
+++ b/gptel-org.el
@@ -89,10 +89,15 @@ of Org."
(nreverse acc)))))
(if (fboundp 'org-element-begin)
(progn (declare-function org-element-begin "org-element")
- (defalias 'gptel-org--element-begin 'org-element-begin))
+ (declare-function org-element-end "org-element")
+ (defalias 'gptel-org--element-begin 'org-element-begin)
+ (defalias 'gptel-org--element-end 'org-element-end))
(defun gptel-org--element-begin (node)
"Get `:begin' property of NODE."
- (org-element-property :begin node))))
+ (org-element-property :begin node))
+ (defun gptel-org--element-end (node)
+ "Get `:end' property of NODE."
+ (org-element-property :end node))))
;;; User options
@@ -140,6 +145,20 @@ This makes it feasible to have multiple conversation
branches."
:type 'boolean
:group 'gptel)
+(defcustom gptel-org-ignore-elements '(property-drawer)
+ "List of Org elements that should be stripped from the prompt
+before sending it.
+
+By default gptel will remove Org property drawers from the
+prompt. For the full list of available elements, please see
+`org-element-all-elements'.
+
+Please note: Removing property-drawer elements is fast, but
+adding elements to this list can significantly slow down
+`gptel-send'."
+ :group 'gptel
+ :type '(repeat symbol))
+
;;; Setting context and creating queries
(defun gptel-org--get-topic-start ()
@@ -229,6 +248,7 @@ value of `gptel-org-branching-context', which see."
(goto-char (point-min)))
(goto-char (point-max))
(gptel-org--unescape-tool-results)
+ (gptel-org--strip-elements)
(gptel-org--strip-block-headers)
(let ((major-mode 'org-mode))
(gptel--parse-buffer gptel-backend max-entries)))))
@@ -244,13 +264,39 @@ value of `gptel-org-branching-context', which see."
(buffer-local-value sym org-buf)))
(insert-buffer-substring org-buf beg end)
(gptel-org--unescape-tool-results)
+ (gptel-org--strip-elements)
(gptel-org--strip-block-headers)
(let ((major-mode 'org-mode))
(gptel--parse-buffer gptel-backend max-entries)))))))
-(defun gptel-org--strip-tool-headers ()
- "Remove all tool_call block headers and footers.
-Every line that matches will be removed entirely."
+(defun gptel-org--strip-elements ()
+ "Remove all elements in `gptel-org-ignore-elements' from the
+prompt."
+ (let ((major-mode 'org-mode) element-markers)
+ (if (equal '(property-drawer) gptel-org-ignore-elements)
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward org-property-drawer-re nil t)
+ ;; ;; Slower but accurate
+ ;; (let ((drawer (org-element-at-point)))
+ ;; (when (org-element-type-p drawer 'property-drawer)
+ ;; (delete-region (org-element-begin drawer) (org-element-end
drawer))))
+
+ ;; Fast but inexact, can have false positives
+ (delete-region (match-beginning 0) (match-end 0))))
+ ;; NOTE: Parsing the buffer is extremely slow. Avoid this path unless
+ ;; required.
+ ;; NOTE: `org-element-map' takes a third KEEP-DEFERRED argument in newer
+ ;; Org versions
+ (org-element-map (org-element-parse-buffer 'element nil)
+ gptel-org-ignore-elements
+ (lambda (node)
+ (push (list (gptel-org--element-begin node)
+ (gptel-org--element-end node))
+ element-markers)))
+ (dolist (bounds element-markers)
+ (apply #'delete-region bounds)))))
+
(defun gptel-org--strip-block-headers ()
"Remove all gptel-specific block headers and footers.
Every line that matches will be removed entirely.