← FlyteCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to Flyte
Snapshot Sep 30, 2026 · 22:59 UTC · version 1.0.1
Collection source: not recorded for this historical snapshot.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Migrates Flyte 1 tasks and workflows to Flyte 2, where the @task, @workflow, and @dynamic decorators collapse into a single @env.task on a flyte.TaskEnvironment and a workflow becomes a task that calls other tasks. Use when the user is migrating Flyte 1 tasks/workflows to Flyte 2. Trigger words: migrate task, migrate workflow, @task, @workflow, @dynamic, TaskEnvironment, env.task.",
"included_files": [],
"name": "flyte-migrate-tasks-workflows",
"skill_md_contents": "---\nname: flyte-migrate-tasks-workflows\ndescription: >-\n Migrates Flyte 1 tasks and workflows to Flyte 2, where the @task, @workflow,\n and @dynamic decorators collapse into a single @env.task on a\n flyte.TaskEnvironment and a workflow becomes a task that calls other tasks.\n Use when the user is migrating Flyte 1 tasks/workflows to Flyte 2. Trigger\n words: migrate task, migrate workflow, @task, @workflow, @dynamic,\n TaskEnvironment, env.task.\n---\n\n# Flyte 1 to 2 Migration: Tasks and Workflows\n\nThe biggest structural change in Flyte 2 is that everything is a task. The Flyte 1 `@task`, `@workflow`, and `@dynamic` decorators all collapse into a single `@env.task` on a `flyte.TaskEnvironment`, and a \"workflow\" is now just a task that calls other tasks. This skill covers the structural shift, sequential ordering without the `>>` operator, nested subworkflows, TaskEnvironment configuration basics, and the full `@task` parameter mapping.\n\n## Grounding References\n\n| Resource | URL |\n|---|---|\n| Migration guide | https://www.union.ai/docs/v2/flyte/user-guide/migration/flyte-2/tasks-and-workflows/ |\n| Official docs | https://www.union.ai/docs/v2/flyte |\n| Docs index (LLMs) | https://www.union.ai/docs/v2/flyte/llms.txt |\n| SDK API reference | https://www.union.ai/docs/v2/union/api-reference/flyte-sdk/ |\n| Example code | https://github.com/unionai/unionai-examples |\n| Flyte MCP tools | Available via `flyte-mcp` server |\n\n## The Structural Shift\n\nIn Flyte 1 you decorated units of work with `@task`, composed them with `@workflow`, and used `@dynamic` for runtime-generated graphs. In Flyte 2 you create one `flyte.TaskEnvironment` that carries the configuration, then decorate every function — leaf tasks and orchestrating \"workflows\" alike — with `@env.task`. There is no separate `@workflow` decorator: the entrypoint is just a task that calls other tasks.\n\n## Hello World: Tasks and Workflows\n\nA `@task` plus `@workflow` becomes two `@env.task`s, where the entrypoint task calls the others. Sequential calls are naturally ordered — no `>>` operator required.\n\n### Flyte 1\n\n```python\nimport flytekit\n\n@flytekit.task\ndef say_hello(name: str) -> str:\n return f\"Hello, {name}!\"\n\n@flytekit.task\ndef to_upper(greeting: str) -> str:\n return greeting.upper()\n\n@flytekit.workflow\ndef main(name: str) -> str:\n greeting = say_hello(name=name)\n return to_upper(greeting=greeting)\n```\n\n### Flyte 2\n\n```python\nimport flyte\n\nenv = flyte.TaskEnvironment(name=\"hello_world\")\n\n@env.task\ndef say_hello(name: str) -> str:\n return f\"Hello, {name}!\"\n\n@env.task\ndef to_upper(greeting: str) -> str:\n return greeting.upper()\n\n# The \"workflow\" is now just a task that calls other tasks.\n@env.task\ndef main(name: str) -> str:\n greeting = say_hello(name)\n return to_upper(greeting)\n```\n\nNote that Flyte 2 task calls pass arguments positionally (`say_hello(name)`) rather than requiring keyword arguments as in Flyte 1 (`say_hello(name=name)`).\n\n## Chaining and Ordering\n\nIn Flyte 1 you sometimes used `>>` to force ordering between tasks with no data dependency. In Flyte 2, sequential (synchronous) calls run in the order they are written, and `await`ing async tasks in sequence does the same. The `>>` operator is gone.\n\n### Flyte 1\n\n```python\nfrom flytekit import task, workflow\n\n@task\ndef clear_staging_table() -> None:\n # Side effect only: truncate the staging table.\n print(\"cleared staging table\")\n\n@task\ndef load_into_staging() -> None:\n # Side effect only: load fresh rows into staging.\n print(\"loaded staging table\")\n\n@task\ndef publish_to_prod() -> None:\n # Side effect only: swap staging into the production table.\n print(\"published to prod\")\n\n@workflow\ndef main() -> None:\n clear = clear_staging_table()\n load = load_into_staging()\n publish = publish_to_prod()\n\n # These tasks pass no data between them, so use the >> operator to force\n # ordering: clear must finish before load, which must finish before publish.\n clear >> load >> publish\n```\n\n### Flyte 2\n\n```python\nimport flyte\n\nenv = flyte.TaskEnvironment(name=\"staging_publish\")\n\n@env.task\ndef clear_staging_table() -> None:\n print(\"cleared staging table\")\n\n@env.task\ndef load_into_staging() -> None:\n print(\"loaded staging table\")\n\n@env.task\ndef publish_to_prod() -> None:\n print(\"published to prod\")\n\n# Sequential (synchronous) calls run in the order they're written, even when no\n# data flows between them. The Flyte 1 `>>` ordering operator is gone.\n@env.task\ndef main() -> None:\n clear_staging_table()\n load_into_staging()\n publish_to_prod()\n```\n\n## Subworkflows\n\nA `@workflow` invoked by another `@workflow` (for example, a reusable preprocessing pipeline) becomes a task that calls other tasks — nest them as deeply as you like.\n\n### Flyte 1\n\n```python\nfrom flytekit import task, workflow\n\n@task\ndef impute(value: float) -> float:\n # Replace missing/negative sentinel values with 0.\n return value if value >= 0 else 0.0\n\n@task\ndef scale(value: float) -> float:\n return value / 100.0\n\n@workflow\ndef preprocess(value: float) -> float:\n imputed = impute(value=value)\n return scale(value=imputed)\n\n@workflow\ndef main(raw_value: float) -> float:\n return preprocess(value=raw_value)\n```\n\n### Flyte 2\n\n```python\nimport flyte\n\nenv = flyte.TaskEnvironment(name=\"subworkflow\")\n\n@env.task\ndef impute(value: float) -> float:\n # Replace missing/negative sentinel values with 0.\n return value if value >= 0 else 0.0\n\n@env.task\ndef scale(value: float) -> float:\n return value / 100.0\n\n# A preprocessing \"subworkflow\" is just a task that calls other tasks.\n@env.task\ndef preprocess(value: float) -> float:\n imputed = impute(value)\n return scale(imputed)\n\n@env.task\ndef main(raw_value: float) -> float:\n return preprocess(raw_value)\n```\n\n## TaskEnvironment Configuration\n\nThe `TaskEnvironment` holds the configuration that Flyte 1 spread across the `@task` decorator. The task decorator can still override a few settings per-task.\n\n```python\nimport flyte\n\nenv = flyte.TaskEnvironment(\n name=\"my_env\", # Required: unique name\n image=flyte.Image.from_debian_base(), # Or a string, or \"auto\"\n resources=flyte.Resources(\n cpu=\"2\",\n memory=\"4Gi\",\n gpu=\"A100:1\",\n disk=\"10Gi\",\n ),\n env_vars={\"LOG_LEVEL\": \"INFO\"},\n secrets=[flyte.Secret(key=\"api-key\", as_env_var=\"API_KEY\")],\n cache=\"auto\", # \"auto\", \"override\", \"disable\", or a Cache object\n reusable=flyte.ReusePolicy(replicas=5, idle_ttl=60),\n interruptible=True,\n)\n\n# The task decorator can override some settings:\n@env.task(\n short_name=\"my_task\", # Display name\n cache=\"disable\", # Override cache\n retries=3, # Retry count\n timeout=3600, # Seconds or a timedelta\n report=True, # Generate an HTML report\n)\ndef my_task(x: int) -> int:\n return x\n```\n\n## Parameter Mapping: `@task` to `TaskEnvironment` + `@env.task`\n\n| Flyte 1 `@task` parameter | Flyte 2 location | Notes |\n|---|---|---|\n| `container_image` | `TaskEnvironment(image=...)` | Env-level only |\n| `requests` | `TaskEnvironment(resources=...)` | Env-level only |\n| `limits` | `TaskEnvironment(resources=...)` | Combined with requests (single value) |\n| `environment` | `TaskEnvironment(env_vars=...)` | Env-level only |\n| `secret_requests` | `TaskEnvironment(secrets=...)` | Env-level only |\n| `cache` | Both | Can override at task level |\n| `cache_version` | `flyte.Cache(version_override=...)` | In a `Cache` object |\n| `retries` | `@env.task(retries=...)` | Task-level only |\n| `timeout` | `@env.task(timeout=...)` | Task-level only |\n| `interruptible` | Both | Can override at task level |\n| `pod_template` | Both | Can override at task level |\n| `deprecated` | N/A | Not in Flyte 2 |\n| `docs` | `@env.task(docs=...)` | Task-level only |\n\nFor image, resource, secret, and caching detail, see the Task configuration migration page.\n\n## Anti-Patterns\n\n1. **Don't reach for `>>`** — the ordering operator is gone. Sequential synchronous calls already run in written order; `await` async tasks in sequence for the same effect.\n2. **Don't look for a `@workflow` decorator** — there isn't one. The orchestrating entrypoint is just another `@env.task` that calls other tasks.\n3. **Don't look for a `@dynamic` decorator** — dynamic graphs also collapse into ordinary `@env.task` functions that call other tasks at runtime.\n4. **Don't put heavy compute in the orchestrating task** — keep the entrypoint task focused on calling other tasks; push CPU/GPU/memory-intensive work into leaf tasks whose resources you can tune per environment.\n5. **Don't set image, resources, or secrets on the `@env.task` decorator** — those are env-level and belong on `TaskEnvironment`. Only per-task overrides like `retries`, `timeout`, `cache`, and `short_name` go on `@env.task`.\n6. **Don't keep passing every argument by keyword** — Flyte 2 task calls accept positional arguments (`say_hello(name)`).\n"
}SHA-256 of public snapshot: 1ef08b319d5b3c659436175744131d13a6c7a81931a3948d799c528d09f9ce00