Beyond Linear: Empowering User Choice with Multi-Output AI Workflow Steps
Dive into how we built a dynamic workflow engine feature that allows AI steps to generate multiple alternative outputs, pause for user selection, and adapt the pipeline based on choice.
In the world of AI-powered applications, flexibility is key. While automated workflows are powerful, sometimes the most valuable insights come from offering users a choice, especially when dealing with the creative or nuanced outputs of Large Language Models (LLMs). We recently tackled this challenge head-on, implementing a significant enhancement to our workflow engine: multi-output step selection.
Imagine a workflow where an AI generates not just one, but several alternative suggestions for a given task – say, three different feature ideas for an application. Our new system allows the workflow to pause, present these options to the user, and then continue the pipeline, dynamically incorporating the chosen alternative. This transforms our rigid, linear workflows into interactive, user-steered experiences.
This post will walk you through the journey of bringing this feature to life, from updating our data models to crafting intuitive UI, and share some valuable lessons learned along the way.
The Vision: Dynamic Choice in AI Workflows
Our primary goal was clear: empower users to guide their workflows. Specifically, we wanted certain LLM-driven steps to:
- Generate
Ndistinct alternative outputs. - Pause the workflow execution.
- Present these alternatives in a clear, actionable way to the user.
- Allow the user to select their preferred alternative.
- Resume the workflow, with subsequent steps utilizing the content of the selected alternative.
This feature is now fully integrated across all six layers of our stack – from the database schema to the user interface – and is already powering dynamic steps in our "Extension Builder" workflow. We also snuck in a highly requested feature: an inline, collapsible prompt viewer and editor directly on the workflow detail page.
Building the Foundation: Data and Logic
To support multi-output steps, we started at the core: our data model and backend logic.
Evolving the Data Model
The first step was to extend our WorkflowStep model in prisma/schema.prisma. We added three crucial fields:
generateCount: An integer indicating how many alternatives an LLM step should generate.alternatives: A JSON array to store the generated options (each containing its content, metadata like token count, and estimated cost).selectedIndex: An integer pointing to the user's chosen alternative within thealternativesarray.
// Example snippet from schema.prisma
model WorkflowStep {
// ... other fields
generateCount Int? // How many alternatives to generate
alternatives Json? // Array of alternative outputs
selectedIndex Int? // Index of the chosen alternative
// ...
}
Intelligent Variation Strategies
Generating N distinct alternatives isn't just about calling the LLM N times. We needed strategic variation. We introduced VARIATION_STRATEGIES constants, which define different prompt suffixes and temperature offsets (e.g., "Balanced", "Conservative", "Ambitious"). These strategies are applied to the LLM call for each alternative, ensuring a diverse set of outputs.
Orchestrating the Engine
The runWorkflow() function, the heart of our engine, now includes a sophisticated alternatives generation loop. When it encounters a step with generateCount > 1:
- It executes the
executeStep()functionNtimes, each time modifying the prompt based on ourVARIATION_STRATEGIES. - It stores these outputs as a JSON array in the
alternativesfield. - Crucially, it then pauses the workflow, waiting for user input.
We also implemented robust resume handling: if a step has alternatives and a selectedIndex, the engine knows to use the content of that specific alternative as the step's output and continue processing. A "resume guard" prevents continuation if alternatives exist but no selection has been made.
The tRPC API for Interaction
To enable user selection, we added a new selectAlternative mutation to our workflows.ts tRPC router. This mutation:
- Validates user ownership and ensures the
selectedIndexis within bounds. - Sets the
selectedIndexon theWorkflowStep. - Updates the step's primary output (
content) with the chosen alternative's content.
We also updated our create and duplicate workflow mutations to properly pass through the generateCount property, ensuring new workflows can leverage this feature from the start.
Crafting the User Experience: Frontend Magic
A powerful backend needs an equally intuitive frontend. We focused on making the selection and interaction seamless.
Builder UI Integration
In our workflow builder, we added generateCount to our StepTemplate and StepConfig interfaces. For LLM steps, a simple 1x/2x/3x toggle now allows users to quickly configure the number of alternatives to generate.
The Execution UI: Choice and Control
The workflow execution page ([id]/page.tsx) received a major overhaul:
- Alternative Selection Panel: When a step pauses for selection, a prominent panel appears. It features radio-style cards for each alternative, displaying a label, estimated tokens, cost, and a preview of the content. These cards can be expanded/collapsed for detailed review.
- Sticky "Continue" Button: A sticky button at the bottom clearly indicates which alternative is selected (e.g., "Continue with 'Balanced Feature Set'") and, when clicked, triggers the
selectAlternativemutation followed by a workflowresume. - Post-Selection View: After a selection, other unchosen alternatives are neatly tucked away in a collapsible "Other alternatives" section, complete with copy buttons for easy access.
- Visual Cues: A
[Nx]badge in the step header immediately signals that a step generates multiple outputs.
Empowering Prompt Editing
While building the alternatives feature, we also seized the opportunity to add a highly requested control: inline prompt editing. Each step now features a collapsible PROMPT + system section that displays the system and user prompts in monospace blocks. Users can edit these prompts directly via text areas and save their changes using a new steps.update mutation, allowing for real-time iteration on prompt engineering.
Lessons Learned: Navigating the Development Minefield
No significant feature development is without its challenges. Here are a few "gotchas" we encountered and how we overcame them:
1. Prisma's Json Type and TypeScript Frustrations
- Challenge: When trying to store our
alternativesarray (which is an array of objects) directly into a PrismaJsonfield, TypeScript threw an error:Record<string, unknown>[]is not assignable toInputJsonValue. - Solution: The workaround involved an explicit type cast:
alternatives as unknown as Prisma.InputJsonValue. We also had to ensureimport { Prisma } from "@prisma/client"was present. This highlights the occasional impedance mismatch between strict TypeScript types and more flexible database JSON types.
2. The Stale Prisma Client
- Challenge: After adding the
generateCountfield to our Prisma schema, attempts to create a new workflow via our tRPC API failed with anUnknown argument 'generateCount'error. - Solution: This is a classic: the Prisma client was stale and hadn't picked up the new schema changes. Running
npx prisma generateand restarting the development server regenerated the client and resolved the issue. Always remember to regenerate your Prisma client after schema changes!
3. Playwright Scrolling Quirks
- Challenge: During Playwright test development, we tried using
window.scrollTo()to capture screenshots of specific elements on mobile viewports. However, the scroll didn't take effect. - Solution: We realized our dashboard uses a specific scrollable container, not the
windowitself. The workaround was to usefullPage: truescreenshots (capturing the entire scrollable content) and programmatically toggle step expansion via button clicks to ensure all elements were visible for testing. This reminds us to be mindful of custom scroll implementations in web apps.
What's Next? Refining and Expanding
With the core feature successfully deployed, we're already looking at the next set of improvements:
- End-to-End Testing: Rigorous testing of the alternatives flow, from creation to selection and downstream content usage, especially on mobile.
- Prompt Editing for Completed Steps: Consider adding the prompt edit button to completed steps as well, which would enable easy re-running of steps with modified prompts.
- Accurate Cost Estimation: Our
estimateWorkflowCostfunction currently doesn't account for thegenerateCountmultiplier. Updating this to reflect the true cost of generating multiple alternatives will provide users with more accurate insights.
This multi-output step selection feature is a significant leap forward in making our AI workflows more interactive, intelligent, and user-centric. By giving users the power to choose, we're not just automating tasks; we're empowering them to steer the creative process.