Commit 53e5ba4b
Changed files (16)
examples
azure
openai
examples/azure/finetuning.ipynb
@@ -40,6 +40,35 @@
"openai.api_version = '2022-03-01-preview' # this may change in the future"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Microsoft Active Directory Authentication\n",
+ "Instead of key based authentication, you can use Active Directory to authenticate using credential tokens. Uncomment the next code section to use credential based authentication:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\"\"\"\n",
+ "from azure.identity import DefaultAzureCredential\n",
+ "\n",
+ "default_credential = DefaultAzureCredential()\n",
+ "token = default_credential.get_token(\"https://cognitiveservices.azure.com\")\n",
+ "\n",
+ "openai.api_type = 'azure_ad'\n",
+ "openai.api_key = token.token\n",
+ "openai.api_version = '2022-03-01-preview' # this may change in the future\n",
+ "\n",
+ "\n",
+ "openai.api_base = '' # Please add your endpoint here\n",
+ "\"\"\""
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -418,10 +447,10 @@
],
"metadata": {
"interpreter": {
- "hash": "1efaa68c6557ae864f04a55d1c611eb06843d0ca160c97bf33f135c19475264d"
+ "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
},
"kernelspec": {
- "display_name": "Python 3.8.10 ('openai-env')",
+ "display_name": "Python 3.8.10 64-bit",
"language": "python",
"name": "python3"
},
openai/api_resources/abstract/api_resource.py
@@ -49,12 +49,12 @@ class APIResource(OpenAIObject):
api_version = self.api_version or openai.api_version
extn = quote_plus(id)
- if self.typed_api_type == ApiType.AZURE:
+ if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
if not api_version:
raise error.InvalidRequestError(
"An API version is required for the Azure API type."
)
-
+
if not operation:
base = self.class_url()
return "/%s%s/%s?api-version=%s" % (
@@ -72,13 +72,13 @@ class APIResource(OpenAIObject):
api_version
)
-
elif self.typed_api_type == ApiType.OPEN_AI:
base = self.class_url()
return "%s/%s" % (base, extn)
else:
- raise error.InvalidAPIType("Unsupported API type %s" % self.api_type)
+ raise error.InvalidAPIType(
+ "Unsupported API type %s" % self.api_type)
# The `method_` and `url_` arguments are suffixed with an underscore to
# avoid conflicting with actual request parameters in `params`.
@@ -111,7 +111,7 @@ class APIResource(OpenAIObject):
@classmethod
def _get_api_type_and_version(cls, api_type: str, api_version: str):
- typed_api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type)
+ typed_api_type = ApiType.from_str(
+ api_type) if api_type else ApiType.from_str(openai.api_type)
typed_api_version = api_version or openai.api_version
return (typed_api_type, typed_api_version)
-
openai/api_resources/abstract/createable_api_resource.py
@@ -24,15 +24,17 @@ class CreateableAPIResource(APIResource):
api_version=api_version,
organization=organization,
)
- typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
+ typed_api_type, api_version = cls._get_api_type_and_version(
+ api_type, api_version)
- if typed_api_type == ApiType.AZURE:
+ if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
base = cls.class_url()
- url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version)
+ url = "/%s%s?api-version=%s" % (cls.azure_api_prefix,
+ base, api_version)
elif typed_api_type == ApiType.OPEN_AI:
url = cls.class_url()
else:
- raise error.InvalidAPIType('Unsupported API type %s' % api_type)
+ raise error.InvalidAPIType('Unsupported API type %s' % api_type)
response, _, api_key = requestor.request(
"post", url, params, request_id=request_id
openai/api_resources/abstract/deletable_api_resource.py
@@ -4,21 +4,25 @@ from openai import error
from openai.api_resources.abstract.api_resource import APIResource
from openai.util import ApiType
+
class DeletableAPIResource(APIResource):
@classmethod
def delete(cls, sid, api_type=None, api_version=None, **params):
if isinstance(cls, APIResource):
- raise ValueError(".delete may only be called as a class method now.")
+ raise ValueError(
+ ".delete may only be called as a class method now.")
base = cls.class_url()
extn = quote_plus(sid)
- typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
- if typed_api_type == ApiType.AZURE:
- url = "/%s%s/%s?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version)
+ typed_api_type, api_version = cls._get_api_type_and_version(
+ api_type, api_version)
+ if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
+ url = "/%s%s/%s?api-version=%s" % (
+ cls.azure_api_prefix, base, extn, api_version)
elif typed_api_type == ApiType.OPEN_AI:
url = "%s/%s" % (base, extn)
else:
- raise error.InvalidAPIType('Unsupported API type %s' % api_type)
-
+ raise error.InvalidAPIType('Unsupported API type %s' % api_type)
+
return cls._static_request("delete", url, api_type=api_type, api_version=api_version, **params)
openai/api_resources/abstract/engine_api_resource.py
@@ -29,9 +29,10 @@ class EngineAPIResource(APIResource):
# Namespaces are separated in object names with periods (.) and in URLs
# with forward slashes (/), so replace the former with the latter.
base = cls.OBJECT_NAME.replace(".", "/") # type: ignore
- typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
+ typed_api_type, api_version = cls._get_api_type_and_version(
+ api_type, api_version)
- if typed_api_type == ApiType.AZURE:
+ if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
if not api_version:
raise error.InvalidRequestError(
"An API version is required for the Azure API type."
@@ -107,7 +108,8 @@ class EngineAPIResource(APIResource):
)
if stream:
- assert not isinstance(response, OpenAIResponse) # must be an iterator
+ # must be an iterator
+ assert not isinstance(response, OpenAIResponse)
return (
util.convert_to_openai_object(
line,
@@ -146,7 +148,7 @@ class EngineAPIResource(APIResource):
extn = quote_plus(id)
params_connector = '?'
- if self.typed_api_type == ApiType.AZURE:
+ if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
api_version = self.api_version or openai.api_version
if not api_version:
raise error.InvalidRequestError(
@@ -163,13 +165,13 @@ class EngineAPIResource(APIResource):
)
params_connector = '&'
-
elif self.typed_api_type == ApiType.OPEN_AI:
base = self.class_url(self.engine, self.api_type, self.api_version)
url = "%s/%s" % (base, extn)
else:
- raise error.InvalidAPIType("Unsupported API type %s" % self.api_type)
+ raise error.InvalidAPIType(
+ "Unsupported API type %s" % self.api_type)
timeout = self.get("timeout")
if timeout is not None:
openai/api_resources/abstract/listable_api_resource.py
@@ -27,15 +27,17 @@ class ListableAPIResource(APIResource):
organization=organization,
)
- typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
+ typed_api_type, api_version = cls._get_api_type_and_version(
+ api_type, api_version)
- if typed_api_type == ApiType.AZURE:
+ if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
base = cls.class_url()
- url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version)
+ url = "/%s%s?api-version=%s" % (cls.azure_api_prefix,
+ base, api_version)
elif typed_api_type == ApiType.OPEN_AI:
url = cls.class_url()
else:
- raise error.InvalidAPIType('Unsupported API type %s' % api_type)
+ raise error.InvalidAPIType('Unsupported API type %s' % api_type)
response, _, api_key = requestor.request(
"get", url, params, request_id=request_id
openai/api_resources/deployment.py
@@ -12,9 +12,11 @@ class Deployment(CreateableAPIResource, ListableAPIResource, DeletableAPIResourc
"""
Creates a new deployment for the provided prompt and parameters.
"""
- typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None)
- if typed_api_type != util.ApiType.AZURE:
- raise APIError("Deployment operations are only available for the Azure API type.")
+ typed_api_type, _ = cls._get_api_type_and_version(
+ kwargs.get("api_type", None), None)
+ if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD):
+ raise APIError(
+ "Deployment operations are only available for the Azure API type.")
if kwargs.get("model", None) is None:
raise InvalidRequestError(
@@ -28,9 +30,9 @@ class Deployment(CreateableAPIResource, ListableAPIResource, DeletableAPIResourc
"Must provide a 'scale_settings' parameter to create a Deployment.",
param="scale_settings",
)
-
+
if "scale_type" not in scale_settings or \
- (scale_settings["scale_type"].lower() == 'manual' and "capacity" not in scale_settings):
+ (scale_settings["scale_type"].lower() == 'manual' and "capacity" not in scale_settings):
raise InvalidRequestError(
"The 'scale_settings' parameter contains invalid or incomplete values.",
param="scale_settings",
@@ -40,24 +42,30 @@ class Deployment(CreateableAPIResource, ListableAPIResource, DeletableAPIResourc
@classmethod
def list(cls, *args, **kwargs):
- typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None)
- if typed_api_type != util.ApiType.AZURE:
- raise APIError("Deployment operations are only available for the Azure API type.")
+ typed_api_type, _ = cls._get_api_type_and_version(
+ kwargs.get("api_type", None), None)
+ if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD):
+ raise APIError(
+ "Deployment operations are only available for the Azure API type.")
return super().list(*args, **kwargs)
@classmethod
def delete(cls, *args, **kwargs):
- typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None)
- if typed_api_type != util.ApiType.AZURE:
- raise APIError("Deployment operations are only available for the Azure API type.")
+ typed_api_type, _ = cls._get_api_type_and_version(
+ kwargs.get("api_type", None), None)
+ if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD):
+ raise APIError(
+ "Deployment operations are only available for the Azure API type.")
return super().delete(*args, **kwargs)
@classmethod
def retrieve(cls, *args, **kwargs):
- typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None)
- if typed_api_type != util.ApiType.AZURE:
- raise APIError("Deployment operations are only available for the Azure API type.")
+ typed_api_type, _ = cls._get_api_type_and_version(
+ kwargs.get("api_type", None), None)
+ if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD):
+ raise APIError(
+ "Deployment operations are only available for the Azure API type.")
return super().retrieve(*args, **kwargs)
openai/api_resources/engine.py
@@ -28,7 +28,7 @@ class Engine(ListableAPIResource, UpdateableAPIResource):
util.log_info("Waiting for model to warm up", error=e)
def search(self, **params):
- if self.typed_api_type == ApiType.AZURE:
+ if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
return self.request("post", self.instance_url("search"), params)
elif self.typed_api_type == ApiType.OPEN_AI:
return self.request("post", self.instance_url() + "/search", params)
openai/api_resources/file.py
@@ -25,7 +25,8 @@ class File(ListableAPIResource, DeletableAPIResource):
user_provided_filename=None,
):
if purpose != "search" and model is not None:
- raise ValueError("'model' is only meaningful if 'purpose' is 'search'")
+ raise ValueError(
+ "'model' is only meaningful if 'purpose' is 'search'")
requestor = api_requestor.APIRequestor(
api_key,
api_base=api_base or openai.api_base,
@@ -33,15 +34,17 @@ class File(ListableAPIResource, DeletableAPIResource):
api_version=api_version,
organization=organization,
)
- typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
+ typed_api_type, api_version = cls._get_api_type_and_version(
+ api_type, api_version)
- if typed_api_type == ApiType.AZURE:
+ if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
base = cls.class_url()
- url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version)
+ url = "/%s%s?api-version=%s" % (cls.azure_api_prefix,
+ base, api_version)
elif typed_api_type == ApiType.OPEN_AI:
url = cls.class_url()
else:
- raise error.InvalidAPIType('Unsupported API type %s' % api_type)
+ raise error.InvalidAPIType('Unsupported API type %s' % api_type)
# Set the filename on 'purpose' and 'model' to None so they are
# interpreted as form data.
@@ -49,7 +52,8 @@ class File(ListableAPIResource, DeletableAPIResource):
if model is not None:
files.append(("model", (None, model)))
if user_provided_filename is not None:
- files.append(("file", (user_provided_filename, file, 'application/octet-stream')))
+ files.append(
+ ("file", (user_provided_filename, file, 'application/octet-stream')))
else:
files.append(("file", ("file", file, 'application/octet-stream')))
response, _, api_key = requestor.request("post", url, files=files)
@@ -59,12 +63,12 @@ class File(ListableAPIResource, DeletableAPIResource):
@classmethod
def download(
- cls,
- id,
- api_key=None,
+ cls,
+ id,
+ api_key=None,
api_base=None,
api_type=None,
- api_version=None,
+ api_version=None,
organization=None
):
requestor = api_requestor.APIRequestor(
@@ -74,16 +78,18 @@ class File(ListableAPIResource, DeletableAPIResource):
api_version=api_version,
organization=organization,
)
- typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
+ typed_api_type, api_version = cls._get_api_type_and_version(
+ api_type, api_version)
- if typed_api_type == ApiType.AZURE:
+ if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
base = cls.class_url()
- url = "/%s%s/%s/content?api-version=%s" % (cls.azure_api_prefix, base, id, api_version)
+ url = "/%s%s/%s/content?api-version=%s" % (
+ cls.azure_api_prefix, base, id, api_version)
elif typed_api_type == ApiType.OPEN_AI:
url = f"{cls.class_url()}/{id}/content"
else:
- raise error.InvalidAPIType('Unsupported API type %s' % api_type)
-
+ raise error.InvalidAPIType('Unsupported API type %s' % api_type)
+
result = requestor.request_raw("get", url)
if not 200 <= result.status_code < 300:
raise requestor.handle_error_response(
openai/api_resources/fine_tune.py
@@ -28,13 +28,15 @@ class FineTune(ListableAPIResource, CreateableAPIResource, DeletableAPIResource)
base = cls.class_url()
extn = quote_plus(id)
- typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
- if typed_api_type == ApiType.AZURE:
- url = "/%s%s/%s/cancel?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version)
+ typed_api_type, api_version = cls._get_api_type_and_version(
+ api_type, api_version)
+ if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
+ url = "/%s%s/%s/cancel?api-version=%s" % (
+ cls.azure_api_prefix, base, extn, api_version)
elif typed_api_type == ApiType.OPEN_AI:
url = "%s/%s/cancel" % (base, extn)
else:
- raise error.InvalidAPIType('Unsupported API type %s' % api_type)
+ raise error.InvalidAPIType('Unsupported API type %s' % api_type)
instance = cls(id, api_key, **params)
return instance.request("post", url, request_id=request_id)
@@ -62,15 +64,17 @@ class FineTune(ListableAPIResource, CreateableAPIResource, DeletableAPIResource)
organization=organization,
)
- typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
+ typed_api_type, api_version = cls._get_api_type_and_version(
+ api_type, api_version)
- if typed_api_type == ApiType.AZURE:
- url = "/%s%s/%s/events?stream=true&api-version=%s" % (cls.azure_api_prefix, base, extn, api_version)
+ if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD):
+ url = "/%s%s/%s/events?stream=true&api-version=%s" % (
+ cls.azure_api_prefix, base, extn, api_version)
elif typed_api_type == ApiType.OPEN_AI:
url = "%s/%s/events?stream=true" % (base, extn)
else:
- raise error.InvalidAPIType('Unsupported API type %s' % api_type)
-
+ raise error.InvalidAPIType('Unsupported API type %s' % api_type)
+
response, _, api_key = requestor.request(
"get", url, params, stream=True, request_id=request_id
)
openai/tests/test_api_requestor.py
@@ -37,7 +37,6 @@ def test_requestor_open_ai_headers() -> None:
headers = api_requestor.request_headers(
method="get", extra=headers, request_id="test_id"
)
- print(headers)
assert "Test_Header" in headers
assert headers["Test_Header"] == "Unit_Test_Header"
assert "Authorization" in headers
@@ -51,8 +50,20 @@ def test_requestor_azure_headers() -> None:
headers = api_requestor.request_headers(
method="get", extra=headers, request_id="test_id"
)
- print(headers)
assert "Test_Header" in headers
assert headers["Test_Header"] == "Unit_Test_Header"
assert "api-key" in headers
assert headers["api-key"] == "test_key"
+
+
+@pytest.mark.requestor
+def test_requestor_azure_ad_headers() -> None:
+ api_requestor = APIRequestor(key="test_key", api_type="azure_ad")
+ headers = {"Test_Header": "Unit_Test_Header"}
+ headers = api_requestor.request_headers(
+ method="get", extra=headers, request_id="test_id"
+ )
+ assert "Test_Header" in headers
+ assert headers["Test_Header"] == "Unit_Test_Header"
+ assert "Authorization" in headers
+ assert headers["Authorization"] == "Bearer test_key"
openai/tests/test_url_composition.py
@@ -15,6 +15,15 @@ def test_completions_url_composition_azure() -> None:
)
+@pytest.mark.url
+def test_completions_url_composition_azure_ad() -> None:
+ url = Completion.class_url("test_engine", "azure_ad", "2021-11-01-preview")
+ assert (
+ url
+ == "/openai/deployments/test_engine/completions?api-version=2021-11-01-preview"
+ )
+
+
@pytest.mark.url
def test_completions_url_composition_default() -> None:
url = Completion.class_url("test_engine")
@@ -48,6 +57,21 @@ def test_completions_url_composition_instance_url_azure() -> None:
)
+@pytest.mark.url
+def test_completions_url_composition_instance_url_azure_ad() -> None:
+ completion = Completion(
+ id="test_id",
+ engine="test_engine",
+ api_type="azure_ad",
+ api_version="2021-11-01-preview",
+ )
+ url = completion.instance_url()
+ assert (
+ url
+ == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview"
+ )
+
+
@pytest.mark.url
def test_completions_url_composition_instance_url_azure_no_version() -> None:
completion = Completion(
@@ -78,7 +102,8 @@ def test_completions_url_composition_instance_url_open_ai() -> None:
@pytest.mark.url
def test_completions_url_composition_instance_url_invalid() -> None:
- completion = Completion(id="test_id", engine="test_engine", api_type="invalid")
+ completion = Completion(
+ id="test_id", engine="test_engine", api_type="invalid")
with pytest.raises(Exception):
url = completion.instance_url()
@@ -101,7 +126,8 @@ def test_completions_url_composition_instance_url_timeout_azure() -> None:
@pytest.mark.url
def test_completions_url_composition_instance_url_timeout_openai() -> None:
- completion = Completion(id="test_id", engine="test_engine", api_type="open_ai")
+ completion = Completion(
+ id="test_id", engine="test_engine", api_type="open_ai")
completion["timeout"] = 12
url = completion.instance_url()
assert url == "/engines/test_engine/completions/test_id?timeout=12"
@@ -109,7 +135,8 @@ def test_completions_url_composition_instance_url_timeout_openai() -> None:
@pytest.mark.url
def test_engine_search_url_composition_azure() -> None:
- engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview")
+ engine = Engine(id="test_id", api_type="azure",
+ api_version="2021-11-01-preview")
assert engine.api_type == "azure"
assert engine.typed_api_type == ApiType.AZURE
url = engine.instance_url("test_operation")
@@ -119,6 +146,19 @@ def test_engine_search_url_composition_azure() -> None:
)
+@pytest.mark.url
+def test_engine_search_url_composition_azure_ad() -> None:
+ engine = Engine(id="test_id", api_type="azure_ad",
+ api_version="2021-11-01-preview")
+ assert engine.api_type == "azure_ad"
+ assert engine.typed_api_type == ApiType.AZURE_AD
+ url = engine.instance_url("test_operation")
+ assert (
+ url
+ == "/openai/deployments/test_id/test_operation?api-version=2021-11-01-preview"
+ )
+
+
@pytest.mark.url
def test_engine_search_url_composition_azure_no_version() -> None:
engine = Engine(id="test_id", api_type="azure", api_version=None)
@@ -130,11 +170,13 @@ def test_engine_search_url_composition_azure_no_version() -> None:
@pytest.mark.url
def test_engine_search_url_composition_azure_no_operation() -> None:
- engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview")
+ engine = Engine(id="test_id", api_type="azure",
+ api_version="2021-11-01-preview")
assert engine.api_type == "azure"
assert engine.typed_api_type == ApiType.AZURE
assert engine.instance_url() == "/openai/engines/test_id?api-version=2021-11-01-preview"
+
@pytest.mark.url
def test_engine_search_url_composition_default() -> None:
engine = Engine(id="test_id")
openai/__init__.py
@@ -32,7 +32,8 @@ api_key_path: Optional[str] = os.environ.get("OPENAI_API_KEY_PATH")
organization = os.environ.get("OPENAI_ORGANIZATION")
api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1")
api_type = os.environ.get("OPENAI_API_TYPE", "open_ai")
-api_version = "2022-03-01-preview" if api_type == "azure" else None
+api_version = "2022-03-01-preview" if api_type in (
+ "azure", "azure_ad", "azuread") else None
verify_ssl_certs = True # No effect. Certificates are always verified.
proxy = None
app_info = None
openai/util.py
@@ -20,7 +20,7 @@ __all__ = [
api_key_to_header = (
lambda api, key: {"Authorization": f"Bearer {key}"}
- if api == ApiType.OPEN_AI
+ if api in (ApiType.OPEN_AI, ApiType.AZURE_AD)
else {"api-key": f"{key}"}
)
@@ -28,11 +28,14 @@ api_key_to_header = (
class ApiType(Enum):
AZURE = 1
OPEN_AI = 2
+ AZURE_AD = 3
@staticmethod
def from_str(label):
if label.lower() == "azure":
return ApiType.AZURE
+ elif label.lower() in ("azure_ad", "azuread"):
+ return ApiType.AZURE_AD
elif label.lower() in ("open_ai", "openai"):
return ApiType.OPEN_AI
else:
@@ -175,7 +178,8 @@ def default_api_key() -> str:
with open(openai.api_key_path, "rt") as k:
api_key = k.read().strip()
if not api_key.startswith("sk-"):
- raise ValueError(f"Malformed API key in {openai.api_key_path}.")
+ raise ValueError(
+ f"Malformed API key in {openai.api_key_path}.")
return api_key
elif openai.api_key is not None:
return openai.api_key
openai/version.py
@@ -1,1 +1,1 @@
-VERSION = "0.20.1"
+VERSION = "0.21.0"
README.md
@@ -65,13 +65,13 @@ openai.api_base = "https://example-endpoint.openai.azure.com"
openai.api_version = "2021-11-01-preview"
# create a completion
-completion = openai.Completion.create(engine="deployment-namme", prompt="Hello world")
+completion = openai.Completion.create(engine="deployment-name", prompt="Hello world")
# print the completion
print(completion.choices[0].text)
# create a search and pass the deployment-name as the engine Id.
-search = openai.Engine(id="deployment-namme").search(documents=["White House", "hospital", "school"], query ="the president")
+search = openai.Engine(id="deployment-name").search(documents=["White House", "hospital", "school"], query ="the president")
# print the search
print(search)
@@ -81,6 +81,27 @@ Please note that for the moment, the Microsoft Azure endpoints can only be used
For a detailed example on how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebook:
[Using Azure fine-tuning](https://github.com/openai/openai-python/blob/main/examples/azure/finetuning.ipynb)
+### Microsoft Azure Active Directory Authentication
+
+In order to use Microsoft Active Directory to authenticate to your Azure endpoint, you need to set the api_type to "azure_ad" and pass the acquired credential token to api_key. The rest of the parameters need to be set as specified in the previous section.
+
+
+```python
+from azure.identity import DefaultAzureCredential
+import openai
+
+# Request credential
+default_credential = DefaultAzureCredential()
+token = default_credential.get_token("https://cognitiveservices.azure.com")
+
+# Setup parameters
+openai.api_type = "azure_ad"
+openai.api_key = token.token
+openai.api_base = "https://example-endpoint.openai.azure.com/"
+openai.api_version = "2022-03-01-preview"
+
+# ...
+```
### Command-line interface
This library additionally provides an `openai` command-line utility