Skip to content

Instantly share code, notes, and snippets.

@ZiweiXU
Last active March 3, 2023 13:30
Show Gist options
  • Select an option

  • Save ZiweiXU/336cbd03bc8113f9500da5de3d1b3077 to your computer and use it in GitHub Desktop.

Select an option

Save ZiweiXU/336cbd03bc8113f9500da5de3d1b3077 to your computer and use it in GitHub Desktop.
A simple Python client of OpenAI chat completion service (backend of ChatGPT).
#!/usr/bin/env python3
"""
GPT-3.5 client for Python 3.6+.
Usage: python gpt.py [command] [options] [text]
Commands:
another-word Find another word with GPT-3.
qa Ask a single question then exit.
q Alias for qa.
rephrase Rephrase text with GPT-3.
interactive Start an interactive chat with GPT-3.
talk Alias for interactive.
Options:
-h, --help - Print help message.
Examples:
python gpt.py qa "What is the meaning of life?"
python gpt.py rephrase "I am a cat."
python gpt.py another-word "cat"
python gpt.py interactive
python gpt.py talk
Commands for an interactive session:
!help Print this help message.
!quit Exit the program.
!usage Print the total usage in this session.
!clear Clear the context.
!save Save the context to a file.
!load Load the context from a file.
!save_dialog Save the dialog to a file.
All context is autosaved to $HOME/.gpt_cli/autosave.
Copyright (c) 2023 Ziwei Xu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import sys
from sys import exit
import os
import readline
import json
import uuid
from argparse import ArgumentParser
from datetime import datetime
try:
import openai
except ImportError:
print('Please install the OpenAI Python library:')
print('pip install openai')
exit(1)
MODEL = 'gpt-3.5-turbo'
MAX_TOKENS = 3096
USD_PER_TOKEN = 0.000002
CONTEXT_AUTOSAVE = True
CONTEXT_AUTOSAVE_DIR = os.path.join(os.environ['HOME'], '.gpt_cli/autosave')
if CONTEXT_AUTOSAVE:
if not os.path.exists(CONTEXT_AUTOSAVE_DIR):
os.makedirs(CONTEXT_AUTOSAVE_DIR)
try:
openai.api_key = os.environ['OPENAI_API_KEY']
except KeyError:
help_str = """Please set the OPENAI_API_KEY environment variable.
You can get your API key from https://platform.openai.com/account/api-keys
You can set the environment variable by running:
export OPENAI_API_KEY=<your API key>"""
print(help_str)
exit(1)
def timestamp():
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
class GPTClient(object):
def __init__(self) -> None:
self.context = []
self.total_usage = 0
# generate session name
self.session_name = str(uuid.uuid4())
# parse command
parser = ArgumentParser(
description='Chat with GPT-3', usage='%(prog)s [command] [options] [text]')
parser.add_argument(
'command', nargs='?', help='q, qa, rephrase, another-word, interactive, talk')
args = parser.parse_args(sys.argv[1:2])
if not args.command or not hasattr(self, args.command):
print('Missing or unrecognized command')
parser.print_help()
exit(1)
# run command
getattr(self, args.command)()
def q(self):
self.qa()
def qa(self):
parser = ArgumentParser(description='Ask GPT-3 a question')
parser.add_argument(
'text', help='Ask a single question then exit.')
args = parser.parse_args(sys.argv[2:])
self.context.append({"role": "user", "datetime": timestamp(), "content": args.text})
self._chat()
def rephrase(self):
parser = ArgumentParser(description='Rephrase text with GPT-3.')
parser.add_argument('text', help='Text to rephrase')
args = parser.parse_args(sys.argv[2:])
prompt = "Rephrase text. Use British English. Make it fluent and concise. Keep its original meaning. The text is: "
self.context.append({"role": "user", "datetime": timestamp(), "content": f'{prompt} \"{args.text}\"'})
self._chat()
def another_word(self):
parser = ArgumentParser(description='Find another word with GPT-3.')
parser.add_argument('text', help='Text to rephrase')
args = parser.parse_args(sys.argv[2:])
prompt = "Find another word for: "
self.context.append({"role": "user", "datetime": timestamp(), "content": f'{prompt} {args.text}'})
self._chat()
def _save_context(self, fn='context.json', verbose=True):
with open(f'{fn}.json', 'w') as f:
json.dump(self.context, f)
if verbose:
print(f'Context saved to {fn}.json')
def _save_dialog(self, fn='dialog.txt', verbose=True):
with open(f'{fn}.txt', 'w') as f:
for message in self.context:
f.write(f'{message["role"]} ({message["datetime"]}):\n')
for line in message["content"].splitlines():
f.write(f'\t{line}\n')
f.write('\n')
if verbose:
print(f'Context saved to {fn}.txt')
print('Note that this is only intended for viewing.')
def _load_context(self, fn='context.json', verbose=True):
fn = input('Enter filename: ')
with open(f'{fn}', 'r') as f:
self.context = json.load(f)
if verbose:
print(f'Context loaded from {fn}')
def _chat(self):
# keep role and content only
context = [{"role": message["role"], "content": message["content"]}
for message in self.context]
response = openai.ChatCompletion.create(
model=MODEL,
messages=context,
n=1,
max_tokens=MAX_TOKENS,
)
for choice in response['choices']:
print()
print(f'🤖 GPT-3:')
print(choice['message']['content'].lstrip('\n'))
self.context.append({"role": "assistant", "datetime": timestamp(),
"content": choice['message']['content'].lstrip('\n')})
self.total_usage += response['usage']['total_tokens']
# Save context
if CONTEXT_AUTOSAVE:
path = os.path.join(CONTEXT_AUTOSAVE_DIR, self.session_name)
self._save_context(f'{path}', verbose=False)
def __del__(self):
self._print_usage()
def _print_usage(self):
print(f'Session ID {self.session_name}: {self.total_usage} tokens ({USD_PER_TOKEN * self.total_usage:.4f} USD).')
def talk(self):
self.interactive()
def interactive_print_help(self):
help_str = """Commands:
!help Print this help message.
!quit Exit the program.
!usage Print the total usage in this session.
!clear Clear the context.
!save Save the context to a file.
!load Load the context from a file.
!save_dialog Save the dialog to a file for viewing.
"""
print(help_str)
def interactive(self):
print('This is an interactive chat with GPT-3.')
print('Type !help to see a list of commands.')
print('Type !quit or Ctrl-D to exit.')
print()
session_name = input(
f'Name this session or hit ENTER to skip [{self.session_name}]: ')
if session_name:
self.session_name = session_name
command_registry = {
'help': self.interactive_print_help,
'quit': exit,
'usage': self._print_usage,
'clear': lambda: self.context.clear(),
'save': lambda fn=f'{self.session_name}': self._save_context(fn),
'load': self._load_context,
'save_dialog': lambda fn=f'{self.session_name}': self._save_dialog(fn),
}
# The main loop
while True:
print()
text = input(f'🧠 You: ')
# Check for commands
if text.startswith('!'):
command = text[1:].split(' ')[0]
if command in command_registry:
command_registry[command]()
else:
print('Unrecognized command.')
continue
# Add message to context
self.context.append(
{"role": "user", "datetime": timestamp(), "content": text})
self._chat()
if __name__ == '__main__':
try:
GPTClient()
except EOFError:
exit(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment