Skip to content

Flow Parameters (parameters.yaml)

Every flow can declare its tunable inputs in a parameters.yaml file. The values are loaded automatically when the flow runs and exposed through the context-aware params accessor — no manual passing, no global state to thread through your task signatures.

flows/
└── my_flow/
    ├── parameters.yaml    # ← this file
    ├── catalog.yaml
    └── flow.py
# flows/my_flow/parameters.yaml
model_name: gpt-4o
batch_size: 32
temperature: 0.0
gates:
  quality: 3.5
  faithfulness: 4.0
from latent.prefect import flow, task, params, logger

@flow("my_flow")
async def my_flow():
    logger.info(f"Scoring with {params.model_name} (batch={params.batch_size})")
    threshold = params.gates["quality"]
    ...

Accessing parameters

Inside any @flow- or @task-decorated function, read parameters off params. Three equivalent forms, plus a safe .get():

params.model_name              # attribute access — raises if missing
params["model_name"]           # item access — raises if missing
params.get("model_name")       # returns None if missing
params.get("batch_size", 32)   # returns 32 if missing (default)

Only inside a flow context

params resolves against the current flow. Reading it outside a @flow/@task body raises RuntimeError ("No flow context available"). Attribute/item access on a missing key raises; use params.get(key, default) when a parameter is optional.


The layering model

parameters.yaml is not read in isolation — the final config is merged from several layers. Highest priority wins; nested dicts are deep-merged (so you can override a single nested key without restating the whole block):

Priority Layer Source
1 (highest) CLI flags latent run <flow> --param value
2 Flow parameters flows/<flow>/parameters.yaml
3 Global defaults flows/global.yaml
4 (lowest) Bundled defaults shipped inside a library flow's package

After merging, ${VAR} / $VAR environment variables are substituted, and finally — if the flow declares one — the result is validated against a Pydantic schema.

global.yaml — shared defaults

Put values common to every flow in flows/global.yaml. Each flow's own parameters.yaml overrides them:

# flows/global.yaml
mlflow_experiment: my-project
model_name: gpt-4o-mini      # default model for all flows
# flows/my_flow/parameters.yaml
model_name: gpt-4o           # this flow overrides the global default
batch_size: 64

At runtime params.model_name == "gpt-4o" and params.mlflow_experiment == "my-project".


Environment variables

Any string value may contain ${VAR_NAME} or $VAR_NAME; it is replaced with the environment variable's value at load time. Substitution is recursive (works inside nested dicts and lists). If the variable is unset, the placeholder is left as-is and a warning is logged.

# flows/my_flow/parameters.yaml
api_base: ${OPENAI_API_BASE}
data_root: ${LATENT_WORKSPACE_ROOT}/data
prompt_version: $PROMPT_VERSION

Tip

latent run exports LATENT_WORKSPACE_ROOT before loading parameters, so ${LATENT_WORKSPACE_ROOT} is always resolvable when you run via the CLI.


Typed parameters

Pass a Pydantic model as config_schema to validate parameters and get typed, auto-completing attribute access. Validation runs after merging and env substitution; a bad config fails fast with a clear error.

from pydantic import BaseModel
from latent.prefect import flow, params

class MyConfig(BaseModel):
    model_name: str
    batch_size: int = 32
    temperature: float = 0.0

@flow("my_flow", config_schema=MyConfig)
async def my_flow():
    # params now proxies the validated MyConfig instance
    reveal_type = params.batch_size   # int, validated and coerced

With a schema, params.batch_size returns the validated/coerced value, and an unknown or mistyped parameter raises a ValueError at flow start rather than surfacing as a confusing failure deep inside a task.


Cross-flow parameters

Read a parameter from another flow's parameters.yaml with dot notation via params.get(). This loads the other flow's merged config on demand:

# Reuse the judge model configured by the scoring flow
judge_model = params.get("scoring_flow.model_name", default="gpt-4o")

Cross-flow access works only through params.get("flow.param") — attribute and item access (params.x) always target the current flow.


Overriding from the CLI

latent run forwards extra flags into the parameter system, overriding parameters.yaml for that run. Underscores and hyphens are interchangeable:

latent run my_flow --model-name gpt-4o --batch-size 64
# inside the flow: params.model_name == "gpt-4o", params.batch_size == 64

Overrides are matched to the flow's declared parameters and made available through params.get(...). See latent run.


See Also