Skip to content

Commit 3c6d4cd

Browse files
committed
Initial commit
0 parents  commit 3c6d4cd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+8074
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.egg-info
2+
__pycache__
3+
/public/dist

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) OpenAI (https://openai.com)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

Makefile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.PHONY: build upload
2+
3+
build:
4+
python setup.py sdist
5+
6+
upload:
7+
twine upload dist/openai-*.tar.gz
8+
rm dist/openai-*.tar.gz
9+

README.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# OpenAI Python Library
2+
3+
The OpenAI Python library provides convenient access to the OpenAI API
4+
from applications written in the Python language. It includes a
5+
pre-defined set of classes for API resources that initialize
6+
themselves dynamically from API responses which makes it compatible
7+
with a wide range of versions of the OpenAI API.
8+
9+
This library additionally provides an `openai` command-line utility
10+
which makes it easy to interact with the API from your terminal. Run
11+
`openai api -h` for usage.
12+
13+
## Documentation
14+
15+
See the [OpenAI API docs](https://beta.openai.com/docs/api-reference?lang=python). (During
16+
the beta, you'll need to be signed into your account to see them.)
17+
18+
## Installation
19+
20+
You don't need this source code unless you want to modify the package. If you just
21+
want to use the package, just run:
22+
23+
```sh
24+
pip install --upgrade openai
25+
```
26+
27+
Install from source with:
28+
29+
```sh
30+
python setup.py install
31+
```
32+
33+
## Requirements
34+
35+
- Python 2.7+ or Python 3.4+ (PyPy supported)
36+
37+
In general we want to support the versions of Python that our
38+
customers are using, so if you run into issues with any version
39+
issues, please let us know at [email protected].
40+
41+
## Credit
42+
43+
This library is forked from the [Stripe Python Library](https://github.com/stripe/stripe-python).

bin/openai

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env python
2+
import argparse
3+
import json
4+
import logging
5+
import os
6+
import sys
7+
8+
import openai
9+
from openai.cli import display_error
10+
from openai.cli import register as api_register
11+
12+
logger = logging.getLogger()
13+
formatter = logging.Formatter("[%(asctime)s] %(message)s")
14+
handler = logging.StreamHandler(sys.stderr)
15+
handler.setFormatter(formatter)
16+
logger.addHandler(handler)
17+
18+
19+
def main():
20+
parser = argparse.ArgumentParser(description=None)
21+
parser.add_argument(
22+
"-v",
23+
"--verbose",
24+
action="count",
25+
dest="verbosity",
26+
default=0,
27+
help="Set verbosity.",
28+
)
29+
parser.add_argument("-b", "--api-base", help="What API base url to use.")
30+
parser.add_argument("-k", "--api-key", help="What API key to use.")
31+
parser.add_argument(
32+
"-o",
33+
"--organization",
34+
help="Which organization to run as (will use your default organization if not specified)",
35+
)
36+
37+
def help(args):
38+
parser.print_help()
39+
40+
parser.set_defaults(func=help)
41+
42+
subparsers = parser.add_subparsers()
43+
sub = subparsers.add_parser("api", help="Direct API calls")
44+
45+
api_register(sub)
46+
47+
args = parser.parse_args()
48+
if args.verbosity == 1:
49+
logger.setLevel(logging.INFO)
50+
elif args.verbosity >= 2:
51+
logger.setLevel(logging.DEBUG)
52+
53+
openai.debug = True
54+
if args.api_key is not None:
55+
openai.api_key = args.api_key
56+
if args.api_base is not None:
57+
openai.api_base = args.api_base
58+
if args.organization is not None:
59+
openai.organization = args.organization
60+
61+
try:
62+
args.func(args)
63+
except openai.error.OpenAIError as e:
64+
display_error(e)
65+
except KeyboardInterrupt:
66+
sys.stderr.write("\n")
67+
return 0
68+
69+
70+
if __name__ == "__main__":
71+
sys.exit(main())

examples/semanticsearch/README.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# semanticsearch
2+
3+
A client-side implementation of our semantic search endpoint (https://beta.openai.com/docs/api-reference/search).
4+
5+
Our endpoint has a special fast implementation of this logic which
6+
makes it very fast for calls involving many documents, so we recommend
7+
using our implementation rather than this one for latency-sensitive
8+
workloads.
9+
10+
We encourage you to try different variants of this client-side logic
11+
-- we don't think our setup is likely optimal at all!
12+
13+
## Sample usage
14+
15+
The following usage will run a client-side semantic search. This
16+
formats each document into a prompt asking the API for the document's
17+
relevance, and then post-processes the logprobs to derive relevance
18+
scores:
19+
20+
```
21+
$ ./semanticsearch.py -q 'positive emotion' -d happy -d sad
22+
[client-side semantic search] {'object': 'list', 'data': [{'object': 'search_result', 'document': 0, 'score': 204.448}, {'object': 'search_result', 'document': 1, 'score': 108.208}], 'model': 'ada:2020-05-03'}
23+
```
24+
25+
We run the exact same logic server-side:
26+
27+
```
28+
$ ./semanticsearch.py -q 'positive emotion' -d happy -d sad -s
29+
[server-side semantic search] {'object': 'list', 'data': [{'object': 'search_result', 'document': 0, 'score': 204.448}, {'object': 'search_result', 'document': 1, 'score': 108.208}], 'model': 'ada:2020-05-03'}
30+
```
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#!/usr/bin/env python
2+
import openai
3+
import argparse
4+
import logging
5+
import sys
6+
from typing import List
7+
8+
logger = logging.getLogger()
9+
formatter = logging.Formatter("[%(asctime)s] [%(process)d] %(message)s")
10+
handler = logging.StreamHandler(sys.stderr)
11+
handler.setFormatter(formatter)
12+
logger.addHandler(handler)
13+
14+
DEFAULT_COND_LOGP_TEMPLATE = (
15+
"<|endoftext|>{document}\n\n---\n\nThe above passage is related to: {query}"
16+
)
17+
SCORE_MULTIPLIER = 100.0
18+
19+
20+
class SearchScorer:
21+
def __init__(
22+
self, *, document, query, cond_logp_template=DEFAULT_COND_LOGP_TEMPLATE
23+
):
24+
self.document = document
25+
self.query = query
26+
self.cond_logp_template = cond_logp_template
27+
self.context = self.cond_logp_template.format(
28+
document=self.document, query=self.query
29+
)
30+
31+
def get_context(self):
32+
return self.context
33+
34+
def get_score(self, choice) -> float:
35+
assert choice.text == self.context
36+
logprobs: List[float] = choice.logprobs.token_logprobs
37+
text = choice.logprobs.tokens
38+
text_len = sum(len(token) for token in text)
39+
if text_len != len(self.context):
40+
raise RuntimeError(
41+
f"text_len={text_len}, len(self.context)={len(self.context)}"
42+
)
43+
total_len = 0
44+
last_used = len(text)
45+
while total_len < len(self.query):
46+
assert last_used > 0
47+
total_len += len(text[last_used - 1])
48+
last_used -= 1
49+
max_len = len(self.context) - self.cond_logp_template.index("{document}")
50+
assert total_len + len(self.document) <= max_len
51+
logits: List[float] = logprobs[last_used:]
52+
return sum(logits) / len(logits) * SCORE_MULTIPLIER
53+
54+
55+
def semantic_search(engine, query, documents):
56+
# add empty document as baseline
57+
scorers = [
58+
SearchScorer(document=document, query=query) for document in [""] + documents
59+
]
60+
completion = openai.Completion.create(
61+
engine=engine,
62+
prompt=[scorer.get_context() for scorer in scorers],
63+
max_tokens=0,
64+
logprobs=0,
65+
echo=True,
66+
)
67+
# put the documents back in order so we can easily normalize by the empty document 0
68+
data = sorted(completion.choices, key=lambda choice: choice.index)
69+
assert len(scorers) == len(
70+
data
71+
), f"len(scorers)={len(scorers)} len(data)={len(data)}"
72+
scores = [scorer.get_score(choice) for scorer, choice in zip(scorers, data)]
73+
# subtract score for empty document
74+
scores = [score - scores[0] for score in scores][1:]
75+
data = {
76+
"object": "list",
77+
"data": [
78+
{
79+
"object": "search_result",
80+
"document": document_idx,
81+
"score": round(score, 3),
82+
}
83+
for document_idx, score in enumerate(scores)
84+
],
85+
"model": completion.model,
86+
}
87+
return data
88+
89+
90+
def main():
91+
parser = argparse.ArgumentParser(description=None)
92+
parser.add_argument(
93+
"-v",
94+
"--verbose",
95+
action="count",
96+
dest="verbosity",
97+
default=0,
98+
help="Set verbosity.",
99+
)
100+
parser.add_argument("-e", "--engine", default="ada")
101+
parser.add_argument("-q", "--query", required=True)
102+
parser.add_argument("-d", "--document", action="append", required=True)
103+
parser.add_argument("-s", "--server-side", action="store_true")
104+
args = parser.parse_args()
105+
106+
if args.verbosity == 1:
107+
logger.setLevel(logging.INFO)
108+
elif args.verbosity >= 2:
109+
logger.setLevel(logging.DEBUG)
110+
111+
if args.server_side:
112+
resp = openai.Engine(id=args.engine).search(
113+
query=args.query, documents=args.document
114+
)
115+
resp = resp.to_dict_recursive()
116+
print(f"[server-side semantic search] {resp}")
117+
else:
118+
resp = semantic_search(args.engine, query=args.query, documents=args.document)
119+
print(f"[client-side semantic search] {resp}")
120+
121+
return 0
122+
123+
124+
if __name__ == "__main__":
125+
sys.exit(main())

openai/__init__.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from __future__ import absolute_import, division, print_function
2+
3+
import os
4+
5+
# OpenAI Python bindings.
6+
#
7+
# Originally forked from the MIT-licensed Stripe Python bindings.
8+
9+
# Configuration variables
10+
11+
api_key = os.environ.get("OPENAI_API_KEY")
12+
organization = os.environ.get("OPENAI_ORGANIZATION")
13+
client_id = None
14+
api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com")
15+
file_api_base = None
16+
api_version = None
17+
verify_ssl_certs = True
18+
proxy = None
19+
default_http_client = None
20+
app_info = None
21+
enable_telemetry = True
22+
max_network_retries = 0
23+
ca_bundle_path = os.path.join(os.path.dirname(__file__), "data/ca-certificates.crt")
24+
debug = False
25+
26+
# Set to either 'debug' or 'info', controls console logging
27+
log = None
28+
29+
# API resources
30+
from openai.api_resources import * # noqa
31+
32+
from openai.error import OpenAIError, APIError, InvalidRequestError

0 commit comments

Comments
 (0)