From 543db9c2dfe43f5d3e03f6ef8721662b4565fb14 Mon Sep 17 00:00:00 2001 From: Keiji Kanazawa Date: Wed, 21 Dec 2022 17:45:37 -0800 Subject: [PATCH] Add Azure OpenAI LLM (#395) Hi! This PR adds support for the Azure OpenAI service to LangChain. I've tried to follow the contributing guidelines. Co-authored-by: Keiji Kanazawa <{ID}+{username}@users.noreply.github.com> --- .../prompts/azure_openai_example.ipynb | 159 ++++++++++++++++++ langchain/llms/__init__.py | 4 +- langchain/llms/openai.py | 43 ++++- 3 files changed, 198 insertions(+), 8 deletions(-) create mode 100644 docs/examples/prompts/azure_openai_example.ipynb diff --git a/docs/examples/prompts/azure_openai_example.ipynb b/docs/examples/prompts/azure_openai_example.ipynb new file mode 100644 index 0000000000..31c483b5c1 --- /dev/null +++ b/docs/examples/prompts/azure_openai_example.ipynb @@ -0,0 +1,159 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9e9b7651", + "metadata": {}, + "source": [ + "# Azure OpenAI LLM Example\n", + "\n", + "This notebook goes over how to use Langchain with [Azure OpenAI](https://aka.ms/azure-openai).\n", + "\n", + "The Azure OpenAI API is compatible with OpenAI's API. The `openai` Python package makes it easy to use both OpenAI and Azure OpenAI. You can call Azure OpenAI the same way you call OpenAI with the exceptions noted below.\n", + "\n", + "### API configuration\n", + "You can configure the `openai` package to use Azure OpenAI using environment variables. The following is for `bash`:\n", + "\n", + "```bash\n", + "# Set this to `azure`\n", + "export OPENAI_API_TYPE=azure\n", + "# The API version you want to use: set this to `2022-12-01` for the released version.\n", + "export OPENAI_API_VERSION=2022-12-01\n", + "# The base URL for your Azure OpenAI resource. You can find this in the Azure portal under your Azure OpenAI resource.\n", + "export OPENAI_API_BASE_URL=https://your-resource-name.openai.azure.com\n", + "# The API key for your Azure OpenAI resource. You can find this in the Azure portal under your Azure OpenAI resource.\n", + "export OPENAI_API_KEY=\n", + "```\n", + "\n", + "Alternatively, you can configure the API right within your running Python environment:\n", + "\n", + "```python\n", + "import os\n", + "os.environ[\"OPENAI_API_TYPE\"] = \"azure\"\n", + "...\n", + "```\n", + "\n", + "### Deployments\n", + "With Azure OpenAI, you set up your own deployments of the common GPT-3 and Codex models. When calling the API, you need to specify the deployment you want to use.\n", + "\n", + "Let's say your deployment name is `text-davinci-002-prod`. In the `openai` Python API, you can specify this deployment with the `engine` parameter. For example:\n", + "\n", + "```python\n", + "import openai\n", + "\n", + "response = openai.Completion.create(\n", + " engine=\"text-davinci-002-prod\",\n", + " prompt=\"This is a test\",\n", + " max_tokens=5\n", + ")\n", + "```\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8fad2a6e", + "metadata": {}, + "outputs": [], + "source": [ + "# Import Azure OpenAI\n", + "from langchain.llms import AzureOpenAI" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "8c80213a", + "metadata": {}, + "outputs": [], + "source": [ + "# Create an instance of Azure OpenAI\n", + "# Replace the deployment name with your own\n", + "llm = AzureOpenAI(deployment_name=\"text-davinci-002-prod\", model_name=\"text-davinci-002\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "592dc404", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\n\\nWhy did the chicken cross the road?\\n\\nTo get to the other side.'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Run the LLM\n", + "llm(\"Tell me a joke\")" + ] + }, + { + "cell_type": "markdown", + "id": "bbfebea1", + "metadata": {}, + "source": [ + "We can also print the LLM and see its custom print." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9c33fa19", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1mAzureOpenAI\u001b[0m\n", + "Params: {'deployment_name': 'text-davinci-002', 'model_name': 'text-davinci-002', 'temperature': 0.7, 'max_tokens': 256, 'top_p': 1, 'frequency_penalty': 0, 'presence_penalty': 0, 'n': 1, 'best_of': 1}\n" + ] + } + ], + "source": [ + "print(llm)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a8b5917", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + }, + "vscode": { + "interpreter": { + "hash": "3bae61d45a4f4d73ecea8149862d4bfbae7d4d4a2f71b6e609a1be8f6c8d4298" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/langchain/llms/__init__.py b/langchain/llms/__init__.py index 72696af482..e07d0d5561 100644 --- a/langchain/llms/__init__.py +++ b/langchain/llms/__init__.py @@ -7,7 +7,7 @@ from langchain.llms.cohere import Cohere from langchain.llms.huggingface_hub import HuggingFaceHub from langchain.llms.huggingface_pipeline import HuggingFacePipeline from langchain.llms.nlpcloud import NLPCloud -from langchain.llms.openai import OpenAI +from langchain.llms.openai import AzureOpenAI, OpenAI __all__ = [ "Cohere", @@ -16,6 +16,7 @@ __all__ = [ "HuggingFaceHub", "HuggingFacePipeline", "AI21", + "AzureOpenAI", ] type_to_cls_dict: Dict[str, Type[BaseLLM]] = { @@ -25,4 +26,5 @@ type_to_cls_dict: Dict[str, Type[BaseLLM]] = { "nlpcloud": NLPCloud, "openai": OpenAI, "huggingface_pipeline": HuggingFacePipeline, + "azure": AzureOpenAI, } diff --git a/langchain/llms/openai.py b/langchain/llms/openai.py index d50795a5e3..0eb170242c 100644 --- a/langchain/llms/openai.py +++ b/langchain/llms/openai.py @@ -9,7 +9,7 @@ from langchain.schema import Generation from langchain.utils import get_from_dict_or_env -class OpenAI(BaseLLM, BaseModel): +class BaseOpenAI(BaseLLM, BaseModel): """Wrapper around OpenAI large language models. To use, you should have the ``openai`` python package installed, and the @@ -119,7 +119,7 @@ class OpenAI(BaseLLM, BaseModel): response = openai.generate(["Tell me a joke."]) """ # TODO: write a unit test for this - params = self._default_params + params = self._invocation_params if stop is not None: if "stop" in params: raise ValueError("`stop` found in both the input and default params.") @@ -141,9 +141,7 @@ class OpenAI(BaseLLM, BaseModel): # Includes prompt, completion, and total tokens used. _keys = ["completion_tokens", "prompt_tokens", "total_tokens"] for _prompts in sub_prompts: - response = self.client.create( - model=self.model_name, prompt=_prompts, **params - ) + response = self.client.create(prompt=_prompts, **params) choices.extend(response["choices"]) for _key in _keys: if _key not in token_usage: @@ -179,14 +177,19 @@ class OpenAI(BaseLLM, BaseModel): for token in generator: yield token """ - params = self._default_params + params = self._invocation_params if params["best_of"] != 1: raise ValueError("OpenAI only supports best_of == 1 for streaming") params["stream"] = True - generator = self.client.create(model=self.model_name, prompt=prompt, **params) + generator = self.client.create(prompt=prompt, **params) return generator + @property + def _invocation_params(self) -> Dict[str, Any]: + """Get the parameters used to invoke the model.""" + return self._default_params + @property def _identifying_params(self) -> Mapping[str, Any]: """Get the identifying parameters.""" @@ -274,3 +277,29 @@ class OpenAI(BaseLLM, BaseModel): # get max context size for model by name max_size = self.modelname_to_contextsize(self.model_name) return max_size - num_tokens + + +class OpenAI(BaseOpenAI): + """Generic OpenAI class that uses model name.""" + + @property + def _invocation_params(self) -> Dict[str, Any]: + return {**{"model": self.model_name}, **super()._invocation_params} + + +class AzureOpenAI(BaseOpenAI): + """Azure specific OpenAI class that uses deployment name.""" + + deployment_name: str = "" + """Deployment name to use.""" + + @property + def _identifying_params(self) -> Mapping[str, Any]: + return { + **{"deployment_name": self.deployment_name}, + **super()._identifying_params, + } + + @property + def _invocation_params(self) -> Dict[str, Any]: + return {**{"engine": self.deployment_name}, **super()._invocation_params}