toolkit / app.py
lgaleana's picture
Implemented sequential execution
5c20801
from typing import NamedTuple, Optional, Type, Union
import gradio as gr
import ai
from utils.io import print_system
MAX_INPUTS = 10
MAX_TASKS = 50
class Input:
def render(self, visible: bool) -> gr.Row:
with gr.Row(visible=visible) as gr_component:
self.output_name = gr.Textbox(
label="Input name (can be referenced with {})",
interactive=True,
placeholder="Variable name",
)
self.output = gr.Textbox(
label="Input value",
interactive=True,
placeholder="Variable value",
)
return gr_component
def execute(self) -> None:
pass
class AITask:
@property
def vars(self):
return [self.prompt]
def render(self, visible: bool) -> gr.Box:
with gr.Box(visible=visible) as gr_component:
gr.Markdown(f"AI task")
with gr.Row():
with gr.Column():
self.prompt = gr.Textbox(
label="Instructions",
lines=13,
interactive=True,
placeholder="What is the AI assistant meant to do?",
)
with gr.Column():
self.output_name = gr.Textbox(
label="Output name (can be referenced with {})",
interactive=True,
placeholder="Variable name",
)
self.output = gr.Textbox(
show_label=False,
lines=10,
interactive=False,
)
return gr_component
def execute(self, prompt: str) -> Optional[str]:
if prompt:
return ai.llm.next([{"role": "user", "content": prompt}])
class Component:
def __init__(
self, id_: float, internal: Union[Input, AITask], visible: bool = False
):
# Internal state
self._id = id_
self.internal = internal
self._source = self.internal.__class__.__name__
self._initial_visibility = visible
# Gradio state
self.component_id: gr.Number
self.source: gr.Textbox
self.visible: gr.Number
self.gr_component = gr.Box
self.output_name: gr.Textbox
self.output: gr.Textbox
def render(self) -> None:
self.component_id = gr.Number(value=self._id, visible=False)
self.source = gr.Textbox(value=self._source, visible=False)
self.visible = gr.Number(int(self._initial_visibility), visible=False)
self.gr_component = self.internal.render(self._initial_visibility)
self.output_name = self.internal.output_name
self.output = self.internal.output
def execute(self, *args):
print_system(f"Executing component :: {self._source}.{self._id}")
return self.internal.execute(*args)
class Variable(NamedTuple):
source: Type[Union[Input, AITask]]
id_: int
name: str
value: str
all_inputs = {float(i): Component(i, Input()) for i in range(MAX_INPUTS)}
all_tasks = {float(i): Component(i, AITask()) for i in range(MAX_TASKS)}
all_inputs[0]._initial_visibility = True
all_tasks[0]._initial_visibility = True
def add_input(*visibility):
for i, visible in enumerate(visibility, 1):
if not bool(visible):
return (
[gr.Row.update(visible=True)] * i
+ [gr.Row.update(visible=False)] * (MAX_INPUTS - i)
+ [1] * i
+ [0] * (MAX_INPUTS - i)
)
def remove_input(*visibility):
for i, visible in reversed(list(enumerate(visibility, 1))):
if bool(visible):
return (
[gr.Row.update(visible=True)] * (i - 1)
+ [gr.Row.update(visible=False)] * (MAX_INPUTS - i + 1)
+ [1] * (i - 1)
+ [0] * (MAX_INPUTS - i + 1)
)
def add_task(*visibility):
for i, visible in enumerate(visibility, 1):
if not bool(visible):
return (
[gr.Box.update(visible=True)] * i
+ [gr.Box.update(visible=False)] * (MAX_TASKS - i)
+ [1] * i
+ [0] * (MAX_TASKS - i)
)
def remove_task(*visibility):
for i, visible in reversed(list(enumerate(visibility, 1))):
if bool(visible):
return (
[gr.Box.update(visible=True)] * (i - 1)
+ [gr.Box.update(visible=False)] * (MAX_TASKS - i + 1)
+ [1] * (i - 1)
+ [0] * (MAX_TASKS - i + 1)
)
def execute_task(id_: float, prompt: str):
if prompt:
return all_tasks[id_].execute(prompt)
with gr.Blocks() as demo:
# Initial layout
for i in all_inputs.values():
i.render()
input_error = gr.HighlightedText(
[("Repeated variable names in inputs. Please pick different names.", "Error")],
show_label=False,
visible=False,
)
with gr.Row():
add_input_btn = gr.Button("Add input variable")
remove_input_btn = gr.Button("Remove input variable")
execute_btn = gr.Button("Execute")
for t in all_tasks.values():
t.render()
task_error = gr.HighlightedText(
[("Repeated variable names in tasks. Please pick different names.", "Error")],
show_label=False,
visible=False,
)
with gr.Row():
add_task_btn = gr.Button("Add task")
remove_task_btn = gr.Button("Remove task")
# Layout editing
add_input_btn.click(
add_input,
inputs=[i.visible for i in all_inputs.values()],
outputs=[i.gr_component for i in all_inputs.values()] # type: ignore
+ [i.visible for i in all_inputs.values()],
)
remove_input_btn.click(
remove_input,
inputs=[i.visible for i in all_inputs.values()],
outputs=[i.gr_component for i in all_inputs.values()] # type: ignore
+ [i.visible for i in all_inputs.values()],
)
add_task_btn.click(
add_task,
inputs=[i.visible for i in all_tasks.values()],
outputs=[i.gr_component for i in all_tasks.values()] # type: ignore
+ [i.visible for i in all_tasks.values()],
)
remove_task_btn.click(
remove_task,
inputs=[i.visible for i in all_tasks.values()],
outputs=[i.gr_component for i in all_tasks.values()] # type: ignore
+ [i.visible for i in all_tasks.values()],
)
# Sequential execution
execution_event = execute_btn.click(
execute_task,
inputs=[all_tasks[0].component_id, all_tasks[0].internal.prompt], # type: ignore
outputs=[all_tasks[0].output],
)
for task in list(all_tasks.values())[1:]:
execution_event = execution_event.then(
execute_task,
inputs=[task.component_id, task.internal.prompt], # type: ignore
outputs=[task.output],
)
demo.launch()