This is a high-impact "quality of life" feature that dramatically improves the workflow building experience. It reduces typos, removes the need to remember exact variable names, and makes the data flow within a workflow much more visible and explicit.
This feature will be implemented entirely within the GUI (editor_app.py), specifically within the StepEditorModal, with zero changes to the core engine or config.json structure.
Phase 1: Augmenting the StepEditorModal's Context
The modal needs more information to build the list of available variables. Currently, it only knows about the step it's editing. It also needs to know about the workflow that contains it.
Modify the App.open_step_editor Method:
When launching the StepEditorModal, we need to pass the entire parent workflow's definition.
Current Code:
modal = StepEditorModal(self, index, step_data, agent_def)
New Code:
parent_workflow_data = self.editor_frame_instance.get_data()
modal = StepEditorModal(self, index, step_data, agent_def, parent_workflow_data)
Update the StepEditorModal.__init__ Method:
Accept and store the new parent_workflow_data and the current step's index.
Current Code:
def __init__(self, parent, index, step_data, agent_def):
...
self.editing_data = copy.deepcopy(step_data)
self.agent_def = agent_def or {}
...
New Code:
def __init__(self, parent, index, step_data, agent_def, parent_workflow_data):
...
self.editing_data = copy.deepcopy(step_data)
self.agent_def = agent_def or {}
self.step_index = index
self.parent_workflow_data = parent_workflow_data
...
Phase 2: Building the Variable Selector UI
This involves creating a new UI component that will house the variable lists.
Modify StepEditorModal.create_widgets:
The StepEditorModal currently has a toolbox frame on the left. We will repurpose or replace this with our new, more powerful selector. Let's call the new frame variable_selector_frame.
The frame will contain a CTkTabview to keep the sources separate and clean.
Conceptual Code:
def create_widgets(self):
selector_frame = ctk.CTkFrame(self, width=250)
selector_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ns")
selector_tabs = ctk.CTkTabview(selector_frame)
selector_tabs.pack(fill="both", expand=True)
self.workflow_inputs_tab = selector_tabs.add("Workflow Inputs")
self.step_outputs_tab = selector_tabs.add("Step Outputs")
self.populate_variable_selector()
Create the populate_variable_selector Method:
This new method in StepEditorModal will do the heavy lifting of figuring out which variables are available and creating the buttons.
Conceptual Code:
def populate_variable_selector(self):
inputs_frame = ctk.CTkScrollableFrame(self.workflow_inputs_tab, label_text="Required")
inputs_frame.pack(fill="both", expand=True, padx=5, pady=5)
optionals_frame = ctk.CTkScrollableFrame(self.workflow_inputs_tab, label_text="Optional")
optionals_frame.pack(fill="both", expand=True, padx=5, pady=5)
outputs_frame = ctk.CTkScrollableFrame(self.step_outputs_tab, label_text="Available Outputs")
outputs_frame.pack(fill="both", expand=True, padx=5, pady=5)
for i in range(self.step_index):
step = self.parent_workflow_data['steps'][i]
agent_name = step.get('agent', 'Unknown')
ctk.CTkLabel(outputs_frame, text=f"Step {i}: {agent_name}", font=ctk.CTkFont(weight="bold")).pack(anchor="w", pady=(5,0))
for output_var in step.get('output', []):
btn = ctk.CTkButton(outputs_frame, text=output_var, anchor="w")
btn.pack(fill="x", padx=10)
Phase 3: The Interaction Logic
This phase makes the selector buttons functional. We need a way to tell the selector which input box is currently "active."
Track the Active Entry Field:
In StepEditorModal.__init__, add a new instance variable: self.active_entry = None.
In refresh_params_form, when creating the value_entry widgets for each parameter, bind a "focus in" event to them.
Conceptual Code (inside refresh_params_form loop):
value_entry.bind("<FocusIn>", lambda event, entry=value_entry: self.set_active_entry(entry))
Create the set_active_entry Method:
A simple method to update the tracked widget.
Code:
def set_active_entry(self, entry_widget):
self.active_entry = entry_widget
Create the append_variable_to_active_entry Method:
This is the command that the selector buttons will call.
Code:
def append_variable_to_active_entry(self, var_name):
if self.active_entry:
current_text = self.active_entry.get()
cursor_pos = self.active_entry.index(ctk.INSERT)
variable_string = f"${var_name}"
if current_text and current_text[cursor_pos-1] != ' ':
variable_string = " " + variable_string
self.active_entry.insert(cursor_pos, variable_string)
self.active_entry.focus()
Connect the Buttons in populate_variable_selector:
Now we can update the button creation logic to use our new command.
New Code (inside populate_variable_selector loops):
btn = ctk.CTkButton(outputs_frame, text=output_var, anchor="w",
command=lambda v=output_var: self.append_variable_to_active_entry(v))
btn.pack(fill="x", padx=10)
Plan Summary & Benefits
This plan provides a complete, robust implementation of the variable selector.
Context-Aware: It correctly calculates the available scope for any given step.
User-Friendly: It provides a clean, tabbed interface to browse available data points, reducing cognitive load.
Intuitive Interaction: The "focus-tracking" mechanism ensures that clicking a variable button inserts the text into the last-used input box, which is the expected behavior.
Reduces Errors: Prevents typos in variable names, a common source of bugs in this kind of system.
Self-Contained: The feature is entirely encapsulated within the StepEditorModal, keeping the rest of the application clean.
No comments:
Post a Comment