gptel-transient: Add transient menus for setting parameters
gptel-transient (gptel-send-menu): Add new transient-prefix `gptel-send-menu' for setting API options for the buffer and (optionally) sending queries. Various infix options are supported. 2023-03-09: Some transient menus are generated dynamically, which requires a more recent version of transient than the latest tagged release. As a result, the dynamic generation code is commented out for now.
This commit is contained in:
parent
8fca5bc762
commit
39376aa3f4
2 changed files with 210 additions and 2 deletions
208
gptel-transient.el
Normal file
208
gptel-transient.el
Normal file
|
@ -0,0 +1,208 @@
|
|||
;;; gptel-transient.el --- Transient menu for GPTel -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2023 Karthik Chikmagalur
|
||||
|
||||
;; Author: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
|
||||
;; Keywords: convenience
|
||||
|
||||
;; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
;; 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:
|
||||
(require 'gptel)
|
||||
(require 'transient)
|
||||
|
||||
;;;###autoload
|
||||
(transient-define-prefix gptel-send-menu ()
|
||||
"Change parameters of prompt to send ChatGPT."
|
||||
[:description
|
||||
(lambda () (format "Directive: %s"
|
||||
(truncate-string-to-width
|
||||
gptel--system-message (max (- (window-width) 14) 20) nil nil t)))
|
||||
("h" "Set directives for chat" gptel-system-prompt)]
|
||||
[["Parameters"
|
||||
(gptel--infix-max-tokens)
|
||||
(gptel--infix-num-messages-to-send)
|
||||
(gptel--infix-temperature)
|
||||
(gptel--infix-model)]
|
||||
["Send"
|
||||
(gptel--suffix-send-existing)
|
||||
(gptel--suffix-send-new)
|
||||
("RET" "Send prompt" gptel-send)]])
|
||||
|
||||
(transient-define-prefix gptel-system-prompt ()
|
||||
"Change the system prompt to send ChatGPT.
|
||||
|
||||
The \"system\" prompt establishes directives for the chat
|
||||
session. Some examples of system prompts are:
|
||||
|
||||
You are a helpful assistant. Answer as concisely as possible.
|
||||
Reply only with shell commands and no prose.
|
||||
You are a poet. Reply only in verse.
|
||||
|
||||
Customize `gptel--system-message-alist' for task-specific
|
||||
prompts."
|
||||
[:description
|
||||
(lambda () (format "Directive: %s"
|
||||
(truncate-string-to-width gptel--system-message (max (- (window-width) 14) 20) nil nil t)))
|
||||
:class transient-column
|
||||
:pad-keys t
|
||||
(gptel--suffix-system-message)
|
||||
("p" "Programming"
|
||||
(lambda () (interactive)
|
||||
(setq gptel--system-message
|
||||
(alist-get 'programming gptel--system-message-alist)))
|
||||
:transient t)
|
||||
("d" "Default"
|
||||
(lambda () (interactive)
|
||||
(setq gptel--system-message
|
||||
(alist-get 'default gptel--system-message-alist)))
|
||||
:transient t)
|
||||
("w" "Writing"
|
||||
(lambda () (interactive)
|
||||
(setq gptel--system-message
|
||||
(alist-get 'writing gptel--system-message-alist)))
|
||||
:transient t)
|
||||
("c" "Chat"
|
||||
(lambda () (interactive)
|
||||
(setq gptel--system-message
|
||||
(alist-get 'chat gptel--system-message-alist)))
|
||||
:transient t)])
|
||||
|
||||
;; TODO: Switch to dynamic Transient menus (below) once there's a new Transient release
|
||||
;; (transient-define-prefix gptel-system-prompt ()
|
||||
;; "Change the system prompt to send ChatGPT."
|
||||
;; [:description (lambda () (format "Current directive: %s"
|
||||
;; (truncate-string-to-width gptel--system-message 100 nil nil t)))
|
||||
;; :class transient-column
|
||||
;; :setup-children gptel-system-prompt--setup
|
||||
;; :pad-keys t])
|
||||
|
||||
;; (defun gptel-system-prompt--setup (_)
|
||||
;; "Set up suffixes for system prompt."
|
||||
;; (transient-parse-suffixes
|
||||
;; 'gptel-system-prompt
|
||||
;; (cl-loop for (type . prompt) in gptel--system-message-alist
|
||||
;; for name = (symbol-name type)
|
||||
;; for key = (substring name 0 1)
|
||||
;; collect (list (key-description key) (capitalize name)
|
||||
;; `(lambda () (interactive)
|
||||
;; (message "Directive: %s" ,prompt)
|
||||
;; (setq gptel--system-message ,prompt))
|
||||
;; :transient t)
|
||||
;; into prompt-suffixes
|
||||
;; finally return (cons (list 'gptel--suffix-system-message)
|
||||
;; prompt-suffixes))))
|
||||
|
||||
(transient-define-infix gptel--infix-num-messages-to-send ()
|
||||
"Number of recent messages to send with each exchange.
|
||||
|
||||
By default, the full conversation history is sent with every new
|
||||
prompt. This retains the full context of the conversation, but
|
||||
can be expensive in token size. Set how many recent messages to
|
||||
include."
|
||||
:description "Number of past messages to send"
|
||||
:class 'transient-lisp-variable
|
||||
:variable 'gptel--num-messages-to-send
|
||||
:key "n"
|
||||
:prompt "Number of past messages to include (leave empty for all): "
|
||||
:reader 'transient-read-number-N+)
|
||||
|
||||
(transient-define-infix gptel--infix-max-tokens ()
|
||||
"Max tokens per response.
|
||||
|
||||
This is roughly the number of words in the response. 100-300 is a
|
||||
reasonable range for short answers, 400 or more for longer
|
||||
responses.
|
||||
|
||||
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!"
|
||||
:description "Response length (tokens)"
|
||||
:class 'transient-lisp-variable
|
||||
:variable 'gptel--max-tokens
|
||||
:key "<"
|
||||
:prompt "Response length in tokens (leave empty: default, 80-200: short, 200-500: long): "
|
||||
:reader 'transient-read-number-N+)
|
||||
|
||||
(transient-define-infix gptel--infix-model ()
|
||||
"AI Model for Chat."
|
||||
:description "GPT Model: "
|
||||
:class 'transient-lisp-variable
|
||||
:variable 'gptel--model
|
||||
:key "M"
|
||||
:choices '("gpt-3.5-turbo-0301" "gpt-3.5-turbo")
|
||||
:reader (lambda (prompt &rest _)
|
||||
(completing-read
|
||||
prompt
|
||||
'("gpt-3.5-turbo-0301" "gpt-3.5-turbo"))))
|
||||
|
||||
(transient-define-infix gptel--infix-temperature ()
|
||||
"Temperature of request."
|
||||
:description "Randomness (0 - 2.0)"
|
||||
:class 'transient-lisp-variable
|
||||
:variable 'gptel--temperature
|
||||
:key "t"
|
||||
:reader '(lambda (_ initial history)
|
||||
(read-from-minibuffer "Set temperature (0.0-2.0, leave empty for default): "
|
||||
(number-to-string gptel--temperature))))
|
||||
|
||||
(transient-define-suffix gptel--suffix-system-message ()
|
||||
"Set Establishing message sent to ChatGPT."
|
||||
:transient nil
|
||||
:description "Set custom directives"
|
||||
:key "h"
|
||||
(interactive)
|
||||
(let ((orig-buf (current-buffer))
|
||||
(msg-start (make-marker)))
|
||||
(with-current-buffer (get-buffer-create "*gptel-system*")
|
||||
(erase-buffer)
|
||||
(text-mode)
|
||||
(insert
|
||||
"# Insert your system message below and press "
|
||||
(propertize "C-c C-c" 'face 'help-key-binding)
|
||||
" when ready.\n"
|
||||
"# Example: You are a helpful assistant. Answer as concisely as possible.\n"
|
||||
"# Example: Reply only with shell commands and no prose.\n"
|
||||
"# Example: You are a poet. Reply only in verse.\n\n")
|
||||
(set-marker msg-start (point))
|
||||
(insert (buffer-local-value 'gptel--system-message orig-buf))
|
||||
(goto-char (line-beginning-position))
|
||||
(push-mark)
|
||||
(goto-char (line-end-position))
|
||||
(activate-mark)
|
||||
(display-buffer (current-buffer)
|
||||
`((display-buffer-below-selected)
|
||||
(body-function . ,#'select-window)
|
||||
(window-height . ,#'fit-window-to-buffer)))
|
||||
(local-set-key (kbd "C-c C-c")
|
||||
(lambda ()
|
||||
(interactive)
|
||||
(setf (buffer-local-value 'gptel--system-message orig-buf)
|
||||
(buffer-substring msg-start (point-max)))
|
||||
(quit-window)
|
||||
(display-buffer
|
||||
orig-buf
|
||||
`((display-buffer-reuse-window
|
||||
display-buffer-use-some-window)
|
||||
(body-function . ,#'select-window)))
|
||||
(call-interactively #'gptel-send-menu))))))
|
||||
|
||||
(provide 'gptel-transient)
|
||||
;;; gptel-transient.el ends here
|
4
gptel.el
4
gptel.el
|
@ -4,7 +4,7 @@
|
|||
|
||||
;; Author: Karthik Chikmagalur
|
||||
;; Version: 0.10
|
||||
;; Package-Requires: ((emacs "27.1") (aio "1.0"))
|
||||
;; Package-Requires: ((emacs "27.1") (aio "1.0") (transient "0.3.7"))
|
||||
;; Keywords: convenience
|
||||
;; URL: https://github.com/karthink/gptel
|
||||
|
||||
|
@ -126,7 +126,7 @@ return the transformed string."
|
|||
(aio-defun gptel-send (&optional arg)
|
||||
"Submit this prompt to ChatGPT."
|
||||
(interactive "P")
|
||||
(if (and arg (featurep 'gptel-transient))
|
||||
(if (and arg (require 'gptel-transient nil t))
|
||||
(call-interactively #'gptel-send-menu)
|
||||
(message "Querying ChatGPT...")
|
||||
(and header-line-format
|
||||
|
|
Loading…
Add table
Reference in a new issue