From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Philip Kaludercic Newsgroups: gmane.emacs.devel Subject: Re: [NonGNU ELPA] Add package gptel Date: Sun, 28 Apr 2024 08:21:31 +0000 Message-ID: <87ttjlsxro.fsf@posteo.net> References: <877cgi9m4w.fsf@gmail.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="6125"; mail-complaints-to="usenet@ciao.gmane.io" Cc: emacs-devel@gnu.org To: Karthik Chikmagalur Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sun Apr 28 10:22:16 2024 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1s0znc-0001QO-H9 for ged-emacs-devel@m.gmane-mx.org; Sun, 28 Apr 2024 10:22:16 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s0zn1-0003aE-I2; Sun, 28 Apr 2024 04:21:39 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s0zmz-0003Zg-Cj for emacs-devel@gnu.org; Sun, 28 Apr 2024 04:21:37 -0400 Original-Received: from mout02.posteo.de ([185.67.36.66]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s0zmw-0004GU-Hj for emacs-devel@gnu.org; Sun, 28 Apr 2024 04:21:37 -0400 Original-Received: from submission (posteo.de [185.67.36.169]) by mout02.posteo.de (Postfix) with ESMTPS id 59414240101 for ; Sun, 28 Apr 2024 10:21:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017; t=1714292492; bh=plMAqKq8jHxXdwrSv1XnNI5zMaSLJvottCXWt9zAY38=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type: From; b=cu1RJF9rtj1r3FvO9nCEdk2qv76VUV0WEATF+3/TVXb1jWPw4N4/bTrzUlBQNNTEe eZ5xjwUjNV9V5T4CHUmWc/U6fj5AqsjYCHmTCPJGRWL4Rcxs53gx/wuxOC8nGnZIBp h5LK8aiITBFoGPdUB/MJIeoXeN5XhueljLmwgdxVX91EBR0vQVsETJencYt5PIbztd GphOgo9Mcge0+FKATTbBHbv/CSLdeGIGhmrZGacK2ezPgIoue0KyTZM7n96ABKCF5S aFCwS7e/sfpztiacPmPjvqRuHD9Kv99gIGriziNgCaLAse37cuJK1lQmzIh86yUnWc bObGSq1lw8eiA== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4VRzvH66PYz6tvs; Sun, 28 Apr 2024 10:21:31 +0200 (CEST) In-Reply-To: <877cgi9m4w.fsf@gmail.com> (Karthik Chikmagalur's message of "Sat, 27 Apr 2024 20:55:27 -0700") X-Hashcash: 1:20:240428:karthikchikmagalur@gmail.com::MxBGv7Er2otKWSsX:0XCg X-Hashcash: 1:20:240428:emacs-devel@gnu.org::B9jl69Ko7IqvSkms:4HK/ Received-SPF: pass client-ip=185.67.36.66; envelope-from=philipk@posteo.net; helo=mout02.posteo.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:318256 Archived-At: --=-=-= Content-Type: text/plain Karthik Chikmagalur writes: > I'd like to submit my package gptel to NonGNU ELPA: > > https://github.com/karthink/gptel > > gptel is an LLM (Large Language Model) client for Emacs that supports > most LLM providers that offer an HTTP API. This includes open source > models running locally on the user's PC or network via Ollama, > Llama.cpp, Llamafiles and GPT4All, and access to larger models provided > by a growing number of companies. This includes ChatGPT, Anthropic > Claude, Gemini, Kagi, Groq, Perplexity, TogetherAI and several more. Here are a few quick comments, but the code is fine overall: --=-=-= Content-Type: text/plain Content-Disposition: inline diff --git a/gptel.el b/gptel.el index b3ff962..96f7626 100644 --- a/gptel.el +++ b/gptel.el @@ -156,7 +156,6 @@ "Path to a proxy to use for gptel interactions. Passed to curl via --proxy arg, for example \"proxy.yourorg.com:80\" Leave it empty if you don't use a proxy." - :group 'gptel :type 'string) (defcustom gptel-api-key #'gptel-api-key-from-auth-source @@ -166,7 +165,6 @@ OpenAI by default. Can also be a function of no arguments that returns an API key (more secure) for the active backend." - :group 'gptel :type '(choice (string :tag "API key") (function :tag "Function that returns the API key"))) @@ -182,13 +180,11 @@ When set to nil, Emacs waits for the full response and inserts it all at once. This wait is asynchronous. \='tis a bit silly." - :group 'gptel :type 'boolean) (make-obsolete-variable 'gptel-playback 'gptel-stream "0.3.0") (defcustom gptel-use-curl (and (executable-find "curl") t) "Whether gptel should prefer Curl when available." - :group 'gptel :type 'boolean) (defcustom gptel-curl-file-size-threshold 130000 @@ -207,11 +203,10 @@ and the typical size of the data being sent in GPTel queries. A larger value may improve performance by avoiding the overhead of creating temporary files for small data payloads, while a smaller value may be needed if the command-line argument size is limited by the operating system." - :group 'gptel - :type 'integer) + :type 'natnum) (defcustom gptel-response-filter-functions - '(gptel--convert-org) + (list #'gptel--convert-org) "Abnormal hook for transforming the response from an LLM. This is used to format the response in some way, such as filling @@ -225,7 +220,6 @@ should return the transformed string. NOTE: This is only used for non-streaming responses. To transform streaming responses, use `gptel-post-stream-hook' and `gptel-post-response-functions'." - :group 'gptel :type 'hook) (defcustom gptel-pre-response-hook nil @@ -235,7 +229,6 @@ This hook is called in the buffer where the LLM response will be inserted. Note: this hook only runs if the request succeeds." - :group 'gptel :type 'hook) (define-obsolete-variable-alias @@ -255,7 +248,6 @@ end positions. Note: this hook runs even if the request fails. In this case the response beginning and end positions are both the cursor position at the time of the request." - :group 'gptel :type 'hook) ;; (defcustom gptel-pre-stream-insert-hook nil @@ -271,18 +263,16 @@ at the time of the request." This hook is called in the buffer from which the prompt was sent to the LLM, and after a text insertion." - :group 'gptel :type 'hook) (defcustom gptel-default-mode (if (fboundp 'markdown-mode) - 'markdown-mode - 'text-mode) + 'markdown-mode + 'text-mode) "The default major mode for dedicated chat buffers. If `markdown-mode' is available, it is used. Otherwise gptel defaults to `text-mode'." - :group 'gptel - :type 'symbol) + :type 'function) ;; TODO: Handle `prog-mode' using the `comment-start' variable (defcustom gptel-prompt-prefix-alist @@ -296,7 +286,6 @@ responses, and is removed from the query before it is sent. This is an alist mapping major modes to the prefix strings. This is only inserted in dedicated gptel buffers." - :group 'gptel :type '(alist :key-type symbol :value-type string)) (defcustom gptel-response-prefix-alist @@ -310,7 +299,6 @@ responses. This is an alist mapping major modes to the reply prefix strings. This is only inserted in dedicated gptel buffers before the AI's response." - :group 'gptel :type '(alist :key-type symbol :value-type string)) (defcustom gptel-use-header-line t @@ -318,8 +306,7 @@ is only inserted in dedicated gptel buffers before the AI's response." When set to nil, use the mode line for (minimal) status information and the echo area for messages." - :type 'boolean - :group 'gptel) + :type 'boolean) (defcustom gptel-display-buffer-action '(pop-to-buffer) "The action used to display gptel chat buffers. @@ -333,17 +320,12 @@ where FUNCTION is a function or a list of functions. Each such function should accept two arguments: a buffer to display and an alist of the same form as ALIST. See info node `(elisp)Choosing Window' for details." - :group 'gptel - :type '(choice - (const :tag "Use display-buffer defaults" nil) - (const :tag "Display in selected window" (pop-to-buffer-same-window)) - (cons :tag "Specify display-buffer action" - (choice function (repeat :tag "Functions" function)) - alist))) + :type display-buffer--action-custom-type) (defcustom gptel-crowdsourced-prompts-file - (let ((cache-dir (or (getenv "XDG_CACHE_HOME") - (getenv "XDG_DATA_HOME") + (let ((cache-dir (or (eval-when-compile + (require 'xdg) + (xdg-cache-home)) user-emacs-directory))) (expand-file-name "gptel-crowdsourced-prompts.csv" cache-dir)) "File used to store crowdsourced system prompts. @@ -351,7 +333,6 @@ Window' for details." 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 @@ -368,8 +349,7 @@ request to the LLM. Each entry in this alist maps a symbol naming the directive to the string that is sent. To set the directive for a chat session interactively call `gptel-send' with a prefix argument." - :group 'gptel - :safe #'always + :safe #'always ;is this really always safe? :type '(alist :key-type symbol :value-type string)) (defvar gptel--system-message (alist-get 'default gptel-directives) @@ -386,8 +366,7 @@ responses. To set the target token count for a chat session interactively call `gptel-send' with a prefix argument." :safe #'always - :group 'gptel - :type '(choice (integer :tag "Specify Token count") + :type '(choice (natnum :tag "Specify Token count") (const :tag "Default" nil))) (defcustom gptel-model "gpt-3.5-turbo" @@ -408,7 +387,6 @@ The current options for ChatGPT are To set the model for a chat session interactively call `gptel-send' with a prefix argument." :safe #'always - :group 'gptel :type '(choice (string :tag "Specify model name") (const :tag "GPT 3.5 turbo" "gpt-3.5-turbo") @@ -428,7 +406,6 @@ of the response, with 2.0 being the most random. To set the temperature for a chat session interactively call `gptel-send' with a prefix argument." :safe #'always - :group 'gptel :type 'number) (defvar gptel--known-backends nil @@ -467,7 +444,6 @@ one of the available backend creation functions: See their documentation for more information and the package README for examples." :safe #'always - :group 'gptel :type `(choice (const :tag "ChatGPT" ,gptel--openai) (restricted-sexp :match-alternatives (gptel-backend-p 'nil) @@ -496,7 +472,6 @@ debug: Log request/response bodies, headers and all other When non-nil, information is logged to `gptel--log-buffer-name', which see." - :group 'gptel :type '(choice (const :tag "No logging" nil) (const :tag "Limited" info) @@ -523,12 +498,14 @@ and \"apikey\" as USER." (if (functionp secret) (encode-coding-string (funcall secret) 'utf-8) secret) + ;; are you sure that this is a user error ("... comes from an + ;; incorrect manipulation by the user")? (user-error "No `gptel-api-key' found in the auth source"))) ;; FIXME Should we utf-8 encode the api-key here? (defun gptel--get-api-key (&optional key) "Get api key from KEY, or from `gptel-api-key'." - (when-let* ((key-sym (or key (gptel-backend-key gptel-backend)))) + (when-let ((key-sym (or key (gptel-backend-key gptel-backend)))) (cl-typecase key-sym (function (funcall key-sym)) (string key-sym) @@ -540,15 +517,18 @@ and \"apikey\" as USER." (defsubst gptel--numberize (val) "Ensure VAL is a number." - (if (stringp val) (string-to-number val) val)) + (cond + ((numberp val) val) + ((stringp val) (string-to-number val)) + ((error "%S cannot be converted to a number" val)))) (defun gptel-auto-scroll () "Scroll window if LLM response continues below viewport. Note: This will move the cursor." - (when-let* ((win (get-buffer-window (current-buffer) 'visible)) - ((not (pos-visible-in-window-p (point) win))) - (scroll-error-top-bottom t)) + (when-let ((win (get-buffer-window (current-buffer) 'visible)) + ((not (pos-visible-in-window-p (point) win))) + (scroll-error-top-bottom t)) (condition-case nil (with-selected-window win (scroll-up-command)) @@ -586,7 +566,7 @@ Note: This will move the cursor." "Execute BODY at end of the current word or punctuation." `(save-excursion (skip-syntax-forward "w.") - ,@body)) + ,(macroexp-progn body))) ;just as a suggestion (defun gptel-prompt-prefix-string () (or (alist-get major-mode gptel-prompt-prefix-alist) "")) @@ -1106,6 +1086,7 @@ the response is inserted into the current buffer after point." (encode-coding-string (gptel--json-encode (plist-get info :data)) 'utf-8))) + ;; why do these checks not occur inside of `gptel--log'? (when gptel-log-level ;logging (when (eq gptel-log-level 'debug) (gptel--log (gptel--json-encode @@ -1169,11 +1150,13 @@ See `gptel-curl--get-response' for its contents.") (error-type (plist-get error-data :type)) (backend-name (gptel-backend-name backend))) (if (stringp error-data) - (progn (message "%s error: (%s) %s" backend-name http-msg error-data) - (setq error-msg (string-trim error-data))) + (progn + (message "%s error: (%s) %s" backend-name http-msg error-data) + (setq error-msg (string-trim error-data))) (when (stringp error-msg) (message "%s error: (%s) %s" backend-name http-msg (string-trim error-msg))) - (when error-type (setq http-msg (concat "(" http-msg ") " (string-trim error-type))))) + (when error-type + (setq http-msg (concat "(" http-msg ") " (string-trim error-type))))) (list nil (concat "(" http-msg ") " (or error-msg ""))))) ((eq response 'json-read-error) (list nil (concat "(" http-msg ") Malformed JSON in response.") "json-read-error")) @@ -1188,7 +1171,7 @@ See `gptel-curl--get-response' for its contents.") "Check if MODEL is available in BACKEND, adjust accordingly. If SHOOSH is true, don't issue a warning." - (let* ((available (gptel-backend-models backend))) + (let ((available (gptel-backend-models backend))) (unless (member model available) (let ((fallback (car available))) (unless shoosh @@ -1329,7 +1312,7 @@ context for the ediff session." "Mark gptel response at point, if any." (interactive) (unless (gptel--in-response-p) (user-error "No gptel response at point")) - (pcase-let* ((`(,beg . ,end) (gptel--get-bounds))) + (pcase-let ((`(,beg . ,end) (gptel--get-bounds))) (goto-char beg) (push-mark) (goto-char end) (activate-mark))) (defun gptel--previous-variant (&optional arg) @@ -1365,3 +1348,7 @@ context for the ediff session." (provide 'gptel) ;;; gptel.el ends here + +;; Local Variables: +;; bug-reference-url-format: "https://github.com/karthink/gptel/issues/%s" +;; End: --=-=-= Content-Type: text/plain I'd be interested if you could explain what the difference is to the already existing ellama package? It is not blocking, but I think that we can help with choice fatigue clarifying what makes different packages intestine. > gptel tries to provide a uniform Emacs-y UI for all backends, and works > as both a chat interface (in dedicated chat buffers) and as a > helper/lookup agent in all Emacs buffers. There is a demo showcasing > its many uses here: > > https://www.youtube.com/watch?v=bsRnh_brggM Is this video mirrored elsewhere? > It has no external dependencies (Emacs packages or otherwise), but uses > Curl if it's available. > > Karthik > > Karthik Chikmagalur writes: >> It has no external dependencies (Emacs packages or otherwise), but uses >> Curl if it's available. > > Just realized this isn't true -- gptel depends on the compat package to > support Emacs 27 and 28. On that topic, why do you require Compat using (require 'compat nil t) -- Philip Kaludercic on icterid --=-=-=--