$npx -y skills add astronomer/agents --skill migrating-ai-sdk-to-common-aiMigrates Airflow projects from airflow-ai-sdk to apache-airflow-providers-common-ai 0.4.0+. Use when replacing airflow-ai-sdk with the official Airflow AI provider - migrating LLM decorators (@task.llm, @task.agent, @task.llm_branch, @task.embed), switching from model strings/obj
| 1 | # Migrate airflow-ai-sdk to apache-airflow-providers-common-ai |
| 2 | |
| 3 | This skill migrates Airflow projects from `airflow-ai-sdk` to `apache-airflow-providers-common-ai` (target **0.4.0+**), the official Airflow AI provider built on PydanticAI. It also covers upgrading projects already on common-ai 0.1.x, since several capabilities (multimodal prompts, `toolsets`, embedding operators, structured-output XCom behavior) changed between 0.1.0 and 0.4.0. |
| 4 | |
| 5 | > **CRITICAL**: The new provider requires **Airflow 3.0+** and (for 0.4.0) **pydantic-ai-slim >= 1.71.0**. The API surface has changed: LLM configuration moves from code (model strings/objects) to Airflow connections (`pydanticai` type). There is no `@task.embed` in the new provider; embeddings move to the LlamaIndex integration or a plain `@task` (see Step 3). |
| 6 | |
| 7 | ## Before starting |
| 8 | |
| 9 | Use the Grep tool with the pattern below to inventory everything that needs to migrate: |
| 10 | |
| 11 | ``` |
| 12 | airflow_ai_sdk|airflow-ai-sdk|ai_sdk|@task\.llm|@task\.agent|@task\.llm_branch|@task\.embed |
| 13 | ``` |
| 14 | |
| 15 | From the results, capture: |
| 16 | |
| 17 | 1. All files importing `airflow-ai-sdk` / `airflow_ai_sdk` |
| 18 | 2. Which decorators are in use: `@task.llm`, `@task.agent`, `@task.llm_branch`, `@task.embed` |
| 19 | 3. The model configuration pattern (string names like `"gpt-5"`, or `OpenAIModel(...)` objects) |
| 20 | 4. Any `airflow_ai_sdk.BaseModel` subclasses used as `output_type` |
| 21 | |
| 22 | Use this inventory to drive the steps below. |
| 23 | |
| 24 | --- |
| 25 | |
| 26 | ## Step 1: Update requirements.txt |
| 27 | |
| 28 | **Remove:** |
| 29 | ``` |
| 30 | airflow-ai-sdk[openai] |
| 31 | # or any variant: airflow-ai-sdk[openai]==0.1.7, airflow-ai-sdk[anthropic], etc. |
| 32 | ``` |
| 33 | |
| 34 | **Add:** |
| 35 | ``` |
| 36 | apache-airflow-providers-common-ai[openai]>=0.4.0 |
| 37 | ``` |
| 38 | |
| 39 | Use the latest available 0.x version unless the user has pinned a specific one. Available extras (0.4.0): `[openai]`, `[anthropic]`, `[google]`, `[bedrock]`, `[llamaindex]`, `[langchain]`, `[mcp]`, plus file-format extras (`[pdf]`, `[docx]`, `[parquet]`, `[avro]`) for `DocumentLoaderOperator` and `[sql]`/`[common-sql]` for the SQL operators. There are no `[groq]`/`[mistral]` extras; for those providers install the matching `pydantic-ai-slim` extra yourself. |
| 40 | |
| 41 | Add `[llamaindex]` if the project migrates `@task.embed` to the `LlamaIndexEmbeddingOperator` (recommended, see Step 3). In that case `sentence-transformers` and `torch` can usually be **removed**, which shrinks the image considerably. Keep them only if the project stays on local sentence-transformers embeddings via plain `@task`. |
| 42 | |
| 43 | --- |
| 44 | |
| 45 | ## Step 2: Create PydanticAI connection |
| 46 | |
| 47 | The new provider uses an Airflow connection instead of model strings or objects in code. |
| 48 | |
| 49 | **Connection type:** `pydanticai` |
| 50 | **Default connection ID:** `pydanticai_default` |
| 51 | |
| 52 | ### Via environment variable (.env) |
| 53 | |
| 54 | ```bash |
| 55 | AIRFLOW_CONN_PYDANTICAI_DEFAULT='{ |
| 56 | "conn_type": "pydanticai", |
| 57 | "password": "<api-key>", |
| 58 | "extra": { |
| 59 | "model": "<provider>:<model-name>" |
| 60 | } |
| 61 | }' |
| 62 | ``` |
| 63 | |
| 64 | ### Model format |
| 65 | |
| 66 | The model field uses `provider:model` format: |
| 67 | |
| 68 | | Provider | Example model value | |
| 69 | |----------|-------------------| |
| 70 | | OpenAI | `openai:gpt-5` | |
| 71 | | Anthropic | `anthropic:claude-sonnet-4-20250514` | |
| 72 | | Google | `google:gemini-2.5-pro` | |
| 73 | | Groq | `groq:llama-3.3-70b-versatile` | |
| 74 | | Mistral | `mistral:mistral-large-latest` | |
| 75 | | Bedrock | `bedrock:us.anthropic.claude-sonnet-4-20250514-v1:0` | |
| 76 | |
| 77 | ### Custom endpoints (Ollama, vLLM, Snowflake Cortex, etc.) |
| 78 | |
| 79 | Set `host` to the base URL: |
| 80 | ```bash |
| 81 | AIRFLOW_CONN_PYDANTICAI_CORTEX='{ |
| 82 | "conn_type": "pydanticai", |
| 83 | "password": "<api-key>", |
| 84 | "host": "https://my-endpoint.com/v1", |
| 85 | "extra": { |
| 86 | "model": "openai:<model-name>" |
| 87 | } |
| 88 | }' |
| 89 | ``` |
| 90 | |
| 91 | Use the `openai:` prefix for any OpenAI-compatible API, regardless of the actual provider. |
| 92 | |
| 93 | ### Connection ID convention |
| 94 | |
| 95 | The env var name determines the connection ID: |
| 96 | - `AIRFLOW_CONN_PYDANTICAI_DEFAULT` creates `pydanticai_default` |
| 97 | - `AIRFLOW_CONN_PYDANTICAI_CORTEX` creates `pydanticai_cortex` |
| 98 | |
| 99 | ### Model resolution priority |
| 100 | |
| 101 | 1. `model_id` parameter on the decorator/operator (highest) |
| 102 | 2. `model` in connection's extra JSON (fallback) |
| 103 | |
| 104 | ### Other connection types (0.4.0) |
| 105 | |
| 106 | Besides `pydanticai`, the provider registers vendor-specific connection types: `pydanticai-azure` (Azure OpenAI: host = endpoint, extra `api_version`), `pydanticai-bedrock` (AWS credentials/region in extra), and `pydanticai-vertex` (GCP project/location in extra). The LlamaIndex and |