diff --git a/docs/commands.md b/docs/commands.md index cc9639c64..49674914a 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -216,6 +216,10 @@ An array of alternating key-value pairs as follows: 1. **BACKEND**: the backend used by the model as a String 1. **DEVICE**: the device used to execute the model as a String 1. **TAG**: the model's tag as a String +1. **BATCHSIZE**: The maximum size of any batch of incoming requests. If `BATCHSIZE` is equal to 0 each incoming request is served immediately. When `BATCHSIZE` is greater than 0, the engine will batch incoming requests from multiple clients that use the model with input tensors of the same shape. +1. **MINBATCHSIZE**: The minimum size of any batch of incoming requests. +1. **INPUTS**: array reply with one or more names of the model's input nodes (applicable only for TensorFlow models) +1. **OUTPUTS**: array reply with one or more names of the model's output nodes (applicable only for TensorFlow models) 1. **BLOB**: a blob containing the serialized model (when called with the `BLOB` argument) as a String **Examples** @@ -224,12 +228,21 @@ Assuming that your model is stored under the 'mymodel' key, you can obtain its m ``` redis> AI.MODELGET mymodel META -1) "backend" -2) TF -3) "device" -4) CPU -5) "tag" -6) imagenet:5.0 + 1) "backend" + 2) "TF" + 3) "device" + 4) "CPU" + 5) "tag" + 6) "imagenet:5.0" + 7) "batchsize" + 8) (integer) 0 + 9) "minbatchsize" +10) (integer) 0 +11) "inputs" +12) 1) "a" + 2) "b" +13) "outputs" +14) 1) "c" ``` You can also save it to the local file 'model.ext' with [`redis-cli`](https://redis.io/topics/cli) like so: diff --git a/src/redisai.c b/src/redisai.c index 17c6c6286..fe812fc57 100644 --- a/src/redisai.c +++ b/src/redisai.c @@ -368,7 +368,7 @@ int RedisAI_ModelGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, RAI_Model *mto; RedisModuleKey *key; - const int status = RAI_GetModelFromKeyspace( ctx, argv[1], &key, &mto, REDISMODULE_READ | REDISMODULE_WRITE); + const int status = RAI_GetModelFromKeyspace( ctx, argv[1], &key, &mto, REDISMODULE_READ ); if (status == REDISMODULE_ERR) { return REDISMODULE_ERR; } @@ -418,12 +418,12 @@ int RedisAI_ModelGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, return REDISMODULE_OK; } - int outentries = blob ? 8 : 6; + const int outentries = blob ? 16 : 14; RedisModule_ReplyWithArray(ctx, outentries); RedisModule_ReplyWithCString(ctx, "backend"); - const char* backendstr = RAI_BackendName(mto->backend); + const char *backendstr = RAI_BackendName(mto->backend); RedisModule_ReplyWithCString(ctx, backendstr); RedisModule_ReplyWithCString(ctx, "device"); @@ -432,6 +432,28 @@ int RedisAI_ModelGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, RedisModule_ReplyWithCString(ctx, "tag"); RedisModule_ReplyWithCString(ctx, mto->tag ? mto->tag : ""); + RedisModule_ReplyWithCString(ctx, "batchsize"); + RedisModule_ReplyWithLongLong(ctx, (long)mto->opts.batchsize); + + RedisModule_ReplyWithCString(ctx, "minbatchsize"); + RedisModule_ReplyWithLongLong(ctx, (long)mto->opts.minbatchsize); + + RedisModule_ReplyWithCString(ctx, "inputs"); + const size_t ninputs = array_len(mto->inputs); + RedisModule_ReplyWithArray(ctx, (long)ninputs); + + for (size_t i = 0; i < ninputs; i++) { + RedisModule_ReplyWithCString(ctx, mto->inputs[i]); + } + + RedisModule_ReplyWithCString(ctx, "outputs"); + const size_t noutputs = array_len(mto->outputs); + RedisModule_ReplyWithArray(ctx, (long)noutputs); + + for (size_t i = 0; i < noutputs; i++) { + RedisModule_ReplyWithCString(ctx, mto->outputs[i]); + } + if (meta && blob) { RedisModule_ReplyWithCString(ctx, "blob"); RedisModule_ReplyWithStringBuffer(ctx, buffer, len); diff --git a/test/tests_onnx.py b/test/tests_onnx.py index b8933f878..291c0f696 100644 --- a/test/tests_onnx.py +++ b/test/tests_onnx.py @@ -36,21 +36,27 @@ def test_onnx_modelrun_mnist(env): ensureSlaveSynced(con, env) ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - env.assertEqual(ret[-1], b'') + env.assertEqual(len(ret), 14) + env.assertEqual(ret[5], b'') + # assert there are no inputs or outputs + env.assertEqual(len(ret[11]), 0) + env.assertEqual(len(ret[13]), 0) - ret = con.execute_command('AI.MODELSET', 'm', 'ONNX', DEVICE, 'TAG', 'asdf', 'BLOB', model_pb) + ret = con.execute_command('AI.MODELSET', 'm', 'ONNX', DEVICE, 'TAG', 'version:2', 'BLOB', model_pb) env.assertEqual(ret, b'OK') ensureSlaveSynced(con, env) ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - env.assertEqual(ret[-1], b'asdf') - - # TODO: enable me - # env.assertEqual(ret[0], b'ONNX') - # env.assertEqual(ret[1], b'CPU') + env.assertEqual(len(ret), 14) + # TODO: enable me. CI is having issues on GPU asserts of ONNX and CPU + if DEVICE == "CPU": + env.assertEqual(ret[1], b'ONNX') + env.assertEqual(ret[3], b'CPU') + env.assertEqual(ret[5], b'version:2') + # assert there are no inputs or outputs + env.assertEqual(len(ret[11]), 0) + env.assertEqual(len(ret[13]), 0) try: con.execute_command('AI.MODELSET', 'm', 'ONNX', DEVICE, 'BLOB', wrong_model_pb) @@ -166,6 +172,19 @@ def test_onnx_modelrun_mnist_autobatch(env): 'BATCHSIZE', 2, 'MINBATCHSIZE', 2, 'BLOB', model_pb) env.assertEqual(ret, b'OK') + ret = con.execute_command('AI.MODELGET', 'm', 'META') + env.assertEqual(len(ret), 14) + # TODO: enable me. CI is having issues on GPU asserts of ONNX and CPU + if DEVICE == "CPU": + env.assertEqual(ret[1], b'ONNX') + env.assertEqual(ret[3], b'CPU') + env.assertEqual(ret[5], b'') + env.assertEqual(ret[7], 2) + env.assertEqual(ret[9], 2) + # assert there are no inputs or outputs + env.assertEqual(len(ret[11]), 0) + env.assertEqual(len(ret[13]), 0) + con.execute_command('AI.TENSORSET', 'a', 'FLOAT', 1, 1, 28, 28, 'BLOB', sample_raw) con.execute_command('AI.TENSORSET', 'c', 'FLOAT', 1, 1, 28, 28, 'BLOB', sample_raw) diff --git a/test/tests_pytorch.py b/test/tests_pytorch.py index 43a04ede4..638be7bf8 100644 --- a/test/tests_pytorch.py +++ b/test/tests_pytorch.py @@ -62,22 +62,31 @@ def test_pytorch_modelrun(env): ensureSlaveSynced(con, env) ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - env.assertEqual(ret[-1], b'') - - ret = con.execute_command('AI.MODELSET', 'm', 'TORCH', DEVICE, 'TAG', 'asdf', 'BLOB', model_pb) + ret = con.execute_command('AI.MODELGET', 'm', 'META') + env.assertEqual(len(ret), 14) + # TODO: enable me. CI is having issues on GPU asserts of TORCH and CPU + if DEVICE == "CPU": + env.assertEqual(ret[1], b'TORCH') + env.assertEqual(ret[3], b'CPU') + env.assertEqual(ret[5], b'') + env.assertEqual(ret[7], 0) + env.assertEqual(ret[9], 0) + # assert there are no inputs or outputs + env.assertEqual(len(ret[11]), 0) + env.assertEqual(len(ret[13]), 0) + + ret = con.execute_command('AI.MODELSET', 'm', 'TORCH', DEVICE, 'TAG', 'my:tag:v3', 'BLOB', model_pb) env.assertEqual(ret, b'OK') ensureSlaveSynced(con, env) ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - env.assertEqual(ret[-1], b'asdf') - - - # TODO: enable me - # env.assertEqual(ret[0], b'TORCH') - # env.assertEqual(ret[1], b'CPU') + env.assertEqual(len(ret), 14) + env.assertEqual(ret[5], b'my:tag:v3') + # TODO: enable me. CI is having issues on GPU asserts of TORCH and CPU + if DEVICE == "CPU": + env.assertEqual(ret[1], b'TORCH') + env.assertEqual(ret[3], b'CPU') try: con.execute_command('AI.MODELSET', 'm', 'TORCH', DEVICE, 'BLOB', wrong_model_pb) diff --git a/test/tests_tensorflow.py b/test/tests_tensorflow.py index 04e93a060..fda1ab59c 100644 --- a/test/tests_tensorflow.py +++ b/test/tests_tensorflow.py @@ -187,23 +187,28 @@ def test_run_tf_model(env): ensureSlaveSynced(con, env) ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - env.assertEqual(ret[-1], b'') + env.assertEqual(len(ret), 14) + env.assertEqual(ret[5], b'') + env.assertEqual(ret[11][0], b'a') + env.assertEqual(ret[11][1], b'b') + env.assertEqual(ret[13][0], b'mul') - ret = con.execute_command('AI.MODELSET', 'm', 'TF', DEVICE, 'TAG', 'asdf', + ret = con.execute_command('AI.MODELSET', 'm', 'TF', DEVICE, 'TAG', 'version:1', 'INPUTS', 'a', 'b', 'OUTPUTS', 'mul', 'BLOB', model_pb) env.assertEqual(ret, b'OK') ensureSlaveSynced(con, env) ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - env.assertEqual(ret[-1], b'asdf') - - - # TODO: enable me - # env.assertEqual(ret[0], b'TF') - # env.assertEqual(ret[1], b'CPU') + env.assertEqual(len(ret), 14) + # TODO: enable me. CI is having issues on GPU asserts of TF and CPU + if DEVICE == "CPU": + env.assertEqual(ret[1], b'TF') + env.assertEqual(ret[3], b'CPU') + env.assertEqual(ret[5], b'version:1') + env.assertEqual(ret[11][0], b'a') + env.assertEqual(ret[11][1], b'b') + env.assertEqual(ret[13][0], b'mul') con.execute_command('AI.TENSORSET', 'a', 'FLOAT', 2, 2, 'VALUES', 2, 3, 2, 3) @@ -258,8 +263,10 @@ def test_run_tf2_model(env): ensureSlaveSynced(con, env) ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - env.assertEqual(ret[-1], b'') + env.assertEqual(len(ret), 14) + env.assertEqual(ret[5], b'') + env.assertEqual(ret[11][0], b'x') + env.assertEqual(ret[13][0], b'Identity') ret = con.execute_command('AI.MODELSET', 'm', 'TF', DEVICE, 'TAG', 'asdf', 'INPUTS', 'x', 'OUTPUTS', 'Identity', 'BLOB', model_pb) @@ -268,8 +275,10 @@ def test_run_tf2_model(env): ensureSlaveSynced(con, env) ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - env.assertEqual(ret[-1], b'asdf') + env.assertEqual(len(ret), 14) + env.assertEqual(ret[5], b'asdf') + env.assertEqual(ret[11][0], b'x') + env.assertEqual(ret[13][0], b'Identity') zero_values = [0] * (28 * 28) diff --git a/test/tests_tflite.py b/test/tests_tflite.py index b16760045..947e486a9 100644 --- a/test/tests_tflite.py +++ b/test/tests_tflite.py @@ -35,15 +35,15 @@ def test_run_tflite_model(env): env.assertEqual(ret, b'OK') ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - env.assertEqual(ret[-1], b'') + env.assertEqual(len(ret), 14) + env.assertEqual(ret[5], b'') ret = con.execute_command('AI.MODELSET', 'm', 'TFLITE', 'CPU', 'TAG', 'asdf', 'BLOB', model_pb) env.assertEqual(ret, b'OK') ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - env.assertEqual(ret[-1], b'asdf') + env.assertEqual(len(ret), 14) + env.assertEqual(ret[5], b'asdf') ret = con.execute_command('AI.TENSORSET', 'a', 'FLOAT', 1, 1, 28, 28, 'BLOB', sample_raw) env.assertEqual(ret, b'OK') @@ -51,10 +51,11 @@ def test_run_tflite_model(env): ensureSlaveSynced(con, env) ret = con.execute_command('AI.MODELGET', 'm', 'META') - env.assertEqual(len(ret), 6) - # TODO: enable me - # env.assertEqual(ret[0], b'TFLITE') - # env.assertEqual(ret[1], b'CPU') + env.assertEqual(len(ret), 14) + # TODO: enable me. CI is having issues on GPU asserts of TFLITE and CPU + if DEVICE == "CPU": + env.assertEqual(ret[1], b'TFLITE') + env.assertEqual(ret[3], b'CPU') con.execute_command('AI.MODELRUN', 'm', 'INPUTS', 'a', 'OUTPUTS', 'b', 'c')