diff --git a/README.org b/README.org index 9fa9bf6..01e13cc 100644 --- a/README.org +++ b/README.org @@ -6,10 +6,11 @@ GPTel is a simple Large Language Model chat client for Emacs, with support for m | 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]] | +| 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]] | | PrivateGPT | Planned | - | | Llama.cpp | Planned | - | @@ -45,6 +46,7 @@ GPTel uses Curl if available, but falls back to url-retrieve to work without ext - [[#azure][Azure]] - [[#gpt4all][GPT4All]] - [[#ollama][Ollama]] + - [[#gemini][Gemini]] - [[#usage][Usage]] - [[#in-any-buffer][In any buffer:]] - [[#in-a-dedicated-chat-buffer][In a dedicated chat buffer:]] @@ -200,6 +202,31 @@ You can pick this backend from the transient menu when using gptel (see Usage), #+html: +**** Gemini +#+html: + +Register a backend with +#+begin_src emacs-lisp +(gptel-make-gemini + "Gemini" + :key "YOUR_GEMINI_API_KEY" + :host "generativelanguage.googleapis.com" + :protocol "https" + :endpoint "/v1beta/models/gemini-pro:generateContent" + ) +#+end_src +These are the required parameters, refer to the documentation of =gptel-make-gemini= for more. Currently stream is not functional. + +You can pick this backend from the transient menu when using gptel (see Usage), or set this as the default value of =gptel-backend=: + +#+begin_src emacs-lisp +;; OPTIONAL configuration +(setq-default gptel-model "Gemini-Pro" ;Pick your default model + gptel-backend (gptel-make-gemini "Gemini" :host ...)) +#+end_src + +#+html: + ** Usage |-------------------+-------------------------------------------------------------------------| diff --git a/gptel-gemini.el b/gptel-gemini.el new file mode 100644 index 0000000..9947d32 --- /dev/null +++ b/gptel-gemini.el @@ -0,0 +1,114 @@ +;;; gptel-gemini.el --- Gemini suppport for gptel -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Karthik Chikmagalur + +;; Author: Karthik Chikmagalur +;; Keywords: + +;; 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 the Gemini API to gptel + +;;; Code: +(require 'gptel) +(require 'cl-generic) + +;;; Gemini +(cl-defstruct + (gptel-gemini (:constructor gptel--make-gemini) + (:copier nil) + (:include gptel-backend))) + +(cl-defmethod gptel--parse-response ((_backend gptel-gemini) response _info) + (map-nested-elt response '(:candidates 0 :content :parts 0 :text))) + +(cl-defmethod gptel--request-data ((_backend gptel-gemini) prompts) + "JSON encode PROMPTS for sending to Gemini." + (let ((prompts-plist + `(:contents [,@prompts] + ))) + prompts-plist)) + +(cl-defmethod gptel--parse-buffer ((_backend gptel-gemini) &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) "model" "user") + :parts + (list :text (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-gemini + (name &key header key + (host "generativelanguage.googleapis.com") + (protocol "https") + (models "gemini-pro") + (endpoint "/v1beta/models/gemini-pro:generateContent")) + + "Register a Gemini backend for gptel with NAME. + +Keyword arguments: + +HOST (optional) is the API host, typically \"generativelanguage.googleapis.com\". + +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 +\"/v1beta/models/gemini-pro:generateContent\". + +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 (optional) is a variable whose value is the API key, or +function that returns the key." + (let ((backend (gptel--make-gemini + :name name + :host host + :header header + :models models + :protocol protocol + :endpoint endpoint + :stream nil + :url (if protocol + (concat protocol "://" host endpoint "?key=" key) + (concat host endpoint "?key=" key))))) + (prog1 backend + (setf (alist-get name gptel--known-backends + nil nil #'equal) + backend)))) + +(provide 'gptel-gemini) +;;; gptel-gemini.el ends here