Skip to content

Seamless interoperability between image and imageList #4

@nathabee

Description

@nathabee

Change Request: Introduce image → imageList adapter + automatic lifting of single-image operators

Context

Before implementing complex grouped / nested pipelines, we need a simpler and more fundamental capability:

Seamless interoperability between image and imageList.

Currently:

  • image and imageList are strictly different artifact types.
  • An operator declared as image → image cannot accept imageList.
  • Users must manually build pipelines with explicit list-aware operators.

This creates friction and unnecessary duplication.


Goal

Introduce:

  1. Implicit adapter: image → imageList
  2. Automatic lifting of single-image operators over imageList

This enables:

imageList
  → resize (image → image)
  → convertToJpeg (image → image)
  → imagesToPdf (imageList → pdf)

Even though resize and convertToJpeg are defined only for image.


Required Behavior

1️⃣ Adapter: image → imageList

If pipeline start type is image, and next operator expects imageList, then:

image → [image]

i.e. wrap single image into a list of length 1.

This should be transparent.


2️⃣ Operator lifting (map semantics)

If:

  • Input artifact is imageList
  • Operator is defined as image → image

Then automatically apply the operator to each element:

imageList [i1, i2, i3]
    → resize
= [ resize(i1), resize(i2), resize(i3) ]

Output type becomes imageList.

This is conceptually:

map(op, imageList)

But should be automatic, not require a separate map operator.


Example Workflow

User selects 5 images:

imageList
  → resize (image → image)
  → convertToJpeg (image → image)
  → imagesToPdf (imageList → pdf)

Expected behavior:

  • Each image resized
  • Each image converted to JPEG
  • Final PDF has N pages

No explicit list-aware resize operator required.


Architectural Proposal

In typing layer

When checking canInsertBetween():

If:

beforeType = imageList
op.input = image

Then allow insertion if:

  • op.output = image

Effective result type becomes:

imageList

In runner

Pseudo-logic:

if (input.type === "imageList" && op.io.input === "image") {
  const results = input.images.map(img => runOpOnSingle(img));
  return { type: "imageList", images: results };
}

No changes required in individual operators.


Benefits

  • Eliminates need for duplicate operators (resizeList, jpegList, etc.)
  • Keeps operator catalogue clean
  • Enables natural chaining
  • Keeps typing system strong
  • No immediate need for DAG / complex pipeline model

Non-Goals (for this phase)

  • No nested imageList of imageList
  • No grouping yet
  • No branching
  • No DAG

This is a foundational interoperability layer before complex pipelines.


Acceptance Criteria

  • Single image can flow into imageList operator via automatic wrapping.
  • imageList can accept any image → image operator.
  • Result remains imageList.
  • PDF export correctly produces N pages.
  • No regression in existing single-image pipelines.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions