Repository: gradio-app/gradio

Issue:
Document in the config if the block function is a generator and if it runs forever
- [x] I have searched to see if a similar issue already exists.


**Is your feature request related to a problem? Please describe.**  
Needed for the several clients as discussed in this thread: https://huggingface.slack.com/archives/C02SPHC1KD1/p1677190325992569


**Describe the solution you'd like**  
A clear and concise description of what you want to happen.

**Additional context**  
Add any other context or screenshots about the feature request here.



Discussion:
We actually already track `every` i think, it is just other generating functions. Maybe we can combine them in the config. Although it would be good to know if they run forever or have a finite number of runs, if we have that info. It might also be good to know if a prediction is the last one, although i don't think that is essential.
> Although it would be good to know if they run forever or have a finite number of runs, if we have that info.

Yea I was thinking of adding two fields to the dependency config

1. `continuous`: True if runs forever.
2. `generator`: True if its is a generating function.

1 implies 2 but not the other way around. 
Would we remove `every` in this case? I think those names are better. Or we could add a `types` field with the function type(s). Either a list or an 'object'. Some degree of namespacing might be nice

I guess we can't remove anything as that might be breaking.
I like the suggestion to use `types` field! Let's not remove every for the reason you mentioned and I think it's good to know the expected frequency anyways.

Resolving patch:
diff --git a/gradio/blocks.py b/gradio/blocks.py
--- a/gradio/blocks.py
+++ b/gradio/blocks.py
@@ -236,6 +236,10 @@ def set_event_trigger(
             "batch": batch,
             "max_batch_size": max_batch_size,
             "cancels": cancels or [],
+            "types": {
+                "continuous": bool(every),
+                "generator": inspect.isgeneratorfunction(fn) or bool(every),
+            },
         }
         Context.root_block.dependencies.append(dependency)
         return dependency
@@ -579,6 +583,8 @@ def iterate_over_children(children_list):
                     with block:
                         iterate_over_children(children)
 
+        derived_fields = ["types"]
+
         with Blocks(theme=config["theme"], css=config["theme"]) as blocks:
             # ID 0 should be the root Blocks component
             original_mapping[0] = Context.root_block or blocks
@@ -596,6 +602,8 @@ def iterate_over_children(children_list):
                 # older demos
                 if dependency["trigger"] == "fake_event":
                     continue
+                for field in derived_fields:
+                    dependency.pop(field, None)
                 targets = dependency.pop("targets")
                 trigger = dependency.pop("trigger")
                 dependency.pop("backend_fn")


Test patch:
diff --git a/gradio/test_data/blocks_configs.py b/gradio/test_data/blocks_configs.py
--- a/gradio/test_data/blocks_configs.py
+++ b/gradio/test_data/blocks_configs.py
@@ -196,6 +196,7 @@
             "max_batch_size": 4,
             "cancels": [],
             "every": None,
+            "types": {"continuous": False, "generator": False},
         },
         {
             "targets": [39],
@@ -212,6 +213,7 @@
             "max_batch_size": 4,
             "cancels": [],
             "every": None,
+            "types": {"continuous": False, "generator": False},
         },
         {
             "targets": [],
@@ -228,6 +230,7 @@
             "max_batch_size": 4,
             "cancels": [],
             "every": None,
+            "types": {"continuous": False, "generator": False},
         },
     ],
 }
@@ -431,6 +434,7 @@
             "max_batch_size": 4,
             "cancels": [],
             "every": None,
+            "types": {"continuous": False, "generator": False},
         },
         {
             "targets": [933],
@@ -447,6 +451,7 @@
             "max_batch_size": 4,
             "cancels": [],
             "every": None,
+            "types": {"continuous": False, "generator": False},
         },
         {
             "targets": [],
@@ -463,6 +468,7 @@
             "max_batch_size": 4,
             "cancels": [],
             "every": None,
+            "types": {"continuous": False, "generator": False},
         },
     ],
 }
diff --git a/test/test_blocks.py b/test/test_blocks.py
--- a/test/test_blocks.py
+++ b/test/test_blocks.py
@@ -325,6 +325,37 @@ async def test_restart_after_close(self):
                     completed = True
             assert msg["output"]["data"][0] == "Victor"
 
+    def test_function_types_documented_in_config(self):
+        def continuous_fn():
+            return 42
+
+        def generator_function():
+            for index in range(10):
+                yield index
+
+        with gr.Blocks() as demo:
+
+            gr.Number(value=lambda: 2, every=2)
+            meaning_of_life = gr.Number()
+            counter = gr.Number()
+            generator_btn = gr.Button(value="Generate")
+            greeting = gr.Textbox()
+            greet_btn = gr.Button(value="Greet")
+
+            greet_btn.click(lambda: "Hello!", inputs=None, outputs=[greeting])
+            generator_btn.click(generator_function, inputs=None, outputs=[counter])
+            demo.load(continuous_fn, inputs=None, outputs=[meaning_of_life], every=1)
+
+        for i, dependency in enumerate(demo.config["dependencies"]):
+            if i == 0:
+                assert dependency["types"] == {"continuous": True, "generator": True}
+            if i == 1:
+                assert dependency["types"] == {"continuous": False, "generator": False}
+            if i == 2:
+                assert dependency["types"] == {"continuous": False, "generator": True}
+            if i == 3:
+                assert dependency["types"] == {"continuous": True, "generator": True}
+
     @pytest.mark.asyncio
     async def test_run_without_launching(self):
         """Test that we can start the app and use queue without calling .launch().