DEV Community

张文超
张文超

Posted on

A State Model for AI Image Workflows That Don't Lose Context

Most AI image interfaces are optimized for one event: submit an input and receive an output. Real creative work is a sequence. A user generates a draft, chooses one result, edits it, removes a background, enhances it, compares versions, and finally exports a file.

If each step is modeled as an isolated form submission, the application loses the relationships that make the workflow useful. The user has to upload the same image again, reconstruct the prompt, and guess which version should be edited next.

The fix is not another image tool. It is a small state model that treats every result as part of a task lineage.

Separate tasks, runs, and assets

A practical model needs at least three entities:

  • Task: the user's durable intent, such as creating a product hero image.
  • Run: one model or editing operation, including its inputs and settings.
  • Asset: a source image or result file that can be reused by later runs.

The important relationship is that a run consumes assets and produces assets. A result is not just a URL returned by a provider; it is an asset with provenance.

type ImageRun = {
  id: string
  taskId: string
  operation: string
  prompt: string | null
  inputAssetIds: string[]
  outputAssetIds: string[]
  model: string
  settings: Record<string, unknown>
  status: 'queued' | 'running' | 'succeeded' | 'failed'
}
Enter fullscreen mode Exit fullscreen mode

This makes "continue from this result" a normal operation: create a new run whose input is the selected output asset. There is no download-upload round trip and no ambiguous current file.

Make result selection explicit

Generating four images does not mean all four are equally important. Store the user's selection instead of inferring it from the most recent timestamp.

A task can have:

  • a selected working asset;
  • an optional final asset;
  • an ordered history of runs;
  • a delivery specification.

The selected working asset is the default input for the next operation. The final asset is the version that passed review. These are different states: a user may continue editing a promising result without declaring it finished.

Preserve the delivery contract

The task should also keep the requirements that determine whether an image is done:

type DeliverySpec = {
  width?: number
  height?: number
  format: 'png' | 'jpg' | 'webp'
  maxBytes?: number
  background?: 'transparent' | 'opaque' | 'either'
  protectedElements?: string[]
}
Enter fullscreen mode Exit fullscreen mode

Finalization can then validate dimensions, format, file size, and required visual constraints before enabling the final download. This is more reliable than treating the last generated image as the deliverable.

Keep provider details behind the run

Different image providers return different job identifiers, status shapes, and result payloads. Normalize those differences inside the run layer. The user-facing task should not depend on a provider's temporary response format.

Useful fields to retain include:

  • provider job ID;
  • model and resolution;
  • normalized cost;
  • raw error category;
  • retry relationship;
  • timestamps for queue, start, and completion.

This also makes retries safer. A retry should create a new run connected to the failed run, not silently overwrite its evidence.

Treat export as a state transition

Export is often implemented as a plain download button. In a result-first workflow it is better modeled as:

  1. select a final asset;
  2. validate it against the delivery spec;
  3. choose filename and output format;
  4. create an export record;
  5. download the exported artifact.

That record answers a useful operational question later: which exact file did the user deliver?

I am applying this model in ImgFast Workspace, where the primary flow is Create → Continue → Final → Download. The implementation details will vary by stack, but the durable idea is the same: preserve the lineage between intent, inputs, runs, results, and delivery.

When the data model reflects that sequence, the UI becomes simpler. The next action can be attached to a visible result, history becomes explainable, and "final" stops being whichever file happens to be newest.

Top comments (0)