-
Notifications
You must be signed in to change notification settings - Fork 173
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
gptel-anthropic: support Anthropic AI's Claude 3 (#229)
* gptel.el: Mention Anthropic in the package description. * gptel-anthropic.el (gptel-anthropic, gptel-make-anthropic, gptel--parse-response, gptel--request-data, gptel--parse-buffer, gptel-curl--parse-stream): Add support for Anthropic AI's Claude 3 models. * README.org: Add instructions for using Anthropic AI's Claude 3 models.
- Loading branch information
Showing
3 changed files
with
203 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
;;; gptel-anthropic.el --- Anthropic AI suppport for gptel -*- lexical-binding: t; -*- | ||
|
||
;; Copyright (C) 2023 Karthik Chikmagalur | ||
|
||
;; Author: Karthik Chikmagalur <karthikchikmagalur@gmail.com> | ||
|
||
;; 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 <https://www.gnu.org/licenses/>. | ||
|
||
;;; 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters