[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/gptel 0f161a466b 109/273: gptel: saving and restoring stat
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/gptel 0f161a466b 109/273: gptel: saving and restoring state for Markdown/Text |
Date: |
Wed, 1 May 2024 10:02:09 -0400 (EDT) |
branch: elpa/gptel
commit 0f161a466b880cde864d8aa709ec04e1a86f445a
Author: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
Commit: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
gptel: saving and restoring state for Markdown/Text
* gptel.el (gptel--save-state, gptel--restore-state,
gptel-temperature, gptel-model, gptel-max-tokens,
gptel-directives, gptel--always, gptel--button-buttonize,
gptel--system-message, gptel--bounds): Write gptel parameters as
file-local variables when saving chats in Markdown or text files.
The local variable gptel--bounds stores the locations of the
responses from the LLM. This is not a great solution, but the best
I can think to do without adding more syntax to the document.
Chats can be restored by turning on `gptel-mode'. One of the
problem with this approach is that if the buffer is modified
before `gptel-mode' is turned on, the state data is out of date.
Another problem is that this metadata block as printed in the
buffer can become quite long. A better approach is needed.
Define helper functions `gptel--always' and
`gptel--button-buttonize' to work around Emacs 27.1 support.
* README.org: Mention saving and restoring chats where
appropriate.
---
README.org | 8 ++++++--
gptel.el | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 61 insertions(+), 9 deletions(-)
diff --git a/README.org b/README.org
index b2f514f746..be7c9d59b4 100644
--- a/README.org
+++ b/README.org
@@ -13,6 +13,7 @@
https://user-images.githubusercontent.com/8607532/230516816-ae4a613a-4d01-4073-a
- Interact with ChatGPT from anywhere in Emacs (any buffer, shell, minibuffer,
wherever)
- ChatGPT's responses are in Markdown or Org markup.
- Supports conversations and multiple independent sessions.
+- Save chats as regular Markdown/Org/Text files and resume them later.
- You can go back and edit your previous prompts, or even ChatGPT's previous
responses when continuing a conversation. These will be fed back to ChatGPT.
GPTel uses Curl if available, but falls back to url-retrieve to work without
external dependencies.
@@ -122,6 +123,8 @@ With a region selected, you can also rewrite prose or
refactor code from here:
That's it. You can go back and edit previous prompts and responses if you want.
+4. Save the chat to a file. To resume, open the file and turn on =gptel-mode=.
+
The default mode is =markdown-mode= if available, else =text-mode=. You can
set =gptel-default-mode= to =org-mode= if desired.
** Using it your way
@@ -167,10 +170,11 @@ Maybe, I'd like to experiment a bit more first. Features
added since the incept
- GPT-4 support
- Response redirection (to the echo area, another buffer, etc)
- A built-in refactor/rewrite prompt
+- Limiting conversation context to Org headings using properties (#58)
+- Saving and restoring chats (#17)
Features being considered or in the pipeline:
-- Limiting conversation context to Org headings using properties (#58)
-- Stateless design (#17)
+- Fully stateless design (#17)
** Alternatives
diff --git a/gptel.el b/gptel.el
index 9d241ad20d..460262555c 100644
--- a/gptel.el
+++ b/gptel.el
@@ -186,9 +186,34 @@ transient menu interface provided by `gptel-menu'."
:group 'gptel
:type 'file)
+;; FIXME This is convoluted, but it's not worth adding the `compat' dependency
+;; just for a couple of helper functions either.
+(cl-macrolet
+ ((gptel--compat
+ () (if (version< "28.1" emacs-version)
+ (macroexp-progn
+ `((defalias 'gptel--button-buttonize #'button-buttonize)
+ (defalias 'gptel--always #'always)))
+ (macroexp-progn
+ `((defun gptel--always (&rest _)
+ "Always return t." t)
+ (defun gptel--button-buttonize (string callback)
+ "Make STRING into a button and return it.
+When clicked, CALLBACK will be called."
+ (propertize string
+ 'face 'button
+ 'button t
+ 'follow-link t
+ 'category t
+ 'button-data nil
+ 'keymap button-map
+ 'action callback)))))))
+ (gptel--compat))
+
;; Model and interaction parameters
(defvar-local gptel--system-message
"You are a large language model living in Emacs and a helpful assistant.
Respond concisely.")
+(put 'gptel--system-message 'safe-local-variable #'gptel--always)
(defcustom gptel-directives
`((default . ,gptel--system-message)
@@ -204,6 +229,7 @@ Each entry in this alist maps a symbol naming the directive
to
the string that is sent. To set the directive for a chat session
interactively call `gptel-send' with a prefix argument."
:group 'gptel
+ :safe #'gptel--always
:type '(alist :key-type symbol :value-type string))
(defcustom gptel-max-tokens nil
@@ -220,6 +246,7 @@ If left unset, ChatGPT will target about 40% of the total
token
count of the conversation so far in each message, so messages
will get progressively longer!"
:local t
+ :safe #'gptel--always
:group 'gptel
:type '(choice (integer :tag "Specify Token count")
(const :tag "Default" nil)))
@@ -232,10 +259,11 @@ The current options are
- \"gpt-3.5-turbo-16k\"
- \"gpt-4\" (experimental)
- \"gpt-4-32k\" (experimental)
-
+
To set the model for a chat session interactively call
`gptel-send' with a prefix argument."
:local t
+ :safe #'gptel--always
:group 'gptel
:type '(choice
(const :tag "GPT 3.5 turbo" "gpt-3.5-turbo")
@@ -252,10 +280,15 @@ of the response, with 2.0 being the most random.
To set the temperature for a chat session interactively call
`gptel-send' with a prefix argument."
:local t
+ :safe #'gptel--always
:group 'gptel
:type 'number)
+(defvar-local gptel--bounds nil)
+(put 'gptel--bounds 'safe-local-variable #'gptel--always)
+
(defvar-local gptel--num-messages-to-send nil)
+(put 'gptel--num-messages-to-send 'safe-local-variable #'gptel--always)
(defvar gptel--debug nil)
(defun gptel-api-key-from-auth-source (&optional host user)
@@ -302,14 +335,20 @@ Currently saving and restoring state is implemented only
for
(read (org-entry-get (point-min) "GPTEL_BOUNDS"))))
(mapc (pcase-lambda (`(,beg . ,end))
(put-text-property beg end 'gptel 'response))
- bounds))
+ bounds)
+ (message "gptel chat restored."))
(when-let ((model (org-entry-get (point-min) "GPTEL_MODEL")))
(setq-local gptel-model model))
(when-let ((system (org-entry-get (point-min) "GPTEL_SYSTEM")))
(setq-local gptel--system-message 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!"))))))))
+ (error (message "Could not restore gptel state, sorry!")))))
+ (_ (when gptel--bounds
+ (mapc (pcase-lambda (`(,beg . ,end))
+ (put-text-property beg end 'gptel 'response))
+ gptel--bounds)
+ (message "gptel chat restored."))))))
(defun gptel--save-state ()
"Write the gptel state to the buffer.
@@ -346,8 +385,17 @@ opening the file."
(> attempts 0))
(funcall write-bounds (1- attempts)))))))
(funcall write-bounds 6))))
- ('markdown-mode
- (message "Saving gptel state is not implemented for `markdown-mode'."))))
+ (_ (save-excursion
+ (save-restriction
+ (add-file-local-variable 'gptel-model gptel-model)
+ (unless (equal (default-value 'gptel-temperature) gptel-temperature)
+ (add-file-local-variable 'gptel-temperature gptel-temperature))
+ (unless (string= (default-value 'gptel--system-message)
+ gptel--system-message)
+ (add-file-local-variable 'gptel--system-message
gptel--system-message))
+ (when gptel-max-tokens
+ (add-file-local-variable 'gptel-max-tokens gptel-max-tokens))
+ (add-file-local-variable 'gptel--bounds (gptel--get-bounds)))))))
(defun gptel--get-bounds ()
"Return the gptel response boundaries as an alist."
@@ -395,14 +443,14 @@ opening the file."
(propertize
" " 'display `(space :align-to ,(max 1 (-
(window-width) (+ 2 l1 l2)))))
(propertize
- (button-buttonize num-exchanges
+ (gptel--button-buttonize num-exchanges
(lambda (&rest _) (gptel-menu)))
'mouse-face 'highlight
'help-echo
"Number of past exchanges to include with each
request")
" "
(propertize
- (button-buttonize (concat "[" gptel-model "]")
+ (gptel--button-buttonize (concat "[" gptel-model "]")
(lambda (&rest _) (gptel-menu)))
'mouse-face 'highlight
'help-echo "OpenAI GPT model in use")))))))
- [nongnu] elpa/gptel 937c754e57 076/273: gptel-transient: Add refactor transient, (continued)
- [nongnu] elpa/gptel 937c754e57 076/273: gptel-transient: Add refactor transient, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 3d98ce8eee 099/273: gptel: Add new turbo 0613 models (#77), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel b92fc389d7 105/273: gptel: Reduce verbosity of gptel--save-state, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 33d8434f3e 006/273: gptel: Tweak header line format, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel c2ad1c004d 054/273: Decode response body as utf-8 and then parse as json, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 1f03655e2d 056/273: Add Doom Emacs installation instructions (#28), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 936c27e28b 058/273: gptel: Fix header-line-format update, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel acf12ee6e3 073/273: gptel: return the gptel buffer, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 706ad703db 090/273: gptel-transient: Allow arbitrary system prompts/directives, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel a5be53d5e3 084/273: gptel: Update version, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 0f161a466b 109/273: gptel: saving and restoring state for Markdown/Text,
ELPA Syncer <=
- [nongnu] elpa/gptel 5c3b26aeec 138/273: gptel-curl: Tweak Curl arguments for windows, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 15404f639d 139/273: README: Update instructions for Azure (#147), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel ddd69cbbcf 147/273: gptel-curl: Replace Curl timeout with speed-time, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 60cb406567 162/273: gptel: Improve documentation of gptel-send, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 8ec233d79c 174/273: gptel: Name gptel buffer according to backend, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 235134a176 186/273: Bit of feedback when copying reponse to kill ring, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 50fb34ab7c 187/273: Fix compilation warnings, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 10f57605ce 195/273: gptel: Improve Org stream conversion, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 6a385ffc2f 199/273: README: Add FAQ entry for OpenAI quota error (#93), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 199595b0c8 216/273: gptel: Handle status HTTP 100, ELPA Syncer, 2024/05/01