Skip to content

latch preview does not send flow metadata (Spoiler, Section, Params not rendered) #582

@nh13

Description

@nh13

Problem

latch preview ignores the flow array in LatchMetadata. Workflows using flow= (with Section, Spoiler, Params, etc.) render with the old default layout and a "Hidden Parameters" fallback instead of their defined UI structure. This forces a full latch register cycle just to verify layout changes.

Root cause

preview.py sends only per-parameter data via wf.interface.to_flyte_idl().inputs. The workflow-level __metadata__ dict — which contains the flow array — is serialized into the workflow's docstring by _inject_metadata() but is never extracted or forwarded by the preview service.

The upsertWorkflowPreview mutation only accepts argAccountId and argInputs, with no field for workflow-level metadata.

Proposed fix

SDK side (src/latch_cli/services/preview.py): Extract the __metadata__ block from the workflow docstring and pass it to the mutation.

import yaml

# After obtaining `wf`, extract workflow-level metadata from docstring
metadata = {}
docstring = wf.python_interface.docstring or ""
if "__metadata__:" in docstring:
    yaml_start = docstring.index("__metadata__:")
    try:
        parsed = yaml.safe_load(docstring[yaml_start:])
        if isinstance(parsed, dict):
            metadata = parsed.get("__metadata__", {})
    except yaml.YAMLError:
        pass

# Include in mutation
execute(
    gql.gql("""
        mutation UpdatePreview($accountId: BigInt!, $inputs: String!, $metadata: JSON) {
            upsertWorkflowPreview(
                input: { argAccountId: $accountId, argInputs: $inputs, argMetadata: $metadata }
            ) {
                clientMutationId
            }
        }
    """),
    {
        "accountId": current_workspace(),
        "inputs": MessageToJson(wf.interface.to_flyte_idl().inputs),
        "metadata": metadata if metadata else None,
    },
)

Backend side: Add an optional argMetadata parameter (JSON) to the upsertWorkflowPreview mutation and persist it so the preview page can use flow for rendering, the same way registered workflows do.

Fallback: When metadata is None or flow is absent, behavior is unchanged — the frontend falls back to per-parameter _tmp.hidden and section_title layout.

Reproduction

from latch.resources.workflow import workflow
from latch.types.metadata import LatchMetadata, LatchParameter, Params, Section, Spoiler

metadata = LatchMetadata(
    display_name="Test Spoiler Preview",
    parameters={
        "name": LatchParameter(display_name="Name"),
        "advanced_opt": LatchParameter(display_name="Advanced Option"),
    },
    flow=[
        Section("Inputs", Params("name")),
        Spoiler("Advanced", Params("advanced_opt")),
    ],
)

@workflow(metadata)
def test_wf(name: str = "hello", advanced_opt: int = 42) -> str:
    ...

Run latch preview . — sections and spoiler are not rendered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions