branch: elpa/gptel commit c97c15aa44781df9bb8a485dbd80e40587174bf1 Author: Karthik Chikmagalur <karthikchikmaga...@gmail.com> Commit: Karthik Chikmagalur <karthikchikmaga...@gmail.com>
gptel-transient: Preset switching via dedicated menu instead * gptel-transient.el: (gptel--format-preset-string, gptel--preset): Use a new preset menu instead of an infix to manage presets. The design is similar to that of `gptel-system-prompt', and includes a command to save the current settings to a preset. Additionally, the "Request Parameters" heading now always shows an "(@preset)" string, since a new preset may be created at any time, even when the registry is nil. (gptel-menu): Add new preset. (gptel--infix-preset, gptel--preset-variable, gptel--preset): Remove the preset-variable class and methods, as they are no longer required. --- gptel-transient.el | 169 ++++++++++++++++++++++++++--------------------------- 1 file changed, 82 insertions(+), 87 deletions(-) diff --git a/gptel-transient.el b/gptel-transient.el index 37f68aafc8..f7eb802f3a 100644 --- a/gptel-transient.el +++ b/gptel-transient.el @@ -418,6 +418,26 @@ which see." context (if dest (concat (pth ", with response to ") dest) (concat (pth ", insert response at point"))))))))) +(defun gptel--format-preset-string () + "Format the preset indicator display for `gptel-menu'." + (concat + (propertize "Request Parameters" 'face 'transient-heading) + (if (and gptel--known-presets gptel--preset) + (apply + #'format " (%s%s)" + (let ((mismatch (gptel--preset-mismatch-p gptel--preset))) + (list (propertize "@" 'face (if mismatch 'transient-key + '( :inherit transient-key + :inherit secondary-selection + :box -1 :weight bold))) + (propertize (format "%s" gptel--preset) 'face + (if mismatch + '(:inherit warning :strike-through t) + '(:inherit secondary-selection :box -1)))))) + (format " (%s%s)" + (propertize "@" 'face 'transient-key) + (propertize "preset" 'face 'transient-inactive-value))))) + ;; * Transient classes and methods for gptel @@ -496,45 +516,6 @@ Their own value is ignored") (propertize display-if-true 'face (if value 'transient-value 'transient-inactive-value)))))) -;; ;; MAYBE(presets): Extend to a general gptel--option class? -;; (defclass gptel--preset (transient-option) -;; ((set-value :initarg :set-value :initform nil)) -;; "Singleton class for displaying and setting gptel presets.") - -(defclass gptel--preset-variable (gptel-lisp-variable) - ((set-props :initarg :set-props :initform nil)) - "Singleton class for displaying and setting gptel presets.") - -(cl-defmethod transient-infix-set ((obj gptel--preset-variable) value) - "Set both the preset variable and the gptel options in the preset." - ;; (funcall (oref obj set-value) value) - ;; (oset obj value value) - (funcall (oref obj set-value) ;First set the preset variable itself - (oref obj variable) - (oset obj value value) - gptel--set-buffer-locally) - ;; Then set the options specified by the preset - (funcall (oref obj set-props) value)) - -(cl-defmethod transient-format-value ((obj gptel--preset-variable)) - (with-slots (value) obj - (if gptel--known-presets ;MAYBE: Make this generic? - (apply #'format "(%s%s)" - (if value - (let ((mismatch (gptel--preset-mismatch-p value))) - (list (propertize "@" 'face - (if mismatch 'transient-key - '( :inherit transient-key - :inherit secondary-selection - :box -1 :weight bold))) - (propertize value 'face - (if mismatch - '(:inherit warning :strike-through t) - '(:inherit secondary-selection :box -1))))) - (list (propertize "@" 'face 'transient-key) - (propertize "preset" 'face 'transient-inactive-value)))) - ""))) - (defclass gptel--scope (gptel--switches) ((display-if-true :initarg :display-if-true :initform "buffer") (display-if-false :initarg :display-if-false :initform "global")) @@ -667,9 +648,9 @@ Also format its value in the Transient menu." (lambda () (interactive) (gptel--handle-tool-use gptel--fsm-last)) :if (lambda () (and gptel--fsm-last (eq (gptel-fsm-state gptel--fsm-last) 'TOOL))))]] - [[(gptel--infix-preset - :description "Request Parameters" :face 'transient-heading - :format "%d %v") + [[(gptel--preset + :key "@" :format "%d" + :description gptel--format-preset-string) (gptel--infix-variable-scope) (gptel--infix-provider) (gptel--infix-max-tokens) @@ -829,6 +810,65 @@ Customize `gptel-directives' for task-specific prompts." 'gptel--system-message "Directive" t))) :pad-keys t]) +;; ** Prefix for saving and applying presets + +(transient-define-prefix gptel--preset () + "Apply a gptel preset, or save the current configuration as a preset. + +A \"preset\" is a collection of gptel settings, such as the model, +backend, system message and enabled tools, that are applied and used +together. See `gptel-make-preset' for details." + :transient-suffix #'transient--do-return + [:description "Save or apply a preset collection of gptel options" + [:pad-keys t + ("C-s" "Save current settings to preset" gptel--save-preset)]] + [:if (lambda () gptel--known-presets) + :class transient-column + :setup-children + (lambda (_) + (transient-parse-suffixes + 'gptel--preset + (cl-loop + for (name-sym . preset) in gptel--known-presets + for name = (symbol-name name-sym) + with unused-keys = (nconc (number-sequence ?a ?z) + (number-sequence ?0 ?9)) + for description = (plist-get preset :description) + for key = (seq-find (lambda (k) (member k unused-keys)) + name (seq-first unused-keys)) + do (setq unused-keys (delq key unused-keys)) + collect + (list + (key-description (list key)) + (concat name + (propertize " " 'display '(space :align-to 20)) + (and description + (propertize (concat + "(" (gptel--describe-directive + description (- (window-width) 30)) + ")") + 'face 'shadow))) + `(lambda () (interactive) + (gptel--set-with-scope 'gptel--preset ',name-sym + gptel--set-buffer-locally) + (gptel--apply-preset + ',(cons name-sym preset) + (lambda (sym val) (gptel--set-with-scope + sym val gptel--set-buffer-locally))) + (message "Applied gptel preset %s" + (propertize ,name 'face 'transient-value)) + (when transient--stack + (run-at-time 0 nil #'transient-setup)))) + into generated + finally return + (nconc (list '(gptel--infix-variable-scope + :format "%d %k %v" + :description + (lambda () (format "%s %s" + (propertize "Apply preset" 'face 'transient-heading) + (propertize "Scope" 'face 'transient-active-prefix))))) + generated))))]) + ;; ** Prefix for selecting tools ;;;###autoload (autoload 'gptel-tools "gptel-transient" nil t) @@ -943,51 +983,6 @@ value of `gptel-use-context', set from here." (cdr (assoc destination choices))))) ;; ** Infixes for model parameters -(transient-define-infix gptel--infix-preset () - "Select and apply a gptel preset. - -Presets are collections of gptel options intended to be applied -together, defined via `gptel-make-preset'. Using this command and they -can be applied globally, buffer-locally or for the next request only." - :always-read t - ;; :argument "@" - ;; :class 'gptel--preset - :format " %k %d (%v)" - :key "@" - :description "Preset" - :class 'gptel--preset-variable - :variable 'gptel--preset - :prompt "Apply preset: " - :set-value #'gptel--set-with-scope - :set-props #'(lambda (name) - (when name - (gptel--apply-preset - (or (assoc name gptel--known-presets) - (assoc (intern-soft name) gptel--known-presets)) - (lambda (sym val) (gptel--set-with-scope - sym val gptel--set-buffer-locally))) - (message "Applied gptel preset %s" - (propertize name 'face 'transient-value))) - (transient-setup) - name) - :reader - #'(lambda (prompt initial history) - (if gptel--known-presets - (let ((completion-extra-properties - `(:annotation-function - ,(lambda (choice) - (when-let* ((desc - (plist-get - (cdr (or (assoc choice gptel--known-presets) - (assoc (intern-soft choice) gptel--known-presets))) - :description))) - (concat (propertize " " 'display '(space :align-to 40)) - desc)))))) - (completing-read - prompt gptel--known-presets nil t initial history)) - (message - "No gptel presets defined! Use `gptel-make-preset' to define presets.") - nil))) (transient-define-infix gptel--infix-variable-scope () "Set gptel's model parameters and system message in this buffer or globally."