From 9c4af204a3091729a1d7978c628fcd87c1a93c65 Mon Sep 17 00:00:00 2001 From: Karthik Chikmagalur Date: Mon, 10 Jul 2023 02:09:16 -0700 Subject: [PATCH] gptel-transient: Add crowdsourced prompts * gptel.el (gptel-crowdsourced-prompts-file): This file holds prompts compiled by the community. * gptel-transient.el (gptel--read-crowdsourced-prompt, gptel--crowdsourced-prompts, gptel-system-prompt--setup, gptel--crowdsourced-prompts-url): Fetch crowdsourced system prompts from https://github.com/f/awesome-chatgpt-prompts and pick one to use from the transient menu. --- gptel-transient.el | 90 ++++++++++++++++++++++++++++++++++++++++++++-- gptel.el | 13 +++++++ 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/gptel-transient.el b/gptel-transient.el index 77ed1eb..9894be0 100644 --- a/gptel-transient.el +++ b/gptel-transient.el @@ -48,6 +48,59 @@ Or is it the other way around?" (substring (symbol-name major-mode) nil -5)) (format "You are a prose editor. Rewrite the following text to be more professional."))) +(defvar gptel--crowdsourced-prompts-url + "https://github.com/f/awesome-chatgpt-prompts/raw/main/prompts.csv" + "URL for crowdsourced ChatGPT system prompts.") + +(defvar gptel--crowdsourced-prompts + (make-hash-table :test #'equal) + "Crowdsourced system prompts for ChatGPT.") + +(defun gptel--crowdsourced-prompts () + "Acquire and read crowdsourced system prompts for ChatGPT. + +These are stored in the variable `gptel--crowdsourced-prompts', +which see." + (when (hash-table-p gptel--crowdsourced-prompts) + (when (hash-table-empty-p gptel--crowdsourced-prompts) + (unless gptel-crowdsourced-prompts-file + (run-at-time 0 nil #'gptel-system-prompt) + (user-error "No crowdsourced prompts available")) + (unless (and (file-exists-p gptel-crowdsourced-prompts-file) + (time-less-p + (time-subtract (current-time) (days-to-time 14)) + (file-attribute-modification-time + (file-attributes gptel-crowdsourced-prompts-file)))) + (when (y-or-n-p + (concat + "Fetch crowdsourced system prompts from " + (propertize "https://github.com/f/awesome-chatgpt-prompts" 'face 'link) + "?")) + ;; Fetch file + (message "Fetching prompts...") + (if (url-copy-file gptel--crowdsourced-prompts-url + gptel-crowdsourced-prompts-file + 'ok-if-already-exists) + (message "Fetching prompts... done.") + (message "Could not retrieve new prompts.")))) + (if (not (file-readable-p gptel-crowdsourced-prompts-file)) + (progn (message "No crowdsourced prompts available") + (call-interactively #'gptel-system-prompt)) + (with-temp-buffer + (insert-file-contents gptel-crowdsourced-prompts-file) + (goto-char (point-min)) + (forward-line 1) + (while (not (eobp)) + (when-let ((act (read (current-buffer)))) + (forward-char) + (save-excursion + (while (re-search-forward "\"\"" (line-end-position) t) + (replace-match "\\\\\""))) + (when-let ((prompt (read (current-buffer)))) + (puthash act prompt gptel--crowdsourced-prompts))) + (forward-line 1))))) + gptel--crowdsourced-prompts)) + ;; * Transient Prefixes (define-obsolete-function-alias 'gptel-send-menu 'gptel-menu "0.3.2") @@ -123,8 +176,12 @@ Or is it the other way around?" (setq gptel--system-message ,prompt)) :transient t) into prompt-suffixes - finally return (cons (list 'gptel--suffix-system-message) - prompt-suffixes)))) + finally return + (nconc + (list (list 'gptel--suffix-system-message)) + prompt-suffixes + (list (list "SPC" "Pick crowdsourced prompt" + 'gptel--read-crowdsourced-prompt)))))) (transient-define-prefix gptel-system-prompt () "Change the system prompt to send ChatGPT. @@ -369,6 +426,35 @@ will get progressively longer!" (reusable-frames . visible)))))) ;; ** Set system message +(defun gptel--read-crowdsourced-prompt () + "Pick a crowdsourced system prompt for gptel. + +This uses the prompts in the variable +`gptel--crowdsourced-prompts', which see." + (interactive) + (if (not (hash-table-empty-p (gptel--crowdsourced-prompts))) + (let ((choice + (completing-read + "Pick and edit prompt: " + (lambda (str pred action) + (if (eq action 'metadata) + `(metadata + (affixation-function . + (lambda (cands) + (mapcar + (lambda (c) + (list c "" + (concat (propertize " " 'display '(space :align-to 22)) + " " (propertize (gethash c gptel--crowdsourced-prompts) + 'face 'completions-annotations)))) + cands)))) + (complete-with-action action gptel--crowdsourced-prompts str pred))) + nil t))) + (when-let ((prompt (gethash choice gptel--crowdsourced-prompts))) + (setq gptel--system-message prompt) + (gptel--suffix-system-message))) + (message "No prompts available."))) + (transient-define-suffix gptel--suffix-system-message () "Set directives sent to ChatGPT." :transient nil diff --git a/gptel.el b/gptel.el index 4ba8dac..a319e5c 100644 --- a/gptel.el +++ b/gptel.el @@ -155,6 +155,19 @@ is only inserted in dedicated gptel buffers." :group 'gptel :type '(alist :key-type symbol :value-type string)) +(defcustom gptel-crowdsourced-prompts-file + (let ((cache-dir (or (getenv "XDG_CACHE_HOME") + (getenv "XDG_DATA_HOME") + user-emacs-directory))) + (expand-file-name "gptel-crowdsourced-prompts.csv" cache-dir)) + "File used to store crowdsourced system prompts. + +These are prompts cached from an online source (see +`gptel--crowdsourced-prompts-url'), and can be set from the +transient menu interface provided by `gptel-menu'." + :group 'gptel + :type 'file) + ;; Model and interaction parameters (defvar-local gptel--system-message "You are a large language model living in Emacs and a helpful assistant. Respond concisely.")