-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add community prompt collection feature
Added a new file `ellama-community-prompts.el` to include functionality for managing and using community prompts. This includes downloading, parsing, and selecting prompts from a CSV file. Also updated `ellama.el` to support sending buffers to new chat sessions and added a new mode `ellama-blueprint-mode` with specific keybindings for handling blueprint buffers. Fix #98
- Loading branch information
1 parent
7ac470c
commit 071f49f
Showing
3 changed files
with
245 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
;;; ellama-community-prompts.el --- Community prompt collection -*- lexical-binding: t -*- | ||
|
||
;; Copyright (C) 2023-2025 Free Software Foundation, Inc. | ||
|
||
;; Author: Sergey Kostyaev <[email protected]> | ||
;; URL: http://github.com/s-kostyaev/ellama | ||
;; Keywords: help local tools | ||
;; Package-Requires: ((emacs "28.1") (llm "0.22.0") (transient "0.7") (compat "29.1")) | ||
;; Version: 1.3.0 | ||
;; SPDX-License-Identifier: GPL-3.0-or-later | ||
;; Created: 8th Oct 2023 | ||
|
||
;; This file 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, or (at your option) | ||
;; any later version. | ||
|
||
;; This file 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
;;; Commentary: | ||
;; | ||
;; Ellama is a tool for interacting with large language models from Emacs. | ||
;; It allows you to ask questions and receive responses from the | ||
;; LLMs. Ellama can perform various tasks such as translation, code | ||
;; review, summarization, enhancing grammar/spelling or wording and | ||
;; more through the Emacs interface. Ellama natively supports streaming | ||
;; output, making it effortless to use with your preferred text editor. | ||
;; | ||
|
||
;;; Code: | ||
(require 'plz) | ||
(require 'ellama) | ||
|
||
(defcustom ellama-community-prompts-url "https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv" | ||
"The URL of the community prompts collection." | ||
:type 'string | ||
:group 'ellama) | ||
|
||
(defcustom ellama-community-prompts-file (expand-file-name | ||
"community-prompts.csv" | ||
(file-name-concat | ||
user-emacs-directory | ||
"ellama")) | ||
"Path to the CSV file containing community prompts. | ||
This file is expected to be located inside an `ellama' subdirectory | ||
within your `user-emacs-directory'." | ||
:type 'file | ||
:group 'ellama) | ||
|
||
(defun ellama-community-prompts-ensure-file () | ||
"Ensure that the community prompt collection file is downloaded. | ||
Downloads the file from `ellama-community-prompts-url` if it does | ||
not already exist." | ||
(unless (file-exists-p ellama-community-prompts-file) | ||
(let* ((directory (file-name-directory ellama-community-prompts-file)) | ||
(response (plz 'get ellama-community-prompts-url | ||
:as 'file | ||
:then (lambda (filename) | ||
(rename-file filename ellama-community-prompts-file t)) | ||
:else (lambda (error) | ||
(message "Failed to download community prompts: %s" error))))) | ||
(when (and response (not (file-directory-p directory))) | ||
(make-directory directory t)) | ||
(when response | ||
(message "Community prompts file downloaded successfully."))))) | ||
|
||
(defun ellama-community-prompts-parse-csv-line (line) | ||
"Parse a single CSV LINE into a list of fields, handling quotes. | ||
LINE is the string to be parsed." | ||
(let ((i 0) | ||
(len (length line))) | ||
(cl-loop | ||
with fields = '() | ||
with current-field = "" | ||
with inside-quotes = nil | ||
while (< i len) | ||
do (let ((char (aref line i))) | ||
(cond | ||
;; Opening quote (start of field) | ||
((and (eq char ?\") (not inside-quotes)) | ||
(setq inside-quotes t) | ||
(cl-incf i)) | ||
;; Closing quote (end of field or escaped quote) | ||
((and (eq char ?\") inside-quotes) | ||
(if (and (< (1+ i) len) (eq (aref line (1+ i)) ?\")) | ||
(progn ; Escaped quote: add single quote, skip next character | ||
(setq current-field (concat current-field "\"")) | ||
(cl-incf i 2)) | ||
(setq inside-quotes nil) ; End of quoted field | ||
(cl-incf i))) | ||
;; Comma separator (outside quotes) | ||
((and (eq char ?,) (not inside-quotes)) | ||
(push current-field fields) | ||
(setq current-field "") | ||
(cl-incf i)) | ||
;; Regular character | ||
(t | ||
(setq current-field (concat current-field (string char))) | ||
(cl-incf i)))) | ||
;; Add the last field after loop ends | ||
finally return (nreverse (cons current-field fields))))) | ||
|
||
(defun ellama-community-prompts-convert-to-plist (parsed-line) | ||
"Convert PARSED-LINE to plist. | ||
PARSED-LINE is expected to be a list with three elements: :act, | ||
:prompt, and :for-devs." | ||
(let ((act (cl-first parsed-line)) | ||
(prompt (cl-second parsed-line)) | ||
(for-devs (string= "TRUE" (cl-third parsed-line)))) | ||
`(:act ,act :prompt ,prompt :for-devs ,for-devs))) | ||
|
||
(defvar ellama-community-prompts-collection nil | ||
"Community prompts collection.") | ||
|
||
(defun ellama-community-prompts-ensure () | ||
"Ensure that the community prompt collection are loaded and available. | ||
This function ensures that the file specified by `ellama-community-prompts-file' | ||
is read and parsed, and the resulting collection of prompts is stored in | ||
`ellama-community-prompts-collection'. If the collection is already populated, | ||
this function does nothing. | ||
Returns the collection of community prompts." | ||
(ellama-community-prompts-ensure-file) | ||
(unless ellama-community-prompts-collection | ||
(setq ellama-community-prompts-collection | ||
(let ((buf (find-file-noselect ellama-community-prompts-file))) | ||
(with-current-buffer buf | ||
(mapcar (lambda (line) | ||
(ellama-community-prompts-convert-to-plist | ||
(ellama-community-prompts-parse-csv-line | ||
line))) | ||
(cdr (string-lines | ||
(buffer-substring-no-properties | ||
(point-min) (point-max))))))))) | ||
ellama-community-prompts-collection) | ||
|
||
(defvar ellama-community-prompts-blurpint-buffer " *ellama-community-prompts-blueprint-buffer*" | ||
"Buffer for community prompt blueprint.") | ||
|
||
;;;###autoload | ||
(defun ellama-community-prompts-select-blueprint (&optional for-devs) | ||
"Select a prompt from the community prompt collection. | ||
The user is prompted to choose a role, and then a | ||
corresponding prompt is inserted into a blueprint buffer. | ||
Optional argument FOR-DEVS filters prompts for developers." | ||
(interactive "P") | ||
(let ((acts '()) | ||
selected-act selected-prompt) | ||
;; Collect unique acts from the filtered collection | ||
(dolist (prompt ellama-community-prompts-collection) | ||
(when (or (not for-devs) (eq for-devs (plist-get prompt :for-devs))) | ||
(cl-pushnew (plist-get prompt :act) acts))) | ||
;; Prompt user to select an act | ||
(setq selected-act (completing-read "Select Act: " acts)) | ||
;; Find the corresponding prompt | ||
(catch 'found-prompt | ||
(dolist (prompt ellama-community-prompts-collection) | ||
(when (and (string= selected-act (plist-get prompt :act)) | ||
(or (not for-devs) (eq for-devs (plist-get prompt :for-devs)))) | ||
(setq selected-prompt (plist-get prompt :prompt)) | ||
(throw 'found-prompt nil)))) | ||
;; Create a new buffer and insert the selected prompt | ||
(with-current-buffer (get-buffer-create ellama-community-prompts-blurpint-buffer) | ||
(erase-buffer) | ||
(let ((hard-newline t)) | ||
(insert selected-prompt) | ||
(fill-region (point-min) (point-max)) | ||
(ellama-blueprint-mode)) | ||
(switch-to-buffer (current-buffer)) | ||
(ellama-community-prompts-update-variables)))) | ||
|
||
(defun ellama-community-prompts-get-variable-list () | ||
"Return a deduplicated list of variables found in the current buffer." | ||
(save-excursion | ||
(let ((vars '())) | ||
(goto-char (point-min)) | ||
(while (re-search-forward "\{\\([^}]+\\)}" nil t) | ||
(push (match-string 1) vars)) | ||
(seq-uniq vars)))) | ||
|
||
(defun ellama-community-prompts-set-variable (var value) | ||
"Replace VAR with VALUE in blueprint buffer." | ||
(save-excursion | ||
(goto-char (point-min)) | ||
(while (search-forward (format "{%s}" var) nil t) | ||
(replace-match value)))) | ||
|
||
;;;###autoload | ||
(defun ellama-community-prompts-update-variables () | ||
"Prompt user for values of variables found in current buffer and update them." | ||
(interactive) | ||
(let ((vars (ellama-community-prompts-get-variable-list))) | ||
(dolist (var vars) | ||
(let ((value (read-string (format "Enter value for %s: " var)))) | ||
(ellama-community-prompts-set-variable var value))))) | ||
|
||
(provide 'ellama-community-prompts) | ||
;;; ellama-community-prompts.el ends here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters