gptel-org: Move session save/restore code for Org

* gptel.el (gptel--restore-backend, gptel--save-state,
gptel--restore-state): Move the Org-specific code for saving and
restoring state to gptel-org.

* gptel-org.el (gptel-org--entry-properties,
gptel-org--save-state, gptel-org--restore-state): Org-specific
code for saving and restoring state using Org properties, moved
from gptel-org.
This commit is contained in:
Karthik Chikmagalur 2024-03-03 12:00:36 -08:00
parent 2982ede17d
commit 8dbcbbb908
2 changed files with 117 additions and 59 deletions

View file

@ -161,6 +161,105 @@ value of `gptel-org-branching-context', which see."
;; Create prompt the usual way ;; Create prompt the usual way
(gptel--parse-buffer gptel-backend max-entries)))) (gptel--parse-buffer gptel-backend max-entries))))
;;; Saving and restoring state
(defun gptel-org--entry-properties (&optional pt)
"Find gptel configuration properties stored in the current heading."
(pcase-let
((`(,system ,backend ,model ,temperature ,tokens)
(mapcar
(lambda (prop) (org-entry-get (or pt (point)) prop 'selective))
'("GPTEL_SYSTEM" "GPTEL_BACKEND" "GPTEL_MODEL"
"GPTEL_TEMPERATURE" "GPTEL_MAX_TOKENS"))))
(when system
(setq system (string-replace "\\n" "\n" system)))
(when backend
(setq backend (alist-get backend gptel--known-backends
nil nil #'equal)))
(when temperature
(setq temperature (gptel--numberize temperature)))
(when tokens (setq tokens (gptel--numberize tokens)))
(list system backend model temperature tokens)))
;; (pcase-let ((`(,gptel--system-message ,gptel-backend
;; ,gptel-model ,gptel-temperature)
;; (if (derived-mode-p 'org-mode)
;; (progn (require 'gptel-org)
;; (gptel-org--entry-properties))
;; `(,gptel--system-message ,gptel-backend
;; ,gptel-model ,gptel-temperature)))))
(defun gptel-org--restore-state ()
"Restore gptel state for Org buffers when turning on `gptel-mode'."
(save-restriction
(widen)
(condition-case status
(progn
(when-let ((bounds (org-entry-get (point-min) "GPTEL_BOUNDS")))
(mapc (pcase-lambda (`(,beg . ,end))
(put-text-property beg end 'gptel 'response))
(read bounds)))
(pcase-let ((`(,system ,backend ,model ,temperature ,tokens)
(gptel-org--entry-properties (point-min))))
(when system (setq-local gptel--system-message system))
(if backend (setq-local gptel-backend backend)
(message
(substitute-command-keys
(concat
"Could not activate gptel backend \"%s\"! "
"Switch backends with \\[universal-argument] \\[gptel-send]"
" before using gptel."))
backend))
(when model (setq-local gptel-model model))
(when temperature (setq-local gptel-temperature temperature))
(when tokens (setq-local gptel-max-tokens tokens))))
(:success (message "gptel chat restored."))
(error (message "Could not restore gptel state, sorry! Error: %s" status)))))
(defun gptel-org-set-properties (pt &optional msg)
"Store the active gptel configuration under the current heading.
The active gptel configuration includes the current system
message, language model and provider (backend), and additional
settings when applicable.
PT is the cursor position by default. If MSG is
non-nil (default), display a message afterwards."
(interactive (list (point) t))
(org-entry-put pt "GPTEL_MODEL" gptel-model)
(org-entry-put pt "GPTEL_BACKEND" (gptel-backend-name gptel-backend))
(unless (equal (default-value 'gptel-temperature) gptel-temperature)
(org-entry-put pt "GPTEL_TEMPERATURE"
(number-to-string gptel-temperature)))
(unless (string= (default-value 'gptel--system-message)
gptel--system-message)
(org-entry-put pt "GPTEL_SYSTEM"
(string-replace "\n" "\\n" gptel--system-message)))
(when gptel-max-tokens
(org-entry-put
pt "GPTEL_MAX_TOKENS" (number-to-string gptel-max-tokens)))
(when msg
(message "Added gptel configuration to current headline.")))
(defun gptel-org--save-state ()
"Write the gptel state to the Org buffer as Org properties."
(org-with-wide-buffer
(goto-char (point-min))
(when (org-at-heading-p)
(org-open-line 1))
(gptel-org-set-properties (point-min))
;; Save response boundaries
(letrec ((write-bounds
(lambda (attempts)
(let* ((bounds (gptel--get-buffer-bounds))
(offset (caar bounds))
(offset-marker (set-marker (make-marker) offset)))
(org-entry-put (point-min) "GPTEL_BOUNDS"
(prin1-to-string (gptel--get-buffer-bounds)))
(when (and (not (= (marker-position offset-marker) offset))
(> attempts 0))
(funcall write-bounds (1- attempts)))))))
(funcall write-bounds 6))))
(provide 'gptel-org) (provide 'gptel-org)

View file

@ -652,48 +652,33 @@ Valid JSON unless NO-JSON is t."
;; Saving and restoring state ;; Saving and restoring state
(defun gptel--restore-backend (name) (declare-function gptel-org--restore-state "gptel-org")
"Activate gptel backend with NAME in current buffer. (declare-function gptel-org--save-state "gptel-org")
If no backend with this name exists, inform the user. Intended
for when gptel restores chat metadata."
(when name
(if-let ((backend (alist-get name gptel--known-backends
nil nil #'equal)))
(setq-local gptel-backend backend)
(message
(substitute-command-keys
"Could not activate gptel backend \"%s\"! Switch backends with \\[universal-argument] \\[gptel-send] before using gptel.")
name))))
(defun gptel--restore-state () (defun gptel--restore-state ()
"Restore gptel state when turning on `gptel-mode'." "Restore gptel state when turning on `gptel-mode'."
(when (buffer-file-name) (when (buffer-file-name)
(pcase major-mode (pcase major-mode
('org-mode ('org-mode
(save-restriction (require 'gptel-org)
(widen) (gptel-org--restore-state))
(condition-case-unless-debug nil
(progn
(when-let ((bounds (org-entry-get (point-min) "GPTEL_BOUNDS")))
(mapc (pcase-lambda (`(,beg . ,end))
(put-text-property beg end 'gptel 'response))
(read bounds))
(message "gptel chat restored."))
(when-let ((model (org-entry-get (point-min) "GPTEL_MODEL")))
(setq-local gptel-model model))
(gptel--restore-backend (org-entry-get (point-min) "GPTEL_BACKEND"))
(when-let ((system (org-entry-get (point-min) "GPTEL_SYSTEM")))
(setq-local gptel--system-message (string-replace "\\n" "\n" system)))
(when-let ((temp (org-entry-get (point-min) "GPTEL_TEMPERATURE")))
(setq-local gptel-temperature (gptel--numberize temp))))
(error (message "Could not restore gptel state, sorry!")))))
(_ (when gptel--bounds (_ (when gptel--bounds
(mapc (pcase-lambda (`(,beg . ,end)) (mapc (pcase-lambda (`(,beg . ,end))
(put-text-property beg end 'gptel 'response)) (put-text-property beg end 'gptel 'response))
gptel--bounds) gptel--bounds)
(message "gptel chat restored.")) (message "gptel chat restored."))
(gptel--restore-backend gptel--backend-name))))) (when gptel--backend-name
(if-let ((backend (alist-get
gptel--backend-name gptel--known-backends
nil nil #'equal)))
(setq-local gptel-backend backend)
(message
(substitute-command-keys
(concat
"Could not activate gptel backend \"%s\"! "
"Switch backends with \\[universal-argument] \\[gptel-send]"
" before using gptel."))
gptel--backend-name)))))))
(defun gptel--save-state () (defun gptel--save-state ()
"Write the gptel state to the buffer. "Write the gptel state to the buffer.
@ -703,34 +688,8 @@ restore a chat session, turn on `gptel-mode' after opening the
file." file."
(pcase major-mode (pcase major-mode
('org-mode ('org-mode
(org-with-wide-buffer (require 'gptel-org)
(goto-char (point-min)) (gptel-org--save-state))
(when (org-at-heading-p)
(org-open-line 1))
(org-entry-put (point-min) "GPTEL_MODEL" gptel-model)
(org-entry-put (point-min) "GPTEL_BACKEND" (gptel-backend-name gptel-backend))
(unless (equal (default-value 'gptel-temperature) gptel-temperature)
(org-entry-put (point-min) "GPTEL_TEMPERATURE"
(number-to-string gptel-temperature)))
(unless (string= (default-value 'gptel--system-message)
gptel--system-message)
(org-entry-put (point-min) "GPTEL_SYSTEM"
(string-replace "\n" "\\n" gptel--system-message)))
(when gptel-max-tokens
(org-entry-put
(point-min) "GPTEL_MAX_TOKENS" gptel-max-tokens))
;; Save response boundaries
(letrec ((write-bounds
(lambda (attempts)
(let* ((bounds (gptel--get-buffer-bounds))
(offset (caar bounds))
(offset-marker (set-marker (make-marker) offset)))
(org-entry-put (point-min) "GPTEL_BOUNDS"
(prin1-to-string (gptel--get-buffer-bounds)))
(when (and (not (= (marker-position offset-marker) offset))
(> attempts 0))
(funcall write-bounds (1- attempts)))))))
(funcall write-bounds 6))))
(_ (let ((print-escape-newlines t)) (_ (let ((print-escape-newlines t))
(save-excursion (save-excursion
(save-restriction (save-restriction