Repository: gradio-app/gradio

Issue:
HF API + Spaces give error + uninformative error message
### Describe the bug

There is [this space](https://huggingface.co/spaces/osanseviero/testtumor) which uses the API under the hood and crashes with a super weird error. This works in a Colab. I concluded it is because cache_examples=True in Spaces, and the API is probably not ready so it of course crashes. If I set cache_examples=False it works fine, but it's not clear I should do that from the error message. I think here we should either improve the error message or not cache when using the API

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Reproduction

See code in https://huggingface.co/spaces/osanseviero/testtumor

### Screenshot

_No response_

### Logs

```shell
Fetching model from: https://huggingface.co/SerdarHelli/ThyroidTumorClassificationModel
/home/user/.local/lib/python3.8/site-packages/gradio/interface.py:286: UserWarning: Currently, only the 'default' theme is supported.
  warnings.warn("Currently, only the 'default' theme is supported.")
Cache at /home/user/app/gradio_cached_examples/log.csv not found. Caching now in 'gradio_cached_examples/' directory.
Traceback (most recent call last):
  File "app.py", line 23, in <module>
    gr.Interface.load("huggingface/SerdarHelli/ThyroidTumorClassificationModel",title=title,description=description,article=article,examples=examples,cache_examples=True).launch()
  File "/home/user/.local/lib/python3.8/site-packages/gradio/interface.py", line 90, in load
    return super().load(name=name, src=src, api_key=api_key, alias=alias, **kwargs)
  File "/home/user/.local/lib/python3.8/site-packages/gradio/blocks.py", line 662, in load
    return external.load_blocks_from_repo(name, src, api_key, alias, **kwargs)
  File "/home/user/.local/lib/python3.8/site-packages/gradio/external.py", line 40, in load_blocks_from_repo
    blocks: gradio.Blocks = factory_methods[src](name, api_key, alias, **kwargs)
  File "/home/user/.local/lib/python3.8/site-packages/gradio/external.py", line 294, in get_models_interface
    interface = gradio.Interface(**kwargs)
  File "/home/user/.local/lib/python3.8/site-packages/gradio/interface.py", line 420, in __init__
    cache_interface_examples(self)
  File "/home/user/.local/lib/python3.8/site-packages/gradio/process_examples.py", line 51, in cache_interface_examples
    raise e
  File "/home/user/.local/lib/python3.8/site-packages/gradio/process_examples.py", line 47, in cache_interface_examples
    prediction = process_example(interface, example_id)
  File "/home/user/.local/lib/python3.8/site-packages/gradio/process_examples.py", line 29, in process_example
    prediction = interface.process(raw_input)
  File "/home/user/.local/lib/python3.8/site-packages/gradio/interface.py", line 748, in process
    predictions = self.run_prediction(processed_input)
  File "/home/user/.local/lib/python3.8/site-packages/gradio/interface.py", line 712, in run_prediction
    prediction = predict_fn(*processed_input)
  File "/home/user/.local/lib/python3.8/site-packages/gradio/external.py", line 259, in query_huggingface_api
    data = pipeline["preprocess"](*params)
  File "/home/user/.local/lib/python3.8/site-packages/gradio/external.py", line 131, in <lambda>
    i.split(",")[1]
IndexError: list index out of range
```
```


### System Info

```shell
Gradio 3.20, Spaces
```


### Severity

annoying


Discussion:
Thank you for filing @osanseviero !

I think the problem is that when the examples are being cached, `api_mode` is set to False since the examples are cached during interface initialization. It is not until after the interface is created that `api_mode` is [set to](https://github.com/gradio-app/gradio/blob/main/gradio/external.py#L295) True.

The interface class does some [special serialization](https://github.com/gradio-app/gradio/blob/main/gradio/interface.py#L709) when `api_mode` is set to True, which is why the space works for running predictions but not caching examples during init.

A quick fix would be to expose `api_mode` as a top level parameter and setting that to `True` in the `Interface` class but there may be better fixes, like setting a `base64` image type that avoids some of the serializing/deserializing we have to do. 

Resolving patch:
diff --git a/demo/image_classifier_interface_load/run.py b/demo/image_classifier_interface_load/run.py
new file mode 100644
--- /dev/null
+++ b/demo/image_classifier_interface_load/run.py
@@ -0,0 +1,29 @@
+import gradio as gr
+
+images = ["cheetah1.jpeg", "cheetah1.jpg", "lion.jpg"]
+
+
+img_classifier = gr.Interface.load(
+    "models/google/vit-base-patch16-224", examples=images, cache_examples=True
+)
+
+
+def func(img, text):
+    return img_classifier(img), text
+
+
+using_img_classifier_as_function = gr.Interface(
+    func,
+    [gr.Image(type="filepath"), "text"],
+    ["label", "text"],
+    examples=[
+        ["cheetah1.jpeg", None],
+        ["cheetah1.jpg", "cheetah"],
+        ["lion.jpg", "lion"],
+    ],
+    cache_examples=True,
+)
+demo = gr.TabbedInterface([using_img_classifier_as_function, img_classifier])
+
+if __name__ == "__main__":
+    demo.launch()
diff --git a/gradio/external.py b/gradio/external.py
--- a/gradio/external.py
+++ b/gradio/external.py
@@ -291,8 +291,8 @@ def query_huggingface_api(*params):
     }
 
     kwargs = dict(interface_info, **kwargs)
+    kwargs["_api_mode"] = True  # So interface doesn't run pre/postprocess.
     interface = gradio.Interface(**kwargs)
-    interface.api_mode = True  # So interface doesn't run pre/postprocess.
     return interface
 
 
@@ -414,8 +414,8 @@ def fn(*data):
     config["fn"] = fn
 
     kwargs = dict(config, **kwargs)
+    kwargs["_api_mode"] = True
     interface = gradio.Interface(**kwargs)
-    interface.api_mode = True  # So interface doesn't run pre/postprocess.
     return interface
 
 
diff --git a/gradio/interface.py b/gradio/interface.py
--- a/gradio/interface.py
+++ b/gradio/interface.py
@@ -149,6 +149,7 @@ def __init__(
         flagging_dir: str = "flagged",
         flagging_callback: FlaggingCallback = CSVLogger(),
         analytics_enabled: Optional[bool] = None,
+        _api_mode: bool = False,
         **kwargs,
     ):
         """
@@ -274,7 +275,7 @@ def __init__(
         else:
             raise ValueError("Invalid value for parameter: interpretation")
 
-        self.api_mode = False
+        self.api_mode = _api_mode
         self.fn = fn
         self.fn_durations = [0, 0]
         self.__name__ = fn.__name__
@@ -600,7 +601,7 @@ def __call__(self, *flag_data):
                     examples=examples,
                     inputs=non_state_inputs,
                     outputs=non_state_outputs,
-                    fn=self.fn,
+                    fn=submit_fn,
                     cache_examples=self.cache_examples,
                     examples_per_page=examples_per_page,
                 )
@@ -668,7 +669,7 @@ def run_prediction(
         if prediction is None or len(self.output_components) == 1:
             prediction = [prediction]
 
-        if self.api_mode:  # Deerialize the input
+        if self.api_mode:  # Deserialize the input
             prediction = [
                 output_component.deserialize(prediction[i])
                 for i, output_component in enumerate(self.output_components)


Test patch:
diff --git a/test/test_external.py b/test/test_external.py
--- a/test/test_external.py
+++ b/test/test_external.py
@@ -1,5 +1,7 @@
 import os
+import pathlib
 import unittest
+from unittest.mock import patch
 
 import pytest
 import transformers
@@ -229,5 +231,15 @@ def test_text_to_text_model_from_pipeline(self):
         self.assertIsNotNone(output)
 
 
+def test_interface_load_cache_examples(tmp_path):
+    test_file_dir = pathlib.Path(pathlib.Path(__file__).parent, "test_files")
+    with patch("gradio.examples.CACHED_FOLDER", tmp_path):
+        gr.Interface.load(
+            name="models/google/vit-base-patch16-224",
+            examples=[pathlib.Path(test_file_dir, "cheetah1.jpg")],
+            cache_examples=True,
+        )
+
+
 if __name__ == "__main__":
     unittest.main()