{"id":11234,"plugin_id":"plugin_asdk_app_6a79985b0ef881918e389c82400ea95c","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T22:59:03.097Z","digest":"a5259a3c10f562528ba78fda4e73590c6e7a156079848c543e9b4a71d87327ca","against":null,"payload":{"description":"Migrates Flyte 1 task configuration, container images, resources, caching, secrets, scheduling, and CLI/config-file usage to Flyte 2 equivalents. Use when migrating Flyte 1 task configuration, images, resources, secrets, scheduling, or CLI/config-file usage to Flyte 2. Trigger words include resources, ImageSpec, cache_version, secrets, LaunchPlan, CronSchedule, pyflyte, config, register, and deploy.","included_files":[],"name":"flyte-migrate-config","skill_md_contents":"---\nname: flyte-migrate-config\ndescription: Migrates Flyte 1 task configuration, container images, resources, caching, secrets, scheduling, and CLI/config-file usage to Flyte 2 equivalents. Use when migrating Flyte 1 task configuration, images, resources, secrets, scheduling, or CLI/config-file usage to Flyte 2. Trigger words include resources, ImageSpec, cache_version, secrets, LaunchPlan, CronSchedule, pyflyte, config, register, and deploy.\n---\n\n# Flyte 1 to Flyte 2 Migration: Task Configuration and CLI/Config\n\nIn Flyte 1, image, resources, caching, secrets, and scheduling were configured per-task on the `@task` decorator or per-workflow on a `LaunchPlan`. In Flyte 2 most of this moves to the `flyte.TaskEnvironment`, so it is declared once and shared. The CLI is renamed from `pyflyte` to `flyte` and the config file is trimmed down. This skill covers migrating those settings and commands.\n\n## Grounding References\n\n| Resource | URL |\n|---|---|\n| Migration guide (Task configuration) | https://www.union.ai/docs/v2/flyte/user-guide/migration/flyte-2/configuration/ |\n| Migration guide (CLI) | https://www.union.ai/docs/v2/flyte/user-guide/migration/flyte-2/cli-and-configuration/ |\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| CLI API reference | https://www.union.ai/docs/v2/union/api-reference/flyte-cli/ |\n| Example code | https://github.com/unionai/unionai-examples |\n\n## Image, resources, and caching move to the TaskEnvironment\n\nImage, resources, and caching move from the `@task` decorator to the `TaskEnvironment`. Per-task settings like `retries` and `timeout` stay on `@env.task`. Note that `mem` is renamed to `memory`, and there are no separate `requests`/`limits` — a single `Resources` value serves as both.\n\n### Flyte 1\n\n```python\nfrom datetime import timedelta\n\nimport flytekit\nfrom flytekit import Resources\n\nimage = flytekit.ImageSpec(\n    name=\"training-image\",\n    packages=[\"scikit-learn\", \"pandas\"],\n)\n\n@flytekit.task(\n    container_image=image,\n    requests=Resources(cpu=\"2\", mem=\"4Gi\"),\n    limits=Resources(cpu=\"4\", mem=\"8Gi\"),\n    cache=True,\n    cache_version=\"1.0\",\n    retries=3,\n    timeout=timedelta(minutes=30),\n)\ndef train_epoch(step: int) -> float:\n    # A stand-in for a training step that returns the current loss.\n    return 1.0 / (step + 1)\n\n@flytekit.workflow\ndef main(step: int) -> float:\n    return train_epoch(step=step)\n```\n\n### Flyte 2\n\n```python\nfrom datetime import timedelta\n\nimport flyte\n\n# Image, resources, and caching move to the TaskEnvironment, so they are declared\n# once and shared by every task in the environment.\nenv = flyte.TaskEnvironment(\n    name=\"training\",\n    image=flyte.Image.from_debian_base().with_pip_packages(\"scikit-learn\", \"pandas\"),\n    resources=flyte.Resources(cpu=\"2\", memory=\"4Gi\"),  # \"memory\", not \"mem\"\n    cache=\"auto\",\n)\n\n# retries and timeout stay on the task decorator.\n@env.task(retries=3, timeout=timedelta(minutes=30))\ndef train_epoch(step: int) -> float:\n    # A stand-in for a training step that returns the current loss.\n    return 1.0 / (step + 1)\n\n@env.task\ndef main(step: int) -> float:\n    return train_epoch(step)\n```\n\n## Container images: ImageSpec to flyte.Image\n\nFlyte 1's `ImageSpec` is replaced by Flyte 2's `flyte.Image` with a fluent builder API. Instead of one constructor with many arguments, you start from a base and chain builder methods.\n\n```python\nfrom flyte import Image\n\nimage = (\n    Image.from_debian_base(name=\"my-image\", registry=\"ghcr.io/myorg\", python_version=(3, 11))\n    .with_pip_packages(\"pandas\", \"numpy\")\n    .with_apt_packages(\"curl\", \"git\")\n    .with_env_vars({\"MY_VAR\": \"value\"})\n)\n```\n\n| Constructor | Use case |\n|---|---|\n| `Image.from_debian_base()` | Most common; includes the Flyte SDK |\n| `Image.from_base(image_uri)` | Start from any existing image |\n| `Image.from_dockerfile(path)` | Complex custom builds |\n| `Image.from_uv_script(path)` | UV-based projects |\n\nCommon chainable builder methods: `.with_pip_packages(...)`, `.with_requirements(path)`, `.with_uv_project(path)`, `.with_apt_packages(...)`, `.with_commands([...])`, `.with_source_file(path, dst=...)`, `.with_source_folder(path, dst=...)`, `.with_env_vars({...})`, and `.with_workdir(...)`.\n\n| Flyte 1 `ImageSpec` | Flyte 2 `Image` | Notes |\n|---|---|---|\n| `name` | `name` (constructor) | Same |\n| `registry` | `registry` (constructor) | Same |\n| `python_version` | `python_version` (tuple) | `\"3.11\"` becomes `(3, 11)` |\n| `packages` | `.with_pip_packages()` | Method instead of param |\n| `apt_packages` | `.with_apt_packages()` | Method instead of param |\n| `requirements` | `.with_requirements()` | Supports txt, poetry.lock, uv.lock |\n| `env` | `.with_env_vars()` | Method instead of param |\n| `commands` | `.with_commands()` | Method instead of param |\n| `copy` / `source_root` | `.with_source_file()` / `.with_source_folder()` | More explicit methods |\n| `base_image` | `Image.from_base()` | Different constructor |\n| `builder` | Config file or `flyte.init()` | Global setting |\n| `platform` | `platform` (constructor) | Tuple: `(\"linux/amd64\", \"linux/arm64\")` |\n\nFor a private registry, create an image-pull secret and reference it:\n\n```bash\nflyte create secret --type image_pull my-registry-secret --from-file ~/.docker/config.json\n```\n\n```python\nimage = Image.from_debian_base(\n    registry=\"private.registry.com\",\n    name=\"my-image\",\n    registry_secret=\"my-registry-secret\",\n)\n```\n\n## Resources and GPUs\n\nA single `flyte.Resources` value serves as both request and limit — there are no separate `requests`/`limits`. Several parameters were renamed.\n\n| Flyte 1 | Flyte 2 | Notes |\n|---|---|---|\n| `cpu=\"1\"` | `cpu=\"1\"` | Same |\n| `mem=\"2Gi\"` | `memory=\"2Gi\"` | Renamed |\n| `gpu=\"1\"` | `gpu=\"A100:1\"` | `Type:count` format |\n| `ephemeral_storage=\"10Gi\"` | `disk=\"10Gi\"` | Renamed |\n| N/A | `shm=\"auto\"` | New: shared memory |\n\nGPU type and count are combined into one string, replacing the separate Flyte 1 `accelerator=` argument:\n\n```python\nenv = flyte.TaskEnvironment(\n    name=\"gpu_env\",\n    resources=flyte.Resources(\n        cpu=\"4\",\n        memory=\"32Gi\",\n        gpu=\"A100:2\",              # Type:count\n        # gpu=\"A100 80G:1\"         # 80GB variant\n        # gpu=flyte.GPU(\"A100\", count=1, partition=\"1g.5gb\")   # MIG partition\n    ),\n)\n```\n\nSupported GPU types include A10, A10G, A100, A100 80G, B200, H100, H200, L4, L40s, T4, V100, RTX PRO 6000, and GB10.\n\n## Caching: cache_version to cache=\"auto\" / CachePolicy\n\nCaching is enabled at the env level with `cache=\"auto\"` (or per-task on `@env.task`). The explicit `cache_version` string moves into a `flyte.Cache` object.\n\n| Behavior | Description |\n|---|---|\n| `\"auto\"` | Cache results and reuse if available |\n| `\"override\"` | Always execute and overwrite the cache |\n| `\"disable\"` | No caching (default for a `TaskEnvironment`) |\n\n```python\n# Flyte 1: @task(cache=True, cache_version=\"1.0\")\n# Flyte 2:\n@env.task(cache=\"auto\")\ndef cached_task(x: int) -> int:\n    return x * 2\n\n# Advanced control (replaces cache_version, serialize, ignored_inputs, ...)\n@env.task(cache=flyte.Cache(\n    behavior=\"auto\",\n    version_override=\"v1.0\",\n    serialize=True,\n    ignored_inputs=(\"debug\",),\n))\ndef advanced(x: int, debug: bool = False) -> int:\n    return x * 2\n```\n\n## Secrets: current_context().secrets to env vars\n\nSecrets move from `secret_requests` on the task to `secrets` on the `TaskEnvironment`, and you read them from environment variables instead of `current_context().secrets` — for example, an API key for a model registry or hosted LLM.\n\n### Flyte 1\n\n```python\nfrom flytekit import task, workflow, Secret, current_context\n\n@task(secret_requests=[Secret(group=\"openai\", key=\"api_key\")])\ndef call_api() -> str:\n    token = current_context().secrets.get(group=\"openai\", key=\"api_key\")\n    return f\"token has {len(token)} chars\"\n\n@workflow\ndef main() -> str:\n    return call_api()\n```\n\n### Flyte 2\n\n```python\nimport os\n\nimport flyte\n\n# Secrets are declared on the TaskEnvironment and injected as environment\n# variables (instead of read through current_context().secrets).\nenv = flyte.TaskEnvironment(\n    name=\"secrets\",\n    secrets=[flyte.Secret(key=\"openai_api_key\", as_env_var=\"OPENAI_API_KEY\")],\n)\n\n@env.task\ndef call_api() -> str:\n    token = os.getenv(\"OPENAI_API_KEY\", \"\")\n    return f\"token has {len(token)} chars\"\n\n@env.task\ndef main() -> str:\n    return call_api()\n```\n\nA `flyte.Secret` can be mounted as an environment variable or as a file, and the access convention changes:\n\n```python\nflyte.Secret(key=\"openai-key\", as_env_var=\"OPENAI_API_KEY\")   # mount as env var\nflyte.Secret(key=\"access-key\", group=\"aws\")                    # env var: AWS_ACCESS_KEY\nflyte.Secret(key=\"ssl-cert\", mount=\"/etc/flyte/secrets\")       # mount as a file\n```\n\n| Flyte 1 pattern | Flyte 2 pattern |\n|---|---|\n| `ctx.secrets.get(key=\"mykey\", group=\"mygroup\")` | `os.environ[\"MYGROUP_MYKEY\"]` (auto-named) |\n| `ctx.secrets.get(key=\"mykey\")` | `os.environ[\"MY_SECRET\"]` (with `as_env_var=\"MY_SECRET\"`) |\n\nCreate and manage secrets from the CLI:\n\n```bash\nflyte create secret MY_SECRET_KEY --value my_secret_value\nflyte create secret MY_SECRET_KEY --from-file /path/to/secret\nflyte get secret\nflyte delete secret MY_SECRET_KEY\n```\n\n## Scheduling: LaunchPlan + CronSchedule to flyte.Trigger + flyte.Cron\n\nA `LaunchPlan` with a `CronSchedule` (say, a nightly retraining job) becomes a `flyte.Trigger` attached directly to the task. Use `flyte.TriggerTime` to bind the scheduled fire time to an input, and deploy the trigger with `flyte deploy`.\n\n### Flyte 1\n\n```python\nfrom flytekit import task, workflow, LaunchPlan, CronSchedule\n\n@task\ndef retrain(kickoff_time: str) -> str:\n    return f\"retrained model at {kickoff_time}\"\n\n@workflow\ndef main(kickoff_time: str) -> str:\n    return retrain(kickoff_time=kickoff_time)\n\n# A LaunchPlan attaches a schedule (and default inputs) to a workflow.\nnightly_retrain = LaunchPlan.get_or_create(\n    workflow=main,\n    name=\"nightly_retrain\",\n    schedule=CronSchedule(\n        schedule=\"0 2 * * *\",  # 2 AM daily\n        kickoff_time_input_arg=\"kickoff_time\",\n    ),\n)\n```\n\n### Flyte 2\n\n```python\nfrom datetime import datetime\n\nimport flyte\n\nenv = flyte.TaskEnvironment(name=\"scheduling\")\n\n# A Trigger replaces LaunchPlan + CronSchedule. It is attached directly to the\n# task and deployed with it (flyte deploy). flyte.TriggerTime binds the\n# scheduled fire time to a task input.\nnightly_retrain = flyte.Trigger(\n    name=\"nightly_retrain\",\n    automation=flyte.Cron(\"0 2 * * *\"),  # 2 AM daily\n    inputs={\"trigger_time\": flyte.TriggerTime},\n    auto_activate=True,\n)\n\n@env.task(triggers=nightly_retrain)\ndef main(trigger_time: datetime = datetime(2024, 1, 1, 2, 0)) -> str:\n    return f\"retrained model at {trigger_time.isoformat()}\"\n```\n\nTriggers support `flyte.Cron(\"0 9 * * *\", timezone=\"America/New_York\")` and `flyte.FixedRate(timedelta(hours=1))` as automations, plus convenience constructors like `flyte.Trigger.hourly()` and `flyte.Trigger.daily()`.\n\n## CLI command mapping: pyflyte to flyte\n\nThe command-line tool is renamed from `pyflyte` to `flyte`, and remote is now the default.\n\n| Flyte 1 | Flyte 2 | Notes |\n|---|---|---|\n| `pyflyte run` | `flyte run` | Similar, different flags |\n| `pyflyte run --remote` | `flyte run` | Remote is the default in Flyte 2 |\n| `pyflyte run` (local) | `flyte run --local` | Local execution is now explicit |\n| `pyflyte register` | `flyte deploy` | Different concept |\n| `pyflyte package` | N/A | Not needed in Flyte 2 |\n| `pyflyte serialize` | N/A | Not needed in Flyte 2 |\n\n### Running tasks — Flyte 1\n\n```bash\n# Local\npyflyte run my_module.py my_workflow --arg1 value1\n\n# Remote\npyflyte --config config.yaml run --remote my_module.py my_workflow --arg1 value1\n```\n\n### Running tasks — Flyte 2\n\n```bash\n# Remote (default)\nflyte run my_module.py my_task --arg1 value1\n\n# Local\nflyte run --local my_module.py my_task --arg1 value1\n\n# With an explicit config file\nflyte --config config.yaml run my_module.py my_task --arg1 value1\n```\n\n### Deploying (register to deploy)\n\nIn Flyte 1 you registered a module; in Flyte 2 you deploy task environments.\n\n#### Flyte 1\n\n```bash\npyflyte register my_module.py -p my-project -d development\n```\n\n#### Flyte 2\n\n```bash\n# Deploy a task environment\nflyte deploy my_module.py my_env --project my-project --domain development\n\n# Deploy all environments in a file\nflyte deploy --all my_module.py\n\n# Deploy with an explicit version, or recursively\nflyte deploy --version v1.0.0 my_module.py my_env\nflyte deploy --recursive --all ./src\n```\n\n### Key flag differences\n\n| Flyte 1 flag | Flyte 2 flag | Notes |\n|---|---|---|\n| `--remote` | (default) | Remote is the default |\n| `--copy-all` | `--copy-style all` | File copying |\n| N/A | `--copy-style loaded_modules` | Default: only imported modules |\n| `-p, --project` | `--project` | Same |\n| `-d, --domain` | `--domain` | Same |\n| `-i, --image` | `--image` | Same format |\n| N/A | `--follow, -f` | Follow execution logs |\n\n## Configuration files\n\nThe config file lives in the same place (`~/.flyte/config.yaml`), but the environment variable changes from `FLYTECTL_CONFIG` to `FLYTE_CONFIG`, and the format is simpler.\n\n### Flyte 1\n\n```yaml\nadmin:\n  endpoint: dns:///your-cluster.hosted.unionai.cloud\n  insecure: false\n  authType: Pkce\n```\n\n### Flyte 2\n\n```yaml\nadmin:\n  endpoint: dns:///your-cluster.hosted.unionai.cloud\n\nimage:\n  builder: remote  # or \"local\"\n\ntask:\n  domain: development\n  org: your-org\n  project: your-project\n```\n\n| Setting | Flyte 1 | Flyte 2 |\n|---|---|---|\n| Endpoint | `admin.endpoint` | `admin.endpoint` |\n| Auth type | `admin.authType` | Auto-detected (PKCE default) |\n| Project | CLI flag `-p` | `task.project` (default) |\n| Domain | CLI flag `-d` | `task.domain` (default) |\n| Organization | CLI flag `--org` | `task.org` (default) |\n| Image builder | N/A | `image.builder` (`local` or `remote`) |\n\n### Configuring in code\n\n```python\nimport flyte\n\n# From a config file (auto-discovers, or pass a path)\nflyte.init_from_config()\nflyte.init_from_config(\"path/to/config.yaml\")\n\n# Programmatically\nflyte.init(\n    endpoint=\"flyte.example.com\",\n    project=\"my-project\",\n    domain=\"development\",\n)\n```\n\nFor API-key authentication in non-interactive environments, use `flyte.init_from_api_key()`.\n\n## Anti-Patterns\n\n1. **Don't keep `image`, `resources`, and `cache` on `@env.task`** — move them onto the shared `flyte.TaskEnvironment`; only per-task settings like `retries` and `timeout` stay on `@env.task`.\n2. **Don't use `mem`, `ephemeral_storage`, or separate `requests`/`limits`** — use `memory`, `disk`, and a single `flyte.Resources` value that serves as both.\n3. **Don't pass GPUs with `gpu=\"1\"` plus `accelerator=`** — combine type and count into one `Type:count` string like `gpu=\"A100:2\"`.\n4. **Don't rebuild `ImageSpec`'s many constructor args** — start from a base (`Image.from_debian_base()`) and chain `.with_*` builder methods.\n5. **Don't keep `cache_version=\"1.0\"`** — use `cache=\"auto\"` for the common case, or `flyte.Cache(version_override=...)` for advanced control.\n6. **Don't read secrets via `current_context().secrets.get(...)`** — declare them on the `TaskEnvironment` and read the injected environment variable with `os.environ` / `os.getenv`.\n7. **Don't recreate `LaunchPlan` + `CronSchedule`** — use `flyte.Trigger` with `flyte.Cron` attached to the task, and deploy it with `flyte deploy`.\n8. **Don't run `pyflyte ... --remote`** — `flyte run` is remote by default; add `--local` explicitly for in-process runs.\n9. **Don't use `pyflyte register`** — use `flyte deploy` to deploy task environments.\n10. **Don't set `FLYTECTL_CONFIG` or rely on `admin.authType`** — use `FLYTE_CONFIG` and the simpler config format with auto-detected auth.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}