Introduce task groupings and fix adding/removing task
Browse files- actions.py +45 -24
- app.py +23 -8
- components.py +96 -16
- requirements.txt +2 -1
actions.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1 |
import re
|
|
|
2 |
|
3 |
import gradio as gr
|
4 |
|
5 |
-
from components import
|
6 |
|
7 |
|
8 |
def add_input(*visibility):
|
@@ -27,34 +28,56 @@ def remove_input(*visibility):
|
|
27 |
)
|
28 |
|
29 |
|
30 |
-
def
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
return (
|
34 |
-
[gr.Box.update(
|
35 |
-
+ [gr.Box.update(visible=
|
36 |
-
+ [
|
37 |
-
+ [
|
|
|
|
|
38 |
)
|
39 |
|
40 |
|
41 |
def remove_task(*visibility):
|
42 |
-
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
return (
|
45 |
-
[gr.Box.update(
|
46 |
-
+ [gr.Box.update(visible=False)] * (
|
47 |
-
+ [
|
48 |
-
+ [0] * (
|
49 |
)
|
50 |
|
51 |
|
52 |
-
def _get_all_vars_up_to(to: int):
|
53 |
-
return [in_.output for in_ in all_inputs.values()] + [
|
54 |
-
t.output for i, t in all_tasks.items() if i < to
|
55 |
-
]
|
56 |
-
|
57 |
-
|
58 |
def _clear_error():
|
59 |
return gr.HighlightedText.update(value=None, visible=False)
|
60 |
|
@@ -75,11 +98,9 @@ def execute_task(id_: int, prev_error_value, n_inputs, *vars_in_scope):
|
|
75 |
|
76 |
# Put all defined variables into a dict, with names (except task inputs)
|
77 |
vars = {
|
78 |
-
f"{Input.
|
79 |
}
|
80 |
-
vars.update(
|
81 |
-
{f"{Task.vname}{i}": task for i, task in enumerate(task_outputs)}
|
82 |
-
)
|
83 |
# Get all variables referenced within the task inputs
|
84 |
prompt_vars = {v for ti in non_empty_task_inputs for v in re.findall("{(.*?)}", ti)}
|
85 |
|
|
|
1 |
import re
|
2 |
+
from typing import List
|
3 |
|
4 |
import gradio as gr
|
5 |
|
6 |
+
from components import all_tasks, Input, MAX_INPUTS, MAX_TASKS, Task
|
7 |
|
8 |
|
9 |
def add_input(*visibility):
|
|
|
28 |
)
|
29 |
|
30 |
|
31 |
+
def _is_task_row_fully_invisible(row: List[int]) -> bool:
|
32 |
+
print(row)
|
33 |
+
for visible in row:
|
34 |
+
if bool(visible):
|
35 |
+
return False
|
36 |
+
return True
|
37 |
+
|
38 |
+
|
39 |
+
def add_task(index, *visibility):
|
40 |
+
visibility = list(visibility)
|
41 |
+
n_avail_tasks = len(Task.AVAILABLE_TASKS)
|
42 |
+
|
43 |
+
for i in range(MAX_TASKS):
|
44 |
+
start_row = i * n_avail_tasks
|
45 |
+
is_row_invisible = _is_task_row_fully_invisible(
|
46 |
+
visibility[start_row : start_row + n_avail_tasks]
|
47 |
+
)
|
48 |
+
if is_row_invisible:
|
49 |
+
unchanged_up_to = start_row + index
|
50 |
return (
|
51 |
+
[gr.Box.update()] * unchanged_up_to
|
52 |
+
+ [gr.Box.update(visible=True)]
|
53 |
+
+ [gr.Box.update()] * (len(visibility) - unchanged_up_to - 1)
|
54 |
+
+ [gr.Number.update()] * unchanged_up_to
|
55 |
+
+ [1]
|
56 |
+
+ [gr.Number.update()] * (len(visibility) - unchanged_up_to - 1)
|
57 |
)
|
58 |
|
59 |
|
60 |
def remove_task(*visibility):
|
61 |
+
visibility = list(visibility)
|
62 |
+
n_avail_tasks = len(Task.AVAILABLE_TASKS)
|
63 |
+
print(visibility, n_avail_tasks)
|
64 |
+
|
65 |
+
for i in range(MAX_TASKS):
|
66 |
+
start_row = i * n_avail_tasks
|
67 |
+
is_row_invisible = _is_task_row_fully_invisible(
|
68 |
+
visibility[start_row : start_row + n_avail_tasks]
|
69 |
+
)
|
70 |
+
print(start_row, start_row + n_avail_tasks, is_row_invisible)
|
71 |
+
if is_row_invisible:
|
72 |
+
unchanged_up_to = start_row - n_avail_tasks
|
73 |
return (
|
74 |
+
[gr.Box.update()] * unchanged_up_to
|
75 |
+
+ [gr.Box.update(visible=False)] * (len(visibility) - unchanged_up_to)
|
76 |
+
+ [gr.Number.update()] * unchanged_up_to
|
77 |
+
+ [0] * (len(visibility) - unchanged_up_to)
|
78 |
)
|
79 |
|
80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
def _clear_error():
|
82 |
return gr.HighlightedText.update(value=None, visible=False)
|
83 |
|
|
|
98 |
|
99 |
# Put all defined variables into a dict, with names (except task inputs)
|
100 |
vars = {
|
101 |
+
f"{Input.VNAME}{i}": input_ for i, input_ in enumerate(input_vars) if input_
|
102 |
}
|
103 |
+
vars.update({f"{Task.VNAME}{i}": task for i, task in enumerate(task_outputs)})
|
|
|
|
|
104 |
# Get all variables referenced within the task inputs
|
105 |
prompt_vars = {v for ti in non_empty_task_inputs for v in re.findall("{(.*?)}", ti)}
|
106 |
|
app.py
CHANGED
@@ -1,7 +1,13 @@
|
|
1 |
import gradio as gr
|
2 |
|
3 |
import actions as a
|
4 |
-
from components import all_inputs, all_tasks
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
|
7 |
with gr.Blocks() as demo:
|
@@ -11,6 +17,7 @@ with gr.Blocks() as demo:
|
|
11 |
# Toolkit
|
12 |
Define input variables to be used in your tasks.
|
13 |
<br>Task outputs can be used in subsequent tasks.
|
|
|
14 |
<br>
|
15 |
<br>AI tasks call into ChatGPT to perform actions.
|
16 |
<br>Chain inputs and tasks to build an E2E application.
|
@@ -25,6 +32,12 @@ with gr.Blocks() as demo:
|
|
25 |
remove_input_btn = gr.Button("Remove input variable")
|
26 |
for t in all_tasks.values():
|
27 |
t.render()
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
with gr.Row():
|
29 |
add_task_btn = gr.Button("Add task")
|
30 |
remove_task_btn = gr.Button("Remove task")
|
@@ -46,15 +59,15 @@ with gr.Blocks() as demo:
|
|
46 |
)
|
47 |
add_task_btn.click(
|
48 |
a.add_task,
|
49 |
-
inputs=[
|
50 |
-
outputs=[
|
51 |
-
+ [
|
52 |
)
|
53 |
remove_task_btn.click(
|
54 |
a.remove_task,
|
55 |
-
inputs=[
|
56 |
-
outputs=[
|
57 |
-
+ [
|
58 |
)
|
59 |
|
60 |
# Sequential execution
|
@@ -64,7 +77,9 @@ with gr.Blocks() as demo:
|
|
64 |
for i, task in all_tasks.items():
|
65 |
execution_event = execution_event.then(
|
66 |
a.execute_task,
|
67 |
-
inputs=[task.component_id, error_message, task.n_inputs]
|
|
|
|
|
68 |
outputs=[task.output, error_message],
|
69 |
)
|
70 |
|
|
|
1 |
import gradio as gr
|
2 |
|
3 |
import actions as a
|
4 |
+
from components import AITask, all_inputs, all_tasks, VisitURL
|
5 |
+
|
6 |
+
|
7 |
+
def _get_all_vars_up_to(to: int):
|
8 |
+
return [in_.output for in_ in all_inputs.values()] + [
|
9 |
+
t.output for i, t in all_tasks.items() if i < to
|
10 |
+
]
|
11 |
|
12 |
|
13 |
with gr.Blocks() as demo:
|
|
|
17 |
# Toolkit
|
18 |
Define input variables to be used in your tasks.
|
19 |
<br>Task outputs can be used in subsequent tasks.
|
20 |
+
<br>5 input variables and 10 tasks allowed (for now).
|
21 |
<br>
|
22 |
<br>AI tasks call into ChatGPT to perform actions.
|
23 |
<br>Chain inputs and tasks to build an E2E application.
|
|
|
32 |
remove_input_btn = gr.Button("Remove input variable")
|
33 |
for t in all_tasks.values():
|
34 |
t.render()
|
35 |
+
task_picker = gr.Dropdown(
|
36 |
+
[AITask.NAME, VisitURL.NAME],
|
37 |
+
value=AITask.NAME,
|
38 |
+
label="Pick a new Task",
|
39 |
+
type="index",
|
40 |
+
)
|
41 |
with gr.Row():
|
42 |
add_task_btn = gr.Button("Add task")
|
43 |
remove_task_btn = gr.Button("Remove task")
|
|
|
59 |
)
|
60 |
add_task_btn.click(
|
61 |
a.add_task,
|
62 |
+
inputs=[task_picker] + [v for t in all_tasks.values() for v in t.visibilities], # type: ignore
|
63 |
+
outputs=[c for t in all_tasks.values() for c in t.gr_components] # type: ignore
|
64 |
+
+ [v for t in all_tasks.values() for v in t.visibilities],
|
65 |
)
|
66 |
remove_task_btn.click(
|
67 |
a.remove_task,
|
68 |
+
inputs=[v for t in all_tasks.values() for v in t.visibilities], # type: ignore
|
69 |
+
outputs=[c for t in all_tasks.values() for c in t.gr_components] # type: ignore
|
70 |
+
+ [v for t in all_tasks.values() for v in t.visibilities],
|
71 |
)
|
72 |
|
73 |
# Sequential execution
|
|
|
77 |
for i, task in all_tasks.items():
|
78 |
execution_event = execution_event.then(
|
79 |
a.execute_task,
|
80 |
+
inputs=[task.component_id, error_message, task.n_inputs]
|
81 |
+
+ task.inputs
|
82 |
+
+ _get_all_vars_up_to(i),
|
83 |
outputs=[task.output, error_message],
|
84 |
)
|
85 |
|
components.py
CHANGED
@@ -2,12 +2,13 @@ from abc import ABC, abstractmethod
|
|
2 |
from typing import Dict, List, Optional
|
3 |
|
4 |
import gradio as gr
|
|
|
5 |
|
6 |
import ai
|
7 |
|
8 |
|
9 |
class Component(ABC):
|
10 |
-
|
11 |
|
12 |
def __init__(self, id_: int, visible: bool = False):
|
13 |
# Internal state
|
@@ -40,11 +41,11 @@ class Component(ABC):
|
|
40 |
|
41 |
|
42 |
class Input(Component):
|
43 |
-
|
44 |
|
45 |
def _render(self, id_: int, visible: bool) -> gr.Textbox:
|
46 |
self.output = gr.Textbox(
|
47 |
-
label=f"Input: {{{self.
|
48 |
interactive=True,
|
49 |
placeholder="Variable value",
|
50 |
visible=visible,
|
@@ -55,12 +56,8 @@ class Input(Component):
|
|
55 |
pass
|
56 |
|
57 |
|
58 |
-
class
|
59 |
-
|
60 |
-
|
61 |
-
def __init__(self, id_: int, visible: bool = False):
|
62 |
-
super().__init__(id_, visible)
|
63 |
-
self.output: gr.Textbox
|
64 |
|
65 |
@abstractmethod
|
66 |
def inputs(self) -> List:
|
@@ -75,19 +72,26 @@ class Task(Component, ABC):
|
|
75 |
self.n_inputs = gr.Number(value=self._n_inputs, visible=False)
|
76 |
|
77 |
|
78 |
-
class AITask(
|
|
|
|
|
79 |
def _render(self, id_: int, visible: bool) -> gr.Box:
|
80 |
with gr.Box(visible=visible) as gr_component:
|
81 |
-
gr.Markdown(
|
|
|
|
|
|
|
|
|
|
|
82 |
with gr.Row():
|
83 |
self.prompt = gr.Textbox(
|
84 |
label="Instructions",
|
85 |
lines=10,
|
86 |
interactive=True,
|
87 |
-
placeholder="
|
88 |
)
|
89 |
self.output = gr.Textbox(
|
90 |
-
label=f"Output: {{{self.
|
91 |
lines=10,
|
92 |
interactive=False,
|
93 |
)
|
@@ -102,12 +106,88 @@ class AITask(Task):
|
|
102 |
return [self.prompt]
|
103 |
|
104 |
|
105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
MAX_TASKS = 10
|
107 |
|
108 |
|
109 |
all_inputs = {i: Input(i) for i in range(MAX_INPUTS)}
|
110 |
-
all_tasks = {i:
|
111 |
|
112 |
all_inputs[0]._initial_visibility = True
|
113 |
-
all_tasks[0]._initial_visibility = True
|
|
|
2 |
from typing import Dict, List, Optional
|
3 |
|
4 |
import gradio as gr
|
5 |
+
import requests
|
6 |
|
7 |
import ai
|
8 |
|
9 |
|
10 |
class Component(ABC):
|
11 |
+
VNAME = None
|
12 |
|
13 |
def __init__(self, id_: int, visible: bool = False):
|
14 |
# Internal state
|
|
|
41 |
|
42 |
|
43 |
class Input(Component):
|
44 |
+
VNAME = "v"
|
45 |
|
46 |
def _render(self, id_: int, visible: bool) -> gr.Textbox:
|
47 |
self.output = gr.Textbox(
|
48 |
+
label=f"Input: {{{self.VNAME}{id_}}}",
|
49 |
interactive=True,
|
50 |
placeholder="Variable value",
|
51 |
visible=visible,
|
|
|
56 |
pass
|
57 |
|
58 |
|
59 |
+
class TaskComponent(Component, ABC):
|
60 |
+
VNAME = "t"
|
|
|
|
|
|
|
|
|
61 |
|
62 |
@abstractmethod
|
63 |
def inputs(self) -> List:
|
|
|
72 |
self.n_inputs = gr.Number(value=self._n_inputs, visible=False)
|
73 |
|
74 |
|
75 |
+
class AITask(TaskComponent):
|
76 |
+
NAME = "AI Task"
|
77 |
+
|
78 |
def _render(self, id_: int, visible: bool) -> gr.Box:
|
79 |
with gr.Box(visible=visible) as gr_component:
|
80 |
+
gr.Markdown(
|
81 |
+
f"""
|
82 |
+
{self.NAME}
|
83 |
+
<br> Use this Task to give instructions to ChatGPT.
|
84 |
+
"""
|
85 |
+
)
|
86 |
with gr.Row():
|
87 |
self.prompt = gr.Textbox(
|
88 |
label="Instructions",
|
89 |
lines=10,
|
90 |
interactive=True,
|
91 |
+
placeholder="Example - summarize this text: {v1}",
|
92 |
)
|
93 |
self.output = gr.Textbox(
|
94 |
+
label=f"Output: {{{self.VNAME}{id_}}}",
|
95 |
lines=10,
|
96 |
interactive=False,
|
97 |
)
|
|
|
106 |
return [self.prompt]
|
107 |
|
108 |
|
109 |
+
class VisitURL(TaskComponent):
|
110 |
+
NAME = "Visit URL"
|
111 |
+
|
112 |
+
def _render(self, id_: int, visible: bool) -> gr.Box:
|
113 |
+
with gr.Box(visible=visible) as gr_component:
|
114 |
+
gr.Markdown(
|
115 |
+
f"""
|
116 |
+
{self.NAME}
|
117 |
+
<br> Use this Task to visit an URL and get its content.
|
118 |
+
"""
|
119 |
+
)
|
120 |
+
with gr.Row():
|
121 |
+
self.url = gr.Textbox(
|
122 |
+
interactive=True,
|
123 |
+
placeholder="URL",
|
124 |
+
show_label=False,
|
125 |
+
)
|
126 |
+
self.output = gr.Textbox(
|
127 |
+
label=f"Output: {{{self.VNAME}{id_}}}",
|
128 |
+
lines=10,
|
129 |
+
interactive=False,
|
130 |
+
)
|
131 |
+
return gr_component
|
132 |
+
|
133 |
+
def _execute(self, url: str, prompt_vars: Dict[str, str]) -> Optional[str]:
|
134 |
+
if url:
|
135 |
+
formatted_url = url.format(**prompt_vars)
|
136 |
+
return requests.get(formatted_url).text
|
137 |
+
|
138 |
+
def inputs(self) -> List[gr.Textbox]:
|
139 |
+
return [self.url]
|
140 |
+
|
141 |
+
|
142 |
+
class Task:
|
143 |
+
AVAILABLE_TASKS = [AITask, VisitURL]
|
144 |
+
VNAME = "t"
|
145 |
+
|
146 |
+
def __init__(self, id_: int):
|
147 |
+
self._id = id_
|
148 |
+
self.active_task = AITask.NAME # Default
|
149 |
+
self.inner_tasks = {t.NAME: t(self._id, False) for t in self.AVAILABLE_TASKS}
|
150 |
+
|
151 |
+
def render(self) -> None:
|
152 |
+
for t in self.inner_tasks.values():
|
153 |
+
t.render()
|
154 |
+
|
155 |
+
@property
|
156 |
+
def component_id(self) -> gr.Textbox:
|
157 |
+
return self.inner_tasks[self.active_task].component_id
|
158 |
+
|
159 |
+
@property
|
160 |
+
def visibilities(self) -> List[gr.Number]:
|
161 |
+
return [t.visible for t in self.inner_tasks.values()]
|
162 |
+
|
163 |
+
@property
|
164 |
+
def gr_components(self) -> List[gr.Box]:
|
165 |
+
return [t.gr_component for t in self.inner_tasks.values()]
|
166 |
+
|
167 |
+
@property
|
168 |
+
def output(self) -> gr.Textbox:
|
169 |
+
return self.inner_tasks[self.active_task].output
|
170 |
+
|
171 |
+
@property
|
172 |
+
def inputs(self) -> List[gr.Textbox]:
|
173 |
+
return self.inner_tasks[self.active_task].inputs()
|
174 |
+
|
175 |
+
@property
|
176 |
+
def n_inputs(self) -> int:
|
177 |
+
return self.inner_tasks[self.active_task].n_inputs
|
178 |
+
|
179 |
+
def execute(self, *args):
|
180 |
+
inner_task = self.inner_tasks[self.active_task]
|
181 |
+
print(f"Executing {inner_task._source} :: {inner_task._id}")
|
182 |
+
return inner_task.execute(*args)
|
183 |
+
|
184 |
+
|
185 |
+
MAX_INPUTS = 5
|
186 |
MAX_TASKS = 10
|
187 |
|
188 |
|
189 |
all_inputs = {i: Input(i) for i in range(MAX_INPUTS)}
|
190 |
+
all_tasks = {i: Task(i) for i in range(MAX_TASKS)}
|
191 |
|
192 |
all_inputs[0]._initial_visibility = True
|
193 |
+
all_tasks[0].inner_tasks[all_tasks[0].active_task]._initial_visibility = True
|
requirements.txt
CHANGED
@@ -1,3 +1,4 @@
|
|
1 |
gradio
|
2 |
openai
|
3 |
-
python-dotenv
|
|
|
|
1 |
gradio
|
2 |
openai
|
3 |
+
python-dotenv
|
4 |
+
requests==2.28.1
|