Skip to content

karthink/gptel

Repository files navigation

gptel: A simple LLM client for Emacs

https://elpa.nongnu.org/nongnu/gptel.svg https://stable.melpa.org/packages/gptel-badge.svg https://melpa.org/packages/gptel-badge.svg

gptel is a simple Large Language Model chat client for Emacs, with support for multiple models and backends.

LLM BackendSupportsRequires
ChatGPTAPI key
AzureDeployment and API key
OllamaOllama running locally
GPT4AllGPT4All running locally
GeminiAPI key
Llama.cppLlama.cpp running locally
LlamafileLocal Llamafile server
Kagi FastGPTAPI key
Kagi SummarizerAPI key
together.aiAPI key
AnyscaleAPI key
PerplexityAPI key
Anthropic (Claude)API key
GroqAPI key
OpenRouterAPI key

General usage: (YouTube Demo)

intro-demo.mp4
intro-demo-2.mp4

Multi-LLM support demo:

gptel-multi.mp4
  • It’s async and fast, streams responses.
  • Interact with LLMs from anywhere in Emacs (any buffer, shell, minibuffer, wherever)
  • LLM responses are in Markdown or Org markup.
  • Supports conversations and multiple independent sessions.
  • Save chats as regular Markdown/Org/Text files and resume them later.
  • You can go back and edit your previous prompts or LLM responses when continuing a conversation. These will be fed back to the model.
  • Don’t like gptel’s workflow? Use it to create your own for any supported model/backend with a simple API.

gptel uses Curl if available, but falls back to url-retrieve to work without external dependencies.

Contents

Installation

gptel is on MELPA. Ensure that MELPA is in your list of sources, then install it with M-x package-install⏎ gptel.

(Optional: Install markdown-mode.)

Straight

(straight-use-package 'gptel)

Installing the markdown-mode package is optional.

Manual

Clone or download this repository and run M-x package-install-file⏎ on the repository directory.

Installing the markdown-mode package is optional.

Doom Emacs

In packages.el

(package! gptel)

In config.el

(use-package! gptel
 :config
 (setq! gptel-api-key "your key"))

“your key” can be the API key itself, or (safer) a function that returns the key. Setting gptel-api-key is optional, you will be asked for a key if it’s not found.

Spacemacs

After installation with M-x package-install⏎ gptel

  • Add gptel to dotspacemacs-additional-packages
  • Add (require 'gptel) to dotspacemacs/user-config

Setup

ChatGPT

Procure an OpenAI API key.

Optional: Set gptel-api-key to the key. Alternatively, you may choose a more secure method such as:

  • Storing in ~/.authinfo. By default, “api.openai.com” is used as HOST and “apikey” as USER.
    machine api.openai.com login apikey password TOKEN
        
  • Setting it to a function that returns the key.

Other LLM backends

Azure

Register a backend with

(gptel-make-azure "Azure-1"             ;Name, whatever you'd like
  :protocol "https"                     ;Optional -- https is the default
  :host "YOUR_RESOURCE_NAME.openai.azure.com"
  :endpoint "/openai/deployments/YOUR_DEPLOYMENT_NAME/chat/completions?api-version=2023-05-15" ;or equivalent
  :stream t                             ;Enable streaming responses
  :key #'gptel-api-key
  :models '("gpt-3.5-turbo" "gpt-4"))

Refer to the documentation of gptel-make-azure to set more parameters.

You can pick this backend from the menu when using gptel. (see Usage).

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq
 gptel-model "gpt-3.5-turbo"
 gptel-backend (gptel-make-azure "Azure-1"
                 :protocol "https"
                 :host "YOUR_RESOURCE_NAME.openai.azure.com"
                 :endpoint "/openai/deployments/YOUR_DEPLOYMENT_NAME/chat/completions?api-version=2023-05-15"
                 :stream t
                 :key #'gptel-api-key
                 :models '("gpt-3.5-turbo" "gpt-4")))

GPT4All

Register a backend with

(gptel-make-gpt4all "GPT4All"           ;Name of your choosing
 :protocol "http"
 :host "localhost:4891"                 ;Where it's running
 :models '("mistral-7b-openorca.Q4_0.gguf")) ;Available models

These are the required parameters, refer to the documentation of gptel-make-gpt4all for more.

You can pick this backend from the menu when using gptel (see Usage).

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above. Additionally you may want to increase the response token size since GPT4All uses very short (often truncated) responses by default.

;; OPTIONAL configuration
(setq
 gptel-max-tokens 500
 gptel-model "mistral-7b-openorca.Q4_0.gguf"
 gptel-backend (gptel-make-gpt4all "GPT4All"
                 :protocol "http"
                 :host "localhost:4891"
                 :models '("mistral-7b-openorca.Q4_0.gguf")))

Ollama

Register a backend with

(gptel-make-ollama "Ollama"             ;Any name of your choosing
  :host "localhost:11434"               ;Where it's running
  :stream t                             ;Stream responses
  :models '("mistral:latest"))          ;List of models

These are the required parameters, refer to the documentation of gptel-make-ollama for more.

You can pick this backend from the menu when using gptel (see Usage)

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq
 gptel-model "mistral:latest"
 gptel-backend (gptel-make-ollama "Ollama"
                 :host "localhost:11434"
                 :stream t
                 :models '("mistral:latest")))

Gemini

Register a backend with

;; :key can be a function that returns the API key.
(gptel-make-gemini "Gemini" :key "YOUR_GEMINI_API_KEY" :stream t)

These are the required parameters, refer to the documentation of gptel-make-gemini for more.

You can pick this backend from the menu when using gptel (see Usage)

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq
 gptel-model "gemini-pro"
 gptel-backend (gptel-make-gemini "Gemini"
                 :key "YOUR_GEMINI_API_KEY"
                 :stream t))

Llama.cpp or Llamafile

(If using a llamafile, run a server llamafile instead of a “command-line llamafile”, and a model that supports text generation.)

Register a backend with

;; Llama.cpp offers an OpenAI compatible API
(gptel-make-openai "llama-cpp"          ;Any name
  :stream t                             ;Stream responses
  :protocol "http"
  :host "localhost:8000"                ;Llama.cpp server location
  :models '("test"))                    ;Any names, doesn't matter for Llama

These are the required parameters, refer to the documentation of gptel-make-openai for more.

You can pick this backend from the menu when using gptel (see Usage)

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq
 gptel-model   "test"
 gptel-backend (gptel-make-openai "llama-cpp"
                 :stream t
                 :protocol "http"
                 :host "localhost:8000"
                 :models '("test")))

Kagi (FastGPT & Summarizer)

Kagi’s FastGPT model and the Universal Summarizer are both supported. A couple of notes:

  1. Universal Summarizer: If there is a URL at point, the summarizer will summarize the contents of the URL. Otherwise the context sent to the model is the same as always: the buffer text upto point, or the contents of the region if the region is active.
  2. Kagi models do not support multi-turn conversations, interactions are “one-shot”. They also do not support streaming responses.

Register a backend with

(gptel-make-kagi "Kagi"                    ;any name
  :key "YOUR_KAGI_API_KEY")                ;can be a function that returns the key

These are the required parameters, refer to the documentation of gptel-make-kagi for more.

You can pick this backend and the model (fastgpt/summarizer) from the transient menu when using gptel.

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq
 gptel-model "fastgpt"
 gptel-backend (gptel-make-kagi "Kagi"
                 :key "YOUR_KAGI_API_KEY"))

The alternatives to fastgpt include summarize:cecil, summarize:agnes, summarize:daphne and summarize:muriel. The difference between the summarizer engines is documented here.

together.ai

Register a backend with

;; Together.ai offers an OpenAI compatible API
(gptel-make-openai "TogetherAI"         ;Any name you want
  :host "api.together.xyz"
  :key "your-api-key"                   ;can be a function that returns the key
  :stream t
  :models '(;; has many more, check together.ai
            "mistralai/Mixtral-8x7B-Instruct-v0.1"
            "codellama/CodeLlama-13b-Instruct-hf"
            "codellama/CodeLlama-34b-Instruct-hf"))

You can pick this backend from the menu when using gptel (see Usage)

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq
 gptel-model   "mistralai/Mixtral-8x7B-Instruct-v0.1"
 gptel-backend
 (gptel-make-openai "TogetherAI"         
   :host "api.together.xyz"
   :key "your-api-key"                   
   :stream t
   :models '(;; has many more, check together.ai
             "mistralai/Mixtral-8x7B-Instruct-v0.1"
             "codellama/CodeLlama-13b-Instruct-hf"
             "codellama/CodeLlama-34b-Instruct-hf")))

Anyscale

Register a backend with

;; Anyscale offers an OpenAI compatible API
(gptel-make-openai "Anyscale"           ;Any name you want
  :host "api.endpoints.anyscale.com"
  :key "your-api-key"                   ;can be a function that returns the key
  :models '(;; has many more, check anyscale
            "mistralai/Mixtral-8x7B-Instruct-v0.1"))

You can pick this backend from the menu when using gptel (see Usage)

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq
 gptel-model   "mistralai/Mixtral-8x7B-Instruct-v0.1"
 gptel-backend
 (gptel-make-openai "Anyscale"
                 :host "api.endpoints.anyscale.com"
                 :key "your-api-key"
                 :models '(;; has many more, check anyscale
                           "mistralai/Mixtral-8x7B-Instruct-v0.1")))

Perplexity

Register a backend with

;; Perplexity offers an OpenAI compatible API
(gptel-make-openai "Perplexity"         ;Any name you want
  :host "api.perplexity.ai"
  :key "your-api-key"                   ;can be a function that returns the key
  :endpoint "/chat/completions"
  :stream t
  :models '(;; has many more, check perplexity.ai
            "pplx-7b-chat"
            "pplx-70b-chat"
            "pplx-7b-online"
            "pplx-70b-online"))

You can pick this backend from the menu when using gptel (see Usage)

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq
 gptel-model   "pplx-7b-chat"
 gptel-backend
 (gptel-make-openai "Perplexity"
   :host "api.perplexity.ai"
   :key "your-api-key"
   :endpoint "/chat/completions"
   :stream t
   :models '(;; has many more, check perplexity.ai
             "pplx-7b-chat"
             "pplx-70b-chat"
             "pplx-7b-online"
             "pplx-70b-online")))

Anthropic (Claude)

Register a backend with

(gptel-make-anthropic "Claude"          ;Any name you want
  :stream t                             ;Streaming responses
  :key "your-api-key")

The :key can be a function that returns the key (more secure).

You can pick this backend from the menu when using gptel (see Usage).

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq
 gptel-model "claude-3-sonnet-20240229" ;  "claude-3-opus-20240229" also available
 gptel-backend (gptel-make-anthropic "Claude"
                 :stream t :key "your-api-key"))

Groq

Register a backend with

;; Groq offers an OpenAI compatible API
(gptel-make-openai "Groq"               ;Any name you want
  :host "api.groq.com"
  :endpoint "/openai/v1/chat/completions"
  :stream t
  :key "your-api-key"                   ;can be a function that returns the key
  :models '("mixtral-8x7b-32768"
            "gemma-7b-it"
            "llama2-70b-4096"))

You can pick this backend from the menu when using gptel (see Usage). Note that Groq is fast enough that you could easily set :stream nil and still get near-instant responses.

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq gptel-model   "mixtral-8x7b-32768"
      gptel-backend
      (gptel-make-openai "Groq"
        :host "api.groq.com"
        :endpoint "/openai/v1/chat/completions"
        :stream t
        :key "your-api-key"
        :models '("mixtral-8x7b-32768"
                  "gemma-7b-it"
                  "llama2-70b-4096")))

OpenRouter

Register a backend with

;; OpenRouter offers an OpenAI compatible API
(gptel-make-openai "OpenRouter"               ;Any name you want
  :host "openrouter.ai"
  :endpoint "/api/v1/chat/completions"
  :stream t
  :key "your-api-key"                   ;can be a function that returns the key
  :models '("openai/gpt-3.5-turbo"
            "mistralai/mixtral-8x7b-instruct"
            "meta-llama/codellama-34b-instruct"
            "codellama/codellama-70b-instruct"
            "google/palm-2-codechat-bison-32k"
            "google/gemini-pro"))

You can pick this backend from the menu when using gptel (see Usage).

(Optional) Set as the default gptel backend

The above code makes the backend available to select. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend. Use this instead of the above.

;; OPTIONAL configuration
(setq gptel-model   "mixtral-8x7b-32768"
      gptel-backend
      (gptel-make-openai "OpenRouter"               ;Any name you want
        :host "openrouter.ai"
        :endpoint "/api/v1/chat/completions"
        :stream t
        :key "your-api-key"                   ;can be a function that returns the key
        :models '("openai/gpt-3.5-turbo"
                  "mistralai/mixtral-8x7b-instruct"
                  "meta-llama/codellama-34b-instruct"
                  "codellama/codellama-70b-instruct"
                  "google/palm-2-codechat-bison-32k"
                  "google/gemini-pro")))

Usage

(This is also a video demo showing various uses of gptel.)

CommandDescription
gptel-sendSend conversation up to (point), or selection if region is active. Works anywhere in Emacs.
gptelCreate a new dedicated chat buffer. Not required to use gptel.
C-u gptel-sendTransient menu for preferences, input/output redirection etc.
gptel-menu(Same)
Command (Org mode only)
gptel-org-set-topicLimit conversation context to an Org heading
gptel-org-set-propertiesWrite gptel configuration as Org properties (for self-contained chat logs)

In any buffer:

  1. Call M-x gptel-send to send the text up to the cursor. The response will be inserted below. Continue the conversation by typing below the response.
  2. If a region is selected, the conversation will be limited to its contents.
  3. Call M-x gptel-send with a prefix argument to
  • set chat parameters (GPT model, directives etc) for this buffer,
  • to read the prompt from elsewhere or redirect the response elsewhere,
  • or to replace the prompt with the response.

https://user-images.githubusercontent.com/8607532/230770018-9ce87644-6c17-44af-bd39-8c899303dce1.png

With a region selected, you can also rewrite prose or refactor code from here:

Code:

https://user-images.githubusercontent.com/8607532/230770162-1a5a496c-ee57-4a67-9c95-d45f238544ae.png

Prose:

https://user-images.githubusercontent.com/8607532/230770352-ee6f45a3-a083-4cf0-b13c-619f7710e9ba.png

In a dedicated chat buffer:

  1. Run M-x gptel to start or switch to the chat buffer. It will ask you for the key if you skipped the previous step. Run it with a prefix-arg (C-u M-x gptel) to start a new session.
  2. In the gptel buffer, send your prompt with M-x gptel-send, bound to C-c RET.
  3. Set chat parameters (LLM provider, model, directives etc) for the session by calling gptel-send with a prefix argument (C-u C-c RET):

https://user-images.githubusercontent.com/8607532/224946059-9b918810-ab8b-46a6-b917-549d50c908f2.png

That’s it. You can go back and edit previous prompts and responses if you want.

The default mode is markdown-mode if available, else text-mode. You can set gptel-default-mode to org-mode if desired.

Save and restore your chat sessions

Saving the file will save the state of the conversation as well. To resume the chat, open the file and turn on gptel-mode before editing the buffer.

Extra Org mode conveniences

gptel offers a few extra conveniences in Org mode.

  • You can limit the conversation context to an Org heading with the command gptel-org-set-topic.
  • You can have branching conversations in Org mode, where each hierarchical outline path through the document is a separate conversation branch. This is also useful for limiting the context size of each query. See the variable gptel-org-branching-context.
  • You can declare the gptel model, backend, temperature, system message and other parameters as Org properties with the command gptel-org-set-properties. gptel queries under the corresponding heading will always use these settings, allowing you to create mostly reproducible LLM chat notebooks, and to have simultaneous chats with different models, model settings and directives under different Org headings.

FAQ

I want the window to scroll automatically as the response is inserted

To be minimally annoying, gptel does not move the cursor by default. Add the following to your configuration to enable auto-scrolling.

(add-hook 'gptel-post-stream-hook 'gptel-auto-scroll)

I want the cursor to move to the next prompt after the response is inserted

To be minimally annoying, gptel does not move the cursor by default. Add the following to your configuration to move the cursor:

(add-hook 'gptel-post-response-functions 'gptel-end-of-response)

You can also call gptel-end-of-response as a command at any time.

I want to change the formatting of the prompt and LLM response

For dedicated chat buffers: customize gptel-prompt-prefix-alist and gptel-response-prefix-alist. You can set a different pair for each major-mode.

Anywhere in Emacs: Use gptel-pre-response-hook and gptel-post-response-functions, which see.

I want the transient menu options to be saved so I only need to set them once

Any model options you set are saved for the current buffer. But the redirection options in the menu are set for the next query only:

https://github.com/karthink/gptel/assets/8607532/2ecc6be9-aa52-4287-a739-ba06e1369ec2

You can make them persistent across this Emacs session by pressing C-x C-s:

https://github.com/karthink/gptel/assets/8607532/b8bcb6ad-c974-41e1-9336-fdba0098a2fe

(You can also cycle through presets you’ve saved with C-x p and C-x n.)

Now these will be enabled whenever you send a query from the transient menu. If you want to use these saved options without invoking the transient menu, you can use a keyboard macro:

;; Replace with your key to invoke the transient menu:
(keymap-global-set "<f6>" "C-u C-c <return> <return>")

Or see this wiki entry.

I want to use gptel in a way that’s not supported by gptel-send or the options menu

gptel’s default usage pattern is simple, and will stay this way: Read input in any buffer and insert the response below it. Some custom behavior is possible with the transient menu (C-u M-x gptel-send).

For more programmable usage, gptel provides a general gptel-request function that accepts a custom prompt and a callback to act on the response. You can use this to build custom workflows not supported by gptel-send. See the documentation of gptel-request, and the wiki for examples.

(Doom Emacs) Sending a query from the gptel menu fails because of a key conflict with Org mode

Doom binds RET in Org mode to +org/dwim-at-point, which appears to conflict with gptel’s transient menu bindings for some reason.

Two solutions:

  • Press C-m instead of the return key.
  • Change the send key from return to a key of your choice:
    (transient-suffix-put 'gptel-menu (kbd "RET") :key "<f8>")
        

(ChatGPT) I get the error “(HTTP/2 429) You exceeded your current quota”

(HTTP/2 429) You exceeded your current quota, please check your plan and billing details.

Using the ChatGPT (or any OpenAI) API requires adding credit to your account.

Why another LLM client?

Other Emacs clients for LLMs prescribe the format of the interaction (a comint shell, org-babel blocks, etc). I wanted:

  1. Something that is as free-form as possible: query the model using any text in any buffer, and redirect the response as required. Using a dedicated gptel buffer just adds some visual flair to the interaction.
  2. Integration with org-mode, not using a walled-off org-babel block, but as regular text. This way the model can generate code blocks that I can run.

Additional Configuration

Connection options
gptel-use-curlUse Curl (default), fallback to Emacs’ built-in url.
gptel-proxyProxy server for requests, passed to curl via --proxy.
gptel-api-keyVariable/function that returns the API key for the active backend.
LLM options(Note: not supported uniformly across LLMs)
gptel-backendDefault LLM Backend.
gptel-modelDefault model to use, depends on the backend.
gptel-streamEnable streaming responses, if the backend supports it.
gptel-directivesAlist of system directives, can switch on the fly.
gptel-max-tokensMaximum token count (in query + response).
gptel-temperatureRandomness in response text, 0 to 2.
Chat UI options
gptel-default-modeMajor mode for dedicated chat buffers.
gptel-prompt-prefix-alistText inserted before queries.
gptel-response-prefix-alistText inserted before responses.
gptel-use-header-lineDisplay status messages in header-line (default) or minibuffer
gptel-display-buffer-actionPlacement of the gptel chat buffer.

Alternatives

Other Emacs clients for LLMs include

  • llm: llm provides a uniform API across language model providers for building LLM clients in Emacs, and is intended as a library for use by package authors. For similar scripting purposes, gptel provides the command gptel-request, which see.
  • Ellama: A full-fledged LLM client built on llm, that supports many LLM providers (Ollama, Open AI, Vertex, GPT4All and more). Its usage differs from gptel in that it provides separate commands for dozens of common tasks, like general chat, summarizing code/text, refactoring code, improving grammar, translation and so on.
  • chatgpt-shell: comint-shell based interaction with ChatGPT. Also supports DALL-E, executable code blocks in the responses, and more.
  • org-ai: Interaction through special #+begin_ai ... #+end_ai Org-mode blocks. Also supports DALL-E, querying ChatGPT with the contents of project files, and more.

There are several more: chatgpt-arcana, leafy-mode, chat.el

Extensions using gptel

These are packages that use gptel to provide additional functionality

  • gptel-extensions: Extra utility functions for gptel.
  • ai-blog.el: Streamline generation of blog posts in Hugo.
  • magit-gptcommit: Generate Commit Messages within magit-status Buffer using gptel.
  • consult-web: Provides gptel as a source when querying multiple local and online sources.

Breaking Changes

  • gptel-post-response-hook has been renamed to gptel-post-response-functions, and functions in this hook are now called with two arguments: the start and end buffer positions of the response. This should make it easy to act on the response text without having to locate it first.
  • Possible breakage, see #120: If streaming responses stop working for you after upgrading to v0.5, try reinstalling gptel and deleting its native comp eln cache in native-comp-eln-load-path.
  • The user option gptel-host is deprecated. If the defaults don’t work for you, use gptel-make-openai (which see) to customize server settings.
  • gptel-api-key-from-auth-source now searches for the API key using the host address for the active LLM backend, i.e. “api.openai.com” when using ChatGPT. You may need to update your ~/.authinfo.

Acknowledgments