Repository: gradio-app/gradio

Issue:
Series and Parallel don't work if prediction contains an ndarray when using Spaces
I was trying to recreate the example from this tweet: 

https://twitter.com/abidlabs/status/1457753971075002376

```
import gradio as gr

general_classifier = gr.Interface.load("spaces/abidlabs/vision-transformer")
bird_classifier = gr.Interface.load("spaces/akhaliq/bird_classifier")

gr.Parallel(general_classifier, bird_classifier).launch(debug=True)
```

But this fails with the following error:

```
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/gradio/networking.py", line 193, in predict
    prediction, durations = app.interface.process(raw_input)
  File "/usr/local/lib/python3.7/dist-packages/gradio/interface.py", line 364, in process
    processed_input, return_duration=True)
  File "/usr/local/lib/python3.7/dist-packages/gradio/interface.py", line 332, in run_prediction
    prediction = predict_fn(*processed_input)
  File "/usr/local/lib/python3.7/dist-packages/gradio/external.py", line 203, in fn
    data = json.dumps({"data": data})
  File "/usr/lib/python3.7/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type ndarray is not JSON serializable
```

![parallel](https://user-images.githubusercontent.com/59344/140841080-51537e88-5999-4b44-8c0a-0a6b9b5cc778.png)





Discussion:
You’re right! We’re working on patching Parallel() as soon as possible

On Mon, Nov 8, 2021 at 6:47 PM Jeroen Van Goey ***@***.***>
wrote:

> I was trying to recreate the example from this tweet:
>
> https://twitter.com/abidlabs/status/1457753971075002376
>
> import gradio as gr
>
> general_classifier = gr.Interface.load("spaces/abidlabs/vision-transformer")
> bird_classifier = gr.Interface.load("spaces/akhaliq/bird_classifier")
>
> gr.Parallel(general_classifier, bird_classifier).launch(debug=True)
>
> But this fails with the following error:
>
> Traceback (most recent call last):
>   File "/usr/local/lib/python3.7/dist-packages/gradio/networking.py", line 193, in predict
>     prediction, durations = app.interface.process(raw_input)
>   File "/usr/local/lib/python3.7/dist-packages/gradio/interface.py", line 364, in process
>     processed_input, return_duration=True)
>   File "/usr/local/lib/python3.7/dist-packages/gradio/interface.py", line 332, in run_prediction
>     prediction = predict_fn(*processed_input)
>   File "/usr/local/lib/python3.7/dist-packages/gradio/external.py", line 203, in fn
>     data = json.dumps({"data": data})
>   File "/usr/lib/python3.7/json/__init__.py", line 231, in dumps
>     return _default_encoder.encode(obj)
>   File "/usr/lib/python3.7/json/encoder.py", line 199, in encode
>     chunks = self.iterencode(o, _one_shot=True)
>   File "/usr/lib/python3.7/json/encoder.py", line 257, in iterencode
>     return _iterencode(o, 0)
>   File "/usr/lib/python3.7/json/encoder.py", line 179, in default
>     raise TypeError(f'Object of type {o.__class__.__name__} '
> TypeError: Object of type ndarray is not JSON serializable
>
> [image: parallel]
> <https://user-images.githubusercontent.com/59344/140841080-51537e88-5999-4b44-8c0a-0a6b9b5cc778.png>
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/gradio-app/gradio/issues/343>, or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AANSE6NY7AJMU7CSGE5MVNTULBVSVANCNFSM5HUAWTUA>
> .
> Triage notifications on the go with GitHub Mobile for iOS
> <https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
> or Android
> <https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
>
>
-- 
Abubakar Abid
Department of Electrical Engineering
Stanford University || Paul & Daisy Soros Fellow
@abidlabs <https://twitter.com/abidlabs> || *abidlabs.github.io
<http://abidlabs.github.io>*

Should work now!
Thanks for the quick fix! I can confirm that it now works for Parallel, but on gradio 2.4.5 Series is still broken. For example:

```
import gradio as gr

remove_bg = gr.Interface.load("spaces/eugenesiow/remove-bg")
anime = gr.Interface.load("spaces/akhaliq/AnimeGANv2")

gr.Series(remove_bg, anime).launch()
```

fails with:

```
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/gradio/networking.py", line 195, in predict
    prediction, durations = app.interface.process(raw_input)
  File "/usr/local/lib/python3.7/dist-packages/gradio/interface.py", line 372, in process
    processed_input, return_duration=True)
  File "/usr/local/lib/python3.7/dist-packages/gradio/interface.py", line 337, in run_prediction
    prediction = predict_fn(*processed_input)
  File "/usr/local/lib/python3.7/dist-packages/gradio/mix.py", line 54, in connected_fn
    prediction = predict_fn(*data)
  File "/usr/local/lib/python3.7/dist-packages/gradio/external.py", line 203, in fn
    data = json.dumps({"data": data})
  File "/usr/lib/python3.7/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type ndarray is not JSON serializable
```
![screenshot](https://pbs.twimg.com/media/FD3Vm8RXIAEhhpd?format=jpg)


Ah you're right, thanks will fix this issue

Resolving patch:
diff --git a/gradio/mix.py b/gradio/mix.py
--- a/gradio/mix.py
+++ b/gradio/mix.py
@@ -45,18 +45,20 @@ def __init__(self, *interfaces, **options):
     
         def connected_fn(*data):  # Run each function with the appropriate preprocessing and postprocessing 
             for idx, io in enumerate(interfaces):
-                # skip preprocessing for first interface since the compound interface will include it
-                if idx > 0:
-                    data = [input_interface.preprocess(data[i]) for i, input_interface in enumerate(io.input_components)]
+                # skip preprocessing for first interface since the Series interface will include it
+                if idx > 0 and not(io.api_mode):
+                    data = [input_component.preprocess(data[i]) for i, input_component in enumerate(io.input_components)]
+
                 # run all of predictions sequentially
                 predictions = []
                 for predict_fn in io.predict:
                     prediction = predict_fn(*data)
                     predictions.append(prediction)
                 data = predictions
-                # skip postprocessing for final interface since the compound interface will include it
-                if idx < len(interfaces) - 1:
-                    data = [output_interface.postprocess(data[i]) for i, output_interface in enumerate(io.output_components)]
+                # skip postprocessing for final interface since the Series interface will include it
+                if idx < len(interfaces) - 1 and not(io.api_mode):
+                    data = [output_component.postprocess(data[i]) for i, output_component in enumerate(io.output_components)]
+
             return data[0]
     
         connected_fn.__name__ = " => ".join([f[0].__name__ for f in fns])
@@ -65,6 +67,7 @@ def connected_fn(*data):  # Run each function with the appropriate preprocessing
             "fn": connected_fn,
             "inputs": interfaces[0].input_components,
             "outputs": interfaces[-1].output_components,
+            "api_mode": interfaces[0].api_mode,  # TODO(abidlabs): allow mixing api_mode and non-api_mode interfaces
         }
         kwargs.update(options)
         super().__init__(**kwargs) 


Test patch:
diff --git a/test/test_mix.py b/test/test_mix.py
--- a/test/test_mix.py
+++ b/test/test_mix.py
@@ -4,6 +4,11 @@
 import os
 
 
+"""
+WARNING: Some of these tests have an external dependency: namely that Hugging Face's Hub and Space APIs do not change, and they keep their most famous models up. So if, e.g. Spaces is down, then these test will not pass.
+"""
+
+
 os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
 
 
@@ -15,6 +20,14 @@ def test_in_interface(self):
         series = mix.Series(io1, io2)
         self.assertEqual(series.process(["Hello"])[0], ["Hello World!"])
 
+    def test_with_external(self):
+        io1 = gr.Interface.load("spaces/abidlabs/image-identity")
+        io2 = gr.Interface.load("spaces/abidlabs/image-classifier")
+        series = mix.Series(io1, io2)
+        output = series("test/test_data/lion.jpg")
+        self.assertGreater(output['lion'], 0.5)
+
+
 class TestParallel(unittest.TestCase):
     def test_in_interface(self):
         io1 = gr.Interface(lambda x: x + " World 1!", "textbox",
@@ -24,6 +37,13 @@ def test_in_interface(self):
         parallel = mix.Parallel(io1, io2)
         self.assertEqual(parallel.process(["Hello"])[0], ["Hello World 1!",
                                                           "Hello World 2!"])
+    def test_with_external(self):
+        io1 = gr.Interface.load("spaces/abidlabs/english_to_spanish")
+        io2 = gr.Interface.load("spaces/abidlabs/english2german")
+        parallel = mix.Parallel(io1, io2)
+        hello_es, hello_de = parallel("Hello")
+        self.assertIn("hola", hello_es.lower())
+        self.assertIn("hallo", hello_de.lower())
 
 
 if __name__ == '__main__':