Skip to content

Commit

Permalink
Merge branch 'main' into batch-embeddings
Browse files Browse the repository at this point in the history
  • Loading branch information
ahyatt committed Nov 4, 2024
2 parents c0fab25 + 70a93c6 commit 52218da
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 23 deletions.
1 change: 1 addition & 0 deletions NEWS.org
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
* Version 0.18.0
- Add batch embeddings capability (currently for just Open AI and Ollama).
- Add Microsoft Azure's Open AI
- Remove testing and other development files from ELPA packaging.
- Remove vendored =plz-event-source= and =plz-media-type=, and add requirements.
- Update list of Ollama models for function calling.
Expand Down
3 changes: 3 additions & 0 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ You can set up with ~make-llm-openai~, with the following parameters:
** Open AI Compatible
There are many Open AI compatible APIs and proxies of Open AI. You can set up one with ~make-llm-openai-compatible~, with the following parameter:
- ~:url~, the URL of leading up to the command ("embeddings" or "chat/completions"). So, for example, "https://api.openai.com/v1/" is the URL to use Open AI (although if you wanted to do that, just use ~make-llm-openai~ instead.
** Azure's Open AI
Microsoft Azure has an Open AI integration, although it doesn't support everything Open AI does, such as function calling. You can set it up with ~make-llm-azure~, with the following parameter:
- ~:url~, the endpoint URL, such as "https://docs-test-001.openai.azure.com/".
** Gemini (not via Google Cloud)
This is Google's AI model. You can get an API key via their [[https://makersuite.google.com/app/apikey][page on Google AI Studio]].
Set this up with ~make-llm-gemini~, with the following parameters:
Expand Down
58 changes: 58 additions & 0 deletions llm-azure.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
;;; llm-azure.el --- llm module for integrating with Azure's Open AI service -*- lexical-binding: t; package-lint-main-file: "llm.el"; byte-compile-docstring-max-column: 200-*-

;; Copyright (c) 2024 Free Software Foundation, Inc.

;; Author: Andrew Hyatt <[email protected]>
;; Homepage: https://github.com/ahyatt/llm
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
;; 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:
;; This file implements the llm functionality defined in llm.el, for Azure's
;; Open AI service.

;;; Code:

(require 'llm)
(require 'llm-openai)
(require 'cl-lib)

(cl-defstruct (llm-azure (:include llm-openai-compatible)))

(cl-defmethod llm-nonfree-message-info ((_ llm-azure))
"Return Azure's nonfree terms of service."
"https://azure.microsoft.com/en-us/support/legal/")

(cl-defmethod llm-provider-chat-url ((provider llm-azure))
(format "%s/openai/deployments/%s/chat/completions?api-version=2024-08-01-preview"
(llm-azure-url provider)
(llm-azure-chat-model provider)))

(cl-defmethod llm-provider-embedding-url ((provider llm-azure))
(format "%s/openai/deployments/%s/embeddings?api-version=2024-08-01-preview"
(llm-azure-url provider)
(llm-azure-embedding-model provider)))

(cl-defmethod llm-provider-headers ((provider llm-azure))
`(("api-key" . ,(llm-azure-key provider))))

(cl-defmethod llm-capabilities ((_ llm-azure))
(list 'streaming 'embedding))

(cl-defmethod llm-name ((provider llm-azure))
(format "Azure OpenAI %s" (llm-azure-chat-model provider)))

(provide 'llm-azure)
;;; llm-azure.el ends here
75 changes: 52 additions & 23 deletions llm-integration-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,15 @@
;; - GEMINI_KEY: A Gemini API key.
;; - VERTEX_PROJECT: A Google Cloud Vertex project.
;; - OLLAMA_CHAT_MODELS: A list of Ollama models to test.
;; - AZURE_URL: The URL of the Azure API.
;; - AZURE_KEY: The key for the Azure API.
;; - AZURE_CHAT_MODEL: The name of the chat model to test.
;; - AZURE_EMBEDDING_MODEL: The name of the embedding model to test.
;; - AZURE_SLEEP: The number of seconds to sleep between tests, to avoid rate
;; limiting.
;;
;; If any of these are set, the corresponding provider will be tested.
;; If any of these are set (except for Azure, which needs multiple), the
;; corresponding provider will be tested.


;;; Code:
Expand Down Expand Up @@ -87,6 +94,11 @@
("capital_of_country" . "Italy"))
"The correct answer to the function call prompt.")

(defun llm-integration-test-rate-limit (provider)
(cond ((eq (type-of provider) 'llm-azure)
;; The free Azure tier has extremely restrictive rate limiting.
(sleep-for (string-to-number (or (getenv "AZURE_SLEEP") "60"))))))

(defun llm-integration-test-providers ()
"Return a list of providers to test."
(let ((providers))
Expand All @@ -102,6 +114,14 @@
(when (getenv "VERTEX_PROJECT")
(require 'llm-vertex)
(push (make-llm-vertex :project (getenv "VERTEX_PROJECT")) providers))
(when (and (getenv "AZURE_URL") (getenv "AZURE_KEY"))
(require 'llm-azure)
;; Because Azure requires setting up each model, we can't just use any
;; model it supports, but the model that the user running tests has set up.
(push (make-llm-azure :key (getenv "AZURE_KEY") :url (getenv "AZURE_URL")
:chat-model (getenv "AZURE_CHAT_MODEL")
:embedding-model (getenv "AZURE_EMBEDDING_MODEL"))
providers))
(when (getenv "OLLAMA_CHAT_MODELS")
(require 'llm-ollama)
;; This variable is a list of models to test.
Expand All @@ -113,9 +133,11 @@
"Define an integration test."
(declare (indent defun))
`(ert-deftest ,name ()
(dolist (,(car arglist) (llm-integration-test-providers))
(ert-info ((format "Using provider %s" (llm-name provider)))
,@body))))
(let ((llm-warn-on-nonfree nil))
(dolist (,(car arglist) (llm-integration-test-providers))
(llm-integration-test-rate-limit provider)
(ert-info ((format "Using provider %s" (llm-name provider)))
,@body)))))

(llm-def-integration-test llm-embedding (provider)
(when (member 'embeddings (llm-capabilities provider))
Expand Down Expand Up @@ -179,17 +201,19 @@
(llm-def-integration-test llm-chat-async (provider)
(let ((result nil)
(buf (current-buffer))
(llm-warn-on-nonfree nil))
(llm-warn-on-nonfree nil)
(err-result nil))
(llm-chat-async
provider
(llm-make-chat-prompt llm-integration-test-chat-prompt)
(lambda (response)
(should (eq (current-buffer) buf))
(setq result response))
(lambda (error)
(error "Error: %s" error)))
(while (null result)
(lambda (_ err)
(setq err-result err)))
(while (not (or result err-result))
(sleep-for 0.1))
(if err-result (error err-result))
(should (equal (string-trim result) llm-integration-test-chat-answer))))

(llm-def-integration-test llm-chat-streaming (provider)
Expand All @@ -198,7 +222,8 @@
(returned-result nil)
(llm-warn-on-nonfree nil)
(buf (current-buffer))
(start-time (current-time)))
(start-time (current-time))
(err-result nil))
(llm-chat-streaming
provider
(llm-make-chat-prompt llm-integration-test-chat-prompt)
Expand All @@ -208,29 +233,33 @@
(lambda (response)
(should (eq (current-buffer) buf))
(setq returned-result response))
(lambda (error)
(error "Error: %s" error)))
(lambda (_ err)
(setq err-result err)))
(while (and (null returned-result)
(null err-result)
(time-less-p (time-subtract (current-time) start-time) 10))
(sleep-for 0.1))
(if err-result (error err-result))
(should (equal (string-trim returned-result) llm-integration-test-chat-answer))
(should (equal (string-trim streamed-result) llm-integration-test-chat-answer)))))

(llm-def-integration-test llm-function-call (provider)
(let ((prompt (llm-integration-test-fc-prompt)))
(should (equal
(llm-chat provider prompt)
llm-integration-test-fc-answer))
;; Test that we can send the function back to the provider without error.
(llm-chat provider prompt)))
(when (member 'function-calls (llm-capabilities provider))
(let ((prompt (llm-integration-test-fc-prompt)))
(should (equal
(llm-chat provider prompt)
llm-integration-test-fc-answer))
;; Test that we can send the function back to the provider without error.
(llm-chat provider prompt))))

(llm-def-integration-test llm-function-call-multiple (provider)
(let ((prompt (llm-integration-test-fc-multiple-prompt)))
;; Sending back multiple answers often doesn't happen, so we can't reliably
;; check for this yet.
(llm-chat provider prompt)
;; Test that we can send the function back to the provider without error.
(llm-chat provider prompt)))
(when (member 'function-calls (llm-capabilities provider))
(let ((prompt (llm-integration-test-fc-multiple-prompt)))
;; Sending back multiple answers often doesn't happen, so we can't reliably
;; check for this yet.
(llm-chat provider prompt)
;; Test that we can send the function back to the provider without error.
(llm-chat provider prompt))))

(llm-def-integration-test llm-count-tokens (provider)
(let ((result (llm-count-tokens provider "What is the capital of France?")))
Expand Down

0 comments on commit 52218da

Please sign in to comment.