@@ -22,12 +22,12 @@ import (
22
22
"io/ioutil"
23
23
"net/http"
24
24
"sort"
25
+ "strconv"
25
26
"strings"
26
27
"time"
27
28
28
29
"github.com/cortexlabs/cortex/cli/types/cliconfig"
29
30
"github.com/cortexlabs/cortex/pkg/consts"
30
- "github.com/cortexlabs/cortex/pkg/lib/cast"
31
31
"github.com/cortexlabs/cortex/pkg/lib/console"
32
32
"github.com/cortexlabs/cortex/pkg/lib/errors"
33
33
"github.com/cortexlabs/cortex/pkg/lib/json"
@@ -70,8 +70,8 @@ func realtimeAPITable(realtimeAPI schema.APIResponse, env cliconfig.Environment)
70
70
71
71
out += fmt .Sprintf ("\n %s curl %s -X POST -H \" Content-Type: application/json\" -d @sample.json\n " , console .Bold ("example curl:" ), realtimeAPI .Endpoint )
72
72
73
- if realtimeAPI .Spec .Predictor .Type == userconfig .TensorFlowPredictorType || realtimeAPI .Spec .Predictor .Type == userconfig . ONNXPredictorType {
74
- out += "\n " + describeModelInput (realtimeAPI .Status , realtimeAPI .Endpoint )
73
+ if ! ( realtimeAPI .Spec .Predictor .Type == userconfig .PythonPredictorType && realtimeAPI .Spec .Predictor .ModelPath == nil && realtimeAPI . Spec . Predictor . Models == nil ) {
74
+ out += "\n " + describeModelInput (realtimeAPI .Status , realtimeAPI .Spec . Predictor , realtimeAPI . Endpoint )
75
75
}
76
76
77
77
out += titleStr ("configuration" ) + strings .TrimSpace (realtimeAPI .Spec .UserStr (env .Provider ))
@@ -232,67 +232,40 @@ func classificationMetricsStr(metrics *metrics.Metrics) string {
232
232
return out
233
233
}
234
234
235
- func describeModelInput (status * status.Status , apiEndpoint string ) string {
235
+ func describeModelInput (status * status.Status , predictor * userconfig. Predictor , apiEndpoint string ) string {
236
236
if status .Updated .Ready + status .Stale .Ready == 0 {
237
- return "the model's input schema will be available when the api is live\n "
237
+ return "the models' metadata schema will be available when the api is live\n "
238
238
}
239
239
240
- apiSummary , err := getAPISummary (apiEndpoint )
241
- if err != nil {
242
- return "error retrieving the model's input schema: " + errors .Message (err ) + "\n "
243
- }
244
-
245
- numRows := 0
246
- for _ , inputSignatures := range apiSummary .ModelSignatures {
247
- numRows += len (inputSignatures )
248
- }
249
-
250
- usesDefaultModel := false
251
- rows := make ([][]interface {}, numRows )
252
- rowNum := 0
253
- for modelName , inputSignatures := range apiSummary .ModelSignatures {
254
- for inputName , inputSignature := range inputSignatures {
255
- shapeStr := make ([]string , len (inputSignature .Shape ))
256
- for idx , dim := range inputSignature .Shape {
257
- shapeStr [idx ] = s .ObjFlatNoQuotes (dim )
258
- }
259
-
260
- shapeRowEntry := ""
261
- if len (shapeStr ) == 1 && shapeStr [0 ] == "scalar" {
262
- shapeRowEntry = "scalar"
263
- } else if len (shapeStr ) == 1 && shapeStr [0 ] == "unknown" {
264
- shapeRowEntry = "unknown"
265
- } else {
266
- shapeRowEntry = "(" + strings .Join (shapeStr , ", " ) + ")"
267
- }
268
- rows [rowNum ] = []interface {}{
269
- modelName ,
270
- inputName ,
271
- inputSignature .Type ,
272
- shapeRowEntry ,
273
- }
274
- rowNum ++
240
+ cachingEnabled := predictor .Models != nil && predictor .Models .CacheSize != nil && predictor .Models .DiskCacheSize != nil
241
+ if predictor .Type == userconfig .TensorFlowPredictorType && ! cachingEnabled {
242
+ apiTFLiveReloadingSummary , err := getAPITFLiveReloadingSummary (apiEndpoint )
243
+ if err != nil {
244
+ return "error retrieving the models' metadata schema: " + errors .Message (err ) + "\n "
275
245
}
276
- if modelName == consts .SingleModelName {
277
- usesDefaultModel = true
246
+ t , err := parseAPITFLiveReloadingSummary (apiTFLiveReloadingSummary )
247
+ if err != nil {
248
+ return "error retrieving the model's input schema: " + errors .Message (err ) + "\n "
278
249
}
250
+ return t
279
251
}
280
252
281
- inputTitle := "input"
282
- if usesDefaultModel {
283
- inputTitle = "model input "
253
+ apiModelSummary , err := getAPIModelSummary ( apiEndpoint )
254
+ if err != nil {
255
+ return "error retrieving the models' metadata schema: " + errors . Message ( err ) + " \n "
284
256
}
285
- t := table.Table {
286
- Headers : []table.Header {
287
- {Title : "model name" , MaxWidth : 32 , Hidden : usesDefaultModel },
288
- {Title : inputTitle , MaxWidth : 32 },
289
- {Title : "type" , MaxWidth : 10 },
290
- {Title : "shape" , MaxWidth : 20 },
291
- },
292
- Rows : rows ,
257
+ t , err := parseAPIModelSummary (apiModelSummary )
258
+ if err != nil {
259
+ return "error retrieving the models' metadata schema: " + errors .Message (err ) + "\n "
293
260
}
261
+ return t
262
+ }
294
263
295
- return t .MustFormat ()
264
+ func getModelFromModelID (modelID string ) (modelName string , modelVersion int64 , err error ) {
265
+ splitIndex := strings .LastIndex (modelID , "-" )
266
+ modelName = modelID [:splitIndex ]
267
+ modelVersion , err = strconv .ParseInt (modelID [splitIndex + 1 :], 10 , 64 )
268
+ return
296
269
}
297
270
298
271
func makeRequest (request * http.Request ) (http.Header , []byte , error ) {
@@ -324,7 +297,26 @@ func makeRequest(request *http.Request) (http.Header, []byte, error) {
324
297
return response .Header , bodyBytes , nil
325
298
}
326
299
327
- func getAPISummary (apiEndpoint string ) (* schema.APISummary , error ) {
300
+ func getAPIModelSummary (apiEndpoint string ) (* schema.APIModelSummary , error ) {
301
+ req , err := http .NewRequest ("GET" , apiEndpoint , nil )
302
+ if err != nil {
303
+ return nil , errors .Wrap (err , "unable to request api summary" )
304
+ }
305
+ req .Header .Set ("Content-Type" , "application/json" )
306
+ _ , response , err := makeRequest (req )
307
+ if err != nil {
308
+ return nil , err
309
+ }
310
+
311
+ var apiModelSummary schema.APIModelSummary
312
+ err = json .DecodeWithNumber (response , & apiModelSummary )
313
+ if err != nil {
314
+ return nil , errors .Wrap (err , "unable to parse api summary response" )
315
+ }
316
+ return & apiModelSummary , nil
317
+ }
318
+
319
+ func getAPITFLiveReloadingSummary (apiEndpoint string ) (* schema.APITFLiveReloadingSummary , error ) {
328
320
req , err := http .NewRequest ("GET" , apiEndpoint , nil )
329
321
if err != nil {
330
322
return nil , errors .Wrap (err , "unable to request api summary" )
@@ -335,17 +327,179 @@ func getAPISummary(apiEndpoint string) (*schema.APISummary, error) {
335
327
return nil , err
336
328
}
337
329
338
- var apiSummary schema.APISummary
339
- err = json .DecodeWithNumber (response , & apiSummary )
330
+ var apiTFLiveReloadingSummary schema.APITFLiveReloadingSummary
331
+ err = json .DecodeWithNumber (response , & apiTFLiveReloadingSummary )
340
332
if err != nil {
341
333
return nil , errors .Wrap (err , "unable to parse api summary response" )
342
334
}
335
+ return & apiTFLiveReloadingSummary , nil
336
+ }
343
337
344
- for _ , inputSignatures := range apiSummary .ModelSignatures {
345
- for _ , inputSignature := range inputSignatures {
346
- inputSignature .Shape = cast .JSONNumbers (inputSignature .Shape )
338
+ func parseAPIModelSummary (summary * schema.APIModelSummary ) (string , error ) {
339
+ rows := make ([][]interface {}, 0 )
340
+
341
+ for modelName , modelMetadata := range summary .ModelMetadata {
342
+ latestVersion := int64 (0 )
343
+ for _ , version := range modelMetadata .Versions {
344
+ v , err := strconv .ParseInt (version , 10 , 64 )
345
+ if err != nil {
346
+ return "" , err
347
+ }
348
+ if v > latestVersion {
349
+ latestVersion = v
350
+ }
347
351
}
352
+ latestStrVersion := strconv .FormatInt (latestVersion , 10 )
353
+
354
+ for idx , version := range modelMetadata .Versions {
355
+ var latestTag string
356
+ if latestStrVersion == version {
357
+ latestTag = " (latest)"
358
+ }
359
+
360
+ timestamp := modelMetadata .Timestamps [idx ]
361
+ date := time .Unix (timestamp , 0 )
362
+
363
+ rows = append (rows , []interface {}{
364
+ modelName ,
365
+ version + latestTag ,
366
+ date .Format (_timeFormat ),
367
+ })
368
+ }
369
+ }
370
+
371
+ _ , usesCortexDefaultModelName := summary .ModelMetadata [consts .SingleModelName ]
372
+
373
+ t := table.Table {
374
+ Headers : []table.Header {
375
+ {
376
+ Title : "model name" ,
377
+ MaxWidth : 32 ,
378
+ Hidden : usesCortexDefaultModelName ,
379
+ },
380
+ {
381
+ Title : "model version" ,
382
+ MaxWidth : 25 ,
383
+ },
384
+ {
385
+ Title : "edit time" ,
386
+ MaxWidth : 32 ,
387
+ },
388
+ },
389
+ Rows : rows ,
390
+ }
391
+
392
+ return t .MustFormat (), nil
393
+ }
394
+
395
+ func parseAPITFLiveReloadingSummary (summary * schema.APITFLiveReloadingSummary ) (string , error ) {
396
+ latestVersions := make (map [string ]int64 )
397
+
398
+ numRows := 0
399
+ models := make (map [string ]schema.GenericModelMetadata , 0 )
400
+ for modelID , modelMetadata := range summary .ModelMetadata {
401
+ timestamp := modelMetadata .Timestamp
402
+ modelName , modelVersion , err := getModelFromModelID (modelID )
403
+ if err != nil {
404
+ return "" , err
405
+ }
406
+ if _ , ok := models [modelName ]; ! ok {
407
+ models [modelName ] = schema.GenericModelMetadata {
408
+ Versions : []string {strconv .FormatInt (modelVersion , 10 )},
409
+ Timestamps : []int64 {timestamp },
410
+ }
411
+ } else {
412
+ model := models [modelName ]
413
+ model .Versions = append (model .Versions , strconv .FormatInt (modelVersion , 10 ))
414
+ model .Timestamps = append (model .Timestamps , timestamp )
415
+ models [modelName ] = model
416
+ }
417
+ if _ , ok := latestVersions [modelName ]; ! ok {
418
+ latestVersions [modelName ] = modelVersion
419
+ } else if modelVersion > latestVersions [modelName ] {
420
+ latestVersions [modelName ] = modelVersion
421
+ }
422
+ numRows += len (modelMetadata .InputSignatures )
423
+ }
424
+
425
+ rows := make ([][]interface {}, 0 , numRows )
426
+ for modelName , model := range models {
427
+ latestVersion := latestVersions [modelName ]
428
+
429
+ for _ , modelVersion := range model .Versions {
430
+ modelID := fmt .Sprintf ("%s-%s" , modelName , modelVersion )
431
+
432
+ inputSignatures := summary .ModelMetadata [modelID ].InputSignatures
433
+ timestamp := summary .ModelMetadata [modelID ].Timestamp
434
+ versionInt , err := strconv .ParseInt (modelVersion , 10 , 64 )
435
+ if err != nil {
436
+ return "" , err
437
+ }
438
+
439
+ var applicableTags string
440
+ if versionInt == latestVersion {
441
+ applicableTags = " (latest)"
442
+ }
443
+
444
+ date := time .Unix (timestamp , 0 )
445
+
446
+ for inputName , inputSignature := range inputSignatures {
447
+ shapeStr := make ([]string , len (inputSignature .Shape ))
448
+ for idx , dim := range inputSignature .Shape {
449
+ shapeStr [idx ] = s .ObjFlatNoQuotes (dim )
450
+ }
451
+ shapeRowEntry := ""
452
+ if len (shapeStr ) == 1 && shapeStr [0 ] == "scalar" {
453
+ shapeRowEntry = "scalar"
454
+ } else if len (shapeStr ) == 1 && shapeStr [0 ] == "unknown" {
455
+ shapeRowEntry = "unknown"
456
+ } else {
457
+ shapeRowEntry = "(" + strings .Join (shapeStr , ", " ) + ")"
458
+ }
459
+ rows = append (rows , []interface {}{
460
+ modelName ,
461
+ modelVersion + applicableTags ,
462
+ inputName ,
463
+ inputSignature .Type ,
464
+ shapeRowEntry ,
465
+ date .Format (_timeFormat ),
466
+ })
467
+ }
468
+ }
469
+ }
470
+
471
+ _ , usesCortexDefaultModelName := summary .ModelMetadata [consts .SingleModelName ]
472
+
473
+ t := table.Table {
474
+ Headers : []table.Header {
475
+ {
476
+ Title : "model name" ,
477
+ MaxWidth : 32 ,
478
+ Hidden : usesCortexDefaultModelName ,
479
+ },
480
+ {
481
+ Title : "model version" ,
482
+ MaxWidth : 25 ,
483
+ },
484
+ {
485
+ Title : "model input" ,
486
+ MaxWidth : 32 ,
487
+ },
488
+ {
489
+ Title : "type" ,
490
+ MaxWidth : 10 ,
491
+ },
492
+ {
493
+ Title : "shape" ,
494
+ MaxWidth : 20 ,
495
+ },
496
+ {
497
+ Title : "edit time" ,
498
+ MaxWidth : 32 ,
499
+ },
500
+ },
501
+ Rows : rows ,
348
502
}
349
503
350
- return & apiSummary , nil
504
+ return t . MustFormat () , nil
351
505
}
0 commit comments