Back to Claude Scientific Skills

Workflow UI, Launch Plans, Messages, Results, and Automations

skills/latchbio-integration/references/ui-and-automation.md

2.55.09.3 KB
Original Source

Workflow UI, Launch Plans, Messages, Results, and Automations

The workflow signature defines types. LatchMetadata defines how users understand and enter those values. Treat interface design as part of workflow correctness.

Metadata

python
from latch.types.metadata import (
    LatchAuthor,
    LatchMetadata,
    LatchParameter,
    LatchRule,
    Params,
    Section,
    Spoiler,
    Text,
)

metadata = LatchMetadata(
    display_name="Bulk RNA-seq QC",
    author=LatchAuthor(
        name="Workflow Team",
        github="https://github.com/example",
    ),
    documentation="https://example.org/docs/rnaseq-qc",
    repository="https://github.com/example/rnaseq-qc",
    license="MIT",
    tags=["RNA-seq", "QC"],
    parameters={
        "reads": LatchParameter(
            display_name="Reads",
            description="FASTQ input files.",
            samplesheet=True,
        ),
        "minimum_quality": LatchParameter(
            display_name="Minimum quality",
            description="Minimum accepted Phred score.",
            rules=[
                LatchRule(
                    regex=r"^(?:[0-9]|[1-3][0-9]|40)$",
                    message="Choose an integer from 0 through 40.",
                )
            ],
        ),
        "output_directory": LatchParameter(
            display_name="Output directory",
            description="Destination for reports and processed outputs.",
            output=True,
        ),
    },
    flow=[
        Section(
            "Inputs",
            Text("Select samples and input reads."),
            Params("reads"),
        ),
        Spoiler(
            "Advanced quality settings",
            Params("minimum_quality"),
        ),
        Section(
            "Outputs",
            Params("output_directory"),
        ),
    ],
)

Apply it:

python
from dataclasses import dataclass
from pathlib import Path

from latch import small_task, workflow
from latch.types import LatchDir, LatchFile, LatchOutputDir


@dataclass
class SampleRow:
    sample_name: str
    reads: LatchFile


@small_task
def run_qc(
    reads: list[SampleRow],
    output_directory: LatchOutputDir,
    minimum_quality: int,
) -> LatchDir:
    local_output = Path("/root/rnaseq-qc")
    local_output.mkdir(parents=True, exist_ok=True)
    (local_output / "summary.txt").write_text(
        f"samples={len(reads)}\nminimum_quality={minimum_quality}\n",
        encoding="utf-8",
    )

    remote = output_directory.remote_path
    if remote is None:
        raise ValueError("output_directory must have a remote path")
    return LatchDir(str(local_output), remote)


@workflow(metadata)
def rnaseq_qc(
    reads: list[SampleRow],
    output_directory: LatchOutputDir,
    minimum_quality: int = 20,
) -> LatchDir:
    return run_qc(
        reads=reads,
        output_directory=output_directory,
        minimum_quality=minimum_quality,
    )

Metadata parameter keys must match the signature. The SDK adds default metadata for signature parameters omitted from the metadata object.

Although about_page_path is documented, SDK 2.76.8 can fail while serializing its Path value. Prefer documentation= and a descriptive workflow docstring until that defect is fixed.

Flow Elements

Current flow classes include:

  • Section(title, *children)
  • Text(markdown)
  • Title(markdown_title)
  • Params(*parameter_names)
  • Spoiler(title, *children)
  • Fork(parameter_name, display_name, **branches)
  • ForkBranch(display_name, *children)

flow replaces the default top-to-bottom layout. Include every visible parameter intentionally.

Use a Fork only with a matching string parameter in the workflow signature:

python
from latch.types.metadata import Fork, ForkBranch, Params

read_type_flow = Fork(
    "read_type",
    "Read layout",
    paired=ForkBranch("Paired-end", Params("read1", "read2")),
    single=ForkBranch("Single-end", Params("read1")),
)

Parameter Guidance

Use:

  • display_name and description for scientific meaning
  • rules for syntactic validation
  • hidden for rarely changed values, not security
  • batch_table_column for high-value bulk-run fields
  • samplesheet=True for dataclass-backed tabular inputs
  • allowed_tables to restrict Registry imports
  • output=True or LatchOutputFile / LatchOutputDir for new destinations

Do not use form defaults to hide scientific assumptions. State reference genome, strandedness, units, and algorithm defaults explicitly.

Launch Plans

LaunchPlan creates a named set of defaults shown under Test Data in the Console:

python
from latch.resources.launch_plan import LaunchPlan
from latch.types import LatchFile, LatchOutputDir

LaunchPlan(
    rnaseq_qc,
    "Small public example",
    {
        "reads": [
            SampleRow(
                sample_name="example",
                reads=LatchFile("latch:///test-data/example.fastq.gz"),
            )
        ],
        "minimum_quality": 20,
        "output_directory": LatchOutputDir(
            "latch:///test-results/rnaseq-qc"
        ),
    },
    description="Small input for interface and integration testing.",
)

Constructor:

python
LaunchPlan(
    workflow,
    name,
    default_params,
    *,
    description=None,
)

Rules:

  • Define launch plans at module scope so registration discovers them.
  • Parameter keys and values must match the workflow.
  • A launch plan name cannot contain ..
  • Use small public or workspace-safe test data.
  • Avoid destinations shared by concurrent users.
  • Never put secret values in a launch plan.

Execution Messages

Use messages for concise user-facing signals, not raw logs:

python
from latch import message, small_task


@small_task
def validate_columns(columns: list[str]) -> bool:
    required = {"sample_id", "condition"}
    missing = sorted(required.difference(columns))

    if missing:
        message(
            typ="error",
            data={
                "title": "Missing required columns",
                "body": ", ".join(missing),
            },
        )
        raise ValueError(f"missing columns: {missing}")

    message(
        typ="info",
        data={
            "title": "Input validated",
            "body": "Required sample columns are present.",
        },
    )
    return True

Valid message types are info, warning, and error. The data dictionary requires title and body.

Do not put secrets, signed URLs, patient identifiers, or excessive raw data in messages.

Results Page

Publish the most useful result locations:

python
from latch import small_task
from latch.executions import add_execution_results
from latch.types import LatchOutputDir


@small_task
def publish_results(output_directory: LatchOutputDir) -> None:
    remote = output_directory.remote_path
    if remote is None:
        raise ValueError("output directory must have a remote path")

    add_execution_results(
        [
            remote,
            f"{remote.rstrip('/')}/multiqc_report.html",
        ]
    )

For Nextflow parameters, NextflowParameter.results_paths can publish selected subpaths under an output directory.

Result links improve discoverability; they do not upload missing outputs or verify scientific validity.

Automations

Automations are configured in the Console and can trigger from:

  • A child added at any depth beneath a watched Latch Data directory
  • A recurring time interval

Data-added workflow contract

The automation workflow must have exactly one parameter:

python
from latch import small_task, workflow
from latch.types import LatchDir


@small_task
def process_new_data(input_directory: LatchDir) -> None:
    children = list(input_directory.iterdir())
    print(f"observed {len(children)} children")


@workflow
def automation_workflow(input_directory: LatchDir) -> None:
    process_new_data(input_directory=input_directory)

The trigger fires after the configured follow-up update period following the last addition. Modification or deletion alone does not trigger it.

Interval workflow contract

The workflow must have no parameters:

python
from latch import small_task, workflow


@small_task
def run_scheduled_check() -> None:
    print("scheduled check started")


@workflow
def scheduled_workflow() -> None:
    run_scheduled_check()

Automation safety

  • Automations can create recurring paid compute. Review frequency, map width, and worst-case cost before activation.
  • Make processing idempotent. A Registry record or durable marker can prevent duplicate work.
  • Do not hard-code secrets. Retrieve them inside tasks with get_secret().
  • Test the workflow manually before enabling the trigger.
  • Start with a long enough debounce/update period to avoid launch storms.
  • Disable the automation while changing its workflow contract.
  • Ensure the watched directory does not include the automation's own outputs.

Official Sources