Repository: replicate/cog

Issue:
What should `cog predict` do if you don't pass an input name?
The syntax of `cog predict` is this:

    cog predict -i image=@image.jpg

But you can also do this:

    cog predict -i @image.jpg

Which implicitly means an input name of `input`. This is a neat short hand but a bit weird for a few reasons:

- `input` is a Python built-in, so we should really be encouraging using that as a variable name.
- It is a magic name.
- For a sufficiently complex model, you probably don't want to call it `input`.

What could we do that is better here? Maybe if you don't pass a name, it defaults to the _first_ input defined, rather than a magic name? This is vaguely backwards compatible, which is neat.


Discussion:

Resolving patch:
diff --git a/pkg/cli/init-templates/predict.py b/pkg/cli/init-templates/predict.py
--- a/pkg/cli/init-templates/predict.py
+++ b/pkg/cli/init-templates/predict.py
@@ -11,12 +11,12 @@ def setup(self):
 
     def predict(
         self,
-        input: Path = Input(description="Grayscale input image"),
+        image: Path = Input(description="Grayscale input image"),
         scale: float = Input(
             description="Factor to scale image by", ge=0, le=10, default=1.5
         ),
     ) -> Path:
         """Run a single prediction on the model"""
-        # processed_input = preprocess(input)
-        # output = self.model(processed_input, scale)
+        # processed_input = preprocess(image)
+        # output = self.model(processed_image, scale)
         # return postprocess(output)


Test patch:
diff --git a/test-integration/test_integration/fixtures/int-project/predict.py b/test-integration/test_integration/fixtures/int-project/predict.py
--- a/test-integration/test_integration/fixtures/int-project/predict.py
+++ b/test-integration/test_integration/fixtures/int-project/predict.py
@@ -2,5 +2,5 @@
 
 
 class Predictor(BasePredictor):
-    def predict(self, input: int) -> int:
-        return input * 2
+    def predict(self, num: int) -> int:
+        return num * 2
diff --git a/test-integration/test_integration/fixtures/string-project/predict.py b/test-integration/test_integration/fixtures/string-project/predict.py
--- a/test-integration/test_integration/fixtures/string-project/predict.py
+++ b/test-integration/test_integration/fixtures/string-project/predict.py
@@ -2,5 +2,5 @@
 
 
 class Predictor(BasePredictor):
-    def predict(self, input: str) -> str:
-        return "hello " + input
+    def predict(self, s: str) -> str:
+        return "hello " + s
diff --git a/test-integration/test_integration/fixtures/subdirectory-project/my-subdir/predict.py b/test-integration/test_integration/fixtures/subdirectory-project/my-subdir/predict.py
--- a/test-integration/test_integration/fixtures/subdirectory-project/my-subdir/predict.py
+++ b/test-integration/test_integration/fixtures/subdirectory-project/my-subdir/predict.py
@@ -4,5 +4,5 @@
 
 
 class Predictor(BasePredictor):
-    def predict(self, input: str) -> str:
-        return concat("hello", input)
+    def predict(self, s: str) -> str:
+        return concat("hello", s)
diff --git a/test-integration/test_integration/test_predict.py b/test-integration/test_integration/test_predict.py
--- a/test-integration/test_integration/test_predict.py
+++ b/test-integration/test_integration/test_predict.py
@@ -51,7 +51,7 @@ def test_predict_writes_files_to_files(tmpdir_factory):
     out_dir = pathlib.Path(tmpdir_factory.mktemp("project"))
     shutil.copytree(project_dir, out_dir, dirs_exist_ok=True)
     result = subprocess.run(
-        ["cog", "predict", "-i", "world", "-o", out_dir / "out.txt"],
+        ["cog", "predict", "-o", out_dir / "out.txt"],
         cwd=out_dir,
         check=True,
         capture_output=True,
@@ -139,7 +139,6 @@ def test_predict_many_inputs(tmpdir_factory):
     out_dir = pathlib.Path(tmpdir_factory.mktemp("project"))
     shutil.copytree(project_dir, out_dir, dirs_exist_ok=True)
     inputs = {
-        "no_default": "hello",
         "path": "@path.txt",
         "image": "@image.jpg",
         "choices": "foo",
@@ -150,6 +149,8 @@ def test_predict_many_inputs(tmpdir_factory):
     with open(out_dir / "image.jpg", "w") as fh:
         fh.write("")
     cmd = ["cog", "predict"]
+    # If no name is specified, it defaults to the first
+    cmd += ["-i", "hello"]
     for k, v in inputs.items():
         cmd += ["-i", f"{k}={v}"]
 
diff --git a/test-integration/test_integration/test_redis_queue.py b/test-integration/test_integration/test_redis_queue.py
--- a/test-integration/test_integration/test_redis_queue.py
+++ b/test-integration/test_integration/test_redis_queue.py
@@ -370,7 +370,7 @@ def test_queue_worker_invalid_input(docker_network, docker_image, redis_client):
                     {
                         "id": predict_id,
                         "input": {
-                            "input": "not a number",
+                            "num": "not a number",
                         },
                         "response_queue": "response-queue",
                     }