[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/gptel 376fb4b423 234/273: gptel-transient: Additional dire
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/gptel 376fb4b423 234/273: gptel-transient: Additional directives option (#249) |
Date: |
Wed, 1 May 2024 10:02:39 -0400 (EDT) |
branch: elpa/gptel
commit 376fb4b423f504a92da5edde81cdf6431c10f7df
Author: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
Commit: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
gptel-transient: Additional directives option (#249)
* gptel.el (gptel-end-of-response, gptel-beginning-of-response,
gptel-expert-commands): Add `gptel-expert-commands` to selectively
enable experimental options in `gptel-menu`. This should keep the
interface from overwhelming new users. Add a command to move to
the beginning of a response.
* gptel-transient.el (gptel-menu, gptel-system-prompt,
gptel--instructions-make-overlay, gptel-option-overlaid,
transient-format-value, gptel--additional-directive,
gptel--additional-directive-get): Add a transient option to
include a (short) additional instruction/directive along with the
system message. This makes it convenient to have an extensive
system message and specify additional, per-response tasks (such as
refactoring) on top. Ensure that the dry run options handle this
correctly. This option is made available when
`gptel-expert-commands` is turned on.
NOTE: WIP design. The nomenclature for `gptel-expert-commands`
and "additional directive" is subject to change.
---
gptel-transient.el | 161 ++++++++++++++++++++++++++++++++++++++++++++---------
gptel.el | 39 ++++++++++---
2 files changed, 166 insertions(+), 34 deletions(-)
diff --git a/gptel-transient.el b/gptel-transient.el
index e3ad6cbe28..c9f0d36c04 100644
--- a/gptel-transient.el
+++ b/gptel-transient.el
@@ -116,11 +116,16 @@ which see."
"Change parameters of prompt to send to the LLM."
;; :incompatible '(("-m" "-n" "-k" "-e"))
[: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 :transient t)]
- [["Session Parameters"
+ (lambda ()
+ (string-replace
+ "\n" "⮐ "
+ (truncate-string-to-width
+ gptel--system-message (max (- (window-width) 6) 14) nil nil t)))
+ [""
+ "Instructions"
+ ("h" "Set system message" gptel-system-prompt :transient t)
+ (gptel--additional-directive :if (lambda () gptel-expert-commands))]]
+ [["Model Parameters"
(gptel--infix-provider)
;; (gptel--infix-model)
(gptel--infix-max-tokens)
@@ -175,19 +180,29 @@ which see."
:transient t)
("E" "Ediff previous" gptel--ediff
:if gptel--at-response-history-p)]
- ["Inspect"
- ("I" "Query as Lisp"
+ ["Dry Run" :if (lambda () (or gptel-log-level gptel-expert-commands))
+ ("I" "Inspect query (Lisp)"
(lambda ()
"Inspect the query that will be sent as a lisp object."
(interactive)
- (gptel--sanitize-model)
- (gptel--inspect-query)))
- ("J" "Query as JSON"
+ (let* ((extra (gptel--additional-directive-get
+ (transient-args
+ transient-current-command)))
+ (gptel--system-message
+ (concat gptel--system-message extra)))
+ (gptel--sanitize-model)
+ (gptel--inspect-query))))
+ ("J" "Inspect query (JSON)"
(lambda ()
"Inspect the query that will be sent as a JSON object."
(interactive)
- (gptel--sanitize-model)
- (gptel--inspect-query 'json)))]]
+ (let* ((extra (gptel--additional-directive-get
+ (transient-args
+ transient-current-command)))
+ (gptel--system-message
+ (concat gptel--system-message extra)))
+ (gptel--sanitize-model)
+ (gptel--inspect-query 'json))))]]
(interactive)
(gptel--sanitize-model)
(transient-setup 'gptel-menu))
@@ -239,15 +254,18 @@ which see."
:transient 'transient--do-exit))))))
(transient-define-prefix gptel-system-prompt ()
- "Change the LLM system prompt.
+ "Set the LLM system message for LLM interactions in this buffer.
-The \"system\" prompt establishes directives for the chat
-session. Some examples of system prompts are:
+The \"system message\" establishes directives for the chat
+session and modifies the behavior of the LLM. 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.
+More extensive system messages can be useful for specific tasks.
+
Customize `gptel-directives' for task-specific prompts."
[:description
(lambda () (format "Current directive: %s"
@@ -402,6 +420,97 @@ responses."
;; ** Infix for the refactor/rewrite system message
+(defun gptel--instructions-make-overlay (text &optional ov)
+ "TODO"
+ (save-excursion
+ (cond
+ ((use-region-p) (goto-char (region-beginning)))
+ ((gptel--in-response-p) (gptel-beginning-of-response))
+ (t (text-property-search-backward 'gptel 'response)))
+ (skip-chars-forward "\n \t")
+ (if (and ov (overlayp ov))
+ (move-overlay ov (point) (point) (current-buffer))
+ (setq ov (make-overlay (point) (point) nil t)))
+ (overlay-put ov 'before-string nil)
+ ;; (unless (or (bobp) (eq (char-before) "\n"))
+ ;; (overlay-put ov 'before-string (propertize "\n" 'font-lock-face
'shadow)))
+ (overlay-put ov 'category 'gptel)
+ (overlay-put
+ ov 'after-string
+ (concat
+ (propertize (concat "GPTEL: " text)
+ 'font-lock-face '(:inherit shadow :box t))
+ "\n"))
+ ov))
+
+(defclass gptel-option-overlaid (transient-option)
+ ((display-nil :initarg :display-nil)
+ (overlay :initarg :overlay))
+ "Transient options for overlays displayed in the working buffer.")
+
+(cl-defmethod transient-format-value ((obj gptel-option-overlaid))
+ "set up the in-buffer overlay for additional directive, a string.
+
+Also format its value in the Transient menu."
+ (let ((value (oref obj value))
+ (ov (oref obj overlay))
+ (argument (oref obj argument)))
+ ;; Making an overlay
+ (if (or (not value) (string-empty-p value))
+ (when ov (delete-overlay ov))
+ (with-current-buffer transient--original-buffer
+ (oset obj overlay (gptel--instructions-make-overlay value ov)))
+ (letrec ((ov-clear-hook
+ (lambda () (when-let* ((ov (oref obj overlay))
+ ((overlayp ov)))
+ (remove-hook 'transient-exit-hook
+ ov-clear-hook)
+ (delete-overlay ov)))))
+ (add-hook 'transient-exit-hook ov-clear-hook)))
+ ;; Updating transient menu display
+ (if value
+ (propertize (concat argument (truncate-string-to-width value 15 nil
nil "..."))
+ 'face 'transient-value)
+ (propertize
+ (concat "(" (symbol-name (oref obj display-nil)) ")")
+ 'face 'transient-inactive-value))))
+
+(transient-define-infix gptel--additional-directive ()
+ "Additional directive intended for the next query only.
+
+This is useful to define a quick task on top of a more extensive
+or detailed system prompt (directive).
+
+For example, with code/text selected:
+
+- Rewrite this function to do X while avoiding Y.
+- Change the tone of the following paragraph to be more direct.
+
+Or in an extended conversation:
+
+- Phrase you next response in ten words or less.
+- Pretend for now that you're an anthropologist."
+ :class 'gptel-option-overlaid
+ ;; :variable 'gptel--instructions
+ :display-nil 'none
+ :overlay nil
+ :argument ":"
+ :prompt "Instructions for next response only: "
+ :reader (lambda (prompt initial history)
+ (let* ((extra (read-string prompt initial history)))
+ (unless (string-empty-p extra) extra)))
+ :format " %k %d %v"
+ :key "d"
+ :argument ":"
+ :description "Additional directive"
+ :transient t)
+
+(defun gptel--additional-directive-get (args)
+ "Find the additional directive in the transient ARGS of this command."
+ (cl-some (lambda (s) (and (string-prefix-p ":" s)
+ (concat "\n\n" (substring s 1))))
+ args))
+
(transient-define-infix gptel--infix-rewrite-prompt ()
"Chat directive (system message) to use for rewriting or refactoring."
:description (lambda () (if (derived-mode-p 'prog-mode)
@@ -435,6 +544,7 @@ responses."
(backend-name (gptel-backend-name gptel-backend))
(buffer) (position)
(callback) (gptel-buffer-name)
+ (system-extra (gptel--additional-directive-get args))
;; Input redirection: grab prompt from elsewhere?
(prompt
(cond
@@ -531,10 +641,18 @@ responses."
(setq buffer (get-buffer-create gptel-buffer-name))
(with-current-buffer buffer (setq position (point)))))
- ;; Create prompt, unless doing input-redirection above
- (unless prompt
- (setq prompt (gptel--create-prompt (gptel--at-word-end (point)))))
+ (gptel-request
+ prompt
+ :buffer (or buffer (current-buffer))
+ :position position
+ :in-place (and in-place (not output-to-other-buffer-p))
+ :stream stream
+ :system (concat gptel--system-message system-extra)
+ :callback callback)
+ ;; NOTE: Possible future race condition here if Emacs ever drops the GIL.
+ ;; The HTTP request callback might modify the buffer before the in-place
+ ;; text is killed below.
(when in-place
;; Kill the latest prompt
(let ((beg
@@ -554,13 +672,6 @@ responses."
(list (buffer-substring-no-properties beg end))))
(kill-region beg end)))
- (gptel-request
- prompt
- :buffer (or buffer (current-buffer))
- :position position
- :in-place (and in-place (not output-to-other-buffer-p))
- :stream stream
- :callback callback)
(when output-to-other-buffer-p
(message (concat "Prompt sent to buffer: "
(propertize gptel-buffer-name 'face 'help-key-binding)))
diff --git a/gptel.el b/gptel.el
index 2b96e0edd3..6f9d3ed1d2 100644
--- a/gptel.el
+++ b/gptel.el
@@ -469,6 +469,11 @@ README for examples."
(restricted-sexp :match-alternatives (gptel-backend-p 'nil)
:tag "Other backend")))
+(defvar gptel-expert-commands nil
+ "Whether experimental gptel options should be enabled.
+
+This opens up advanced options in `gptel-menu'.")
+
(defvar-local gptel--bounds nil)
(put 'gptel--bounds 'safe-local-variable #'always)
@@ -545,16 +550,32 @@ Note: This will move the cursor."
(scroll-up-command))
(error nil))))
-(defun gptel-end-of-response (_ _ &optional arg)
+(defun gptel-beginning-of-response (&optional _ _ arg)
+ "Move point to the beginning of the LLM response ARG times."
+ (interactive "p")
+ ;; FIXME: Only works for arg == 1
+ (gptel-end-of-response nil nil (- (or arg 1))))
+
+(defun gptel-end-of-response (&optional _ _ arg)
"Move point to the end of the LLM response ARG times."
- (interactive (list nil nil current-prefix-arg))
- (dotimes (_ (if arg (abs arg) 1))
- (text-property-search-forward 'gptel 'response t)
- (when (looking-at (concat "\n\\{1,2\\}"
- (regexp-quote
- (gptel-prompt-prefix-string))
- "?"))
- (goto-char (match-end 0)))))
+ (interactive (list nil nil
+ (prefix-numeric-value current-prefix-arg)))
+ (let ((search (if (> arg 0)
+ #'text-property-search-forward
+ #'text-property-search-backward)))
+ (dotimes (_ (abs arg))
+ (funcall search 'gptel 'response t)
+ (if (> arg 0)
+ (when (looking-at (concat "\n\\{1,2\\}"
+ (regexp-quote
+ (gptel-prompt-prefix-string))
+ "?"))
+ (goto-char (match-end 0)))
+ (when (looking-back (concat (regexp-quote
+ (gptel-response-prefix-string))
+ "?")
+ (point-min))
+ (goto-char (match-beginning 0)))))))
(defmacro gptel--at-word-end (&rest body)
"Execute BODY at end of the current word or punctuation."
- [nongnu] elpa/gptel 7271d0e408 154/273: gptel: Try to save/restore gptel-backend in files, (continued)
- [nongnu] elpa/gptel 7271d0e408 154/273: gptel: Try to save/restore gptel-backend in files, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 7b19cdf117 188/273: Sanitize gpt-model value just in time, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 89decb4201 192/273: README: Mention transient menu option saving, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel c41a53f526 194/273: gptel: save/restore multi-line directives (#180), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel e2eccd8b08 205/273: gptel: Remove references to gptel--debug (fix #205), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel ef8b9093d2 206/273: gptel-gemini: Use permissive API safety settings, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 5e9e36d854 219/273: gptel: rear-nonsticky text-property by default, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 3d6147830e 220/273: gptel: Fix model/backend mismatch (#242), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 07992f79cc 227/273: gptel-anthropic: Support for the Claude haiku model, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel fbb0ee29c4 224/273: gptel-org-test: Add mores tests for org conversion, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 376fb4b423 234/273: gptel-transient: Additional directives option (#249),
ELPA Syncer <=
- [nongnu] elpa/gptel 34a52aa047 246/273: gptel-anthropic: Remove debug code, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel e3b3591d73 240/273: README: Add support for Groq (#257), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 81bb467104 250/273: gptel: Set window when running post-response hook, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel b4088e3f7b 261/273: README: New pacakge based on gptel, magit-gptcommit (#281), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 9b094b8b1e 265/273: gptel: Fix url-retrieve response parser bug, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 2b2dbe2664 043/273: gptel: Fix parsing error in url-retrieve buffer, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 5ebaf361f1 046/273: gptel: Handle the prompt prefix string automatically, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel c6abda6f0f 075/273: gptel-transient: Rename gptel-send-menu -> gptel-menu, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 8a9ae56e77 074/273: gptel: Add streaming, in-place options to gptel-request, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel ba133267ec 063/273: gptel: Run post-response-hook after inserting response, ELPA Syncer, 2024/05/01