gptel is a simple Large Language Model chat client for Emacs, with support for multiple models and backends. It works in the spirit of Emacs, available at any time and uniformly in any buffer.
General usage: (YouTube Demo)
intro-demo.mp4 intro-demo-2.mp4In-place usage
gptel-rewrite-demo-1.mp4Tool use
gptel-tool-use-filesystem-confirm-demo-2.mp4 gptel-tool-use-feed-search-demo.mp4See also this youtube demo (2 minutes) by Armin Darvish.
gptel uses Curl if available, but falls back to the built-in url-retrieve to work without external dependencies.
Note: gptel requires Transient 0.7.4 or higher. Transient is a built-in package and Emacs does not update it by default. Ensure that package-install-upgrade-built-in
is true, or update Transient manually.
M-x package-install
⏎ gptel
in Emacs.M-x package-install
⏎ gptel
.markdown-mode
.(straight-use-package 'gptel)
Note: gptel requires Transient 0.7.4 or higher. Transient is a built-in package and Emacs does not update it by default. Ensure that package-install-upgrade-built-in
is true, or update Transient manually.
Clone or download this repository and run M-x package-install-file⏎
on the repository directory.
In packages.el
(package! gptel :recipe (:nonrecursive t))
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.
In your .spacemacs
file, add llm-client
to dotspacemacs-configuration-layers
.
(llm-client :variables llm-client-enable-gptel t)
Procure an OpenAI API key.
Optional: Set gptel-api-key
to the key. Alternatively, you may choose a more secure method such as:
gptel-api-key-from-auth-source
function which reads keys from ~/.authinfo
. (See authinfo details)ChatGPT is configured out of the box. If you want to use other LLM backends (like Ollama, Claude/Anthropic or Gemini) you need to register and configure them first.
As an example, registering a backend typically looks like the following:
(gptel-make-anthropic "Claude" :stream t :key gptel-api-key)
Once this backend is registered, you’ll see model names prefixed by “Claude:” appear in gptel’s menu.
See below for details on your preferred LLM provider, including local LLMs.
(Optional) Securing API keys withauthinfo
You can use Emacs’ built-in support for authinfo
to store API keys required by gptel. Add your API keys to ~/.authinfo
, and leave gptel-api-key
set to its default. By default, the API endpoint DNS name (e.g. “api.openai.com”) is used as HOST and “apikey” as USER.
machine api.openai.com login apikey password sk-secret-openai-api-key-goes-here machine api.anthropic.com login apikey password sk-secret-anthropic-api-key-goes-here
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 backendThe 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)))
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 backendThe 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)))
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 backendThe 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)))
Open WebUI is an open source, self-hosted system which provides a multi-user web chat interface and an API endpoint for accessing LLMs, especially LLMs running locally on inference servers like Ollama.
Because it presents an OpenAI-compatible endpoint, you use gptel-make-openai
to register it as a backend.
For instance, you can use this form to register a backend for a local instance of Open Web UI served via http on port 3000:
(gptel-make-openai "OpenWebUI" :host "localhost:3000" :protocol "http" :key "KEY_FOR_ACCESSING_OPENWEBUI" :endpoint "/api/chat/completions" :stream t :models '("gemma3n:latest"))
Or if you are running Open Web UI on another host on your local network (box.local
), serving via https with self-signed certificates, this will work:
(gptel-make-openai "OpenWebUI" :host "box.local" :curl-args '("--insecure") ; needed for self-signed certs :key "KEY_FOR_ACCESSING_OPENWEBUI" :endpoint "/api/chat/completions" :stream t :models '("gemma3n:latest"))
To find your API key in Open WebUI, click the user name in the bottom left, Settings, Account, and then Show by API Keys section.
Refer to the documentation of gptel-make-openai
for more configuration options.
You can pick this backend from the menu when using gptel (see Usage)
(Optional) Set as the default gptel backendThe 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 "gemma3n:latest" gptel-backend (gptel-make-openai "OpenWebUI" :host "localhost:3000" :protocol "http" :key "KEY_FOR_ACCESSING_OPENWEBUI" :endpoint "/api/chat/completions" :stream t :models '("gemma3n:latest")))
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 backendThe 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-2.5-pro-exp-03-25 gptel-backend (gptel-make-gemini "Gemini" :key "YOUR_GEMINI_API_KEY" :stream t))
(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 backendThe 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:
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 backendThe 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.
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 backendThe 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)))
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 backendThe 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)))
Register a backend with
(gptel-make-perplexity "Perplexity" ;Any name you want :key "your-api-key" ;can be a function that returns the key :stream t) ;If you want responses to be streamed
You can pick this backend from the menu when using gptel (see Usage)
(Optional) Set as the default gptel backendThe 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 'sonar gptel-backend (gptel-make-perplexity "Perplexity" :key "your-api-key" :stream t))
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 backendThe 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"))(Optional) Interim support for Claude 3.7 Sonnet
To use Claude 3.7 Sonnet model in its “thinking” mode, you can define a second Claude backend and select it via the UI or elisp:
(gptel-make-anthropic "Claude-thinking" ;Any name you want :key "your-API-key" :stream t :models '(claude-sonnet-4-20250514 claude-3-7-sonnet-20250219) :request-params '(:thinking (:type "enabled" :budget_tokens 2048) :max_tokens 4096))
You can set the reasoning budget tokens and max tokens for this usage via the :budget_tokens
and :max_tokens
keys here, respectively.
You can control whether/how the reasoning output is shown via gptel’s menu or gptel-include-reasoning
, see handling reasoning content.
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 '(llama-3.1-70b-versatile llama-3.1-8b-instant llama3-70b-8192 llama3-8b-8192 mixtral-8x7b-32768 gemma-7b-it))
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.
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 '(llama-3.1-70b-versatile llama-3.1-8b-instant llama3-70b-8192 llama3-8b-8192 mixtral-8x7b-32768 gemma-7b-it)))
Register a backend with
;; Mistral offers an OpenAI compatible API (gptel-make-openai "MistralLeChat" ;Any name you want :host "api.mistral.ai" :endpoint "/v1/chat/completions" :protocol "https" :key "your-api-key" ;can be a function that returns the key :models '("mistral-small"))
You can pick this backend from the menu when using gptel (see Usage).
(Optional) Set as the default gptel backendThe 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-small gptel-backend (gptel-make-openai "MistralLeChat" ;Any name you want :host "api.mistral.ai" :endpoint "/v1/chat/completions" :protocol "https" :key "your-api-key" ;can be a function that returns the key :models '("mistral-small")))
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 backendThe 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)))
Register a backend with
(gptel-make-privategpt "privateGPT" ;Any name you want :protocol "http" :host "localhost:8001" :stream t :context t ;Use context provided by embeddings :sources t ;Return information about source documents :models '(private-gpt))
You can pick this backend from the menu when using gptel (see Usage).
(Optional) Set as the default gptel backendThe 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 'private-gpt gptel-backend (gptel-make-privategpt "privateGPT" ;Any name you want :protocol "http" :host "localhost:8001" :stream t :context t ;Use context provided by embeddings :sources t ;Return information about source documents :models '(private-gpt)))
Register a backend with
(gptel-make-deepseek "DeepSeek" ;Any name you want :stream t ;for streaming responses :key "your-api-key") ;can be a function that returns the key
You can pick this backend from the menu when using gptel (see Usage).
(Optional) Set as the default gptel backendThe 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 'deepseek-reasoner gptel-backend (gptel-make-deepseek "DeepSeek" :stream t :key "your-api-key"))
Sambanova offers various LLMs through their Samba Nova Cloud offering, with Deepseek-R1 being one of them. The token speed for Deepseek R1 via Sambanova is about 6 times faster than when accessed through deepseek.com
Register a backend with
(gptel-make-openai "Sambanova" ;Any name you want :host "api.sambanova.ai" :endpoint "/v1/chat/completions" :stream t ;for streaming responses :key "your-api-key" ;can be a function that returns the key :models '(DeepSeek-R1))
You can pick this backend from the menu when using gptel (see Usage).
(Optional) Set as the default gptel backendThe code aboves makes the backend available for selection. If you want it to be the default backend for gptel, you can set this as the value of gptel-backend
. Add these two lines to your configuration:
;; OPTIONAL configuration (setq gptel-model 'DeepSeek-R1) (setq gptel-backend (gptel-get-backend "Sambanova"))
Register a backend with
;; Cerebras offers an instant OpenAI compatible API (gptel-make-openai "Cerebras" :host "api.cerebras.ai" :endpoint "/v1/chat/completions" :stream t ;optionally nil as Cerebras is instant AI :key "your-api-key" ;can be a function that returns the key :models '(llama3.1-70b llama3.1-8b))
You can pick this backend from the menu when using gptel (see Usage).
(Optional) Set as the default gptel backendThe 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 'llama3.1-8b gptel-backend (gptel-make-openai "Cerebras" :host "api.cerebras.ai" :endpoint "/v1/chat/completions" :stream nil :key "your-api-key" :models '(llama3.1-70b llama3.1-8b)))
NOTE: GitHub Models is not GitHub Copilot! If you want to use GitHub Copilot chat via gptel, look at the instructions for GitHub CopilotChat below instead.
Register a backend with
;; Github Models offers an OpenAI compatible API (gptel-make-openai "Github Models" ;Any name you want :host "models.inference.ai.azure.com" :endpoint "/chat/completions?api-version=2024-05-01-preview" :stream t :key "your-github-token" :models '(gpt-4o))
You will need to create a github token.
For all the available models, check the marketplace.
You can pick this backend from the menu when using (see Usage).
(Optional) Set as the default gptel backendThe 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-4o gptel-backend (gptel-make-openai "Github Models" ;Any name you want :host "models.inference.ai.azure.com" :endpoint "/chat/completions?api-version=2024-05-01-preview" :stream t :key "your-github-token" :models '(gpt-4o))
Register a backend with
;; Novita AI offers an OpenAI compatible API (gptel-make-openai "NovitaAI" ;Any name you want :host "api.novita.ai" :endpoint "/v3/openai" :key "your-api-key" ;can be a function that returns the key :stream t :models '(;; has many more, check https://novita.ai/llm-api gryphe/mythomax-l2-13b meta-llama/llama-3-70b-instruct meta-llama/llama-3.1-70b-instruct))
You can pick this backend from the menu when using gptel (see Usage)
(Optional) Set as the default gptel backendThe 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 'gryphe/mythomax-l2-13b gptel-backend (gptel-make-openai "NovitaAI" :host "api.novita.ai" :endpoint "/v3/openai" :key "your-api-key" :stream t :models '(;; has many more, check https://novita.ai/llm-api mistralai/Mixtral-8x7B-Instruct-v0.1 meta-llama/llama-3-70b-instruct meta-llama/llama-3.1-70b-instruct)))
Register a backend with
(gptel-make-xai "xAI" ; Any name you want :stream t :key "your-api-key") ; can be a function that returns the key
You can pick this backend from the menu when using gptel (see Usage)
(Optional) Set as the default gptel backendThe 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.
(setq gptel-model 'grok-3-latest gptel-backend (gptel-make-xai "xAI" ; Any name you want :key "your-api-key" ; can be a function that returns the key :stream t))
AI/ML API provides 300+ AI models including Deepseek, Gemini, ChatGPT. The models run at enterprise-grade rate limits and uptimes.
Register a backend with
;; AI/ML API offers an OpenAI compatible API (gptel-make-openai "AI/ML API" ;Any name you want :host "api.aimlapi.com" :endpoint "/v1/chat/completions" :stream t :key "your-api-key" ;can be a function that returns the key :models '(deepseek-chat gemini-pro gpt-4o))
You can pick this backend from the menu when using gptel (see Usage).
(Optional) Set as the default gptel backendThe 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-4o gptel-backend (gptel-make-openai "AI/ML API" :host "api.aimlapi.com" :endpoint "/v1/chat/completions" :stream t :key "your-api-key" :models '(deepseek-chat gemini-pro gpt-4o)))
Register a backend with
(gptel-make-gh-copilot "Copilot")
You will be informed to login into GitHub
as required. You can pick this backend from the menu when using gptel (see Usage).
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.7-sonnet gptel-backend (gptel-make-gh-copilot "Copilot"))
Register a backend with
(gptel-make-bedrock "AWS" ;; optionally enable streaming :stream t :region "ap-northeast-1" ;; subset of gptel--bedrock-models :models '(claude-sonnet-4-20250514) ;; Model region for cross-region inference profiles. Required for models such ;; as Claude without on-demand throughput support. One of 'apac, 'eu or 'us. ;; https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-use.html :model-region 'apac)
The Bedrock backend gets your AWS credentials from the environment variables. It expects to find either AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
, AWS_SESSION_TOKEN
(optional), or if present, can use AWS_PROFILE
to get these directly from the aws
cli.
NOTE: The Bedrock backend needs curl >= 8.5 in order for the sigv4 signing to work properly, curl/curl#11794
An error will be signalled if gptel-curl
is NIL
.
You can pick this backend from the menu when using gptel (see Usage).
(Optional) Set as the default gptel backendThe 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-sonnet-4-20250514 gptel-backend (gptel-make-bedrock "AWS" ;; optionally enable streaming :stream t :region "ap-northeast-1" ;; subset of gptel--bedrock-models :models '(claude-sonnet-4-20250514) ;; Model region for cross-region inference profiles. Required for models such ;; as Claude without on-demand throughput support. One of 'apac, 'eu or 'us. ;; https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-use.html :model-region 'apac))
Register a backend with
(gptel-make-openai "Moonshot" :host "api.moonshot.cn" ;; or "api.moonshot.ai" for the global site :key "your-api-key" :stream t ;; optionally enable streaming :models '(kimi-latest kimi-k2-0711-preview))
See Moonshot.ai document for a complete list of models.
(Optional) Use the builtin search toolMoonshot supports a builtin search tool that does not requires the user to provide the tool implementation. To use that, you first need to define the tool and add to gptel-tools
(while it does not requires the client to provide the search implementation, it does expects the client to reply a tool call message with its given argument, to be consistent with other tool calls):
(setq gptel-tools (list (gptel-make-tool :name "$web_search" :function (lambda (&optional search_result) (json-serialize `(:search_result ,search_result))) :description "Moonshot builtin web search. Only usable by moonshot model (kimi), ignore this if you are not." :args '((:name "search_result" :type object :optional t)) :category "web")))
Then you also need to add the tool declaration via :request-params
because it needs a special builtin_function
type:
(gptel-make-openai "Moonshot" :host "api.moonshot.cn" ;; or "api.moonshot.ai" for the global site :key "your-api-key" :stream t ;; optionally enable streaming :models '(kimi-latest kimi-k2-0711-preview) :request-params '(:tools [(:type "builtin_function" :function (:name "$web_search"))]))
Now the chat should be able to automatically use search. Try “what’s new today” and you should expect the up-to-date news in response.
gptel provides a few powerful, general purpose and flexible commands. You can dynamically tweak their behavior to the needs of your task with directives, redirection options and more. There is a video demo showing various uses of gptel – but gptel-send
might be all you need.
gptel-send
Send all text up to (point)
, or the selection if region is active. Works anywhere in Emacs. gptel
Create a new dedicated chat buffer. Not required to use gptel. gptel-rewrite
Rewrite, refactor or change the selected region. Can diff/ediff changes before merging/applying. To tweak behavior C-u
gptel-send
Transient menu for preferences, input/output redirection etc. gptel-menu
(Same) To add context gptel-add
Add/remove a region or buffer to gptel’s context. In Dired, add/remove marked files. gptel-add-file
Add a file (text or supported media type) to gptel’s context. Also available from the transient menu. Org mode bonuses gptel-org-set-topic
Limit conversation context to an Org heading. (For branching conversations see below.) gptel-org-set-properties
Write gptel configuration as Org properties, for per-heading chat configuration. GitHub Copilot gptel-gh-login
Authenticate with GitHub Copilot. (Automatically handled, but can be forced if required.)
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.M-x gptel-send
with a prefix argument (C-u
)
You can also define a “preset” bundle of options that are applied together, see Option presets below.
In a dedicated chat buffer:Note: gptel works anywhere in Emacs. The dedicated chat buffer only adds some conveniences.
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.M-x gptel-send
, bound to C-c RET
.gptel-send
with a prefix argument (C-u C-c RET
):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.
You can also define a “preset” bundle of options that are applied together, see Option presets below.
Including media (images, documents or plain-text files) with requestsgptel supports sending media in Markdown and Org chat buffers, but this feature is disabled by default.
gptel-track-media
.There are two ways to include media or plain-text files with requests:
gptel-add-file
, described further below.To include plain-text files, images or other supported document types with requests in chat buffers, you can include links to them in the chat buffer. Such a link must be “standalone”, i.e. on a line by itself surrounded by whitespace.
In Org mode, for example, the following are all valid ways of including an image with the request:
In this yaml file, I have some key-remapping configuration: [[file:/path/to/remap.yaml]] Could you explain what it does, and which program might be using it?
Describe this picture [[file:/path/to/screenshot.png]] Focus specifically on the text content.
Describe this picture [[file:/path/to/screenshot.png][some picture]] Focus specifically on the text content.
Describe this picture <file:/path/to/screenshot.png> Focus specifically on the text content.
The following links are not valid, and the text of the link will be sent instead of the file contents:
Describe this [[file:/path/to/screenshot.png][picture]]. Focus specifically on the text content.
Describe this picture: [[file:/path/to/screenshot.png]] Focus specifically on the text content.
Describe the picture file:/path/to/screenshot.png
Similar criteria apply to Markdown chat buffers.
Save and restore your chat sessionsSaving 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.
Most gptel options can be set from gptel’s transient menu, available by calling gptel-send
with a prefix-argument, or via gptel-menu
. To change their default values in your configuration, see Additional Configuration. Chat buffer-specific options are also available via the header-line in chat buffers.
Selecting a model and backend can be done interactively via the -m
command of gptel-menu
. Available registered models are prefixed by the name of their backend with a string like ChatGPT:gpt-4o-mini
, where ChatGPT
is the backend name you used to register it and gpt-4o-mini
is the name of the model.
By default, gptel will query the LLM with the active region or the buffer contents up to the cursor. Often it can be helpful to provide the LLM with additional context from outside the current buffer. For example, when you’re in a chat buffer but want to ask questions about a (possibly changing) code buffer and auxiliary project files.
You can include additional text regions, buffers or files with gptel’s queries in two ways. The first is via links in chat buffers, as described above (see “Including media with requests”).
The second is globally via dedicated context commands: you can add a selected region, buffer or file to gptel’s context from the menu, or call gptel-add
. To add a file use gptel-add
in Dired, or use the dedicated gptel-add-file
command. Directories will have their files added recursively after prompting for confirmation.
This additional context is “live” and not a snapshot. Once added, the regions, buffers or files are scanned and included at the time of each query. When using multi-modal models, added files can be of any supported type – typically images.
You can examine the active context from the menu:
And then browse through or remove context from the context buffer:
Handle “reasoning” contentSome LLMs include in their response a “thinking” or “reasoning” block. This text improves the quality of the LLM’s final output, but may not be interesting to you by itself. You can decide how you would like this “reasoning” content to be handled by gptel by setting the user option gptel-include-reasoning
. You can include it in the LLM response (the default), omit it entirely, include it in the buffer but ignore it on subsequent conversation turns, or redirect it to another buffer. As with most options, you can specify this behvaior from gptel’s transient menu globally, buffer-locally or for the next request only.
When included with the response, reasoning content will be delimited by Org blocks or markdown backticks.
gptel can provide the LLM with client-side elisp “tools”, or function specifications, along with the request. If the LLM decides to run the tool, it supplies the tool call arguments, which gptel uses to run the tool in your Emacs session. The result is optionally returned to the LLM to complete the task.
This exchange can be used to equip the LLM with capabilities or knowledge beyond what is available out of the box – for instance, you can get the LLM to control your Emacs frame, create or modify files and directories, or look up information relevant to your request via web search or in a local database. Here is a very simple example:
screencast_20241222T075329.mp4To use tools in gptel, you need
Defining a gptel tool requires an elisp function and associated metadata. Here are two simple tool definitions:
To read the contents of an Emacs buffer:
(gptel-make-tool :name "read_buffer" ; javascript-style snake_case name :function (lambda (buffer) ; the function that will run (unless (buffer-live-p (get-buffer buffer)) (error "error: buffer %s is not live." buffer)) (with-current-buffer buffer (buffer-substring-no-properties (point-min) (point-max)))) :description "return the contents of an emacs buffer" :args (list '(:name "buffer" :type string ; :type value must be a symbol :description "the name of the buffer whose contents are to be retrieved")) :category "emacs") ; An arbitrary label for grouping
Besides the function itself, which can be named or anonymous (as above), the tool specification requires a :name
, :description
and a list of argument specifications in :args
. Each argument specification is a plist with atleast the keys :name
, :type
and :description
.
To create a text file:
(gptel-make-tool :name "create_file" ; javascript-style snake_case name :function (lambda (path filename content) ; the function that runs (let ((full-path (expand-file-name filename path))) (with-temp-buffer (insert content) (write-file full-path)) (format "Created file %s in %s" filename path))) :description "Create a new file with the specified content" :args (list '(:name "path" ; a list of argument specifications :type string :description "The directory where to create the file") '(:name "filename" :type string :description "The name of the file to create") '(:name "content" :type string :description "The content to write to the file")) :category "filesystem") ; An arbitrary label for grouping
With some prompting, you can get an LLM to write these tools for you.
Tools can also be asynchronous, use optional arguments and arguments with more structure (enums, arrays, objects etc). See gptel-make-tool
for details.
Once defined, tools can be selected (globally, buffer-locally or for the next request only) from gptel’s transient menu:
From here you can also require confirmation for all tool calls, and decide if tool call results should be included in the LLM response. See Additional Configuration for doing these things via elisp.
Model Context Protocol (MCP) integrationThe Model Context Protocol (MCP) is a protocol for providing resources and tools to LLMs, and many MCP servers exist that provide LLM tools for file access, database connections, API integrations etc. The mcp.el package for Emacs can act as an MCP client and manage these tool calls for gptel.
To use MCP servers with gptel, you thus need three pieces:
gptel includes gptel-integrations
, a small library to make this more convenient. This library is not automatically loaded by gptel, so if you would like to use it you have to require it:
(require 'gptel-integrations)
Once loaded, you can run the gptel-mcp-connect
and gptel-mcp-disconnect
commands to register and unregister MCP-provided tools in gptel. These will also show up in the tools menu in gptel, accessed via M-x gptel-menu
or M-x gptel-tools
:
MCP-provided tools can be used as normal with gptel. Here is a screencast of the process. (In this example the “github” MCP server is installed separately using npm.)
gptel-mcp-01.mp4Here’s an example of using these tools:
gptel-mcp-github.mp4 Rewrite, refactor or fill in a regionIn any buffer: with a region selected, you can modify text, rewrite prose or refactor code with gptel-rewrite
. Example with prose:
The result is previewed over the original text. By default, the buffer is not modified.
Pressing RET
or clicking in the rewritten region should give you a list of options: you can iterate on, diff, ediff, merge or accept the replacement. Example with code:
Acting on the LLM response:
If you would like one of these things to happen automatically, you can customize gptel-rewrite-default-action
.
These options are also available from gptel-rewrite
:
And you can call them directly when the cursor is in the rewritten region:
Extra Org mode conveniencesgptel offers a few extra conveniences in Org mode.
Limit conversation context to an Org headingYou can limit the conversation context to an Org heading with the command gptel-org-set-topic
.
(This sets an Org property (GPTEL_TOPIC
) under the heading. You can also add this property manually instead.)
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
.
If this variable is non-nil, you should probably edit gptel-prompt-prefix-alist
and gptel-response-prefix-alist
so that the prefix strings for org-mode are not Org headings, e.g.
(setf (alist-get 'org-mode gptel-prompt-prefix-alist) "@user\n") (setf (alist-get 'org-mode gptel-response-prefix-alist) "@assistant\n")
Otherwise, the default prompt prefix will make successive prompts sibling headings, and therefore on different conversation branches, which probably isn’t what you want.
Note: using this option requires Org 9.7 or higher to be available. The ai-org-chat package uses gptel to provide this branching conversation behavior for older versions of Org.
Save gptel parameters to Org headings (reproducible chats)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.
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.
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.
gptel uses text-properties to watermark LLM responses. Thus this text is interpreted as a response even if you copy it into another buffer. In regular buffers (buffers without gptel-mode
enabled), you can turn off this tracking by unsetting gptel-track-response
.
When restoring a chat state from a file on disk, gptel will apply these properties from saved metadata in the file when you turn on gptel-mode
.
gptel does not use any prefix or semantic/syntax element in the buffer (such as headings) to separate prompts and responses. The reason for this is that gptel aims to integrate as seamlessly as possible into your regular Emacs usage: LLM interaction is not the objective, it’s just another tool at your disposal. So requiring a bunch of “user” and “assistant” tags in the buffer is noisy and restrictive. If you want these demarcations, you can customize gptel-prompt-prefix-alist
and gptel-response-prefix-alist
. Note that these prefixes are for your readability only and purely cosmetic.
In every menu used to set options, gptel provides a “scope” option, bound to the =
key:
You can flip this switch before setting the option to buffer
or oneshot
. You only need to flip this switch once, it’s a persistent setting. buffer
sets the option buffer-locally, oneshot
will set it for the next gptel request only. The default scope is global.
Any model options you set are saved according to the scope (see previous question). But the redirection options in the menu are set for the next query only:
You can make them persistent across this Emacs session by pressing C-x C-s
:
(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.
Using the transient menu leaves behind extra windowsIf using gptel’s transient menus causes new/extra window splits to be created, check your value of transient-display-buffer-action
. See this discussion for more context.
If you are using Helm, see Transient#361.
In general, do not customize this Transient option unless you know what you’re doing!
Can I change the transient menu key bindings?Yes, see transient-suffix-put
. This changes the key to select a backend/model from “-m” to “M” in gptel’s menu:
(transient-suffix-put 'gptel-menu (kbd "-m") :key "M")(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:
C-m
instead of the return key.(transient-suffix-put 'gptel-menu (kbd "RET") :key "<f8>")
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.
(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.
Other Emacs clients for LLMs prescribe the format of the interaction (a comint shell, org-babel blocks, etc). I wanted:
gptel
buffer just adds some visual flair to the interaction.gptel-use-curl
Use Curl? (default), fallback to Emacs’ built-in url
. You can also specify the Curl path here. gptel-proxy
Proxy server for requests, passed to curl via --proxy
. gptel-curl-extra-args
Extra arguments passed to Curl. gptel-api-key
Variable/function that returns the API key for the active backend. LLM request options (Note: not supported uniformly across LLMs) gptel-backend
Default LLM Backend. gptel-model
Default model to use, depends on the backend. gptel-stream
Enable streaming responses, if the backend supports it. gptel-directives
Alist of system directives, can switch on the fly. gptel-max-tokens
Maximum token count (in query + response). gptel-temperature
Randomness in response text, 0 to 2. gptel-cache
Cache prompts, system message or tools (Anthropic only) gptel-use-context
How/whether to include additional context gptel-use-tools
Disable, allow or force LLM tool-use gptel-tools
List of tools to include with requests Chat UI options gptel-default-mode
Major mode for dedicated chat buffers. gptel-prompt-prefix-alist
Text inserted before queries. gptel-response-prefix-alist
Text inserted before responses. gptel-track-response
Distinguish between user messages and LLM responses? gptel-track-media
Send text, images or other media from links? gptel-confirm-tool-calls
Confirm all tool calls? gptel-include-tool-results
Include tool results in the LLM response? gptel-use-header-line
Display status messages in header-line (default) or minibuffer gptel-display-buffer-action
Placement of the gptel chat buffer. Org mode UI options gptel-org-branching-context
Make each outline path a separate conversation branch gptel-org-ignore-elements
Ignore parts of the buffer when sending a query Hooks for customization gptel-save-state-hook
Runs before saving the chat state to a file on disk gptel-prompt-transform-functions
Runs in a temp buffer to transform text before sending gptel-post-request-hook
Runs immediately after dispatching a gptel-request
. gptel-pre-response-hook
Runs before inserting the LLM response into the buffer gptel-post-response-functions
Runs after inserting the full LLM response into the buffer gptel-post-stream-hook
Runs after each streaming insertion gptel-context-wrap-function
To include additional context formatted your way gptel-rewrite-default-action
Automatically diff, ediff, merge or replace refactored text gptel-post-rewrite-functions
Runs after a gptel-rewrite
request succeeds
If you use several LLMs for different tasks with accompanying system prompts (instructions) and tool configurations, manually adjusting gptel
settings each time can become tedious. Presets are a bundle of gptel settings – such as the model, backend, system message, and enabled tools – that you can switch to at once.
Once defined, presets can be applied from gptel’s transient menu:
To define a preset, use the gptel-make-preset
function, which takes a name and keyword-value pairs of settings.
Presets can be used to set individual options. Here is an example of a preset to set the system message (and do nothing else):
(gptel-make-preset 'explain :system "Explain what this code does to a novice programmer.")
More generally, you can specify a bundle of options:
(gptel-make-preset 'gpt4coding ;preset name, a symbol :description "A preset optimized for coding tasks" ;for your reference :backend "Claude" ;gptel backend or backend name :model 'claude-3-7-sonnet-20250219.1 :system "You are an expert coding assistant. Your role is to provide high-quality code solutions, refactorings, and explanations." :tools '("read_buffer" "modify_buffer")) ;gptel tools or tool names
Besides a couple of special keys (:description
, :parents
to inherit other presets), there is no predefined list of keys. Instead, the key :foo
corresponds to setting gptel-foo
(preferred) or gptel--foo
. So the preset can include the value of any gptel option. For example, the following preset sets gptel-temperature
and gptel-use-context
:
(gptel-make-preset 'proofreader :description "Preset for proofreading tasks" :backend "ChatGPT" :model 'gpt-4.1-mini :tools '("read_buffer" "spell_check" "grammar_check") :temperature 0.7 ;sets gptel-temperature :use-context 'system) ;sets gptel-use-context
Switching to a preset applies the specified settings without affecting other settings. Depending on the scope option (=
in gptel’s transient menu), presets can be applied globally, buffer-locally or for the next request only.
You can apply a preset to a single query by including @preset-name
in the prompt, where preset-name
is the name of the preset. (The oneshot
scope option in gptel’s transient menus is another way to do this, see the FAQ.)
For example, if you have a preset named websearch
defined which includes tools for web access and search:
(gptel-make-preset 'websearch :description "Haiku with basic web search capability." :backend "Claude" :model 'claude-3-5-haiku-20241022 :tools '("search_web" "read_url" "get_youtube_meta"))
The following query is sent with this preset applied:
@websearch Are there any 13” e-ink monitors on the market? Create a table comparing them, sourcing specs and reviews from online sources. Also do the same for “transreflective-LCD” displays – I’m not sure what exactly they’re called but they’re comparable to e-ink.
This @preset-name
cookie only applies to the final user turn of the coversation that is sent. So the presence of the cookie in past messages/turns is not significant.
The @preset-name
cookie can be anywhere in the prompt. For example:
<long piece of text>
What do you make of the above description, @proofreader?
In chat buffers this prefix will be offered as a completion and fontified, making it easy to use and spot.
Other Emacs clients for LLMs include
gptel-request
, which see.#+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: chat.el, gpt.el, le-gpt, robby.
gptel is a general-purpose package for chat and ad-hoc LLM interaction. The following packages use gptel to provide additional or specialized functionality:
gptel-org-branching-context
), but requires a recent version of Org mode 9.7 or later to be installed.)url-retrieve
.RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4