1
+ import datetime
1
2
import json
3
+ import os
4
+ import signal
2
5
import sys
3
6
import warnings
4
7
@@ -205,21 +208,34 @@ def list(cls, args):
205
208
print (file )
206
209
207
210
208
- class FineTuneCLI :
211
+ class FineTune :
209
212
@classmethod
210
213
def list (cls , args ):
211
214
resp = openai .FineTune .list ()
212
215
print (resp )
213
216
217
+ @classmethod
218
+ def _get_or_upload (cls , file ):
219
+ try :
220
+ openai .File .retrieve (file )
221
+ except openai .error .InvalidRequestError as e :
222
+ if e .http_status == 404 and os .path .isfile (file ):
223
+ resp = openai .File .create (file = open (file ), purpose = "fine-tune" )
224
+ sys .stdout .write (
225
+ "Uploaded file from {file}: {id}\n " .format (file = file , id = resp ["id" ])
226
+ )
227
+ return resp ["id" ]
228
+ return file
229
+
214
230
@classmethod
215
231
def create (cls , args ):
216
232
create_args = {
217
- "train_file " : args .train_file ,
233
+ "training_file " : cls . _get_or_upload ( args .training_file ) ,
218
234
}
219
- if args .test_file :
220
- create_args ["test_file " ] = args .test_file
221
- if args .base_model :
222
- create_args ["base_model " ] = args .base_model
235
+ if args .validation_file :
236
+ create_args ["validation_file " ] = cls . _get_or_upload ( args .validation_file )
237
+ if args .model :
238
+ create_args ["model " ] = args .model
223
239
if args .hparams :
224
240
try :
225
241
hparams = json .loads (args .hparams )
@@ -231,7 +247,35 @@ def create(cls, args):
231
247
create_args .update (hparams )
232
248
233
249
resp = openai .FineTune .create (** create_args )
234
- print (resp )
250
+
251
+ if args .no_wait :
252
+ print (resp )
253
+ return
254
+
255
+ sys .stdout .write (
256
+ "Created job: {job_id}\n "
257
+ "Streaming events until the job is complete...\n \n "
258
+ "(Ctrl-C will interrupt the stream, but not cancel the job)\n " .format (
259
+ job_id = resp ["id" ]
260
+ )
261
+ )
262
+ cls ._stream_events (resp ["id" ])
263
+
264
+ resp = openai .FineTune .retrieve (id = resp ["id" ])
265
+ status = resp ["status" ]
266
+ sys .stdout .write ("\n Job complete! Status: {status}" .format (status = status ))
267
+ if status == "succeeded" :
268
+ sys .stdout .write (" 🎉" )
269
+ sys .stdout .write (
270
+ "\n Try out your fine-tuned model: {model}\n "
271
+ "(Pass this as the model parameter to a completion request)" .format (
272
+ model = resp ["fine_tuned_model" ]
273
+ )
274
+ )
275
+ # TODO(rachel): Print instructions on how to use the model here.
276
+ elif status == "failed" :
277
+ sys .
stdout .
write (
"\n Please contact [email protected] for assistance." )
278
+ sys .stdout .write ("\n " )
235
279
236
280
@classmethod
237
281
def get (cls , args ):
@@ -240,8 +284,39 @@ def get(cls, args):
240
284
241
285
@classmethod
242
286
def events (cls , args ):
243
- resp = openai .FineTune .list_events (id = args .id )
244
- print (resp )
287
+ if not args .stream :
288
+ resp = openai .FineTune .list_events (id = args .id )
289
+ print (resp )
290
+ return
291
+ cls ._stream_events (args .id )
292
+
293
+ @classmethod
294
+ def _stream_events (cls , job_id ):
295
+ def signal_handler (sig , frame ):
296
+ status = openai .FineTune .retrieve (job_id ).status
297
+ sys .stdout .write (
298
+ "\n Stream interrupted. Job is still {status}. "
299
+ "To cancel your job, run:\n "
300
+ "`openai api fine_tunes.cancel -i {job_id}`\n " .format (
301
+ status = status , job_id = job_id
302
+ )
303
+ )
304
+ sys .exit (0 )
305
+
306
+ signal .signal (signal .SIGINT , signal_handler )
307
+
308
+ events = openai .FineTune .stream_events (job_id )
309
+ # TODO(rachel): Add a nifty spinner here.
310
+ for event in events :
311
+ sys .stdout .write (
312
+ "[%s] %s"
313
+ % (
314
+ datetime .datetime .fromtimestamp (event ["created_at" ]),
315
+ event ["message" ],
316
+ )
317
+ )
318
+ sys .stdout .write ("\n " )
319
+ sys .stdout .flush ()
245
320
246
321
@classmethod
247
322
def cancel (cls , args ):
@@ -436,27 +511,52 @@ def help(args):
436
511
437
512
# Finetune
438
513
sub = subparsers .add_parser ("fine_tunes.list" )
439
- sub .set_defaults (func = FineTuneCLI .list )
514
+ sub .set_defaults (func = FineTune .list )
440
515
441
516
sub = subparsers .add_parser ("fine_tunes.create" )
442
- sub .add_argument ("-t" , "--train_file" , required = True , help = "File to train" )
443
- sub .add_argument ("--test_file" , help = "File to test" )
444
517
sub .add_argument (
445
- "-b" ,
446
- "--base_model" ,
447
- help = "The model name to start the run from" ,
518
+ "-t" ,
519
+ "--training_file" ,
520
+ required = True ,
521
+ help = "JSONL file containing prompt-completion examples for training. This can "
522
+ "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345) "
523
+ "or a local file path." ,
524
+ )
525
+ sub .add_argument (
526
+ "-v" ,
527
+ "--validation_file" ,
528
+ help = "JSONL file containing prompt-completion examples for validation. This can "
529
+ "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345) "
530
+ "or a local file path." ,
531
+ )
532
+ sub .add_argument (
533
+ "-m" ,
534
+ "--model" ,
535
+ help = "The model to start fine-tuning from" ,
536
+ )
537
+ sub .add_argument (
538
+ "--no_wait" ,
539
+ action = "store_true" ,
540
+ help = "If set, returns immediately after creating the job. Otherwise, waits for the job to complete." ,
448
541
)
449
542
sub .add_argument ("-p" , "--hparams" , help = "Hyperparameter JSON" )
450
- sub .set_defaults (func = FineTuneCLI .create )
543
+ sub .set_defaults (func = FineTune .create )
451
544
452
545
sub = subparsers .add_parser ("fine_tunes.get" )
453
546
sub .add_argument ("-i" , "--id" , required = True , help = "The id of the fine-tune job" )
454
- sub .set_defaults (func = FineTuneCLI .get )
547
+ sub .set_defaults (func = FineTune .get )
455
548
456
549
sub = subparsers .add_parser ("fine_tunes.events" )
457
550
sub .add_argument ("-i" , "--id" , required = True , help = "The id of the fine-tune job" )
458
- sub .set_defaults (func = FineTuneCLI .events )
551
+ sub .add_argument (
552
+ "-s" ,
553
+ "--stream" ,
554
+ action = "store_true" ,
555
+ help = "If set, events will be streamed until the job is done. Otherwise, "
556
+ "displays the event history to date." ,
557
+ )
558
+ sub .set_defaults (func = FineTune .events )
459
559
460
560
sub = subparsers .add_parser ("fine_tunes.cancel" )
461
561
sub .add_argument ("-i" , "--id" , required = True , help = "The id of the fine-tune job" )
462
- sub .set_defaults (func = FineTuneCLI .cancel )
562
+ sub .set_defaults (func = FineTune .cancel )
0 commit comments