Mastodon Politics, Power, and Science: Implementation Plan: Context-Aware Variable Selector

Monday, August 4, 2025

Implementation Plan: Context-Aware Variable Selector

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.

  1. Modify the App.open_step_editor Method:

    • When launching the StepEditorModal, we need to pass the entire parent workflow's definition.

    • Current Code:

      Generated python
      modal = StepEditorModal(self, index, step_data, agent_def)
          
    • New Code:

      Generated python
      # Get a reference to the parent workflow's full data
      parent_workflow_data = self.editor_frame_instance.get_data()
      
      modal = StepEditorModal(self, index, step_data, agent_def, parent_workflow_data)
          
  2. Update the StepEditorModal.__init__ Method:

    • Accept and store the new parent_workflow_data and the current step's index.

    • Current Code:

      Generated python
      def __init__(self, parent, index, step_data, agent_def):
          ...
          self.editing_data = copy.deepcopy(step_data)
          self.agent_def = agent_def or {}
          ...
          
    • New Code:

      Generated python
      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 # Store the current step's index
          self.parent_workflow_data = parent_workflow_data # Store the parent workflow
          ...
          

Phase 2: Building the Variable Selector UI

This involves creating a new UI component that will house the variable lists.

  1. 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:

      Generated python
      def create_widgets(self):
          # ... existing setup ...
          
          # Replace the old 'toolbox' with our new selector
          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)
      
          # Create the tabs
          self.workflow_inputs_tab = selector_tabs.add("Workflow Inputs")
          self.step_outputs_tab = selector_tabs.add("Step Outputs")
          # Optional: a tab for constants or global config values
      
          # ... rest of the widget creation for the form on the right ...
      
          # Call a new method to populate the lists
          self.populate_variable_selector()
          
  2. 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:

      Generated python
      def populate_variable_selector(self):
          # --- Populate Workflow Inputs Tab ---
          inputs_frame = ctk.CTkScrollableFrame(self.workflow_inputs_tab, label_text="Required")
          inputs_frame.pack(fill="both", expand=True, padx=5, pady=5)
          # Add buttons for each item in self.parent_workflow_data.get('inputs', [])
      
          optionals_frame = ctk.CTkScrollableFrame(self.workflow_inputs_tab, label_text="Optional")
          optionals_frame.pack(fill="both", expand=True, padx=5, pady=5)
          # Add buttons for each item in self.parent_workflow_data.get('optional_inputs', [])
      
          # --- Populate Step Outputs Tab ---
          outputs_frame = ctk.CTkScrollableFrame(self.step_outputs_tab, label_text="Available Outputs")
          outputs_frame.pack(fill="both", expand=True, padx=5, pady=5)
          
          # Iterate through all steps BEFORE the current one
          for i in range(self.step_index):
              step = self.parent_workflow_data['steps'][i]
              agent_name = step.get('agent', 'Unknown')
              
              # Add a non-clickable label for the step to provide context
              ctk.CTkLabel(outputs_frame, text=f"Step {i}: {agent_name}", font=ctk.CTkFont(weight="bold")).pack(anchor="w", pady=(5,0))
              
              # For each output of that step, create a clickable button
              for output_var in step.get('output', []):
                  # The button's command will call a new method to append the text
                  btn = ctk.CTkButton(outputs_frame, text=output_var, anchor="w")
                  btn.pack(fill="x", padx=10)
                  # We need a way to link this button to an entry field. This is the tricky part.
                  # We will solve this in the next phase.
          

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."

  1. 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):

      Generated python
      # ... create value_entry ...
      value_entry.bind("<FocusIn>", lambda event, entry=value_entry: self.set_active_entry(entry))
          
  2. Create the set_active_entry Method:

    • A simple method to update the tracked widget.

    • Code:

      Generated python
      def set_active_entry(self, entry_widget):
          self.active_entry = entry_widget
          
  3. Create the append_variable_to_active_entry Method:

    • This is the command that the selector buttons will call.

    • Code:

      Generated python
      def append_variable_to_active_entry(self, var_name):
          if self.active_entry:
              # Get current text and cursor position
              current_text = self.active_entry.get()
              cursor_pos = self.active_entry.index(ctk.INSERT)
              
              # Construct the variable string
              variable_string = f"${var_name}"
              
              # Insert the text. Check if we need a space.
              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() # Set focus back to the entry
          
  4. 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):

      Generated python
      # ... inside loop creating buttons ...
      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

Progress on the campaign manager

You can see that you can build tactical maps automatically from the world map data.  You can place roads, streams, buildings. The framework ...