Skip to content

Commit

Permalink
Add src
Browse files Browse the repository at this point in the history
  • Loading branch information
jcs090218 committed Feb 11, 2024
1 parent 7ed4eb4 commit 973e6b1
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ dist/
# packaging
*-autoloads.el
*-pkg.el

# ignore internal test
_test/
58 changes: 58 additions & 0 deletions google-gemini-content.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
;;; google-gemini-content.el --- Create generate content with Google Gemini API -*- lexical-binding: t; -*-

;; Copyright (C) 2024 Shen, Jen-Chieh

;; This file is not part of GNU Emacs.

;; 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:
;;
;; Create generate content with Google Gemini API.
;;

;;; Code:

(require 'google-gemini)

;;
;;; API

;;;###autoload
(cl-defun google-gemini-generate-content ( contents callback
&key
(content-type "application/json"))
"Send generate content request."
(request (format "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=%s"
google-gemini-key)
:type "POST"
:headers (google-gemini--headers content-type)
:data (google-gemini--json-encode
`(("contents" . ,contents)))
:parser 'json-read
:complete (cl-function
(lambda (&key data &allow-other-keys)
(funcall callback data)))))

;;
;;; Application

;;;###autoload
(defun google-gemini-say ()
"Start making a conversation to Google Gemini."
(interactive)
)

(provide 'google-gemini-content)
;;; google-gemini-content.el ends here
92 changes: 90 additions & 2 deletions google-gemini.el
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
;; Maintainer: JenChieh <[email protected]>
;; URL: https://github.com/emacs-openai/google-gemini
;; Version: 0.1.0
;; Package-Requires: ((emacs "26.1"))
;; Package-Requires: ((emacs "26.1") (request "0.3.0"))
;; Keywords: comm google gemini

;; This file is not part of GNU Emacs.
Expand All @@ -31,7 +31,95 @@

;;; Code:

;; Happy coding! ;)
(require 'auth-source)
(require 'cl-lib)
(require 'let-alist)
(require 'pcase)
(require 'pp)
(require 'json)

(require 'request)

(defgroup google-gemini nil
"Elisp library for the Google Gemini API."
:prefix "google-gemini-"
:group 'comm
:link '(url-link :tag "Repository" "https://github.com/emacs-openai/google-gemini"))

;;
;;; Logger

(defvar google-gemini--show-log nil
"Get more information from the program.")

(defun google-gemini--log (fmt &rest args)
"Debug message like function `message' with same argument FMT and ARGS."
(when google-gemini--show-log
(apply 'message fmt args)))

;;
;;; Request

(defvar google-gemini-key ""
"Variable storing the gemini key or a function name to retrieve it.
The function should take no arguments and return a string containing the key.
A function, `google-gemini-key-auth-source', that retrieves the key from
auth-source is provided for convenience.")

(defcustom google-gemini-base-url "googleapis.com"
"The base URL for Google Gemini API requests."
:type 'string
:group 'google-gemini)

;;;###autoload
(defun google-gemini-key-auth-source (&optional base-url)
"Retrieve the Google Gemini API key from auth-source given a BASE-URL.
If BASE-URL is not specified, it defaults to `google-gemini-base-url'."
(if-let ((auth-info
(auth-source-search :max 1
:host (or (url-host (url-generic-parse-url (or base-url google-gemini-base-url)))
google-gemini-base-url)
:require '(:user :secret))))
(funcall (plist-get (car auth-info) :secret))
(error "Google Gemini API key not found in auth-source")))

(defun google-gemini--alist-omit-null (alist)
"Omit null value or empty string in ALIST."
(cl-remove-if (lambda (pair)
(let ((value (cdr pair)))
(or (null value) ; ignore null
(and (stringp value) ; ignore empty string
(string-empty-p value)))))
alist))

(defun google-gemini--headers (content-type)
"Construct request headers.
Arguments CONTENT-TYPE are common request headers."
(google-gemini--alist-omit-null
`(("Content-Type" . ,content-type)
,(if (or (null key)
(string-empty-p key))
""
(pcase openai-key-type
(:bearer `("Authorization" . ,(concat "Bearer " key)))
(:azure-api `("api-key" . ,key))
(_ (user-error "Invalid key type: %s"
openai-key-type))))
)))

(defun google-gemini--json-encode (object)
"Wrapper for function `json-encode' but it remove nil value before
constructing JSON data.
The argument OBJECT is an alist that can be construct to JSON data; see function
`json-encode' for the detials."
(let* ((object (google-gemini--alist-omit-null object))
(encoded (json-encode object)))
(google-gemini--log "[ENCODED]: %s" encoded)
encoded))

(provide 'google-gemini)
;;; google-gemini.el ends here

0 comments on commit 973e6b1

Please sign in to comment.