From 090fde07d7a013b9be845df5d68b4bbda61d9a28 Mon Sep 17 00:00:00 2001 From: Rachel Lim Date: Thu, 20 May 2021 15:22:58 -0700 Subject: [PATCH 1/5] CLI cleanup * Also support uploading files as a convenience to the user --- openai/cli.py | 59 ++++++++++++++++++++++++++++++++++------------- openai/version.py | 4 ++++ 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/openai/cli.py b/openai/cli.py index fb210c507e..68187f4ee5 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -1,4 +1,5 @@ import json +import os import sys import warnings @@ -205,21 +206,34 @@ def list(cls, args): print(file) -class FineTuneCLI: +class FineTune: @classmethod def list(cls, args): resp = openai.FineTune.list() print(resp) + @classmethod + def _get_or_upload(cls, file): + try: + openai.File.retrieve(file) + except openai.error.InvalidRequestError as e: + if e.http_status == 404 and os.path.isfile(file): + resp = openai.File.create(file=open(file), purpose="fine-tune") + sys.stdout.write( + "Uploaded file from {file}: {id}\n".format(file=file, id=resp["id"]) + ) + return resp["id"] + return file + @classmethod def create(cls, args): create_args = { - "train_file": args.train_file, + "training_file": cls._get_or_upload(args.training_file), } - if args.test_file: - create_args["test_file"] = args.test_file - if args.base_model: - create_args["base_model"] = args.base_model + if args.validation_file: + create_args["validation_file"] = cls._get_or_upload(args.validation_file) + if args.model: + create_args["model"] = args.model if args.hparams: try: hparams = json.loads(args.hparams) @@ -436,27 +450,40 @@ def help(args): # Finetune sub = subparsers.add_parser("fine_tunes.list") - sub.set_defaults(func=FineTuneCLI.list) + sub.set_defaults(func=FineTune.list) sub = subparsers.add_parser("fine_tunes.create") - sub.add_argument("-t", "--train_file", required=True, help="File to train") - sub.add_argument("--test_file", help="File to test") sub.add_argument( - "-b", - "--base_model", - help="The model name to start the run from", + "-t", + "--training_file", + required=True, + help="JSONL file containing prompt-completion examples for training. This can " + "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345) " + "or a local file path.", + ) + sub.add_argument( + "-v", + "--validation_file", + help="JSONL file containing prompt-completion examples for validation. This can " + "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345) " + "or a local file path.", + ) + sub.add_argument( + "-m", + "--model", + help="The model to start fine-tuning from", ) sub.add_argument("-p", "--hparams", help="Hyperparameter JSON") - sub.set_defaults(func=FineTuneCLI.create) + sub.set_defaults(func=FineTune.create) sub = subparsers.add_parser("fine_tunes.get") sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTuneCLI.get) + sub.set_defaults(func=FineTune.get) sub = subparsers.add_parser("fine_tunes.events") sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTuneCLI.events) + sub.set_defaults(func=FineTune.events) sub = subparsers.add_parser("fine_tunes.cancel") sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTuneCLI.cancel) + sub.set_defaults(func=FineTune.cancel) diff --git a/openai/version.py b/openai/version.py index df1c617d09..0c2054fe1d 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1,5 @@ +<<<<<<< HEAD VERSION = "0.6.3" +======= +VERSION = "0.7.0" +>>>>>>> a6e1129... CLI cleanup (#22) From 1857d01cd83e99e52d974adb2cfbb67c14918249 Mon Sep 17 00:00:00 2001 From: Rachel Lim Date: Thu, 20 May 2021 16:39:59 -0700 Subject: [PATCH 2/5] Events in the CLI (#23) * Events in the CLI * Update message about ctrl-c --- openai/api_resources/fine_tune.py | 40 +++++++++++++++- openai/cli.py | 79 +++++++++++++++++++++++++++++-- openai/util.py | 1 + 3 files changed, 115 insertions(+), 5 deletions(-) diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index da888a17d8..029e6606c4 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -4,7 +4,7 @@ nested_resource_class_methods, ) from openai.six.moves.urllib.parse import quote_plus -from openai import util +from openai import api_requestor, util @nested_resource_class_methods("event", operations=["list"]) @@ -18,4 +18,40 @@ def cancel(cls, id, api_key=None, request_id=None, **params): url = "%s/%s/cancel" % (base, extn) instance = cls(id, api_key, **params) headers = util.populate_headers(request_id=request_id) - return instance.request("post", url, headers=headers) \ No newline at end of file + return instance.request("post", url, headers=headers) + + @classmethod + def stream_events( + cls, + id, + api_key=None, + api_base=None, + request_id=None, + api_version=None, + organization=None, + **params + ): + base = cls.class_url() + extn = quote_plus(id) + + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base, + api_version=api_version, + organization=organization, + ) + url = "%s/%s/events?stream=true" % (base, extn) + headers = util.populate_headers(request_id=request_id) + response, _, api_key = requestor.request( + "get", url, params, headers=headers, stream=True + ) + + return ( + util.convert_to_openai_object( + line, + api_key, + api_version, + organization, + ) + for line in response + ) diff --git a/openai/cli.py b/openai/cli.py index 68187f4ee5..3a644f4f05 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -1,5 +1,7 @@ +import datetime import json import os +import signal import sys import warnings @@ -245,7 +247,35 @@ def create(cls, args): create_args.update(hparams) resp = openai.FineTune.create(**create_args) - print(resp) + + if args.no_wait: + print(resp) + return + + sys.stdout.write( + "Created job: {job_id}\n" + "Streaming events until the job is complete...\n\n" + "(Ctrl-C will interrupt the stream, but not cancel the job)\n".format( + job_id=resp["id"] + ) + ) + cls._stream_events(resp["id"]) + + resp = openai.FineTune.retrieve(id=resp["id"]) + status = resp["status"] + sys.stdout.write("\nJob complete! Status: {status}".format(status=status)) + if status == "succeeded": + sys.stdout.write(" 🎉") + sys.stdout.write( + "\nTry out your fine-tuned model: {model}\n" + "(Pass this as the model parameter to a completion request)".format( + model=resp["fine_tuned_model"] + ) + ) + # TODO(rachel): Print instructions on how to use the model here. + elif status == "failed": + sys.stdout.write("\nPlease contact support@openai.com for assistance.") + sys.stdout.write("\n") @classmethod def get(cls, args): @@ -254,8 +284,39 @@ def get(cls, args): @classmethod def events(cls, args): - resp = openai.FineTune.list_events(id=args.id) - print(resp) + if not args.stream: + resp = openai.FineTune.list_events(id=args.id) + print(resp) + return + cls._stream_events(args.id) + + @classmethod + def _stream_events(cls, job_id): + def signal_handler(sig, frame): + status = openai.FineTune.retrieve(job_id).status + sys.stdout.write( + "\nStream interrupted. Job is still {status}. " + "To cancel your job, run:\n" + "`openai api fine_tunes.cancel -i {job_id}`\n".format( + status=status, job_id=job_id + ) + ) + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + + events = openai.FineTune.stream_events(job_id) + # TODO(rachel): Add a nifty spinner here. + for event in events: + sys.stdout.write( + "[%s] %s" + % ( + datetime.datetime.fromtimestamp(event["created_at"]), + event["message"], + ) + ) + sys.stdout.write("\n") + sys.stdout.flush() @classmethod def cancel(cls, args): @@ -473,6 +534,11 @@ def help(args): "--model", help="The model to start fine-tuning from", ) + sub.add_argument( + "--no_wait", + action="store_true", + help="If set, returns immediately after creating the job. Otherwise, waits for the job to complete.", + ) sub.add_argument("-p", "--hparams", help="Hyperparameter JSON") sub.set_defaults(func=FineTune.create) @@ -482,6 +548,13 @@ def help(args): sub = subparsers.add_parser("fine_tunes.events") sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.add_argument( + "-s", + "--stream", + action="store_true", + help="If set, events will be streamed until the job is done. Otherwise, " + "displays the event history to date.", + ) sub.set_defaults(func=FineTune.events) sub = subparsers.add_parser("fine_tunes.cancel") diff --git a/openai/util.py b/openai/util.py index 7295710c44..aef35e412a 100644 --- a/openai/util.py +++ b/openai/util.py @@ -64,6 +64,7 @@ def log_info(message, **params): print(msg, file=sys.stderr) logger.info(msg) + def log_warn(message, **params): msg = logfmt(dict(message=message, **params)) print(msg, file=sys.stderr) From a57c3788e10cc2dccaecc5cbaa0a6c19209ec6a3 Mon Sep 17 00:00:00 2001 From: Rachel Lim Date: Thu, 20 May 2021 16:42:56 -0700 Subject: [PATCH 3/5] Version --- openai/version.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openai/version.py b/openai/version.py index 0c2054fe1d..f40610919c 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1,5 +1 @@ -<<<<<<< HEAD -VERSION = "0.6.3" -======= VERSION = "0.7.0" ->>>>>>> a6e1129... CLI cleanup (#22) From 47e49885b8b6b3a560e1bc56d14639a136047475 Mon Sep 17 00:00:00 2001 From: hallacy Date: Mon, 12 Apr 2021 12:20:28 -0700 Subject: [PATCH 4/5] Forgot to use the api_base arg (#20) * Forgot to use the api_base arg * Bump version --- openai/api_resources/file.py | 2 +- openai/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index ec3d31cf85..7b9a03a56c 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -17,7 +17,7 @@ def create( ): requestor = api_requestor.APIRequestor( api_key, - api_base=openai.file_api_base or openai.api_base, + api_base=api_base or openai.file_api_base or openai.api_base, api_version=api_version, organization=organization, ) diff --git a/openai/version.py b/openai/version.py index f40610919c..70e7925a43 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.7.0" +VERSION = "0.6.4" \ No newline at end of file From 4c3de5a0f76f54bd35f19656ba2edd9ddd59cf16 Mon Sep 17 00:00:00 2001 From: Rachel Lim Date: Thu, 20 May 2021 17:56:36 -0700 Subject: [PATCH 5/5] newline --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index 70e7925a43..451e5a51b3 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.6.4" \ No newline at end of file +VERSION = "0.6.4"