[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/gptel 2982ede17d 255/273: gptel-org: Add gptel-org
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/gptel 2982ede17d 255/273: gptel-org: Add gptel-org |
Date: |
Wed, 1 May 2024 10:02:45 -0400 (EDT) |
branch: elpa/gptel
commit 2982ede17d449c3d06cb7b99aa6ff9ebc5d8ce4f
Author: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
Commit: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
gptel-org: Add gptel-org
* gptel.el (gptel--create-prompt): Split the Org mode logic in
`gptel--create-prompt` into gptel-org.el.
* gptel-org.el (gptel-org-branching-context,
gptel-org--create-prompt): Handle prompt creation for Org buffers
in `gptel-org--create-prompt`. The option
`gptel-org-branching-context` limits the context in Org buffers to
the direct ancestors of the active heading. This is useful for
conversations with branches. gptel-org.el is intended to
eventually contain all the Org mode specific gptel code, and will
not be loaded unless gptel is called in an Org buffer.
---
gptel-org.el | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gptel.el | 22 ++++----
2 files changed, 178 insertions(+), 11 deletions(-)
diff --git a/gptel-org.el b/gptel-org.el
new file mode 100644
index 0000000000..63ab4aaa7f
--- /dev/null
+++ b/gptel-org.el
@@ -0,0 +1,167 @@
+;;; gptel-org.el --- Org functions for gptel -*- lexical-binding: t;
-*-
+
+;; Copyright (C) 2024 Karthik Chikmagalur
+
+;; Author: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
+;; Keywords:
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+(eval-when-compile (require 'cl-lib))
+(require 'org-element)
+(require 'outline)
+
+(declare-function org-at-heading-p "org")
+
+
+;;; User options
+(defcustom gptel-org-branching-context nil
+ "Use the lineage of the current heading as the context for gptel in Org
buffers.
+
+This makes each same level heading a separate conversation
+branch.
+
+By default, gptel uses a linear context: all the text up to the
+cursor is sent to the LLM. Enabling this option makes the
+context the hierarchical lineage of the current Org heading. In
+this example:
+
+-----
+Top level text
+
+* Heading 1
+heading 1 text
+
+* Heading 2
+heading 2 text
+
+** Heading 2.1
+heading 2.1 text
+** Heading 2.2
+heading 2.2 text
+-----
+
+With the cursor at the end of the buffer, the text sent to the
+LLM will be limited to
+
+-----
+Top level text
+
+* Heading 2
+heading 2 text
+
+** Heading 2.2
+heading 2.2 text
+-----
+
+This makes it feasible to have multiple conversation branches."
+ :local t
+ :type 'boolean
+ :group 'gptel)
+
+
+;;; Setting context and creating queries
+(defun gptel-org--get-topic-start ()
+ "If a conversation topic is set, return it."
+ (when (org-entry-get (point) "GPTEL_TOPIC" 'inherit)
+ (marker-position org-entry-property-inherited-from)))
+
+(defun gptel-org-set-topic (topic)
+ "Set a topic and limit this conversation to the current heading.
+
+This limits the context sent to the LLM to the text between the
+current heading and the cursor position."
+ (interactive
+ (list
+ (progn
+ (or (derived-mode-p 'org-mode)
+ (user-error "Support for multiple topics per buffer is only
implemented for `org-mode'."))
+ (completing-read "Set topic as: "
+ (org-property-values "GPTEL_TOPIC")
+ nil nil (downcase
+ (truncate-string-to-width
+ (substring-no-properties
+ (replace-regexp-in-string
+ "\\s-+" "-"
+ (org-get-heading)))
+ 50))))))
+ (when (stringp topic) (org-set-property "GPTEL_TOPIC" topic)))
+
+;; NOTE: This can be converted to a cl-defmethod for `gptel--parse-buffer'
+;; (conceptually cleaner), but will cause load-order issues in gptel.el and
+;; might be harder to debug.
+(defun gptel-org--create-prompt (&optional prompt-end)
+ "Return a full conversation prompt from the contents of this Org buffer.
+
+If `gptel--num-messages-to-send' is set, limit to that many
+recent exchanges.
+
+The prompt is constructed from the contents of the buffer up to
+point, or PROMPT-END if provided. Its contents depend on the
+value of `gptel-org-branching-context', which see."
+ (unless prompt-end (setq prompt-end (point)))
+ (let ((max-entries (and gptel--num-messages-to-send
+ (* 2 gptel--num-messages-to-send)))
+ (topic-start (gptel--get-topic-start)))
+ (when topic-start
+ ;; narrow to GPTEL_TOPIC property scope
+ (narrow-to-region topic-start prompt-end))
+ (if gptel-org-branching-context
+ ;; Create prompt from direct ancestors of point
+ (save-excursion
+ (let* ((org-buf (current-buffer))
+ (start-bounds (org-element-lineage-map
+ (org-element-at-point) #'org-element-begin
+ '(headline org-data) 'with-self))
+ (end-bounds
+ (cl-loop
+ for pos in (cdr start-bounds)
+ while
+ (and (>= pos (point-min)) ;respect narrowing
+ (goto-char pos)
+ ;; org-element-lineage always returns an extra
+ ;; (org-data) element at point 1. If there is also a
+ ;; heading here, it is either a false positive or we
+ ;; would be double counting it. So we reject this node
+ ;; when also at a heading.
+ (not (and (eq pos 1) (org-at-heading-p))))
+ do (outline-next-heading)
+ collect (point) into ends
+ finally return (cons prompt-end ends))))
+ (with-temp-buffer
+ (setq-local gptel-backend
+ (buffer-local-value 'gptel-backend org-buf)
+ gptel--system-message
+ (buffer-local-value 'gptel--system-message org-buf)
+ gptel-model
+ (buffer-local-value 'gptel-model org-buf))
+ (cl-loop for start in start-bounds
+ for end in end-bounds
+ do (insert-buffer-substring org-buf start end)
+ (goto-char (point-min)))
+ (goto-char (point-max))
+ (let ((major-mode 'org-mode))
+ (gptel--parse-buffer gptel-backend max-entries)))))
+ ;; Create prompt the usual way
+ (gptel--parse-buffer gptel-backend max-entries))))
+
+
+
+(provide 'gptel-org)
+;;; gptel-org.el ends here
diff --git a/gptel.el b/gptel.el
index 8a014a4bb4..07fb917137 100644
--- a/gptel.el
+++ b/gptel.el
@@ -1097,19 +1097,19 @@ If PROMPT-END (a marker) is provided, end the prompt
contents
there."
(save-excursion
(save-restriction
- (cond
- ((use-region-p)
- ;; Narrow to region
- (narrow-to-region (region-beginning) (region-end))
- (goto-char (point-max)))
- ((when-let ((topic-start (gptel--get-topic-start)))
- ;; Narrow to topic
- (narrow-to-region topic-start (or prompt-end (point-max)))
- (goto-char (point-max))))
- (t (goto-char (or prompt-end (point-max)))))
(let ((max-entries (and gptel--num-messages-to-send
(* 2 gptel--num-messages-to-send))))
- (gptel--parse-buffer gptel-backend max-entries)))))
+ (cond
+ ((use-region-p)
+ ;; Narrow to region
+ (narrow-to-region (region-beginning) (region-end))
+ (goto-char (point-max))
+ (gptel--parse-buffer gptel-backend max-entries))
+ ((derived-mode-p 'org-mode)
+ (require 'gptel-org)
+ (gptel-org--create-prompt (or prompt-end (point-max))))
+ (t (goto-char (or prompt-end (point-max)))
+ (gptel--parse-buffer gptel-backend max-entries)))))))
(cl-defgeneric gptel--parse-buffer (backend max-entries)
"Parse current buffer backwards from point and return a list of prompts.
- [nongnu] elpa/gptel 9126bed43f 159/273: gptel: Set window when doing auto-scrolling, (continued)
- [nongnu] elpa/gptel 9126bed43f 159/273: gptel: Set window when doing auto-scrolling, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 85bd47cb4c 164/273: README: Add support for llama.cpp, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 4d01dddf7d 155/273: gptel, gptel-curl: Address checkdoc warnings, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel e5357383ce 166/273: gptel: Appease byte-compiler and linter, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel d5b10c3d6d 169/273: gptel: gptel-model can be an arbitrary string, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel d6ef79f621 177/273: gptel-transient: Show descriptions of directives, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 0690c8b6a9 161/273: gptel-transient: Exit transient when writing directive, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 149261ee79 211/273: gptel-transient: Avoid clashes with the custom directive key (#219), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel dd5833eef3 023/273: gptel: Improve gptel-mode setup, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 3d0df72bd3 027/273: gptel-transient: linting for MELPA, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 2982ede17d 255/273: gptel-org: Add gptel-org,
ELPA Syncer <=
- [nongnu] elpa/gptel 9bc54bed9c 244/273: gptel-transient: Remove "-ts" suffix from major mode (#259), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 9a5a4a60d5 248/273: README: Mention wiki entry on saving transient flags, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 8dbcbbb908 256/273: gptel-org: Move session save/restore code for Org, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 1b47235e25 071/273: README: Add section on gptel-request, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel bb8b37d8c0 100/273: gptel, gptel-curl: Fix byte-compile warnings, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 219e163dc7 091/273: gptel-curl: Add gptel-abort, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel a660e13a8b 106/273: gptel, gptel-transient: Fix read temperature from minibuffer (#85), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel c0ffce0849 110/273: gptel: Fix reading bounds in org files (#98), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 648fa228a1 115/273: gptel: Fix check for markdown-mode (#109), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 63027083cd 125/273: README: Update additional customization section, ELPA Syncer, 2024/05/01