branch: elpa/gptel commit b7eb4fc7ad62d5a02eb318f4c5882ede06bbd4fd Author: Karthik Chikmagalur <karthikchikmaga...@gmail.com> Commit: Karthik Chikmagalur <karthikchikmaga...@gmail.com>
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.