Upload 77 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- openai/__init__.py +73 -0
- openai/__pycache__/__init__.cpython-39.pyc +0 -0
- openai/__pycache__/_openai_scripts.cpython-39.pyc +0 -0
- openai/__pycache__/api_requestor.cpython-39.pyc +0 -0
- openai/__pycache__/cli.cpython-39.pyc +0 -0
- openai/__pycache__/embeddings_utils.cpython-39.pyc +0 -0
- openai/__pycache__/error.cpython-39.pyc +0 -0
- openai/__pycache__/object_classes.cpython-39.pyc +0 -0
- openai/__pycache__/openai_object.cpython-39.pyc +0 -0
- openai/__pycache__/openai_response.cpython-39.pyc +0 -0
- openai/__pycache__/upload_progress.cpython-39.pyc +0 -0
- openai/__pycache__/util.cpython-39.pyc +0 -0
- openai/__pycache__/validators.cpython-39.pyc +0 -0
- openai/__pycache__/version.cpython-39.pyc +0 -0
- openai/__pycache__/wandb_logger.cpython-39.pyc +0 -0
- openai/_openai_scripts.py +74 -0
- openai/api_requestor.py +365 -0
- openai/api_resources/__init__.py +13 -0
- openai/api_resources/__pycache__/__init__.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/answer.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/classification.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/completion.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/customer.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/deployment.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/edit.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/embedding.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/engine.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/error_object.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/file.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/fine_tune.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/model.cpython-39.pyc +0 -0
- openai/api_resources/__pycache__/search.cpython-39.pyc +0 -0
- openai/api_resources/abstract/__init__.py +10 -0
- openai/api_resources/abstract/__pycache__/__init__.cpython-39.pyc +0 -0
- openai/api_resources/abstract/__pycache__/api_resource.cpython-39.pyc +0 -0
- openai/api_resources/abstract/__pycache__/createable_api_resource.cpython-39.pyc +0 -0
- openai/api_resources/abstract/__pycache__/deletable_api_resource.cpython-39.pyc +0 -0
- openai/api_resources/abstract/__pycache__/engine_api_resource.cpython-39.pyc +0 -0
- openai/api_resources/abstract/__pycache__/listable_api_resource.cpython-39.pyc +0 -0
- openai/api_resources/abstract/__pycache__/nested_resource_class_methods.cpython-39.pyc +0 -0
- openai/api_resources/abstract/__pycache__/updateable_api_resource.cpython-39.pyc +0 -0
- openai/api_resources/abstract/api_resource.py +117 -0
- openai/api_resources/abstract/createable_api_resource.py +47 -0
- openai/api_resources/abstract/deletable_api_resource.py +24 -0
- openai/api_resources/abstract/engine_api_resource.py +192 -0
- openai/api_resources/abstract/listable_api_resource.py +47 -0
- openai/api_resources/abstract/nested_resource_class_methods.py +102 -0
- openai/api_resources/abstract/updateable_api_resource.py +10 -0
- openai/api_resources/answer.py +12 -0
- openai/api_resources/classification.py +12 -0
openai/__init__.py
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# OpenAI Python bindings.
|
2 |
+
#
|
3 |
+
# Originally forked from the MIT-licensed Stripe Python bindings.
|
4 |
+
|
5 |
+
import os
|
6 |
+
from typing import Optional
|
7 |
+
|
8 |
+
from openai.api_resources import (
|
9 |
+
Answer,
|
10 |
+
Classification,
|
11 |
+
Completion,
|
12 |
+
Customer,
|
13 |
+
Edit,
|
14 |
+
Deployment,
|
15 |
+
Embedding,
|
16 |
+
Engine,
|
17 |
+
ErrorObject,
|
18 |
+
File,
|
19 |
+
FineTune,
|
20 |
+
Model,
|
21 |
+
Search,
|
22 |
+
)
|
23 |
+
from openai.error import APIError, InvalidRequestError, OpenAIError
|
24 |
+
|
25 |
+
api_key = os.environ.get("OPENAI_API_KEY")
|
26 |
+
# Path of a file with an API key, whose contents can change. Supercedes
|
27 |
+
# `api_key` if set. The main use case is volume-mounted Kubernetes secrets,
|
28 |
+
# which are updated automatically.
|
29 |
+
api_key_path: Optional[str] = os.environ.get("OPENAI_API_KEY_PATH")
|
30 |
+
|
31 |
+
organization = os.environ.get("OPENAI_ORGANIZATION")
|
32 |
+
api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1")
|
33 |
+
api_type = os.environ.get("OPENAI_API_TYPE", "open_ai")
|
34 |
+
api_version = "2021-11-01-preview" if api_type == "azure" else None
|
35 |
+
verify_ssl_certs = True # No effect. Certificates are always verified.
|
36 |
+
proxy = None
|
37 |
+
app_info = None
|
38 |
+
enable_telemetry = False # Ignored; the telemetry feature was removed.
|
39 |
+
ca_bundle_path = None # No longer used, feature was removed
|
40 |
+
debug = False
|
41 |
+
log = None # Set to either 'debug' or 'info', controls console logging
|
42 |
+
|
43 |
+
__all__ = [
|
44 |
+
"APIError",
|
45 |
+
"Answer",
|
46 |
+
"Classification",
|
47 |
+
"Completion",
|
48 |
+
"Customer",
|
49 |
+
"Edit",
|
50 |
+
"Deployment",
|
51 |
+
"Embedding",
|
52 |
+
"Engine",
|
53 |
+
"ErrorObject",
|
54 |
+
"File",
|
55 |
+
"FineTune",
|
56 |
+
"InvalidRequestError",
|
57 |
+
"Model",
|
58 |
+
"OpenAIError",
|
59 |
+
"Search",
|
60 |
+
"api_base",
|
61 |
+
"api_key",
|
62 |
+
"api_type",
|
63 |
+
"api_key_path",
|
64 |
+
"api_version",
|
65 |
+
"app_info",
|
66 |
+
"ca_bundle_path",
|
67 |
+
"debug",
|
68 |
+
"enable_elemetry",
|
69 |
+
"log",
|
70 |
+
"organization",
|
71 |
+
"proxy",
|
72 |
+
"verify_ssl_certs",
|
73 |
+
]
|
openai/__pycache__/__init__.cpython-39.pyc
ADDED
Binary file (1.3 kB). View file
|
|
openai/__pycache__/_openai_scripts.cpython-39.pyc
ADDED
Binary file (2.06 kB). View file
|
|
openai/__pycache__/api_requestor.cpython-39.pyc
ADDED
Binary file (9.27 kB). View file
|
|
openai/__pycache__/cli.cpython-39.pyc
ADDED
Binary file (26.4 kB). View file
|
|
openai/__pycache__/embeddings_utils.cpython-39.pyc
ADDED
Binary file (7.83 kB). View file
|
|
openai/__pycache__/error.cpython-39.pyc
ADDED
Binary file (4.7 kB). View file
|
|
openai/__pycache__/object_classes.cpython-39.pyc
ADDED
Binary file (484 Bytes). View file
|
|
openai/__pycache__/openai_object.cpython-39.pyc
ADDED
Binary file (6.94 kB). View file
|
|
openai/__pycache__/openai_response.cpython-39.pyc
ADDED
Binary file (1.18 kB). View file
|
|
openai/__pycache__/upload_progress.cpython-39.pyc
ADDED
Binary file (2.05 kB). View file
|
|
openai/__pycache__/util.cpython-39.pyc
ADDED
Binary file (5.35 kB). View file
|
|
openai/__pycache__/validators.cpython-39.pyc
ADDED
Binary file (28.7 kB). View file
|
|
openai/__pycache__/version.cpython-39.pyc
ADDED
Binary file (210 Bytes). View file
|
|
openai/__pycache__/wandb_logger.cpython-39.pyc
ADDED
Binary file (7.27 kB). View file
|
|
openai/_openai_scripts.py
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
import argparse
|
3 |
+
import logging
|
4 |
+
import sys
|
5 |
+
|
6 |
+
import openai
|
7 |
+
from openai.cli import api_register, display_error, tools_register, wandb_register
|
8 |
+
|
9 |
+
logger = logging.getLogger()
|
10 |
+
formatter = logging.Formatter("[%(asctime)s] %(message)s")
|
11 |
+
handler = logging.StreamHandler(sys.stderr)
|
12 |
+
handler.setFormatter(formatter)
|
13 |
+
logger.addHandler(handler)
|
14 |
+
|
15 |
+
|
16 |
+
def main():
|
17 |
+
parser = argparse.ArgumentParser(description=None)
|
18 |
+
parser.add_argument(
|
19 |
+
"-v",
|
20 |
+
"--verbose",
|
21 |
+
action="count",
|
22 |
+
dest="verbosity",
|
23 |
+
default=0,
|
24 |
+
help="Set verbosity.",
|
25 |
+
)
|
26 |
+
parser.add_argument("-b", "--api-base", help="What API base url to use.")
|
27 |
+
parser.add_argument("-k", "--api-key", help="What API key to use.")
|
28 |
+
parser.add_argument(
|
29 |
+
"-o",
|
30 |
+
"--organization",
|
31 |
+
help="Which organization to run as (will use your default organization if not specified)",
|
32 |
+
)
|
33 |
+
|
34 |
+
def help(args):
|
35 |
+
parser.print_help()
|
36 |
+
|
37 |
+
parser.set_defaults(func=help)
|
38 |
+
|
39 |
+
subparsers = parser.add_subparsers()
|
40 |
+
sub_api = subparsers.add_parser("api", help="Direct API calls")
|
41 |
+
sub_tools = subparsers.add_parser("tools", help="Client side tools for convenience")
|
42 |
+
sub_wandb = subparsers.add_parser("wandb", help="Logging with Weights & Biases")
|
43 |
+
|
44 |
+
api_register(sub_api)
|
45 |
+
tools_register(sub_tools)
|
46 |
+
wandb_register(sub_wandb)
|
47 |
+
|
48 |
+
args = parser.parse_args()
|
49 |
+
if args.verbosity == 1:
|
50 |
+
logger.setLevel(logging.INFO)
|
51 |
+
elif args.verbosity >= 2:
|
52 |
+
logger.setLevel(logging.DEBUG)
|
53 |
+
|
54 |
+
openai.debug = True
|
55 |
+
if args.api_key is not None:
|
56 |
+
openai.api_key = args.api_key
|
57 |
+
if args.api_base is not None:
|
58 |
+
openai.api_base = args.api_base
|
59 |
+
if args.organization is not None:
|
60 |
+
openai.organization = args.organization
|
61 |
+
|
62 |
+
try:
|
63 |
+
args.func(args)
|
64 |
+
except openai.error.OpenAIError as e:
|
65 |
+
display_error(e)
|
66 |
+
return 1
|
67 |
+
except KeyboardInterrupt:
|
68 |
+
sys.stderr.write("\n")
|
69 |
+
return 1
|
70 |
+
return 0
|
71 |
+
|
72 |
+
|
73 |
+
if __name__ == "__main__":
|
74 |
+
sys.exit(main())
|
openai/api_requestor.py
ADDED
@@ -0,0 +1,365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import platform
|
3 |
+
import threading
|
4 |
+
import warnings
|
5 |
+
from email import header
|
6 |
+
from json import JSONDecodeError
|
7 |
+
from typing import Dict, Iterator, Optional, Tuple, Union
|
8 |
+
from urllib.parse import urlencode, urlsplit, urlunsplit
|
9 |
+
|
10 |
+
import requests
|
11 |
+
|
12 |
+
import openai
|
13 |
+
from openai import error, util, version
|
14 |
+
from openai.openai_response import OpenAIResponse
|
15 |
+
from openai.util import ApiType
|
16 |
+
|
17 |
+
TIMEOUT_SECS = 600
|
18 |
+
MAX_CONNECTION_RETRIES = 2
|
19 |
+
|
20 |
+
# Has one attribute per thread, 'session'.
|
21 |
+
_thread_context = threading.local()
|
22 |
+
|
23 |
+
|
24 |
+
def _build_api_url(url, query):
|
25 |
+
scheme, netloc, path, base_query, fragment = urlsplit(url)
|
26 |
+
|
27 |
+
if base_query:
|
28 |
+
query = "%s&%s" % (base_query, query)
|
29 |
+
|
30 |
+
return urlunsplit((scheme, netloc, path, query, fragment))
|
31 |
+
|
32 |
+
|
33 |
+
def _requests_proxies_arg(proxy) -> Optional[Dict[str, str]]:
|
34 |
+
"""Returns a value suitable for the 'proxies' argument to 'requests.request."""
|
35 |
+
if proxy is None:
|
36 |
+
return None
|
37 |
+
elif isinstance(proxy, str):
|
38 |
+
return {"http": proxy, "https": proxy}
|
39 |
+
elif isinstance(proxy, dict):
|
40 |
+
return proxy.copy()
|
41 |
+
else:
|
42 |
+
raise ValueError(
|
43 |
+
"'openai.proxy' must be specified as either a string URL or a dict with string URL under the https and/or http keys."
|
44 |
+
)
|
45 |
+
|
46 |
+
|
47 |
+
def _make_session() -> requests.Session:
|
48 |
+
if not openai.verify_ssl_certs:
|
49 |
+
warnings.warn("verify_ssl_certs is ignored; openai always verifies.")
|
50 |
+
s = requests.Session()
|
51 |
+
proxies = _requests_proxies_arg(openai.proxy)
|
52 |
+
if proxies:
|
53 |
+
s.proxies = proxies
|
54 |
+
s.mount(
|
55 |
+
"https://",
|
56 |
+
requests.adapters.HTTPAdapter(max_retries=MAX_CONNECTION_RETRIES),
|
57 |
+
)
|
58 |
+
return s
|
59 |
+
|
60 |
+
|
61 |
+
def parse_stream(rbody):
|
62 |
+
for line in rbody:
|
63 |
+
if line:
|
64 |
+
if line == b"data: [DONE]":
|
65 |
+
# return here will cause GeneratorExit exception in urllib3
|
66 |
+
# and it will close http connection with TCP Reset
|
67 |
+
continue
|
68 |
+
if hasattr(line, "decode"):
|
69 |
+
line = line.decode("utf-8")
|
70 |
+
if line.startswith("data: "):
|
71 |
+
line = line[len("data: ") :]
|
72 |
+
yield line
|
73 |
+
|
74 |
+
|
75 |
+
class APIRequestor:
|
76 |
+
def __init__(
|
77 |
+
self,
|
78 |
+
key=None,
|
79 |
+
api_base=None,
|
80 |
+
api_type=None,
|
81 |
+
api_version=None,
|
82 |
+
organization=None,
|
83 |
+
):
|
84 |
+
self.api_base = api_base or openai.api_base
|
85 |
+
self.api_key = key or util.default_api_key()
|
86 |
+
self.api_type = (
|
87 |
+
ApiType.from_str(api_type)
|
88 |
+
if api_type
|
89 |
+
else ApiType.from_str(openai.api_type)
|
90 |
+
)
|
91 |
+
self.api_version = api_version or openai.api_version
|
92 |
+
self.organization = organization or openai.organization
|
93 |
+
|
94 |
+
@classmethod
|
95 |
+
def format_app_info(cls, info):
|
96 |
+
str = info["name"]
|
97 |
+
if info["version"]:
|
98 |
+
str += "/%s" % (info["version"],)
|
99 |
+
if info["url"]:
|
100 |
+
str += " (%s)" % (info["url"],)
|
101 |
+
return str
|
102 |
+
|
103 |
+
def request(
|
104 |
+
self,
|
105 |
+
method,
|
106 |
+
url,
|
107 |
+
params=None,
|
108 |
+
headers=None,
|
109 |
+
files=None,
|
110 |
+
stream=False,
|
111 |
+
request_id: Optional[str] = None,
|
112 |
+
) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool, str]:
|
113 |
+
result = self.request_raw(
|
114 |
+
method.lower(),
|
115 |
+
url,
|
116 |
+
params=params,
|
117 |
+
supplied_headers=headers,
|
118 |
+
files=files,
|
119 |
+
stream=stream,
|
120 |
+
request_id=request_id,
|
121 |
+
)
|
122 |
+
resp, got_stream = self._interpret_response(result, stream)
|
123 |
+
return resp, got_stream, self.api_key
|
124 |
+
|
125 |
+
def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False):
|
126 |
+
try:
|
127 |
+
error_data = resp["error"]
|
128 |
+
except (KeyError, TypeError):
|
129 |
+
raise error.APIError(
|
130 |
+
"Invalid response object from API: %r (HTTP response code "
|
131 |
+
"was %d)" % (rbody, rcode),
|
132 |
+
rbody,
|
133 |
+
rcode,
|
134 |
+
resp,
|
135 |
+
)
|
136 |
+
|
137 |
+
if "internal_message" in error_data:
|
138 |
+
error_data["message"] += "\n\n" + error_data["internal_message"]
|
139 |
+
|
140 |
+
util.log_info(
|
141 |
+
"OpenAI API error received",
|
142 |
+
error_code=error_data.get("code"),
|
143 |
+
error_type=error_data.get("type"),
|
144 |
+
error_message=error_data.get("message"),
|
145 |
+
error_param=error_data.get("param"),
|
146 |
+
stream_error=stream_error,
|
147 |
+
)
|
148 |
+
|
149 |
+
# Rate limits were previously coded as 400's with code 'rate_limit'
|
150 |
+
if rcode == 429:
|
151 |
+
return error.RateLimitError(
|
152 |
+
error_data.get("message"), rbody, rcode, resp, rheaders
|
153 |
+
)
|
154 |
+
elif rcode in [400, 404, 415]:
|
155 |
+
return error.InvalidRequestError(
|
156 |
+
error_data.get("message"),
|
157 |
+
error_data.get("param"),
|
158 |
+
error_data.get("code"),
|
159 |
+
rbody,
|
160 |
+
rcode,
|
161 |
+
resp,
|
162 |
+
rheaders,
|
163 |
+
)
|
164 |
+
elif rcode == 401:
|
165 |
+
return error.AuthenticationError(
|
166 |
+
error_data.get("message"), rbody, rcode, resp, rheaders
|
167 |
+
)
|
168 |
+
elif rcode == 403:
|
169 |
+
return error.PermissionError(
|
170 |
+
error_data.get("message"), rbody, rcode, resp, rheaders
|
171 |
+
)
|
172 |
+
elif rcode == 409:
|
173 |
+
return error.TryAgain(
|
174 |
+
error_data.get("message"), rbody, rcode, resp, rheaders
|
175 |
+
)
|
176 |
+
elif stream_error:
|
177 |
+
# TODO: we will soon attach status codes to stream errors
|
178 |
+
parts = [error_data.get("message"), "(Error occurred while streaming.)"]
|
179 |
+
message = " ".join([p for p in parts if p is not None])
|
180 |
+
return error.APIError(message, rbody, rcode, resp, rheaders)
|
181 |
+
else:
|
182 |
+
return error.APIError(
|
183 |
+
error_data.get("message"), rbody, rcode, resp, rheaders
|
184 |
+
)
|
185 |
+
|
186 |
+
def request_headers(
|
187 |
+
self, method: str, extra, request_id: Optional[str]
|
188 |
+
) -> Dict[str, str]:
|
189 |
+
user_agent = "OpenAI/v1 PythonBindings/%s" % (version.VERSION,)
|
190 |
+
if openai.app_info:
|
191 |
+
user_agent += " " + self.format_app_info(openai.app_info)
|
192 |
+
|
193 |
+
uname_without_node = " ".join(
|
194 |
+
v for k, v in platform.uname()._asdict().items() if k != "node"
|
195 |
+
)
|
196 |
+
ua = {
|
197 |
+
"bindings_version": version.VERSION,
|
198 |
+
"httplib": "requests",
|
199 |
+
"lang": "python",
|
200 |
+
"lang_version": platform.python_version(),
|
201 |
+
"platform": platform.platform(),
|
202 |
+
"publisher": "openai",
|
203 |
+
"uname": uname_without_node,
|
204 |
+
}
|
205 |
+
if openai.app_info:
|
206 |
+
ua["application"] = openai.app_info
|
207 |
+
|
208 |
+
headers = {
|
209 |
+
"X-OpenAI-Client-User-Agent": json.dumps(ua),
|
210 |
+
"User-Agent": user_agent,
|
211 |
+
}
|
212 |
+
|
213 |
+
headers.update(util.api_key_to_header(self.api_type, self.api_key))
|
214 |
+
|
215 |
+
if self.organization:
|
216 |
+
headers["OpenAI-Organization"] = self.organization
|
217 |
+
|
218 |
+
if self.api_version is not None and self.api_type == ApiType.OPEN_AI:
|
219 |
+
headers["OpenAI-Version"] = self.api_version
|
220 |
+
if request_id is not None:
|
221 |
+
headers["X-Request-Id"] = request_id
|
222 |
+
if openai.debug:
|
223 |
+
headers["OpenAI-Debug"] = "true"
|
224 |
+
headers.update(extra)
|
225 |
+
|
226 |
+
return headers
|
227 |
+
|
228 |
+
def _validate_headers(
|
229 |
+
self, supplied_headers: Optional[Dict[str, str]]
|
230 |
+
) -> Dict[str, str]:
|
231 |
+
headers: Dict[str, str] = {}
|
232 |
+
if supplied_headers is None:
|
233 |
+
return headers
|
234 |
+
|
235 |
+
if not isinstance(supplied_headers, dict):
|
236 |
+
raise TypeError("Headers must be a dictionary")
|
237 |
+
|
238 |
+
for k, v in supplied_headers.items():
|
239 |
+
if not isinstance(k, str):
|
240 |
+
raise TypeError("Header keys must be strings")
|
241 |
+
if not isinstance(v, str):
|
242 |
+
raise TypeError("Header values must be strings")
|
243 |
+
headers[k] = v
|
244 |
+
|
245 |
+
# NOTE: It is possible to do more validation of the headers, but a request could always
|
246 |
+
# be made to the API manually with invalid headers, so we need to handle them server side.
|
247 |
+
|
248 |
+
return headers
|
249 |
+
|
250 |
+
def request_raw(
|
251 |
+
self,
|
252 |
+
method,
|
253 |
+
url,
|
254 |
+
*,
|
255 |
+
params=None,
|
256 |
+
supplied_headers: Dict[str, str] = None,
|
257 |
+
files=None,
|
258 |
+
stream: bool = False,
|
259 |
+
request_id: Optional[str] = None,
|
260 |
+
) -> requests.Response:
|
261 |
+
abs_url = "%s%s" % (self.api_base, url)
|
262 |
+
headers = self._validate_headers(supplied_headers)
|
263 |
+
|
264 |
+
data = None
|
265 |
+
if method == "get" or method == "delete":
|
266 |
+
if params:
|
267 |
+
encoded_params = urlencode(
|
268 |
+
[(k, v) for k, v in params.items() if v is not None]
|
269 |
+
)
|
270 |
+
abs_url = _build_api_url(abs_url, encoded_params)
|
271 |
+
elif method in {"post", "put"}:
|
272 |
+
if params and files:
|
273 |
+
raise ValueError("At most one of params and files may be specified.")
|
274 |
+
if params:
|
275 |
+
data = json.dumps(params).encode()
|
276 |
+
headers["Content-Type"] = "application/json"
|
277 |
+
else:
|
278 |
+
raise error.APIConnectionError(
|
279 |
+
"Unrecognized HTTP method %r. This may indicate a bug in the "
|
280 |
+
"OpenAI bindings. Please contact support@openai.com for "
|
281 |
+
"assistance." % (method,)
|
282 |
+
)
|
283 |
+
|
284 |
+
headers = self.request_headers(method, headers, request_id)
|
285 |
+
|
286 |
+
util.log_info("Request to OpenAI API", method=method, path=abs_url)
|
287 |
+
util.log_debug("Post details", data=data, api_version=self.api_version)
|
288 |
+
|
289 |
+
if not hasattr(_thread_context, "session"):
|
290 |
+
_thread_context.session = _make_session()
|
291 |
+
try:
|
292 |
+
result = _thread_context.session.request(
|
293 |
+
method,
|
294 |
+
abs_url,
|
295 |
+
headers=headers,
|
296 |
+
data=data,
|
297 |
+
files=files,
|
298 |
+
stream=stream,
|
299 |
+
timeout=TIMEOUT_SECS,
|
300 |
+
)
|
301 |
+
except requests.exceptions.RequestException as e:
|
302 |
+
raise error.APIConnectionError("Error communicating with OpenAI") from e
|
303 |
+
util.log_info(
|
304 |
+
"OpenAI API response",
|
305 |
+
path=abs_url,
|
306 |
+
response_code=result.status_code,
|
307 |
+
processing_ms=result.headers.get("OpenAI-Processing-Ms"),
|
308 |
+
)
|
309 |
+
# Don't read the whole stream for debug logging unless necessary.
|
310 |
+
if openai.log == "debug":
|
311 |
+
util.log_debug(
|
312 |
+
"API response body", body=result.content, headers=result.headers
|
313 |
+
)
|
314 |
+
return result
|
315 |
+
|
316 |
+
def _interpret_response(
|
317 |
+
self, result: requests.Response, stream: bool
|
318 |
+
) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool]:
|
319 |
+
"""Returns the response(s) and a bool indicating whether it is a stream."""
|
320 |
+
if stream and "text/event-stream" in result.headers.get("Content-Type", ""):
|
321 |
+
return (
|
322 |
+
self._interpret_response_line(
|
323 |
+
line, result.status_code, result.headers, stream=True
|
324 |
+
)
|
325 |
+
for line in parse_stream(result.iter_lines())
|
326 |
+
), True
|
327 |
+
else:
|
328 |
+
return (
|
329 |
+
self._interpret_response_line(
|
330 |
+
result.content, result.status_code, result.headers, stream=False
|
331 |
+
),
|
332 |
+
False,
|
333 |
+
)
|
334 |
+
|
335 |
+
def _interpret_response_line(
|
336 |
+
self, rbody, rcode, rheaders, stream: bool
|
337 |
+
) -> OpenAIResponse:
|
338 |
+
# HTTP 204 response code does not have any content in the body.
|
339 |
+
if rcode == 204:
|
340 |
+
return OpenAIResponse(None, rheaders)
|
341 |
+
|
342 |
+
if rcode == 503:
|
343 |
+
raise error.ServiceUnavailableError(
|
344 |
+
"The server is overloaded or not ready yet.",
|
345 |
+
rbody,
|
346 |
+
rcode,
|
347 |
+
headers=rheaders,
|
348 |
+
)
|
349 |
+
try:
|
350 |
+
if hasattr(rbody, "decode"):
|
351 |
+
rbody = rbody.decode("utf-8")
|
352 |
+
data = json.loads(rbody)
|
353 |
+
except (JSONDecodeError, UnicodeDecodeError):
|
354 |
+
raise error.APIError(
|
355 |
+
f"HTTP code {rcode} from API ({rbody})", rbody, rcode, headers=rheaders
|
356 |
+
)
|
357 |
+
resp = OpenAIResponse(data, rheaders)
|
358 |
+
# In the future, we might add a "status" parameter to errors
|
359 |
+
# to better handle the "error while streaming" case.
|
360 |
+
stream_error = stream and "error" in resp.data
|
361 |
+
if stream_error or not 200 <= rcode < 300:
|
362 |
+
raise self.handle_error_response(
|
363 |
+
rbody, rcode, resp.data, rheaders, stream_error=stream_error
|
364 |
+
)
|
365 |
+
return resp
|
openai/api_resources/__init__.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from openai.api_resources.answer import Answer # noqa: F401
|
2 |
+
from openai.api_resources.classification import Classification # noqa: F401
|
3 |
+
from openai.api_resources.completion import Completion # noqa: F401
|
4 |
+
from openai.api_resources.customer import Customer # noqa: F401
|
5 |
+
from openai.api_resources.edit import Edit # noqa: F401
|
6 |
+
from openai.api_resources.deployment import Deployment # noqa: F401
|
7 |
+
from openai.api_resources.embedding import Embedding # noqa: F401
|
8 |
+
from openai.api_resources.engine import Engine # noqa: F401
|
9 |
+
from openai.api_resources.error_object import ErrorObject # noqa: F401
|
10 |
+
from openai.api_resources.file import File # noqa: F401
|
11 |
+
from openai.api_resources.fine_tune import FineTune # noqa: F401
|
12 |
+
from openai.api_resources.model import Model # noqa: F401
|
13 |
+
from openai.api_resources.search import Search # noqa: F401
|
openai/api_resources/__pycache__/__init__.cpython-39.pyc
ADDED
Binary file (1.01 kB). View file
|
|
openai/api_resources/__pycache__/answer.cpython-39.pyc
ADDED
Binary file (736 Bytes). View file
|
|
openai/api_resources/__pycache__/classification.cpython-39.pyc
ADDED
Binary file (776 Bytes). View file
|
|
openai/api_resources/__pycache__/completion.cpython-39.pyc
ADDED
Binary file (1.52 kB). View file
|
|
openai/api_resources/__pycache__/customer.cpython-39.pyc
ADDED
Binary file (795 Bytes). View file
|
|
openai/api_resources/__pycache__/deployment.cpython-39.pyc
ADDED
Binary file (2.26 kB). View file
|
|
openai/api_resources/__pycache__/edit.cpython-39.pyc
ADDED
Binary file (1.28 kB). View file
|
|
openai/api_resources/__pycache__/embedding.cpython-39.pyc
ADDED
Binary file (1.82 kB). View file
|
|
openai/api_resources/__pycache__/engine.cpython-39.pyc
ADDED
Binary file (1.69 kB). View file
|
|
openai/api_resources/__pycache__/error_object.cpython-39.pyc
ADDED
Binary file (856 Bytes). View file
|
|
openai/api_resources/__pycache__/file.cpython-39.pyc
ADDED
Binary file (2.98 kB). View file
|
|
openai/api_resources/__pycache__/fine_tune.cpython-39.pyc
ADDED
Binary file (2.39 kB). View file
|
|
openai/api_resources/__pycache__/model.cpython-39.pyc
ADDED
Binary file (513 Bytes). View file
|
|
openai/api_resources/__pycache__/search.cpython-39.pyc
ADDED
Binary file (1.38 kB). View file
|
|
openai/api_resources/abstract/__init__.py
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# flake8: noqa
|
2 |
+
|
3 |
+
from openai.api_resources.abstract.api_resource import APIResource
|
4 |
+
from openai.api_resources.abstract.createable_api_resource import CreateableAPIResource
|
5 |
+
from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource
|
6 |
+
from openai.api_resources.abstract.listable_api_resource import ListableAPIResource
|
7 |
+
from openai.api_resources.abstract.nested_resource_class_methods import (
|
8 |
+
nested_resource_class_methods,
|
9 |
+
)
|
10 |
+
from openai.api_resources.abstract.updateable_api_resource import UpdateableAPIResource
|
openai/api_resources/abstract/__pycache__/__init__.cpython-39.pyc
ADDED
Binary file (797 Bytes). View file
|
|
openai/api_resources/abstract/__pycache__/api_resource.cpython-39.pyc
ADDED
Binary file (3.04 kB). View file
|
|
openai/api_resources/abstract/__pycache__/createable_api_resource.cpython-39.pyc
ADDED
Binary file (1.36 kB). View file
|
|
openai/api_resources/abstract/__pycache__/deletable_api_resource.cpython-39.pyc
ADDED
Binary file (1.25 kB). View file
|
|
openai/api_resources/abstract/__pycache__/engine_api_resource.cpython-39.pyc
ADDED
Binary file (4.25 kB). View file
|
|
openai/api_resources/abstract/__pycache__/listable_api_resource.cpython-39.pyc
ADDED
Binary file (1.54 kB). View file
|
|
openai/api_resources/abstract/__pycache__/nested_resource_class_methods.cpython-39.pyc
ADDED
Binary file (3.37 kB). View file
|
|
openai/api_resources/abstract/__pycache__/updateable_api_resource.cpython-39.pyc
ADDED
Binary file (770 Bytes). View file
|
|
openai/api_resources/abstract/api_resource.py
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from urllib.parse import quote_plus
|
2 |
+
|
3 |
+
import openai
|
4 |
+
from openai import api_requestor, error, util
|
5 |
+
from openai.openai_object import OpenAIObject
|
6 |
+
from openai.util import ApiType
|
7 |
+
|
8 |
+
|
9 |
+
class APIResource(OpenAIObject):
|
10 |
+
api_prefix = ""
|
11 |
+
azure_api_prefix = "openai"
|
12 |
+
azure_deployments_prefix = "deployments"
|
13 |
+
|
14 |
+
@classmethod
|
15 |
+
def retrieve(cls, id, api_key=None, request_id=None, **params):
|
16 |
+
instance = cls(id, api_key, **params)
|
17 |
+
instance.refresh(request_id=request_id)
|
18 |
+
return instance
|
19 |
+
|
20 |
+
def refresh(self, request_id=None):
|
21 |
+
self.refresh_from(
|
22 |
+
self.request("get", self.instance_url(), request_id=request_id)
|
23 |
+
)
|
24 |
+
return self
|
25 |
+
|
26 |
+
@classmethod
|
27 |
+
def class_url(cls):
|
28 |
+
if cls == APIResource:
|
29 |
+
raise NotImplementedError(
|
30 |
+
"APIResource is an abstract class. You should perform actions on its subclasses."
|
31 |
+
)
|
32 |
+
# Namespaces are separated in object names with periods (.) and in URLs
|
33 |
+
# with forward slashes (/), so replace the former with the latter.
|
34 |
+
base = cls.OBJECT_NAME.replace(".", "/") # type: ignore
|
35 |
+
if cls.api_prefix:
|
36 |
+
return "/%s/%s" % (cls.api_prefix, base)
|
37 |
+
return "/%s" % (base)
|
38 |
+
|
39 |
+
def instance_url(self, operation=None):
|
40 |
+
id = self.get("id")
|
41 |
+
|
42 |
+
if not isinstance(id, str):
|
43 |
+
raise error.InvalidRequestError(
|
44 |
+
"Could not determine which URL to request: %s instance "
|
45 |
+
"has invalid ID: %r, %s. ID should be of type `str` (or"
|
46 |
+
" `unicode`)" % (type(self).__name__, id, type(id)),
|
47 |
+
"id",
|
48 |
+
)
|
49 |
+
api_version = self.api_version or openai.api_version
|
50 |
+
extn = quote_plus(id)
|
51 |
+
|
52 |
+
if self.typed_api_type == ApiType.AZURE:
|
53 |
+
if not api_version:
|
54 |
+
raise error.InvalidRequestError(
|
55 |
+
"An API version is required for the Azure API type."
|
56 |
+
)
|
57 |
+
|
58 |
+
if not operation:
|
59 |
+
base = self.class_url()
|
60 |
+
return "/%s%s/%s?api-version=%s" % (
|
61 |
+
self.azure_api_prefix,
|
62 |
+
base,
|
63 |
+
extn,
|
64 |
+
api_version
|
65 |
+
)
|
66 |
+
|
67 |
+
return "/%s/%s/%s/%s?api-version=%s" % (
|
68 |
+
self.azure_api_prefix,
|
69 |
+
self.azure_deployments_prefix,
|
70 |
+
extn,
|
71 |
+
operation,
|
72 |
+
api_version
|
73 |
+
)
|
74 |
+
|
75 |
+
|
76 |
+
elif self.typed_api_type == ApiType.OPEN_AI:
|
77 |
+
base = self.class_url()
|
78 |
+
return "%s/%s" % (base, extn)
|
79 |
+
|
80 |
+
else:
|
81 |
+
raise error.InvalidAPIType("Unsupported API type %s" % self.api_type)
|
82 |
+
|
83 |
+
# The `method_` and `url_` arguments are suffixed with an underscore to
|
84 |
+
# avoid conflicting with actual request parameters in `params`.
|
85 |
+
@classmethod
|
86 |
+
def _static_request(
|
87 |
+
cls,
|
88 |
+
method_,
|
89 |
+
url_,
|
90 |
+
api_key=None,
|
91 |
+
api_base=None,
|
92 |
+
api_type=None,
|
93 |
+
request_id=None,
|
94 |
+
api_version=None,
|
95 |
+
organization=None,
|
96 |
+
**params,
|
97 |
+
):
|
98 |
+
requestor = api_requestor.APIRequestor(
|
99 |
+
api_key,
|
100 |
+
api_version=api_version,
|
101 |
+
organization=organization,
|
102 |
+
api_base=api_base,
|
103 |
+
api_type=api_type
|
104 |
+
)
|
105 |
+
response, _, api_key = requestor.request(
|
106 |
+
method_, url_, params, request_id=request_id
|
107 |
+
)
|
108 |
+
return util.convert_to_openai_object(
|
109 |
+
response, api_key, api_version, organization
|
110 |
+
)
|
111 |
+
|
112 |
+
@classmethod
|
113 |
+
def _get_api_type_and_version(cls, api_type: str, api_version: str):
|
114 |
+
typed_api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type)
|
115 |
+
typed_api_version = api_version or openai.api_version
|
116 |
+
return (typed_api_type, typed_api_version)
|
117 |
+
|
openai/api_resources/abstract/createable_api_resource.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from openai import api_requestor, util, error
|
2 |
+
from openai.api_resources.abstract.api_resource import APIResource
|
3 |
+
from openai.util import ApiType
|
4 |
+
|
5 |
+
|
6 |
+
class CreateableAPIResource(APIResource):
|
7 |
+
plain_old_data = False
|
8 |
+
|
9 |
+
@classmethod
|
10 |
+
def create(
|
11 |
+
cls,
|
12 |
+
api_key=None,
|
13 |
+
api_base=None,
|
14 |
+
api_type=None,
|
15 |
+
request_id=None,
|
16 |
+
api_version=None,
|
17 |
+
organization=None,
|
18 |
+
**params,
|
19 |
+
):
|
20 |
+
requestor = api_requestor.APIRequestor(
|
21 |
+
api_key,
|
22 |
+
api_base=api_base,
|
23 |
+
api_type=api_type,
|
24 |
+
api_version=api_version,
|
25 |
+
organization=organization,
|
26 |
+
)
|
27 |
+
typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
|
28 |
+
|
29 |
+
if typed_api_type == ApiType.AZURE:
|
30 |
+
base = cls.class_url()
|
31 |
+
url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version)
|
32 |
+
elif typed_api_type == ApiType.OPEN_AI:
|
33 |
+
url = cls.class_url()
|
34 |
+
else:
|
35 |
+
raise error.InvalidAPIType('Unsupported API type %s' % api_type)
|
36 |
+
|
37 |
+
response, _, api_key = requestor.request(
|
38 |
+
"post", url, params, request_id=request_id
|
39 |
+
)
|
40 |
+
|
41 |
+
return util.convert_to_openai_object(
|
42 |
+
response,
|
43 |
+
api_key,
|
44 |
+
api_version,
|
45 |
+
organization,
|
46 |
+
plain_old_data=cls.plain_old_data,
|
47 |
+
)
|
openai/api_resources/abstract/deletable_api_resource.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from urllib.parse import quote_plus
|
2 |
+
|
3 |
+
from openai import error
|
4 |
+
from openai.api_resources.abstract.api_resource import APIResource
|
5 |
+
from openai.util import ApiType
|
6 |
+
|
7 |
+
class DeletableAPIResource(APIResource):
|
8 |
+
@classmethod
|
9 |
+
def delete(cls, sid, api_type=None, api_version=None, **params):
|
10 |
+
if isinstance(cls, APIResource):
|
11 |
+
raise ValueError(".delete may only be called as a class method now.")
|
12 |
+
|
13 |
+
base = cls.class_url()
|
14 |
+
extn = quote_plus(sid)
|
15 |
+
|
16 |
+
typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
|
17 |
+
if typed_api_type == ApiType.AZURE:
|
18 |
+
url = "/%s%s/%s?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version)
|
19 |
+
elif typed_api_type == ApiType.OPEN_AI:
|
20 |
+
url = "%s/%s" % (base, extn)
|
21 |
+
else:
|
22 |
+
raise error.InvalidAPIType('Unsupported API type %s' % api_type)
|
23 |
+
|
24 |
+
return cls._static_request("delete", url, api_type=api_type, api_version=api_version, **params)
|
openai/api_resources/abstract/engine_api_resource.py
ADDED
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import time
|
2 |
+
from pydoc import apropos
|
3 |
+
from typing import Optional
|
4 |
+
from urllib.parse import quote_plus
|
5 |
+
|
6 |
+
import openai
|
7 |
+
from openai import api_requestor, error, util
|
8 |
+
from openai.api_resources.abstract.api_resource import APIResource
|
9 |
+
from openai.openai_response import OpenAIResponse
|
10 |
+
from openai.util import ApiType
|
11 |
+
|
12 |
+
MAX_TIMEOUT = 20
|
13 |
+
|
14 |
+
|
15 |
+
class EngineAPIResource(APIResource):
|
16 |
+
engine_required = True
|
17 |
+
plain_old_data = False
|
18 |
+
|
19 |
+
def __init__(self, engine: Optional[str] = None, **kwargs):
|
20 |
+
super().__init__(engine=engine, **kwargs)
|
21 |
+
|
22 |
+
@classmethod
|
23 |
+
def class_url(
|
24 |
+
cls,
|
25 |
+
engine: Optional[str] = None,
|
26 |
+
api_type: Optional[str] = None,
|
27 |
+
api_version: Optional[str] = None,
|
28 |
+
):
|
29 |
+
# Namespaces are separated in object names with periods (.) and in URLs
|
30 |
+
# with forward slashes (/), so replace the former with the latter.
|
31 |
+
base = cls.OBJECT_NAME.replace(".", "/") # type: ignore
|
32 |
+
typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
|
33 |
+
|
34 |
+
if typed_api_type == ApiType.AZURE:
|
35 |
+
if not api_version:
|
36 |
+
raise error.InvalidRequestError(
|
37 |
+
"An API version is required for the Azure API type."
|
38 |
+
)
|
39 |
+
if engine is None:
|
40 |
+
raise error.InvalidRequestError(
|
41 |
+
"You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service"
|
42 |
+
)
|
43 |
+
extn = quote_plus(engine)
|
44 |
+
return "/%s/%s/%s/%s?api-version=%s" % (
|
45 |
+
cls.azure_api_prefix,
|
46 |
+
cls.azure_deployments_prefix,
|
47 |
+
extn,
|
48 |
+
base,
|
49 |
+
api_version
|
50 |
+
)
|
51 |
+
|
52 |
+
elif typed_api_type == ApiType.OPEN_AI:
|
53 |
+
if engine is None:
|
54 |
+
return "/%s" % (base)
|
55 |
+
|
56 |
+
extn = quote_plus(engine)
|
57 |
+
return "/engines/%s/%s" % (extn, base)
|
58 |
+
|
59 |
+
else:
|
60 |
+
raise error.InvalidAPIType("Unsupported API type %s" % api_type)
|
61 |
+
|
62 |
+
@classmethod
|
63 |
+
def create(
|
64 |
+
cls,
|
65 |
+
api_key=None,
|
66 |
+
api_base=None,
|
67 |
+
api_type=None,
|
68 |
+
request_id=None,
|
69 |
+
api_version=None,
|
70 |
+
organization=None,
|
71 |
+
**params,
|
72 |
+
):
|
73 |
+
engine = params.pop("engine", None)
|
74 |
+
timeout = params.pop("timeout", None)
|
75 |
+
stream = params.get("stream", False)
|
76 |
+
headers = params.pop("headers", None)
|
77 |
+
if engine is None and cls.engine_required:
|
78 |
+
raise error.InvalidRequestError(
|
79 |
+
"Must provide an 'engine' parameter to create a %s" % cls, "engine"
|
80 |
+
)
|
81 |
+
|
82 |
+
if timeout is None:
|
83 |
+
# No special timeout handling
|
84 |
+
pass
|
85 |
+
elif timeout > 0:
|
86 |
+
# API only supports timeouts up to MAX_TIMEOUT
|
87 |
+
params["timeout"] = min(timeout, MAX_TIMEOUT)
|
88 |
+
timeout = (timeout - params["timeout"]) or None
|
89 |
+
elif timeout == 0:
|
90 |
+
params["timeout"] = MAX_TIMEOUT
|
91 |
+
|
92 |
+
requestor = api_requestor.APIRequestor(
|
93 |
+
api_key,
|
94 |
+
api_base=api_base,
|
95 |
+
api_type=api_type,
|
96 |
+
api_version=api_version,
|
97 |
+
organization=organization,
|
98 |
+
)
|
99 |
+
url = cls.class_url(engine, api_type, api_version)
|
100 |
+
response, _, api_key = requestor.request(
|
101 |
+
"post",
|
102 |
+
url,
|
103 |
+
params=params,
|
104 |
+
headers=headers,
|
105 |
+
stream=stream,
|
106 |
+
request_id=request_id,
|
107 |
+
)
|
108 |
+
|
109 |
+
if stream:
|
110 |
+
assert not isinstance(response, OpenAIResponse) # must be an iterator
|
111 |
+
return (
|
112 |
+
util.convert_to_openai_object(
|
113 |
+
line,
|
114 |
+
api_key,
|
115 |
+
api_version,
|
116 |
+
organization,
|
117 |
+
engine=engine,
|
118 |
+
plain_old_data=cls.plain_old_data,
|
119 |
+
)
|
120 |
+
for line in response
|
121 |
+
)
|
122 |
+
else:
|
123 |
+
obj = util.convert_to_openai_object(
|
124 |
+
response,
|
125 |
+
api_key,
|
126 |
+
api_version,
|
127 |
+
organization,
|
128 |
+
engine=engine,
|
129 |
+
plain_old_data=cls.plain_old_data,
|
130 |
+
)
|
131 |
+
|
132 |
+
if timeout is not None:
|
133 |
+
obj.wait(timeout=timeout or None)
|
134 |
+
|
135 |
+
return obj
|
136 |
+
|
137 |
+
def instance_url(self):
|
138 |
+
id = self.get("id")
|
139 |
+
|
140 |
+
if not isinstance(id, str):
|
141 |
+
raise error.InvalidRequestError(
|
142 |
+
f"Could not determine which URL to request: {type(self).__name__} instance has invalid ID: {id}, {type(id)}. ID should be of type str.",
|
143 |
+
"id",
|
144 |
+
)
|
145 |
+
|
146 |
+
extn = quote_plus(id)
|
147 |
+
params_connector = '?'
|
148 |
+
|
149 |
+
if self.typed_api_type == ApiType.AZURE:
|
150 |
+
api_version = self.api_version or openai.api_version
|
151 |
+
if not api_version:
|
152 |
+
raise error.InvalidRequestError(
|
153 |
+
"An API version is required for the Azure API type."
|
154 |
+
)
|
155 |
+
base = self.OBJECT_NAME.replace(".", "/")
|
156 |
+
url = "/%s/%s/%s/%s/%s?api-version=%s" % (
|
157 |
+
self.azure_api_prefix,
|
158 |
+
self.azure_deployments_prefix,
|
159 |
+
self.engine,
|
160 |
+
base,
|
161 |
+
extn,
|
162 |
+
api_version
|
163 |
+
)
|
164 |
+
params_connector = '&'
|
165 |
+
|
166 |
+
|
167 |
+
elif self.typed_api_type == ApiType.OPEN_AI:
|
168 |
+
base = self.class_url(self.engine, self.api_type, self.api_version)
|
169 |
+
url = "%s/%s" % (base, extn)
|
170 |
+
|
171 |
+
else:
|
172 |
+
raise error.InvalidAPIType("Unsupported API type %s" % self.api_type)
|
173 |
+
|
174 |
+
timeout = self.get("timeout")
|
175 |
+
if timeout is not None:
|
176 |
+
timeout = quote_plus(str(timeout))
|
177 |
+
url += params_connector + "timeout={}".format(timeout)
|
178 |
+
return url
|
179 |
+
|
180 |
+
def wait(self, timeout=None):
|
181 |
+
start = time.time()
|
182 |
+
while self.status != "complete":
|
183 |
+
self.timeout = (
|
184 |
+
min(timeout + start - time.time(), MAX_TIMEOUT)
|
185 |
+
if timeout is not None
|
186 |
+
else MAX_TIMEOUT
|
187 |
+
)
|
188 |
+
if self.timeout < 0:
|
189 |
+
del self.timeout
|
190 |
+
break
|
191 |
+
self.refresh()
|
192 |
+
return self
|
openai/api_resources/abstract/listable_api_resource.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from openai import api_requestor, util, error
|
2 |
+
from openai.api_resources.abstract.api_resource import APIResource
|
3 |
+
from openai.util import ApiType
|
4 |
+
|
5 |
+
|
6 |
+
class ListableAPIResource(APIResource):
|
7 |
+
@classmethod
|
8 |
+
def auto_paging_iter(cls, *args, **params):
|
9 |
+
return cls.list(*args, **params).auto_paging_iter()
|
10 |
+
|
11 |
+
@classmethod
|
12 |
+
def list(
|
13 |
+
cls,
|
14 |
+
api_key=None,
|
15 |
+
request_id=None,
|
16 |
+
api_version=None,
|
17 |
+
organization=None,
|
18 |
+
api_base=None,
|
19 |
+
api_type=None,
|
20 |
+
**params,
|
21 |
+
):
|
22 |
+
requestor = api_requestor.APIRequestor(
|
23 |
+
api_key,
|
24 |
+
api_base=api_base or cls.api_base(),
|
25 |
+
api_version=api_version,
|
26 |
+
api_type=api_type,
|
27 |
+
organization=organization,
|
28 |
+
)
|
29 |
+
|
30 |
+
typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version)
|
31 |
+
|
32 |
+
if typed_api_type == ApiType.AZURE:
|
33 |
+
base = cls.class_url()
|
34 |
+
url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version)
|
35 |
+
elif typed_api_type == ApiType.OPEN_AI:
|
36 |
+
url = cls.class_url()
|
37 |
+
else:
|
38 |
+
raise error.InvalidAPIType('Unsupported API type %s' % api_type)
|
39 |
+
|
40 |
+
response, _, api_key = requestor.request(
|
41 |
+
"get", url, params, request_id=request_id
|
42 |
+
)
|
43 |
+
openai_object = util.convert_to_openai_object(
|
44 |
+
response, api_key, api_version, organization
|
45 |
+
)
|
46 |
+
openai_object._retrieve_params = params
|
47 |
+
return openai_object
|
openai/api_resources/abstract/nested_resource_class_methods.py
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from urllib.parse import quote_plus
|
2 |
+
|
3 |
+
from openai import api_requestor, util
|
4 |
+
|
5 |
+
|
6 |
+
def nested_resource_class_methods(
|
7 |
+
resource, path=None, operations=None, resource_plural=None
|
8 |
+
):
|
9 |
+
if resource_plural is None:
|
10 |
+
resource_plural = "%ss" % resource
|
11 |
+
if path is None:
|
12 |
+
path = resource_plural
|
13 |
+
if operations is None:
|
14 |
+
raise ValueError("operations list required")
|
15 |
+
|
16 |
+
def wrapper(cls):
|
17 |
+
def nested_resource_url(cls, id, nested_id=None):
|
18 |
+
url = "%s/%s/%s" % (cls.class_url(), quote_plus(id), quote_plus(path))
|
19 |
+
if nested_id is not None:
|
20 |
+
url += "/%s" % quote_plus(nested_id)
|
21 |
+
return url
|
22 |
+
|
23 |
+
resource_url_method = "%ss_url" % resource
|
24 |
+
setattr(cls, resource_url_method, classmethod(nested_resource_url))
|
25 |
+
|
26 |
+
def nested_resource_request(
|
27 |
+
cls,
|
28 |
+
method,
|
29 |
+
url,
|
30 |
+
api_key=None,
|
31 |
+
request_id=None,
|
32 |
+
api_version=None,
|
33 |
+
organization=None,
|
34 |
+
**params,
|
35 |
+
):
|
36 |
+
requestor = api_requestor.APIRequestor(
|
37 |
+
api_key, api_version=api_version, organization=organization
|
38 |
+
)
|
39 |
+
response, _, api_key = requestor.request(
|
40 |
+
method, url, params, request_id=request_id
|
41 |
+
)
|
42 |
+
return util.convert_to_openai_object(
|
43 |
+
response, api_key, api_version, organization
|
44 |
+
)
|
45 |
+
|
46 |
+
resource_request_method = "%ss_request" % resource
|
47 |
+
setattr(cls, resource_request_method, classmethod(nested_resource_request))
|
48 |
+
|
49 |
+
for operation in operations:
|
50 |
+
if operation == "create":
|
51 |
+
|
52 |
+
def create_nested_resource(cls, id, **params):
|
53 |
+
url = getattr(cls, resource_url_method)(id)
|
54 |
+
return getattr(cls, resource_request_method)("post", url, **params)
|
55 |
+
|
56 |
+
create_method = "create_%s" % resource
|
57 |
+
setattr(cls, create_method, classmethod(create_nested_resource))
|
58 |
+
|
59 |
+
elif operation == "retrieve":
|
60 |
+
|
61 |
+
def retrieve_nested_resource(cls, id, nested_id, **params):
|
62 |
+
url = getattr(cls, resource_url_method)(id, nested_id)
|
63 |
+
return getattr(cls, resource_request_method)("get", url, **params)
|
64 |
+
|
65 |
+
retrieve_method = "retrieve_%s" % resource
|
66 |
+
setattr(cls, retrieve_method, classmethod(retrieve_nested_resource))
|
67 |
+
|
68 |
+
elif operation == "update":
|
69 |
+
|
70 |
+
def modify_nested_resource(cls, id, nested_id, **params):
|
71 |
+
url = getattr(cls, resource_url_method)(id, nested_id)
|
72 |
+
return getattr(cls, resource_request_method)("post", url, **params)
|
73 |
+
|
74 |
+
modify_method = "modify_%s" % resource
|
75 |
+
setattr(cls, modify_method, classmethod(modify_nested_resource))
|
76 |
+
|
77 |
+
elif operation == "delete":
|
78 |
+
|
79 |
+
def delete_nested_resource(cls, id, nested_id, **params):
|
80 |
+
url = getattr(cls, resource_url_method)(id, nested_id)
|
81 |
+
return getattr(cls, resource_request_method)(
|
82 |
+
"delete", url, **params
|
83 |
+
)
|
84 |
+
|
85 |
+
delete_method = "delete_%s" % resource
|
86 |
+
setattr(cls, delete_method, classmethod(delete_nested_resource))
|
87 |
+
|
88 |
+
elif operation == "list":
|
89 |
+
|
90 |
+
def list_nested_resources(cls, id, **params):
|
91 |
+
url = getattr(cls, resource_url_method)(id)
|
92 |
+
return getattr(cls, resource_request_method)("get", url, **params)
|
93 |
+
|
94 |
+
list_method = "list_%s" % resource_plural
|
95 |
+
setattr(cls, list_method, classmethod(list_nested_resources))
|
96 |
+
|
97 |
+
else:
|
98 |
+
raise ValueError("Unknown operation: %s" % operation)
|
99 |
+
|
100 |
+
return cls
|
101 |
+
|
102 |
+
return wrapper
|
openai/api_resources/abstract/updateable_api_resource.py
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from urllib.parse import quote_plus
|
2 |
+
|
3 |
+
from openai.api_resources.abstract.api_resource import APIResource
|
4 |
+
|
5 |
+
|
6 |
+
class UpdateableAPIResource(APIResource):
|
7 |
+
@classmethod
|
8 |
+
def modify(cls, sid, **params):
|
9 |
+
url = "%s/%s" % (cls.class_url(), quote_plus(sid))
|
10 |
+
return cls._static_request("post", url, **params)
|
openai/api_resources/answer.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from openai.openai_object import OpenAIObject
|
2 |
+
|
3 |
+
|
4 |
+
class Answer(OpenAIObject):
|
5 |
+
@classmethod
|
6 |
+
def get_url(self):
|
7 |
+
return "/answers"
|
8 |
+
|
9 |
+
@classmethod
|
10 |
+
def create(cls, **params):
|
11 |
+
instance = cls()
|
12 |
+
return instance.request("post", cls.get_url(), params)
|
openai/api_resources/classification.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from openai.openai_object import OpenAIObject
|
2 |
+
|
3 |
+
|
4 |
+
class Classification(OpenAIObject):
|
5 |
+
@classmethod
|
6 |
+
def get_url(self):
|
7 |
+
return "/classifications"
|
8 |
+
|
9 |
+
@classmethod
|
10 |
+
def create(cls, **params):
|
11 |
+
instance = cls()
|
12 |
+
return instance.request("post", cls.get_url(), params)
|