gptel: Add minimal status indicator via mode-line-process

* gptel.el (gptel-update-destination, gptel-use-header-line,
gptel--update-status, gptel-mode): Improve status messaging when not
using the header-line.  When the user option
`gptel-use-header-line` (renamed from `gptel-update-destination`)
is set to nil, we use `mode-line-process` to report on in-progress
requests, and show the active LLM (model) otherwise.  Error
messages are sent to the echo area.  Close #9.

* README.org: Change `gptel-update-destination` to
`gptel-use-header-line` and tweak description.
This commit is contained in:
Karthik Chikmagalur 2023-12-21 17:55:21 -08:00
parent 4775ade6e0
commit 8973498378
2 changed files with 61 additions and 53 deletions

View file

@ -344,7 +344,7 @@ Other Emacs clients for LLMs prescribe the format of the interaction (a comint s
| =gptel-default-mode= | Major mode for dedicated chat buffers. | | =gptel-default-mode= | Major mode for dedicated chat buffers. |
| =gptel-prompt-prefix-alist= | Text inserted before queries. | | =gptel-prompt-prefix-alist= | Text inserted before queries. |
| =gptel-response-prefix-alist= | Text inserted before responses. | | =gptel-response-prefix-alist= | Text inserted before responses. |
| =gptel-update-destination= | Display status messages in headerline (default) or minibuffer | | =gptel-use-header-line= | Display status messages in header-line (default) or minibuffer |
|-----------------------------+----------------------------------------| |-----------------------------+----------------------------------------|
** COMMENT Will you add feature X? ** COMMENT Will you add feature X?

112
gptel.el
View file

@ -275,6 +275,14 @@ is only inserted in dedicated gptel buffers before the AI's response."
:group 'gptel :group 'gptel
:type '(alist :key-type symbol :value-type string)) :type '(alist :key-type symbol :value-type string))
(defcustom gptel-use-header-line t
"Whether gptel-mode should use the header-line for status information.
When set to nil, use the mode line for (minimal) status
information and the echo area for messages."
:type 'boolean
:group 'gptel)
(defcustom gptel-crowdsourced-prompts-file (defcustom gptel-crowdsourced-prompts-file
(let ((cache-dir (or (getenv "XDG_CACHE_HOME") (let ((cache-dir (or (getenv "XDG_CACHE_HOME")
(getenv "XDG_DATA_HOME") (getenv "XDG_DATA_HOME")
@ -372,15 +380,6 @@ To set the model for a chat session interactively call
(const :tag "GPT 4" "gpt-4") (const :tag "GPT 4" "gpt-4")
(const :tag "GPT 4 turbo (preview)" "gpt-4-1106-preview"))) (const :tag "GPT 4 turbo (preview)" "gpt-4-1106-preview")))
(defcustom gptel-update-destination "headerline"
"Where update messages appear."
:local t
:safe #'gptel--always
:group 'gptel
:type '(choice
(const :tag "Headerline" "headerline")
(const :tag "Minibufer" "minibuffer")))
(defcustom gptel-temperature 1.0 (defcustom gptel-temperature 1.0
"\"Temperature\" of ChatGPT response. "\"Temperature\" of ChatGPT response.
@ -447,8 +446,10 @@ and \"apikey\" as USER."
(cl-typecase key-sym (cl-typecase key-sym
(function (funcall key-sym)) (function (funcall key-sym))
(string key-sym) (string key-sym)
(symbol (gptel--get-api-key (symbol (if-let ((val (symbol-value key-sym)))
(symbol-value key-sym))) (gptel--get-api-key
(symbol-value key-sym))
(error "`gptel-api-key' is not valid")))
(t (error "`gptel-api-key' is not valid"))))) (t (error "`gptel-api-key' is not valid")))))
(defsubst gptel--numberize (val) (defsubst gptel--numberize (val)
@ -594,51 +595,58 @@ opening the file."
(user-error (format "`gptel-mode' is not supported in `%s'." major-mode))) (user-error (format "`gptel-mode' is not supported in `%s'." major-mode)))
(add-hook 'before-save-hook #'gptel--save-state nil t) (add-hook 'before-save-hook #'gptel--save-state nil t)
(gptel--restore-state) (gptel--restore-state)
(setq gptel--old-header-line header-line-format (if gptel-use-header-line
header-line-format (setq gptel--old-header-line header-line-format
(list '(:eval (concat (propertize " " 'display '(space :align-to 0)) header-line-format
(format "%s" (gptel-backend-name gptel-backend)))) (list '(:eval (concat (propertize " " 'display '(space :align-to 0))
(propertize " Ready" 'face 'success) (format "%s" (gptel-backend-name gptel-backend))))
'(:eval (propertize " Ready" 'face 'success)
(let* ((l1 (length gptel-model)) '(:eval
(num-exchanges (let* ((l1 (length gptel-model))
(if gptel--num-messages-to-send (num-exchanges
(format "[Send: %s exchanges]" gptel--num-messages-to-send) (if gptel--num-messages-to-send
"[Send: buffer]")) (format "[Send: %s exchanges]" gptel--num-messages-to-send)
(l2 (length num-exchanges))) "[Send: buffer]"))
(concat (l2 (length num-exchanges)))
(propertize (concat
" " 'display `(space :align-to ,(max 1 (- (window-width) (+ 2 l1 l2))))) (propertize
(propertize " " 'display `(space :align-to ,(max 1 (- (window-width) (+ 2 l1 l2)))))
(gptel--button-buttonize num-exchanges (propertize
(lambda (&rest _) (gptel-menu))) (gptel--button-buttonize num-exchanges
'mouse-face 'highlight (lambda (&rest _) (gptel-menu)))
'help-echo 'mouse-face 'highlight
"Number of past exchanges to include with each request") 'help-echo
" " "Number of past exchanges to include with each request")
(propertize " "
(gptel--button-buttonize (concat "[" gptel-model "]") (propertize
(lambda (&rest _) (gptel-menu))) (gptel--button-buttonize (concat "[" gptel-model "]")
'mouse-face 'highlight (lambda (&rest _) (gptel-menu)))
'help-echo "GPT model in use"))))))) 'mouse-face 'highlight
(setq header-line-format gptel--old-header-line))) 'help-echo "GPT model in use"))))))
(setq mode-line-process
'(:eval (concat " "
(gptel--button-buttonize gptel-model
(lambda (&rest _) (gptel-menu))))))))
(if gptel-use-header-line
(setq header-line-format gptel--old-header-line
gptel--old-header-line nil)
(setq mode-line-process nil))))
(defun gptel--update-header-line (msg face) (defun gptel--update-status (&optional msg face)
"Update header line with status MSG in FACE."
(and gptel-mode (consp header-line-format)
(setf (nth 1 header-line-format)
(propertize msg 'face face))
(force-mode-line-update)))
(defun gptel--update-status (msg face)
"Update status MSG in FACE." "Update status MSG in FACE."
(when gptel-mode (when gptel-mode
(if (string= gptel-update-destination "modeline") (if gptel-use-header-line
(message (propertize msg 'face face)) (and (consp header-line-format)
(and (consp header-line-format)
(setf (nth 1 header-line-format) (setf (nth 1 header-line-format)
(propertize msg 'face face)) (propertize msg 'face face)))
(force-mode-line-update))))) (if (member msg '(" Typing..." " Waiting..."))
(setq mode-line-process (propertize msg 'face face))
(setq mode-line-process
'(:eval (concat " "
(gptel--button-buttonize gptel-model
(lambda (&rest _) (gptel-menu))))))
(message (propertize msg 'face face))))
(force-mode-line-update)))
(cl-defun gptel-request (cl-defun gptel-request
(&optional prompt &key callback (&optional prompt &key callback