Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Callable blocks #1437

Merged
merged 17 commits into from
Jun 2, 2022
Merged

Callable blocks #1437

merged 17 commits into from
Jun 2, 2022

Conversation

abidlabs
Copy link
Member

@abidlabs abidlabs commented Jun 1, 2022

This PR allows Blocks to be used as regular functions, just like Interfaces. This feature will be much nicer to use once we have api_names (PR #1397), but this PR sets up the basic functionality in place.

  • Here is example usage:
import gradio as gr
import asyncio

demo = gr.Interface.load("spaces/gradio/question-answering")
demo("Pakistan, officially the Islamic Republic of Pakistan, is a country in South Asia.", "Where is Pakistan?")

Notice that calling the function returns an async coroutine so you must await it or async.run it. The reason for this is now we support async functions inside Blocks, so everything is treated asynchronously. This is also nice because you can call multiple api requests concurrently (yay parallelization!)

  • Works with images, etc. as well:
import gradio as gr

io = gr.Interface.load("spaces/gradio/Echocardiogram-Segmentation")
io("lion.jpg")
  • How about calling non-Interface Blocks? You can specify the fn_index -- here's example usage:
import gradio as gr

io = gr.Interface.load("spaces/abidlabs/blocks-dev-test")

This is a super simple Space that has 2 functions, one that doubles the word, and one that triples the word. You can call each of the functions by doing:

io("a", fn_index=0)
>> "aa"
io("a", fn_index=1)
>> "aaa"

Right now, this requires you to know the fn_index for each function. This will be nicer when we can use the api_name instead. cc @apolinario


This PR also does two other things:

  1. @dawoodkhan82 and I discovered a bug with image uploads. It fixes that in Upload.svelte

  2. It addresses Gradio 3.0 cannot load interfaces from Gradio 2 #1417 by making somewhat of an assumption (that people will only use gradio.mix if they load an Interface, and that for an Interface, the first blocks function is the prediction function). Although this may not always be True, the use case in Gradio 3.0 cannot load interfaces from Gradio 2 #1417 is important enough such that we should support it (I've added warnings to be safe). You can test the code sample in Gradio 3.0 cannot load interfaces from Gradio 2 #1417 to make sure it works:

import gradio as gr

iface1 = gr.Interface.load("spaces/mrm8488/GPT-J-6B")
iface2 = gr.Interface.load("spaces/akhaliq/T0pp")

iface3 = gr.mix.Parallel(
  iface1, iface2, 
  examples = [
    ['Which country will win the 2002 World Cup?'],
    ["A is the son's of B's uncle. What is the family relationship between A and B?"],
    ["In 2030, "],
  ],
  theme="default",
  css=".footer{display:none !important}")
  
iface3.launch()

cc @osanseviero

Closes: #1417

@abidlabs abidlabs marked this pull request as ready for review June 1, 2022 21:45
@apolinario
Copy link
Contributor

I have installed this branch locally and got this issue while testing it with Dalle-Mini:

dallemini = gr.Interface.load("spaces/dalle-mini/dalle-mini")
Fetching interface from: https://huggingface.co/spaces/dalle-mini/dalle-mini
Traceback (most recent call last):
  File "/mnt/c/Users/poli/Documents/GitHub/gradio/gradio/external.py", line 303, in get_spaces
    config = json.loads(result.group(1))
AttributeError: 'NoneType' object has no attribute 'group'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/mnt/c/Users/poli/Documents/GitHub/gradio/app.py", line 3, in <module>
    dallemini = gr.Interface.load("spaces/dalle-mini/dalle-mini")
  File "/mnt/c/Users/poli/Documents/GitHub/gradio/gradio/interface.py", line 90, in load
    return super().load(name=name, src=src, api_key=api_key, alias=alias, **kwargs)
  File "/mnt/c/Users/poli/Documents/GitHub/gradio/gradio/blocks.py", line 582, in load
    return external.load_blocks_from_repo(name, src, api_key, alias, **kwargs)
  File "/mnt/c/Users/poli/Documents/GitHub/gradio/gradio/external.py", line 34, in load_blocks_from_repo
    blocks: gradio.Blocks = factory_methods[src](name, api_key, alias, **kwargs)
  File "/mnt/c/Users/poli/Documents/GitHub/gradio/gradio/external.py", line 305, in get_spaces
    raise ValueError("Could not load the Space: {}".format(model_name))
ValueError: Could not load the Space: dalle-mini/dalle-mini

@apolinario
Copy link
Contributor

apolinario commented Jun 2, 2022

It did work with CLIP Guided Faces though:

faces = gr.Interface.load("spaces/Gradio-Blocks/clip-guided-faces")

Works (loading, haven't tested executing it yet)

However, would it be possible to keep the mini-auto-documentation feature of 2.x? Having some way to print or list each of the fn_index you would see a list and amount of settings to add, and the order too (as with blocks you cant just look at the interface to guess the order logic).

I was thinking something equivalent/same to 2.x loaded spaces, that if you print the function generated it creates this

inputs:
|-textbox
|-radio
|-dropdown
outputs:
|-image
|-textbox

@abidlabs
Copy link
Member Author

abidlabs commented Jun 2, 2022

I was thinking something equivalent/same to 2.x loaded spaces, that if you print the function generated it creates this

Good idea @apolinario, I'll add this functionality, and look into why dalle-mini isn't working (there are sooo many cases to consider lol)

@abidlabs
Copy link
Member Author

abidlabs commented Jun 2, 2022

Ok so the reason that gr.Interface.load("spaces/dalle-mini/dalle-mini") doesn't work is that dalle-mini isn't actually a proper Gradio app.

It just uses the Gradio components on the frontend and then makes an API request in javascript. As a result, it can't really be used with gr.Interface.load

@abidlabs
Copy link
Member Author

abidlabs commented Jun 2, 2022

Added a string representation for Blocks that looks like this:

image

@abidlabs
Copy link
Member Author

abidlabs commented Jun 2, 2022

Also checked loading and using the top Spaces of the Week (see below) and they seemed to work well. So ready for review @aliabid94!

  • nateraw/animegan-v2-for-videos
  • radames/Depth-Image-to-Autostereogram
  • Gradio-Blocks/GPTJ6B_Poetry_LatentDiff_Illustration
  • Gradio-Blocks/DualStyleGAN

@aliabid94
Copy link
Collaborator

aliabid94 commented Jun 2, 2022

I get the error
SyntaxError: 'await' outside function
trying to run the blocks-dev-test example?
with Python 3.8.9

@abidlabs
Copy link
Member Author

abidlabs commented Jun 2, 2022

So like I mention in the first example, if you are not in an async function, you will need to do something like this:

import asyncio
import gradio as gr

io = gr.Interface.load("spaces/abidlabs/blocks-dev-test")

asyncio.run(io("a", fn_index=0))

because under the hood, everything is async

@abidlabs
Copy link
Member Author

abidlabs commented Jun 2, 2022

After discussing with @aliabid94, decided to make the calls synchronous instead of asynchronous. This drops support for async functions when using __call__ (almost certainly a very small use case), but makes the API much more user-friendly.

I've updated the usage examples above

Copy link
Collaborator

@aliabid94 aliabid94 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after addressing/fixing comments

@@ -310,8 +312,91 @@ def iterate_over_children(children_list):
event_method = getattr(original_mapping[target], trigger)
event_method(fn=fn, **dependency)

# Allows some use of Interface-specific methods with loaded Spaces
Copy link
Collaborator

@aliabid94 aliabid94 Jun 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of implementing Interface specific things in Blocks, can you overwrite __call__ in Interface?

Copy link
Collaborator

@aliabid94 aliabid94 Jun 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can call super().__call__ where necessary

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is in from_config() not __call__()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, I don't think it's actually appropriate to put it in Interface. Because it's actually a Blocks that gets created, not an Interface. This additional hack simply lets the Blocks be used in some ways that Interfaces are used -- it's more or less an experimental thing, not necessarily something we want to support fully right now.

@abidlabs abidlabs merged commit 71bcfdb into main Jun 2, 2022
@abidlabs abidlabs deleted the callable-blocks branch June 2, 2022 22:37
processed_input = self.preprocess_data(fn_index, serialized_params, None)

if inspect.iscoroutinefunction(block_fn.fn):
raise ValueError(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abidlabs why not run async function in asyncio.run(), I think we should support async functions as well, or this feature is incomplete.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that this approach wouldn't work inside of a jupyter notebook. You get this error: https://stackoverflow.com/questions/55409641/asyncio-run-cannot-be-called-from-a-running-event-loop

I wasn't sure how to fix it myself, so I left this case as unsupported for now

Copy link
Contributor

@omerXfaruq omerXfaruq Jun 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, today I looked into a little bit, it is a bit problematic case, it might be solved with fsspec library, will look into it in the future!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Gradio 3.0 cannot load interfaces from Gradio 2
4 participants