-
Notifications
You must be signed in to change notification settings - Fork 162
Home
GPTel provides gptel-request
, a lower level function, to query ChatGPT with custom behavior.
Its signature is as follows:
(gptel-request
"my prompt" ;the prompt to send to ChatGPT
;; The below keys are all optional
:buffer some-buffer-or-name ;defaults to (current-buffer)
:system "Chat directive here" ;defaults to gptel--system-message
:position some-pt ;defaults to (point)
:context (list "any other info") ;will be available to the callback
:callback (lambda (response info) ...)) ;called with the response and an info plist
;defaults to inserting the response at :position
See its documentation for details.
For example, to define a command that accepts a prompt in the minibuffer and pops up a window with the response, you could define the following:
(defvar gptel-quick--history nil)
(defun gptel-quick (prompt)
(interactive (list (read-string "Ask ChatGPT: " nil gptel-quick--history)))
(when (string= prompt "") (user-error "A prompt is required."))
(gptel-request
prompt
:callback
(lambda (response info)
(if (not response)
(message "gptel-quick failed with message: %s" (plist-get info :status))
(with-current-buffer (get-buffer-create "*gptel-quick*")
(let ((inhibit-read-only t))
(erase-buffer)
(insert response))
(special-mode)
(display-buffer (current-buffer)
`((display-buffer-in-side-window)
(side . bottom)
(window-height . ,#'fit-window-to-buffer))))))))
A command that asks ChatGPT to rewrite and replace the current region, sentence or line. Calling with a prefix-arg will query the user for the instructions to include with the text.
(defun gptel-rewrite-and-replace (bounds &optional directive)
(interactive
(list
(cond
((use-region-p) (cons (region-beginning) (region-end)))
((derived-mode-p 'text-mode)
(list (bounds-of-thing-at-point 'sentence)))
(t (cons (line-beginning-position) (line-end-position))))
(and current-prefix-arg
(read-string "ChatGPT Directive: "
"You are a prose editor. Rewrite my prompt more professionally."))))
(gptel-request
(buffer-substring-no-properties (car bounds) (cdr bounds)) ;the prompt
:system (or directive "You are a prose editor. Rewrite my prompt more professionally.")
:buffer (current-buffer)
:context (cons (set-marker (make-marker) (car bounds))
(set-marker (make-marker) (cdr bounds)))
:callback
(lambda (response info)
(if (not response)
(message "ChatGPT response failed with: %s" (plist-get info :status))
(let* ((bounds (plist-get info :context))
(beg (car bounds))
(end (cdr bounds))
(buf (plist-get info :buffer)))
(with-current-buffer buf
(save-excursion
(goto-char beg)
(kill-region beg end)
(insert response)
(set-marker beg nil)
(set-marker end nil)
(message "Rewrote line. Original line saved to kill-ring."))))))))
Depending on your model or prompt, sometimes returned in-buffer refactored code would not be perfect, i.e. it would have incorrect indentation or even include Markdown formatting. The below is an example for how to add a hook to automatically fix such cases.
(cl-defun my/clean-up-gptel-refactored-code ()
"Clean up the code responses for refactored code in the current buffer.
Current buffer is guaranteed to be the response buffer."
(when gptel-mode ; Don't want this to happen in the dedicated buffer.
(cl-return-from my/clean-up-gptel-refactored-code))
(save-excursion
(let* ((res-beg (point-min))
(res-end nil)
(contents nil))
(unless (get-text-property res-beg 'gptel)
(setq res-beg (next-single-property-change res-beg 'gptel)))
(while res-beg
(setq res-end (next-single-property-change res-beg 'gptel))
(unless res-end
(setq res-end (point-max)))
(setq contents (buffer-substring-no-properties res-beg
res-end))
(setq contents (replace-regexp-in-string "\n*``.*\n*"
""
contents))
(delete-region res-beg res-end)
(goto-char res-beg)
(insert contents)
(setq res-end (point))
;; Indent the code to match the buffer indentation if it's messed up.
(indent-region res-beg res-end)
(pulse-momentary-highlight-region res-beg res-end)
(setq res-beg (next-single-property-change res-beg 'gptel))))))
Then, where you config gptel
, add:
(add-hook 'gptel-post-response-hook #'my/clean-up-gptel-refactored-code)
What this does is remove the Markdown tags and automatically indent the code with respect to the buffer. When you send the prompt, you can see the streamed-in bad code, and after the streaming is complete, it refactors it.