diff --git a/README.org b/README.org
index 8f8c140f..dfae2170 100644
--- a/README.org
+++ b/README.org
@@ -4,20 +4,21 @@
GPTel is a simple Large Language Model chat client for Emacs, with support for multiple models and backends.
-| LLM Backend | Supports | Requires |
-|-----------------+----------+---------------------------|
-| ChatGPT | ✓ | [[https://platform.openai.com/account/api-keys][API key]] |
-| Azure | ✓ | Deployment and API key |
-| Ollama | ✓ | [[https://ollama.ai/][Ollama running locally]] |
-| GPT4All | ✓ | [[https://gpt4all.io/index.html][GPT4All running locally]] |
-| Gemini | ✓ | [[https://makersuite.google.com/app/apikey][API key]] |
-| Llama.cpp | ✓ | [[https://github.com/ggerganov/llama.cpp/tree/master/examples/server#quick-start][Llama.cpp running locally]] |
-| Llamafile | ✓ | [[https://github.com/Mozilla-Ocho/llamafile#quickstart][Local Llamafile server]] |
-| Kagi FastGPT | ✓ | [[https://kagi.com/settings?p=api][API key]] |
-| Kagi Summarizer | ✓ | [[https://kagi.com/settings?p=api][API key]] |
-| together.ai | ✓ | [[https://api.together.xyz/settings/api-keys][API key]] |
-| Anyscale | ✓ | [[https://docs.endpoints.anyscale.com/][API key]] |
-| Perplexity | ✓ | [[https://docs.perplexity.ai/docs/getting-started][API key]] |
+| LLM Backend | Supports | Requires |
+|--------------------+----------+---------------------------|
+| ChatGPT | ✓ | [[https://platform.openai.com/account/api-keys][API key]] |
+| Azure | ✓ | Deployment and API key |
+| Ollama | ✓ | [[https://ollama.ai/][Ollama running locally]] |
+| GPT4All | ✓ | [[https://gpt4all.io/index.html][GPT4All running locally]] |
+| Gemini | ✓ | [[https://makersuite.google.com/app/apikey][API key]] |
+| Llama.cpp | ✓ | [[https://github.com/ggerganov/llama.cpp/tree/master/examples/server#quick-start][Llama.cpp running locally]] |
+| Llamafile | ✓ | [[https://github.com/Mozilla-Ocho/llamafile#quickstart][Local Llamafile server]] |
+| Kagi FastGPT | ✓ | [[https://kagi.com/settings?p=api][API key]] |
+| Kagi Summarizer | ✓ | [[https://kagi.com/settings?p=api][API key]] |
+| together.ai | ✓ | [[https://api.together.xyz/settings/api-keys][API key]] |
+| Anyscale | ✓ | [[https://docs.endpoints.anyscale.com/][API key]] |
+| Perplexity | ✓ | [[https://docs.perplexity.ai/docs/getting-started][API key]] |
+| Anthropic (Claude) | ✓ | [[https://www.anthropic.com/api][API key]] |
*General usage*: ([[https://www.youtube.com/watch?v=bsRnh_brggM][YouTube Demo]])
@@ -57,6 +58,7 @@ GPTel uses Curl if available, but falls back to url-retrieve to work without ext
- [[#togetherai][together.ai]]
- [[#anyscale][Anyscale]]
- [[#perplexity][Perplexity]]
+ - [[#anthropic-claude][Anthropic (Claude)]]
- [[#usage][Usage]]
- [[#in-any-buffer][In any buffer:]]
- [[#in-a-dedicated-chat-buffer][In a dedicated chat buffer:]]
@@ -438,6 +440,31 @@ The above code makes the backend available to select. If you want it to be the
"pplx-70b-online")))
#+end_src
+#+html:
+#+html:
+**** Anthropic (Claude)
+#+html:
+Register a backend with
+#+begin_src emacs-lisp
+(gptel-make-anthropic "Claude" ;Any name you want
+ :stream t ;Streaming responses
+ :key "your-api-key")
+#+end_src
+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][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 default value of =gptel-backend=. Use this instead of the above.
+#+begin_src emacs-lisp
+;; OPTIONAL configuration
+(setq-default
+ gptel-model "claude-3-sonnet-20240229" ; "claude-3-opus-20240229" also available
+ gptel-backend (gptel-make-anthropic "Claude"
+ :stream t :key "your-api-key"))
+#+end_src
+
#+html:
** Usage
diff --git a/gptel-anthropic.el b/gptel-anthropic.el
new file mode 100644
index 00000000..c742c7fd
--- /dev/null
+++ b/gptel-anthropic.el
@@ -0,0 +1,156 @@
+;;; gptel-anthropic.el --- Anthropic AI suppport for gptel -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2023 Karthik Chikmagalur
+
+;; Author: Karthik Chikmagalur
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see .
+
+;;; Commentary:
+
+;; This file adds support for Anthropic's Messages API to gptel
+
+;;; Code:
+(require 'cl-generic)
+(eval-when-compile
+ (require 'cl-lib))
+(require 'map)
+(require 'gptel)
+
+(defvar json-object-type)
+
+(declare-function prop-match-value "text-property-search")
+(declare-function text-property-search-backward "text-property-search")
+(declare-function json-read "json")
+
+;;; Anthropic (Messages API)
+(cl-defstruct (gptel-anthropic (:constructor gptel--make-anthropic)
+ (:copier nil)
+ (:include gptel-backend)))
+
+(cl-defmethod gptel-curl--parse-stream ((_backend gptel-anthropic) _info)
+ (let* ((json-object-type 'plist)
+ (content-strs))
+ (condition-case nil
+ (while (re-search-forward "^event: " nil t)
+ (cond
+ ((looking-at "content_block_\\(?:start\\|delta\\|stop\\)")
+ (save-match-data
+ (forward-line 1) (forward-char 5)
+ (when-let* ((response (json-read))
+ (content (map-nested-elt
+ response '(:delta :text))))
+ (push content content-strs))))))
+ (error
+ (goto-char (match-beginning 0))))
+ (apply #'concat (nreverse content-strs))))
+
+(cl-defmethod gptel--parse-response ((_backend gptel-anthropic) response _info)
+ (with-current-buffer (get-buffer "*gptel-log*")
+ (princ response))
+ (map-nested-elt response '(:content 0 :text)))
+
+(cl-defmethod gptel--request-data ((_backend gptel-anthropic) prompts)
+ "JSON encode PROMPTS for sending to ChatGPT."
+ (let ((prompts-plist
+ `(:model ,gptel-model
+ :messages [,@prompts]
+ :system ,gptel--system-message
+ :stream ,(or (and gptel-stream gptel-use-curl
+ (gptel-backend-stream gptel-backend))
+ :json-false)
+ :max_tokens ,(or gptel-max-tokens 1024))))
+ (when gptel-temperature
+ (plist-put prompts-plist :temperature gptel-temperature))
+ prompts-plist))
+
+(cl-defmethod gptel--parse-buffer ((_backend gptel-anthropic) &optional max-entries)
+ (let ((prompts) (prop))
+ (while (and
+ (or (not max-entries) (>= max-entries 0))
+ (setq prop (text-property-search-backward
+ 'gptel 'response
+ (when (get-char-property (max (point-min) (1- (point)))
+ 'gptel)
+ t))))
+ (push (list :role (if (prop-match-value prop) "assistant" "user")
+ :content
+ (string-trim
+ (buffer-substring-no-properties (prop-match-beginning prop)
+ (prop-match-end prop))
+ (format "[\t\r\n ]*\\(?:%s\\)?[\t\r\n ]*"
+ (regexp-quote (gptel-prompt-prefix-string)))
+ (format "[\t\r\n ]*\\(?:%s\\)?[\t\r\n ]*"
+ (regexp-quote (gptel-response-prefix-string)))))
+ prompts)
+ (and max-entries (cl-decf max-entries)))
+ prompts))
+
+;;;###autoload
+(cl-defun gptel-make-anthropic
+ (name &key curl-args stream key
+ (header
+ (lambda () (when-let (key (gptel--get-api-key))
+ `(("x-api-key" . ,key)
+ ("anthropic-version" . "2023-06-01")))))
+ (models '("claude-3-sonnet-20240229" "claude-3-opus-20240229"))
+ (host "api.anthropic.com")
+ (protocol "https")
+ (endpoint "/v1/messages"))
+ "Register an Anthropic API-compatible backend for gptel with NAME.
+
+Keyword arguments:
+
+CURL-ARGS (optional) is a list of additional Curl arguments.
+
+HOST (optional) is the API host, \"api.anthropic.com\" by default.
+
+MODELS is a list of available model names.
+
+STREAM is a boolean to toggle streaming responses, defaults to
+false.
+
+PROTOCOL (optional) specifies the protocol, https by default.
+
+ENDPOINT (optional) is the API endpoint for completions, defaults to
+\"/v1/messages\".
+
+HEADER (optional) is for additional headers to send with each
+request. It should be an alist or a function that retuns an
+alist, like:
+((\"Content-Type\" . \"application/json\"))
+
+KEY is a variable whose value is the API key, or function that
+returns the key."
+ (declare (indent 1))
+ (let ((backend (gptel--make-anthropic
+ :curl-args curl-args
+ :name name
+ :host host
+ :header header
+ :key key
+ :models models
+ :protocol protocol
+ :endpoint endpoint
+ :stream stream
+ :url (if protocol
+ (concat protocol "://" host endpoint)
+ (concat host endpoint)))))
+ (prog1 backend
+ (setf (alist-get name gptel--known-backends
+ nil nil #'equal)
+ backend))))
+
+(provide 'gptel-anthropic)
+;;; gptel-backends.el ends here
diff --git a/gptel.el b/gptel.el
index d1231007..2b9cce79 100644
--- a/gptel.el
+++ b/gptel.el
@@ -30,7 +30,9 @@
;; gptel is a simple Large Language Model chat client, with support for multiple models/backends.
;;
;; gptel supports
-;; - The services ChatGPT, Azure, Gemini, Anyscale, Together.ai and Kagi (FastGPT & Summarizer)
+;;
+;; - The services ChatGPT, Azure, Gemini, Anthropic AI, Anyscale, Together.ai,
+;; Perplexity, and Kagi (FastGPT & Summarizer)
;; - Local models via Ollama, Llama.cpp, Llamafiles or GPT4All
;;
;; Additionally, any LLM service (local or remote) that provides an
@@ -54,7 +56,9 @@
;;
;; - For Azure: define a gptel-backend with `gptel-make-azure', which see.
;; - For Gemini: define a gptel-backend with `gptel-make-gemini', which see.
-;; - For Kagi: define a gptel-backend with `gptel-make-kagi', which see
+;; - For Anthropic (Claude): define a gptel-backend with `gptel-make-anthropic',
+;; which see
+;; - For Kagi: define a gptel-backend with `gptel-make-kagi', which see.
;;
;; For local models using Ollama, Llama.cpp or GPT4All:
;;